ASoC: Intel: bytcr_rt5640: Enable jack detection
authorHans de Goede <hdegoede@redhat.com>
Sun, 13 May 2018 07:24:28 +0000 (09:24 +0200)
committerMark Brown <broonie@kernel.org>
Thu, 17 May 2018 06:46:37 +0000 (15:46 +0900)
Add code to support setting jack-detect parameters through quirks and
extend the existing DMI quirk table entries for the Asus T100TA and the
Dell Venue 8 Pro 5830 to enable jack detection.

Tested-by: Hans de Goede <hdegoede@redhat.com>
Signed-off-by: Hans de Goede <hdegoede@redhat.com>
Acked-by: Pierre-Louis Bossart <pierre-louis.bossart@linux.intel.com>
Signed-off-by: Mark Brown <broonie@kernel.org>
sound/soc/intel/boards/bytcr_rt5640.c

index d0706ea3b04f503732b8416e16b727f785ba1b56..3cdaf87480ef9eab1b2c5d9cc0569030fd1c2244 100644 (file)
@@ -26,6 +26,7 @@
 #include <linux/clk.h>
 #include <linux/device.h>
 #include <linux/dmi.h>
+#include <linux/input.h>
 #include <linux/slab.h>
 #include <asm/cpu_device_id.h>
 #include <asm/platform_sst_audio.h>
@@ -46,19 +47,46 @@ enum {
        BYT_RT5640_IN3_MAP,
 };
 
-#define BYT_RT5640_MAP(quirk)  ((quirk) &  GENMASK(7, 0))
-#define BYT_RT5640_MONO_SPEAKER BIT(17)
-#define BYT_RT5640_DIFF_MIC     BIT(18) /* defaut is single-ended */
-#define BYT_RT5640_SSP2_AIF2    BIT(19) /* default is using AIF1  */
-#define BYT_RT5640_SSP0_AIF1    BIT(20)
-#define BYT_RT5640_SSP0_AIF2    BIT(21)
-#define BYT_RT5640_MCLK_EN     BIT(22)
-#define BYT_RT5640_MCLK_25MHZ  BIT(23)
+enum {
+       BYT_RT5640_JD_SRC_GPIO1         = (RT5640_JD_SRC_GPIO1 << 4),
+       BYT_RT5640_JD_SRC_JD1_IN4P      = (RT5640_JD_SRC_JD1_IN4P << 4),
+       BYT_RT5640_JD_SRC_JD2_IN4N      = (RT5640_JD_SRC_JD2_IN4N << 4),
+       BYT_RT5640_JD_SRC_GPIO2         = (RT5640_JD_SRC_GPIO2 << 4),
+       BYT_RT5640_JD_SRC_GPIO3         = (RT5640_JD_SRC_GPIO3 << 4),
+       BYT_RT5640_JD_SRC_GPIO4         = (RT5640_JD_SRC_GPIO4 << 4),
+};
+
+enum {
+       BYT_RT5640_OVCD_TH_600UA        = (6 << 8),
+       BYT_RT5640_OVCD_TH_1500UA       = (15 << 8),
+       BYT_RT5640_OVCD_TH_2000UA       = (20 << 8),
+};
+
+enum {
+       BYT_RT5640_OVCD_SF_0P5          = (RT5640_OVCD_SF_0P5 << 13),
+       BYT_RT5640_OVCD_SF_0P75         = (RT5640_OVCD_SF_0P75 << 13),
+       BYT_RT5640_OVCD_SF_1P0          = (RT5640_OVCD_SF_1P0 << 13),
+       BYT_RT5640_OVCD_SF_1P5          = (RT5640_OVCD_SF_1P5 << 13),
+};
 
-/* in-diff or dmic-pin + terminating empty entry */
-#define MAX_NO_PROPS 2
+#define BYT_RT5640_MAP(quirk)          ((quirk) &  GENMASK(3, 0))
+#define BYT_RT5640_JDSRC(quirk)                (((quirk) & GENMASK(7, 4)) >> 4)
+#define BYT_RT5640_OVCD_TH(quirk)      (((quirk) & GENMASK(12, 8)) >> 8)
+#define BYT_RT5640_OVCD_SF(quirk)      (((quirk) & GENMASK(14, 13)) >> 13)
+#define BYT_RT5640_JD_NOT_INV          BIT(16)
+#define BYT_RT5640_MONO_SPEAKER                BIT(17)
+#define BYT_RT5640_DIFF_MIC            BIT(18) /* default is single-ended */
+#define BYT_RT5640_SSP2_AIF2           BIT(19) /* default is using AIF1  */
+#define BYT_RT5640_SSP0_AIF1           BIT(20)
+#define BYT_RT5640_SSP0_AIF2           BIT(21)
+#define BYT_RT5640_MCLK_EN             BIT(22)
+#define BYT_RT5640_MCLK_25MHZ          BIT(23)
+
+/* in-diff or dmic-pin + jdsrc + ovcd-th + -sf + jd-inv + terminating entry */
+#define MAX_NO_PROPS 6
 
 struct byt_rt5640_private {
+       struct snd_soc_jack jack;
        struct clk *mclk;
 };
 static bool is_bytcr;
@@ -95,6 +123,16 @@ static void log_quirks(struct device *dev)
                dev_err(dev, "quirk map 0x%x is not supported, microphone input will not work\n", map);
                break;
        }
+       if (BYT_RT5640_JDSRC(byt_rt5640_quirk)) {
+               dev_info(dev, "quirk realtek,jack-detect-source %ld\n",
+                        BYT_RT5640_JDSRC(byt_rt5640_quirk));
+               dev_info(dev, "quirk realtek,over-current-threshold-microamp %ld\n",
+                        BYT_RT5640_OVCD_TH(byt_rt5640_quirk) * 100);
+               dev_info(dev, "quirk realtek,over-current-scale-factor %ld\n",
+                        BYT_RT5640_OVCD_SF(byt_rt5640_quirk));
+       }
+       if (byt_rt5640_quirk & BYT_RT5640_JD_NOT_INV)
+               dev_info(dev, "quirk JD_NOT_INV enabled\n");
        if (byt_rt5640_quirk & BYT_RT5640_MONO_SPEAKER)
                dev_info(dev, "quirk MONO_SPEAKER enabled\n");
        if (byt_rt5640_quirk & BYT_RT5640_DIFF_MIC)
@@ -330,6 +368,17 @@ static const struct snd_kcontrol_new byt_rt5640_controls[] = {
        SOC_DAPM_PIN_SWITCH("Speaker"),
 };
 
+static struct snd_soc_jack_pin rt5640_pins[] = {
+       {
+               .pin    = "Headphone",
+               .mask   = SND_JACK_HEADPHONE,
+       },
+       {
+               .pin    = "Headset Mic",
+               .mask   = SND_JACK_MICROPHONE,
+       },
+};
+
 static int byt_rt5640_aif1_hw_params(struct snd_pcm_substream *substream,
                                        struct snd_pcm_hw_params *params)
 {
@@ -353,6 +402,9 @@ static const struct dmi_system_id byt_rt5640_quirk_table[] = {
                        DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "T100TA"),
                },
                .driver_data = (void *)(BYT_RT5640_IN1_MAP |
+                                       BYT_RT5640_JD_SRC_JD2_IN4N |
+                                       BYT_RT5640_OVCD_TH_2000UA |
+                                       BYT_RT5640_OVCD_SF_0P75 |
                                        BYT_RT5640_MCLK_EN),
        },
        {
@@ -374,6 +426,9 @@ static const struct dmi_system_id byt_rt5640_quirk_table[] = {
                        DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "Venue 8 Pro 5830"),
                },
                .driver_data = (void *)(BYT_RT5640_DMIC1_MAP |
+                                       BYT_RT5640_JD_SRC_JD2_IN4N |
+                                       BYT_RT5640_OVCD_TH_2000UA |
+                                       BYT_RT5640_OVCD_SF_0P75 |
                                        BYT_RT5640_MONO_SPEAKER |
                                        BYT_RT5640_MCLK_EN),
        },
@@ -463,6 +518,23 @@ static int byt_rt5640_add_codec_device_props(const char *i2c_dev_name)
                break;
        }
 
+       if (BYT_RT5640_JDSRC(byt_rt5640_quirk)) {
+               props[cnt++] = PROPERTY_ENTRY_U32(
+                                   "realtek,jack-detect-source",
+                                   BYT_RT5640_JDSRC(byt_rt5640_quirk));
+
+               props[cnt++] = PROPERTY_ENTRY_U32(
+                                   "realtek,over-current-threshold-microamp",
+                                   BYT_RT5640_OVCD_TH(byt_rt5640_quirk) * 100);
+
+               props[cnt++] = PROPERTY_ENTRY_U32(
+                                   "realtek,over-current-scale-factor",
+                                   BYT_RT5640_OVCD_SF(byt_rt5640_quirk));
+       }
+
+       if (byt_rt5640_quirk & BYT_RT5640_JD_NOT_INV)
+               props[cnt++] = PROPERTY_ENTRY_BOOL("realtek,jack-detect-not-inverted");
+
        ret = device_add_properties(i2c_dev, props);
        put_device(i2c_dev);
 
@@ -480,6 +552,11 @@ static int byt_rt5640_init(struct snd_soc_pcm_runtime *runtime)
 
        card->dapm.idle_bias_off = true;
 
+       /* Start with RC clk for jack-detect (we disable MCLK below) */
+       if (byt_rt5640_quirk & BYT_RT5640_MCLK_EN)
+               snd_soc_component_update_bits(component, RT5640_GLB_CLK,
+                       RT5640_SCLK_SRC_MASK, RT5640_SCLK_SRC_RCCLK);
+
        rt5640_sel_asrc_clk_src(component,
                                RT5640_DA_STEREO_FILTER |
                                RT5640_DA_MONO_L_FILTER |
@@ -573,11 +650,27 @@ static int byt_rt5640_init(struct snd_soc_pcm_runtime *runtime)
                else
                        ret = clk_set_rate(priv->mclk, 19200000);
 
-               if (ret)
+               if (ret) {
                        dev_err(card->dev, "unable to set MCLK rate\n");
+                       return ret;
+               }
        }
 
-       return ret;
+       if (BYT_RT5640_JDSRC(byt_rt5640_quirk)) {
+               ret = snd_soc_card_jack_new(card, "Headset",
+                                           SND_JACK_HEADSET | SND_JACK_BTN_0,
+                                           &priv->jack, rt5640_pins,
+                                           ARRAY_SIZE(rt5640_pins));
+               if (ret) {
+                       dev_err(card->dev, "Jack creation failed %d\n", ret);
+                       return ret;
+               }
+               snd_jack_set_key(priv->jack.jack, SND_JACK_BTN_0,
+                                KEY_PLAYPAUSE);
+               snd_soc_component_set_jack(component, &priv->jack, NULL);
+       }
+
+       return 0;
 }
 
 static const struct snd_soc_pcm_stream byt_rt5640_dai_params = {
@@ -719,6 +812,47 @@ static struct snd_soc_dai_link byt_rt5640_dais[] = {
 };
 
 /* SoC card */
+static char byt_rt5640_codec_name[SND_ACPI_I2C_ID_LEN];
+static char byt_rt5640_codec_aif_name[12]; /*  = "rt5640-aif[1|2]" */
+static char byt_rt5640_cpu_dai_name[10]; /*  = "ssp[0|2]-port" */
+
+static int byt_rt5640_suspend(struct snd_soc_card *card)
+{
+       struct snd_soc_component *component;
+
+       if (!BYT_RT5640_JDSRC(byt_rt5640_quirk))
+               return 0;
+
+       list_for_each_entry(component, &card->component_dev_list, card_list) {
+               if (!strcmp(component->name, byt_rt5640_codec_name)) {
+                       dev_dbg(component->dev, "disabling jack detect before suspend\n");
+                       snd_soc_component_set_jack(component, NULL, NULL);
+                       break;
+               }
+       }
+
+       return 0;
+}
+
+static int byt_rt5640_resume(struct snd_soc_card *card)
+{
+       struct byt_rt5640_private *priv = snd_soc_card_get_drvdata(card);
+       struct snd_soc_component *component;
+
+       if (!BYT_RT5640_JDSRC(byt_rt5640_quirk))
+               return 0;
+
+       list_for_each_entry(component, &card->component_dev_list, card_list) {
+               if (!strcmp(component->name, byt_rt5640_codec_name)) {
+                       dev_dbg(component->dev, "re-enabling jack detect after resume\n");
+                       snd_soc_component_set_jack(component, &priv->jack, NULL);
+                       break;
+               }
+       }
+
+       return 0;
+}
+
 static struct snd_soc_card byt_rt5640_card = {
        .name = "bytcr-rt5640",
        .owner = THIS_MODULE,
@@ -729,12 +863,10 @@ static struct snd_soc_card byt_rt5640_card = {
        .dapm_routes = byt_rt5640_audio_map,
        .num_dapm_routes = ARRAY_SIZE(byt_rt5640_audio_map),
        .fully_routed = true,
+       .suspend_pre = byt_rt5640_suspend,
+       .resume_post = byt_rt5640_resume,
 };
 
-static char byt_rt5640_codec_name[SND_ACPI_I2C_ID_LEN];
-static char byt_rt5640_codec_aif_name[12]; /*  = "rt5640-aif[1|2]" */
-static char byt_rt5640_cpu_dai_name[10]; /*  = "ssp[0|2]-port" */
-
 static bool is_valleyview(void)
 {
        static const struct x86_cpu_id cpu_ids[] = {