ASoC: qcom: apq8016-sbc: Add support to Headset JACK
[sfrench/cifs-2.6.git] / sound / soc / qcom / apq8016_sbc.c
index d084d746829988f5e76c3131e894e3e74e1ff786..d49adc822a110ed2a9b61f1916570ed70065a3fd 100644 (file)
 #include <linux/platform_device.h>
 #include <sound/pcm.h>
 #include <sound/pcm_params.h>
+#include <sound/jack.h>
 #include <sound/soc.h>
+#include <uapi/linux/input-event-codes.h>
 #include <dt-bindings/sound/apq8016-lpass.h>
 
 struct apq8016_sbc_data {
        void __iomem *mic_iomux;
        void __iomem *spkr_iomux;
+       struct snd_soc_jack jack;
+       bool jack_setup;
        struct snd_soc_dai_link dai_link[];     /* dynamically allocated */
 };
 
@@ -34,13 +38,16 @@ struct apq8016_sbc_data {
 #define MIC_CTRL_QUA_WS_SLAVE_SEL_10   BIT(17)
 #define MIC_CTRL_TLMM_SCLK_EN          BIT(1)
 #define        SPKR_CTL_PRI_WS_SLAVE_SEL_11    (BIT(17) | BIT(16))
+#define DEFAULT_MCLK_RATE              9600000
 
 static int apq8016_sbc_dai_init(struct snd_soc_pcm_runtime *rtd)
 {
        struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
+       struct snd_soc_codec *codec;
+       struct snd_soc_dai_link *dai_link = rtd->dai_link;
        struct snd_soc_card *card = rtd->card;
        struct apq8016_sbc_data *pdata = snd_soc_card_get_drvdata(card);
-       int rval = 0;
+       int i, rval;
 
        switch (cpu_dai->id) {
        case MI2S_PRIMARY:
@@ -63,12 +70,54 @@ static int apq8016_sbc_dai_init(struct snd_soc_pcm_runtime *rtd)
 
        default:
                dev_err(card->dev, "unsupported cpu dai configuration\n");
-               rval = -EINVAL;
-               break;
+               return -EINVAL;
+
+       }
+
+       if (!pdata->jack_setup) {
+               struct snd_jack *jack;
+
+               rval = snd_soc_card_jack_new(card, "Headset Jack",
+                                            SND_JACK_HEADSET |
+                                            SND_JACK_HEADPHONE |
+                                            SND_JACK_BTN_0 | SND_JACK_BTN_1 |
+                                            SND_JACK_BTN_2 | SND_JACK_BTN_3 |
+                                            SND_JACK_BTN_4,
+                                            &pdata->jack, NULL, 0);
+
+               if (rval < 0) {
+                       dev_err(card->dev, "Unable to add Headphone Jack\n");
+                       return rval;
+               }
 
+               jack = pdata->jack.jack;
+
+               snd_jack_set_key(jack, SND_JACK_BTN_0, KEY_MEDIA);
+               snd_jack_set_key(jack, SND_JACK_BTN_1, KEY_VOICECOMMAND);
+               snd_jack_set_key(jack, SND_JACK_BTN_2, KEY_VOLUMEUP);
+               snd_jack_set_key(jack, SND_JACK_BTN_3, KEY_VOLUMEDOWN);
+               pdata->jack_setup = true;
+       }
+
+       for (i = 0 ; i < dai_link->num_codecs; i++) {
+               struct snd_soc_dai *dai = rtd->codec_dais[i];
+
+               codec = dai->codec;
+               /* Set default mclk for internal codec */
+               rval = snd_soc_codec_set_sysclk(codec, 0, 0, DEFAULT_MCLK_RATE,
+                                      SND_SOC_CLOCK_IN);
+               if (rval != 0 && rval != -ENOTSUPP) {
+                       dev_warn(card->dev, "Failed to set mclk: %d\n", rval);
+                       return rval;
+               }
+               rval = snd_soc_codec_set_jack(codec, &pdata->jack, NULL);
+               if (rval != 0 && rval != -ENOTSUPP) {
+                       dev_warn(card->dev, "Failed to set jack: %d\n", rval);
+                       return rval;
+               }
        }
 
-       return rval;
+       return 0;
 }
 
 static struct apq8016_sbc_data *apq8016_sbc_parse_of(struct snd_soc_card *card)
@@ -191,7 +240,6 @@ static int apq8016_sbc_platform_probe(struct platform_device *pdev)
        if (IS_ERR(data->spkr_iomux))
                return PTR_ERR(data->spkr_iomux);
 
-       platform_set_drvdata(pdev, data);
        snd_soc_card_set_drvdata(card, data);
 
        return devm_snd_soc_register_card(&pdev->dev, card);