fedora core 6 1.2949 + vserver 2.2.0
[linux-2.6.git] / sound / pci / emu10k1 / emumixer.c
index 45692f5..c31f3d0 100644 (file)
@@ -3,6 +3,7 @@
  *                   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>
  *
  *  BUGS:
  *    --
 #include <sound/core.h>
 #include <sound/emu10k1.h>
 
-#define chip_t emu10k1_t
-
 #define AC97_ID_STAC9758       0x83847658
 
-static int snd_emu10k1_spdif_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo)
+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(snd_kcontrol_t * kcontrol,
-                                 snd_ctl_elem_value_t * ucontrol)
+static int snd_emu10k1_spdif_get(struct snd_kcontrol *kcontrol,
+                                 struct snd_ctl_elem_value *ucontrol)
 {
-       emu10k1_t *emu = snd_kcontrol_chip(kcontrol);
+       struct snd_emu10k1 *emu = snd_kcontrol_chip(kcontrol);
        unsigned int idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
        unsigned long flags;
 
@@ -56,23 +55,110 @@ static int snd_emu10k1_spdif_get(snd_kcontrol_t * kcontrol,
        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;
+       return 0;
 }
 
-static int snd_emu10k1_spdif_get_mask(snd_kcontrol_t * kcontrol,
-                                     snd_ctl_elem_value_t * ucontrol)
+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;
+       return 0;
 }
 
-static int snd_emu10k1_spdif_put(snd_kcontrol_t * kcontrol,
-                                 snd_ctl_elem_value_t * ucontrol)
+#if 0
+static int snd_audigy_spdif_output_rate_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
 {
-       emu10k1_t *emu = snd_kcontrol_chip(kcontrol);
+       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;
@@ -89,31 +175,31 @@ static int snd_emu10k1_spdif_put(snd_kcontrol_t * kcontrol,
                emu->spdif_bits[idx] = val;
        }
        spin_unlock_irqrestore(&emu->reg_lock, flags);
-        return change;
+       return change;
 }
 
-static snd_kcontrol_new_t snd_emu10k1_spdif_mask_control =
+static struct snd_kcontrol_new snd_emu10k1_spdif_mask_control =
 {
        .access =       SNDRV_CTL_ELEM_ACCESS_READ,
-        .iface =        SNDRV_CTL_ELEM_IFACE_MIXER,
-        .name =         SNDRV_CTL_NAME_IEC958("",PLAYBACK,MASK),
+       .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
+       .info =         snd_emu10k1_spdif_info,
+       .get =          snd_emu10k1_spdif_get_mask
 };
 
-static snd_kcontrol_new_t snd_emu10k1_spdif_control =
+static struct snd_kcontrol_new snd_emu10k1_spdif_control =
 {
-        .iface =       SNDRV_CTL_ELEM_IFACE_MIXER,
-        .name =         SNDRV_CTL_NAME_IEC958("",PLAYBACK,DEFAULT),
+       .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
+       .info =         snd_emu10k1_spdif_info,
+       .get =          snd_emu10k1_spdif_get,
+       .put =          snd_emu10k1_spdif_put
 };
 
 
-static void update_emu10k1_fxrt(emu10k1_t *emu, int voice, unsigned char *route)
+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,
@@ -126,7 +212,7 @@ static void update_emu10k1_fxrt(emu10k1_t *emu, int voice, unsigned char *route)
        }
 }
 
-static void update_emu10k1_send_volume(emu10k1_t *emu, int voice, unsigned char *volume)
+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]);
@@ -141,9 +227,11 @@ static void update_emu10k1_send_volume(emu10k1_t *emu, int voice, unsigned char
        }
 }
 
-static int snd_emu10k1_send_routing_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo)
+/* PCM stream controls */
+
+static int snd_emu10k1_send_routing_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
 {
-       emu10k1_t *emu = snd_kcontrol_chip(kcontrol);
+       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;
@@ -151,12 +239,13 @@ static int snd_emu10k1_send_routing_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_
        return 0;
 }
 
-static int snd_emu10k1_send_routing_get(snd_kcontrol_t * kcontrol,
-                                        snd_ctl_elem_value_t * ucontrol)
+static int snd_emu10k1_send_routing_get(struct snd_kcontrol *kcontrol,
+                                        struct snd_ctl_elem_value *ucontrol)
 {
        unsigned long flags;
-       emu10k1_t *emu = snd_kcontrol_chip(kcontrol);
-       emu10k1_pcm_mixer_t *mix = &emu->pcm_mixer[snd_ctl_get_ioffidx(kcontrol, &ucontrol->id)];
+       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;
@@ -167,15 +256,16 @@ static int snd_emu10k1_send_routing_get(snd_kcontrol_t * kcontrol,
                        ucontrol->value.integer.value[(voice * num_efx) + idx] = 
                                mix->send_routing[voice][idx] & mask;
        spin_unlock_irqrestore(&emu->reg_lock, flags);
-        return 0;
+       return 0;
 }
 
-static int snd_emu10k1_send_routing_put(snd_kcontrol_t * kcontrol,
-                                        snd_ctl_elem_value_t * ucontrol)
+static int snd_emu10k1_send_routing_put(struct snd_kcontrol *kcontrol,
+                                        struct snd_ctl_elem_value *ucontrol)
 {
        unsigned long flags;
-       emu10k1_t *emu = snd_kcontrol_chip(kcontrol);
-       emu10k1_pcm_mixer_t *mix = &emu->pcm_mixer[snd_ctl_get_ioffidx(kcontrol, &ucontrol->id)];
+       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;
@@ -201,23 +291,23 @@ static int snd_emu10k1_send_routing_put(snd_kcontrol_t * kcontrol,
                }
        }
        spin_unlock_irqrestore(&emu->reg_lock, flags);
-        return change;
+       return change;
 }
 
-static snd_kcontrol_new_t snd_emu10k1_send_routing_control =
+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_MIXER,
-        .name =         "EMU10K1 PCM Send Routing",
+       .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
+       .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(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo)
+static int snd_emu10k1_send_volume_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
 {
-       emu10k1_t *emu = snd_kcontrol_chip(kcontrol);
+       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;
@@ -225,12 +315,13 @@ static int snd_emu10k1_send_volume_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_i
        return 0;
 }
 
-static int snd_emu10k1_send_volume_get(snd_kcontrol_t * kcontrol,
-                                       snd_ctl_elem_value_t * ucontrol)
+static int snd_emu10k1_send_volume_get(struct snd_kcontrol *kcontrol,
+                                       struct snd_ctl_elem_value *ucontrol)
 {
        unsigned long flags;
-       emu10k1_t *emu = snd_kcontrol_chip(kcontrol);
-       emu10k1_pcm_mixer_t *mix = &emu->pcm_mixer[snd_ctl_get_ioffidx(kcontrol, &ucontrol->id)];
+       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;
 
@@ -238,15 +329,16 @@ static int snd_emu10k1_send_volume_get(snd_kcontrol_t * kcontrol,
        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;
+       return 0;
 }
 
-static int snd_emu10k1_send_volume_put(snd_kcontrol_t * kcontrol,
-                                       snd_ctl_elem_value_t * ucontrol)
+static int snd_emu10k1_send_volume_put(struct snd_kcontrol *kcontrol,
+                                       struct snd_ctl_elem_value *ucontrol)
 {
        unsigned long flags;
-       emu10k1_t *emu = snd_kcontrol_chip(kcontrol);
-       emu10k1_pcm_mixer_t *mix = &emu->pcm_mixer[snd_ctl_get_ioffidx(kcontrol, &ucontrol->id)];
+       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;
 
@@ -270,21 +362,21 @@ static int snd_emu10k1_send_volume_put(snd_kcontrol_t * kcontrol,
                }
        }
        spin_unlock_irqrestore(&emu->reg_lock, flags);
-        return change;
+       return change;
 }
 
-static snd_kcontrol_new_t snd_emu10k1_send_volume_control =
+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_MIXER,
-        .name =         "EMU10K1 PCM Send Volume",
+       .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
+       .info =         snd_emu10k1_send_volume_info,
+       .get =          snd_emu10k1_send_volume_get,
+       .put =          snd_emu10k1_send_volume_put
 };
 
-static int snd_emu10k1_attn_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo)
+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;
@@ -293,11 +385,12 @@ static int snd_emu10k1_attn_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t *
        return 0;
 }
 
-static int snd_emu10k1_attn_get(snd_kcontrol_t * kcontrol,
-                                snd_ctl_elem_value_t * ucontrol)
+static int snd_emu10k1_attn_get(struct snd_kcontrol *kcontrol,
+                                struct snd_ctl_elem_value *ucontrol)
 {
-       emu10k1_t *emu = snd_kcontrol_chip(kcontrol);
-       emu10k1_pcm_mixer_t *mix = &emu->pcm_mixer[snd_ctl_get_ioffidx(kcontrol, &ucontrol->id)];
+       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;
 
@@ -305,15 +398,16 @@ static int snd_emu10k1_attn_get(snd_kcontrol_t * kcontrol,
        for (idx = 0; idx < 3; idx++)
                ucontrol->value.integer.value[idx] = mix->attn[idx];
        spin_unlock_irqrestore(&emu->reg_lock, flags);
-        return 0;
+       return 0;
 }
 
-static int snd_emu10k1_attn_put(snd_kcontrol_t * kcontrol,
-                               snd_ctl_elem_value_t * ucontrol)
+static int snd_emu10k1_attn_put(struct snd_kcontrol *kcontrol,
+                               struct snd_ctl_elem_value *ucontrol)
 {
        unsigned long flags;
-       emu10k1_t *emu = snd_kcontrol_chip(kcontrol);
-       emu10k1_pcm_mixer_t *mix = &emu->pcm_mixer[snd_ctl_get_ioffidx(kcontrol, &ucontrol->id)];
+       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);
@@ -333,21 +427,218 @@ static int snd_emu10k1_attn_put(snd_kcontrol_t * kcontrol,
                }
        }
        spin_unlock_irqrestore(&emu->reg_lock, flags);
-        return change;
+       return change;
 }
 
-static snd_kcontrol_new_t snd_emu10k1_attn_control =
+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_MIXER,
-        .name =         "EMU10K1 PCM Volume",
+       .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
+       .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(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo)
+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;
@@ -356,23 +647,23 @@ static int snd_emu10k1_shared_spdif_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_
        return 0;
 }
 
-static int snd_emu10k1_shared_spdif_get(snd_kcontrol_t * kcontrol,
-                                       snd_ctl_elem_value_t * ucontrol)
+static int snd_emu10k1_shared_spdif_get(struct snd_kcontrol *kcontrol,
+                                       struct snd_ctl_elem_value *ucontrol)
 {
-       emu10k1_t *emu = snd_kcontrol_chip(kcontrol);
+       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;
+       return 0;
 }
 
-static int snd_emu10k1_shared_spdif_put(snd_kcontrol_t * kcontrol,
-                                       snd_ctl_elem_value_t * ucontrol)
+static int snd_emu10k1_shared_spdif_put(struct snd_kcontrol *kcontrol,
+                                       struct snd_ctl_elem_value *ucontrol)
 {
        unsigned long flags;
-       emu10k1_t *emu = snd_kcontrol_chip(kcontrol);
+       struct snd_emu10k1 *emu = snd_kcontrol_chip(kcontrol);
        unsigned int reg, val;
        int change = 0;
 
@@ -396,10 +687,10 @@ static int snd_emu10k1_shared_spdif_put(snd_kcontrol_t * kcontrol,
                outl(reg | val, emu->port + HCFG);
        }
        spin_unlock_irqrestore(&emu->reg_lock, flags);
-        return change;
+       return change;
 }
 
-static snd_kcontrol_new_t snd_emu10k1_shared_spdif __devinitdata =
+static struct snd_kcontrol_new snd_emu10k1_shared_spdif __devinitdata =
 {
        .iface =        SNDRV_CTL_ELEM_IFACE_MIXER,
        .name =         "SB Live Analog/Digital Output Jack",
@@ -408,7 +699,7 @@ static snd_kcontrol_new_t snd_emu10k1_shared_spdif __devinitdata =
        .put =          snd_emu10k1_shared_spdif_put
 };
 
-static snd_kcontrol_new_t snd_audigy_shared_spdif __devinitdata =
+static struct snd_kcontrol_new snd_audigy_shared_spdif __devinitdata =
 {
        .iface =        SNDRV_CTL_ELEM_IFACE_MIXER,
        .name =         "Audigy Analog/Digital Output Jack",
@@ -419,35 +710,35 @@ static snd_kcontrol_new_t snd_audigy_shared_spdif __devinitdata =
 
 /*
  */
-static void snd_emu10k1_mixer_free_ac97(ac97_t *ac97)
+static void snd_emu10k1_mixer_free_ac97(struct snd_ac97 *ac97)
 {
-       emu10k1_t *emu = snd_magic_cast(emu10k1_t, ac97->private_data, return);
+       struct snd_emu10k1 *emu = ac97->private_data;
        emu->ac97 = NULL;
 }
 
 /*
  */
-static int remove_ctl(snd_card_t *card, const char *name)
+static int remove_ctl(struct snd_card *card, const char *name)
 {
-       snd_ctl_elem_id_t id;
+       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 snd_kcontrol_t *ctl_find(snd_card_t *card, const char *name)
+static struct snd_kcontrol *ctl_find(struct snd_card *card, const char *name)
 {
-       snd_ctl_elem_id_t sid;
+       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(snd_card_t *card, const char *src, const char *dst)
+static int rename_ctl(struct snd_card *card, const char *src, const char *dst)
 {
-       snd_kcontrol_t *kctl = ctl_find(card, src);
+       struct snd_kcontrol *kctl = ctl_find(card, src);
        if (kctl) {
                strcpy(kctl->id.name, dst);
                return 0;
@@ -455,11 +746,12 @@ static int rename_ctl(snd_card_t *card, const char *src, const char *dst)
        return -ENOENT;
 }
 
-int __devinit snd_emu10k1_mixer(emu10k1_t *emu)
+int __devinit snd_emu10k1_mixer(struct snd_emu10k1 *emu,
+                               int pcm_device, int multi_device)
 {
        int err, pcm;
-       snd_kcontrol_t *kctl;
-       snd_card_t *card = emu->card;
+       struct snd_kcontrol *kctl;
+       struct snd_card *card = emu->card;
        char **c;
        static char *emu10k1_remove_ctls[] = {
                /* no AC97 mono, surround, center/lfe */
@@ -467,6 +759,8 @@ int __devinit snd_emu10k1_mixer(emu10k1_t *emu)
                "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",
@@ -483,6 +777,8 @@ int __devinit snd_emu10k1_mixer(emu10k1_t *emu)
        };
        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",
@@ -510,28 +806,81 @@ int __devinit snd_emu10k1_mixer(emu10k1_t *emu)
                "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->no_ac97) {
-               ac97_bus_t bus, *pbus;
-               ac97_t ac97;
+       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,
+               };
 
-               memset(&bus, 0, sizeof(bus));
-               bus.write = snd_emu10k1_ac97_write;
-               bus.read = snd_emu10k1_ac97_read;
-               if ((err = snd_ac97_bus(emu->card, &bus, &pbus)) < 0)
+               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;
-               if ((err = snd_ac97_mixer(pbus, &ac97, &emu->ac97)) < 0)
-                       return err;
+               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(emu->ac97, AC97_MASTER, 0x0202);
+                       snd_ac97_write_cache(emu->ac97, AC97_MASTER, 0x0000);
                        /* set capture source to mic */
-                       snd_ac97_write(emu->ac97, AC97_REC_SEL, 0x0000);
-                       c = audigy_remove_ctls;
+                       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:
@@ -543,14 +892,15 @@ int __devinit snd_emu10k1_mixer(emu10k1_t *emu)
                                snd_emu10k1_ptr_write(emu, AC97SLOT, 0, AC97SLOT_CNTR|AC97SLOT_LFE|AC97SLOT_REAR_LEFT|AC97SLOT_REAR_RIGHT);
                        }
                        /* remove unused AC97 controls */
-                       snd_ac97_write(emu->ac97, AC97_SURROUND_MASTER, 0x0202);
-                       snd_ac97_write(emu->ac97, AC97_CENTER_LFE_MASTER, 0x0202);
+                       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 {
-               if (emu->APS)
+       no_ac97:
+               if (emu->card_capabilities->ecard)
                        strcpy(emu->card->mixername, "EMU APS");
                else if (emu->audigy)
                        strcpy(emu->card->mixername, "SB Audigy");
@@ -559,28 +909,63 @@ int __devinit snd_emu10k1_mixer(emu10k1_t *emu)
        }
 
        if (emu->audigy)
-               c = audigy_rename_ctls;
+               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;
 
-       /* intiailize the routing and volume table for each pcm playback stream */
+       /* initialize the routing and volume table for each pcm playback stream */
        for (pcm = 0; pcm < 32; pcm++) {
-               emu10k1_pcm_mixer_t *mix;
+               struct snd_emu10k1_pcm_mixer *mix;
                int v;
                
                mix = &emu->pcm_mixer[pcm];
@@ -598,35 +983,68 @@ int __devinit snd_emu10k1_mixer(emu10k1_t *emu)
                mix->attn[0] = mix->attn[1] = mix->attn[2] = 0xffff;
        }
        
-       if (! emu->APS) { /* FIXME: APS has these controls? */
+       /* 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 = ctl_find(card, SNDRV_CTL_NAME_IEC958("",PLAYBACK,DEFAULT))) != NULL) {
-                       /* already defined by ac97, remove it */
-                       /* FIXME: or do we need both controls? */
-                       remove_ctl(card, SNDRV_CTL_NAME_IEC958("",PLAYBACK,DEFAULT));
-               }
                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->audigy) {
+       if ( emu->card_capabilities->emu1212m) {
+               ;  /* 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;
-       } else if (! emu->APS) {
+#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;
+       }
+               
        return 0;
 }