[ALSA] emu10k1 - EMU 1212 with 16 capture channels
[powerpc.git] / sound / pci / emu10k1 / emumixer.c
index a118fee..7b2c1dc 100644 (file)
 #include <sound/core.h>
 #include <sound/emu10k1.h>
 #include <linux/delay.h>
+#include <sound/tlv.h>
+
+#include "p17v.h"
 
 #define AC97_ID_STAC9758       0x83847658
 
+static const DECLARE_TLV_DB_SCALE(snd_audigy_db_scale2, -10350, 50, 1); /* WM8775 gain scale */
+
 static int snd_emu10k1_spdif_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
 {
        uinfo->type = SNDRV_CTL_ELEM_TYPE_IEC958;
@@ -72,6 +77,10 @@ static int snd_emu10k1_spdif_get_mask(struct snd_kcontrol *kcontrol,
        return 0;
 }
 
+/*
+ * Items labels in enum mixer controls assigning source data to
+ * each destination
+ */
 static char *emu1010_src_texts[] = { 
        "Silence",
        "Dock Mic A",
@@ -128,6 +137,9 @@ static char *emu1010_src_texts[] = {
        "DSP 31",
 };
 
+/*
+ * List of data sources available for each destination
+ */
 static unsigned int emu1010_src_regs[] = {
        EMU_SRC_SILENCE,/* 0 */
        EMU_SRC_DOCK_MIC_A1, /* 1 */
@@ -184,6 +196,10 @@ static unsigned int emu1010_src_regs[] = {
        EMU_SRC_ALICE_EMU32B+0xf, /* 52 */
 };
 
+/*
+ * Data destinations - physical EMU outputs.
+ * Each destination has an enum mixer control to choose a data source
+ */
 static unsigned int emu1010_output_dst[] = {
        EMU_DST_DOCK_DAC1_LEFT1, /* 0 */
        EMU_DST_DOCK_DAC1_RIGHT1, /* 1 */
@@ -211,6 +227,11 @@ static unsigned int emu1010_output_dst[] = {
        EMU_DST_HANA_ADAT+7, /* 23 */
 };
 
+/*
+ * Data destinations - HANA outputs going to Alice2 (audigy) for
+ *   capture (EMU32 + I2S links)
+ * Each destination has an enum mixer control to choose a data source
+ */
 static unsigned int emu1010_input_dst[] = {
        EMU_DST_ALICE2_EMU32_0,
        EMU_DST_ALICE2_EMU32_1,
@@ -316,30 +337,30 @@ static int snd_emu1010_input_source_put(struct snd_kcontrol *kcontrol,
 }
 
 static struct snd_kcontrol_new snd_emu1010_output_enum_ctls[] __devinitdata = {
-       EMU1010_SOURCE_OUTPUT("Dock DAC1 Left Playback Switch", 0),
-       EMU1010_SOURCE_OUTPUT("Dock DAC1 Right Playback Switch", 1),
-       EMU1010_SOURCE_OUTPUT("Dock DAC2 Left Playback Switch", 2),
-       EMU1010_SOURCE_OUTPUT("Dock DAC2 Right Playback Switch", 3),
-       EMU1010_SOURCE_OUTPUT("Dock DAC3 Left Playback Switch", 4),
-       EMU1010_SOURCE_OUTPUT("Dock DAC3 Right Playback Switch", 5),
-       EMU1010_SOURCE_OUTPUT("Dock DAC4 Left Playback Switch", 6),
-       EMU1010_SOURCE_OUTPUT("Dock DAC4 Right Playback Switch", 7),
-       EMU1010_SOURCE_OUTPUT("Dock Phones Left Playback Switch", 8),
-       EMU1010_SOURCE_OUTPUT("Dock Phones Right Playback Switch", 9),
-       EMU1010_SOURCE_OUTPUT("Dock SPDIF Left Playback Switch", 0xa),
-       EMU1010_SOURCE_OUTPUT("Dock SPDIF Right Playback Switch", 0xb),
-       EMU1010_SOURCE_OUTPUT("1010 SPDIF Left Playback Switch", 0xc),
-       EMU1010_SOURCE_OUTPUT("1010 SPDIF Right Playback Switch", 0xd),
-       EMU1010_SOURCE_OUTPUT("0202 DAC Left Playback Switch", 0xe),
-       EMU1010_SOURCE_OUTPUT("0202 DAC Right Playback Switch", 0xf),
-       EMU1010_SOURCE_OUTPUT("1010 ADAT 0 Playback Switch", 0x10),
-       EMU1010_SOURCE_OUTPUT("1010 ADAT 1 Playback Switch", 0x11),
-       EMU1010_SOURCE_OUTPUT("1010 ADAT 2 Playback Switch", 0x12),
-       EMU1010_SOURCE_OUTPUT("1010 ADAT 3 Playback Switch", 0x13),
-       EMU1010_SOURCE_OUTPUT("1010 ADAT 4 Playback Switch", 0x14),
-       EMU1010_SOURCE_OUTPUT("1010 ADAT 5 Playback Switch", 0x15),
-       EMU1010_SOURCE_OUTPUT("1010 ADAT 6 Playback Switch", 0x16),
-       EMU1010_SOURCE_OUTPUT("1010 ADAT 7 Playback Switch", 0x17),
+       EMU1010_SOURCE_OUTPUT("Dock DAC1 Left Playback Enum", 0),
+       EMU1010_SOURCE_OUTPUT("Dock DAC1 Right Playback Enum", 1),
+       EMU1010_SOURCE_OUTPUT("Dock DAC2 Left Playback Enum", 2),
+       EMU1010_SOURCE_OUTPUT("Dock DAC2 Right Playback Enum", 3),
+       EMU1010_SOURCE_OUTPUT("Dock DAC3 Left Playback Enum", 4),
+       EMU1010_SOURCE_OUTPUT("Dock DAC3 Right Playback Enum", 5),
+       EMU1010_SOURCE_OUTPUT("Dock DAC4 Left Playback Enum", 6),
+       EMU1010_SOURCE_OUTPUT("Dock DAC4 Right Playback Enum", 7),
+       EMU1010_SOURCE_OUTPUT("Dock Phones Left Playback Enum", 8),
+       EMU1010_SOURCE_OUTPUT("Dock Phones Right Playback Enum", 9),
+       EMU1010_SOURCE_OUTPUT("Dock SPDIF Left Playback Enum", 0xa),
+       EMU1010_SOURCE_OUTPUT("Dock SPDIF Right Playback Enum", 0xb),
+       EMU1010_SOURCE_OUTPUT("1010 SPDIF Left Playback Enum", 0xc),
+       EMU1010_SOURCE_OUTPUT("1010 SPDIF Right Playback Enum", 0xd),
+       EMU1010_SOURCE_OUTPUT("0202 DAC Left Playback Enum", 0xe),
+       EMU1010_SOURCE_OUTPUT("0202 DAC Right Playback Enum", 0xf),
+       EMU1010_SOURCE_OUTPUT("1010 ADAT 0 Playback Enum", 0x10),
+       EMU1010_SOURCE_OUTPUT("1010 ADAT 1 Playback Enum", 0x11),
+       EMU1010_SOURCE_OUTPUT("1010 ADAT 2 Playback Enum", 0x12),
+       EMU1010_SOURCE_OUTPUT("1010 ADAT 3 Playback Enum", 0x13),
+       EMU1010_SOURCE_OUTPUT("1010 ADAT 4 Playback Enum", 0x14),
+       EMU1010_SOURCE_OUTPUT("1010 ADAT 5 Playback Enum", 0x15),
+       EMU1010_SOURCE_OUTPUT("1010 ADAT 6 Playback Enum", 0x16),
+       EMU1010_SOURCE_OUTPUT("1010 ADAT 7 Playback Enum", 0x17),
 };
 
 #define EMU1010_SOURCE_INPUT(xname,chid) \
@@ -353,28 +374,28 @@ static struct snd_kcontrol_new snd_emu1010_output_enum_ctls[] __devinitdata = {
 }
 
 static struct snd_kcontrol_new snd_emu1010_input_enum_ctls[] __devinitdata = {
-       EMU1010_SOURCE_INPUT("DSP 0 Capture Switch", 0),
-       EMU1010_SOURCE_INPUT("DSP 1 Capture Switch", 1),
-       EMU1010_SOURCE_INPUT("DSP 2 Capture Switch", 2),
-       EMU1010_SOURCE_INPUT("DSP 3 Capture Switch", 3),
-       EMU1010_SOURCE_INPUT("DSP 4 Capture Switch", 4),
-       EMU1010_SOURCE_INPUT("DSP 5 Capture Switch", 5),
-       EMU1010_SOURCE_INPUT("DSP 6 Capture Switch", 6),
-       EMU1010_SOURCE_INPUT("DSP 7 Capture Switch", 7),
-       EMU1010_SOURCE_INPUT("DSP 8 Capture Switch", 8),
-       EMU1010_SOURCE_INPUT("DSP 9 Capture Switch", 9),
-       EMU1010_SOURCE_INPUT("DSP A Capture Switch", 0xa),
-       EMU1010_SOURCE_INPUT("DSP B Capture Switch", 0xb),
-       EMU1010_SOURCE_INPUT("DSP C Capture Switch", 0xc),
-       EMU1010_SOURCE_INPUT("DSP D Capture Switch", 0xd),
-       EMU1010_SOURCE_INPUT("DSP E Capture Switch", 0xe),
-       EMU1010_SOURCE_INPUT("DSP F Capture Switch", 0xf),
-       EMU1010_SOURCE_INPUT("DSP 10 Capture Switch", 0x10),
-       EMU1010_SOURCE_INPUT("DSP 11 Capture Switch", 0x11),
-       EMU1010_SOURCE_INPUT("DSP 12 Capture Switch", 0x12),
-       EMU1010_SOURCE_INPUT("DSP 13 Capture Switch", 0x13),
-       EMU1010_SOURCE_INPUT("DSP 14 Capture Switch", 0x14),
-       EMU1010_SOURCE_INPUT("DSP 15 Capture Switch", 0x15),
+       EMU1010_SOURCE_INPUT("DSP 0 Capture Enum", 0),
+       EMU1010_SOURCE_INPUT("DSP 1 Capture Enum", 1),
+       EMU1010_SOURCE_INPUT("DSP 2 Capture Enum", 2),
+       EMU1010_SOURCE_INPUT("DSP 3 Capture Enum", 3),
+       EMU1010_SOURCE_INPUT("DSP 4 Capture Enum", 4),
+       EMU1010_SOURCE_INPUT("DSP 5 Capture Enum", 5),
+       EMU1010_SOURCE_INPUT("DSP 6 Capture Enum", 6),
+       EMU1010_SOURCE_INPUT("DSP 7 Capture Enum", 7),
+       EMU1010_SOURCE_INPUT("DSP 8 Capture Enum", 8),
+       EMU1010_SOURCE_INPUT("DSP 9 Capture Enum", 9),
+       EMU1010_SOURCE_INPUT("DSP A Capture Enum", 0xa),
+       EMU1010_SOURCE_INPUT("DSP B Capture Enum", 0xb),
+       EMU1010_SOURCE_INPUT("DSP C Capture Enum", 0xc),
+       EMU1010_SOURCE_INPUT("DSP D Capture Enum", 0xd),
+       EMU1010_SOURCE_INPUT("DSP E Capture Enum", 0xe),
+       EMU1010_SOURCE_INPUT("DSP F Capture Enum", 0xf),
+       EMU1010_SOURCE_INPUT("DSP 10 Capture Enum", 0x10),
+       EMU1010_SOURCE_INPUT("DSP 11 Capture Enum", 0x11),
+       EMU1010_SOURCE_INPUT("DSP 12 Capture Enum", 0x12),
+       EMU1010_SOURCE_INPUT("DSP 13 Capture Enum", 0x13),
+       EMU1010_SOURCE_INPUT("DSP 14 Capture Enum", 0x14),
+       EMU1010_SOURCE_INPUT("DSP 15 Capture Enum", 0x15),
 };
 
 
@@ -542,7 +563,7 @@ static int snd_emu1010_internal_clock_put(struct snd_kcontrol *kcontrol,
                        snd_emu1010_fpga_write(emu, EMU_HANA_DOCK_LEDS_2,
                                EMU_HANA_DOCK_LEDS_2_44K | EMU_HANA_DOCK_LEDS_2_LOCK );
                        /* Allow DLL to settle */
-                       udelay(10000);
+                       msleep(10);
                        /* Unmute all */
                        snd_emu1010_fpga_write(emu, EMU_HANA_UNMUTE, EMU_UNMUTE );
                        break;
@@ -559,7 +580,7 @@ static int snd_emu1010_internal_clock_put(struct snd_kcontrol *kcontrol,
                        snd_emu1010_fpga_write(emu, EMU_HANA_DOCK_LEDS_2,
                                EMU_HANA_DOCK_LEDS_2_48K | EMU_HANA_DOCK_LEDS_2_LOCK );
                        /* Allow DLL to settle */
-                       udelay(10000);
+                       msleep(10);
                        /* Unmute all */
                        snd_emu1010_fpga_write(emu, EMU_HANA_UNMUTE, EMU_UNMUTE );
                        break;
@@ -579,6 +600,162 @@ static struct snd_kcontrol_new snd_emu1010_internal_clock =
        .put =          snd_emu1010_internal_clock_put
 };
 
+static int snd_audigy_i2c_capture_source_info(struct snd_kcontrol *kcontrol,
+                                         struct snd_ctl_elem_info *uinfo)
+{
+#if 0
+       static char *texts[4] = {
+               "Unknown1", "Unknown2", "Mic", "Line"
+       };
+#endif
+       static char *texts[2] = {
+               "Mic", "Line"
+       };
+
+       uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
+       uinfo->count = 1;
+       uinfo->value.enumerated.items = 2;
+       if (uinfo->value.enumerated.item > 1)
+                uinfo->value.enumerated.item = 1;
+       strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]);
+       return 0;
+}
+
+static int snd_audigy_i2c_capture_source_get(struct snd_kcontrol *kcontrol,
+                                       struct snd_ctl_elem_value *ucontrol)
+{
+       struct snd_emu10k1 *emu = snd_kcontrol_chip(kcontrol);
+
+       ucontrol->value.enumerated.item[0] = emu->i2c_capture_source;
+       return 0;
+}
+
+static int snd_audigy_i2c_capture_source_put(struct snd_kcontrol *kcontrol,
+                                       struct snd_ctl_elem_value *ucontrol)
+{
+       struct snd_emu10k1 *emu = snd_kcontrol_chip(kcontrol);
+       unsigned int source_id;
+       unsigned int ngain, ogain;
+       u32 gpio;
+       int change = 0;
+       unsigned long flags;
+       u32 source;
+       /* If the capture source has changed,
+        * update the capture volume from the cached value
+        * for the particular source.
+        */
+       source_id = ucontrol->value.enumerated.item[0]; /* Use 2 and 3 */
+       change = (emu->i2c_capture_source != source_id);
+       if (change) {
+               snd_emu10k1_i2c_write(emu, ADC_MUX, 0); /* Mute input */
+               spin_lock_irqsave(&emu->emu_lock, flags);
+               gpio = inl(emu->port + A_IOCFG);
+               if (source_id==0)
+                       outl(gpio | 0x4, emu->port + A_IOCFG);
+               else
+                       outl(gpio & ~0x4, emu->port + A_IOCFG);
+               spin_unlock_irqrestore(&emu->emu_lock, flags);
+
+               ngain = emu->i2c_capture_volume[source_id][0]; /* Left */
+               ogain = emu->i2c_capture_volume[emu->i2c_capture_source][0]; /* Left */
+               if (ngain != ogain)
+                       snd_emu10k1_i2c_write(emu, ADC_ATTEN_ADCL, ((ngain) & 0xff));
+               ngain = emu->i2c_capture_volume[source_id][1]; /* Right */
+               ogain = emu->i2c_capture_volume[emu->i2c_capture_source][1]; /* Right */
+               if (ngain != ogain)
+                       snd_emu10k1_i2c_write(emu, ADC_ATTEN_ADCR, ((ngain) & 0xff));
+
+               source = 1 << (source_id + 2);
+               snd_emu10k1_i2c_write(emu, ADC_MUX, source); /* Set source */
+               emu->i2c_capture_source = source_id;
+       }
+        return change;
+}
+
+static struct snd_kcontrol_new snd_audigy_i2c_capture_source =
+{
+               .iface =        SNDRV_CTL_ELEM_IFACE_MIXER,
+               .name =         "Capture Source",
+               .info =         snd_audigy_i2c_capture_source_info,
+               .get =          snd_audigy_i2c_capture_source_get,
+               .put =          snd_audigy_i2c_capture_source_put
+};
+
+static int snd_audigy_i2c_volume_info(struct snd_kcontrol *kcontrol,
+                                 struct snd_ctl_elem_info *uinfo)
+{
+       uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+       uinfo->count = 2;
+       uinfo->value.integer.min = 0;
+       uinfo->value.integer.max = 255;
+       return 0;
+}
+
+static int snd_audigy_i2c_volume_get(struct snd_kcontrol *kcontrol,
+                                struct snd_ctl_elem_value *ucontrol)
+{
+       struct snd_emu10k1 *emu = snd_kcontrol_chip(kcontrol);
+       int source_id;
+
+       source_id = kcontrol->private_value;
+
+       ucontrol->value.integer.value[0] = emu->i2c_capture_volume[source_id][0];
+       ucontrol->value.integer.value[1] = emu->i2c_capture_volume[source_id][1];
+       return 0;
+}
+
+static int snd_audigy_i2c_volume_put(struct snd_kcontrol *kcontrol,
+                                struct snd_ctl_elem_value *ucontrol)
+{
+       struct snd_emu10k1 *emu = snd_kcontrol_chip(kcontrol);
+       unsigned int ogain;
+       unsigned int ngain;
+       int source_id;
+       int change = 0;
+
+       source_id = kcontrol->private_value;
+       ogain = emu->i2c_capture_volume[source_id][0]; /* Left */
+       ngain = ucontrol->value.integer.value[0];
+       if (ngain > 0xff)
+               return 0;
+       if (ogain != ngain) {
+               if (emu->i2c_capture_source == source_id)
+                       snd_emu10k1_i2c_write(emu, ADC_ATTEN_ADCL, ((ngain) & 0xff) );
+               emu->i2c_capture_volume[source_id][0] = ucontrol->value.integer.value[0];
+               change = 1;
+       }
+       ogain = emu->i2c_capture_volume[source_id][1]; /* Right */
+       ngain = ucontrol->value.integer.value[1];
+       if (ngain > 0xff)
+               return 0;
+       if (ogain != ngain) {
+               if (emu->i2c_capture_source == source_id)
+                       snd_emu10k1_i2c_write(emu, ADC_ATTEN_ADCR, ((ngain) & 0xff));
+               emu->i2c_capture_volume[source_id][1] = ucontrol->value.integer.value[1];
+               change = 1;
+       }
+
+       return change;
+}
+
+#define I2C_VOLUME(xname,chid) \
+{                                                              \
+       .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname,     \
+       .access = SNDRV_CTL_ELEM_ACCESS_READWRITE |             \
+                 SNDRV_CTL_ELEM_ACCESS_TLV_READ,               \
+       .info =  snd_audigy_i2c_volume_info,                    \
+       .get =   snd_audigy_i2c_volume_get,                     \
+       .put =   snd_audigy_i2c_volume_put,                     \
+       .tlv = { .p = snd_audigy_db_scale2 },                   \
+       .private_value = chid                                   \
+}
+
+
+static struct snd_kcontrol_new snd_audigy_i2c_volume_ctls[] __devinitdata = {
+       I2C_VOLUME("Mic Capture Volume", 0),
+       I2C_VOLUME("Line Capture Volume", 0)
+};
+
 #if 0
 static int snd_audigy_spdif_output_rate_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
 {
@@ -1179,7 +1356,9 @@ static int snd_emu10k1_shared_spdif_put(struct snd_kcontrol *kcontrol,
        int change = 0;
 
        spin_lock_irqsave(&emu->reg_lock, flags);
-       if (emu->audigy) {
+       if ( emu->card_capabilities->i2c_adc) {
+               /* Do nothing for Audigy 2 ZS Notebook */
+       } else if (emu->audigy) {
                reg = inl(emu->port + A_IOCFG);
                val = ucontrol->value.integer.value[0] ? A_IOCFG_GPOUT0 : 0;
                change = (reg & A_IOCFG_GPOUT0) != val;
@@ -1317,6 +1496,24 @@ int __devinit snd_emu10k1_mixer(struct snd_emu10k1 *emu,
                "AMic Playback Volume", "Mic Playback Volume",
                NULL
        };
+       static char *audigy_rename_ctls_i2c_adc[] = {
+               //"Analog Mix Capture Volume","OLD Analog Mix Capture Volume",
+               "Line Capture Volume", "Analog Mix Capture Volume",
+               "Wave Playback Volume", "OLD PCM Playback Volume",
+               "Wave Master Playback Volume", "Master Playback Volume",
+               "AMic Playback Volume", "Old Mic Playback Volume",
+               "CD Capture Volume", "IEC958 Optical Capture Volume",
+               NULL
+       };
+       static char *audigy_remove_ctls_i2c_adc[] = {
+               /* On the Audigy2 ZS Notebook
+                * Capture via WM8775  */
+               "Mic Capture Volume",
+               "Analog Mix Capture Volume",
+               "Aux Capture Volume",
+               "IEC958 Optical Capture Volume",
+               NULL
+       };
        static char *audigy_remove_ctls_1361t_adc[] = {
                /* On the Audigy2 the AC97 playback is piped into
                 * the Philips ADC for 24bit capture */
@@ -1401,6 +1598,7 @@ int __devinit snd_emu10k1_mixer(struct snd_emu10k1 *emu,
                        if (emu->ac97->id == AC97_ID_STAC9758) {
                                emu->rear_ac97 = 1;
                                snd_emu10k1_ptr_write(emu, AC97SLOT, 0, AC97SLOT_CNTR|AC97SLOT_LFE|AC97SLOT_REAR_LEFT|AC97SLOT_REAR_RIGHT);
+                               snd_ac97_write_cache(emu->ac97, AC97_HEADPHONE, 0x0202);
                        }
                        /* remove unused AC97 controls */
                        snd_ac97_write_cache(emu->ac97, AC97_SURROUND_MASTER, 0x0202);
@@ -1409,6 +1607,10 @@ int __devinit snd_emu10k1_mixer(struct snd_emu10k1 *emu,
                }
                for (; *c; c++)
                        remove_ctl(card, *c);
+       } else if (emu->card_capabilities->i2c_adc) {
+               c = audigy_remove_ctls_i2c_adc;
+               for (; *c; c++)
+                       remove_ctl(card, *c);
        } else {
        no_ac97:
                if (emu->card_capabilities->ecard)
@@ -1422,6 +1624,8 @@ int __devinit snd_emu10k1_mixer(struct snd_emu10k1 *emu,
        if (emu->audigy)
                if (emu->card_capabilities->adc_1361t)
                        c = audigy_rename_ctls_1361t_adc;
+               else if (emu->card_capabilities->i2c_adc)
+                       c = audigy_rename_ctls_i2c_adc;
                else
                        c = audigy_rename_ctls;
        else
@@ -1584,6 +1788,20 @@ int __devinit snd_emu10k1_mixer(struct snd_emu10k1 *emu,
                if (err < 0)
                        return err;
        }
+
+       if ( emu->card_capabilities->i2c_adc) {
+               int i;
+
+               err = snd_ctl_add(card, snd_ctl_new1(&snd_audigy_i2c_capture_source, emu));
+               if (err < 0)
+                       return err;
+
+               for (i = 0; i < ARRAY_SIZE(snd_audigy_i2c_volume_ctls); i++) {
+                       err = snd_ctl_add(card, snd_ctl_new1(&snd_audigy_i2c_volume_ctls[i], emu));
+                       if (err < 0)
+                               return err;
+               }
+       }
                
        return 0;
 }