ASoC: Intel: Add machine driver for CX2072X on BYT/CHT platforms
authorTakashi Iwai <tiwai@suse.de>
Tue, 21 May 2019 06:26:53 +0000 (08:26 +0200)
committerMark Brown <broonie@kernel.org>
Wed, 22 May 2019 12:19:06 +0000 (13:19 +0100)
This is an implementation of a machine driver needed for Conexant
CX2072X codec on Intel Baytrail and Cherrytrail platforms.  The
current patch is based on the initial work by Pierre-Louis Bossart and
the other Intel machine drivers.

The jack detection support (driven via the standard GPIO) was added on
top of the original work.

Tested with ASUS E200HA laptop.

Bugzilla: https://bugzilla.kernel.org/show_bug.cgi?id=115531
Acked-by: Pierre-Louis Bossart <pierre-louis.bossart@linux.intel.com>
Signed-off-by: Takashi Iwai <tiwai@suse.de>
Signed-off-by: Mark Brown <broonie@kernel.org>
sound/soc/intel/boards/Kconfig
sound/soc/intel/boards/Makefile
sound/soc/intel/boards/bytcht_cx2072x.c [new file with mode: 0644]
sound/soc/intel/common/soc-acpi-intel-byt-match.c
sound/soc/intel/common/soc-acpi-intel-cht-match.c

index e39473a6a5d90d9eb2a91e6f1155c64061a7ff45..59e366edc16b3df88532a4a35924fc6476a317fb 100644 (file)
@@ -155,6 +155,17 @@ config SND_SOC_INTEL_CHT_BSW_NAU8824_MACH
          Say Y or m if you have such a device. This is a recommended option.
          If unsure select "N".
 
+config SND_SOC_INTEL_BYT_CHT_CX2072X_MACH
+       tristate "Baytrail & Cherrytrail with CX2072X codec"
+       depends on X86_INTEL_LPSS && I2C && ACPI
+       select SND_SOC_ACPI
+       select SND_SOC_CX2072X
+       help
+         This adds support for ASoC machine driver for Intel(R) Baytrail &
+         Cherrytrail platforms with Conexant CX2072X audio codec.
+         Say Y or m if you have such a device. This is a recommended option.
+         If unsure select "N".
+
 config SND_SOC_INTEL_BYT_CHT_DA7213_MACH
        tristate "Baytrail & Cherrytrail with DA7212/7213 codec"
        depends on I2C && ACPI
index 451b3bd7d9c5919575963057398d0ca5c29cf094..6445f90ea542cbdd0490bb450e809719bc9c7745 100644 (file)
@@ -13,6 +13,7 @@ snd-soc-sst-cht-bsw-rt5672-objs := cht_bsw_rt5672.o
 snd-soc-sst-cht-bsw-rt5645-objs := cht_bsw_rt5645.o
 snd-soc-sst-cht-bsw-max98090_ti-objs := cht_bsw_max98090_ti.o
 snd-soc-sst-cht-bsw-nau8824-objs := cht_bsw_nau8824.o
+snd-soc-sst-byt-cht-cx2072x-objs := bytcht_cx2072x.o
 snd-soc-sst-byt-cht-da7213-objs := bytcht_da7213.o
 snd-soc-sst-byt-cht-es8316-objs := bytcht_es8316.o
 snd-soc-sst-byt-cht-nocodec-objs := bytcht_nocodec.o
@@ -42,6 +43,7 @@ obj-$(CONFIG_SND_SOC_INTEL_CHT_BSW_RT5672_MACH) += snd-soc-sst-cht-bsw-rt5672.o
 obj-$(CONFIG_SND_SOC_INTEL_CHT_BSW_RT5645_MACH) += snd-soc-sst-cht-bsw-rt5645.o
 obj-$(CONFIG_SND_SOC_INTEL_CHT_BSW_MAX98090_TI_MACH) += snd-soc-sst-cht-bsw-max98090_ti.o
 obj-$(CONFIG_SND_SOC_INTEL_CHT_BSW_NAU8824_MACH) += snd-soc-sst-cht-bsw-nau8824.o
+obj-$(CONFIG_SND_SOC_INTEL_BYT_CHT_CX2072X_MACH) += snd-soc-sst-byt-cht-cx2072x.o
 obj-$(CONFIG_SND_SOC_INTEL_BYT_CHT_DA7213_MACH) += snd-soc-sst-byt-cht-da7213.o
 obj-$(CONFIG_SND_SOC_INTEL_BYT_CHT_ES8316_MACH) += snd-soc-sst-byt-cht-es8316.o
 obj-$(CONFIG_SND_SOC_INTEL_BYT_CHT_NOCODEC_MACH) += snd-soc-sst-byt-cht-nocodec.o
diff --git a/sound/soc/intel/boards/bytcht_cx2072x.c b/sound/soc/intel/boards/bytcht_cx2072x.c
new file mode 100644 (file)
index 0000000..b21b0e7
--- /dev/null
@@ -0,0 +1,262 @@
+// SPDX-License-Identifier: GPL-2.0
+//
+// ASoC DPCM Machine driver for Baytrail / Cherrytrail platforms with
+// CX2072X codec
+//
+
+#include <linux/acpi.h>
+#include <linux/device.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <asm/platform_sst_audio.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/jack.h>
+#include <sound/soc.h>
+#include <sound/soc-acpi.h>
+#include "../../codecs/cx2072x.h"
+#include "../atom/sst-atom-controls.h"
+
+static const struct snd_soc_dapm_widget byt_cht_cx2072x_widgets[] = {
+       SND_SOC_DAPM_HP("Headphone", NULL),
+       SND_SOC_DAPM_MIC("Headset Mic", NULL),
+       SND_SOC_DAPM_MIC("Int Mic", NULL),
+       SND_SOC_DAPM_SPK("Ext Spk", NULL),
+};
+
+static const struct snd_soc_dapm_route byt_cht_cx2072x_audio_map[] = {
+       /* External Speakers: HFL, HFR */
+       {"Headphone", NULL, "PORTA"},
+       {"Ext Spk", NULL, "PORTG"},
+       {"PORTC", NULL, "Int Mic"},
+       {"PORTD", NULL, "Headset Mic"},
+
+       {"Playback", NULL, "ssp2 Tx"},
+       {"ssp2 Tx", NULL, "codec_out0"},
+       {"ssp2 Tx", NULL, "codec_out1"},
+       {"codec_in0", NULL, "ssp2 Rx"},
+       {"codec_in1", NULL, "ssp2 Rx"},
+       {"ssp2 Rx", NULL, "Capture"},
+};
+
+static const struct snd_kcontrol_new byt_cht_cx2072x_controls[] = {
+       SOC_DAPM_PIN_SWITCH("Headphone"),
+       SOC_DAPM_PIN_SWITCH("Headset Mic"),
+       SOC_DAPM_PIN_SWITCH("Int Mic"),
+       SOC_DAPM_PIN_SWITCH("Ext Spk"),
+};
+
+static struct snd_soc_jack byt_cht_cx2072x_headset;
+
+/* Headset jack detection DAPM pins */
+static struct snd_soc_jack_pin byt_cht_cx2072x_headset_pins[] = {
+       {
+               .pin = "Headset Mic",
+               .mask = SND_JACK_MICROPHONE,
+       },
+       {
+               .pin = "Headphone",
+               .mask = SND_JACK_HEADPHONE,
+       },
+};
+
+static const struct acpi_gpio_params byt_cht_cx2072x_headset_gpios;
+static const struct acpi_gpio_mapping byt_cht_cx2072x_acpi_gpios[] = {
+       { "headset-gpios", &byt_cht_cx2072x_headset_gpios, 1 },
+       {},
+};
+
+static int byt_cht_cx2072x_init(struct snd_soc_pcm_runtime *rtd)
+{
+       struct snd_soc_card *card = rtd->card;
+       struct snd_soc_component *codec = rtd->codec_dai->component;
+       int ret;
+
+       if (devm_acpi_dev_add_driver_gpios(codec->dev,
+                                          byt_cht_cx2072x_acpi_gpios))
+               dev_warn(rtd->dev, "Unable to add GPIO mapping table\n");
+
+       card->dapm.idle_bias_off = true;
+
+       /* set the default PLL rate, the clock is handled by the codec driver */
+       ret = snd_soc_dai_set_sysclk(rtd->codec_dai, CX2072X_MCLK_EXTERNAL_PLL,
+                                    19200000, SND_SOC_CLOCK_IN);
+       if (ret) {
+               dev_err(rtd->dev, "Could not set sysclk\n");
+               return ret;
+       }
+
+       ret = snd_soc_card_jack_new(card, "Headset",
+                                   SND_JACK_HEADSET | SND_JACK_BTN_0,
+                                   &byt_cht_cx2072x_headset,
+                                   byt_cht_cx2072x_headset_pins,
+                                   ARRAY_SIZE(byt_cht_cx2072x_headset_pins));
+       if (ret)
+               return ret;
+
+       snd_soc_component_set_jack(codec, &byt_cht_cx2072x_headset, NULL);
+
+       snd_soc_dai_set_bclk_ratio(rtd->codec_dai, 50);
+
+       return ret;
+}
+
+static int byt_cht_cx2072x_fixup(struct snd_soc_pcm_runtime *rtd,
+                                struct snd_pcm_hw_params *params)
+{
+       struct snd_interval *rate =
+               hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE);
+       struct snd_interval *channels =
+               hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS);
+       int ret;
+
+       /* The DSP will covert the FE rate to 48k, stereo, 24bits */
+       rate->min = rate->max = 48000;
+       channels->min = channels->max = 2;
+
+       /* set SSP2 to 24-bit */
+       params_set_format(params, SNDRV_PCM_FORMAT_S24_LE);
+
+       /*
+        * Default mode for SSP configuration is TDM 4 slot, override config
+        * with explicit setting to I2S 2ch 24-bit. The word length is set with
+        * dai_set_tdm_slot() since there is no other API exposed
+        */
+       ret = snd_soc_dai_set_fmt(rtd->cpu_dai,
+                               SND_SOC_DAIFMT_I2S     |
+                               SND_SOC_DAIFMT_NB_NF   |
+                               SND_SOC_DAIFMT_CBS_CFS);
+       if (ret < 0) {
+               dev_err(rtd->dev, "can't set format to I2S, err %d\n", ret);
+               return ret;
+       }
+
+       ret = snd_soc_dai_set_tdm_slot(rtd->cpu_dai, 0x3, 0x3, 2, 24);
+       if (ret < 0) {
+               dev_err(rtd->dev, "can't set I2S config, err %d\n", ret);
+               return ret;
+       }
+
+       return 0;
+}
+
+static int byt_cht_cx2072x_aif1_startup(struct snd_pcm_substream *substream)
+{
+       return snd_pcm_hw_constraint_single(substream->runtime,
+                                           SNDRV_PCM_HW_PARAM_RATE, 48000);
+}
+
+static struct snd_soc_ops byt_cht_cx2072x_aif1_ops = {
+       .startup = byt_cht_cx2072x_aif1_startup,
+};
+
+static struct snd_soc_dai_link byt_cht_cx2072x_dais[] = {
+       [MERR_DPCM_AUDIO] = {
+               .name = "Audio Port",
+               .stream_name = "Audio",
+               .cpu_dai_name = "media-cpu-dai",
+               .codec_dai_name = "snd-soc-dummy-dai",
+               .codec_name = "snd-soc-dummy",
+               .platform_name = "sst-mfld-platform",
+               .nonatomic = true,
+               .dynamic = 1,
+               .dpcm_playback = 1,
+               .dpcm_capture = 1,
+               .ops = &byt_cht_cx2072x_aif1_ops,
+       },
+       [MERR_DPCM_DEEP_BUFFER] = {
+               .name = "Deep-Buffer Audio Port",
+               .stream_name = "Deep-Buffer Audio",
+               .cpu_dai_name = "deepbuffer-cpu-dai",
+               .codec_dai_name = "snd-soc-dummy-dai",
+               .codec_name = "snd-soc-dummy",
+               .platform_name = "sst-mfld-platform",
+               .nonatomic = true,
+               .dynamic = 1,
+               .dpcm_playback = 1,
+               .ops = &byt_cht_cx2072x_aif1_ops,
+       },
+       /* back ends */
+       {
+               .name = "SSP2-Codec",
+               .id = 0,
+               .cpu_dai_name = "ssp2-port",
+               .platform_name = "sst-mfld-platform",
+               .no_pcm = 1,
+               .codec_dai_name = "cx2072x-hifi",
+               .codec_name = "i2c-14F10720:00",
+               .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF
+                                             | SND_SOC_DAIFMT_CBS_CFS,
+               .init = byt_cht_cx2072x_init,
+               .be_hw_params_fixup = byt_cht_cx2072x_fixup,
+               .nonatomic = true,
+               .dpcm_playback = 1,
+               .dpcm_capture = 1,
+       },
+};
+
+/* SoC card */
+static struct snd_soc_card byt_cht_cx2072x_card = {
+       .name = "bytcht-cx2072x",
+       .owner = THIS_MODULE,
+       .dai_link = byt_cht_cx2072x_dais,
+       .num_links = ARRAY_SIZE(byt_cht_cx2072x_dais),
+       .dapm_widgets = byt_cht_cx2072x_widgets,
+       .num_dapm_widgets = ARRAY_SIZE(byt_cht_cx2072x_widgets),
+       .dapm_routes = byt_cht_cx2072x_audio_map,
+       .num_dapm_routes = ARRAY_SIZE(byt_cht_cx2072x_audio_map),
+       .controls = byt_cht_cx2072x_controls,
+       .num_controls = ARRAY_SIZE(byt_cht_cx2072x_controls),
+};
+
+static char codec_name[SND_ACPI_I2C_ID_LEN];
+
+static int snd_byt_cht_cx2072x_probe(struct platform_device *pdev)
+{
+       struct snd_soc_acpi_mach *mach;
+       struct acpi_device *adev;
+       int dai_index = 0;
+       int i, ret;
+
+       byt_cht_cx2072x_card.dev = &pdev->dev;
+       mach = dev_get_platdata(&pdev->dev);
+
+       /* fix index of codec dai */
+       for (i = 0; i < ARRAY_SIZE(byt_cht_cx2072x_dais); i++) {
+               if (!strcmp(byt_cht_cx2072x_dais[i].codec_name,
+                           "i2c-14F10720:00")) {
+                       dai_index = i;
+                       break;
+               }
+       }
+
+       /* fixup codec name based on HID */
+       adev = acpi_dev_get_first_match_dev(mach->id, NULL, -1);
+       if (adev) {
+               snprintf(codec_name, sizeof(codec_name), "i2c-%s",
+                        acpi_dev_name(adev));
+               put_device(&adev->dev);
+               byt_cht_cx2072x_dais[dai_index].codec_name = codec_name;
+       }
+
+       /* override plaform name, if required */
+       ret = snd_soc_fixup_dai_links_platform_name(&byt_cht_cx2072x_card,
+                                                   mach->mach_params.platform);
+       if (ret)
+               return ret;
+
+       return devm_snd_soc_register_card(&pdev->dev, &byt_cht_cx2072x_card);
+}
+
+static struct platform_driver snd_byt_cht_cx2072x_driver = {
+       .driver = {
+               .name = "bytcht_cx2072x",
+       },
+       .probe = snd_byt_cht_cx2072x_probe,
+};
+module_platform_driver(snd_byt_cht_cx2072x_driver);
+
+MODULE_DESCRIPTION("ASoC Intel(R) Baytrail/Cherrytrail Machine driver");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:bytcht_cx2072x");
index 0cfab247876ab2881386c8024c7edf5701b0e75d..9cc7b17e0b100103457cbd3e64f6f0e7a7df5908 100644 (file)
@@ -217,6 +217,14 @@ struct snd_soc_acpi_mach  snd_soc_acpi_intel_baytrail_machines[] = {
                .sof_fw_filename = "sof-byt.ri",
                .sof_tplg_filename = "sof-byt-max98090.tplg",
        },
+       {
+               .id = "14F10720",
+               .drv_name = "bytcht_cx2072x",
+               .fw_filename = "intel/fw_sst_0f28.bin",
+               .board = "bytcht_cx2072x",
+               .sof_fw_filename = "sof-byt.ri",
+               .sof_tplg_filename = "sof-byt-cx2072x.tplg",
+       },
 #if IS_ENABLED(CONFIG_SND_SOC_INTEL_BYT_CHT_NOCODEC_MACH)
        /*
         * This is always last in the table so that it is selected only when
index ff9c31a39ad400212253d295cb37c944797c1cd3..6d0755f1353a117ab147f074fada7f0c062647e6 100644 (file)
@@ -175,6 +175,14 @@ struct snd_soc_acpi_mach  snd_soc_acpi_intel_cherrytrail_machines[] = {
                .sof_fw_filename = "sof-cht.ri",
                .sof_tplg_filename = "sof-cht-rt5651.tplg",
        },
+       {
+               .id = "14F10720",
+               .drv_name = "bytcht_cx2072x",
+               .fw_filename = "intel/fw_sst_22a8.bin",
+               .board = "bytcht_cx2072x",
+               .sof_fw_filename = "sof-cht.ri",
+               .sof_tplg_filename = "sof-cht-cx2072x.tplg",
+       },
 #if IS_ENABLED(CONFIG_SND_SOC_INTEL_BYT_CHT_NOCODEC_MACH)
        /*
         * This is always last in the table so that it is selected only when