From cd7795bf4b065287abaa1a8f360d15ed9722216f Mon Sep 17 00:00:00 2001 From: Naveen Manohar Date: Tue, 3 Dec 2019 10:38:47 +0530 Subject: [PATCH] ASoC: Intel: boards: tgl_sdw_rt5682: Add machine driver TGL machine driver with RT5682 connected SNDW Link0 with HDMI Audio & DMIC Support Signed-off-by: Naveen Manohar --- sound/soc/intel/boards/Kconfig | 18 ++ sound/soc/intel/boards/Makefile | 2 + sound/soc/intel/boards/tgl_sdw_rt5682.c | 354 ++++++++++++++++++++++++ 3 files changed, 374 insertions(+) create mode 100644 sound/soc/intel/boards/tgl_sdw_rt5682.c diff --git a/sound/soc/intel/boards/Kconfig b/sound/soc/intel/boards/Kconfig index b76fabec6853a0..b65ca2a58b5ce4 100644 --- a/sound/soc/intel/boards/Kconfig +++ b/sound/soc/intel/boards/Kconfig @@ -580,4 +580,22 @@ config SND_SOC_INTEL_SOUNDWIRE_RT711_RT1308_RT715_MACH endif +if SND_SOC_SOF_TIGERLAKE && SND_SOC_SOF_INTEL_SOUNDWIRE + +config SND_SOC_INTEL_TGL_SOUNDWIRE_RT5682_MACH + tristate "TGL with RT5682 in SDW mode" + depends on I2C && ACPI + depends on MFD_INTEL_LPSS || COMPILE_TEST + select SND_SOC_RT5682_SDW + select SND_HDA_CODEC_HDMI if SND_SOC_SOF_HDA_CODEC + select SND_SOC_HDAC_HDMI if SND_SOC_SOF_HDA_LINK + help + This adds support for ASoC machine driver for Tigerlake platforms + with SoundWire MAX98373 audio codec. + Say Y if you have such a device. + If unsure select "N". + + +endif ## SND_SOC_SOF_TIGERLAKE && SND_SOC_SOF_INTEL_SOUNDWIRE + endif ## SND_SOC_INTEL_MACH diff --git a/sound/soc/intel/boards/Makefile b/sound/soc/intel/boards/Makefile index 26845366e27fd8..47179068dc9536 100644 --- a/sound/soc/intel/boards/Makefile +++ b/sound/soc/intel/boards/Makefile @@ -35,6 +35,7 @@ snd-soc-sof_da7219_max98373-objs := sof_da7219_max98373.o hda_dsp_common.o snd-soc-tgl-rt711-rt1308-objs := sdw_rt711_i2s_rt1308.o hda_dsp_common.o snd-soc-sdw-rt700-objs := sdw_rt700.o hda_dsp_common.o snd-soc-sdw-rt711-rt1308-rt715-objs := sdw_rt711_rt1308_rt715.o hda_dsp_common.o +snd-soc-tgl-sdw-rt5682-objs := tgl_sdw_rt5682.o hda_dsp_common.o obj-$(CONFIG_SND_SOC_INTEL_SOF_RT5682_MACH) += snd-soc-sof_rt5682.o obj-$(CONFIG_SND_SOC_INTEL_HASWELL_MACH) += snd-soc-sst-haswell.o @@ -72,3 +73,4 @@ obj-$(CONFIG_SND_SOC_INTEL_SOF_DA7219_MAX98373_MACH) += snd-soc-sof_da7219_max98 obj-$(CONFIG_SND_SOC_INTEL_TGL_RT711_RT1308_MACH) += snd-soc-tgl-rt711-rt1308.o obj-$(CONFIG_SND_SOC_INTEL_SOUNDWIRE_RT700_MACH) += snd-soc-sdw-rt700.o obj-$(CONFIG_SND_SOC_INTEL_SOUNDWIRE_RT711_RT1308_RT715_MACH) += snd-soc-sdw-rt711-rt1308-rt715.o +obj-$(CONFIG_SND_SOC_INTEL_TGL_SOUNDWIRE_RT5682_MACH) += snd-soc-tgl-sdw-rt5682.o diff --git a/sound/soc/intel/boards/tgl_sdw_rt5682.c b/sound/soc/intel/boards/tgl_sdw_rt5682.c new file mode 100644 index 00000000000000..1466d6a075d3ac --- /dev/null +++ b/sound/soc/intel/boards/tgl_sdw_rt5682.c @@ -0,0 +1,354 @@ +// SPDX-License-Identifier: GPL-2.0 +// Copyright (c) 2020 Intel Corporation + +/* + * sdw_tgl_rt5682 - ASOC Machine driver for Intel SoundWire platforms + * connected to rt5682 Realtek device + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "../../codecs/hdac_hdmi.h" +#include "hda_dsp_common.h" + +struct snd_soc_jack rt5682_headset; + +struct tgl_card_private { + struct list_head hdmi_pcm_list; + bool common_hdmi_codec_drv; +}; + +#if IS_ENABLED(CONFIG_SND_HDA_CODEC_HDMI) +static struct snd_soc_jack tgl_hdmi[4]; + +struct tgl_hdmi_pcm { + struct list_head head; + struct snd_soc_dai *codec_dai; + int device; +}; + +#define NAME_SIZE 32 +static int card_late_probe(struct snd_soc_card *card) +{ + struct tgl_card_private *ctx = snd_soc_card_get_drvdata(card); + struct tgl_hdmi_pcm *pcm; + struct snd_soc_component *component = NULL; + int err, i = 0; + char jack_name[NAME_SIZE]; + + if (list_empty(&ctx->hdmi_pcm_list)) + return -EINVAL; + + if (ctx->common_hdmi_codec_drv) { + pcm = list_first_entry(&ctx->hdmi_pcm_list, struct tgl_hdmi_pcm, + head); + component = pcm->codec_dai->component; + return hda_dsp_hdmi_build_controls(card, component); + } + + list_for_each_entry(pcm, &ctx->hdmi_pcm_list, head) { + component = pcm->codec_dai->component; + snprintf(jack_name, sizeof(jack_name), + "HDMI/DP, pcm=%d Jack", pcm->device); + err = snd_soc_card_jack_new(card, jack_name, + SND_JACK_AVOUT, &tgl_hdmi[i], + NULL, 0); + + if (err) + return err; + + err = hdac_hdmi_jack_init(pcm->codec_dai, pcm->device, + &tgl_hdmi[i]); + if (err < 0) + return err; + + i++; + } + + if (!component) + return -EINVAL; + + return hdac_hdmi_jack_port_init(component, &card->dapm); +} +#else +static int card_late_probe(struct snd_soc_card *card) +{ + return 0; +} +#endif + +static int tgl_hdmi_init(struct snd_soc_pcm_runtime *rtd) +{ + struct tgl_card_private *ctx = snd_soc_card_get_drvdata(rtd->card); + struct snd_soc_dai *dai = rtd->codec_dai; + struct tgl_hdmi_pcm *pcm; + + pcm = devm_kzalloc(rtd->card->dev, sizeof(*pcm), GFP_KERNEL); + if (!pcm) + return -ENOMEM; + + /* dai_link id is 1:1 mapped to the PCM device */ + pcm->device = rtd->dai_link->id; + pcm->codec_dai = dai; + + list_add_tail(&pcm->head, &ctx->hdmi_pcm_list); + + return 0; +} + +static struct snd_soc_jack_pin sdw_jack_pins[] = { + { + .pin = "Headphone", + .mask = SND_JACK_HEADPHONE, + }, + { + .pin = "Headset Mic", + .mask = SND_JACK_MICROPHONE, + }, +}; + +static int rt5682_codec_init(struct snd_soc_pcm_runtime *rtd) +{ + struct snd_soc_component *component = rtd->codec_dai->component; + struct snd_soc_jack *jack; + int ret; + + /* + * Headset buttons map to the google Reference headset. + * These can be configured by userspace. + */ + ret = snd_soc_card_jack_new(rtd->card, "Headset Jack", + SND_JACK_HEADSET | SND_JACK_BTN_0 | + SND_JACK_BTN_1 | SND_JACK_BTN_2 | + SND_JACK_BTN_3, + &rt5682_headset, + sdw_jack_pins, + ARRAY_SIZE(sdw_jack_pins)); + if (ret) { + dev_err(rtd->dev, "Headset Jack creation failed: %d\n", ret); + return ret; + } + + jack = &rt5682_headset; + + snd_jack_set_key(jack->jack, SND_JACK_BTN_0, KEY_VOLUMEUP); + snd_jack_set_key(jack->jack, SND_JACK_BTN_1, KEY_PLAYPAUSE); + snd_jack_set_key(jack->jack, SND_JACK_BTN_2, KEY_VOLUMEDOWN); + snd_jack_set_key(jack->jack, SND_JACK_BTN_3, KEY_PLAYPAUSE); + ret = snd_soc_component_set_jack(component, jack, NULL); + + if (ret == -EAGAIN) { + msleep(500); + ret = snd_soc_component_set_jack(component, jack, NULL); + } + + if (ret) { + dev_err(rtd->dev, "Headset Jack call-back failed: %d\n", ret); + return ret; + } + + return ret; +} + +static const struct snd_soc_dapm_widget widgets[] = { + SND_SOC_DAPM_HP("Headphone", NULL), + SND_SOC_DAPM_MIC("Headset Mic", NULL), +}; + +static const struct snd_soc_dapm_route map[] = { + /*Headphones*/ + { "Headphone", NULL, "HPOL" }, + { "Headphone", NULL, "HPOR" }, + { "IN1P", NULL, "Headset Mic" }, +}; + +static const struct snd_kcontrol_new controls[] = { + SOC_DAPM_PIN_SWITCH("Headphone"), + SOC_DAPM_PIN_SWITCH("Headset Mic"), +}; + +SND_SOC_DAILINK_DEF(sdw0_pin2, + DAILINK_COMP_ARRAY(COMP_CPU("SDW0 Pin2"))); +SND_SOC_DAILINK_DEF(sdw0_pin3, + DAILINK_COMP_ARRAY(COMP_CPU("SDW0 Pin3"))); +SND_SOC_DAILINK_DEF(sdw0_codec, + DAILINK_COMP_ARRAY(COMP_CODEC("sdw:0:25d:5682:0", "rt5682-sdw"))); + +SND_SOC_DAILINK_DEF(platform, + DAILINK_COMP_ARRAY(COMP_PLATFORM("0000:00:1f.3"))); + +SND_SOC_DAILINK_DEF(dmic_pin, + DAILINK_COMP_ARRAY(COMP_CPU("DMIC01 Pin"))); +SND_SOC_DAILINK_DEF(dmic_codec, + DAILINK_COMP_ARRAY(COMP_CODEC("dmic-codec", "dmic-hifi"))); + +SND_SOC_DAILINK_DEF(idisp1_pin, + DAILINK_COMP_ARRAY(COMP_CPU("iDisp1 Pin"))); +SND_SOC_DAILINK_DEF(idisp1_codec, + DAILINK_COMP_ARRAY(COMP_CODEC("ehdaudio0D2", "intel-hdmi-hifi1"))); + +SND_SOC_DAILINK_DEF(idisp2_pin, + DAILINK_COMP_ARRAY(COMP_CPU("iDisp2 Pin"))); +SND_SOC_DAILINK_DEF(idisp2_codec, + DAILINK_COMP_ARRAY(COMP_CODEC("ehdaudio0D2", "intel-hdmi-hifi2"))); + +SND_SOC_DAILINK_DEF(idisp3_pin, + DAILINK_COMP_ARRAY(COMP_CPU("iDisp3 Pin"))); +SND_SOC_DAILINK_DEF(idisp3_codec, + DAILINK_COMP_ARRAY(COMP_CODEC("ehdaudio0D2", "intel-hdmi-hifi3"))); + +SND_SOC_DAILINK_DEF(idisp4_pin, + DAILINK_COMP_ARRAY(COMP_CPU("iDisp4 Pin"))); +SND_SOC_DAILINK_DEF(idisp4_codec, + DAILINK_COMP_ARRAY(COMP_CODEC("ehdaudio0D2", "intel-hdmi-hifi4"))); + +struct snd_soc_dai_link dailink[] = { + { + .name = "SDW0-Playback", + .id = 0, + .init = rt5682_codec_init, + .no_pcm = 1, + .dpcm_playback = 1, + .nonatomic = true, + SND_SOC_DAILINK_REG(sdw0_pin2, sdw0_codec, platform), + }, + { + .name = "SDW0-Capture", + .id = 1, + .no_pcm = 1, + .dpcm_capture = 1, + .nonatomic = true, + SND_SOC_DAILINK_REG(sdw0_pin3, sdw0_codec, platform), + }, + { + .name = "dmic01", + .id = 4, + .ignore_suspend = 1, + .dpcm_capture = 1, + .no_pcm = 1, + SND_SOC_DAILINK_REG(dmic_pin, dmic_codec, platform), + }, +#if IS_ENABLED(CONFIG_SND_SOC_HDAC_HDMI) + { + .name = "iDisp1", + .id = 5, + .init = tgl_hdmi_init, + .dpcm_playback = 1, + .no_pcm = 1, + SND_SOC_DAILINK_REG(idisp1_pin, idisp1_codec, platform), + }, + { + .name = "iDisp2", + .id = 6, + .init = tgl_hdmi_init, + .dpcm_playback = 1, + .no_pcm = 1, + SND_SOC_DAILINK_REG(idisp2_pin, idisp2_codec, platform), + }, + { + .name = "iDisp3", + .id = 7, + .init = tgl_hdmi_init, + .dpcm_playback = 1, + .no_pcm = 1, + SND_SOC_DAILINK_REG(idisp3_pin, idisp3_codec, platform), + }, + { + .name = "iDisp4", + .id = 8, + .init = tgl_hdmi_init, + .dpcm_playback = 1, + .no_pcm = 1, + SND_SOC_DAILINK_REG(idisp4_pin, idisp4_codec, platform), + }, +#endif +}; + +/* SoC card */ +static struct snd_soc_card card_rt5682 = { + .name = "tgl-sdw-rt5682", + .dai_link = dailink, + .num_links = ARRAY_SIZE(dailink), + .controls = controls, + .num_controls = ARRAY_SIZE(controls), + .dapm_widgets = widgets, + .num_dapm_widgets = ARRAY_SIZE(widgets), + .dapm_routes = map, + .num_dapm_routes = ARRAY_SIZE(map), + .late_probe = card_late_probe, +}; + +static int mc_probe(struct platform_device *pdev) +{ + struct tgl_card_private *ctx; + struct snd_soc_acpi_mach *mach; + const char *platform_name; + struct snd_soc_card *card = &card_rt5682; + int ret = 0; + + dev_dbg(&pdev->dev, "Entry %s\n", __func__); + + ctx = devm_kzalloc(&pdev->dev, sizeof(*ctx), GFP_KERNEL); + if (!ctx) + return -ENOMEM; + +#if IS_ENABLED(CONFIG_SND_HDA_CODEC_HDMI) + INIT_LIST_HEAD(&ctx->hdmi_pcm_list); +#endif + + card->dev = &pdev->dev; + + /* override platform name, if required */ + mach = (&pdev->dev)->platform_data; + platform_name = mach->mach_params.platform; + + ret = snd_soc_fixup_dai_links_platform_name(card, platform_name); + if (ret) + return ret; + + snd_soc_card_set_drvdata(card, ctx); + ctx->common_hdmi_codec_drv = mach->mach_params.common_hdmi_codec_drv; + + /* Register the card */ + ret = devm_snd_soc_register_card(&pdev->dev, card); + if (ret) { + dev_err(card->dev, "snd_soc_register_card failed %d\n", ret); + return ret; + } + + platform_set_drvdata(pdev, card); + + return ret; +} + +static struct platform_driver sdw_rt5682_driver = { + .driver = { + .name = "tgl_sdw_rt5682", + .pm = &snd_soc_pm_ops, + }, + .probe = mc_probe, +}; + +module_platform_driver(sdw_rt5682_driver); + +MODULE_DESCRIPTION("TGL ASoC SoundWire RT5682 Machine driver"); +MODULE_AUTHOR("Bard Liao "); +MODULE_AUTHOR("Naveen Manohar "); +MODULE_LICENSE("GPL v2"); +MODULE_ALIAS("platform:tgl_sdw_rt5682");