]> git.karo-electronics.de Git - mv-sheeva.git/commitdiff
Merge branch 'topic/misc' into for-linus
authorTakashi Iwai <tiwai@suse.de>
Sun, 18 Mar 2012 17:22:33 +0000 (18:22 +0100)
committerTakashi Iwai <tiwai@suse.de>
Sun, 18 Mar 2012 17:22:33 +0000 (18:22 +0100)
33 files changed:
include/sound/pcm.h
include/sound/version.h
include/sound/ymfpci.h
sound/aoa/codecs/onyx.c
sound/aoa/codecs/tas.c
sound/core/control.c
sound/core/init.c
sound/core/misc.c
sound/core/pcm_lib.c
sound/core/pcm_native.c
sound/pci/au88x0/au88x0.h
sound/pci/au88x0/au88x0_core.c
sound/pci/au88x0/au88x0_pcm.c
sound/pci/ctxfi/ctvmem.c
sound/pci/ice1712/ice1724.c
sound/pci/ymfpci/ymfpci_main.c
sound/spi/at73c213.c
sound/usb/6fire/chip.c
sound/usb/6fire/chip.h
sound/usb/6fire/comm.c
sound/usb/6fire/comm.h
sound/usb/6fire/common.h
sound/usb/6fire/control.c
sound/usb/6fire/control.h
sound/usb/6fire/firmware.c
sound/usb/6fire/midi.c
sound/usb/6fire/midi.h
sound/usb/6fire/pcm.c
sound/usb/6fire/pcm.h
sound/usb/Kconfig
sound/usb/pcm.c
sound/usb/usx2y/usbusx2yaudio.c
sound/usb/usx2y/usx2yhwdeppcm.c

index 0cf91b2f08caaf4f9a8a70cee07a25cacc108986..4ae9e22c48274347bab444cca22dbf9c28d0fc60 100644 (file)
@@ -264,7 +264,7 @@ struct snd_pcm_hw_constraint_ratdens {
 
 struct snd_pcm_hw_constraint_list {
        unsigned int count;
-       unsigned int *list;
+       const unsigned int *list;
        unsigned int mask;
 };
 
@@ -781,7 +781,8 @@ void snd_interval_muldivk(const struct snd_interval *a, const struct snd_interva
                          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_list(struct snd_interval *i, unsigned int count,
+                     const 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);
index 8fc5321e1ecc739fa7bd0cdb30c7e63ac0fc1e43..cc75024c10895c651d8771f525517080de16de70 100644 (file)
@@ -1,3 +1,3 @@
 /* include/version.h */
-#define CONFIG_SND_VERSION "1.0.24"
+#define CONFIG_SND_VERSION "1.0.25"
 #define CONFIG_SND_DATE ""
index 444cd6ba0ba70645c8652089d39250e58d30f8cf..41199664666bb4a96d98b5336f6433b333ef12db 100644 (file)
@@ -366,6 +366,8 @@ struct snd_ymfpci {
 #ifdef CONFIG_PM
        u32 *saved_regs;
        u32 saved_ydsxgr_mode;
+       u16 saved_dsxg_legacy;
+       u16 saved_dsxg_elegacy;
 #endif
 };
 
index 762af68c8996fbb83a822638f76f2c29a7d63f9a..270790d384e20f06a38f369cf5aa9c980aac4ed6 100644 (file)
@@ -1132,15 +1132,4 @@ static struct i2c_driver onyx_driver = {
        .id_table = onyx_i2c_id,
 };
 
-static int __init onyx_init(void)
-{
-       return i2c_add_driver(&onyx_driver);
-}
-
-static void __exit onyx_exit(void)
-{
-       i2c_del_driver(&onyx_driver);
-}
-
-module_init(onyx_init);
-module_exit(onyx_exit);
+module_i2c_driver(onyx_driver);
index fd2188c3df2b4c436c9a6112cbc577c309d2898e..8e63d1f35ce1228cc1f6abb76cb39e1d421ea43a 100644 (file)
@@ -1026,15 +1026,4 @@ static struct i2c_driver tas_driver = {
        .id_table = tas_i2c_id,
 };
 
-static int __init tas_init(void)
-{
-       return i2c_add_driver(&tas_driver);
-}
-
-static void __exit tas_exit(void)
-{
-       i2c_del_driver(&tas_driver);
-}
-
-module_init(tas_init);
-module_exit(tas_exit);
+module_i2c_driver(tas_driver);
index 819a5c579a3975f129b2b35e67be0ad12d2780bd..2487a6bb1c545a3338aa19fbd244c952724f1058 100644 (file)
@@ -1313,7 +1313,7 @@ static int snd_ctl_tlv_ioctl(struct snd_ctl_file *file,
                        err = -EPERM;
                        goto __kctl_end;
                }
-               err = kctl->tlv.c(kctl, op_flag, tlv.length, _tlv->tlv); 
+               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);
index 3ac49b1b7cb82ac4c6c6632691efb9d3178cb09e..068cf08d3ffb82360e3e6ee4c24b78be3abde545 100644 (file)
@@ -480,74 +480,104 @@ int snd_card_free(struct snd_card *card)
 
 EXPORT_SYMBOL(snd_card_free);
 
-static void snd_card_set_id_no_lock(struct snd_card *card, const char *nid)
+/* retrieve the last word of shortname or longname */
+static const char *retrieve_id_from_card_name(const char *name)
 {
-       int i, len, idx_flag = 0, loops = SNDRV_CARDS;
-       const char *spos, *src;
-       char *id;
-       
-       if (nid == NULL) {
-               id = card->shortname;
-               spos = src = id;
-               while (*id != '\0') {
-                       if (*id == ' ')
-                               spos = id + 1;
-                       id++;
-               }
-       } else {
-               spos = src = nid;
+       const char *spos = name;
+
+       while (*name) {
+               if (isspace(*name) && isalnum(name[1]))
+                       spos = name + 1;
+               name++;
        }
-       id = card->id;
-       while (*spos != '\0' && !isalnum(*spos))
-               spos++;
-       if (isdigit(*spos))
-               *id++ = isalpha(src[0]) ? src[0] : 'D';
-       while (*spos != '\0' && (size_t)(id - card->id) < sizeof(card->id) - 1) {
-               if (isalnum(*spos))
-                       *id++ = *spos;
-               spos++;
+       return spos;
+}
+
+/* return true if the given id string doesn't conflict any other card ids */
+static bool card_id_ok(struct snd_card *card, const char *id)
+{
+       int i;
+       if (!snd_info_check_reserved_words(id))
+               return false;
+       for (i = 0; i < snd_ecards_limit; i++) {
+               if (snd_cards[i] && snd_cards[i] != card &&
+                   !strcmp(snd_cards[i]->id, id))
+                       return false;
        }
-       *id = '\0';
+       return true;
+}
 
-       id = card->id;
+/* copy to card->id only with valid letters from nid */
+static void copy_valid_id_string(struct snd_card *card, const char *src,
+                                const char *nid)
+{
+       char *id = card->id;
+
+       while (*nid && !isalnum(*nid))
+               nid++;
+       if (isdigit(*nid))
+               *id++ = isalpha(*src) ? *src : 'D';
+       while (*nid && (size_t)(id - card->id) < sizeof(card->id) - 1) {
+               if (isalnum(*nid))
+                       *id++ = *nid;
+               nid++;
+       }
+       *id = 0;
+}
+
+/* Set card->id from the given string
+ * If the string conflicts with other ids, add a suffix to make it unique.
+ */
+static void snd_card_set_id_no_lock(struct snd_card *card, const char *src,
+                                   const char *nid)
+{
+       int len, loops;
+       bool with_suffix;
+       bool is_default = false;
+       char *id;
        
-       if (*id == '\0')
+       copy_valid_id_string(card, src, nid);
+       id = card->id;
+
+ again:
+       /* use "Default" for obviously invalid strings
+        * ("card" conflicts with proc directories)
+        */
+       if (!*id || !strncmp(id, "card", 4)) {
                strcpy(id, "Default");
+               is_default = true;
+       }
 
-       while (1) {
-               if (loops-- == 0) {
-                       snd_printk(KERN_ERR "unable to set card id (%s)\n", 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;
+       with_suffix = false;
+       for (loops = 0; loops < SNDRV_CARDS; loops++) {
+               if (card_id_ok(card, id))
+                       return; /* OK */
 
-             __change:
                len = strlen(id);
-               if (idx_flag) {
-                       if (id[len-1] != '9')
-                               id[len-1]++;
-                       else
-                               id[len-1] = 'A';
-               } else if ((size_t)len <= sizeof(card->id) - 3) {
-                       strcat(id, "_1");
-                       idx_flag++;
+               if (!with_suffix) {
+                       /* add the "_X" suffix */
+                       char *spos = id + len;
+                       if (len >  sizeof(card->id) - 3)
+                               spos = id + sizeof(card->id) - 3;
+                       strcpy(spos, "_1");
+                       with_suffix = true;
                } else {
-                       spos = id + len - 2;
-                       if ((size_t)len <= sizeof(card->id) - 2)
-                               spos++;
-                       *(char *)spos++ = '_';
-                       *(char *)spos++ = '1';
-                       *(char *)spos++ = '\0';
-                       idx_flag++;
+                       /* modify the existing suffix */
+                       if (id[len - 1] != '9')
+                               id[len - 1]++;
+                       else
+                               id[len - 1] = 'A';
                }
        }
+       /* fallback to the default id */
+       if (!is_default) {
+               *id = 0;
+               goto again;
+       }
+       /* last resort... */
+       snd_printk(KERN_ERR "unable to set card id (%s)\n", id);
+       if (card->proc_root->name)
+               strcpy(card->id, card->proc_root->name);
 }
 
 /**
@@ -564,7 +594,7 @@ void snd_card_set_id(struct snd_card *card, const char *nid)
        if (card->id[0] != '\0')
                return;
        mutex_lock(&snd_card_mutex);
-       snd_card_set_id_no_lock(card, nid);
+       snd_card_set_id_no_lock(card, nid, nid);
        mutex_unlock(&snd_card_mutex);
 }
 EXPORT_SYMBOL(snd_card_set_id);
@@ -596,22 +626,12 @@ card_id_store_attr(struct device *dev, struct device_attribute *attr,
        memcpy(buf1, buf, copy);
        buf1[copy] = '\0';
        mutex_lock(&snd_card_mutex);
-       if (!snd_info_check_reserved_words(buf1)) {
-            __exist:
+       if (!card_id_ok(NULL, buf1)) {
                mutex_unlock(&snd_card_mutex);
                return -EEXIST;
        }
-       for (idx = 0; idx < snd_ecards_limit; idx++) {
-               if (snd_cards[idx] && !strcmp(snd_cards[idx]->id, buf1)) {
-                       if (card == snd_cards[idx])
-                               goto __ok;
-                       else
-                               goto __exist;
-               }
-       }
        strcpy(card->id, buf1);
        snd_info_card_id_change(card);
-__ok:
        mutex_unlock(&snd_card_mutex);
 
        return count;
@@ -665,7 +685,18 @@ int snd_card_register(struct snd_card *card)
                mutex_unlock(&snd_card_mutex);
                return 0;
        }
-       snd_card_set_id_no_lock(card, card->id[0] == '\0' ? NULL : card->id);
+       if (*card->id) {
+               /* make a unique id name from the given string */
+               char tmpid[sizeof(card->id)];
+               memcpy(tmpid, card->id, sizeof(card->id));
+               snd_card_set_id_no_lock(card, tmpid, tmpid);
+       } else {
+               /* create an id from either shortname or longname */
+               const char *src;
+               src = *card->shortname ? card->shortname : card->longname;
+               snd_card_set_id_no_lock(card, src,
+                                       retrieve_id_from_card_name(src));
+       }
        snd_cards[card->number] = card;
        mutex_unlock(&snd_card_mutex);
        init_info_for_card(card);
index 465f0ce772cb594dabe34e3d835c5766ec6d873b..768167925409ecb9be45b3c2ac82c885685da9bc 100644 (file)
@@ -72,7 +72,7 @@ void __snd_printk(unsigned int level, const char *path, int line,
        char verbose_fmt[] = KERN_DEFAULT "ALSA %s:%d %pV";
 #endif
 
-#ifdef CONFIG_SND_DEBUG        
+#ifdef CONFIG_SND_DEBUG
        if (debug < level)
                return;
 #endif
index 3420bd3da5d70ffec570e058d84b20f39aab49b8..4d18941178e6e933cb0f80f969c4d705c4777caf 100644 (file)
@@ -1029,7 +1029,8 @@ static int snd_interval_ratden(struct snd_interval *i,
  *
  * Returns non-zero if the value is changed, zero if not changed.
  */
-int snd_interval_list(struct snd_interval *i, unsigned int count, unsigned int *list, unsigned int mask)
+int snd_interval_list(struct snd_interval *i, unsigned int count,
+                     const unsigned int *list, unsigned int mask)
 {
         unsigned int k;
        struct snd_interval list_range;
index 25ed9fe41b89ceaf245278438c7113bc89ddc0f3..3fe99e644eb838a34913f8c6870742e67c227a31 100644 (file)
@@ -1586,12 +1586,18 @@ static int snd_pcm_link(struct snd_pcm_substream *substream, int fd)
        struct file *file;
        struct snd_pcm_file *pcm_file;
        struct snd_pcm_substream *substream1;
+       struct snd_pcm_group *group;
 
        file = snd_pcm_file_fd(fd);
        if (!file)
                return -EBADFD;
        pcm_file = file->private_data;
        substream1 = pcm_file->substream;
+       group = kmalloc(sizeof(*group), GFP_KERNEL);
+       if (!group) {
+               res = -ENOMEM;
+               goto _nolock;
+       }
        down_write(&snd_pcm_link_rwsem);
        write_lock_irq(&snd_pcm_link_rwlock);
        if (substream->runtime->status->state == SNDRV_PCM_STATE_OPEN ||
@@ -1604,11 +1610,7 @@ static int snd_pcm_link(struct snd_pcm_substream *substream, int fd)
                goto _end;
        }
        if (!snd_pcm_stream_linked(substream)) {
-               substream->group = kmalloc(sizeof(struct snd_pcm_group), GFP_ATOMIC);
-               if (substream->group == NULL) {
-                       res = -ENOMEM;
-                       goto _end;
-               }
+               substream->group = group;
                spin_lock_init(&substream->group->lock);
                INIT_LIST_HEAD(&substream->group->substreams);
                list_add_tail(&substream->link_list, &substream->group->substreams);
@@ -1620,7 +1622,10 @@ static int snd_pcm_link(struct snd_pcm_substream *substream, int fd)
  _end:
        write_unlock_irq(&snd_pcm_link_rwlock);
        up_write(&snd_pcm_link_rwsem);
+ _nolock:
        fput(file);
+       if (res < 0)
+               kfree(group);
        return res;
 }
 
index bb938153a964811b41f471f064fbd293fb660e77..466a5c8e8354f2c7eb3b08cda3130405968db759 100644 (file)
@@ -26,7 +26,7 @@
 #include <sound/mpu401.h>
 #include <sound/hwdep.h>
 #include <sound/ac97_codec.h>
-
+#include <sound/tlv.h>
 #endif
 
 #ifndef CHIP_AU8820
 #define NR_WTPB 0x20           /* WT channels per each bank. */
 #define NR_PCM 0x10
 
+struct pcm_vol {
+       struct snd_kcontrol *kctl;
+       int active;
+       int dma;
+       int mixin[4];
+       int vol[4];
+};
+
 /* Structs */
 typedef struct {
        //int this_08;          /* Still unknown */
@@ -168,6 +176,7 @@ struct snd_vortex {
        /* Xtalk canceler */
        int xt_mode;            /* 1: speakers, 0:headphones. */
 #endif
+       struct pcm_vol pcm_vol[NR_PCM];
 
        int isquad;             /* cache of extended ID codec flag. */
 
@@ -239,7 +248,7 @@ static int vortex_alsafmt_aspfmt(int alsafmt);
 /* Connection  stuff. */
 static void vortex_connect_default(vortex_t * vortex, int en);
 static int vortex_adb_allocroute(vortex_t * vortex, int dma, int nr_ch,
-                                int dir, int type);
+                                int dir, int type, int subdev);
 static char vortex_adb_checkinout(vortex_t * vortex, int resmap[], int out,
                                  int restype);
 #ifndef CHIP_AU8810
index 6933a27a5d76279e1c98b59953200dbd52b84e08..525f881f04096bd25cec390d0a2a26443a93aa0c 100644 (file)
@@ -2050,8 +2050,6 @@ vortex_adb_checkinout(vortex_t * vortex, int resmap[], int out, int restype)
 }
 
 /* Default Connections  */
-static int
-vortex_adb_allocroute(vortex_t * vortex, int dma, int nr_ch, int dir, int type);
 
 static void vortex_connect_default(vortex_t * vortex, int en)
 {
@@ -2111,15 +2109,13 @@ static void vortex_connect_default(vortex_t * vortex, int en)
   Return: Return allocated DMA or same DMA passed as "dma" when dma >= 0.
 */
 static int
-vortex_adb_allocroute(vortex_t * vortex, int dma, int nr_ch, int dir, int type)
+vortex_adb_allocroute(vortex_t *vortex, int dma, int nr_ch, int dir,
+                       int type, int subdev)
 {
        stream_t *stream;
        int i, en;
+       struct pcm_vol *p;
        
-       if ((nr_ch == 3)
-           || ((dir == SNDRV_PCM_STREAM_CAPTURE) && (nr_ch > 2)))
-               return -EBUSY;
-
        if (dma >= 0) {
                en = 0;
                vortex_adb_checkinout(vortex,
@@ -2250,6 +2246,14 @@ vortex_adb_allocroute(vortex_t * vortex, int dma, int nr_ch, int dir, int type)
                                                              MIX_DEFIGAIN);
 #endif
                        }
+                       if (stream->type == VORTEX_PCM_ADB && en) {
+                               p = &vortex->pcm_vol[subdev];
+                               p->dma = dma;
+                               for (i = 0; i < nr_ch; i++)
+                                       p->mixin[i] = mix[i];
+                               for (i = 0; i < ch_top; i++)
+                                       p->vol[i] = 0;
+                       }
                }
 #ifndef CHIP_AU8820
                else {
@@ -2473,7 +2477,7 @@ static irqreturn_t vortex_interrupt(int irq, void *dev_id)
                hwread(vortex->mmio, VORTEX_IRQ_STAT);
                handled = 1;
        }
-       if (source & IRQ_MIDI) {
+       if ((source & IRQ_MIDI) && vortex->rmidi) {
                snd_mpu401_uart_interrupt(vortex->irq,
                                          vortex->rmidi->private_data);
                handled = 1;
index 0ef2f97122080f206699a01e32b7f1f1ce7b6825..e59f120742a48e4140a54d80c63f6592c325c220 100644 (file)
@@ -122,6 +122,18 @@ static struct snd_pcm_hw_constraint_list hw_constraints_au8830_channels = {
        .mask = 0,
 };
 #endif
+
+static void vortex_notify_pcm_vol_change(struct snd_card *card,
+                       struct snd_kcontrol *kctl, int activate)
+{
+       if (activate)
+               kctl->vd[0].access &= ~SNDRV_CTL_ELEM_ACCESS_INACTIVE;
+       else
+               kctl->vd[0].access |= SNDRV_CTL_ELEM_ACCESS_INACTIVE;
+       snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_VALUE |
+                               SNDRV_CTL_EVENT_MASK_INFO, &(kctl->id));
+}
+
 /* open callback */
 static int snd_vortex_pcm_open(struct snd_pcm_substream *substream)
 {
@@ -230,12 +242,14 @@ snd_vortex_pcm_hw_params(struct snd_pcm_substream *substream,
                if (stream != NULL)
                        vortex_adb_allocroute(chip, stream->dma,
                                              stream->nr_ch, stream->dir,
-                                             stream->type);
+                                             stream->type,
+                                             substream->number);
                /* Alloc routes. */
                dma =
                    vortex_adb_allocroute(chip, -1,
                                          params_channels(hw_params),
-                                         substream->stream, type);
+                                         substream->stream, type,
+                                         substream->number);
                if (dma < 0) {
                        spin_unlock_irq(&chip->lock);
                        return dma;
@@ -246,6 +260,11 @@ snd_vortex_pcm_hw_params(struct snd_pcm_substream *substream,
                vortex_adbdma_setbuffers(chip, dma,
                                         params_period_bytes(hw_params),
                                         params_periods(hw_params));
+               if (VORTEX_PCM_TYPE(substream->pcm) == VORTEX_PCM_ADB) {
+                       chip->pcm_vol[substream->number].active = 1;
+                       vortex_notify_pcm_vol_change(chip->card,
+                               chip->pcm_vol[substream->number].kctl, 1);
+               }
        }
 #ifndef CHIP_AU8810
        else {
@@ -275,10 +294,18 @@ static int snd_vortex_pcm_hw_free(struct snd_pcm_substream *substream)
        spin_lock_irq(&chip->lock);
        // Delete audio routes.
        if (VORTEX_PCM_TYPE(substream->pcm) != VORTEX_PCM_WT) {
-               if (stream != NULL)
+               if (stream != NULL) {
+                       if (VORTEX_PCM_TYPE(substream->pcm) == VORTEX_PCM_ADB) {
+                               chip->pcm_vol[substream->number].active = 0;
+                               vortex_notify_pcm_vol_change(chip->card,
+                                       chip->pcm_vol[substream->number].kctl,
+                                       0);
+                       }
                        vortex_adb_allocroute(chip, stream->dma,
                                              stream->nr_ch, stream->dir,
-                                             stream->type);
+                                             stream->type,
+                                             substream->number);
+               }
        }
 #ifndef CHIP_AU8810
        else {
@@ -506,6 +533,83 @@ static struct snd_kcontrol_new snd_vortex_mixer_spdif[] __devinitdata = {
        },
 };
 
+/* subdevice PCM Volume control */
+
+static int snd_vortex_pcm_vol_info(struct snd_kcontrol *kcontrol,
+                               struct snd_ctl_elem_info *uinfo)
+{
+       vortex_t *vortex = snd_kcontrol_chip(kcontrol);
+       uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+       uinfo->count = (VORTEX_IS_QUAD(vortex) ? 4 : 2);
+       uinfo->value.integer.min = -128;
+       uinfo->value.integer.max = 32;
+       return 0;
+}
+
+static int snd_vortex_pcm_vol_get(struct snd_kcontrol *kcontrol,
+                               struct snd_ctl_elem_value *ucontrol)
+{
+       int i;
+       vortex_t *vortex = snd_kcontrol_chip(kcontrol);
+       int subdev = kcontrol->id.subdevice;
+       struct pcm_vol *p = &vortex->pcm_vol[subdev];
+       int max_chn = (VORTEX_IS_QUAD(vortex) ? 4 : 2);
+       for (i = 0; i < max_chn; i++)
+               ucontrol->value.integer.value[i] = p->vol[i];
+       return 0;
+}
+
+static int snd_vortex_pcm_vol_put(struct snd_kcontrol *kcontrol,
+                               struct snd_ctl_elem_value *ucontrol)
+{
+       int i;
+       int changed = 0;
+       int mixin;
+       unsigned char vol;
+       vortex_t *vortex = snd_kcontrol_chip(kcontrol);
+       int subdev = kcontrol->id.subdevice;
+       struct pcm_vol *p = &vortex->pcm_vol[subdev];
+       int max_chn = (VORTEX_IS_QUAD(vortex) ? 4 : 2);
+       for (i = 0; i < max_chn; i++) {
+               if (p->vol[i] != ucontrol->value.integer.value[i]) {
+                       p->vol[i] = ucontrol->value.integer.value[i];
+                       if (p->active) {
+                               switch (vortex->dma_adb[p->dma].nr_ch) {
+                               case 1:
+                                       mixin = p->mixin[0];
+                                       break;
+                               case 2:
+                               default:
+                                       mixin = p->mixin[(i < 2) ? i : (i - 2)];
+                                       break;
+                               case 4:
+                                       mixin = p->mixin[i];
+                                       break;
+                               };
+                               vol = p->vol[i];
+                               vortex_mix_setinputvolumebyte(vortex,
+                                       vortex->mixplayb[i], mixin, vol);
+                       }
+                       changed = 1;
+               }
+       }
+       return changed;
+}
+
+static const DECLARE_TLV_DB_MINMAX(vortex_pcm_vol_db_scale, -9600, 2400);
+
+static struct snd_kcontrol_new snd_vortex_pcm_vol __devinitdata = {
+       .iface = SNDRV_CTL_ELEM_IFACE_PCM,
+       .name = "PCM Playback Volume",
+       .access = SNDRV_CTL_ELEM_ACCESS_READWRITE |
+               SNDRV_CTL_ELEM_ACCESS_TLV_READ |
+               SNDRV_CTL_ELEM_ACCESS_INACTIVE,
+       .info = snd_vortex_pcm_vol_info,
+       .get = snd_vortex_pcm_vol_get,
+       .put = snd_vortex_pcm_vol_put,
+       .tlv = { .p = vortex_pcm_vol_db_scale },
+};
+
 /* create a pcm device */
 static int __devinit snd_vortex_new_pcm(vortex_t *chip, int idx, int nr)
 {
@@ -555,5 +659,20 @@ static int __devinit snd_vortex_new_pcm(vortex_t *chip, int idx, int nr)
                                return err;
                }
        }
+       if (VORTEX_PCM_TYPE(pcm) == VORTEX_PCM_ADB) {
+               for (i = 0; i < NR_PCM; i++) {
+                       chip->pcm_vol[i].active = 0;
+                       chip->pcm_vol[i].dma = -1;
+                       kctl = snd_ctl_new1(&snd_vortex_pcm_vol, chip);
+                       if (!kctl)
+                               return -ENOMEM;
+                       chip->pcm_vol[i].kctl = kctl;
+                       kctl->id.device = 0;
+                       kctl->id.subdevice = i;
+                       err = snd_ctl_add(chip->card, kctl);
+                       if (err < 0)
+                               return err;
+               }
+       }
        return 0;
 }
index b78f3fc3c33caabd26d49a5b41ed4bfda861d39d..6109490b83e83294373f36956a5a8a01dd923e20 100644 (file)
@@ -36,7 +36,7 @@ get_vm_block(struct ct_vm *vm, unsigned int size)
 
        size = CT_PAGE_ALIGN(size);
        if (size > vm->size) {
-               printk(KERN_ERR "ctxfi: Fail! No sufficient device virtural "
+               printk(KERN_ERR "ctxfi: Fail! No sufficient device virtual "
                                  "memory space available!\n");
                return NULL;
        }
index 92362973764dbbfebeab03085a669f36d2f21bf9..812d10e43ae0aa270517b9204be80a2c64200ff6 100644 (file)
@@ -1013,6 +1013,25 @@ static int set_rate_constraints(struct snd_ice1712 *ice,
                                          ice->hw_rates);
 }
 
+/* if the card has the internal rate locked (is_pro_locked), limit runtime
+   hw rates to the current internal rate only.
+*/
+static void constrain_rate_if_locked(struct snd_pcm_substream *substream)
+{
+       struct snd_ice1712 *ice = snd_pcm_substream_chip(substream);
+       struct snd_pcm_runtime *runtime = substream->runtime;
+       unsigned int rate;
+       if (is_pro_rate_locked(ice)) {
+               rate = ice->get_rate(ice);
+               if (rate >= runtime->hw.rate_min
+                   && rate <= runtime->hw.rate_max) {
+                       runtime->hw.rate_min = rate;
+                       runtime->hw.rate_max = rate;
+               }
+       }
+}
+
+
 /* multi-channel playback needs alignment 8x32bit regardless of the channels
  * actually used
  */
@@ -1046,6 +1065,7 @@ static int snd_vt1724_playback_pro_open(struct snd_pcm_substream *substream)
                                   VT1724_BUFFER_ALIGN);
        snd_pcm_hw_constraint_step(runtime, 0, SNDRV_PCM_HW_PARAM_BUFFER_BYTES,
                                   VT1724_BUFFER_ALIGN);
+       constrain_rate_if_locked(substream);
        if (ice->pro_open)
                ice->pro_open(ice, substream);
        return 0;
@@ -1066,6 +1086,7 @@ static int snd_vt1724_capture_pro_open(struct snd_pcm_substream *substream)
                                   VT1724_BUFFER_ALIGN);
        snd_pcm_hw_constraint_step(runtime, 0, SNDRV_PCM_HW_PARAM_BUFFER_BYTES,
                                   VT1724_BUFFER_ALIGN);
+       constrain_rate_if_locked(substream);
        if (ice->pro_open)
                ice->pro_open(ice, substream);
        return 0;
@@ -1215,6 +1236,7 @@ static int snd_vt1724_playback_spdif_open(struct snd_pcm_substream *substream)
                                   VT1724_BUFFER_ALIGN);
        snd_pcm_hw_constraint_step(runtime, 0, SNDRV_PCM_HW_PARAM_BUFFER_BYTES,
                                   VT1724_BUFFER_ALIGN);
+       constrain_rate_if_locked(substream);
        if (ice->spdif.ops.open)
                ice->spdif.ops.open(ice, substream);
        return 0;
@@ -1251,6 +1273,7 @@ static int snd_vt1724_capture_spdif_open(struct snd_pcm_substream *substream)
                                   VT1724_BUFFER_ALIGN);
        snd_pcm_hw_constraint_step(runtime, 0, SNDRV_PCM_HW_PARAM_BUFFER_BYTES,
                                   VT1724_BUFFER_ALIGN);
+       constrain_rate_if_locked(substream);
        if (ice->spdif.ops.open)
                ice->spdif.ops.open(ice, substream);
        return 0;
index 12a9a2b0338719c556446c2c716d5ff4a8742196..a8159b81e9c438ea42b63bb79e9bdcd65e521d8d 100644 (file)
@@ -2317,6 +2317,10 @@ int snd_ymfpci_suspend(struct pci_dev *pci, pm_message_t state)
        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);
+       pci_read_config_word(chip->pci, PCIR_DSXG_LEGACY,
+                            &chip->saved_dsxg_legacy);
+       pci_read_config_word(chip->pci, PCIR_DSXG_ELEGACY,
+                            &chip->saved_dsxg_elegacy);
        snd_ymfpci_writel(chip, YDSXGR_NATIVEDACOUTVOL, 0);
        snd_ymfpci_writel(chip, YDSXGR_BUF441OUTVOL, 0);
        snd_ymfpci_disable_dsp(chip);
@@ -2351,6 +2355,11 @@ int snd_ymfpci_resume(struct pci_dev *pci)
 
        snd_ac97_resume(chip->ac97);
 
+       pci_write_config_word(chip->pci, PCIR_DSXG_LEGACY,
+                             chip->saved_dsxg_legacy);
+       pci_write_config_word(chip->pci, PCIR_DSXG_ELEGACY,
+                             chip->saved_dsxg_elegacy);
+
        /* start hw again */
        if (chip->start_count > 0) {
                spin_lock_irq(&chip->reg_lock);
index 4dd051bdf4fd1365498cc0740173d95b3b4bdc60..c6500d00053b21314086f9d88f8dcd72e91e4cc7 100644 (file)
@@ -1112,17 +1112,7 @@ static struct spi_driver at73c213_driver = {
        .remove         = __devexit_p(snd_at73c213_remove),
 };
 
-static int __init at73c213_init(void)
-{
-       return spi_register_driver(&at73c213_driver);
-}
-module_init(at73c213_init);
-
-static void __exit at73c213_exit(void)
-{
-       spi_unregister_driver(&at73c213_driver);
-}
-module_exit(at73c213_exit);
+module_spi_driver(at73c213_driver);
 
 MODULE_AUTHOR("Hans-Christian Egtvedt <egtvedt@samfundet.no>");
 MODULE_DESCRIPTION("Sound driver for AT73C213 with Atmel SSC");
index 8af92e3e9c18f9557810f5b71a27ab27f3345c40..fc8cc823e4388ad15929d9b65f1fb065172fa2de 100644 (file)
@@ -5,7 +5,6 @@
  *
  * Author:     Torsten Schenk <torsten.schenk@zoho.com>
  * Created:    Jan 01, 2011
- * Version:    0.3.0
  * Copyright:  (C) Torsten Schenk
  *
  * This program is free software; you can redistribute it and/or modify
@@ -29,7 +28,7 @@
 #include <sound/initval.h>
 
 MODULE_AUTHOR("Torsten Schenk <torsten.schenk@zoho.com>");
-MODULE_DESCRIPTION("TerraTec DMX 6Fire USB audio driver, version 0.3.0");
+MODULE_DESCRIPTION("TerraTec DMX 6Fire USB audio driver");
 MODULE_LICENSE("GPL v2");
 MODULE_SUPPORTED_DEVICE("{{TerraTec, DMX 6Fire USB}}");
 
index d11e5cb520f02c388460f2b41834152456f21619..bde02d105a515381ee185d99de78b6138bc88fab 100644 (file)
@@ -3,7 +3,6 @@
  *
  * Author:     Torsten Schenk <torsten.schenk@zoho.com>
  * Created:    Jan 01, 2011
- * Version:    0.3.0
  * Copyright:  (C) Torsten Schenk
  *
  * This program is free software; you can redistribute it and/or modify
index c994daa57af2424a6f8fd8bef1f387ab19b017ac..6c3d531a250efdc265acaf6bced48ab82aa32d05 100644 (file)
@@ -5,7 +5,6 @@
  *
  * Author:     Torsten Schenk <torsten.schenk@zoho.com>
  * Created:    Jan 01, 2011
- * Version:    0.3.0
  * Copyright:  (C) Torsten Schenk
  *
  * This program is free software; you can redistribute it and/or modify
index edc5dc84b8881f6bad21d0add5cf88c11bf0e719..d2af0a5ddcf355995087c1e111a0b447b412b6a4 100644 (file)
@@ -3,7 +3,6 @@
  *
  * Author:     Torsten Schenk <torsten.schenk@zoho.com>
  * Created:    Jan 01, 2011
- * Version:    0.3.0
  * Copyright:  (C) Torsten Schenk
  *
  * This program is free software; you can redistribute it and/or modify
index 7dbeb4a378316ae650be1360be735aea03123834..b6eb03ed1c2c8eedd28f9ce6a99f235159ad5558 100644 (file)
@@ -3,7 +3,6 @@
  *
  * Author:     Torsten Schenk <torsten.schenk@zoho.com>
  * Created:    Jan 01, 2011
- * Version:    0.3.0
  * Copyright:  (C) Torsten Schenk
  *
  * This program is free software; you can redistribute it and/or modify
index ac828eff1a6347e080b74c78214b75a49fa9e663..07ed914d5e71a3c165287e88f2381fac98614726 100644 (file)
@@ -5,9 +5,12 @@
  *
  * Author:     Torsten Schenk <torsten.schenk@zoho.com>
  * Created:    Jan 01, 2011
- * Version:    0.3.0
  * Copyright:  (C) Torsten Schenk
  *
+ * Thanks to:
+ * - Holger Ruckdeschel: he found out how to control individual channel
+ *   volumes and introduced mute switch
+ *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
  * the Free Software Foundation; either version 2 of the License, or
@@ -16,6 +19,7 @@
 
 #include <linux/interrupt.h>
 #include <sound/control.h>
+#include <sound/tlv.h>
 
 #include "control.h"
 #include "comm.h"
 static char *opt_coax_texts[2] = { "Optical", "Coax" };
 static char *line_phono_texts[2] = { "Line", "Phono" };
 
-/*
- * calculated with $value\[i\] = 128 \cdot sqrt[3]{\frac{i}{128}}$
- * this is done because the linear values cause rapid degredation
- * of volume in the uppermost region.
- */
-static const u8 log_volume_table[128] = {
-       0x00, 0x19, 0x20, 0x24, 0x28, 0x2b, 0x2e, 0x30, 0x32, 0x34,
-       0x36, 0x38, 0x3a, 0x3b, 0x3d, 0x3e, 0x40, 0x41, 0x42, 0x43,
-       0x44, 0x46, 0x47, 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e,
-       0x4e, 0x4f, 0x50, 0x51, 0x52, 0x53, 0x53, 0x54, 0x55, 0x56,
-       0x56, 0x57, 0x58, 0x58, 0x59, 0x5a, 0x5b, 0x5b, 0x5c, 0x5c,
-       0x5d, 0x5e, 0x5e, 0x5f, 0x60, 0x60, 0x61, 0x61, 0x62, 0x62,
-       0x63, 0x63, 0x64, 0x65, 0x65, 0x66, 0x66, 0x67, 0x67, 0x68,
-       0x68, 0x69, 0x69, 0x6a, 0x6a, 0x6b, 0x6b, 0x6c, 0x6c, 0x6c,
-       0x6d, 0x6d, 0x6e, 0x6e, 0x6f, 0x6f, 0x70, 0x70, 0x70, 0x71,
-       0x71, 0x72, 0x72, 0x73, 0x73, 0x73, 0x74, 0x74, 0x75, 0x75,
-       0x75, 0x76, 0x76, 0x77, 0x77, 0x77, 0x78, 0x78, 0x78, 0x79,
-       0x79, 0x7a, 0x7a, 0x7a, 0x7b, 0x7b, 0x7b, 0x7c, 0x7c, 0x7c,
-       0x7d, 0x7d, 0x7d, 0x7e, 0x7e, 0x7e, 0x7f, 0x7f };
-
 /*
  * data that needs to be sent to device. sets up card internal stuff.
  * values dumped from windows driver and filtered by trial'n'error.
@@ -59,7 +43,7 @@ init_data[] = {
        { 0x22, 0x03, 0x00 }, { 0x20, 0x03, 0x08 }, { 0x22, 0x04, 0x00 },
        { 0x20, 0x04, 0x08 }, { 0x22, 0x05, 0x01 }, { 0x20, 0x05, 0x08 },
        { 0x22, 0x04, 0x01 }, { 0x12, 0x04, 0x00 }, { 0x12, 0x05, 0x00 },
-       { 0x12, 0x0d, 0x78 }, { 0x12, 0x21, 0x82 }, { 0x12, 0x22, 0x80 },
+       { 0x12, 0x0d, 0x38 }, { 0x12, 0x21, 0x82 }, { 0x12, 0x22, 0x80 },
        { 0x12, 0x23, 0x00 }, { 0x12, 0x06, 0x02 }, { 0x12, 0x03, 0x00 },
        { 0x12, 0x02, 0x00 }, { 0x22, 0x03, 0x01 },
        { 0 } /* TERMINATING ENTRY */
@@ -70,20 +54,47 @@ static const int rates_altsetting[] = { 1, 1, 2, 2, 3, 3 };
 static const u16 rates_6fire_vl[] = {0x00, 0x01, 0x00, 0x01, 0x00, 0x01};
 static const u16 rates_6fire_vh[] = {0x11, 0x11, 0x10, 0x10, 0x00, 0x00};
 
+static DECLARE_TLV_DB_MINMAX(tlv_output, -9000, 0);
+static DECLARE_TLV_DB_MINMAX(tlv_input, -1500, 1500);
+
 enum {
        DIGITAL_THRU_ONLY_SAMPLERATE = 3
 };
 
-static void usb6fire_control_master_vol_update(struct control_runtime *rt)
+static void usb6fire_control_output_vol_update(struct control_runtime *rt)
 {
        struct comm_runtime *comm_rt = rt->chip->comm;
-       if (comm_rt) {
-               /* set volume */
-               comm_rt->write8(comm_rt, 0x12, 0x0f, 0x7f -
-                               log_volume_table[rt->master_vol]);
-                /* unmute */
-               comm_rt->write8(comm_rt, 0x12, 0x0e, 0x00);
-       }
+       int i;
+
+       if (comm_rt)
+               for (i = 0; i < 6; i++)
+                       if (!(rt->ovol_updated & (1 << i))) {
+                               comm_rt->write8(comm_rt, 0x12, 0x0f + i,
+                                       180 - rt->output_vol[i]);
+                               rt->ovol_updated |= 1 << i;
+                       }
+}
+
+static void usb6fire_control_output_mute_update(struct control_runtime *rt)
+{
+       struct comm_runtime *comm_rt = rt->chip->comm;
+
+       if (comm_rt)
+               comm_rt->write8(comm_rt, 0x12, 0x0e, ~rt->output_mute);
+}
+
+static void usb6fire_control_input_vol_update(struct control_runtime *rt)
+{
+       struct comm_runtime *comm_rt = rt->chip->comm;
+       int i;
+
+       if (comm_rt)
+               for (i = 0; i < 2; i++)
+                       if (!(rt->ivol_updated & (1 << i))) {
+                               comm_rt->write8(comm_rt, 0x12, 0x1c + i,
+                                       rt->input_vol[i] & 0x3f);
+                               rt->ivol_updated |= 1 << i;
+                       }
 }
 
 static void usb6fire_control_line_phono_update(struct control_runtime *rt)
@@ -165,34 +176,147 @@ static int usb6fire_control_streaming_update(struct control_runtime *rt)
        return -EINVAL;
 }
 
-static int usb6fire_control_master_vol_info(struct snd_kcontrol *kcontrol,
+static int usb6fire_control_output_vol_info(struct snd_kcontrol *kcontrol,
                struct snd_ctl_elem_info *uinfo)
 {
        uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
-       uinfo->count = 1;
+       uinfo->count = 2;
        uinfo->value.integer.min = 0;
-       uinfo->value.integer.max = 127;
+       uinfo->value.integer.max = 180;
        return 0;
 }
 
-static int usb6fire_control_master_vol_put(struct snd_kcontrol *kcontrol,
+static int usb6fire_control_output_vol_put(struct snd_kcontrol *kcontrol,
                struct snd_ctl_elem_value *ucontrol)
 {
        struct control_runtime *rt = snd_kcontrol_chip(kcontrol);
+       unsigned int ch = kcontrol->private_value;
        int changed = 0;
-       if (rt->master_vol != ucontrol->value.integer.value[0]) {
-               rt->master_vol = ucontrol->value.integer.value[0];
-               usb6fire_control_master_vol_update(rt);
+
+       if (ch > 4) {
+               snd_printk(KERN_ERR PREFIX "Invalid channel in volume control.");
+               return -EINVAL;
+       }
+
+       if (rt->output_vol[ch] != ucontrol->value.integer.value[0]) {
+               rt->output_vol[ch] = ucontrol->value.integer.value[0];
+               rt->ovol_updated &= ~(1 << ch);
                changed = 1;
        }
+       if (rt->output_vol[ch + 1] != ucontrol->value.integer.value[1]) {
+               rt->output_vol[ch + 1] = ucontrol->value.integer.value[1];
+               rt->ovol_updated &= ~(2 << ch);
+               changed = 1;
+       }
+
+       if (changed)
+               usb6fire_control_output_vol_update(rt);
+
        return changed;
 }
 
-static int usb6fire_control_master_vol_get(struct snd_kcontrol *kcontrol,
+static int usb6fire_control_output_vol_get(struct snd_kcontrol *kcontrol,
                struct snd_ctl_elem_value *ucontrol)
 {
        struct control_runtime *rt = snd_kcontrol_chip(kcontrol);
-       ucontrol->value.integer.value[0] = rt->master_vol;
+       unsigned int ch = kcontrol->private_value;
+
+       if (ch > 4) {
+               snd_printk(KERN_ERR PREFIX "Invalid channel in volume control.");
+               return -EINVAL;
+       }
+
+       ucontrol->value.integer.value[0] = rt->output_vol[ch];
+       ucontrol->value.integer.value[1] = rt->output_vol[ch + 1];
+       return 0;
+}
+
+static int usb6fire_control_output_mute_put(struct snd_kcontrol *kcontrol,
+       struct snd_ctl_elem_value *ucontrol)
+{
+       struct control_runtime *rt = snd_kcontrol_chip(kcontrol);
+       unsigned int ch = kcontrol->private_value;
+       u8 old = rt->output_mute;
+       u8 value = 0;
+
+       if (ch > 4) {
+               snd_printk(KERN_ERR PREFIX "Invalid channel in volume control.");
+               return -EINVAL;
+       }
+
+       rt->output_mute &= ~(3 << ch);
+       if (ucontrol->value.integer.value[0])
+               value |= 1;
+       if (ucontrol->value.integer.value[1])
+               value |= 2;
+       rt->output_mute |= value << ch;
+
+       if (rt->output_mute != old)
+               usb6fire_control_output_mute_update(rt);
+
+       return rt->output_mute != old;
+}
+
+static int usb6fire_control_output_mute_get(struct snd_kcontrol *kcontrol,
+       struct snd_ctl_elem_value *ucontrol)
+{
+       struct control_runtime *rt = snd_kcontrol_chip(kcontrol);
+       unsigned int ch = kcontrol->private_value;
+       u8 value = rt->output_mute >> ch;
+
+       if (ch > 4) {
+               snd_printk(KERN_ERR PREFIX "Invalid channel in volume control.");
+               return -EINVAL;
+       }
+
+       ucontrol->value.integer.value[0] = 1 & value;
+       value >>= 1;
+       ucontrol->value.integer.value[1] = 1 & value;
+
+       return 0;
+}
+
+static int usb6fire_control_input_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 = 30;
+       return 0;
+}
+
+static int usb6fire_control_input_vol_put(struct snd_kcontrol *kcontrol,
+               struct snd_ctl_elem_value *ucontrol)
+{
+       struct control_runtime *rt = snd_kcontrol_chip(kcontrol);
+       int changed = 0;
+
+       if (rt->input_vol[0] != ucontrol->value.integer.value[0]) {
+               rt->input_vol[0] = ucontrol->value.integer.value[0] - 15;
+               rt->ivol_updated &= ~(1 << 0);
+               changed = 1;
+       }
+       if (rt->input_vol[1] != ucontrol->value.integer.value[1]) {
+               rt->input_vol[1] = ucontrol->value.integer.value[1] - 15;
+               rt->ivol_updated &= ~(1 << 1);
+               changed = 1;
+       }
+
+       if (changed)
+               usb6fire_control_input_vol_update(rt);
+
+       return changed;
+}
+
+static int usb6fire_control_input_vol_get(struct snd_kcontrol *kcontrol,
+               struct snd_ctl_elem_value *ucontrol)
+{
+       struct control_runtime *rt = snd_kcontrol_chip(kcontrol);
+
+       ucontrol->value.integer.value[0] = rt->input_vol[0] + 15;
+       ucontrol->value.integer.value[1] = rt->input_vol[1] + 15;
+
        return 0;
 }
 
@@ -287,16 +411,81 @@ static int usb6fire_control_digital_thru_get(struct snd_kcontrol *kcontrol,
        return 0;
 }
 
-static struct __devinitdata snd_kcontrol_new elements[] = {
+static struct __devinitdata snd_kcontrol_new vol_elements[] = {
        {
                .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
-               .name = "Master Playback Volume",
+               .name = "Analog Playback Volume",
                .index = 0,
+               .private_value = 0,
+               .access = SNDRV_CTL_ELEM_ACCESS_READWRITE |
+                       SNDRV_CTL_ELEM_ACCESS_TLV_READ,
+               .info = usb6fire_control_output_vol_info,
+               .get = usb6fire_control_output_vol_get,
+               .put = usb6fire_control_output_vol_put,
+               .tlv = { .p = tlv_output }
+       },
+       {
+               .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+               .name = "Analog Playback Volume",
+               .index = 1,
+               .private_value = 2,
+               .access = SNDRV_CTL_ELEM_ACCESS_READWRITE |
+                       SNDRV_CTL_ELEM_ACCESS_TLV_READ,
+               .info = usb6fire_control_output_vol_info,
+               .get = usb6fire_control_output_vol_get,
+               .put = usb6fire_control_output_vol_put,
+               .tlv = { .p = tlv_output }
+       },
+       {
+               .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+               .name = "Analog Playback Volume",
+               .index = 2,
+               .private_value = 4,
+               .access = SNDRV_CTL_ELEM_ACCESS_READWRITE |
+                       SNDRV_CTL_ELEM_ACCESS_TLV_READ,
+               .info = usb6fire_control_output_vol_info,
+               .get = usb6fire_control_output_vol_get,
+               .put = usb6fire_control_output_vol_put,
+               .tlv = { .p = tlv_output }
+       },
+       {}
+};
+
+static struct __devinitdata snd_kcontrol_new mute_elements[] = {
+       {
+               .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+               .name = "Analog Playback Switch",
+               .index = 0,
+               .private_value = 0,
+               .access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
+               .info = snd_ctl_boolean_stereo_info,
+               .get = usb6fire_control_output_mute_get,
+               .put = usb6fire_control_output_mute_put,
+       },
+       {
+               .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+               .name = "Analog Playback Switch",
+               .index = 1,
+               .private_value = 2,
                .access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
-               .info = usb6fire_control_master_vol_info,
-               .get = usb6fire_control_master_vol_get,
-               .put = usb6fire_control_master_vol_put
+               .info = snd_ctl_boolean_stereo_info,
+               .get = usb6fire_control_output_mute_get,
+               .put = usb6fire_control_output_mute_put,
        },
+       {
+               .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+               .name = "Analog Playback Switch",
+               .index = 2,
+               .private_value = 4,
+               .access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
+               .info = snd_ctl_boolean_stereo_info,
+               .get = usb6fire_control_output_mute_get,
+               .put = usb6fire_control_output_mute_put,
+       },
+       {}
+};
+
+static struct __devinitdata snd_kcontrol_new elements[] = {
        {
                .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
                .name = "Line/Phono Capture Route",
@@ -324,9 +513,54 @@ static struct __devinitdata snd_kcontrol_new elements[] = {
                .get = usb6fire_control_digital_thru_get,
                .put = usb6fire_control_digital_thru_put
        },
+       {
+               .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+               .name = "Analog Capture Volume",
+               .index = 0,
+               .access = SNDRV_CTL_ELEM_ACCESS_READWRITE |
+                       SNDRV_CTL_ELEM_ACCESS_TLV_READ,
+               .info = usb6fire_control_input_vol_info,
+               .get = usb6fire_control_input_vol_get,
+               .put = usb6fire_control_input_vol_put,
+               .tlv = { .p = tlv_input }
+       },
        {}
 };
 
+static int usb6fire_control_add_virtual(
+       struct control_runtime *rt,
+       struct snd_card *card,
+       char *name,
+       struct snd_kcontrol_new *elems)
+{
+       int ret;
+       int i;
+       struct snd_kcontrol *vmaster =
+               snd_ctl_make_virtual_master(name, tlv_output);
+       struct snd_kcontrol *control;
+
+       if (!vmaster)
+               return -ENOMEM;
+       ret = snd_ctl_add(card, vmaster);
+       if (ret < 0)
+               return ret;
+
+       i = 0;
+       while (elems[i].name) {
+               control = snd_ctl_new1(&elems[i], rt);
+               if (!control)
+                       return -ENOMEM;
+               ret = snd_ctl_add(card, control);
+               if (ret < 0)
+                       return ret;
+               ret = snd_ctl_add_slave(vmaster, control);
+               if (ret < 0)
+                       return ret;
+               i++;
+       }
+       return 0;
+}
+
 int __devinit usb6fire_control_init(struct sfire_chip *chip)
 {
        int i;
@@ -352,9 +586,26 @@ int __devinit usb6fire_control_init(struct sfire_chip *chip)
 
        usb6fire_control_opt_coax_update(rt);
        usb6fire_control_line_phono_update(rt);
-       usb6fire_control_master_vol_update(rt);
+       usb6fire_control_output_vol_update(rt);
+       usb6fire_control_output_mute_update(rt);
+       usb6fire_control_input_vol_update(rt);
        usb6fire_control_streaming_update(rt);
 
+       ret = usb6fire_control_add_virtual(rt, chip->card,
+               "Master Playback Volume", vol_elements);
+       if (ret) {
+               snd_printk(KERN_ERR PREFIX "cannot add control.\n");
+               kfree(rt);
+               return ret;
+       }
+       ret = usb6fire_control_add_virtual(rt, chip->card,
+               "Master Playback Switch", mute_elements);
+       if (ret) {
+               snd_printk(KERN_ERR PREFIX "cannot add control.\n");
+               kfree(rt);
+               return ret;
+       }
+
        i = 0;
        while (elements[i].name) {
                ret = snd_ctl_add(chip->card, snd_ctl_new1(&elements[i], rt));
index 8f5aeead2e3d931938f41e98deaf857b0194fa2c..9a596d95474a997afbf4c5e2bdaa5534c061fa19 100644 (file)
@@ -3,7 +3,6 @@
  *
  * Author:     Torsten Schenk <torsten.schenk@zoho.com>
  * Created:    Jan 01, 2011
- * Version:    0.3.0
  * Copyright:  (C) Torsten Schenk
  *
  * This program is free software; you can redistribute it and/or modify
@@ -44,7 +43,11 @@ struct control_runtime {
        bool line_phono_switch;
        bool digital_thru_switch;
        bool usb_streaming;
-       u8 master_vol;
+       u8 output_vol[6];
+       u8 ovol_updated;
+       u8 output_mute;
+       s8 input_vol[2];
+       u8 ivol_updated;
 };
 
 int __devinit usb6fire_control_init(struct sfire_chip *chip);
index 3b5f517a3972a0ff375d3c98a0ac750dd8e40fb8..6f9715ab32fe77cc3fd39de42410efe1c2aa1006 100644 (file)
@@ -5,7 +5,6 @@
  *
  * Author:     Torsten Schenk <torsten.schenk@zoho.com>
  * Created:    Jan 01, 2011
- * Version:    0.3.0
  * Copyright:  (C) Torsten Schenk
  *
  * This program is free software; you can redistribute it and/or modify
index 13f4509dce2b98939d4ba269fb8a47508b02666f..f0e5179b242bd48ce3c17314b96b3e27fdf5c85b 100644 (file)
@@ -5,7 +5,6 @@
  *
  * Author:     Torsten Schenk <torsten.schenk@zoho.com>
  * Created:    Jan 01, 2011
- * Version:    0.3.0
  * Copyright:  (C) Torsten Schenk
  *
  * This program is free software; you can redistribute it and/or modify
index 97a7bf669135024e2f7a30330907c3888de62357..5114eccc1d8ee972222ca57e8738cdfde450ec48 100644 (file)
@@ -3,7 +3,6 @@
  *
  * Author:     Torsten Schenk <torsten.schenk@zoho.com>
  * Created:    Jan 01, 2011
- * Version:    0.3.0
  * Copyright:  (C) Torsten Schenk
  *
  * This program is free software; you can redistribute it and/or modify
index d144cdb2f15909acefec2f0c45ea0cd1ff523030..c97d05f0e966e95b8fdc3d0778e509e360c127c1 100644 (file)
@@ -5,7 +5,6 @@
  *
  * Author:     Torsten Schenk <torsten.schenk@zoho.com>
  * Created:    Jan 01, 2011
- * Version:    0.3.0
  * Copyright:  (C) Torsten Schenk
  *
  * This program is free software; you can redistribute it and/or modify
index 2bee81374002f6e65eb532e797196dae6ac7712f..3104301b257df032a5047b3ea0dad5d4b7d43d3e 100644 (file)
@@ -3,7 +3,6 @@
  *
  * Author:     Torsten Schenk <torsten.schenk@zoho.com>
  * Created:    Jan 01, 2011
- * Version:    0.3.0
  * Copyright:  (C) Torsten Schenk
  *
  * This program is free software; you can redistribute it and/or modify
index 3efc21c3d67c610250a1d6fe3705f03e2a27c58a..ff77b28f3da167fe0dd0788bfc110b52964c4b6f 100644 (file)
@@ -106,6 +106,7 @@ config SND_USB_6FIRE
         select BITREVERSE
         select SND_RAWMIDI
         select SND_PCM
+        select SND_VMASTER
         help
           Say Y here to include support for TerraTec 6fire DMX USB interface.
 
index 0220b0f335b9ad230ebcb954ed78ab25731ca58e..0eed6115c2d444e166d770008238d5e11e4800fb 100644 (file)
@@ -695,6 +695,7 @@ static int snd_usb_pcm_check_knot(struct snd_pcm_runtime *runtime,
                                  struct snd_usb_substream *subs)
 {
        struct audioformat *fp;
+       int *rate_list;
        int count = 0, needs_knot = 0;
        int err;
 
@@ -708,7 +709,8 @@ static int snd_usb_pcm_check_knot(struct snd_pcm_runtime *runtime,
        if (!needs_knot)
                return 0;
 
-       subs->rate_list.list = kmalloc(sizeof(int) * count, GFP_KERNEL);
+       subs->rate_list.list = rate_list =
+               kmalloc(sizeof(int) * count, GFP_KERNEL);
        if (!subs->rate_list.list)
                return -ENOMEM;
        subs->rate_list.count = count;
@@ -717,7 +719,7 @@ static int snd_usb_pcm_check_knot(struct snd_pcm_runtime *runtime,
        list_for_each_entry(fp, &subs->fmt_list, list) {
                int i;
                for (i = 0; i < fp->nr_rates; i++)
-                       subs->rate_list.list[count++] = fp->rate_table[i];
+                       rate_list[count++] = fp->rate_table[i];
        }
        err = snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_RATE,
                                         &subs->rate_list);
index 6ffb3713b60cf812cec624c1c1050ff1be71e7e5..520ef96d7c75f14094c12ce4cc950e72c846c5d2 100644 (file)
@@ -80,7 +80,7 @@ static int usX2Y_urb_capt_retire(struct snd_usX2Y_substream *subs)
                cp = (unsigned char*)urb->transfer_buffer + urb->iso_frame_desc[i].offset;
                if (urb->iso_frame_desc[i].status) { /* active? hmm, skip this */
                        snd_printk(KERN_ERR "active frame status %i. "
-                                  "Most propably some hardware problem.\n",
+                                  "Most probably some hardware problem.\n",
                                   urb->iso_frame_desc[i].status);
                        return urb->iso_frame_desc[i].status;
                }
@@ -300,7 +300,7 @@ static void usX2Y_error_sequence(struct usX2Ydev *usX2Y,
 {
        snd_printk(KERN_ERR
 "Sequence Error!(hcd_frame=%i ep=%i%s;wait=%i,frame=%i).\n"
-"Most propably some urb of usb-frame %i is still missing.\n"
+"Most probably some urb of usb-frame %i is still missing.\n"
 "Cause could be too long delays in usb-hcd interrupt handling.\n",
                   usb_get_current_frame_number(usX2Y->dev),
                   subs->endpoint, usb_pipein(urb->pipe) ? "in" : "out",
index a51340f6f2db8d936321fa77ff7350955034fb30..8e40b6e67e9eeeb7ea8a9dd53dd4c73fced97b5c 100644 (file)
@@ -74,7 +74,7 @@ static int usX2Y_usbpcm_urb_capt_retire(struct snd_usX2Y_substream *subs)
        }
        for (i = 0; i < nr_of_packs(); i++) {
                if (urb->iso_frame_desc[i].status) { /* active? hmm, skip this */
-                       snd_printk(KERN_ERR "activ frame status %i. Most propably some hardware problem.\n", urb->iso_frame_desc[i].status);
+                       snd_printk(KERN_ERR "active frame status %i. Most probably some hardware problem.\n", urb->iso_frame_desc[i].status);
                        return urb->iso_frame_desc[i].status;
                }
                lens += urb->iso_frame_desc[i].actual_length / usX2Y->stride;