]> www.pilppa.org Git - linux-2.6-omap-h63xx.git/commitdiff
Merge branch 'topic/asoc' into next/asoc
authorTakashi Iwai <tiwai@suse.de>
Thu, 15 Jan 2009 17:27:20 +0000 (18:27 +0100)
committerTakashi Iwai <tiwai@suse.de>
Thu, 15 Jan 2009 17:27:20 +0000 (18:27 +0100)
31 files changed:
Documentation/sound/alsa/soc/dapm.txt
include/linux/mfd/wm8350/audio.h
include/sound/soc-dapm.h
include/sound/soc.h
sound/soc/Kconfig
sound/soc/Makefile
sound/soc/codecs/ad1980.c
sound/soc/codecs/ak4535.c
sound/soc/codecs/ssm2602.c
sound/soc/codecs/tlv320aic23.c
sound/soc/codecs/tlv320aic3x.c
sound/soc/codecs/twl4030.c
sound/soc/codecs/uda134x.c
sound/soc/codecs/uda1380.c
sound/soc/codecs/wm8350.c
sound/soc/codecs/wm8350.h
sound/soc/codecs/wm8510.c
sound/soc/codecs/wm8580.c
sound/soc/codecs/wm8728.c
sound/soc/codecs/wm8731.c
sound/soc/codecs/wm8750.c
sound/soc/codecs/wm8753.c
sound/soc/codecs/wm8900.c
sound/soc/codecs/wm8903.c
sound/soc/codecs/wm8971.c
sound/soc/codecs/wm8990.c
sound/soc/codecs/wm9712.c
sound/soc/codecs/wm9713.c
sound/soc/soc-core.c
sound/soc/soc-dapm.c
sound/soc/soc-jack.c [new file with mode: 0644]

index 46f9684d0b29d8550824073f0ddf925035b5c327..9e6763264a2ee1cb5b549cc27c51f47557d3ba0d 100644 (file)
@@ -116,6 +116,9 @@ SOC_DAPM_SINGLE("HiFi Playback Switch", WM8731_APANA, 4, 1, 0),
 SND_SOC_DAPM_MIXER("Output Mixer", WM8731_PWR, 4, 1, wm8731_output_mixer_controls,
        ARRAY_SIZE(wm8731_output_mixer_controls)),
 
+If you dont want the mixer elements prefixed with the name of the mixer widget,
+you can use SND_SOC_DAPM_MIXER_NAMED_CTL instead. the parameters are the same
+as for SND_SOC_DAPM_MIXER.
 
 2.3 Platform/Machine domain Widgets
 -----------------------------------
index af95a1d2f3a11e333fbe6a00fcb2354f16657a2a..d899dc0223ba642b41757bd6d85ffa5c20aae292 100644 (file)
 /*
  * R231 (0xE7) - Jack Status
  */
+#define WM8350_JACK_L_LVL                      0x0800
 #define WM8350_JACK_R_LVL                       0x0400
 
 /*
index 93a4edb148b50815beb254a2a310373b50aff779..0accdba211f9870ff65e6a2efd8f5fd937fde253 100644 (file)
         wcontrols, wncontrols)\
 {      .id = snd_soc_dapm_mixer, .name = wname, .reg = wreg, .shift = wshift, \
        .invert = winvert, .kcontrols = wcontrols, .num_kcontrols = wncontrols}
+#define SND_SOC_DAPM_MIXER_NAMED_CTL(wname, wreg, wshift, winvert, \
+        wcontrols, wncontrols)\
+{       .id = snd_soc_dapm_mixer_named_ctl, .name = wname, .reg = wreg, \
+       .shift = wshift, .invert = winvert, .kcontrols = wcontrols, \
+       .num_kcontrols = wncontrols}
 #define SND_SOC_DAPM_MICBIAS(wname, wreg, wshift, winvert) \
 {      .id = snd_soc_dapm_micbias, .name = wname, .reg = wreg, .shift = wshift, \
        .invert = winvert, .kcontrols = NULL, .num_kcontrols = 0}
 {      .id = snd_soc_dapm_mixer, .name = wname, .reg = wreg, .shift = wshift, \
        .invert = winvert, .kcontrols = wcontrols, .num_kcontrols = wncontrols, \
        .event = wevent, .event_flags = wflags}
+#define SND_SOC_DAPM_MIXER_NAMED_CTL_E(wname, wreg, wshift, winvert, \
+       wcontrols, wncontrols, wevent, wflags) \
+{       .id = snd_soc_dapm_mixer, .name = wname, .reg = wreg, .shift = wshift, \
+       .invert = winvert, .kcontrols = wcontrols, \
+       .num_kcontrols = wncontrols, .event = wevent, .event_flags = wflags}
 #define SND_SOC_DAPM_MICBIAS_E(wname, wreg, wshift, winvert, wevent, wflags) \
 {      .id = snd_soc_dapm_micbias, .name = wname, .reg = wreg, .shift = wshift, \
        .invert = winvert, .kcontrols = NULL, .num_kcontrols = 0, \
@@ -250,10 +260,10 @@ int snd_soc_dapm_set_bias_level(struct snd_soc_device *socdev,
 int snd_soc_dapm_sys_add(struct device *dev);
 
 /* dapm audio pin control and status */
-int snd_soc_dapm_enable_pin(struct snd_soc_codec *codec, char *pin);
-int snd_soc_dapm_disable_pin(struct snd_soc_codec *codec, char *pin);
-int snd_soc_dapm_nc_pin(struct snd_soc_codec *codec, char *pin);
-int snd_soc_dapm_get_pin_status(struct snd_soc_codec *codec, char *pin);
+int snd_soc_dapm_enable_pin(struct snd_soc_codec *codec, const char *pin);
+int snd_soc_dapm_disable_pin(struct snd_soc_codec *codec, const char *pin);
+int snd_soc_dapm_nc_pin(struct snd_soc_codec *codec, const char *pin);
+int snd_soc_dapm_get_pin_status(struct snd_soc_codec *codec, const char *pin);
 int snd_soc_dapm_sync(struct snd_soc_codec *codec);
 
 /* dapm widget types */
@@ -263,6 +273,7 @@ enum snd_soc_dapm_type {
        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_mixer_named_ctl,           /* mixer with named controls */
        snd_soc_dapm_pga,                       /* programmable gain/attenuation (volume) */
        snd_soc_dapm_adc,                       /* analog to digital converter */
        snd_soc_dapm_dac,                       /* digital to analog converter */
index 24593ac3ea19528749da10d77aeb0ca1e00c3f94..7039343e8a78b2a3cd7ff515496b7c47e1fa9da0 100644 (file)
@@ -154,6 +154,8 @@ enum snd_soc_bias_level {
        SND_SOC_BIAS_OFF,
 };
 
+struct snd_jack;
+struct snd_soc_card;
 struct snd_soc_device;
 struct snd_soc_pcm_stream;
 struct snd_soc_ops;
@@ -164,6 +166,8 @@ struct snd_soc_platform;
 struct snd_soc_codec;
 struct soc_enum;
 struct snd_soc_ac97_ops;
+struct snd_soc_jack;
+struct snd_soc_jack_pin;
 
 typedef int (*hw_write_t)(void *,const char* ,int);
 typedef int (*hw_read_t)(void *,char* ,int);
@@ -184,6 +188,13 @@ int snd_soc_init_card(struct snd_soc_device *socdev);
 int snd_soc_set_runtime_hwparams(struct snd_pcm_substream *substream,
        const struct snd_pcm_hardware *hw);
 
+/* Jack reporting */
+int snd_soc_jack_new(struct snd_soc_card *card, const char *id, int type,
+                    struct snd_soc_jack *jack);
+void snd_soc_jack_report(struct snd_soc_jack *jack, int status, int mask);
+int snd_soc_jack_add_pins(struct snd_soc_jack *jack, int count,
+                         struct snd_soc_jack_pin *pins);
+
 /* codec IO */
 #define snd_soc_read(codec, reg) codec->read(codec, reg)
 #define snd_soc_write(codec, reg, value) codec->write(codec, reg, value)
@@ -203,6 +214,8 @@ void snd_soc_free_ac97_codec(struct snd_soc_codec *codec);
  */
 struct snd_kcontrol *snd_soc_cnew(const struct snd_kcontrol_new *_template,
        void *data, char *long_name);
+int snd_soc_add_controls(struct snd_soc_codec *codec,
+       const struct snd_kcontrol_new *controls, int num_controls);
 int snd_soc_info_enum_double(struct snd_kcontrol *kcontrol,
        struct snd_ctl_elem_info *uinfo);
 int snd_soc_info_enum_ext(struct snd_kcontrol *kcontrol,
@@ -237,6 +250,27 @@ int snd_soc_get_volsw_s8(struct snd_kcontrol *kcontrol,
 int snd_soc_put_volsw_s8(struct snd_kcontrol *kcontrol,
        struct snd_ctl_elem_value *ucontrol);
 
+/**
+ * struct snd_soc_jack_pin - Describes a pin to update based on jack detection
+ *
+ * @pin:    name of the pin to update
+ * @mask:   bits to check for in reported jack status
+ * @invert: if non-zero then pin is enabled when status is not reported
+ */
+struct snd_soc_jack_pin {
+       struct list_head list;
+       const char *pin;
+       int mask;
+       bool invert;
+};
+
+struct snd_soc_jack {
+       struct snd_jack *jack;
+       struct snd_soc_card *card;
+       struct list_head pins;
+       int status;
+};
+
 /* SoC PCM stream information */
 struct snd_soc_pcm_stream {
        char *stream_name;
index ef025c66cc669c49c7b4537893ba76860fbd2397..3d2bb6fc6dcc46ad7f95ef5703059d6144be7849 100644 (file)
@@ -6,6 +6,7 @@ menuconfig SND_SOC
        tristate "ALSA for SoC audio support"
        select SND_PCM
        select AC97_BUS if SND_SOC_AC97_BUS
+       select SND_JACK if INPUT=y || INPUT=SND
        ---help---
 
          If you want ASoC support, you should say Y here and also to the
index 86a9b1f5b0f3cbf83c315505613fd751c8356306..0237879fd4125b47330a7a65a24449643fdaf3a7 100644 (file)
@@ -1,4 +1,4 @@
-snd-soc-core-objs := soc-core.o soc-dapm.o
+snd-soc-core-objs := soc-core.o soc-dapm.o soc-jack.o
 
 obj-$(CONFIG_SND_SOC)  += snd-soc-core.o
 obj-$(CONFIG_SND_SOC)  += codecs/
index 73fdbb4d4a3d4702cbf6d9b3b25d59d00a287e2b..c3c5d0eee37ac728ce3c9bd6d20e155db80bf845 100644 (file)
@@ -93,20 +93,6 @@ SOC_ENUM("Capture Source", ad1980_cap_src),
 SOC_SINGLE("Mic Boost Switch", AC97_MIC, 6, 1, 0),
 };
 
-/* add non dapm controls */
-static int ad1980_add_controls(struct snd_soc_codec *codec)
-{
-       int err, i;
-
-       for (i = 0; i < ARRAY_SIZE(ad1980_snd_ac97_controls); i++) {
-               err = snd_ctl_add(codec->card, snd_soc_cnew(
-                               &ad1980_snd_ac97_controls[i], codec, NULL));
-               if (err < 0)
-                       return err;
-       }
-       return 0;
-}
-
 static unsigned int ac97_read(struct snd_soc_codec *codec,
        unsigned int reg)
 {
@@ -269,7 +255,8 @@ static int ad1980_soc_probe(struct platform_device *pdev)
        ext_status = ac97_read(codec, AC97_EXTENDED_STATUS);
        ac97_write(codec, AC97_EXTENDED_STATUS, ext_status&~0x3800);
 
-       ad1980_add_controls(codec);
+       snd_soc_add_controls(codec, ad1980_snd_ac97_controls,
+                               ARRAY_SIZE(ad1980_snd_ac97_controls));
        ret = snd_soc_init_card(socdev);
        if (ret < 0) {
                printk(KERN_ERR "ad1980: failed to register card\n");
index 81300d8d42ca2a5f2648665fec39aef5490c0f37..f17c363cb1dbe60692402bffa1ce44a18ff81eb8 100644 (file)
@@ -155,21 +155,6 @@ static const struct snd_kcontrol_new ak4535_snd_controls[] = {
        SOC_SINGLE("Mic Sidetone Volume", AK4535_VOL, 4, 7, 0),
 };
 
-/* add non dapm controls */
-static int ak4535_add_controls(struct snd_soc_codec *codec)
-{
-       int err, i;
-
-       for (i = 0; i < ARRAY_SIZE(ak4535_snd_controls); i++) {
-               err = snd_ctl_add(codec->card,
-                       snd_soc_cnew(&ak4535_snd_controls[i], codec, NULL));
-               if (err < 0)
-                       return err;
-       }
-
-       return 0;
-}
-
 /* Mono 1 Mixer */
 static const struct snd_kcontrol_new ak4535_mono1_mixer_controls[] = {
        SOC_DAPM_SINGLE("Mic Sidetone Switch", AK4535_SIG1, 4, 1, 0),
@@ -510,7 +495,8 @@ static int ak4535_init(struct snd_soc_device *socdev)
        /* power on device */
        ak4535_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
 
-       ak4535_add_controls(codec);
+       snd_soc_add_controls(codec, ak4535_snd_controls,
+                               ARRAY_SIZE(ak4535_snd_controls));
        ak4535_add_widgets(codec);
        ret = snd_soc_init_card(socdev);
        if (ret < 0) {
index cac373616768b67f8accb9ddad91fb1805bb3876..ec7fe3b7b0cb468c885e8c6a3b9068d16ab89274 100644 (file)
@@ -151,21 +151,6 @@ SOC_ENUM("Capture Source", ssm2602_enum[0]),
 SOC_ENUM("Playback De-emphasis", ssm2602_enum[1]),
 };
 
-/* add non dapm controls */
-static int ssm2602_add_controls(struct snd_soc_codec *codec)
-{
-       int err, i;
-
-       for (i = 0; i < ARRAY_SIZE(ssm2602_snd_controls); i++) {
-               err = snd_ctl_add(codec->card,
-                       snd_soc_cnew(&ssm2602_snd_controls[i], codec, NULL));
-               if (err < 0)
-                       return err;
-       }
-
-       return 0;
-}
-
 /* Output Mixer */
 static const struct snd_kcontrol_new ssm2602_output_mixer_controls[] = {
 SOC_DAPM_SINGLE("Line Bypass Switch", SSM2602_APANA, 3, 1, 0),
@@ -622,7 +607,8 @@ static int ssm2602_init(struct snd_soc_device *socdev)
                        APANA_ENABLE_MIC_BOOST);
        ssm2602_write(codec, SSM2602_PWR, 0);
 
-       ssm2602_add_controls(codec);
+       snd_soc_add_controls(codec, ssm2602_snd_controls,
+                               ARRAY_SIZE(ssm2602_snd_controls));
        ssm2602_add_widgets(codec);
        ret = snd_soc_init_card(socdev);
        if (ret < 0) {
index cfdea007c4cb3ca7536ca14a7527363af7b849ee..a0e47c1dcd641afc43678cd454b5691729db7d24 100644 (file)
@@ -183,24 +183,6 @@ static const struct snd_kcontrol_new tlv320aic23_snd_controls[] = {
        SOC_ENUM("Playback De-emphasis", tlv320aic23_deemph),
 };
 
-/* add non dapm controls */
-static int tlv320aic23_add_controls(struct snd_soc_codec *codec)
-{
-
-       int err, i;
-
-       for (i = 0; i < ARRAY_SIZE(tlv320aic23_snd_controls); i++) {
-               err = snd_ctl_add(codec->card,
-                                 snd_soc_cnew(&tlv320aic23_snd_controls[i],
-                                              codec, NULL));
-               if (err < 0)
-                       return err;
-       }
-
-       return 0;
-
-}
-
 /* PGA Mixer controls for Line and Mic switch */
 static const struct snd_kcontrol_new tlv320aic23_output_mixer_controls[] = {
        SOC_DAPM_SINGLE("Line Bypass Switch", TLV320AIC23_ANLG, 3, 1, 0),
@@ -718,7 +700,8 @@ static int tlv320aic23_init(struct snd_soc_device *socdev)
 
        tlv320aic23_write(codec, TLV320AIC23_ACTIVE, 0x1);
 
-       tlv320aic23_add_controls(codec);
+       snd_soc_add_controls(codec, tlv320aic23_snd_controls,
+                               ARRAY_SIZE(tlv320aic23_snd_controls));
        tlv320aic23_add_widgets(codec);
        ret = snd_soc_init_card(socdev);
        if (ret < 0) {
index b47a749c5ea2aecaf08e92fe0eb03b9538ad14f2..36ab0198ca3fc9ecb4b98912307b4872d419cf14 100644 (file)
@@ -311,22 +311,6 @@ static const struct snd_kcontrol_new aic3x_snd_controls[] = {
        SOC_ENUM("ADC HPF Cut-off", aic3x_enum[ADC_HPF_ENUM]),
 };
 
-/* add non dapm controls */
-static int aic3x_add_controls(struct snd_soc_codec *codec)
-{
-       int err, i;
-
-       for (i = 0; i < ARRAY_SIZE(aic3x_snd_controls); i++) {
-               err = snd_ctl_add(codec->card,
-                                 snd_soc_cnew(&aic3x_snd_controls[i],
-                                              codec, NULL));
-               if (err < 0)
-                       return err;
-       }
-
-       return 0;
-}
-
 /* Left DAC Mux */
 static const struct snd_kcontrol_new aic3x_left_dac_mux_controls =
 SOC_DAPM_ENUM("Route", aic3x_enum[LDAC_ENUM]);
@@ -1224,7 +1208,8 @@ static int aic3x_init(struct snd_soc_device *socdev)
        aic3x_write(codec, AIC3X_GPIO1_REG, (setup->gpio_func[0] & 0xf) << 4);
        aic3x_write(codec, AIC3X_GPIO2_REG, (setup->gpio_func[1] & 0xf) << 4);
 
-       aic3x_add_controls(codec);
+       snd_soc_add_controls(codec, aic3x_snd_controls,
+                               ARRAY_SIZE(aic3x_snd_controls));
        aic3x_add_widgets(codec);
        ret = snd_soc_init_card(socdev);
        if (ret < 0) {
index ea370a4f86d5a2c74d97b176d977a55947ab120e..ddc9f37d863fdf99de849ae8038aaf12e231d231 100644 (file)
@@ -670,22 +670,6 @@ static const struct snd_kcontrol_new twl4030_snd_controls[] = {
                0, 3, 5, 0, input_gain_tlv),
 };
 
-/* add non dapm controls */
-static int twl4030_add_controls(struct snd_soc_codec *codec)
-{
-       int err, i;
-
-       for (i = 0; i < ARRAY_SIZE(twl4030_snd_controls); i++) {
-               err = snd_ctl_add(codec->card,
-                                 snd_soc_cnew(&twl4030_snd_controls[i],
-                                               codec, NULL));
-               if (err < 0)
-                       return err;
-       }
-
-       return 0;
-}
-
 static const struct snd_soc_dapm_widget twl4030_dapm_widgets[] = {
        /* Left channel inputs */
        SND_SOC_DAPM_INPUT("MAINMIC"),
@@ -1233,7 +1217,8 @@ static int twl4030_init(struct snd_soc_device *socdev)
        /* power on device */
        twl4030_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
 
-       twl4030_add_controls(codec);
+       snd_soc_add_controls(codec, twl4030_snd_controls,
+                               ARRAY_SIZE(twl4030_snd_controls));
        twl4030_add_widgets(codec);
 
        ret = snd_soc_init_card(socdev);
index a2c5064a774b12b1f65d16e92f88366f9facc26e..277825d155a6be37a8bdc4491633b3af30733e37 100644 (file)
@@ -431,39 +431,6 @@ SOC_ENUM("PCM Playback De-emphasis", uda134x_mixer_enum[1]),
 SOC_SINGLE("DC Filter Enable Switch", UDA134X_STATUS0, 0, 1, 0),
 };
 
-static int uda134x_add_controls(struct snd_soc_codec *codec)
-{
-       int err, i, n;
-       const struct snd_kcontrol_new *ctrls;
-       struct uda134x_platform_data *pd = codec->control_data;
-
-       switch (pd->model) {
-       case UDA134X_UDA1340:
-       case UDA134X_UDA1344:
-               n = ARRAY_SIZE(uda1340_snd_controls);
-               ctrls = uda1340_snd_controls;
-               break;
-       case UDA134X_UDA1341:
-               n = ARRAY_SIZE(uda1341_snd_controls);
-               ctrls = uda1341_snd_controls;
-               break;
-       default:
-               printk(KERN_ERR "%s unkown codec type: %d",
-                      __func__, pd->model);
-               return -EINVAL;
-       }
-
-       for (i = 0; i < n; i++) {
-               err = snd_ctl_add(codec->card,
-                                 snd_soc_cnew(&ctrls[i],
-                                              codec, NULL));
-               if (err < 0)
-                       return err;
-       }
-
-       return 0;
-}
-
 struct snd_soc_dai uda134x_dai = {
        .name = "UDA134X",
        /* playback capabilities */
@@ -572,7 +539,22 @@ static int uda134x_soc_probe(struct platform_device *pdev)
                goto pcm_err;
        }
 
-       ret = uda134x_add_controls(codec);
+       switch (pd->model) {
+       case UDA134X_UDA1340:
+       case UDA134X_UDA1344:
+               ret = snd_soc_add_controls(codec, uda1340_snd_controls,
+                                       ARRAY_SIZE(uda1340_snd_controls));
+       break;
+       case UDA134X_UDA1341:
+               ret = snd_soc_add_controls(codec, uda1341_snd_controls,
+                                       ARRAY_SIZE(uda1341_snd_controls));
+       break;
+       default:
+               printk(KERN_ERR "%s unkown codec type: %d",
+                       __func__, pd->model);
+       return -EINVAL;
+       }
+
        if (ret < 0) {
                printk(KERN_ERR "UDA134X: failed to register controls\n");
                goto pcm_err;
index e6bf0844fbf3e4dd770b293bf01673f1be78c86d..a957b4365b9d6af0050fa5c4f5eb50238dbfc8cd 100644 (file)
@@ -271,21 +271,6 @@ static const struct snd_kcontrol_new uda1380_snd_controls[] = {
        SOC_SINGLE("AGC Switch", UDA1380_AGC, 0, 1, 0),
 };
 
-/* add non dapm controls */
-static int uda1380_add_controls(struct snd_soc_codec *codec)
-{
-       int err, i;
-
-       for (i = 0; i < ARRAY_SIZE(uda1380_snd_controls); i++) {
-               err = snd_ctl_add(codec->card,
-                       snd_soc_cnew(&uda1380_snd_controls[i], codec, NULL));
-               if (err < 0)
-                       return err;
-       }
-
-       return 0;
-}
-
 /* Input mux */
 static const struct snd_kcontrol_new uda1380_input_mux_control =
        SOC_DAPM_ENUM("Route", uda1380_input_sel_enum);
@@ -675,7 +660,8 @@ static int uda1380_init(struct snd_soc_device *socdev, int dac_clk)
        }
 
        /* uda1380 init */
-       uda1380_add_controls(codec);
+       snd_soc_add_controls(codec, uda1380_snd_controls,
+                               ARRAY_SIZE(uda1380_snd_controls));
        uda1380_add_widgets(codec);
        ret = snd_soc_init_card(socdev);
        if (ret < 0) {
index e3989d406f54d17bb861da78e32d691ba541c1cc..2e0db29b4998b5de4f4273659de18280fad36b35 100644 (file)
@@ -51,10 +51,17 @@ struct wm8350_output {
        u16 mute;
 };
 
+struct wm8350_jack_data {
+       struct snd_soc_jack *jack;
+       int report;
+};
+
 struct wm8350_data {
        struct snd_soc_codec codec;
        struct wm8350_output out1;
        struct wm8350_output out2;
+       struct wm8350_jack_data hpl;
+       struct wm8350_jack_data hpr;
        struct regulator_bulk_data supplies[ARRAY_SIZE(supply_names)];
 };
 
@@ -775,21 +782,6 @@ static const struct snd_soc_dapm_route audio_map[] = {
        {"Beep", NULL, "IN3R PGA"},
 };
 
-static int wm8350_add_controls(struct snd_soc_codec *codec)
-{
-       int err, i;
-
-       for (i = 0; i < ARRAY_SIZE(wm8350_snd_controls); i++) {
-               err = snd_ctl_add(codec->card,
-                                 snd_soc_cnew(&wm8350_snd_controls[i],
-                                              codec, NULL));
-               if (err < 0)
-                       return err;
-       }
-
-       return 0;
-}
-
 static int wm8350_add_widgets(struct snd_soc_codec *codec)
 {
        int ret;
@@ -1328,6 +1320,95 @@ static int wm8350_resume(struct platform_device *pdev)
        return 0;
 }
 
+static void wm8350_hp_jack_handler(struct wm8350 *wm8350, int irq, void *data)
+{
+       struct wm8350_data *priv = data;
+       u16 reg;
+       int report;
+       int mask;
+       struct wm8350_jack_data *jack = NULL;
+
+       switch (irq) {
+       case WM8350_IRQ_CODEC_JCK_DET_L:
+               jack = &priv->hpl;
+               mask = WM8350_JACK_L_LVL;
+               break;
+
+       case WM8350_IRQ_CODEC_JCK_DET_R:
+               jack = &priv->hpr;
+               mask = WM8350_JACK_R_LVL;
+               break;
+
+       default:
+               BUG();
+       }
+
+       if (!jack->jack) {
+               dev_warn(wm8350->dev, "Jack interrupt called with no jack\n");
+               return;
+       }
+
+       /* Debounce */
+       msleep(200);
+
+       reg = wm8350_reg_read(wm8350, WM8350_JACK_PIN_STATUS);
+       if (reg & mask)
+               report = jack->report;
+       else
+               report = 0;
+
+       snd_soc_jack_report(jack->jack, report, jack->report);
+}
+
+/**
+ * wm8350_hp_jack_detect - Enable headphone jack detection.
+ *
+ * @codec:  WM8350 codec
+ * @which:  left or right jack detect signal
+ * @jack:   jack to report detection events on
+ * @report: value to report
+ *
+ * Enables the headphone jack detection of the WM8350.
+ */
+int wm8350_hp_jack_detect(struct snd_soc_codec *codec, enum wm8350_jack which,
+                         struct snd_soc_jack *jack, int report)
+{
+       struct wm8350_data *priv = codec->private_data;
+       struct wm8350 *wm8350 = codec->control_data;
+       int irq;
+       int ena;
+
+       switch (which) {
+       case WM8350_JDL:
+               priv->hpl.jack = jack;
+               priv->hpl.report = report;
+               irq = WM8350_IRQ_CODEC_JCK_DET_L;
+               ena = WM8350_JDL_ENA;
+               break;
+
+       case WM8350_JDR:
+               priv->hpr.jack = jack;
+               priv->hpr.report = report;
+               irq = WM8350_IRQ_CODEC_JCK_DET_R;
+               ena = WM8350_JDR_ENA;
+               break;
+
+       default:
+               return -EINVAL;
+       }
+
+       wm8350_set_bits(wm8350, WM8350_POWER_MGMT_4, WM8350_TOCLK_ENA);
+       wm8350_set_bits(wm8350, WM8350_JACK_DETECT, ena);
+
+       /* Sync status */
+       wm8350_hp_jack_handler(wm8350, irq, priv);
+
+       wm8350_unmask_irq(wm8350, irq);
+
+       return 0;
+}
+EXPORT_SYMBOL_GPL(wm8350_hp_jack_detect);
+
 static struct snd_soc_codec *wm8350_codec;
 
 static int wm8350_probe(struct platform_device *pdev)
@@ -1381,13 +1462,21 @@ static int wm8350_probe(struct platform_device *pdev)
        wm8350_set_bits(wm8350, WM8350_ROUT2_VOLUME,
                        WM8350_OUT2_VU | WM8350_OUT2R_MUTE);
 
+       wm8350_mask_irq(wm8350, WM8350_IRQ_CODEC_JCK_DET_L);
+       wm8350_mask_irq(wm8350, WM8350_IRQ_CODEC_JCK_DET_R);
+       wm8350_register_irq(wm8350, WM8350_IRQ_CODEC_JCK_DET_L,
+                           wm8350_hp_jack_handler, priv);
+       wm8350_register_irq(wm8350, WM8350_IRQ_CODEC_JCK_DET_R,
+                           wm8350_hp_jack_handler, priv);
+
        ret = snd_soc_new_pcms(socdev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1);
        if (ret < 0) {
                dev_err(&pdev->dev, "failed to create pcms\n");
                return ret;
        }
 
-       wm8350_add_controls(codec);
+       snd_soc_add_controls(codec, wm8350_snd_controls,
+                               ARRAY_SIZE(wm8350_snd_controls));
        wm8350_add_widgets(codec);
 
        wm8350_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
@@ -1411,8 +1500,21 @@ static int wm8350_remove(struct platform_device *pdev)
        struct snd_soc_device *socdev = platform_get_drvdata(pdev);
        struct snd_soc_codec *codec = socdev->codec;
        struct wm8350 *wm8350 = codec->control_data;
+       struct wm8350_data *priv = codec->private_data;
        int ret;
 
+       wm8350_clear_bits(wm8350, WM8350_JACK_DETECT,
+                         WM8350_JDL_ENA | WM8350_JDR_ENA);
+       wm8350_clear_bits(wm8350, WM8350_POWER_MGMT_4, WM8350_TOCLK_ENA);
+
+       wm8350_mask_irq(wm8350, WM8350_IRQ_CODEC_JCK_DET_L);
+       wm8350_mask_irq(wm8350, WM8350_IRQ_CODEC_JCK_DET_R);
+       wm8350_free_irq(wm8350, WM8350_IRQ_CODEC_JCK_DET_L);
+       wm8350_free_irq(wm8350, WM8350_IRQ_CODEC_JCK_DET_R);
+
+       priv->hpl.jack = NULL;
+       priv->hpr.jack = NULL;
+
        /* cancel any work waiting to be queued. */
        ret = cancel_delayed_work(&codec->delayed_work);
 
index cc2887aa6c38ff0508e9a90d79c6489e1f31927e..d11bd9288cf9170761e735e2ff4094c52353a437 100644 (file)
 extern struct snd_soc_dai wm8350_dai;
 extern struct snd_soc_codec_device soc_codec_dev_wm8350;
 
+enum wm8350_jack {
+       WM8350_JDL = 1,
+       WM8350_JDR = 2,
+};
+
+int wm8350_hp_jack_detect(struct snd_soc_codec *codec, enum wm8350_jack which,
+                         struct snd_soc_jack *jack, int report);
+
 #endif
index 40f8238df7174d65b6c306fbd23e51d505a85d1c..abe7cce8771437ef838faeeb6f780722fd5fa94f 100644 (file)
@@ -171,22 +171,6 @@ SOC_SINGLE("Capture Boost(+20dB)", WM8510_ADCBOOST,  8, 1, 0),
 SOC_SINGLE("Mono Playback Switch", WM8510_MONOMIX, 6, 1, 1),
 };
 
-/* add non dapm controls */
-static int wm8510_add_controls(struct snd_soc_codec *codec)
-{
-       int err, i;
-
-       for (i = 0; i < ARRAY_SIZE(wm8510_snd_controls); i++) {
-               err = snd_ctl_add(codec->card,
-                               snd_soc_cnew(&wm8510_snd_controls[i], codec,
-                                       NULL));
-               if (err < 0)
-                       return err;
-       }
-
-       return 0;
-}
-
 /* Speaker Output Mixer */
 static const struct snd_kcontrol_new wm8510_speaker_mixer_controls[] = {
 SOC_DAPM_SINGLE("Line Bypass Switch", WM8510_SPKMIX, 1, 1, 0),
@@ -656,7 +640,8 @@ static int wm8510_init(struct snd_soc_device *socdev)
        /* power on device */
        codec->bias_level = SND_SOC_BIAS_OFF;
        wm8510_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
-       wm8510_add_controls(codec);
+       snd_soc_add_controls(codec, wm8510_snd_controls,
+                               ARRAY_SIZE(wm8510_snd_controls));
        wm8510_add_widgets(codec);
        ret = snd_soc_init_card(socdev);
        if (ret < 0) {
index d004e5845298d2b8e6f4871629b75c78702e8724..9b75a377453eec0270e6c6654ff3e6521ecf6d5e 100644 (file)
@@ -330,20 +330,6 @@ SOC_DOUBLE("ADC Mute Switch", WM8580_ADC_CONTROL1, 0, 1, 1, 0),
 SOC_SINGLE("ADC High-Pass Filter Switch", WM8580_ADC_CONTROL1, 4, 1, 0),
 };
 
-/* Add non-DAPM controls */
-static int wm8580_add_controls(struct snd_soc_codec *codec)
-{
-       int err, i;
-
-       for (i = 0; i < ARRAY_SIZE(wm8580_snd_controls); i++) {
-               err = snd_ctl_add(codec->card,
-                                 snd_soc_cnew(&wm8580_snd_controls[i],
-                                              codec, NULL));
-               if (err < 0)
-                       return err;
-       }
-       return 0;
-}
 static const struct snd_soc_dapm_widget wm8580_dapm_widgets[] = {
 SND_SOC_DAPM_DAC("DAC1", "Playback", WM8580_PWRDN1, 2, 1),
 SND_SOC_DAPM_DAC("DAC2", "Playback", WM8580_PWRDN1, 3, 1),
@@ -866,7 +852,8 @@ static int wm8580_init(struct snd_soc_device *socdev)
                goto pcm_err;
        }
 
-       wm8580_add_controls(codec);
+       snd_soc_add_controls(codec, wm8580_snd_controls,
+                               ARRAY_SIZE(wm8580_snd_controls));
        wm8580_add_widgets(codec);
 
        ret = snd_soc_init_card(socdev);
index 80b11983e1373802639f1f4a37ca25c2c2d4dfc6..defa310bc7d9fd4e2e3f90a8834ff39bcca44028 100644 (file)
@@ -92,21 +92,6 @@ SOC_DOUBLE_R_TLV("Digital Playback Volume", WM8728_DACLVOL, WM8728_DACRVOL,
 SOC_SINGLE("Deemphasis", WM8728_DACCTL, 1, 1, 0),
 };
 
-static int wm8728_add_controls(struct snd_soc_codec *codec)
-{
-       int err, i;
-
-       for (i = 0; i < ARRAY_SIZE(wm8728_snd_controls); i++) {
-               err = snd_ctl_add(codec->card,
-                                 snd_soc_cnew(&wm8728_snd_controls[i],
-                                               codec, NULL));
-               if (err < 0)
-                       return err;
-       }
-
-       return 0;
-}
-
 /*
  * DAPM controls.
  */
@@ -330,7 +315,8 @@ static int wm8728_init(struct snd_soc_device *socdev)
        /* power on device */
        wm8728_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
 
-       wm8728_add_controls(codec);
+       snd_soc_add_controls(codec, wm8728_snd_controls,
+                               ARRAY_SIZE(wm8728_snd_controls));
        wm8728_add_widgets(codec);
        ret = snd_soc_init_card(socdev);
        if (ret < 0) {
index c444b9f2701ed7625baa550cc0030bf35d6d6712..96d6e1aeaf430f0c99bb804e4b7d4ea1eb0bc2fc 100644 (file)
@@ -129,22 +129,6 @@ SOC_SINGLE("Store DC Offset Switch", WM8731_APDIGI, 4, 1, 0),
 SOC_ENUM("Playback De-emphasis", wm8731_enum[1]),
 };
 
-/* add non dapm controls */
-static int wm8731_add_controls(struct snd_soc_codec *codec)
-{
-       int err, i;
-
-       for (i = 0; i < ARRAY_SIZE(wm8731_snd_controls); i++) {
-               err = snd_ctl_add(codec->card,
-                                 snd_soc_cnew(&wm8731_snd_controls[i],
-                                               codec, NULL));
-               if (err < 0)
-                       return err;
-       }
-
-       return 0;
-}
-
 /* Output Mixer */
 static const struct snd_kcontrol_new wm8731_output_mixer_controls[] = {
 SOC_DAPM_SINGLE("Line Bypass Switch", WM8731_APANA, 3, 1, 0),
@@ -543,7 +527,8 @@ static int wm8731_init(struct snd_soc_device *socdev)
        reg = wm8731_read_reg_cache(codec, WM8731_RINVOL);
        wm8731_write(codec, WM8731_RINVOL, reg & ~0x0100);
 
-       wm8731_add_controls(codec);
+       snd_soc_add_controls(codec, wm8731_snd_controls,
+                               ARRAY_SIZE(wm8731_snd_controls));
        wm8731_add_widgets(codec);
        ret = snd_soc_init_card(socdev);
        if (ret < 0) {
index 5997fa68e0d58f08c1fb5af468e953bbb2a64ded..1578569793a23591331fed85e5e0f65c7c7cda0e 100644 (file)
@@ -231,21 +231,6 @@ SOC_SINGLE("Mono Playback Volume", WM8750_MOUTV, 0, 127, 0),
 
 };
 
-/* add non dapm controls */
-static int wm8750_add_controls(struct snd_soc_codec *codec)
-{
-       int err, i;
-
-       for (i = 0; i < ARRAY_SIZE(wm8750_snd_controls); i++) {
-               err = snd_ctl_add(codec->card,
-                               snd_soc_cnew(&wm8750_snd_controls[i],
-                                               codec, NULL));
-               if (err < 0)
-                       return err;
-       }
-       return 0;
-}
-
 /*
  * DAPM Controls
  */
@@ -816,7 +801,8 @@ static int wm8750_init(struct snd_soc_device *socdev)
        reg = wm8750_read_reg_cache(codec, WM8750_RINVOL);
        wm8750_write(codec, WM8750_RINVOL, reg | 0x0100);
 
-       wm8750_add_controls(codec);
+       snd_soc_add_controls(codec, wm8750_snd_controls,
+                               ARRAY_SIZE(wm8750_snd_controls));
        wm8750_add_widgets(codec);
        ret = snd_soc_init_card(socdev);
        if (ret < 0) {
index 6c21b50c93758f28c7aea6d59ae396405d232530..7283178e0eb52adf71eb86b46e1d4a05c27387df 100644 (file)
@@ -339,21 +339,6 @@ SOC_ENUM("ADC Data Select", wm8753_enum[27]),
 SOC_ENUM("ROUT2 Phase", wm8753_enum[28]),
 };
 
-/* add non dapm controls */
-static int wm8753_add_controls(struct snd_soc_codec *codec)
-{
-       int err, i;
-
-       for (i = 0; i < ARRAY_SIZE(wm8753_snd_controls); i++) {
-               err = snd_ctl_add(codec->card,
-                               snd_soc_cnew(&wm8753_snd_controls[i],
-                                               codec, NULL));
-               if (err < 0)
-                       return err;
-       }
-       return 0;
-}
-
 /*
  * _DAPM_ Controls
  */
@@ -1603,7 +1588,8 @@ static int wm8753_init(struct snd_soc_device *socdev)
        reg = wm8753_read_reg_cache(codec, WM8753_RINVOL);
        wm8753_write(codec, WM8753_RINVOL, reg | 0x0100);
 
-       wm8753_add_controls(codec);
+       snd_soc_add_controls(codec, wm8753_snd_controls,
+                               ARRAY_SIZE(wm8753_snd_controls));
        wm8753_add_widgets(codec);
        ret = snd_soc_init_card(socdev);
        if (ret < 0) {
index 6767de10ded0a64ff008e7c1d039d42569b9a4e4..1e08d4f065f2261dd0f4b2a4e4f197999423c5ca 100644 (file)
@@ -517,22 +517,6 @@ SOC_SINGLE("LINEOUT2 LP -12dB", WM8900_REG_LOUTMIXCTL1,
 
 };
 
-/* add non dapm controls */
-static int wm8900_add_controls(struct snd_soc_codec *codec)
-{
-       int err, i;
-
-       for (i = 0; i < ARRAY_SIZE(wm8900_snd_controls); i++) {
-               err = snd_ctl_add(codec->card,
-                                 snd_soc_cnew(&wm8900_snd_controls[i],
-                                              codec, NULL));
-               if (err < 0)
-                       return err;
-       }
-
-       return 0;
-}
-
 static const struct snd_kcontrol_new wm8900_dapm_loutput2_control =
 SOC_DAPM_SINGLE("LINEOUT2L Switch", WM8900_REG_POWER3, 6, 1, 0);
 
@@ -1439,7 +1423,8 @@ static int wm8900_probe(struct platform_device *pdev)
                goto pcm_err;
        }
 
-       wm8900_add_controls(codec);
+       snd_soc_add_controls(codec, wm8900_snd_controls,
+                               ARRAY_SIZE(wm8900_snd_controls));
        wm8900_add_widgets(codec);
 
        ret = snd_soc_init_card(socdev);
index bde74546db4a7050ee28e1617dc190390822fcc1..6ff34b957dceea134130940b9972643b0ed4c78d 100644 (file)
@@ -744,21 +744,6 @@ SOC_DOUBLE_R_TLV("Speaker Volume",
                 0, 63, 0, out_tlv),
 };
 
-static int wm8903_add_controls(struct snd_soc_codec *codec)
-{
-       int err, i;
-
-       for (i = 0; i < ARRAY_SIZE(wm8903_snd_controls); i++) {
-               err = snd_ctl_add(codec->card,
-                                 snd_soc_cnew(&wm8903_snd_controls[i],
-                                              codec, NULL));
-               if (err < 0)
-                       return err;
-       }
-
-       return 0;
-}
-
 static const struct snd_kcontrol_new linput_mode_mux =
        SOC_DAPM_ENUM("Left Input Mode Mux", linput_mode_enum);
 
@@ -1737,7 +1722,8 @@ static int wm8903_probe(struct platform_device *pdev)
                goto err;
        }
 
-       wm8903_add_controls(socdev->codec);
+       snd_soc_add_controls(socdev->codec, wm8903_snd_controls,
+                               ARRAY_SIZE(wm8903_snd_controls));
        wm8903_add_widgets(socdev->codec);
 
        ret = snd_soc_init_card(socdev);
index 88ead7f8dd98007cf4b909a06b30679ff65721d3..c8bd9b06f330f02dd645dc2f17944824117bff8e 100644 (file)
@@ -195,21 +195,6 @@ static const struct snd_kcontrol_new wm8971_snd_controls[] = {
        SOC_DOUBLE_R("Mic Boost", WM8971_LADCIN, WM8971_RADCIN, 4, 3, 0),
 };
 
-/* add non-DAPM controls */
-static int wm8971_add_controls(struct snd_soc_codec *codec)
-{
-       int err, i;
-
-       for (i = 0; i < ARRAY_SIZE(wm8971_snd_controls); i++) {
-               err = snd_ctl_add(codec->card,
-                               snd_soc_cnew(&wm8971_snd_controls[i],
-                                            codec, NULL));
-               if (err < 0)
-                       return err;
-       }
-       return 0;
-}
-
 /*
  * DAPM Controls
  */
@@ -745,7 +730,8 @@ static int wm8971_init(struct snd_soc_device *socdev)
        reg = wm8971_read_reg_cache(codec, WM8971_RINVOL);
        wm8971_write(codec, WM8971_RINVOL, reg | 0x0100);
 
-       wm8971_add_controls(codec);
+       snd_soc_add_controls(codec, wm8971_snd_controls,
+                               ARRAY_SIZE(wm8971_snd_controls));
        wm8971_add_widgets(codec);
        ret = snd_soc_init_card(socdev);
        if (ret < 0) {
index 5b5afc14447817ede31e23807af451f4def6e989..6b2778632d5ee5d57349d199b9864b6022a5e260 100644 (file)
@@ -417,21 +417,6 @@ SOC_SINGLE("RIN34 Mute Switch", WM8990_RIGHT_LINE_INPUT_3_4_VOLUME,
 
 };
 
-/* add non dapm controls */
-static int wm8990_add_controls(struct snd_soc_codec *codec)
-{
-       int err, i;
-
-       for (i = 0; i < ARRAY_SIZE(wm8990_snd_controls); i++) {
-               err = snd_ctl_add(codec->card,
-                               snd_soc_cnew(&wm8990_snd_controls[i], codec,
-                                       NULL));
-               if (err < 0)
-                       return err;
-       }
-       return 0;
-}
-
 /*
  * _DAPM_ Controls
  */
@@ -1460,7 +1445,8 @@ static int wm8990_init(struct snd_soc_device *socdev)
        wm8990_write(codec, WM8990_LEFT_OUTPUT_VOLUME, 0x50 | (1<<8));
        wm8990_write(codec, WM8990_RIGHT_OUTPUT_VOLUME, 0x50 | (1<<8));
 
-       wm8990_add_controls(codec);
+       snd_soc_add_controls(codec, wm8990_snd_controls,
+                               ARRAY_SIZE(wm8990_snd_controls));
        wm8990_add_widgets(codec);
        ret = snd_soc_init_card(socdev);
        if (ret < 0) {
index af83d629078a47f2dbaac75d6d47036086b57ce1..1b0ace0f4dcafc2b64e4cf955a98fa7bb5782e6e 100644 (file)
@@ -154,21 +154,6 @@ SOC_SINGLE("Mic 2 Volume", AC97_MIC, 0, 31, 1),
 SOC_SINGLE("Mic 20dB Boost Switch", AC97_MIC, 7, 1, 0),
 };
 
-/* add non dapm controls */
-static int wm9712_add_controls(struct snd_soc_codec *codec)
-{
-       int err, i;
-
-       for (i = 0; i < ARRAY_SIZE(wm9712_snd_ac97_controls); i++) {
-               err = snd_ctl_add(codec->card,
-                                 snd_soc_cnew(&wm9712_snd_ac97_controls[i],
-                                              codec, NULL));
-               if (err < 0)
-                       return err;
-       }
-       return 0;
-}
-
 /* We have to create a fake left and right HP mixers because
  * the codec only has a single control that is shared by both channels.
  * This makes it impossible to determine the audio path.
@@ -698,7 +683,8 @@ static int wm9712_soc_probe(struct platform_device *pdev)
        ac97_write(codec, AC97_VIDEO, ac97_read(codec, AC97_VIDEO) | 0x3000);
 
        wm9712_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
-       wm9712_add_controls(codec);
+       snd_soc_add_controls(codec, wm9712_snd_ac97_controls,
+                               ARRAY_SIZE(wm9712_snd_ac97_controls));
        wm9712_add_widgets(codec);
        ret = snd_soc_init_card(socdev);
        if (ret < 0) {
index f3ca8aaf013944a33a9516f9c7fd9bbc690e17f0..a45622620db7d85b4426f7452c1657021f4a633b 100644 (file)
@@ -190,21 +190,6 @@ SOC_SINGLE("3D Lower Cut-off Switch", AC97_REC_GAIN_MIC, 4, 1, 0),
 SOC_SINGLE("3D Depth", AC97_REC_GAIN_MIC, 0, 15, 1),
 };
 
-/* add non dapm controls */
-static int wm9713_add_controls(struct snd_soc_codec *codec)
-{
-       int err, i;
-
-       for (i = 0; i < ARRAY_SIZE(wm9713_snd_ac97_controls); i++) {
-               err = snd_ctl_add(codec->card,
-                               snd_soc_cnew(&wm9713_snd_ac97_controls[i],
-                                       codec, NULL));
-               if (err < 0)
-                       return err;
-       }
-       return 0;
-}
-
 /* We have to create a fake left and right HP mixers because
  * the codec only has a single control that is shared by both channels.
  * This makes it impossible to determine the audio path using the current
@@ -1245,7 +1230,8 @@ static int wm9713_soc_probe(struct platform_device *pdev)
        reg = ac97_read(codec, AC97_CD) & 0x7fff;
        ac97_write(codec, AC97_CD, reg);
 
-       wm9713_add_controls(codec);
+       snd_soc_add_controls(codec, wm9713_snd_ac97_controls,
+                               ARRAY_SIZE(wm9713_snd_ac97_controls));
        wm9713_add_widgets(codec);
        ret = snd_soc_init_card(socdev);
        if (ret < 0)
index 55fdb4abb1796dc728dfedd147dc09eaf01298fc..8313d52a6e8cc42264b3f38d67e94645e071d27b 100644 (file)
@@ -1494,6 +1494,37 @@ struct snd_kcontrol *snd_soc_cnew(const struct snd_kcontrol_new *_template,
 }
 EXPORT_SYMBOL_GPL(snd_soc_cnew);
 
+/**
+ * snd_soc_add_controls - add an array of controls to a codec.
+ * Convienience function to add a list of controls. Many codecs were
+ * duplicating this code.
+ *
+ * @codec: codec to add controls to
+ * @controls: array of controls to add
+ * @num_controls: number of elements in the array
+ *
+ * Return 0 for success, else error.
+ */
+int snd_soc_add_controls(struct snd_soc_codec *codec,
+       const struct snd_kcontrol_new *controls, int num_controls)
+{
+       struct snd_card *card = codec->card;
+       int err, i;
+
+       for (i = 0; i < num_controls; i++) {
+               const struct snd_kcontrol_new *control = &controls[i];
+               err = snd_ctl_add(card, snd_soc_cnew(control, codec, NULL));
+               if (err < 0) {
+                       dev_err(codec->dev, "%s: Failed to add %s\n",
+                               codec->name, control->name);
+                       return err;
+               }
+       }
+
+       return 0;
+}
+EXPORT_SYMBOL_GPL(snd_soc_add_controls);
+
 /**
  * snd_soc_info_enum_double - enumerated double mixer info callback
  * @kcontrol: mixer control
index a2f1da8b464602fbac5ec55e86295d0f5fcda7ec..54b4564b82b4c9faa921a1132f5663c6d717a2a8 100644 (file)
 static int dapm_up_seq[] = {
        snd_soc_dapm_pre, snd_soc_dapm_micbias, snd_soc_dapm_mic,
        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
+       snd_soc_dapm_mixer, snd_soc_dapm_mixer_named_ctl, 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_value_mux,
-       snd_soc_dapm_post
+       snd_soc_dapm_pga, snd_soc_dapm_mixer_named_ctl, snd_soc_dapm_mixer,
+       snd_soc_dapm_dac, snd_soc_dapm_mic, snd_soc_dapm_micbias,
+       snd_soc_dapm_mux, snd_soc_dapm_value_mux, snd_soc_dapm_post
 };
 
 static int dapm_status = 1;
@@ -101,7 +102,8 @@ static void dapm_set_path_status(struct snd_soc_dapm_widget *w,
 {
        switch (w->id) {
        case snd_soc_dapm_switch:
-       case snd_soc_dapm_mixer: {
+       case snd_soc_dapm_mixer:
+       case snd_soc_dapm_mixer_named_ctl: {
                int val;
                struct soc_mixer_control *mc = (struct soc_mixer_control *)
                        w->kcontrols[i].private_value;
@@ -323,15 +325,33 @@ static int dapm_new_mixer(struct snd_soc_codec *codec,
                        if (path->name != (char*)w->kcontrols[i].name)
                                continue;
 
-                       /* add dapm control with long name */
-                       name_len = 2 + strlen(w->name)
-                               + strlen(w->kcontrols[i].name);
+                       /* add dapm control with long name.
+                        * for dapm_mixer this is the concatenation of the
+                        * mixer and kcontrol name.
+                        * for dapm_mixer_named_ctl this is simply the
+                        * kcontrol name.
+                        */
+                       name_len = strlen(w->kcontrols[i].name) + 1;
+                       if (w->id == snd_soc_dapm_mixer)
+                               name_len += 1 + strlen(w->name);
+
                        path->long_name = kmalloc(name_len, GFP_KERNEL);
+
                        if (path->long_name == NULL)
                                return -ENOMEM;
 
-                       snprintf(path->long_name, name_len, "%s %s",
-                                w->name, w->kcontrols[i].name);
+                       switch (w->id) {
+                       case snd_soc_dapm_mixer:
+                       default:
+                               snprintf(path->long_name, name_len, "%s %s",
+                                        w->name, w->kcontrols[i].name);
+                       break;
+                       case snd_soc_dapm_mixer_named_ctl:
+                               snprintf(path->long_name, name_len, "%s",
+                                        w->kcontrols[i].name);
+                       break;
+                       }
+
                        path->long_name[name_len - 1] = '\0';
 
                        path->kcontrol = snd_soc_cnew(&w->kcontrols[i], w,
@@ -687,6 +707,7 @@ static void dbg_dump_dapm(struct snd_soc_codec* codec, const char *action)
                case snd_soc_dapm_adc:
                case snd_soc_dapm_pga:
                case snd_soc_dapm_mixer:
+               case snd_soc_dapm_mixer_named_ctl:
                        if (w->name) {
                                in = is_connected_input_ep(w);
                                dapm_clear_walk(w->codec);
@@ -760,6 +781,7 @@ static int dapm_mixer_update_power(struct snd_soc_dapm_widget *widget,
        int found = 0;
 
        if (widget->id != snd_soc_dapm_mixer &&
+           widget->id != snd_soc_dapm_mixer_named_ctl &&
            widget->id != snd_soc_dapm_switch)
                return -ENODEV;
 
@@ -813,6 +835,7 @@ static ssize_t dapm_widget_show(struct device *dev,
                case snd_soc_dapm_adc:
                case snd_soc_dapm_pga:
                case snd_soc_dapm_mixer:
+               case snd_soc_dapm_mixer_named_ctl:
                        if (w->name)
                                count += sprintf(buf + count, "%s: %s\n",
                                        w->name, w->power ? "On":"Off");
@@ -876,7 +899,7 @@ static void dapm_free_widgets(struct snd_soc_codec *codec)
 }
 
 static int snd_soc_dapm_set_pin(struct snd_soc_codec *codec,
-       char *pin, int status)
+                               const char *pin, int status)
 {
        struct snd_soc_dapm_widget *w;
 
@@ -991,6 +1014,7 @@ static int snd_soc_dapm_add_route(struct snd_soc_codec *codec,
                break;
        case snd_soc_dapm_switch:
        case snd_soc_dapm_mixer:
+       case snd_soc_dapm_mixer_named_ctl:
                ret = dapm_connect_mixer(codec, wsource, wsink, path, control);
                if (ret != 0)
                        goto err;
@@ -1068,6 +1092,7 @@ int snd_soc_dapm_new_widgets(struct snd_soc_codec *codec)
                switch(w->id) {
                case snd_soc_dapm_switch:
                case snd_soc_dapm_mixer:
+               case snd_soc_dapm_mixer_named_ctl:
                        dapm_new_mixer(codec, w);
                        break;
                case snd_soc_dapm_mux:
@@ -1549,7 +1574,7 @@ int snd_soc_dapm_set_bias_level(struct snd_soc_device *socdev,
  * NOTE: snd_soc_dapm_sync() needs to be called after this for DAPM to
  * do any widget power switching.
  */
-int snd_soc_dapm_enable_pin(struct snd_soc_codec *codec, char *pin)
+int snd_soc_dapm_enable_pin(struct snd_soc_codec *codec, const char *pin)
 {
        return snd_soc_dapm_set_pin(codec, pin, 1);
 }
@@ -1564,7 +1589,7 @@ EXPORT_SYMBOL_GPL(snd_soc_dapm_enable_pin);
  * NOTE: snd_soc_dapm_sync() needs to be called after this for DAPM to
  * do any widget power switching.
  */
-int snd_soc_dapm_disable_pin(struct snd_soc_codec *codec, char *pin)
+int snd_soc_dapm_disable_pin(struct snd_soc_codec *codec, const char *pin)
 {
        return snd_soc_dapm_set_pin(codec, pin, 0);
 }
@@ -1584,7 +1609,7 @@ EXPORT_SYMBOL_GPL(snd_soc_dapm_disable_pin);
  * NOTE: snd_soc_dapm_sync() needs to be called after this for DAPM to
  * do any widget power switching.
  */
-int snd_soc_dapm_nc_pin(struct snd_soc_codec *codec, char *pin)
+int snd_soc_dapm_nc_pin(struct snd_soc_codec *codec, const char *pin)
 {
        return snd_soc_dapm_set_pin(codec, pin, 0);
 }
@@ -1599,7 +1624,7 @@ EXPORT_SYMBOL_GPL(snd_soc_dapm_nc_pin);
  *
  * Returns 1 for connected otherwise 0.
  */
-int snd_soc_dapm_get_pin_status(struct snd_soc_codec *codec, char *pin)
+int snd_soc_dapm_get_pin_status(struct snd_soc_codec *codec, const char *pin)
 {
        struct snd_soc_dapm_widget *w;
 
diff --git a/sound/soc/soc-jack.c b/sound/soc/soc-jack.c
new file mode 100644 (file)
index 0000000..8cc00c3
--- /dev/null
@@ -0,0 +1,138 @@
+/*
+ * soc-jack.c  --  ALSA SoC jack handling
+ *
+ * Copyright 2008 Wolfson Microelectronics PLC.
+ *
+ * Author: Mark Brown <broonie@opensource.wolfsonmicro.com>
+ *
+ *  This program is free software; you can redistribute  it and/or modify it
+ *  under  the terms of  the GNU General  Public License as published by the
+ *  Free Software Foundation;  either version 2 of the  License, or (at your
+ *  option) any later version.
+ */
+
+#include <sound/jack.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+
+/**
+ * snd_soc_jack_new - Create a new jack
+ * @card:  ASoC card
+ * @id:    an identifying string for this jack
+ * @type:  a bitmask of enum snd_jack_type values that can be detected by
+ *         this jack
+ * @jack:  structure to use for the jack
+ *
+ * Creates a new jack object.
+ *
+ * Returns zero if successful, or a negative error code on failure.
+ * On success jack will be initialised.
+ */
+int snd_soc_jack_new(struct snd_soc_card *card, const char *id, int type,
+                    struct snd_soc_jack *jack)
+{
+       jack->card = card;
+       INIT_LIST_HEAD(&jack->pins);
+
+       return snd_jack_new(card->socdev->codec->card, id, type, &jack->jack);
+}
+EXPORT_SYMBOL_GPL(snd_soc_jack_new);
+
+/**
+ * snd_soc_jack_report - Report the current status for a jack
+ *
+ * @jack:   the jack
+ * @status: a bitmask of enum snd_jack_type values that are currently detected.
+ * @mask:   a bitmask of enum snd_jack_type values that being reported.
+ *
+ * If configured using snd_soc_jack_add_pins() then the associated
+ * DAPM pins will be enabled or disabled as appropriate and DAPM
+ * synchronised.
+ *
+ * Note: This function uses mutexes and should be called from a
+ * context which can sleep (such as a workqueue).
+ */
+void snd_soc_jack_report(struct snd_soc_jack *jack, int status, int mask)
+{
+       struct snd_soc_codec *codec = jack->card->socdev->codec;
+       struct snd_soc_jack_pin *pin;
+       int enable;
+       int oldstatus;
+
+       if (!jack) {
+               WARN_ON_ONCE(!jack);
+               return;
+       }
+
+       mutex_lock(&codec->mutex);
+
+       oldstatus = jack->status;
+
+       jack->status &= ~mask;
+       jack->status |= status;
+
+       /* The DAPM sync is expensive enough to be worth skipping */
+       if (jack->status == oldstatus)
+               goto out;
+
+       list_for_each_entry(pin, &jack->pins, list) {
+               enable = pin->mask & status;
+
+               if (pin->invert)
+                       enable = !enable;
+
+               if (enable)
+                       snd_soc_dapm_enable_pin(codec, pin->pin);
+               else
+                       snd_soc_dapm_disable_pin(codec, pin->pin);
+       }
+
+       snd_soc_dapm_sync(codec);
+
+       snd_jack_report(jack->jack, status);
+
+out:
+       mutex_unlock(&codec->mutex);
+}
+EXPORT_SYMBOL_GPL(snd_soc_jack_report);
+
+/**
+ * snd_soc_jack_add_pins - Associate DAPM pins with an ASoC jack
+ *
+ * @jack:  ASoC jack
+ * @count: Number of pins
+ * @pins:  Array of pins
+ *
+ * After this function has been called the DAPM pins specified in the
+ * pins array will have their status updated to reflect the current
+ * state of the jack whenever the jack status is updated.
+ */
+int snd_soc_jack_add_pins(struct snd_soc_jack *jack, int count,
+                         struct snd_soc_jack_pin *pins)
+{
+       int i;
+
+       for (i = 0; i < count; i++) {
+               if (!pins[i].pin) {
+                       printk(KERN_ERR "No name for pin %d\n", i);
+                       return -EINVAL;
+               }
+               if (!pins[i].mask) {
+                       printk(KERN_ERR "No mask for pin %d (%s)\n", i,
+                              pins[i].pin);
+                       return -EINVAL;
+               }
+
+               INIT_LIST_HEAD(&pins[i].list);
+               list_add(&(pins[i].list), &jack->pins);
+       }
+
+       /* Update to reflect the last reported status; canned jack
+        * implementations are likely to set their state before the
+        * card has an opportunity to associate pins.
+        */
+       snd_soc_jack_report(jack, 0, 0);
+
+       return 0;
+}
+EXPORT_SYMBOL_GPL(snd_soc_jack_add_pins);