From 3917da94f787e6c907e440653ead0c666a71379e Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Tue, 21 May 2019 08:26:53 +0200 Subject: [PATCH] ASoC: Intel: Add machine driver for CX2072X on BYT/CHT platforms 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 Signed-off-by: Takashi Iwai Signed-off-by: Mark Brown --- sound/soc/intel/boards/Kconfig | 11 + sound/soc/intel/boards/Makefile | 2 + sound/soc/intel/boards/bytcht_cx2072x.c | 262 ++++++++++++++++++ .../intel/common/soc-acpi-intel-byt-match.c | 8 + .../intel/common/soc-acpi-intel-cht-match.c | 8 + 5 files changed, 291 insertions(+) create mode 100644 sound/soc/intel/boards/bytcht_cx2072x.c diff --git a/sound/soc/intel/boards/Kconfig b/sound/soc/intel/boards/Kconfig index e39473a6a5d9..59e366edc16b 100644 --- a/sound/soc/intel/boards/Kconfig +++ b/sound/soc/intel/boards/Kconfig @@ -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 diff --git a/sound/soc/intel/boards/Makefile b/sound/soc/intel/boards/Makefile index 451b3bd7d9c5..6445f90ea542 100644 --- a/sound/soc/intel/boards/Makefile +++ b/sound/soc/intel/boards/Makefile @@ -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 index 000000000000..b21b0e7f981a --- /dev/null +++ b/sound/soc/intel/boards/bytcht_cx2072x.c @@ -0,0 +1,262 @@ +// SPDX-License-Identifier: GPL-2.0 +// +// ASoC DPCM Machine driver for Baytrail / Cherrytrail platforms with +// CX2072X codec +// + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#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"); diff --git a/sound/soc/intel/common/soc-acpi-intel-byt-match.c b/sound/soc/intel/common/soc-acpi-intel-byt-match.c index 0cfab247876a..9cc7b17e0b10 100644 --- a/sound/soc/intel/common/soc-acpi-intel-byt-match.c +++ b/sound/soc/intel/common/soc-acpi-intel-byt-match.c @@ -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 diff --git a/sound/soc/intel/common/soc-acpi-intel-cht-match.c b/sound/soc/intel/common/soc-acpi-intel-cht-match.c index ff9c31a39ad4..6d0755f1353a 100644 --- a/sound/soc/intel/common/soc-acpi-intel-cht-match.c +++ b/sound/soc/intel/common/soc-acpi-intel-cht-match.c @@ -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 -- 2.30.2