]> bbs.cooldavid.org Git - net-next-2.6.git/commitdiff
Merge branch 'topic/asoc' into for-linus
authorTakashi Iwai <tiwai@suse.de>
Tue, 6 Jan 2009 08:48:51 +0000 (09:48 +0100)
committerTakashi Iwai <tiwai@suse.de>
Tue, 6 Jan 2009 08:48:51 +0000 (09:48 +0100)
include/sound/soc-dapm.h
include/sound/soc.h
sound/arm/pxa2xx-ac97-lib.c
sound/soc/codecs/twl4030.c
sound/soc/davinci/davinci-evm.c
sound/soc/omap/Kconfig
sound/soc/omap/omap3pandora.c
sound/soc/soc-core.c
sound/soc/soc-dapm.c

index 7ee2f70ca42e9009038d02b60e147603ad919d13..4af1083e3287aa332491bc081d5b3ee2d30f6d13 100644 (file)
 #define SND_SOC_DAPM_MUX(wname, wreg, wshift, winvert, wcontrols) \
 {      .id = snd_soc_dapm_mux, .name = wname, .reg = wreg, .shift = wshift, \
        .invert = winvert, .kcontrols = wcontrols, .num_kcontrols = 1}
+#define SND_SOC_DAPM_VALUE_MUX(wname, wreg, wshift, winvert, wcontrols) \
+{      .id = snd_soc_dapm_value_mux, .name = wname, .reg = wreg, \
+       .shift = wshift, .invert = winvert, .kcontrols = wcontrols, \
+       .num_kcontrols = 1}
 
 /* path domain with event - event handler must return 0 for success */
 #define SND_SOC_DAPM_PGA_E(wname, wreg, wshift, winvert, wcontrols, \
        .get = snd_soc_dapm_get_enum_double, \
        .put = snd_soc_dapm_put_enum_double, \
        .private_value = (unsigned long)&xenum }
+#define SOC_DAPM_VALUE_ENUM(xname, xenum) \
+{      .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \
+       .info = snd_soc_info_value_enum_double, \
+       .get = snd_soc_dapm_get_value_enum_double, \
+       .put = snd_soc_dapm_put_value_enum_double, \
+       .private_value = (unsigned long)&xenum }
 
 /* dapm stream operations */
 #define SND_SOC_DAPM_STREAM_NOP                        0x0
@@ -214,6 +224,10 @@ int snd_soc_dapm_get_enum_double(struct snd_kcontrol *kcontrol,
        struct snd_ctl_elem_value *ucontrol);
 int snd_soc_dapm_put_enum_double(struct snd_kcontrol *kcontrol,
        struct snd_ctl_elem_value *ucontrol);
+int snd_soc_dapm_get_value_enum_double(struct snd_kcontrol *kcontrol,
+       struct snd_ctl_elem_value *ucontrol);
+int snd_soc_dapm_put_value_enum_double(struct snd_kcontrol *kcontrol,
+       struct snd_ctl_elem_value *ucontrol);
 int snd_soc_dapm_new_control(struct snd_soc_codec *codec,
        const struct snd_soc_dapm_widget *widget);
 int snd_soc_dapm_new_controls(struct snd_soc_codec *codec,
@@ -247,6 +261,7 @@ enum snd_soc_dapm_type {
        snd_soc_dapm_input = 0,         /* input pin */
        snd_soc_dapm_output,            /* output pin */
        snd_soc_dapm_mux,                       /* selects 1 analog signal from many inputs */
+       snd_soc_dapm_value_mux,                 /* selects 1 analog signal from many inputs */
        snd_soc_dapm_mixer,                     /* mixes several analog signals together */
        snd_soc_dapm_pga,                       /* programmable gain/attenuation (volume) */
        snd_soc_dapm_adc,                       /* analog to digital converter */
index f86e455d3828b222e6cab7331d6cbfda5fef0456..9b930d34211666873670c93f732b38a11dec3437 100644 (file)
        SOC_ENUM_DOUBLE(xreg, xshift, xshift, xmax, xtexts)
 #define SOC_ENUM_SINGLE_EXT(xmax, xtexts) \
 {      .max = xmax, .texts = xtexts }
+#define SOC_VALUE_ENUM_DOUBLE(xreg, xshift_l, xshift_r, xmask, xmax, xtexts, xvalues) \
+{      .reg = xreg, .shift_l = xshift_l, .shift_r = xshift_r, \
+       .mask = xmask, .max = xmax, .texts = xtexts, .values = xvalues}
+#define SOC_VALUE_ENUM_SINGLE(xreg, xshift, xmask, xmax, xtexts, xvalues) \
+       SOC_VALUE_ENUM_DOUBLE(xreg, xshift, xshift, xmask, xmax, xtexts, xvalues)
 #define SOC_ENUM(xname, xenum) \
 {      .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname,\
        .info = snd_soc_info_enum_double, \
        .get = snd_soc_get_enum_double, .put = snd_soc_put_enum_double, \
        .private_value = (unsigned long)&xenum }
+#define SOC_VALUE_ENUM(xname, xenum) \
+{      .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname,\
+       .info = snd_soc_info_value_enum_double, \
+       .get = snd_soc_get_value_enum_double, \
+       .put = snd_soc_put_value_enum_double, \
+       .private_value = (unsigned long)&xenum }
 #define SOC_SINGLE_EXT(xname, xreg, xshift, xmax, xinvert,\
         xhandler_get, xhandler_put) \
 {      .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \
@@ -200,6 +211,12 @@ int snd_soc_get_enum_double(struct snd_kcontrol *kcontrol,
        struct snd_ctl_elem_value *ucontrol);
 int snd_soc_put_enum_double(struct snd_kcontrol *kcontrol,
        struct snd_ctl_elem_value *ucontrol);
+int snd_soc_info_value_enum_double(struct snd_kcontrol *kcontrol,
+       struct snd_ctl_elem_info *uinfo);
+int snd_soc_get_value_enum_double(struct snd_kcontrol *kcontrol,
+       struct snd_ctl_elem_value *ucontrol);
+int snd_soc_put_value_enum_double(struct snd_kcontrol *kcontrol,
+       struct snd_ctl_elem_value *ucontrol);
 int snd_soc_info_volsw(struct snd_kcontrol *kcontrol,
        struct snd_ctl_elem_info *uinfo);
 int snd_soc_info_volsw_ext(struct snd_kcontrol *kcontrol,
@@ -406,6 +423,19 @@ struct soc_enum {
        void *dapm;
 };
 
+/* semi enumerated kcontrol */
+struct soc_value_enum {
+       unsigned short reg;
+       unsigned short reg2;
+       unsigned char shift_l;
+       unsigned char shift_r;
+       unsigned int max;
+       unsigned int mask;
+       const char **texts;
+       const unsigned int *values;
+       void *dapm;
+};
+
 #include <sound/soc-dai.h>
 
 #endif
index ef6539eea5793c94b98b2072c0059efc31f9903f..35afd0c33be58b1a74b1e1be7685bd88dd65a801 100644 (file)
@@ -321,10 +321,6 @@ int __devinit pxa2xx_ac97_hw_probe(struct platform_device *dev)
 {
        int ret;
 
-       ret = request_irq(IRQ_AC97, pxa2xx_ac97_irq, 0, "AC97", NULL);
-       if (ret < 0)
-               goto err;
-
        if (cpu_is_pxa25x() || cpu_is_pxa27x()) {
                pxa_gpio_mode(GPIO31_SYNC_AC97_MD);
                pxa_gpio_mode(GPIO30_SDATA_OUT_AC97_MD);
@@ -339,7 +335,7 @@ int __devinit pxa2xx_ac97_hw_probe(struct platform_device *dev)
                if (IS_ERR(ac97conf_clk)) {
                        ret = PTR_ERR(ac97conf_clk);
                        ac97conf_clk = NULL;
-                       goto err_irq;
+                       goto err_conf;
                }
        }
 
@@ -347,19 +343,30 @@ int __devinit pxa2xx_ac97_hw_probe(struct platform_device *dev)
        if (IS_ERR(ac97_clk)) {
                ret = PTR_ERR(ac97_clk);
                ac97_clk = NULL;
-               goto err_irq;
+               goto err_clk;
        }
 
-       return clk_enable(ac97_clk);
+       ret = clk_enable(ac97_clk);
+       if (ret)
+               goto err_clk2;
+
+       ret = request_irq(IRQ_AC97, pxa2xx_ac97_irq, IRQF_DISABLED, "AC97", NULL);
+       if (ret < 0)
+               goto err_irq;
+
+       return 0;
 
 err_irq:
        GCR |= GCR_ACLINK_OFF;
+err_clk2:
+       clk_put(ac97_clk);
+       ac97_clk = NULL;
+err_clk:
        if (ac97conf_clk) {
                clk_put(ac97conf_clk);
                ac97conf_clk = NULL;
        }
-       free_irq(IRQ_AC97, NULL);
-err:
+err_conf:
        return ret;
 }
 EXPORT_SYMBOL_GPL(pxa2xx_ac97_hw_probe);
index 31e44e346dc83c63744566c5721df440dc493bf2..fd0f338374a7cfbea4fffacf624c3c148c63c6c9 100644 (file)
@@ -192,39 +192,51 @@ static void twl4030_init_chip(struct snd_soc_codec *codec)
 
 /* Earpiece */
 static const char *twl4030_earpiece_texts[] =
-               {"Off", "DACL1", "DACL2", "Invalid", "DACR1"};
+               {"Off", "DACL1", "DACL2", "DACR1"};
 
-static const struct soc_enum twl4030_earpiece_enum =
-       SOC_ENUM_SINGLE(TWL4030_REG_EAR_CTL, 1,
+static const unsigned int twl4030_earpiece_values[] =
+               {0x0, 0x1, 0x2, 0x4};
+
+static const struct soc_value_enum twl4030_earpiece_enum =
+       SOC_VALUE_ENUM_SINGLE(TWL4030_REG_EAR_CTL, 1, 0x7,
                        ARRAY_SIZE(twl4030_earpiece_texts),
-                       twl4030_earpiece_texts);
+                       twl4030_earpiece_texts,
+                       twl4030_earpiece_values);
 
 static const struct snd_kcontrol_new twl4030_dapm_earpiece_control =
-SOC_DAPM_ENUM("Route", twl4030_earpiece_enum);
+SOC_DAPM_VALUE_ENUM("Route", twl4030_earpiece_enum);
 
 /* PreDrive Left */
 static const char *twl4030_predrivel_texts[] =
-               {"Off", "DACL1", "DACL2", "Invalid", "DACR2"};
+               {"Off", "DACL1", "DACL2", "DACR2"};
+
+static const unsigned int twl4030_predrivel_values[] =
+               {0x0, 0x1, 0x2, 0x4};
 
-static const struct soc_enum twl4030_predrivel_enum =
-       SOC_ENUM_SINGLE(TWL4030_REG_PREDL_CTL, 1,
+static const struct soc_value_enum twl4030_predrivel_enum =
+       SOC_VALUE_ENUM_SINGLE(TWL4030_REG_PREDL_CTL, 1, 0x7,
                        ARRAY_SIZE(twl4030_predrivel_texts),
-                       twl4030_predrivel_texts);
+                       twl4030_predrivel_texts,
+                       twl4030_predrivel_values);
 
 static const struct snd_kcontrol_new twl4030_dapm_predrivel_control =
-SOC_DAPM_ENUM("Route", twl4030_predrivel_enum);
+SOC_DAPM_VALUE_ENUM("Route", twl4030_predrivel_enum);
 
 /* PreDrive Right */
 static const char *twl4030_predriver_texts[] =
-               {"Off", "DACR1", "DACR2", "Invalid", "DACL2"};
+               {"Off", "DACR1", "DACR2", "DACL2"};
 
-static const struct soc_enum twl4030_predriver_enum =
-       SOC_ENUM_SINGLE(TWL4030_REG_PREDR_CTL, 1,
+static const unsigned int twl4030_predriver_values[] =
+               {0x0, 0x1, 0x2, 0x4};
+
+static const struct soc_value_enum twl4030_predriver_enum =
+       SOC_VALUE_ENUM_SINGLE(TWL4030_REG_PREDR_CTL, 1, 0x7,
                        ARRAY_SIZE(twl4030_predriver_texts),
-                       twl4030_predriver_texts);
+                       twl4030_predriver_texts,
+                       twl4030_predriver_values);
 
 static const struct snd_kcontrol_new twl4030_dapm_predriver_control =
-SOC_DAPM_ENUM("Route", twl4030_predriver_enum);
+SOC_DAPM_VALUE_ENUM("Route", twl4030_predriver_enum);
 
 /* Headset Left */
 static const char *twl4030_hsol_texts[] =
@@ -300,28 +312,35 @@ SOC_DAPM_ENUM("Route", twl4030_handsfreer_enum);
 
 /* Left analog microphone selection */
 static const char *twl4030_analoglmic_texts[] =
-               {"Off", "Main mic", "Headset mic", "Invalid", "AUXL",
-                "Invalid", "Invalid", "Invalid", "Carkit mic"};
+               {"Off", "Main mic", "Headset mic", "AUXL", "Carkit mic"};
+
+static const unsigned int twl4030_analoglmic_values[] =
+               {0x0, 0x1, 0x2, 0x4, 0x8};
 
-static const struct soc_enum twl4030_analoglmic_enum =
-       SOC_ENUM_SINGLE(TWL4030_REG_ANAMICL, 0,
+static const struct soc_value_enum twl4030_analoglmic_enum =
+       SOC_VALUE_ENUM_SINGLE(TWL4030_REG_ANAMICL, 0, 0xf,
                        ARRAY_SIZE(twl4030_analoglmic_texts),
-                       twl4030_analoglmic_texts);
+                       twl4030_analoglmic_texts,
+                       twl4030_analoglmic_values);
 
 static const struct snd_kcontrol_new twl4030_dapm_analoglmic_control =
-SOC_DAPM_ENUM("Route", twl4030_analoglmic_enum);
+SOC_DAPM_VALUE_ENUM("Route", twl4030_analoglmic_enum);
 
 /* Right analog microphone selection */
 static const char *twl4030_analogrmic_texts[] =
-               {"Off", "Sub mic", "Invalid", "Invalid", "AUXR"};
+               {"Off", "Sub mic", "AUXR"};
 
-static const struct soc_enum twl4030_analogrmic_enum =
-       SOC_ENUM_SINGLE(TWL4030_REG_ANAMICR, 0,
+static const unsigned int twl4030_analogrmic_values[] =
+               {0x0, 0x1, 0x4};
+
+static const struct soc_value_enum twl4030_analogrmic_enum =
+       SOC_VALUE_ENUM_SINGLE(TWL4030_REG_ANAMICR, 0, 0x5,
                        ARRAY_SIZE(twl4030_analogrmic_texts),
-                       twl4030_analogrmic_texts);
+                       twl4030_analogrmic_texts,
+                       twl4030_analogrmic_values);
 
 static const struct snd_kcontrol_new twl4030_dapm_analogrmic_control =
-SOC_DAPM_ENUM("Route", twl4030_analogrmic_enum);
+SOC_DAPM_VALUE_ENUM("Route", twl4030_analogrmic_enum);
 
 /* TX1 L/R Analog/Digital microphone selection */
 static const char *twl4030_micpathtx1_texts[] =
@@ -347,28 +366,6 @@ static const struct soc_enum twl4030_micpathtx2_enum =
 static const struct snd_kcontrol_new twl4030_dapm_micpathtx2_control =
 SOC_DAPM_ENUM("Route", twl4030_micpathtx2_enum);
 
-/*
- * This function filters out the non valid mux settings, named as "Invalid"
- * in the enum texts.
- * Just refuse to set an invalid mux mode.
- */
-static int twl4030_enum_event(struct snd_soc_dapm_widget *w,
-       struct snd_kcontrol *kcontrol, int event)
-{
-       struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
-       int ret = 0;
-       int val;
-
-       val = w->value >> e->shift_l;
-       if (!strcmp("Invalid", e->texts[val])) {
-               printk(KERN_WARNING "Invalid MUX setting on 0x%02x (%d)\n",
-                       e->reg, val);
-               ret = -1;
-       }
-
-       return ret;
-}
-
 static int micpath_event(struct snd_soc_dapm_widget *w,
        struct snd_kcontrol *kcontrol, int event)
 {
@@ -737,16 +734,13 @@ static const struct snd_soc_dapm_widget twl4030_dapm_widgets[] = {
 
        /* Output MUX controls */
        /* Earpiece */
-       SND_SOC_DAPM_MUX_E("Earpiece Mux", SND_SOC_NOPM, 0, 0,
-               &twl4030_dapm_earpiece_control, twl4030_enum_event,
-               SND_SOC_DAPM_PRE_REG),
+       SND_SOC_DAPM_VALUE_MUX("Earpiece Mux", SND_SOC_NOPM, 0, 0,
+               &twl4030_dapm_earpiece_control),
        /* PreDrivL/R */
-       SND_SOC_DAPM_MUX_E("PredriveL Mux", SND_SOC_NOPM, 0, 0,
-               &twl4030_dapm_predrivel_control, twl4030_enum_event,
-               SND_SOC_DAPM_PRE_REG),
-       SND_SOC_DAPM_MUX_E("PredriveR Mux", SND_SOC_NOPM, 0, 0,
-               &twl4030_dapm_predriver_control, twl4030_enum_event,
-               SND_SOC_DAPM_PRE_REG),
+       SND_SOC_DAPM_VALUE_MUX("PredriveL Mux", SND_SOC_NOPM, 0, 0,
+               &twl4030_dapm_predrivel_control),
+       SND_SOC_DAPM_VALUE_MUX("PredriveR Mux", SND_SOC_NOPM, 0, 0,
+               &twl4030_dapm_predriver_control),
        /* HeadsetL/R */
        SND_SOC_DAPM_MUX("HeadsetL Mux", SND_SOC_NOPM, 0, 0,
                &twl4030_dapm_hsol_control),
@@ -789,12 +783,10 @@ static const struct snd_soc_dapm_widget twl4030_dapm_widgets[] = {
                SND_SOC_DAPM_POST_REG),
 
        /* Analog input muxes with power switch for the physical ADCL/R */
-       SND_SOC_DAPM_MUX_E("Analog Left Capture Route",
-               TWL4030_REG_AVADC_CTL, 3, 0, &twl4030_dapm_analoglmic_control,
-               twl4030_enum_event, SND_SOC_DAPM_PRE_REG),
-       SND_SOC_DAPM_MUX_E("Analog Right Capture Route",
-               TWL4030_REG_AVADC_CTL, 1, 0, &twl4030_dapm_analogrmic_control,
-               twl4030_enum_event, SND_SOC_DAPM_PRE_REG),
+       SND_SOC_DAPM_VALUE_MUX("Analog Left Capture Route",
+               TWL4030_REG_AVADC_CTL, 3, 0, &twl4030_dapm_analoglmic_control),
+       SND_SOC_DAPM_VALUE_MUX("Analog Right Capture Route",
+               TWL4030_REG_AVADC_CTL, 1, 0, &twl4030_dapm_analogrmic_control),
 
        SND_SOC_DAPM_PGA("Analog Left Amplifier",
                TWL4030_REG_ANAMICL, 4, 0, NULL, 0),
index 01b948bb55a1ee6fdc5f5a13a43713980d8f0d32..54851f318568b7ba277284bf65d9f01077f64527 100644 (file)
@@ -26,7 +26,6 @@
 #include "davinci-pcm.h"
 #include "davinci-i2s.h"
 
-#define EVM_CODEC_CLOCK 22579200
 
 #define AUDIO_FORMAT (SND_SOC_DAIFMT_DSP_B | \
                SND_SOC_DAIFMT_CBM_CFM | SND_SOC_DAIFMT_IB_NF)
@@ -37,6 +36,21 @@ static int evm_hw_params(struct snd_pcm_substream *substream,
        struct snd_soc_dai *codec_dai = rtd->dai->codec_dai;
        struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai;
        int ret = 0;
+       unsigned sysclk;
+
+       /* ASP1 on DM355 EVM is clocked by an external oscillator */
+       if (machine_is_davinci_dm355_evm())
+               sysclk = 27000000;
+
+       /* ASP0 in DM6446 EVM is clocked by U55, as configured by
+        * board-dm644x-evm.c using GPIOs from U18.  There are six
+        * options; here we "know" we use a 48 KHz sample rate.
+        */
+       else if (machine_is_davinci_evm())
+               sysclk = 12288000;
+
+       else
+               return -EINVAL;
 
        /* set codec DAI configuration */
        ret = snd_soc_dai_set_fmt(codec_dai, AUDIO_FORMAT);
@@ -49,8 +63,7 @@ static int evm_hw_params(struct snd_pcm_substream *substream,
                return ret;
 
        /* set the codec system clock */
-       ret = snd_soc_dai_set_sysclk(codec_dai, 0, EVM_CODEC_CLOCK,
-                                           SND_SOC_CLOCK_OUT);
+       ret = snd_soc_dai_set_sysclk(codec_dai, 0, sysclk, SND_SOC_CLOCK_OUT);
        if (ret < 0)
                return ret;
 
index a7b1d77b2105ec6e722e11301b822fe270c30e60..4f7f040145850b2e0d86a76aaa95657baa2b6d48 100644 (file)
@@ -10,6 +10,7 @@ config SND_OMAP_SOC_N810
        tristate "SoC Audio support for Nokia N810"
        depends on SND_OMAP_SOC && MACH_NOKIA_N810
        select SND_OMAP_SOC_MCBSP
+       select OMAP_MUX
        select SND_SOC_TLV320AIC3X
        help
          Say Y if you want to add support for SoC audio on Nokia N810.
index bd91594496b1b142d8fa19a6f6e9ad64915cf61a..fcc2f5d9a87884c069813c013cfcea9ae2ac2356 100644 (file)
@@ -180,6 +180,19 @@ static int omap3pandora_in_init(struct snd_soc_codec *codec)
 {
        int ret;
 
+       /* All TWL4030 output pins are floating */
+       snd_soc_dapm_nc_pin(codec, "OUTL"),
+       snd_soc_dapm_nc_pin(codec, "OUTR"),
+       snd_soc_dapm_nc_pin(codec, "EARPIECE"),
+       snd_soc_dapm_nc_pin(codec, "PREDRIVEL"),
+       snd_soc_dapm_nc_pin(codec, "PREDRIVER"),
+       snd_soc_dapm_nc_pin(codec, "HSOL"),
+       snd_soc_dapm_nc_pin(codec, "HSOR"),
+       snd_soc_dapm_nc_pin(codec, "CARKITL"),
+       snd_soc_dapm_nc_pin(codec, "CARKITR"),
+       snd_soc_dapm_nc_pin(codec, "HFL"),
+       snd_soc_dapm_nc_pin(codec, "HFR"),
+
        ret = snd_soc_dapm_new_controls(codec, omap3pandora_in_dapm_widgets,
                                ARRAY_SIZE(omap3pandora_in_dapm_widgets));
        if (ret < 0)
index f73c1341437cf2aaf1e0e8442b466a05b67cc183..6cbe7e82f2385f1a405372412b4b2ff1f6db7ef5 100644 (file)
@@ -1584,6 +1584,113 @@ int snd_soc_put_enum_double(struct snd_kcontrol *kcontrol,
 }
 EXPORT_SYMBOL_GPL(snd_soc_put_enum_double);
 
+/**
+ * snd_soc_info_value_enum_double - semi enumerated double mixer info callback
+ * @kcontrol: mixer control
+ * @uinfo: control element information
+ *
+ * Callback to provide information about a double semi enumerated
+ * mixer control.
+ *
+ * Semi enumerated mixer: the enumerated items are referred as values. Can be
+ * used for handling bitfield coded enumeration for example.
+ *
+ * Returns 0 for success.
+ */
+int snd_soc_info_value_enum_double(struct snd_kcontrol *kcontrol,
+       struct snd_ctl_elem_info *uinfo)
+{
+       struct soc_value_enum *e = (struct soc_value_enum *)
+                       kcontrol->private_value;
+
+       uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
+       uinfo->count = e->shift_l == e->shift_r ? 1 : 2;
+       uinfo->value.enumerated.items = e->max;
+
+       if (uinfo->value.enumerated.item > e->max - 1)
+               uinfo->value.enumerated.item = e->max - 1;
+       strcpy(uinfo->value.enumerated.name,
+               e->texts[uinfo->value.enumerated.item]);
+       return 0;
+}
+EXPORT_SYMBOL_GPL(snd_soc_info_value_enum_double);
+
+/**
+ * snd_soc_get_value_enum_double - semi enumerated double mixer get callback
+ * @kcontrol: mixer control
+ * @ucontrol: control element information
+ *
+ * Callback to get the value of a double semi enumerated mixer.
+ *
+ * Semi enumerated mixer: the enumerated items are referred as values. Can be
+ * used for handling bitfield coded enumeration for example.
+ *
+ * Returns 0 for success.
+ */
+int snd_soc_get_value_enum_double(struct snd_kcontrol *kcontrol,
+       struct snd_ctl_elem_value *ucontrol)
+{
+       struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+       struct soc_value_enum *e = (struct soc_value_enum *)
+                       kcontrol->private_value;
+       unsigned short reg_val, val, mux;
+
+       reg_val = snd_soc_read(codec, e->reg);
+       val = (reg_val >> e->shift_l) & e->mask;
+       for (mux = 0; mux < e->max; mux++) {
+               if (val == e->values[mux])
+                       break;
+       }
+       ucontrol->value.enumerated.item[0] = mux;
+       if (e->shift_l != e->shift_r) {
+               val = (reg_val >> e->shift_r) & e->mask;
+               for (mux = 0; mux < e->max; mux++) {
+                       if (val == e->values[mux])
+                               break;
+               }
+               ucontrol->value.enumerated.item[1] = mux;
+       }
+
+       return 0;
+}
+EXPORT_SYMBOL_GPL(snd_soc_get_value_enum_double);
+
+/**
+ * snd_soc_put_value_enum_double - semi enumerated double mixer put callback
+ * @kcontrol: mixer control
+ * @ucontrol: control element information
+ *
+ * Callback to set the value of a double semi enumerated mixer.
+ *
+ * Semi enumerated mixer: the enumerated items are referred as values. Can be
+ * used for handling bitfield coded enumeration for example.
+ *
+ * Returns 0 for success.
+ */
+int snd_soc_put_value_enum_double(struct snd_kcontrol *kcontrol,
+       struct snd_ctl_elem_value *ucontrol)
+{
+       struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+       struct soc_value_enum *e = (struct soc_value_enum *)
+                       kcontrol->private_value;
+       unsigned short val;
+       unsigned short mask;
+
+       if (ucontrol->value.enumerated.item[0] > e->max - 1)
+               return -EINVAL;
+       val = e->values[ucontrol->value.enumerated.item[0]] << e->shift_l;
+       mask = e->mask << e->shift_l;
+       if (e->shift_l != e->shift_r) {
+               if (ucontrol->value.enumerated.item[1] > e->max - 1)
+                       return -EINVAL;
+               val |= e->values[ucontrol->value.enumerated.item[1]] << e->shift_r;
+               mask |= e->mask << e->shift_r;
+       }
+
+       return snd_soc_update_bits(codec, e->reg, mask, val);
+}
+EXPORT_SYMBOL_GPL(snd_soc_put_value_enum_double);
+
 /**
  * snd_soc_info_enum_ext - external enumerated single mixer info callback
  * @kcontrol: mixer control
index 6c79ca6df0bf284ba59e8075152e911518c7c2dc..ad0d801677c13ccd64eaa7942f1c120ecb07b144 100644 (file)
 /* dapm power sequences - make this per codec in the future */
 static int dapm_up_seq[] = {
        snd_soc_dapm_pre, snd_soc_dapm_micbias, snd_soc_dapm_mic,
-       snd_soc_dapm_mux, snd_soc_dapm_dac, snd_soc_dapm_mixer, snd_soc_dapm_pga,
-       snd_soc_dapm_adc, snd_soc_dapm_hp, snd_soc_dapm_spk, snd_soc_dapm_post
+       snd_soc_dapm_mux, snd_soc_dapm_value_mux, snd_soc_dapm_dac,
+       snd_soc_dapm_mixer, snd_soc_dapm_pga, snd_soc_dapm_adc, snd_soc_dapm_hp,
+       snd_soc_dapm_spk, snd_soc_dapm_post
 };
 static int dapm_down_seq[] = {
        snd_soc_dapm_pre, snd_soc_dapm_adc, snd_soc_dapm_hp, snd_soc_dapm_spk,
        snd_soc_dapm_pga, snd_soc_dapm_mixer, snd_soc_dapm_dac, snd_soc_dapm_mic,
-       snd_soc_dapm_micbias, snd_soc_dapm_mux, snd_soc_dapm_post
+       snd_soc_dapm_micbias, snd_soc_dapm_mux, snd_soc_dapm_value_mux,
+       snd_soc_dapm_post
 };
 
 static int dapm_status = 1;
@@ -134,6 +136,25 @@ static void dapm_set_path_status(struct snd_soc_dapm_widget *w,
                }
        }
        break;
+       case snd_soc_dapm_value_mux: {
+               struct soc_value_enum *e = (struct soc_value_enum *)
+                       w->kcontrols[i].private_value;
+               int val, item;
+
+               val = snd_soc_read(w->codec, e->reg);
+               val = (val >> e->shift_l) & e->mask;
+               for (item = 0; item < e->max; item++) {
+                       if (val == e->values[item])
+                               break;
+               }
+
+               p->connect = 0;
+               for (i = 0; i < e->max; i++) {
+                       if (!(strcmp(p->name, e->texts[i])) && item == i)
+                               p->connect = 1;
+               }
+       }
+       break;
        /* does not effect routing - always connected */
        case snd_soc_dapm_pga:
        case snd_soc_dapm_output:
@@ -179,6 +200,30 @@ static int dapm_connect_mux(struct snd_soc_codec *codec,
        return -ENODEV;
 }
 
+/* connect value_mux widget to it's interconnecting audio paths */
+static int dapm_connect_value_mux(struct snd_soc_codec *codec,
+       struct snd_soc_dapm_widget *src, struct snd_soc_dapm_widget *dest,
+       struct snd_soc_dapm_path *path, const char *control_name,
+       const struct snd_kcontrol_new *kcontrol)
+{
+       struct soc_value_enum *e = (struct soc_value_enum *)
+                       kcontrol->private_value;
+       int i;
+
+       for (i = 0; i < e->max; i++) {
+               if (!(strcmp(control_name, e->texts[i]))) {
+                       list_add(&path->list, &codec->dapm_paths);
+                       list_add(&path->list_sink, &dest->sources);
+                       list_add(&path->list_source, &src->sinks);
+                       path->name = (char *)e->texts[i];
+                       dapm_set_path_status(dest, path, 0);
+                       return 0;
+               }
+       }
+
+       return -ENODEV;
+}
+
 /* connect mixer widget to it's interconnecting audio paths */
 static int dapm_connect_mixer(struct snd_soc_codec *codec,
        struct snd_soc_dapm_widget *src, struct snd_soc_dapm_widget *dest,
@@ -653,6 +698,7 @@ static void dbg_dump_dapm(struct snd_soc_codec* codec, const char *action)
                case snd_soc_dapm_vmid:
                        continue;
                case snd_soc_dapm_mux:
+               case snd_soc_dapm_value_mux:
                case snd_soc_dapm_output:
                case snd_soc_dapm_input:
                case snd_soc_dapm_switch:
@@ -728,6 +774,45 @@ static int dapm_mux_update_power(struct snd_soc_dapm_widget *widget,
        return 0;
 }
 
+/* test and update the power status of a value_mux widget */
+static int dapm_value_mux_update_power(struct snd_soc_dapm_widget *widget,
+                                struct snd_kcontrol *kcontrol, int mask,
+                                int mux, int val, struct soc_value_enum *e)
+{
+       struct snd_soc_dapm_path *path;
+       int found = 0;
+
+       if (widget->id != snd_soc_dapm_value_mux)
+               return -ENODEV;
+
+       if (!snd_soc_test_bits(widget->codec, e->reg, mask, val))
+               return 0;
+
+       /* find dapm widget path assoc with kcontrol */
+       list_for_each_entry(path, &widget->codec->dapm_paths, list) {
+               if (path->kcontrol != kcontrol)
+                       continue;
+
+               if (!path->name || !e->texts[mux])
+                       continue;
+
+               found = 1;
+               /* we now need to match the string in the enum to the path */
+               if (!(strcmp(path->name, e->texts[mux])))
+                       path->connect = 1; /* new connection */
+               else
+                       path->connect = 0; /* old connection must be
+                                             powered down */
+       }
+
+       if (found) {
+               dapm_power_widgets(widget->codec, SND_SOC_DAPM_STREAM_NOP);
+               dump_dapm(widget->codec, "mux power update");
+       }
+
+       return 0;
+}
+
 /* test and update the power status of a mixer or switch widget */
 static int dapm_mixer_update_power(struct snd_soc_dapm_widget *widget,
                                   struct snd_kcontrol *kcontrol, int reg,
@@ -965,6 +1050,12 @@ static int snd_soc_dapm_add_route(struct snd_soc_codec *codec,
                if (ret != 0)
                        goto err;
                break;
+       case snd_soc_dapm_value_mux:
+               ret = dapm_connect_value_mux(codec, wsource, wsink, path,
+                       control, &wsink->kcontrols[0]);
+               if (ret != 0)
+                       goto err;
+               break;
        case snd_soc_dapm_switch:
        case snd_soc_dapm_mixer:
                ret = dapm_connect_mixer(codec, wsource, wsink, path, control);
@@ -1047,6 +1138,7 @@ int snd_soc_dapm_new_widgets(struct snd_soc_codec *codec)
                        dapm_new_mixer(codec, w);
                        break;
                case snd_soc_dapm_mux:
+               case snd_soc_dapm_value_mux:
                        dapm_new_mux(codec, w);
                        break;
                case snd_soc_dapm_adc:
@@ -1273,6 +1365,105 @@ out:
 }
 EXPORT_SYMBOL_GPL(snd_soc_dapm_put_enum_double);
 
+/**
+ * snd_soc_dapm_get_value_enum_double - dapm semi enumerated double mixer get
+ *                                     callback
+ * @kcontrol: mixer control
+ * @ucontrol: control element information
+ *
+ * Callback to get the value of a dapm semi enumerated double mixer control.
+ *
+ * Semi enumerated mixer: the enumerated items are referred as values. Can be
+ * used for handling bitfield coded enumeration for example.
+ *
+ * Returns 0 for success.
+ */
+int snd_soc_dapm_get_value_enum_double(struct snd_kcontrol *kcontrol,
+       struct snd_ctl_elem_value *ucontrol)
+{
+       struct snd_soc_dapm_widget *widget = snd_kcontrol_chip(kcontrol);
+       struct soc_value_enum *e = (struct soc_value_enum *)
+                       kcontrol->private_value;
+       unsigned short reg_val, val, mux;
+
+       reg_val = snd_soc_read(widget->codec, e->reg);
+       val = (reg_val >> e->shift_l) & e->mask;
+       for (mux = 0; mux < e->max; mux++) {
+               if (val == e->values[mux])
+                       break;
+       }
+       ucontrol->value.enumerated.item[0] = mux;
+       if (e->shift_l != e->shift_r) {
+               val = (reg_val >> e->shift_r) & e->mask;
+               for (mux = 0; mux < e->max; mux++) {
+                       if (val == e->values[mux])
+                               break;
+               }
+               ucontrol->value.enumerated.item[1] = mux;
+       }
+
+       return 0;
+}
+EXPORT_SYMBOL_GPL(snd_soc_dapm_get_value_enum_double);
+
+/**
+ * snd_soc_dapm_put_value_enum_double - dapm semi enumerated double mixer set
+ *                                     callback
+ * @kcontrol: mixer control
+ * @ucontrol: control element information
+ *
+ * Callback to set the value of a dapm semi enumerated double mixer control.
+ *
+ * Semi enumerated mixer: the enumerated items are referred as values. Can be
+ * used for handling bitfield coded enumeration for example.
+ *
+ * Returns 0 for success.
+ */
+int snd_soc_dapm_put_value_enum_double(struct snd_kcontrol *kcontrol,
+       struct snd_ctl_elem_value *ucontrol)
+{
+       struct snd_soc_dapm_widget *widget = snd_kcontrol_chip(kcontrol);
+       struct soc_value_enum *e = (struct soc_value_enum *)
+                       kcontrol->private_value;
+       unsigned short val, mux;
+       unsigned short mask;
+       int ret = 0;
+
+       if (ucontrol->value.enumerated.item[0] > e->max - 1)
+               return -EINVAL;
+       mux = ucontrol->value.enumerated.item[0];
+       val = e->values[ucontrol->value.enumerated.item[0]] << e->shift_l;
+       mask = e->mask << e->shift_l;
+       if (e->shift_l != e->shift_r) {
+               if (ucontrol->value.enumerated.item[1] > e->max - 1)
+                       return -EINVAL;
+               val |= e->values[ucontrol->value.enumerated.item[1]] << e->shift_r;
+               mask |= e->mask << e->shift_r;
+       }
+
+       mutex_lock(&widget->codec->mutex);
+       widget->value = val;
+       dapm_value_mux_update_power(widget, kcontrol, mask, mux, val, e);
+       if (widget->event) {
+               if (widget->event_flags & SND_SOC_DAPM_PRE_REG) {
+                       ret = widget->event(widget,
+                               kcontrol, SND_SOC_DAPM_PRE_REG);
+                       if (ret < 0)
+                               goto out;
+               }
+               ret = snd_soc_update_bits(widget->codec, e->reg, mask, val);
+               if (widget->event_flags & SND_SOC_DAPM_POST_REG)
+                       ret = widget->event(widget,
+                               kcontrol, SND_SOC_DAPM_POST_REG);
+       } else
+               ret = snd_soc_update_bits(widget->codec, e->reg, mask, val);
+
+out:
+       mutex_unlock(&widget->codec->mutex);
+       return ret;
+}
+EXPORT_SYMBOL_GPL(snd_soc_dapm_put_value_enum_double);
+
 /**
  * snd_soc_dapm_new_control - create new dapm control
  * @codec: audio codec