diff options
Diffstat (limited to 'sound/soc')
34 files changed, 1992 insertions, 450 deletions
diff --git a/sound/soc/codecs/hdac_hdmi.c b/sound/soc/codecs/hdac_hdmi.c index 2abb742fc47b..4e181b270d95 100644 --- a/sound/soc/codecs/hdac_hdmi.c +++ b/sound/soc/codecs/hdac_hdmi.c @@ -1124,8 +1124,10 @@ static void hdac_hdmi_present_sense(struct hdac_hdmi_pin *pin, int repoll) } hdac_hdmi_parse_eld(edev, pin); - print_hex_dump_bytes("ELD: ", DUMP_PREFIX_OFFSET, - pin->eld.eld_buffer, pin->eld.eld_size); + print_hex_dump_debug("ELD: ", + DUMP_PREFIX_OFFSET, 16, 1, + pin->eld.eld_buffer, pin->eld.eld_size, + true); } else { pin->eld.monitor_present = false; pin->eld.eld_valid = false; @@ -1816,6 +1818,7 @@ static const struct dev_pm_ops hdac_hdmi_pm = { static const struct hda_device_id hdmi_list[] = { HDA_CODEC_EXT_ENTRY(0x80862809, 0x100000, "Skylake HDMI", 0), HDA_CODEC_EXT_ENTRY(0x8086280a, 0x100000, "Broxton HDMI", 0), + HDA_CODEC_EXT_ENTRY(0x8086280b, 0x100000, "Kabylake HDMI", 0), {} }; diff --git a/sound/soc/codecs/rt286.c b/sound/soc/codecs/rt286.c index 1bd31644a782..74c0e4eb3788 100644 --- a/sound/soc/codecs/rt286.c +++ b/sound/soc/codecs/rt286.c @@ -1100,6 +1100,13 @@ static const struct dmi_system_id force_combo_jack_table[] = { DMI_MATCH(DMI_PRODUCT_NAME, "Skylake Client platform") } }, + { + .ident = "Intel Kabylake RVP", + .matches = { + DMI_MATCH(DMI_PRODUCT_NAME, "Kabylake Client platform") + } + }, + { } }; diff --git a/sound/soc/intel/Kconfig b/sound/soc/intel/Kconfig index 91c15abb625e..a20c3dfbcb5d 100644 --- a/sound/soc/intel/Kconfig +++ b/sound/soc/intel/Kconfig @@ -7,7 +7,7 @@ config SND_MFLD_MACHINE help This adds support for ASoC machine driver for Intel(R) MID Medfield platform used as alsa device in audio substem in Intel(R) MID devices - Say Y if you have such a device + Say Y if you have such a device. If unsure select "N". config SND_SST_MFLD_PLATFORM @@ -25,7 +25,6 @@ config SND_SST_IPC_ACPI tristate select SND_SST_IPC select SND_SOC_INTEL_SST - depends on ACPI config SND_SOC_INTEL_SST tristate @@ -33,6 +32,12 @@ config SND_SOC_INTEL_SST select SND_SOC_INTEL_SST_MATCH if ACPI depends on (X86 || COMPILE_TEST) +# firmware stuff depends DW_DMAC_CORE; since there is no depends-on from +# the reverse selection, each machine driver needs to select +# SND_SOC_INTEL_SST_FIRMWARE carefully depending on DW_DMAC_CORE +config SND_SOC_INTEL_SST_FIRMWARE + tristate + config SND_SOC_INTEL_SST_ACPI tristate @@ -48,16 +53,33 @@ config SND_SOC_INTEL_BAYTRAIL config SND_SOC_INTEL_HASWELL_MACH tristate "ASoC Audio DSP support for Intel Haswell Lynxpoint" depends on X86_INTEL_LPSS && I2C && I2C_DESIGNWARE_PLATFORM - depends on DW_DMAC_CORE=y + depends on DW_DMAC_CORE select SND_SOC_INTEL_SST + select SND_SOC_INTEL_SST_FIRMWARE select SND_SOC_INTEL_HASWELL select SND_SOC_RT5640 help This adds support for the Lynxpoint Audio DSP on Intel(R) Haswell Ultrabook platforms. - Say Y if you have such a device + Say Y if you have such a device. If unsure select "N". +config SND_SOC_INTEL_BXT_DA7219_MAX98357A_MACH + tristate "ASoC Audio driver for Broxton with DA7219 and MAX98357A in I2S Mode" + depends on X86 && ACPI && I2C + select SND_SOC_INTEL_SST + select SND_SOC_INTEL_SKYLAKE + select SND_SOC_DA7219 + select SND_SOC_MAX98357A + select SND_SOC_DMIC + select SND_SOC_HDAC_HDMI + select SND_HDA_DSP_LOADER + help + This adds support for ASoC machine driver for Broxton-P platforms + with DA7219 + MAX98357A I2S audio codec. + Say Y if you have such a device. + If unsure select "N". + config SND_SOC_INTEL_BXT_RT298_MACH tristate "ASoC Audio driver for Broxton with RT298 I2S mode" depends on X86 && ACPI && I2C @@ -70,26 +92,28 @@ config SND_SOC_INTEL_BXT_RT298_MACH help This adds support for ASoC machine driver for Broxton platforms with RT286 I2S audio codec. - Say Y if you have such a device + Say Y if you have such a device. If unsure select "N". config SND_SOC_INTEL_BYT_RT5640_MACH tristate "ASoC Audio driver for Intel Baytrail with RT5640 codec" depends on X86_INTEL_LPSS && I2C - depends on DW_DMAC_CORE=y && (SND_SST_IPC_ACPI = n) + depends on DW_DMAC_CORE && (SND_SST_IPC_ACPI = n) select SND_SOC_INTEL_SST + select SND_SOC_INTEL_SST_FIRMWARE select SND_SOC_INTEL_BAYTRAIL select SND_SOC_RT5640 help This adds audio driver for Intel Baytrail platform based boards with the RT5640 audio codec. This driver is deprecated, use - SND_SOC_INTEL_BYTCR_RT5640_MACH instead for better functionality + SND_SOC_INTEL_BYTCR_RT5640_MACH instead for better functionality. config SND_SOC_INTEL_BYT_MAX98090_MACH tristate "ASoC Audio driver for Intel Baytrail with MAX98090 codec" depends on X86_INTEL_LPSS && I2C - depends on DW_DMAC_CORE=y && (SND_SST_IPC_ACPI = n) + depends on DW_DMAC_CORE && (SND_SST_IPC_ACPI = n) select SND_SOC_INTEL_SST + select SND_SOC_INTEL_SST_FIRMWARE select SND_SOC_INTEL_BAYTRAIL select SND_SOC_MAX98090 help @@ -100,19 +124,20 @@ config SND_SOC_INTEL_BROADWELL_MACH tristate "ASoC Audio DSP support for Intel Broadwell Wildcatpoint" depends on X86_INTEL_LPSS && I2C && DW_DMAC && \ I2C_DESIGNWARE_PLATFORM - depends on DW_DMAC_CORE=y + depends on DW_DMAC_CORE select SND_SOC_INTEL_SST + select SND_SOC_INTEL_SST_FIRMWARE select SND_SOC_INTEL_HASWELL select SND_SOC_RT286 help This adds support for the Wilcatpoint Audio DSP on Intel(R) Broadwell Ultrabook platforms. - Say Y if you have such a device + Say Y if you have such a device. If unsure select "N". config SND_SOC_INTEL_BYTCR_RT5640_MACH tristate "ASoC Audio driver for Intel Baytrail and Baytrail-CR with RT5640 codec" - depends on X86 && I2C + depends on X86 && I2C && ACPI select SND_SOC_RT5640 select SND_SST_MFLD_PLATFORM select SND_SST_IPC_ACPI @@ -120,12 +145,12 @@ config SND_SOC_INTEL_BYTCR_RT5640_MACH help This adds support for ASoC machine driver for Intel(R) Baytrail and Baytrail-CR platforms with RT5640 audio codec. - Say Y if you have such a device + Say Y if you have such a device. If unsure select "N". config SND_SOC_INTEL_BYTCR_RT5651_MACH tristate "ASoC Audio driver for Intel Baytrail and Baytrail-CR with RT5651 codec" - depends on X86 && I2C + depends on X86 && I2C && ACPI select SND_SOC_RT5651 select SND_SST_MFLD_PLATFORM select SND_SST_IPC_ACPI @@ -133,12 +158,12 @@ config SND_SOC_INTEL_BYTCR_RT5651_MACH help This adds support for ASoC machine driver for Intel(R) Baytrail and Baytrail-CR platforms with RT5651 audio codec. - Say Y if you have such a device + Say Y if you have such a device. If unsure select "N". config SND_SOC_INTEL_CHT_BSW_RT5672_MACH tristate "ASoC Audio driver for Intel Cherrytrail & Braswell with RT5672 codec" - depends on X86_INTEL_LPSS && I2C + depends on X86_INTEL_LPSS && I2C && ACPI select SND_SOC_RT5670 select SND_SST_MFLD_PLATFORM select SND_SST_IPC_ACPI @@ -146,12 +171,12 @@ config SND_SOC_INTEL_CHT_BSW_RT5672_MACH help This adds support for ASoC machine driver for Intel(R) Cherrytrail & Braswell platforms with RT5672 audio codec. - Say Y if you have such a device + Say Y if you have such a device. If unsure select "N". config SND_SOC_INTEL_CHT_BSW_RT5645_MACH tristate "ASoC Audio driver for Intel Cherrytrail & Braswell with RT5645/5650 codec" - depends on X86_INTEL_LPSS && I2C + depends on X86_INTEL_LPSS && I2C && ACPI select SND_SOC_RT5645 select SND_SST_MFLD_PLATFORM select SND_SST_IPC_ACPI @@ -163,16 +188,16 @@ config SND_SOC_INTEL_CHT_BSW_RT5645_MACH config SND_SOC_INTEL_CHT_BSW_MAX98090_TI_MACH tristate "ASoC Audio driver for Intel Cherrytrail & Braswell with MAX98090 & TI codec" - depends on X86_INTEL_LPSS && I2C + depends on X86_INTEL_LPSS && I2C && ACPI select SND_SOC_MAX98090 select SND_SOC_TS3A227E select SND_SST_MFLD_PLATFORM select SND_SST_IPC_ACPI select SND_SOC_INTEL_SST_MATCH if ACPI help - This adds support for ASoC machine driver for Intel(R) Cherrytrail & Braswell - platforms with MAX98090 audio codec it also can support TI jack chip as aux device. - If unsure select "N". + This adds support for ASoC machine driver for Intel(R) Cherrytrail & Braswell + platforms with MAX98090 audio codec it also can support TI jack chip as aux device. + If unsure select "N". config SND_SOC_INTEL_SKYLAKE tristate @@ -192,7 +217,7 @@ config SND_SOC_INTEL_SKL_RT286_MACH help This adds support for ASoC machine driver for Skylake platforms with RT286 I2S audio codec. - Say Y if you have such a device + Say Y if you have such a device. If unsure select "N". config SND_SOC_INTEL_SKL_NAU88L25_SSM4567_MACH @@ -207,7 +232,7 @@ config SND_SOC_INTEL_SKL_NAU88L25_SSM4567_MACH help This adds support for ASoC Onboard Codec I2S machine driver. This will create an alsa sound card for NAU88L25 + SSM4567. - Say Y if you have such a device + Say Y if you have such a device. If unsure select "N". config SND_SOC_INTEL_SKL_NAU88L25_MAX98357A_MACH @@ -222,5 +247,5 @@ config SND_SOC_INTEL_SKL_NAU88L25_MAX98357A_MACH help This adds support for ASoC Onboard Codec I2S machine driver. This will create an alsa sound card for NAU88L25 + MAX98357A. - Say Y if you have such a device + Say Y if you have such a device. If unsure select "N". diff --git a/sound/soc/intel/atom/sst/sst_acpi.c b/sound/soc/intel/atom/sst/sst_acpi.c index 3bc4b63b2f9d..4d3184971227 100644 --- a/sound/soc/intel/atom/sst/sst_acpi.c +++ b/sound/soc/intel/atom/sst/sst_acpi.c @@ -28,6 +28,7 @@ #include <linux/firmware.h> #include <linux/pm_runtime.h> #include <linux/pm_qos.h> +#include <linux/dmi.h> #include <linux/acpi.h> #include <asm/platform_sst_audio.h> #include <sound/core.h> @@ -237,6 +238,9 @@ static int sst_acpi_probe(struct platform_device *pdev) dev_err(dev, "No matching machine driver found\n"); return -ENODEV; } + if (mach->machine_quirk) + mach = mach->machine_quirk(mach); + pdata = mach->pdata; ret = kstrtouint(id->id, 16, &dev_id); @@ -320,6 +324,44 @@ static int sst_acpi_remove(struct platform_device *pdev) return 0; } +static unsigned long cht_machine_id; + +#define CHT_SURFACE_MACH 1 + +static int cht_surface_quirk_cb(const struct dmi_system_id *id) +{ + cht_machine_id = CHT_SURFACE_MACH; + return 1; +} + + +static const struct dmi_system_id cht_table[] = { + { + .callback = cht_surface_quirk_cb, + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Microsoft Corporation"), + DMI_MATCH(DMI_PRODUCT_NAME, "Surface 3"), + }, + }, +}; + + +static struct sst_acpi_mach cht_surface_mach = { + "10EC5640", "cht-bsw-rt5645", "intel/fw_sst_22a8.bin", "cht-bsw", NULL, + &chv_platform_data }; + +static struct sst_acpi_mach *cht_quirk(void *arg) +{ + struct sst_acpi_mach *mach = arg; + + dmi_check_system(cht_table); + + if (cht_machine_id == CHT_SURFACE_MACH) + return &cht_surface_mach; + else + return mach; +} + static struct sst_acpi_mach sst_acpi_bytcr[] = { {"10EC5640", "bytcr_rt5640", "intel/fw_sst_0f28.bin", "bytcr_rt5640", NULL, &byt_rvp_platform_data }, @@ -343,7 +385,7 @@ static struct sst_acpi_mach sst_acpi_chv[] = { {"193C9890", "cht-bsw-max98090", "intel/fw_sst_22a8.bin", "cht-bsw", NULL, &chv_platform_data }, /* some CHT-T platforms rely on RT5640, use Baytrail machine driver */ - {"10EC5640", "bytcr_rt5640", "intel/fw_sst_22a8.bin", "bytcr_rt5640", NULL, + {"10EC5640", "bytcr_rt5640", "intel/fw_sst_22a8.bin", "bytcr_rt5640", cht_quirk, &chv_platform_data }, {}, diff --git a/sound/soc/intel/boards/Makefile b/sound/soc/intel/boards/Makefile index a8506774f510..dac03a06bfd8 100644 --- a/sound/soc/intel/boards/Makefile +++ b/sound/soc/intel/boards/Makefile @@ -2,6 +2,7 @@ snd-soc-sst-haswell-objs := haswell.o snd-soc-sst-byt-rt5640-mach-objs := byt-rt5640.o snd-soc-sst-byt-max98090-mach-objs := byt-max98090.o snd-soc-sst-broadwell-objs := broadwell.o +snd-soc-sst-bxt-da7219_max98357a-objs := bxt_da7219_max98357a.o snd-soc-sst-bxt-rt298-objs := bxt_rt298.o snd-soc-sst-bytcr-rt5640-objs := bytcr_rt5640.o snd-soc-sst-bytcr-rt5651-objs := bytcr_rt5651.o @@ -15,6 +16,7 @@ snd-soc-skl_nau88l25_ssm4567-objs := skl_nau88l25_ssm4567.o obj-$(CONFIG_SND_SOC_INTEL_HASWELL_MACH) += snd-soc-sst-haswell.o obj-$(CONFIG_SND_SOC_INTEL_BYT_RT5640_MACH) += snd-soc-sst-byt-rt5640-mach.o obj-$(CONFIG_SND_SOC_INTEL_BYT_MAX98090_MACH) += snd-soc-sst-byt-max98090-mach.o +obj-$(CONFIG_SND_SOC_INTEL_BXT_DA7219_MAX98357A_MACH) += snd-soc-sst-bxt-da7219_max98357a.o obj-$(CONFIG_SND_SOC_INTEL_BXT_RT298_MACH) += snd-soc-sst-bxt-rt298.o obj-$(CONFIG_SND_SOC_INTEL_BROADWELL_MACH) += snd-soc-sst-broadwell.o obj-$(CONFIG_SND_SOC_INTEL_BYTCR_RT5640_MACH) += snd-soc-sst-bytcr-rt5640.o diff --git a/sound/soc/intel/boards/bxt_da7219_max98357a.c b/sound/soc/intel/boards/bxt_da7219_max98357a.c new file mode 100644 index 000000000000..3774b117d365 --- /dev/null +++ b/sound/soc/intel/boards/bxt_da7219_max98357a.c @@ -0,0 +1,460 @@ +/* + * Intel Broxton-P I2S Machine Driver + * + * Copyright (C) 2016, Intel Corporation. All rights reserved. + * + * Modified from: + * Intel Skylake I2S Machine driver + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License version + * 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include <linux/module.h> +#include <linux/platform_device.h> +#include <sound/core.h> +#include <sound/jack.h> +#include <sound/pcm.h> +#include <sound/pcm_params.h> +#include <sound/soc.h> +#include "../../codecs/hdac_hdmi.h" +#include "../../codecs/da7219.h" +#include "../../codecs/da7219-aad.h" + +#define BXT_DIALOG_CODEC_DAI "da7219-hifi" +#define BXT_MAXIM_CODEC_DAI "HiFi" +#define DUAL_CHANNEL 2 + +static struct snd_soc_jack broxton_headset; + +enum { + BXT_DPCM_AUDIO_PB = 0, + BXT_DPCM_AUDIO_CP, + BXT_DPCM_AUDIO_REF_CP, + BXT_DPCM_AUDIO_HDMI1_PB, + BXT_DPCM_AUDIO_HDMI2_PB, + BXT_DPCM_AUDIO_HDMI3_PB, +}; + +static const struct snd_kcontrol_new broxton_controls[] = { + SOC_DAPM_PIN_SWITCH("Headphone Jack"), + SOC_DAPM_PIN_SWITCH("Headset Mic"), + SOC_DAPM_PIN_SWITCH("Spk"), +}; + +static const struct snd_soc_dapm_widget broxton_widgets[] = { + SND_SOC_DAPM_HP("Headphone Jack", NULL), + SND_SOC_DAPM_MIC("Headset Mic", NULL), + SND_SOC_DAPM_SPK("Spk", NULL), + SND_SOC_DAPM_MIC("SoC DMIC", NULL), + SND_SOC_DAPM_SPK("HDMI1", NULL), + SND_SOC_DAPM_SPK("HDMI2", NULL), + SND_SOC_DAPM_SPK("HDMI3", NULL), +}; + +static const struct snd_soc_dapm_route broxton_map[] = { + /* HP jack connectors - unknown if we have jack detection */ + {"Headphone Jack", NULL, "HPL"}, + {"Headphone Jack", NULL, "HPR"}, + + /* speaker */ + {"Spk", NULL, "Speaker"}, + + /* other jacks */ + {"MIC", NULL, "Headset Mic"}, + + /* digital mics */ + {"DMic", NULL, "SoC DMIC"}, + + /* CODEC BE connections */ + {"HiFi Playback", NULL, "ssp5 Tx"}, + {"ssp5 Tx", NULL, "codec0_out"}, + + {"Playback", NULL, "ssp1 Tx"}, + {"ssp1 Tx", NULL, "codec1_out"}, + + {"codec0_in", NULL, "ssp1 Rx"}, + {"ssp1 Rx", NULL, "Capture"}, + + {"HDMI1", NULL, "hif5 Output"}, + {"HDMI2", NULL, "hif6 Output"}, + {"HDMI3", NULL, "hif7 Output"}, + + {"hifi3", NULL, "iDisp3 Tx"}, + {"iDisp3 Tx", NULL, "iDisp3_out"}, + {"hifi2", NULL, "iDisp2 Tx"}, + {"iDisp2 Tx", NULL, "iDisp2_out"}, + {"hifi1", NULL, "iDisp1 Tx"}, + {"iDisp1 Tx", NULL, "iDisp1_out"}, + + /* DMIC */ + {"dmic01_hifi", NULL, "DMIC01 Rx"}, + {"DMIC01 Rx", NULL, "DMIC AIF"}, +}; + +static int broxton_ssp_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); + struct snd_mask *fmt = hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT); + + /* The ADSP will convert the FE rate to 48k, stereo */ + rate->min = rate->max = 48000; + channels->min = channels->max = DUAL_CHANNEL; + + /* set SSP to 24 bit */ + snd_mask_none(fmt); + snd_mask_set(fmt, SNDRV_PCM_FORMAT_S24_LE); + + return 0; +} + +static int broxton_da7219_codec_init(struct snd_soc_pcm_runtime *rtd) +{ + int ret; + struct snd_soc_codec *codec = rtd->codec; + + /* + * 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, &broxton_headset, + NULL, 0); + if (ret) { + dev_err(rtd->dev, "Headset Jack creation failed: %d\n", ret); + return ret; + } + + da7219_aad_jack_det(codec, &broxton_headset); + + snd_soc_dapm_ignore_suspend(&rtd->card->dapm, "SoC DMIC"); + + return ret; +} + +static int broxton_hdmi_init(struct snd_soc_pcm_runtime *rtd) +{ + struct snd_soc_dai *dai = rtd->codec_dai; + + return hdac_hdmi_jack_init(dai, BXT_DPCM_AUDIO_HDMI1_PB + dai->id); +} + +static int broxton_da7219_fe_init(struct snd_soc_pcm_runtime *rtd) +{ + struct snd_soc_dapm_context *dapm; + struct snd_soc_component *component = rtd->cpu_dai->component; + + dapm = snd_soc_component_get_dapm(component); + snd_soc_dapm_ignore_suspend(dapm, "Reference Capture"); + + return 0; +} + +static unsigned int rates[] = { + 48000, +}; + +static struct snd_pcm_hw_constraint_list constraints_rates = { + .count = ARRAY_SIZE(rates), + .list = rates, + .mask = 0, +}; + +static unsigned int channels[] = { + DUAL_CHANNEL, +}; + +static struct snd_pcm_hw_constraint_list constraints_channels = { + .count = ARRAY_SIZE(channels), + .list = channels, + .mask = 0, +}; + +static int bxt_fe_startup(struct snd_pcm_substream *substream) +{ + struct snd_pcm_runtime *runtime = substream->runtime; + + /* + * On this platform for PCM device we support, + * 48Khz + * stereo + * 16 bit audio + */ + + runtime->hw.channels_max = DUAL_CHANNEL; + snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS, + &constraints_channels); + + runtime->hw.formats = SNDRV_PCM_FMTBIT_S16_LE; + snd_pcm_hw_constraint_msbits(runtime, 0, 16, 16); + + snd_pcm_hw_constraint_list(runtime, 0, + SNDRV_PCM_HW_PARAM_RATE, &constraints_rates); + + return 0; +} + +static const struct snd_soc_ops broxton_da7219_fe_ops = { + .startup = bxt_fe_startup, +}; + +static int broxton_da7219_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_dai *codec_dai = rtd->codec_dai; + int ret; + + ret = snd_soc_dai_set_sysclk(codec_dai, + DA7219_CLKSRC_MCLK, 19200000, SND_SOC_CLOCK_IN); + if (ret < 0) + dev_err(codec_dai->dev, "can't set codec sysclk configuration\n"); + + ret = snd_soc_dai_set_pll(codec_dai, 0, + DA7219_SYSCLK_PLL_SRM, 0, DA7219_PLL_FREQ_OUT_98304); + if (ret < 0) { + dev_err(codec_dai->dev, "failed to start PLL: %d\n", ret); + return -EIO; + } + + return ret; +} + +static int broxton_da7219_hw_free(struct snd_pcm_substream *substream) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_dai *codec_dai = rtd->codec_dai; + int ret; + + ret = snd_soc_dai_set_pll(codec_dai, 0, + DA7219_SYSCLK_MCLK, 0, 0); + if (ret < 0) { + dev_err(codec_dai->dev, "failed to stop PLL: %d\n", ret); + return -EIO; + } + + return ret; +} + +static struct snd_soc_ops broxton_da7219_ops = { + .hw_params = broxton_da7219_hw_params, + .hw_free = broxton_da7219_hw_free, +}; + +/* broxton digital audio interface glue - connects codec <--> CPU */ +static struct snd_soc_dai_link broxton_dais[] = { + /* Front End DAI links */ + [BXT_DPCM_AUDIO_PB] + { + .name = "Bxt Audio Port", + .stream_name = "Audio", + .cpu_dai_name = "System Pin", + .platform_name = "0000:00:0e.0", + .dynamic = 1, + .codec_name = "snd-soc-dummy", + .codec_dai_name = "snd-soc-dummy-dai", + .nonatomic = 1, + .init = broxton_da7219_fe_init, + .trigger = { + SND_SOC_DPCM_TRIGGER_POST, SND_SOC_DPCM_TRIGGER_POST}, + .dpcm_playback = 1, + .ops = &broxton_da7219_fe_ops, + }, + [BXT_DPCM_AUDIO_CP] + { + .name = "Bxt Audio Capture Port", + .stream_name = "Audio Record", + .cpu_dai_name = "System Pin", + .platform_name = "0000:00:0e.0", + .dynamic = 1, + .codec_name = "snd-soc-dummy", + .codec_dai_name = "snd-soc-dummy-dai", + .nonatomic = 1, + .trigger = { + SND_SOC_DPCM_TRIGGER_POST, SND_SOC_DPCM_TRIGGER_POST}, + .dpcm_capture = 1, + .ops = &broxton_da7219_fe_ops, + }, + [BXT_DPCM_AUDIO_REF_CP] + { + .name = "Bxt Audio Reference cap", + .stream_name = "Refcap", + .cpu_dai_name = "Reference Pin", + .codec_name = "snd-soc-dummy", + .codec_dai_name = "snd-soc-dummy-dai", + .platform_name = "0000:00:0e.0", + .init = NULL, + .dpcm_capture = 1, + .ignore_suspend = 1, + .nonatomic = 1, + .dynamic = 1, + }, + [BXT_DPCM_AUDIO_HDMI1_PB] + { + .name = "Bxt HDMI Port1", + .stream_name = "Hdmi1", + .cpu_dai_name = "HDMI1 Pin", + .codec_name = "snd-soc-dummy", + .codec_dai_name = "snd-soc-dummy-dai", + .platform_name = "0000:00:0e.0", + .dpcm_playback = 1, + .init = NULL, + .nonatomic = 1, + .dynamic = 1, + }, + [BXT_DPCM_AUDIO_HDMI2_PB] + { + .name = "Bxt HDMI Port2", + .stream_name = "Hdmi2", + .cpu_dai_name = "HDMI2 Pin", + .codec_name = "snd-soc-dummy", + .codec_dai_name = "snd-soc-dummy-dai", + .platform_name = "0000:00:0e.0", + .dpcm_playback = 1, + .init = NULL, + .nonatomic = 1, + .dynamic = 1, + }, + [BXT_DPCM_AUDIO_HDMI3_PB] + { + .name = "Bxt HDMI Port3", + .stream_name = "Hdmi3", + .cpu_dai_name = "HDMI3 Pin", + .codec_name = "snd-soc-dummy", + .codec_dai_name = "snd-soc-dummy-dai", + .platform_name = "0000:00:0e.0", + .dpcm_playback = 1, + .init = NULL, + .nonatomic = 1, + .dynamic = 1, + }, + /* Back End DAI links */ + { + /* SSP5 - Codec */ + .name = "SSP5-Codec", + .id = 0, + .cpu_dai_name = "SSP5 Pin", + .platform_name = "0000:00:0e.0", + .no_pcm = 1, + .codec_name = "MX98357A:00", + .codec_dai_name = BXT_MAXIM_CODEC_DAI, + .dai_fmt = SND_SOC_DAIFMT_I2S | + SND_SOC_DAIFMT_NB_NF | + SND_SOC_DAIFMT_CBS_CFS, + .ignore_pmdown_time = 1, + .be_hw_params_fixup = broxton_ssp_fixup, + .dpcm_playback = 1, + }, + { + /* SSP1 - Codec */ + .name = "SSP1-Codec", + .id = 1, + .cpu_dai_name = "SSP1 Pin", + .platform_name = "0000:00:0e.0", + .no_pcm = 1, + .codec_name = "i2c-DLGS7219:00", + .codec_dai_name = BXT_DIALOG_CODEC_DAI, + .init = broxton_da7219_codec_init, + .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | + SND_SOC_DAIFMT_CBS_CFS, + .ignore_pmdown_time = 1, + .be_hw_params_fixup = broxton_ssp_fixup, + .ops = &broxton_da7219_ops, + .dpcm_playback = 1, + .dpcm_capture = 1, + }, + { + .name = "dmic01", + .id = 2, + .cpu_dai_name = "DMIC01 Pin", + .codec_name = "dmic-codec", + .codec_dai_name = "dmic-hifi", + .platform_name = "0000:00:0e.0", + .ignore_suspend = 1, + .dpcm_capture = 1, + .no_pcm = 1, + }, + { + .name = "iDisp1", + .id = 3, + .cpu_dai_name = "iDisp1 Pin", + .codec_name = "ehdaudio0D2", + .codec_dai_name = "intel-hdmi-hifi1", + .platform_name = "0000:00:0e.0", + .init = broxton_hdmi_init, + .dpcm_playback = 1, + .no_pcm = 1, + }, + { + .name = "iDisp2", + .id = 4, + .cpu_dai_name = "iDisp2 Pin", + .codec_name = "ehdaudio0D2", + .codec_dai_name = "intel-hdmi-hifi2", + .platform_name = "0000:00:0e.0", + .init = broxton_hdmi_init, + .dpcm_playback = 1, + .no_pcm = 1, + }, + { + .name = "iDisp3", + .id = 5, + .cpu_dai_name = "iDisp3 Pin", + .codec_name = "ehdaudio0D2", + .codec_dai_name = "intel-hdmi-hifi3", + .platform_name = "0000:00:0e.0", + .init = broxton_hdmi_init, + .dpcm_playback = 1, + .no_pcm = 1, + }, +}; + +/* broxton audio machine driver for SPT + da7219 */ +static struct snd_soc_card broxton_audio_card = { + .name = "bxtda7219max", + .owner = THIS_MODULE, + .dai_link = broxton_dais, + .num_links = ARRAY_SIZE(broxton_dais), + .controls = broxton_controls, + .num_controls = ARRAY_SIZE(broxton_controls), + .dapm_widgets = broxton_widgets, + .num_dapm_widgets = ARRAY_SIZE(broxton_widgets), + .dapm_routes = broxton_map, + .num_dapm_routes = ARRAY_SIZE(broxton_map), + .fully_routed = true, +}; + +static int broxton_audio_probe(struct platform_device *pdev) +{ + broxton_audio_card.dev = &pdev->dev; + return devm_snd_soc_register_card(&pdev->dev, &broxton_audio_card); +} + +static struct platform_driver broxton_audio = { + .probe = broxton_audio_probe, + .driver = { + .name = "bxt_da7219_max98357a_i2s", + .pm = &snd_soc_pm_ops, + }, +}; +module_platform_driver(broxton_audio) + +/* Module information */ +MODULE_DESCRIPTION("Audio Machine driver-DA7219 & MAX98357A in I2S mode"); +MODULE_AUTHOR("Sathyanarayana Nujella <sathyanarayana.nujella@intel.com>"); +MODULE_AUTHOR("Rohit Ainapure <rohit.m.ainapure@intel.com>"); +MODULE_AUTHOR("Harsha Priya <harshapriya.n@intel.com>"); +MODULE_AUTHOR("Conrad Cooke <conrad.cooke@intel.com>"); +MODULE_LICENSE("GPL v2"); +MODULE_ALIAS("platform:bxt_da7219_max98357a_i2s"); diff --git a/sound/soc/intel/boards/bxt_rt298.c b/sound/soc/intel/boards/bxt_rt298.c index f4787515c0ed..253d7bfbf511 100644 --- a/sound/soc/intel/boards/bxt_rt298.c +++ b/sound/soc/intel/boards/bxt_rt298.c @@ -33,6 +33,7 @@ enum { BXT_DPCM_AUDIO_PB = 0, BXT_DPCM_AUDIO_CP, BXT_DPCM_AUDIO_REF_CP, + BXT_DPCM_AUDIO_DMIC_CP, BXT_DPCM_AUDIO_HDMI1_PB, BXT_DPCM_AUDIO_HDMI2_PB, BXT_DPCM_AUDIO_HDMI3_PB, @@ -88,6 +89,7 @@ static const struct snd_soc_dapm_route broxton_rt298_map[] = { /* CODEC BE connections */ { "AIF1 Playback", NULL, "ssp5 Tx"}, { "ssp5 Tx", NULL, "codec0_out"}, + { "ssp5 Tx", NULL, "codec1_out"}, { "codec0_in", NULL, "ssp5 Rx" }, { "ssp5 Rx", NULL, "AIF1 Capture" }, @@ -104,6 +106,17 @@ static const struct snd_soc_dapm_route broxton_rt298_map[] = { }; +static int broxton_rt298_fe_init(struct snd_soc_pcm_runtime *rtd) +{ + struct snd_soc_dapm_context *dapm; + struct snd_soc_component *component = rtd->cpu_dai->component; + + dapm = snd_soc_component_get_dapm(component); + snd_soc_dapm_ignore_suspend(dapm, "Reference Capture"); + + return 0; +} + static int broxton_rt298_codec_init(struct snd_soc_pcm_runtime *rtd) { struct snd_soc_codec *codec = rtd->codec; @@ -118,6 +131,9 @@ static int broxton_rt298_codec_init(struct snd_soc_pcm_runtime *rtd) return ret; rt298_mic_detect(codec, &broxton_headset); + + snd_soc_dapm_ignore_suspend(&rtd->card->dapm, "SoC DMIC"); + return 0; } @@ -169,6 +185,89 @@ static struct snd_soc_ops broxton_rt298_ops = { .hw_params = broxton_rt298_hw_params, }; +static unsigned int rates[] = { + 48000, +}; + +static struct snd_pcm_hw_constraint_list constraints_rates = { + .count = ARRAY_SIZE(rates), + .list = rates, + .mask = 0, +}; + +static int broxton_dmic_fixup(struct snd_soc_pcm_runtime *rtd, + struct snd_pcm_hw_params *params) +{ + struct snd_interval *channels = hw_param_interval(params, + SNDRV_PCM_HW_PARAM_CHANNELS); + if (params_channels(params) == 2) + channels->min = channels->max = 2; + else + channels->min = channels->max = 4; + + return 0; +} + +static unsigned int channels_dmic[] = { + 2, 4, +}; + +static struct snd_pcm_hw_constraint_list constraints_dmic_channels = { + .count = ARRAY_SIZE(channels_dmic), + .list = channels_dmic, + .mask = 0, +}; + +static int broxton_dmic_startup(struct snd_pcm_substream *substream) +{ + struct snd_pcm_runtime *runtime = substream->runtime; + + runtime->hw.channels_max = 4; + snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS, + &constraints_dmic_channels); + + return snd_pcm_hw_constraint_list(substream->runtime, 0, + SNDRV_PCM_HW_PARAM_RATE, &constraints_rates); +} + +static struct snd_soc_ops broxton_dmic_ops = { + .startup = broxton_dmic_startup, +}; + +static unsigned int channels[] = { + 2, +}; + +static struct snd_pcm_hw_constraint_list constraints_channels = { + .count = ARRAY_SIZE(channels), + .list = channels, + .mask = 0, +}; + +static int bxt_fe_startup(struct snd_pcm_substream *substream) +{ + struct snd_pcm_runtime *runtime = substream->runtime; + + /* + * on this platform for PCM device we support: + * 48Khz + * stereo + */ + + runtime->hw.channels_max = 2; + snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS, + &constraints_channels); + + snd_pcm_hw_constraint_list(runtime, 0, + SNDRV_PCM_HW_PARAM_RATE, &constraints_rates); + + return 0; +} + +static const struct snd_soc_ops broxton_rt286_fe_ops = { + .startup = bxt_fe_startup, +}; + /* broxton digital audio interface glue - connects codec <--> CPU */ static struct snd_soc_dai_link broxton_rt298_dais[] = { /* Front End DAI links */ @@ -182,8 +281,10 @@ static struct snd_soc_dai_link broxton_rt298_dais[] = { .dynamic = 1, .codec_name = "snd-soc-dummy", .codec_dai_name = "snd-soc-dummy-dai", + .init = broxton_rt298_fe_init, .trigger = {SND_SOC_DPCM_TRIGGER_POST, SND_SOC_DPCM_TRIGGER_POST}, .dpcm_playback = 1, + .ops = &broxton_rt286_fe_ops, }, [BXT_DPCM_AUDIO_CP] { @@ -197,6 +298,7 @@ static struct snd_soc_dai_link broxton_rt298_dais[] = { .codec_dai_name = "snd-soc-dummy-dai", .trigger = {SND_SOC_DPCM_TRIGGER_POST, SND_SOC_DPCM_TRIGGER_POST}, .dpcm_capture = 1, + .ops = &broxton_rt286_fe_ops, }, [BXT_DPCM_AUDIO_REF_CP] { @@ -211,6 +313,20 @@ static struct snd_soc_dai_link broxton_rt298_dais[] = { .nonatomic = 1, .dynamic = 1, }, + [BXT_DPCM_AUDIO_DMIC_CP] + { + .name = "Bxt Audio DMIC cap", + .stream_name = "dmiccap", + .cpu_dai_name = "DMIC Pin", + .codec_name = "snd-soc-dummy", + .codec_dai_name = "snd-soc-dummy-dai", + .platform_name = "0000:00:0e.0", + .init = NULL, + .dpcm_capture = 1, + .nonatomic = 1, + .dynamic = 1, + .ops = &broxton_dmic_ops, + }, [BXT_DPCM_AUDIO_HDMI1_PB] { .name = "Bxt HDMI Port1", @@ -276,6 +392,7 @@ static struct snd_soc_dai_link broxton_rt298_dais[] = { .codec_name = "dmic-codec", .codec_dai_name = "dmic-hifi", .platform_name = "0000:00:0e.0", + .be_hw_params_fixup = broxton_dmic_fixup, .ignore_suspend = 1, .dpcm_capture = 1, .no_pcm = 1, @@ -341,6 +458,7 @@ static struct platform_driver broxton_audio = { .probe = broxton_audio_probe, .driver = { .name = "bxt_alc298s_i2s", + .pm = &snd_soc_pm_ops, }, }; module_platform_driver(broxton_audio) diff --git a/sound/soc/intel/boards/cht_bsw_rt5645.c b/sound/soc/intel/boards/cht_bsw_rt5645.c index d7ef292c402d..56056ed7fcfd 100644 --- a/sound/soc/intel/boards/cht_bsw_rt5645.c +++ b/sound/soc/intel/boards/cht_bsw_rt5645.c @@ -30,6 +30,7 @@ #include <sound/jack.h> #include "../../codecs/rt5645.h" #include "../atom/sst-atom-controls.h" +#include "../common/sst-acpi.h" #define CHT_PLAT_CLK_3_HZ 19200000 #define CHT_CODEC_DAI "rt5645-aif1" @@ -340,10 +341,13 @@ static struct snd_soc_card snd_soc_card_chtrt5650 = { }; static struct cht_acpi_card snd_soc_cards[] = { + {"10EC5640", CODEC_TYPE_RT5645, &snd_soc_card_chtrt5645}, {"10EC5645", CODEC_TYPE_RT5645, &snd_soc_card_chtrt5645}, {"10EC5650", CODEC_TYPE_RT5650, &snd_soc_card_chtrt5650}, }; +static char cht_rt5640_codec_name[16]; /* i2c-<HID>:00 with HID being 8 chars */ + static int snd_cht_mc_probe(struct platform_device *pdev) { int ret_val = 0; @@ -351,6 +355,9 @@ static int snd_cht_mc_probe(struct platform_device *pdev) struct cht_mc_private *drv; struct snd_soc_card *card = snd_soc_cards[0].soc_card; char codec_name[16]; + struct sst_acpi_mach *mach; + const char *i2c_name = NULL; + int dai_index = 0; drv = devm_kzalloc(&pdev->dev, sizeof(*drv), GFP_ATOMIC); if (!drv) @@ -366,12 +373,23 @@ static int snd_cht_mc_probe(struct platform_device *pdev) } } card->dev = &pdev->dev; + mach = card->dev->platform_data; sprintf(codec_name, "i2c-%s:00", drv->acpi_card->codec_id); /* set correct codec name */ for (i = 0; i < ARRAY_SIZE(cht_dailink); i++) - if (!strcmp(card->dai_link[i].codec_name, "i2c-10EC5645:00")) + if (!strcmp(card->dai_link[i].codec_name, "i2c-10EC5645:00")) { card->dai_link[i].codec_name = kstrdup(codec_name, GFP_KERNEL); + dai_index = i; + } + + /* fixup codec name based on HID */ + i2c_name = sst_acpi_find_name_from_hid(mach->id); + if (i2c_name != NULL) { + snprintf(cht_rt5640_codec_name, sizeof(cht_rt5640_codec_name), + "%s%s", "i2c-", i2c_name); + cht_dailink[dai_index].codec_name = cht_rt5640_codec_name; + } snd_soc_card_set_drvdata(card, drv); ret_val = devm_snd_soc_register_card(&pdev->dev, card); diff --git a/sound/soc/intel/boards/skl_nau88l25_max98357a.c b/sound/soc/intel/boards/skl_nau88l25_max98357a.c index d2808652b974..25db5be7fdfa 100644 --- a/sound/soc/intel/boards/skl_nau88l25_max98357a.c +++ b/sound/soc/intel/boards/skl_nau88l25_max98357a.c @@ -23,12 +23,15 @@ #include <sound/soc.h> #include "../../codecs/nau8825.h" #include "../../codecs/hdac_hdmi.h" +#include "../skylake/skl.h" #define SKL_NUVOTON_CODEC_DAI "nau8825-hifi" #define SKL_MAXIM_CODEC_DAI "HiFi" +#define DMIC_CH(p) p->list[p->count-1] static struct snd_soc_jack skylake_headset; static struct snd_soc_card skylake_audio_card; +static const struct snd_pcm_hw_constraint_list *dmic_constraints; struct skl_hdmi_pcm { struct list_head head; @@ -339,7 +342,7 @@ static int skylake_dmic_fixup(struct snd_soc_pcm_runtime *rtd, struct snd_interval *channels = hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS); - if (params_channels(params) == 2) + if (params_channels(params) == 2 || DMIC_CH(dmic_constraints) == 2) channels->min = channels->max = 2; else channels->min = channels->max = 4; @@ -357,13 +360,23 @@ static struct snd_pcm_hw_constraint_list constraints_dmic_channels = { .mask = 0, }; +static const unsigned int dmic_2ch[] = { + 2, +}; + +static const struct snd_pcm_hw_constraint_list constraints_dmic_2ch = { + .count = ARRAY_SIZE(dmic_2ch), + .list = dmic_2ch, + .mask = 0, +}; + static int skylake_dmic_startup(struct snd_pcm_substream *substream) { struct snd_pcm_runtime *runtime = substream->runtime; - runtime->hw.channels_max = 4; + runtime->hw.channels_max = DMIC_CH(dmic_constraints); snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS, - &constraints_dmic_channels); + dmic_constraints); return snd_pcm_hw_constraint_list(substream->runtime, 0, SNDRV_PCM_HW_PARAM_RATE, &constraints_rates); @@ -382,8 +395,22 @@ static struct snd_pcm_hw_constraint_list constraints_16000 = { .list = rates_16000, }; +static const unsigned int ch_mono[] = { + 1, +}; + +static const struct snd_pcm_hw_constraint_list constraints_refcap = { + .count = ARRAY_SIZE(ch_mono), + .list = ch_mono, +}; + static int skylake_refcap_startup(struct snd_pcm_substream *substream) { + substream->runtime->hw.channels_max = 1; + snd_pcm_hw_constraint_list(substream->runtime, 0, + SNDRV_PCM_HW_PARAM_CHANNELS, + &constraints_refcap); + return snd_pcm_hw_constraint_list(substream->runtime, 0, SNDRV_PCM_HW_PARAM_RATE, &constraints_16000); @@ -610,6 +637,7 @@ static struct snd_soc_card skylake_audio_card = { static int skylake_audio_probe(struct platform_device *pdev) { struct skl_nau8825_private *ctx; + struct skl_machine_pdata *pdata; ctx = devm_kzalloc(&pdev->dev, sizeof(*ctx), GFP_ATOMIC); if (!ctx) @@ -620,15 +648,27 @@ static int skylake_audio_probe(struct platform_device *pdev) skylake_audio_card.dev = &pdev->dev; snd_soc_card_set_drvdata(&skylake_audio_card, ctx); + pdata = dev_get_drvdata(&pdev->dev); + if (pdata) + dmic_constraints = pdata->dmic_num == 2 ? + &constraints_dmic_2ch : &constraints_dmic_channels; + return devm_snd_soc_register_card(&pdev->dev, &skylake_audio_card); } +static const struct platform_device_id skl_board_ids[] = { + { .name = "skl_n88l25_m98357a" }, + { .name = "kbl_n88l25_m98357a" }, + { } +}; + static struct platform_driver skylake_audio = { .probe = skylake_audio_probe, .driver = { - .name = "skl_nau88l25_max98357a_i2s", + .name = "skl_n88l25_m98357a", .pm = &snd_soc_pm_ops, }, + .id_table = skl_board_ids, }; module_platform_driver(skylake_audio) @@ -637,4 +677,5 @@ module_platform_driver(skylake_audio) MODULE_DESCRIPTION("Audio Machine driver-NAU88L25 & MAX98357A in I2S mode"); MODULE_AUTHOR("Rohit Ainapure <rohit.m.ainapure@intel.com"); MODULE_LICENSE("GPL v2"); -MODULE_ALIAS("platform:skl_nau88l25_max98357a_i2s"); +MODULE_ALIAS("platform:skl_n88l25_m98357a"); +MODULE_ALIAS("platform:kbl_n88l25_m98357a"); diff --git a/sound/soc/intel/boards/skl_nau88l25_ssm4567.c b/sound/soc/intel/boards/skl_nau88l25_ssm4567.c index e19aa99c4f72..69c5d5da4e86 100644 --- a/sound/soc/intel/boards/skl_nau88l25_ssm4567.c +++ b/sound/soc/intel/boards/skl_nau88l25_ssm4567.c @@ -27,12 +27,15 @@ #include <sound/pcm_params.h> #include "../../codecs/nau8825.h" #include "../../codecs/hdac_hdmi.h" +#include "../skylake/skl.h" #define SKL_NUVOTON_CODEC_DAI "nau8825-hifi" #define SKL_SSM_CODEC_DAI "ssm4567-hifi" +#define DMIC_CH(p) p->list[p->count-1] static struct snd_soc_jack skylake_headset; static struct snd_soc_card skylake_audio_card; +static const struct snd_pcm_hw_constraint_list *dmic_constraints; struct skl_hdmi_pcm { struct list_head head; @@ -367,7 +370,7 @@ static int skylake_dmic_fixup(struct snd_soc_pcm_runtime *rtd, { struct snd_interval *channels = hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS); - if (params_channels(params) == 2) + if (params_channels(params) == 2 || DMIC_CH(dmic_constraints) == 2) channels->min = channels->max = 2; else channels->min = channels->max = 4; @@ -405,13 +408,23 @@ static struct snd_pcm_hw_constraint_list constraints_dmic_channels = { .mask = 0, }; +static const unsigned int dmic_2ch[] = { + 2, +}; + +static const struct snd_pcm_hw_constraint_list constraints_dmic_2ch = { + .count = ARRAY_SIZE(dmic_2ch), + .list = dmic_2ch, + .mask = 0, +}; + static int skylake_dmic_startup(struct snd_pcm_substream *substream) { struct snd_pcm_runtime *runtime = substream->runtime; - runtime->hw.channels_max = 4; + runtime->hw.channels_max = DMIC_CH(dmic_constraints); snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS, - &constraints_dmic_channels); + dmic_constraints); return snd_pcm_hw_constraint_list(substream->runtime, 0, SNDRV_PCM_HW_PARAM_RATE, &constraints_rates); @@ -430,8 +443,22 @@ static struct snd_pcm_hw_constraint_list constraints_16000 = { .list = rates_16000, }; +static const unsigned int ch_mono[] = { + 1, +}; + +static const struct snd_pcm_hw_constraint_list constraints_refcap = { + .count = ARRAY_SIZE(ch_mono), + .list = ch_mono, +}; + static int skylake_refcap_startup(struct snd_pcm_substream *substream) { + substream->runtime->hw.channels_max = 1; + snd_pcm_hw_constraint_list(substream->runtime, 0, + SNDRV_PCM_HW_PARAM_CHANNELS, + &constraints_refcap); + return snd_pcm_hw_constraint_list(substream->runtime, 0, SNDRV_PCM_HW_PARAM_RATE, &constraints_16000); @@ -662,6 +689,7 @@ static struct snd_soc_card skylake_audio_card = { static int skylake_audio_probe(struct platform_device *pdev) { struct skl_nau88125_private *ctx; + struct skl_machine_pdata *pdata; ctx = devm_kzalloc(&pdev->dev, sizeof(*ctx), GFP_ATOMIC); if (!ctx) @@ -672,15 +700,27 @@ static int skylake_audio_probe(struct platform_device *pdev) skylake_audio_card.dev = &pdev->dev; snd_soc_card_set_drvdata(&skylake_audio_card, ctx); + pdata = dev_get_drvdata(&pdev->dev); + if (pdata) + dmic_constraints = pdata->dmic_num == 2 ? + &constraints_dmic_2ch : &constraints_dmic_channels; + return devm_snd_soc_register_card(&pdev->dev, &skylake_audio_card); } +static const struct platform_device_id skl_board_ids[] = { + { .name = "skl_n88l25_s4567" }, + { .name = "kbl_n88l25_s4567" }, + { } +}; + static struct platform_driver skylake_audio = { .probe = skylake_audio_probe, .driver = { - .name = "skl_nau88l25_ssm4567_i2s", + .name = "skl_n88l25_s4567", .pm = &snd_soc_pm_ops, }, + .id_table = skl_board_ids, }; module_platform_driver(skylake_audio) @@ -693,4 +733,5 @@ MODULE_AUTHOR("Sathya Prakash M R <sathya.prakash.m.r@intel.com>"); MODULE_AUTHOR("Yong Zhi <yong.zhi@intel.com>"); MODULE_DESCRIPTION("Intel Audio Machine driver for SKL with NAU88L25 and SSM4567 in I2S Mode"); MODULE_LICENSE("GPL v2"); -MODULE_ALIAS("platform:skl_nau88l25_ssm4567_i2s"); +MODULE_ALIAS("platform:skl_n88l25_s4567"); +MODULE_ALIAS("platform:kbl_n88l25_s4567"); diff --git a/sound/soc/intel/boards/skl_rt286.c b/sound/soc/intel/boards/skl_rt286.c index 426b48233fdb..88c61e8cb87f 100644 --- a/sound/soc/intel/boards/skl_rt286.c +++ b/sound/soc/intel/boards/skl_rt286.c @@ -505,12 +505,20 @@ static int skylake_audio_probe(struct platform_device *pdev) return devm_snd_soc_register_card(&pdev->dev, &skylake_rt286); } +static const struct platform_device_id skl_board_ids[] = { + { .name = "skl_alc286s_i2s" }, + { .name = "kbl_alc286s_i2s" }, + { } +}; + static struct platform_driver skylake_audio = { .probe = skylake_audio_probe, .driver = { .name = "skl_alc286s_i2s", .pm = &snd_soc_pm_ops, }, + .id_table = skl_board_ids, + }; module_platform_driver(skylake_audio) @@ -520,3 +528,4 @@ MODULE_AUTHOR("Omair Mohammed Abdullah <omair.m.abdullah@intel.com>"); MODULE_DESCRIPTION("Intel SST Audio for Skylake"); MODULE_LICENSE("GPL v2"); MODULE_ALIAS("platform:skl_alc286s_i2s"); +MODULE_ALIAS("platform:kbl_alc286s_i2s"); diff --git a/sound/soc/intel/common/Makefile b/sound/soc/intel/common/Makefile index fbbb25c2ceed..1a35149bcad7 100644 --- a/sound/soc/intel/common/Makefile +++ b/sound/soc/intel/common/Makefile @@ -2,9 +2,9 @@ snd-soc-sst-dsp-objs := sst-dsp.o snd-soc-sst-acpi-objs := sst-acpi.o snd-soc-sst-match-objs := sst-match-acpi.o snd-soc-sst-ipc-objs := sst-ipc.o - -snd-soc-sst-dsp-$(CONFIG_DW_DMAC_CORE) += sst-firmware.o +snd-soc-sst-firmware-objs := sst-firmware.o obj-$(CONFIG_SND_SOC_INTEL_SST) += snd-soc-sst-dsp.o snd-soc-sst-ipc.o obj-$(CONFIG_SND_SOC_INTEL_SST_ACPI) += snd-soc-sst-acpi.o obj-$(CONFIG_SND_SOC_INTEL_SST_MATCH) += snd-soc-sst-match.o +obj-$(CONFIG_SND_SOC_INTEL_SST_FIRMWARE) += snd-soc-sst-firmware.o diff --git a/sound/soc/intel/common/sst-acpi.h b/sound/soc/intel/common/sst-acpi.h index 8398cb227ba9..5d2949324d0e 100644 --- a/sound/soc/intel/common/sst-acpi.h +++ b/sound/soc/intel/common/sst-acpi.h @@ -20,7 +20,7 @@ #if IS_ENABLED(CONFIG_ACPI) const char *sst_acpi_find_name_from_hid(const u8 hid[ACPI_ID_LEN]); #else -inline const char *sst_acpi_find_name_from_hid(const u8 hid[ACPI_ID_LEN]) +static inline const char *sst_acpi_find_name_from_hid(const u8 hid[ACPI_ID_LEN]) { return NULL; } @@ -40,6 +40,6 @@ struct sst_acpi_mach { /* board name */ const char *board; - void (*machine_quirk)(void); + struct sst_acpi_mach * (*machine_quirk)(void *arg); void *pdata; }; diff --git a/sound/soc/intel/common/sst-dsp-priv.h b/sound/soc/intel/common/sst-dsp-priv.h index 97dc1ae05e69..d13c84364c3c 100644 --- a/sound/soc/intel/common/sst-dsp-priv.h +++ b/sound/soc/intel/common/sst-dsp-priv.h @@ -383,10 +383,6 @@ struct sst_mem_block *sst_mem_block_register(struct sst_dsp *dsp, u32 offset, u32 index, void *private); void sst_mem_block_unregister_all(struct sst_dsp *dsp); -/* Create/Free DMA resources */ -int sst_dma_new(struct sst_dsp *sst); -void sst_dma_free(struct sst_dma *dma); - u32 sst_dsp_get_offset(struct sst_dsp *dsp, u32 offset, enum sst_mem_type type); #endif diff --git a/sound/soc/intel/common/sst-dsp.c b/sound/soc/intel/common/sst-dsp.c index b5bbdf4fe93a..c00ede4ea4d7 100644 --- a/sound/soc/intel/common/sst-dsp.c +++ b/sound/soc/intel/common/sst-dsp.c @@ -285,7 +285,7 @@ int sst_dsp_register_poll(struct sst_dsp *ctx, u32 offset, u32 mask, } reg = sst_dsp_shim_read_unlocked(ctx, offset); - dev_info(ctx->dev, "FW Poll Status: reg=%#x %s %s\n", reg, operation, + dev_dbg(ctx->dev, "FW Poll Status: reg=%#x %s %s\n", reg, operation, (time < timeout) ? "successful" : "timedout"); ret = time < timeout ? 0 : -ETIME; @@ -420,73 +420,6 @@ void sst_dsp_inbox_read(struct sst_dsp *sst, void *message, size_t bytes) } EXPORT_SYMBOL_GPL(sst_dsp_inbox_read); -#ifdef CONFIG_DW_DMAC_CORE -struct sst_dsp *sst_dsp_new(struct device *dev, - struct sst_dsp_device *sst_dev, struct sst_pdata *pdata) -{ - struct sst_dsp *sst; - int err; - - dev_dbg(dev, "initialising audio DSP id 0x%x\n", pdata->id); - - sst = devm_kzalloc(dev, sizeof(*sst), GFP_KERNEL); - if (sst == NULL) - return NULL; - - spin_lock_init(&sst->spinlock); - mutex_init(&sst->mutex); - sst->dev = dev; - sst->dma_dev = pdata->dma_dev; - sst->thread_context = sst_dev->thread_context; - sst->sst_dev = sst_dev; - sst->id = pdata->id; - sst->irq = pdata->irq; - sst->ops = sst_dev->ops; - sst->pdata = pdata; - INIT_LIST_HEAD(&sst->used_block_list); - INIT_LIST_HEAD(&sst->free_block_list); - INIT_LIST_HEAD(&sst->module_list); - INIT_LIST_HEAD(&sst->fw_list); - INIT_LIST_HEAD(&sst->scratch_block_list); - - /* Initialise SST Audio DSP */ - if (sst->ops->init) { - err = sst->ops->init(sst, pdata); - if (err < 0) - return NULL; - } - - /* Register the ISR */ - err = request_threaded_irq(sst->irq, sst->ops->irq_handler, - sst_dev->thread, IRQF_SHARED, "AudioDSP", sst); - if (err) - goto irq_err; - - err = sst_dma_new(sst); - if (err) - dev_warn(dev, "sst_dma_new failed %d\n", err); - - return sst; - -irq_err: - if (sst->ops->free) - sst->ops->free(sst); - - return NULL; -} -EXPORT_SYMBOL_GPL(sst_dsp_new); - -void sst_dsp_free(struct sst_dsp *sst) -{ - free_irq(sst->irq, sst); - if (sst->ops->free) - sst->ops->free(sst); - - sst_dma_free(sst->dma); -} -EXPORT_SYMBOL_GPL(sst_dsp_free); -#endif - /* Module information */ MODULE_AUTHOR("Liam Girdwood"); MODULE_DESCRIPTION("Intel SST Core"); diff --git a/sound/soc/intel/common/sst-dsp.h b/sound/soc/intel/common/sst-dsp.h index 0b84c719ec48..859f0de00339 100644 --- a/sound/soc/intel/common/sst-dsp.h +++ b/sound/soc/intel/common/sst-dsp.h @@ -216,7 +216,7 @@ struct sst_pdata { void *dsp; }; -#ifdef CONFIG_DW_DMAC_CORE +#if IS_ENABLED(CONFIG_DW_DMAC_CORE) /* Initialization */ struct sst_dsp *sst_dsp_new(struct device *dev, struct sst_dsp_device *sst_dev, struct sst_pdata *pdata); diff --git a/sound/soc/intel/common/sst-firmware.c b/sound/soc/intel/common/sst-firmware.c index 25993527370b..a086c35f91bb 100644 --- a/sound/soc/intel/common/sst-firmware.c +++ b/sound/soc/intel/common/sst-firmware.c @@ -1211,3 +1211,71 @@ u32 sst_dsp_get_offset(struct sst_dsp *dsp, u32 offset, } } EXPORT_SYMBOL_GPL(sst_dsp_get_offset); + +struct sst_dsp *sst_dsp_new(struct device *dev, + struct sst_dsp_device *sst_dev, struct sst_pdata *pdata) +{ + struct sst_dsp *sst; + int err; + + dev_dbg(dev, "initialising audio DSP id 0x%x\n", pdata->id); + + sst = devm_kzalloc(dev, sizeof(*sst), GFP_KERNEL); + if (sst == NULL) + return NULL; + + spin_lock_init(&sst->spinlock); + mutex_init(&sst->mutex); + sst->dev = dev; + sst->dma_dev = pdata->dma_dev; + sst->thread_context = sst_dev->thread_context; + sst->sst_dev = sst_dev; + sst->id = pdata->id; + sst->irq = pdata->irq; + sst->ops = sst_dev->ops; + sst->pdata = pdata; + INIT_LIST_HEAD(&sst->used_block_list); + INIT_LIST_HEAD(&sst->free_block_list); + INIT_LIST_HEAD(&sst->module_list); + INIT_LIST_HEAD(&sst->fw_list); + INIT_LIST_HEAD(&sst->scratch_block_list); + + /* Initialise SST Audio DSP */ + if (sst->ops->init) { + err = sst->ops->init(sst, pdata); + if (err < 0) + return NULL; + } + + /* Register the ISR */ + err = request_threaded_irq(sst->irq, sst->ops->irq_handler, + sst_dev->thread, IRQF_SHARED, "AudioDSP", sst); + if (err) + goto irq_err; + + err = sst_dma_new(sst); + if (err) + dev_warn(dev, "sst_dma_new failed %d\n", err); + + return sst; + +irq_err: + if (sst->ops->free) + sst->ops->free(sst); + + return NULL; +} +EXPORT_SYMBOL_GPL(sst_dsp_new); + +void sst_dsp_free(struct sst_dsp *sst) +{ + free_irq(sst->irq, sst); + if (sst->ops->free) + sst->ops->free(sst); + + sst_dma_free(sst->dma); +} +EXPORT_SYMBOL_GPL(sst_dsp_free); + +MODULE_DESCRIPTION("Intel SST Firmware Loader"); +MODULE_LICENSE("GPL v2"); diff --git a/sound/soc/intel/haswell/sst-haswell-pcm.c b/sound/soc/intel/haswell/sst-haswell-pcm.c index 994256b39b9c..3154525c2b83 100644 --- a/sound/soc/intel/haswell/sst-haswell-pcm.c +++ b/sound/soc/intel/haswell/sst-haswell-pcm.c @@ -819,7 +819,6 @@ static int hsw_pcm_open(struct snd_pcm_substream *substream) mutex_lock(&pcm_data->mutex); pm_runtime_get_sync(pdata->dev); - snd_soc_pcm_set_drvdata(rtd, pcm_data); pcm_data->substream = substream; snd_soc_set_runtime_hwparams(substream, &hsw_pcm_hardware); diff --git a/sound/soc/intel/skylake/Makefile b/sound/soc/intel/skylake/Makefile index c28f5d0e1d99..60fbc9bbe473 100644 --- a/sound/soc/intel/skylake/Makefile +++ b/sound/soc/intel/skylake/Makefile @@ -5,6 +5,6 @@ obj-$(CONFIG_SND_SOC_INTEL_SKYLAKE) += snd-soc-skl.o # Skylake IPC Support snd-soc-skl-ipc-objs := skl-sst-ipc.o skl-sst-dsp.o skl-sst-cldma.o \ - skl-sst.o bxt-sst.o + skl-sst.o bxt-sst.o skl-sst-utils.o obj-$(CONFIG_SND_SOC_INTEL_SKYLAKE) += snd-soc-skl-ipc.o diff --git a/sound/soc/intel/skylake/bxt-sst.c b/sound/soc/intel/skylake/bxt-sst.c index 8b95e09e23e8..2663781278aa 100644 --- a/sound/soc/intel/skylake/bxt-sst.c +++ b/sound/soc/intel/skylake/bxt-sst.c @@ -37,11 +37,19 @@ #define BXT_ADSP_SRAM1_BASE 0xA0000 +#define BXT_INSTANCE_ID 0 +#define BXT_BASE_FW_MODULE_ID 0 + static unsigned int bxt_get_errorcode(struct sst_dsp *ctx) { return sst_dsp_shim_read(ctx, BXT_ADSP_ERROR_CODE); } +/* + * First boot sequence has some extra steps. Core 0 waits for power + * status on core 1, so power up core 1 also momentarily, keep it in + * reset/stall and then turn it off + */ static int sst_bxt_prepare_fw(struct sst_dsp *ctx, const void *fwdata, u32 fwsize) { @@ -49,7 +57,7 @@ static int sst_bxt_prepare_fw(struct sst_dsp *ctx, u32 reg; stream_tag = ctx->dsp_ops.prepare(ctx->dev, 0x40, fwsize, &ctx->dmab); - if (stream_tag < 0) { + if (stream_tag <= 0) { dev_err(ctx->dev, "Failed to prepare DMA FW loading err: %x\n", stream_tag); return stream_tag; @@ -58,17 +66,27 @@ static int sst_bxt_prepare_fw(struct sst_dsp *ctx, ctx->dsp_ops.stream_tag = stream_tag; memcpy(ctx->dmab.area, fwdata, fwsize); - /* Purge FW request */ + /* Step 1: Power up core 0 and core1 */ + ret = skl_dsp_core_power_up(ctx, SKL_DSP_CORE0_MASK | + SKL_DSP_CORE_MASK(1)); + if (ret < 0) { + dev_err(ctx->dev, "dsp core0/1 power up failed\n"); + goto base_fw_load_failed; + } + + /* Step 2: Purge FW request */ sst_dsp_shim_write(ctx, SKL_ADSP_REG_HIPCI, SKL_ADSP_REG_HIPCI_BUSY | - BXT_IPC_PURGE_FW | (stream_tag - 1)); + (BXT_IPC_PURGE_FW | ((stream_tag - 1) << 9))); - ret = skl_dsp_enable_core(ctx); + /* Step 3: Unset core0 reset state & unstall/run core0 */ + ret = skl_dsp_start_core(ctx, SKL_DSP_CORE0_MASK); if (ret < 0) { - dev_err(ctx->dev, "Boot dsp core failed ret: %d\n", ret); + dev_err(ctx->dev, "Start dsp core failed ret: %d\n", ret); ret = -EIO; goto base_fw_load_failed; } + /* Step 4: Wait for DONE Bit */ for (i = BXT_INIT_TIMEOUT; i > 0; --i) { reg = sst_dsp_shim_read(ctx, SKL_ADSP_REG_HIPCIE); @@ -88,10 +106,18 @@ static int sst_bxt_prepare_fw(struct sst_dsp *ctx, SKL_ADSP_REG_HIPCIE_DONE); } - /* enable Interrupt */ + /* Step 5: power down core1 */ + ret = skl_dsp_core_power_down(ctx, SKL_DSP_CORE_MASK(1)); + if (ret < 0) { + dev_err(ctx->dev, "dsp core1 power down failed\n"); + goto base_fw_load_failed; + } + + /* Step 6: Enable Interrupt */ skl_ipc_int_enable(ctx); skl_ipc_op_int_enable(ctx); + /* Step 7: Wait for ROM init */ for (i = BXT_INIT_TIMEOUT; i > 0; --i) { if (SKL_FW_INIT == (sst_dsp_shim_read(ctx, BXT_ADSP_FW_STATUS) & @@ -112,7 +138,8 @@ static int sst_bxt_prepare_fw(struct sst_dsp *ctx, base_fw_load_failed: ctx->dsp_ops.cleanup(ctx->dev, &ctx->dmab, stream_tag); - skl_dsp_disable_core(ctx); + skl_dsp_core_power_down(ctx, SKL_DSP_CORE_MASK(1)); + skl_dsp_disable_core(ctx, SKL_DSP_CORE0_MASK); return ret; } @@ -130,23 +157,41 @@ static int sst_transfer_fw_host_dma(struct sst_dsp *ctx) return ret; } +#define BXT_ADSP_FW_BIN_HDR_OFFSET 0x2000 + static int bxt_load_base_firmware(struct sst_dsp *ctx) { - const struct firmware *fw = NULL; + struct firmware stripped_fw; struct skl_sst *skl = ctx->thread_context; int ret; - ret = request_firmware(&fw, ctx->fw_name, ctx->dev); + ret = request_firmware(&ctx->fw, ctx->fw_name, ctx->dev); if (ret < 0) { dev_err(ctx->dev, "Request firmware failed %d\n", ret); goto sst_load_base_firmware_failed; } - ret = sst_bxt_prepare_fw(ctx, fw->data, fw->size); + /* check for extended manifest */ + if (ctx->fw == NULL) + goto sst_load_base_firmware_failed; + + ret = snd_skl_parse_uuids(ctx, BXT_ADSP_FW_BIN_HDR_OFFSET); + if (ret < 0) + goto sst_load_base_firmware_failed; + + stripped_fw.data = ctx->fw->data; + stripped_fw.size = ctx->fw->size; + skl_dsp_strip_extended_manifest(&stripped_fw); + + ret = sst_bxt_prepare_fw(ctx, stripped_fw.data, stripped_fw.size); /* Retry Enabling core and ROM load. Retry seemed to help */ if (ret < 0) { - ret = sst_bxt_prepare_fw(ctx, fw->data, fw->size); + ret = sst_bxt_prepare_fw(ctx, stripped_fw.data, stripped_fw.size); if (ret < 0) { + dev_err(ctx->dev, "Error code=0x%x: FW status=0x%x\n", + sst_dsp_shim_read(ctx, BXT_ADSP_ERROR_CODE), + sst_dsp_shim_read(ctx, BXT_ADSP_FW_STATUS)); + dev_err(ctx->dev, "Core En/ROM load fail:%d\n", ret); goto sst_load_base_firmware_failed; } @@ -159,83 +204,135 @@ static int bxt_load_base_firmware(struct sst_dsp *ctx) sst_dsp_shim_read(ctx, BXT_ADSP_ERROR_CODE), sst_dsp_shim_read(ctx, BXT_ADSP_FW_STATUS)); - skl_dsp_disable_core(ctx); + skl_dsp_disable_core(ctx, SKL_DSP_CORE0_MASK); } else { dev_dbg(ctx->dev, "Firmware download successful\n"); ret = wait_event_timeout(skl->boot_wait, skl->boot_complete, msecs_to_jiffies(SKL_IPC_BOOT_MSECS)); if (ret == 0) { dev_err(ctx->dev, "DSP boot fail, FW Ready timeout\n"); - skl_dsp_disable_core(ctx); + skl_dsp_disable_core(ctx, SKL_DSP_CORE0_MASK); ret = -EIO; } else { - skl_dsp_set_state_locked(ctx, SKL_DSP_RUNNING); ret = 0; + skl->fw_loaded = true; } } sst_load_base_firmware_failed: - release_firmware(fw); + release_firmware(ctx->fw); return ret; } -static int bxt_set_dsp_D0(struct sst_dsp *ctx) +static int bxt_set_dsp_D0(struct sst_dsp *ctx, unsigned int core_id) { struct skl_sst *skl = ctx->thread_context; int ret; + struct skl_ipc_dxstate_info dx; + unsigned int core_mask = SKL_DSP_CORE_MASK(core_id); - skl->boot_complete = false; - - ret = skl_dsp_enable_core(ctx); - if (ret < 0) { - dev_err(ctx->dev, "enable dsp core failed ret: %d\n", ret); + if (skl->fw_loaded == false) { + skl->boot_complete = false; + ret = bxt_load_base_firmware(ctx); + if (ret < 0) + dev_err(ctx->dev, "reload fw failed: %d\n", ret); return ret; } - /* enable interrupt */ - skl_ipc_int_enable(ctx); - skl_ipc_op_int_enable(ctx); + /* If core 0 is being turned on, turn on core 1 as well */ + if (core_id == SKL_DSP_CORE0_ID) + ret = skl_dsp_core_power_up(ctx, core_mask | + SKL_DSP_CORE_MASK(1)); + else + ret = skl_dsp_core_power_up(ctx, core_mask); + + if (ret < 0) + goto err; + + if (core_id == SKL_DSP_CORE0_ID) { + + /* + * Enable interrupt after SPA is set and before + * DSP is unstalled + */ + skl_ipc_int_enable(ctx); + skl_ipc_op_int_enable(ctx); + skl->boot_complete = false; + } - ret = wait_event_timeout(skl->boot_wait, skl->boot_complete, - msecs_to_jiffies(SKL_IPC_BOOT_MSECS)); - if (ret == 0) { - dev_err(ctx->dev, "ipc: error DSP boot timeout\n"); - dev_err(ctx->dev, "Error code=0x%x: FW status=0x%x\n", - sst_dsp_shim_read(ctx, BXT_ADSP_ERROR_CODE), - sst_dsp_shim_read(ctx, BXT_ADSP_FW_STATUS)); - return -EIO; + ret = skl_dsp_start_core(ctx, core_mask); + if (ret < 0) + goto err; + + if (core_id == SKL_DSP_CORE0_ID) { + ret = wait_event_timeout(skl->boot_wait, + skl->boot_complete, + msecs_to_jiffies(SKL_IPC_BOOT_MSECS)); + + /* If core 1 was turned on for booting core 0, turn it off */ + skl_dsp_core_power_down(ctx, SKL_DSP_CORE_MASK(1)); + if (ret == 0) { + dev_err(ctx->dev, "%s: DSP boot timeout\n", __func__); + dev_err(ctx->dev, "Error code=0x%x: FW status=0x%x\n", + sst_dsp_shim_read(ctx, BXT_ADSP_ERROR_CODE), + sst_dsp_shim_read(ctx, BXT_ADSP_FW_STATUS)); + dev_err(ctx->dev, "Failed to set core0 to D0 state\n"); + ret = -EIO; + goto err; + } + } + + /* Tell FW if additional core in now On */ + + if (core_id != SKL_DSP_CORE0_ID) { + dx.core_mask = core_mask; + dx.dx_mask = core_mask; + + ret = skl_ipc_set_dx(&skl->ipc, BXT_INSTANCE_ID, + BXT_BASE_FW_MODULE_ID, &dx); + if (ret < 0) { + dev_err(ctx->dev, "IPC set_dx for core %d fail: %d\n", + core_id, ret); + goto err; + } } - skl_dsp_set_state_locked(ctx, SKL_DSP_RUNNING); + skl->cores.state[core_id] = SKL_DSP_RUNNING; return 0; +err: + if (core_id == SKL_DSP_CORE0_ID) + core_mask |= SKL_DSP_CORE_MASK(1); + skl_dsp_disable_core(ctx, core_mask); + + return ret; } -static int bxt_set_dsp_D3(struct sst_dsp *ctx) +static int bxt_set_dsp_D3(struct sst_dsp *ctx, unsigned int core_id) { + int ret; struct skl_ipc_dxstate_info dx; struct skl_sst *skl = ctx->thread_context; - int ret = 0; + unsigned int core_mask = SKL_DSP_CORE_MASK(core_id); - if (!is_skl_dsp_running(ctx)) - return ret; - - dx.core_mask = SKL_DSP_CORE0_MASK; + dx.core_mask = core_mask; dx.dx_mask = SKL_IPC_D3_MASK; - ret = skl_ipc_set_dx(&skl->ipc, SKL_INSTANCE_ID, - SKL_BASE_FW_MODULE_ID, &dx); - if (ret < 0) { - dev_err(ctx->dev, "Failed to set DSP to D3 state: %d\n", ret); - return ret; - } + dev_dbg(ctx->dev, "core mask=%x dx_mask=%x\n", + dx.core_mask, dx.dx_mask); + + ret = skl_ipc_set_dx(&skl->ipc, BXT_INSTANCE_ID, + BXT_BASE_FW_MODULE_ID, &dx); + if (ret < 0) + dev_err(ctx->dev, + "Failed to set DSP to D3:core id = %d;Continue reset\n", + core_id); - ret = skl_dsp_disable_core(ctx); + ret = skl_dsp_disable_core(ctx, core_mask); if (ret < 0) { - dev_err(ctx->dev, "disbale dsp core failed: %d\n", ret); - ret = -EIO; + dev_err(ctx->dev, "Failed to disable core %d", ret); + return ret; } - - skl_dsp_set_state_locked(ctx, SKL_DSP_RESET); + skl->cores.state[core_id] = SKL_DSP_RESET; return 0; } @@ -274,6 +371,7 @@ int bxt_sst_dsp_init(struct device *dev, void __iomem *mmio_base, int irq, skl->dev = dev; skl_dev.thread_context = skl; + INIT_LIST_HEAD(&skl->uuid_list); skl->dsp = skl_dsp_ctx_init(dev, &skl_dev, irq); if (!skl->dsp) { @@ -296,6 +394,7 @@ int bxt_sst_dsp_init(struct device *dev, void __iomem *mmio_base, int irq, if (ret) return ret; + skl->cores.count = 2; skl->boot_complete = false; init_waitqueue_head(&skl->boot_wait); @@ -305,6 +404,8 @@ int bxt_sst_dsp_init(struct device *dev, void __iomem *mmio_base, int irq, return ret; } + skl_dsp_init_core_state(sst); + if (dsp) *dsp = skl; @@ -315,6 +416,7 @@ EXPORT_SYMBOL_GPL(bxt_sst_dsp_init); void bxt_sst_dsp_cleanup(struct device *dev, struct skl_sst *ctx) { + skl_freeup_uuid_list(ctx); skl_ipc_free(&ctx->ipc); ctx->dsp->cl_dev.ops.cl_cleanup_controller(ctx->dsp); diff --git a/sound/soc/intel/skylake/skl-messages.c b/sound/soc/intel/skylake/skl-messages.c index 226db84ba20f..44ab595ce21a 100644 --- a/sound/soc/intel/skylake/skl-messages.c +++ b/sound/soc/intel/skylake/skl-messages.c @@ -206,6 +206,12 @@ static const struct skl_dsp_ops dsp_ops[] = { .cleanup = skl_sst_dsp_cleanup }, { + .id = 0x9d71, + .loader_ops = skl_get_loader_ops, + .init = skl_sst_dsp_init, + .cleanup = skl_sst_dsp_cleanup + }, + { .id = 0x5a98, .loader_ops = bxt_get_loader_ops, .init = bxt_sst_dsp_init, @@ -730,7 +736,7 @@ static int skl_set_module_format(struct skl_sst *ctx, dev_dbg(ctx->dev, "Module type=%d config size: %d bytes\n", module_config->id.module_id, param_size); - print_hex_dump(KERN_DEBUG, "Module params:", DUMP_PREFIX_OFFSET, 8, 4, + print_hex_dump_debug("Module params:", DUMP_PREFIX_OFFSET, 8, 4, *param_data, param_size, false); return 0; } @@ -1046,7 +1052,7 @@ int skl_delete_pipe(struct skl_sst *ctx, struct skl_pipe *pipe) dev_dbg(ctx->dev, "%s: pipe = %d\n", __func__, pipe->ppl_id); - /* If pipe is not started, do not try to stop the pipe in FW. */ + /* If pipe is started, do stop the pipe in FW. */ if (pipe->state > SKL_PIPE_STARTED) { ret = skl_set_pipe_state(ctx, pipe, PPL_PAUSED); if (ret < 0) { @@ -1055,18 +1061,20 @@ int skl_delete_pipe(struct skl_sst *ctx, struct skl_pipe *pipe) } pipe->state = SKL_PIPE_PAUSED; - } else { - /* If pipe was not created in FW, do not try to delete it */ - if (pipe->state < SKL_PIPE_CREATED) - return 0; + } - ret = skl_ipc_delete_pipeline(&ctx->ipc, pipe->ppl_id); - if (ret < 0) - dev_err(ctx->dev, "Failed to delete pipeline\n"); + /* If pipe was not created in FW, do not try to delete it */ + if (pipe->state < SKL_PIPE_CREATED) + return 0; - pipe->state = SKL_PIPE_INVALID; + ret = skl_ipc_delete_pipeline(&ctx->ipc, pipe->ppl_id); + if (ret < 0) { + dev_err(ctx->dev, "Failed to delete pipeline\n"); + return ret; } + pipe->state = SKL_PIPE_INVALID; + return ret; } @@ -1125,7 +1133,30 @@ int skl_stop_pipe(struct skl_sst *ctx, struct skl_pipe *pipe) return ret; } - pipe->state = SKL_PIPE_CREATED; + pipe->state = SKL_PIPE_PAUSED; + + return 0; +} + +/* + * Reset the pipeline by sending set pipe state IPC this will reset the DMA + * from the DSP side + */ +int skl_reset_pipe(struct skl_sst *ctx, struct skl_pipe *pipe) +{ + int ret; + + /* If pipe was not created in FW, do not try to pause or delete */ + if (pipe->state < SKL_PIPE_PAUSED) + return 0; + + ret = skl_set_pipe_state(ctx, pipe, PPL_RESET); + if (ret < 0) { + dev_dbg(ctx->dev, "Failed to reset pipe ret=%d\n", ret); + return ret; + } + + pipe->state = SKL_PIPE_RESET; return 0; } diff --git a/sound/soc/intel/skylake/skl-nhlt.c b/sound/soc/intel/skylake/skl-nhlt.c index 7d73648e5f9a..3f8e6f0b7eb5 100644 --- a/sound/soc/intel/skylake/skl-nhlt.c +++ b/sound/soc/intel/skylake/skl-nhlt.c @@ -17,6 +17,7 @@ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ * */ +#include <linux/pci.h> #include "skl.h" /* Unique identification for getting NHLT blobs */ @@ -149,6 +150,45 @@ struct nhlt_specific_cfg return NULL; } +int skl_get_dmic_geo(struct skl *skl) +{ + struct nhlt_acpi_table *nhlt = (struct nhlt_acpi_table *)skl->nhlt; + struct nhlt_endpoint *epnt; + struct nhlt_dmic_array_config *cfg; + struct device *dev = &skl->pci->dev; + unsigned int dmic_geo = 0; + u8 j; + + epnt = (struct nhlt_endpoint *)nhlt->desc; + + for (j = 0; j < nhlt->endpoint_count; j++) { + if (epnt->linktype == NHLT_LINK_DMIC) { + cfg = (struct nhlt_dmic_array_config *) + (epnt->config.caps); + switch (cfg->array_type) { + case NHLT_MIC_ARRAY_2CH_SMALL: + case NHLT_MIC_ARRAY_2CH_BIG: + dmic_geo |= MIC_ARRAY_2CH; + break; + + case NHLT_MIC_ARRAY_4CH_1ST_GEOM: + case NHLT_MIC_ARRAY_4CH_L_SHAPED: + case NHLT_MIC_ARRAY_4CH_2ND_GEOM: + dmic_geo |= MIC_ARRAY_4CH; + break; + + default: + dev_warn(dev, "undefined DMIC array_type 0x%0x\n", + cfg->array_type); + + } + } + epnt = (struct nhlt_endpoint *)((u8 *)epnt + epnt->length); + } + + return dmic_geo; +} + static void skl_nhlt_trim_space(struct skl *skl) { char *s = skl->tplg_name; diff --git a/sound/soc/intel/skylake/skl-nhlt.h b/sound/soc/intel/skylake/skl-nhlt.h index 3769f9fefe2b..116534e7b3c5 100644 --- a/sound/soc/intel/skylake/skl-nhlt.h +++ b/sound/soc/intel/skylake/skl-nhlt.h @@ -103,4 +103,26 @@ struct nhlt_resource_desc { u64 length; } __packed; +#define MIC_ARRAY_2CH 2 +#define MIC_ARRAY_4CH 4 + +struct nhlt_tdm_config { + u8 virtual_slot; + u8 config_type; +} __packed; + +struct nhlt_dmic_array_config { + struct nhlt_tdm_config tdm_config; + u8 array_type; +} __packed; + +enum { + NHLT_MIC_ARRAY_2CH_SMALL = 0xa, + NHLT_MIC_ARRAY_2CH_BIG = 0xb, + NHLT_MIC_ARRAY_4CH_1ST_GEOM = 0xc, + NHLT_MIC_ARRAY_4CH_L_SHAPED = 0xd, + NHLT_MIC_ARRAY_4CH_2ND_GEOM = 0xe, + NHLT_MIC_ARRAY_VENDOR_DEFINED = 0xf, +}; + #endif diff --git a/sound/soc/intel/skylake/skl-pcm.c b/sound/soc/intel/skylake/skl-pcm.c index 7c81b31748ff..6e05bf8622f7 100644 --- a/sound/soc/intel/skylake/skl-pcm.c +++ b/sound/soc/intel/skylake/skl-pcm.c @@ -227,16 +227,25 @@ static int skl_pcm_prepare(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) { struct hdac_ext_stream *stream = get_hdac_ext_stream(substream); + struct skl *skl = get_skl_ctx(dai->dev); unsigned int format_val; int err; + struct skl_module_cfg *mconfig; dev_dbg(dai->dev, "%s: %s\n", __func__, dai->name); + mconfig = skl_tplg_fe_get_cpr_module(dai, substream->stream); + format_val = skl_get_format(substream, dai); dev_dbg(dai->dev, "stream_tag=%d formatvalue=%d\n", hdac_stream(stream)->stream_tag, format_val); snd_hdac_stream_reset(hdac_stream(stream)); + /* In case of XRUN recovery, reset the FW pipe to clean state */ + if (mconfig && (substream->runtime->status->state == + SNDRV_PCM_STATE_XRUN)) + skl_reset_pipe(skl->skl_sst, mconfig->pipe); + err = snd_hdac_stream_set_params(hdac_stream(stream), format_val); if (err < 0) return err; @@ -521,6 +530,8 @@ static int skl_link_pcm_prepare(struct snd_pcm_substream *substream, struct skl_dma_params *dma_params; struct snd_soc_dai *codec_dai = rtd->codec_dai; struct hdac_ext_link *link; + struct skl *skl = get_skl_ctx(dai->dev); + struct skl_module_cfg *mconfig = NULL; dma_params = (struct skl_dma_params *) snd_soc_dai_get_dma_data(codec_dai, substream); @@ -535,6 +546,12 @@ static int skl_link_pcm_prepare(struct snd_pcm_substream *substream, snd_hdac_ext_link_stream_reset(link_dev); + /* In case of XRUN recovery, reset the FW pipe to clean state */ + mconfig = skl_tplg_be_get_cpr_module(dai, substream->stream); + if (mconfig && (substream->runtime->status->state == + SNDRV_PCM_STATE_XRUN)) + skl_reset_pipe(skl->skl_sst, mconfig->pipe); + snd_hdac_ext_link_stream_setup(link_dev, format_val); snd_hdac_ext_link_set_stream_id(link, hdac_stream(link_dev)->stream_tag); @@ -1009,51 +1026,11 @@ static int skl_platform_pcm_trigger(struct snd_pcm_substream *substream, return 0; } -/* calculate runtime delay from LPIB */ -static int skl_get_delay_from_lpib(struct hdac_ext_bus *ebus, - struct hdac_ext_stream *sstream, - unsigned int pos) -{ - struct hdac_bus *bus = ebus_to_hbus(ebus); - struct hdac_stream *hstream = hdac_stream(sstream); - struct snd_pcm_substream *substream = hstream->substream; - int stream = substream->stream; - unsigned int lpib_pos = snd_hdac_stream_get_pos_lpib(hstream); - int delay; - - if (stream == SNDRV_PCM_STREAM_PLAYBACK) - delay = pos - lpib_pos; - else - delay = lpib_pos - pos; - - if (delay < 0) { - if (delay >= hstream->delay_negative_threshold) - delay = 0; - else - delay += hstream->bufsize; - } - - if (hstream->bufsize == delay) - delay = 0; - - if (delay >= hstream->period_bytes) { - dev_info(bus->dev, - "Unstable LPIB (%d >= %d); disabling LPIB delay counting\n", - delay, hstream->period_bytes); - delay = 0; - } - - return bytes_to_frames(substream->runtime, delay); -} - -static unsigned int skl_get_position(struct hdac_ext_stream *hstream, - int codec_delay) +static snd_pcm_uframes_t skl_platform_pcm_pointer + (struct snd_pcm_substream *substream) { - struct hdac_stream *hstr = hdac_stream(hstream); - struct snd_pcm_substream *substream = hstr->substream; - struct hdac_ext_bus *ebus; + struct hdac_ext_stream *hstream = get_hdac_ext_stream(substream); unsigned int pos; - int delay; /* use the position buffer as default */ pos = snd_hdac_stream_get_pos_posbuf(hdac_stream(hstream)); @@ -1061,23 +1038,7 @@ static unsigned int skl_get_position(struct hdac_ext_stream *hstream, if (pos >= hdac_stream(hstream)->bufsize) pos = 0; - if (substream->runtime) { - ebus = get_bus_ctx(substream); - delay = skl_get_delay_from_lpib(ebus, hstream, pos) - + codec_delay; - substream->runtime->delay += delay; - } - - return pos; -} - -static snd_pcm_uframes_t skl_platform_pcm_pointer - (struct snd_pcm_substream *substream) -{ - struct hdac_ext_stream *hstream = get_hdac_ext_stream(substream); - - return bytes_to_frames(substream->runtime, - skl_get_position(hstream, 0)); + return bytes_to_frames(substream->runtime, pos); } static u64 skl_adjust_codec_delay(struct snd_pcm_substream *substream, @@ -1180,9 +1141,17 @@ static int skl_pcm_new(struct snd_soc_pcm_runtime *rtd) static int skl_platform_soc_probe(struct snd_soc_platform *platform) { struct hdac_ext_bus *ebus = dev_get_drvdata(platform->dev); + struct skl *skl = ebus_to_skl(ebus); + int ret; - if (ebus->ppcap) - return skl_tplg_init(platform, ebus); + if (ebus->ppcap) { + ret = skl_tplg_init(platform, ebus); + if (ret < 0) { + dev_err(platform->dev, "Failed to init topology!\n"); + return ret; + } + skl->platform = platform; + } return 0; } diff --git a/sound/soc/intel/skylake/skl-sst-dsp.c b/sound/soc/intel/skylake/skl-sst-dsp.c index 13c19855ee1a..c3deefab65d6 100644 --- a/sound/soc/intel/skylake/skl-sst-dsp.c +++ b/sound/soc/intel/skylake/skl-sst-dsp.c @@ -34,33 +34,84 @@ void skl_dsp_set_state_locked(struct sst_dsp *ctx, int state) mutex_unlock(&ctx->mutex); } -static int skl_dsp_core_set_reset_state(struct sst_dsp *ctx) +/* + * Initialize core power state and usage count. To be called after + * successful first boot. Hence core 0 will be running and other cores + * will be reset + */ +void skl_dsp_init_core_state(struct sst_dsp *ctx) +{ + struct skl_sst *skl = ctx->thread_context; + int i; + + skl->cores.state[SKL_DSP_CORE0_ID] = SKL_DSP_RUNNING; + skl->cores.usage_count[SKL_DSP_CORE0_ID] = 1; + + for (i = SKL_DSP_CORE0_ID + 1; i < SKL_DSP_CORES_MAX; i++) { + skl->cores.state[i] = SKL_DSP_RESET; + skl->cores.usage_count[i] = 0; + } +} + +/* Get the mask for all enabled cores */ +unsigned int skl_dsp_get_enabled_cores(struct sst_dsp *ctx) +{ + struct skl_sst *skl = ctx->thread_context; + unsigned int core_mask, en_cores_mask; + u32 val; + + core_mask = SKL_DSP_CORES_MASK(skl->cores.count); + + val = sst_dsp_shim_read_unlocked(ctx, SKL_ADSP_REG_ADSPCS); + + /* Cores having CPA bit set */ + en_cores_mask = (val & SKL_ADSPCS_CPA_MASK(core_mask)) >> + SKL_ADSPCS_CPA_SHIFT; + + /* And cores having CRST bit cleared */ + en_cores_mask &= (~val & SKL_ADSPCS_CRST_MASK(core_mask)) >> + SKL_ADSPCS_CRST_SHIFT; + + /* And cores having CSTALL bit cleared */ + en_cores_mask &= (~val & SKL_ADSPCS_CSTALL_MASK(core_mask)) >> + SKL_ADSPCS_CSTALL_SHIFT; + en_cores_mask &= core_mask; + + dev_dbg(ctx->dev, "DSP enabled cores mask = %x\n", en_cores_mask); + + return en_cores_mask; +} + +static int +skl_dsp_core_set_reset_state(struct sst_dsp *ctx, unsigned int core_mask) { int ret; /* update bits */ sst_dsp_shim_update_bits_unlocked(ctx, - SKL_ADSP_REG_ADSPCS, SKL_ADSPCS_CRST_MASK, - SKL_ADSPCS_CRST(SKL_DSP_CORES_MASK)); + SKL_ADSP_REG_ADSPCS, SKL_ADSPCS_CRST_MASK(core_mask), + SKL_ADSPCS_CRST_MASK(core_mask)); /* poll with timeout to check if operation successful */ ret = sst_dsp_register_poll(ctx, SKL_ADSP_REG_ADSPCS, - SKL_ADSPCS_CRST_MASK, - SKL_ADSPCS_CRST(SKL_DSP_CORES_MASK), + SKL_ADSPCS_CRST_MASK(core_mask), + SKL_ADSPCS_CRST_MASK(core_mask), SKL_DSP_RESET_TO, "Set reset"); if ((sst_dsp_shim_read_unlocked(ctx, SKL_ADSP_REG_ADSPCS) & - SKL_ADSPCS_CRST(SKL_DSP_CORES_MASK)) != - SKL_ADSPCS_CRST(SKL_DSP_CORES_MASK)) { - dev_err(ctx->dev, "Set reset state failed\n"); + SKL_ADSPCS_CRST_MASK(core_mask)) != + SKL_ADSPCS_CRST_MASK(core_mask)) { + dev_err(ctx->dev, "Set reset state failed: core_mask %x\n", + core_mask); ret = -EIO; } return ret; } -static int skl_dsp_core_unset_reset_state(struct sst_dsp *ctx) +int skl_dsp_core_unset_reset_state( + struct sst_dsp *ctx, unsigned int core_mask) { int ret; @@ -68,152 +119,160 @@ static int skl_dsp_core_unset_reset_state(struct sst_dsp *ctx) /* update bits */ sst_dsp_shim_update_bits_unlocked(ctx, SKL_ADSP_REG_ADSPCS, - SKL_ADSPCS_CRST_MASK, 0); + SKL_ADSPCS_CRST_MASK(core_mask), 0); /* poll with timeout to check if operation successful */ ret = sst_dsp_register_poll(ctx, SKL_ADSP_REG_ADSPCS, - SKL_ADSPCS_CRST_MASK, + SKL_ADSPCS_CRST_MASK(core_mask), 0, SKL_DSP_RESET_TO, "Unset reset"); if ((sst_dsp_shim_read_unlocked(ctx, SKL_ADSP_REG_ADSPCS) & - SKL_ADSPCS_CRST(SKL_DSP_CORES_MASK)) != 0) { - dev_err(ctx->dev, "Unset reset state failed\n"); + SKL_ADSPCS_CRST_MASK(core_mask)) != 0) { + dev_err(ctx->dev, "Unset reset state failed: core_mask %x\n", + core_mask); ret = -EIO; } return ret; } -static bool is_skl_dsp_core_enable(struct sst_dsp *ctx) +static bool +is_skl_dsp_core_enable(struct sst_dsp *ctx, unsigned int core_mask) { int val; bool is_enable; val = sst_dsp_shim_read_unlocked(ctx, SKL_ADSP_REG_ADSPCS); - is_enable = ((val & SKL_ADSPCS_CPA(SKL_DSP_CORES_MASK)) && - (val & SKL_ADSPCS_SPA(SKL_DSP_CORES_MASK)) && - !(val & SKL_ADSPCS_CRST(SKL_DSP_CORES_MASK)) && - !(val & SKL_ADSPCS_CSTALL(SKL_DSP_CORES_MASK))); + is_enable = ((val & SKL_ADSPCS_CPA_MASK(core_mask)) && + (val & SKL_ADSPCS_SPA_MASK(core_mask)) && + !(val & SKL_ADSPCS_CRST_MASK(core_mask)) && + !(val & SKL_ADSPCS_CSTALL_MASK(core_mask))); + + dev_dbg(ctx->dev, "DSP core(s) enabled? %d : core_mask %x\n", + is_enable, core_mask); - dev_dbg(ctx->dev, "DSP core is enabled=%d\n", is_enable); return is_enable; } -static int skl_dsp_reset_core(struct sst_dsp *ctx) +static int skl_dsp_reset_core(struct sst_dsp *ctx, unsigned int core_mask) { /* stall core */ - sst_dsp_shim_write_unlocked(ctx, SKL_ADSP_REG_ADSPCS, - sst_dsp_shim_read_unlocked(ctx, SKL_ADSP_REG_ADSPCS) & - SKL_ADSPCS_CSTALL(SKL_DSP_CORES_MASK)); + sst_dsp_shim_update_bits_unlocked(ctx, SKL_ADSP_REG_ADSPCS, + SKL_ADSPCS_CSTALL_MASK(core_mask), + SKL_ADSPCS_CSTALL_MASK(core_mask)); /* set reset state */ - return skl_dsp_core_set_reset_state(ctx); + return skl_dsp_core_set_reset_state(ctx, core_mask); } -static int skl_dsp_start_core(struct sst_dsp *ctx) +int skl_dsp_start_core(struct sst_dsp *ctx, unsigned int core_mask) { int ret; /* unset reset state */ - ret = skl_dsp_core_unset_reset_state(ctx); - if (ret < 0) { - dev_dbg(ctx->dev, "dsp unset reset fails\n"); + ret = skl_dsp_core_unset_reset_state(ctx, core_mask); + if (ret < 0) return ret; - } /* run core */ - dev_dbg(ctx->dev, "run core...\n"); - sst_dsp_shim_write_unlocked(ctx, SKL_ADSP_REG_ADSPCS, - sst_dsp_shim_read_unlocked(ctx, SKL_ADSP_REG_ADSPCS) & - ~SKL_ADSPCS_CSTALL(SKL_DSP_CORES_MASK)); - - if (!is_skl_dsp_core_enable(ctx)) { - skl_dsp_reset_core(ctx); - dev_err(ctx->dev, "DSP core enable failed\n"); + dev_dbg(ctx->dev, "unstall/run core: core_mask = %x\n", core_mask); + sst_dsp_shim_update_bits_unlocked(ctx, SKL_ADSP_REG_ADSPCS, + SKL_ADSPCS_CSTALL_MASK(core_mask), 0); + + if (!is_skl_dsp_core_enable(ctx, core_mask)) { + skl_dsp_reset_core(ctx, core_mask); + dev_err(ctx->dev, "DSP start core failed: core_mask %x\n", + core_mask); ret = -EIO; } return ret; } -static int skl_dsp_core_power_up(struct sst_dsp *ctx) +int skl_dsp_core_power_up(struct sst_dsp *ctx, unsigned int core_mask) { int ret; /* update bits */ sst_dsp_shim_update_bits_unlocked(ctx, SKL_ADSP_REG_ADSPCS, - SKL_ADSPCS_SPA_MASK, SKL_ADSPCS_SPA(SKL_DSP_CORES_MASK)); + SKL_ADSPCS_SPA_MASK(core_mask), + SKL_ADSPCS_SPA_MASK(core_mask)); /* poll with timeout to check if operation successful */ ret = sst_dsp_register_poll(ctx, SKL_ADSP_REG_ADSPCS, - SKL_ADSPCS_CPA_MASK, - SKL_ADSPCS_CPA(SKL_DSP_CORES_MASK), + SKL_ADSPCS_CPA_MASK(core_mask), + SKL_ADSPCS_CPA_MASK(core_mask), SKL_DSP_PU_TO, "Power up"); if ((sst_dsp_shim_read_unlocked(ctx, SKL_ADSP_REG_ADSPCS) & - SKL_ADSPCS_CPA(SKL_DSP_CORES_MASK)) != - SKL_ADSPCS_CPA(SKL_DSP_CORES_MASK)) { - dev_err(ctx->dev, "DSP core power up failed\n"); + SKL_ADSPCS_CPA_MASK(core_mask)) != + SKL_ADSPCS_CPA_MASK(core_mask)) { + dev_err(ctx->dev, "DSP core power up failed: core_mask %x\n", + core_mask); ret = -EIO; } return ret; } -static int skl_dsp_core_power_down(struct sst_dsp *ctx) +int skl_dsp_core_power_down(struct sst_dsp *ctx, unsigned int core_mask) { /* update bits */ sst_dsp_shim_update_bits_unlocked(ctx, SKL_ADSP_REG_ADSPCS, - SKL_ADSPCS_SPA_MASK, 0); + SKL_ADSPCS_SPA_MASK(core_mask), 0); /* poll with timeout to check if operation successful */ return sst_dsp_register_poll(ctx, SKL_ADSP_REG_ADSPCS, - SKL_ADSPCS_CPA_MASK, + SKL_ADSPCS_CPA_MASK(core_mask), 0, SKL_DSP_PD_TO, "Power down"); } -int skl_dsp_enable_core(struct sst_dsp *ctx) +int skl_dsp_enable_core(struct sst_dsp *ctx, unsigned int core_mask) { int ret; /* power up */ - ret = skl_dsp_core_power_up(ctx); + ret = skl_dsp_core_power_up(ctx, core_mask); if (ret < 0) { - dev_dbg(ctx->dev, "dsp core power up failed\n"); + dev_err(ctx->dev, "dsp core power up failed: core_mask %x\n", + core_mask); return ret; } - return skl_dsp_start_core(ctx); + return skl_dsp_start_core(ctx, core_mask); } -int skl_dsp_disable_core(struct sst_dsp *ctx) +int skl_dsp_disable_core(struct sst_dsp *ctx, unsigned int core_mask) { int ret; - ret = skl_dsp_reset_core(ctx); + ret = skl_dsp_reset_core(ctx, core_mask); if (ret < 0) { - dev_err(ctx->dev, "dsp core reset failed\n"); + dev_err(ctx->dev, "dsp core reset failed: core_mask %x\n", + core_mask); return ret; } /* power down core*/ - ret = skl_dsp_core_power_down(ctx); + ret = skl_dsp_core_power_down(ctx, core_mask); if (ret < 0) { - dev_err(ctx->dev, "dsp core power down failed\n"); + dev_err(ctx->dev, "dsp core power down fail mask %x: %d\n", + core_mask, ret); return ret; } - if (is_skl_dsp_core_enable(ctx)) { - dev_err(ctx->dev, "DSP core disable failed\n"); + if (is_skl_dsp_core_enable(ctx, core_mask)) { + dev_err(ctx->dev, "dsp core disable fail mask %x: %d\n", + core_mask, ret); ret = -EIO; } @@ -224,28 +283,25 @@ int skl_dsp_boot(struct sst_dsp *ctx) { int ret; - if (is_skl_dsp_core_enable(ctx)) { - dev_dbg(ctx->dev, "dsp core is already enabled, so reset the dap core\n"); - ret = skl_dsp_reset_core(ctx); + if (is_skl_dsp_core_enable(ctx, SKL_DSP_CORE0_MASK)) { + ret = skl_dsp_reset_core(ctx, SKL_DSP_CORE0_MASK); if (ret < 0) { - dev_err(ctx->dev, "dsp reset failed\n"); + dev_err(ctx->dev, "dsp core0 reset fail: %d\n", ret); return ret; } - ret = skl_dsp_start_core(ctx); + ret = skl_dsp_start_core(ctx, SKL_DSP_CORE0_MASK); if (ret < 0) { - dev_err(ctx->dev, "dsp start failed\n"); + dev_err(ctx->dev, "dsp core0 start fail: %d\n", ret); return ret; } } else { - dev_dbg(ctx->dev, "disable and enable to make sure DSP is invalid state\n"); - ret = skl_dsp_disable_core(ctx); - + ret = skl_dsp_disable_core(ctx, SKL_DSP_CORE0_MASK); if (ret < 0) { - dev_err(ctx->dev, "dsp disable core failes\n"); + dev_err(ctx->dev, "dsp core0 disable fail: %d\n", ret); return ret; } - ret = skl_dsp_enable_core(ctx); + ret = skl_dsp_enable_core(ctx, SKL_DSP_CORE0_MASK); } return ret; @@ -281,16 +337,74 @@ irqreturn_t skl_dsp_sst_interrupt(int irq, void *dev_id) return result; } +/* + * skl_dsp_get_core/skl_dsp_put_core will be called inside DAPM context + * within the dapm mutex. Hence no separate lock is used. + */ +int skl_dsp_get_core(struct sst_dsp *ctx, unsigned int core_id) +{ + struct skl_sst *skl = ctx->thread_context; + int ret = 0; + + if (core_id >= skl->cores.count) { + dev_err(ctx->dev, "invalid core id: %d\n", core_id); + return -EINVAL; + } + + if (skl->cores.state[core_id] == SKL_DSP_RESET) { + ret = ctx->fw_ops.set_state_D0(ctx, core_id); + if (ret < 0) { + dev_err(ctx->dev, "unable to get core%d\n", core_id); + return ret; + } + } + + skl->cores.usage_count[core_id]++; + + dev_dbg(ctx->dev, "core id %d state %d usage_count %d\n", + core_id, skl->cores.state[core_id], + skl->cores.usage_count[core_id]); + + return ret; +} +EXPORT_SYMBOL_GPL(skl_dsp_get_core); + +int skl_dsp_put_core(struct sst_dsp *ctx, unsigned int core_id) +{ + struct skl_sst *skl = ctx->thread_context; + int ret = 0; + + if (core_id >= skl->cores.count) { + dev_err(ctx->dev, "invalid core id: %d\n", core_id); + return -EINVAL; + } + + if (--skl->cores.usage_count[core_id] == 0) { + ret = ctx->fw_ops.set_state_D3(ctx, core_id); + if (ret < 0) { + dev_err(ctx->dev, "unable to put core %d: %d\n", + core_id, ret); + skl->cores.usage_count[core_id]++; + } + } + + dev_dbg(ctx->dev, "core id %d state %d usage_count %d\n", + core_id, skl->cores.state[core_id], + skl->cores.usage_count[core_id]); + + return ret; +} +EXPORT_SYMBOL_GPL(skl_dsp_put_core); int skl_dsp_wake(struct sst_dsp *ctx) { - return ctx->fw_ops.set_state_D0(ctx); + return skl_dsp_get_core(ctx, SKL_DSP_CORE0_ID); } EXPORT_SYMBOL_GPL(skl_dsp_wake); int skl_dsp_sleep(struct sst_dsp *ctx) { - return ctx->fw_ops.set_state_D3(ctx); + return skl_dsp_put_core(ctx, SKL_DSP_CORE0_ID); } EXPORT_SYMBOL_GPL(skl_dsp_sleep); @@ -337,9 +451,7 @@ void skl_dsp_free(struct sst_dsp *dsp) free_irq(dsp->irq, dsp); skl_ipc_op_int_disable(dsp); - skl_ipc_int_disable(dsp); - - skl_dsp_disable_core(dsp); + skl_dsp_disable_core(dsp, SKL_DSP_CORE0_MASK); } EXPORT_SYMBOL_GPL(skl_dsp_free); diff --git a/sound/soc/intel/skylake/skl-sst-dsp.h b/sound/soc/intel/skylake/skl-sst-dsp.h index deabe7308d3b..0f8629ef79ac 100644 --- a/sound/soc/intel/skylake/skl-sst-dsp.h +++ b/sound/soc/intel/skylake/skl-sst-dsp.h @@ -19,6 +19,7 @@ #include <linux/interrupt.h> #include <sound/memalloc.h> #include "skl-sst-cldma.h" +#include "skl-tplg-interface.h" struct sst_dsp; struct skl_sst; @@ -76,35 +77,53 @@ struct sst_dsp_device; #define SKL_ADSPIC_IPC 1 #define SKL_ADSPIS_IPC 1 +/* Core ID of core0 */ +#define SKL_DSP_CORE0_ID 0 + +/* Mask for a given core index, c = 0.. number of supported cores - 1 */ +#define SKL_DSP_CORE_MASK(c) BIT(c) + +/* + * Core 0 mask = SKL_DSP_CORE_MASK(0); Defined separately + * since Core0 is primary core and it is used often + */ +#define SKL_DSP_CORE0_MASK BIT(0) + +/* + * Mask for a given number of cores + * nc = number of supported cores + */ +#define SKL_DSP_CORES_MASK(nc) GENMASK((nc - 1), 0) + /* ADSPCS - Audio DSP Control & Status */ -#define SKL_DSP_CORES 1 -#define SKL_DSP_CORE0_MASK 1 -#define SKL_DSP_CORES_MASK ((1 << SKL_DSP_CORES) - 1) - -/* Core Reset - asserted high */ -#define SKL_ADSPCS_CRST_SHIFT 0 -#define SKL_ADSPCS_CRST_MASK (SKL_DSP_CORES_MASK << SKL_ADSPCS_CRST_SHIFT) -#define SKL_ADSPCS_CRST(x) ((x << SKL_ADSPCS_CRST_SHIFT) & SKL_ADSPCS_CRST_MASK) - -/* Core run/stall - when set to '1' core is stalled */ -#define SKL_ADSPCS_CSTALL_SHIFT 8 -#define SKL_ADSPCS_CSTALL_MASK (SKL_DSP_CORES_MASK << \ - SKL_ADSPCS_CSTALL_SHIFT) -#define SKL_ADSPCS_CSTALL(x) ((x << SKL_ADSPCS_CSTALL_SHIFT) & \ - SKL_ADSPCS_CSTALL_MASK) - -/* Set Power Active - when set to '1' turn cores on */ -#define SKL_ADSPCS_SPA_SHIFT 16 -#define SKL_ADSPCS_SPA_MASK (SKL_DSP_CORES_MASK << SKL_ADSPCS_SPA_SHIFT) -#define SKL_ADSPCS_SPA(x) ((x << SKL_ADSPCS_SPA_SHIFT) & SKL_ADSPCS_SPA_MASK) - -/* Current Power Active - power status of cores, set by hardware */ -#define SKL_ADSPCS_CPA_SHIFT 24 -#define SKL_ADSPCS_CPA_MASK (SKL_DSP_CORES_MASK << SKL_ADSPCS_CPA_SHIFT) -#define SKL_ADSPCS_CPA(x) ((x << SKL_ADSPCS_CPA_SHIFT) & SKL_ADSPCS_CPA_MASK) - -#define SST_DSP_POWER_D0 0x0 /* full On */ -#define SST_DSP_POWER_D3 0x3 /* Off */ + +/* + * Core Reset - asserted high + * CRST Mask for a given core mask pattern, cm + */ +#define SKL_ADSPCS_CRST_SHIFT 0 +#define SKL_ADSPCS_CRST_MASK(cm) ((cm) << SKL_ADSPCS_CRST_SHIFT) + +/* + * Core run/stall - when set to '1' core is stalled + * CSTALL Mask for a given core mask pattern, cm + */ +#define SKL_ADSPCS_CSTALL_SHIFT 8 +#define SKL_ADSPCS_CSTALL_MASK(cm) ((cm) << SKL_ADSPCS_CSTALL_SHIFT) + +/* + * Set Power Active - when set to '1' turn cores on + * SPA Mask for a given core mask pattern, cm + */ +#define SKL_ADSPCS_SPA_SHIFT 16 +#define SKL_ADSPCS_SPA_MASK(cm) ((cm) << SKL_ADSPCS_SPA_SHIFT) + +/* + * Current Power Active - power status of cores, set by hardware + * CPA Mask for a given core mask pattern, cm + */ +#define SKL_ADSPCS_CPA_SHIFT 24 +#define SKL_ADSPCS_CPA_MASK(cm) ((cm) << SKL_ADSPCS_CPA_SHIFT) enum skl_dsp_states { SKL_DSP_RUNNING = 1, @@ -115,8 +134,8 @@ struct skl_dsp_fw_ops { int (*load_fw)(struct sst_dsp *ctx); /* FW module parser/loader */ int (*parse_fw)(struct sst_dsp *ctx); - int (*set_state_D0)(struct sst_dsp *ctx); - int (*set_state_D3)(struct sst_dsp *ctx); + int (*set_state_D0)(struct sst_dsp *ctx, unsigned int core_id); + int (*set_state_D3)(struct sst_dsp *ctx, unsigned int core_id); unsigned int (*get_fw_errcode)(struct sst_dsp *ctx); int (*load_mod)(struct sst_dsp *ctx, u16 mod_id, u8 *mod_name); int (*unload_mod)(struct sst_dsp *ctx, u16 mod_id); @@ -157,14 +176,26 @@ int skl_cldma_prepare(struct sst_dsp *ctx); void skl_dsp_set_state_locked(struct sst_dsp *ctx, int state); struct sst_dsp *skl_dsp_ctx_init(struct device *dev, struct sst_dsp_device *sst_dev, int irq); -int skl_dsp_enable_core(struct sst_dsp *ctx); -int skl_dsp_disable_core(struct sst_dsp *ctx); bool is_skl_dsp_running(struct sst_dsp *ctx); + +unsigned int skl_dsp_get_enabled_cores(struct sst_dsp *ctx); +void skl_dsp_init_core_state(struct sst_dsp *ctx); +int skl_dsp_enable_core(struct sst_dsp *ctx, unsigned int core_mask); +int skl_dsp_disable_core(struct sst_dsp *ctx, unsigned int core_mask); +int skl_dsp_core_power_up(struct sst_dsp *ctx, unsigned int core_mask); +int skl_dsp_core_power_down(struct sst_dsp *ctx, unsigned int core_mask); +int skl_dsp_core_unset_reset_state(struct sst_dsp *ctx, + unsigned int core_mask); +int skl_dsp_start_core(struct sst_dsp *ctx, unsigned int core_mask); + irqreturn_t skl_dsp_sst_interrupt(int irq, void *dev_id); int skl_dsp_wake(struct sst_dsp *ctx); int skl_dsp_sleep(struct sst_dsp *ctx); void skl_dsp_free(struct sst_dsp *dsp); +int skl_dsp_get_core(struct sst_dsp *ctx, unsigned int core_id); +int skl_dsp_put_core(struct sst_dsp *ctx, unsigned int core_id); + int skl_dsp_boot(struct sst_dsp *ctx); int skl_sst_dsp_init(struct device *dev, void __iomem *mmio_base, int irq, const char *fw_name, struct skl_dsp_loader_ops dsp_ops, @@ -175,4 +206,11 @@ int bxt_sst_dsp_init(struct device *dev, void __iomem *mmio_base, int irq, void skl_sst_dsp_cleanup(struct device *dev, struct skl_sst *ctx); void bxt_sst_dsp_cleanup(struct device *dev, struct skl_sst *ctx); +int snd_skl_get_module_info(struct skl_sst *ctx, u8 *uuid, + struct skl_dfw_module *dfw_config); +int snd_skl_parse_uuids(struct sst_dsp *ctx, unsigned int offset); +void skl_freeup_uuid_list(struct skl_sst *ctx); + +int skl_dsp_strip_extended_manifest(struct firmware *fw); + #endif /*__SKL_SST_DSP_H__*/ diff --git a/sound/soc/intel/skylake/skl-sst-ipc.c b/sound/soc/intel/skylake/skl-sst-ipc.c index 543460293b00..96f2f6889b18 100644 --- a/sound/soc/intel/skylake/skl-sst-ipc.c +++ b/sound/soc/intel/skylake/skl-sst-ipc.c @@ -363,7 +363,7 @@ static void skl_ipc_process_reply(struct sst_generic_ipc *ipc, /* first process the header */ switch (reply) { case IPC_GLB_REPLY_SUCCESS: - dev_info(ipc->dev, "ipc FW reply %x: success\n", header.primary); + dev_dbg(ipc->dev, "ipc FW reply %x: success\n", header.primary); /* copy the rx data from the mailbox */ sst_dsp_inbox_read(ipc->dsp, msg->rx_data, msg->rx_size); break; @@ -692,7 +692,7 @@ int skl_ipc_init_instance(struct sst_generic_ipc *ipc, /* param_block_size must be in dwords */ u16 param_block_size = msg->param_data_size / sizeof(u32); - print_hex_dump(KERN_DEBUG, NULL, DUMP_PREFIX_NONE, + print_hex_dump_debug("Param data:", DUMP_PREFIX_NONE, 16, 4, buffer, param_block_size, false); header.primary = IPC_MSG_TARGET(IPC_MOD_MSG); diff --git a/sound/soc/intel/skylake/skl-sst-ipc.h b/sound/soc/intel/skylake/skl-sst-ipc.h index d59d1ba62a43..2e3d4e80ef97 100644 --- a/sound/soc/intel/skylake/skl-sst-ipc.h +++ b/sound/soc/intel/skylake/skl-sst-ipc.h @@ -45,6 +45,14 @@ struct skl_ipc_header { u32 extension; }; +#define SKL_DSP_CORES_MAX 2 + +struct skl_dsp_cores { + unsigned int count; + enum skl_dsp_states state[SKL_DSP_CORES_MAX]; + int usage_count[SKL_DSP_CORES_MAX]; +}; + struct skl_sst { struct device *dev; struct sst_dsp *dsp; @@ -60,6 +68,15 @@ struct skl_sst { void (*enable_miscbdcge)(struct device *dev, bool enable); /*Is CGCTL.MISCBDCGE disabled*/ bool miscbdcg_disabled; + + /* Populate module information */ + struct list_head uuid_list; + + /* Is firmware loaded */ + bool fw_loaded; + + /* multi-core */ + struct skl_dsp_cores cores; }; struct skl_ipc_init_instance_msg { @@ -136,5 +153,6 @@ void skl_ipc_int_disable(struct sst_dsp *dsp); bool skl_ipc_int_status(struct sst_dsp *dsp); void skl_ipc_free(struct sst_generic_ipc *ipc); int skl_ipc_init(struct device *dev, struct skl_sst *skl); +void skl_clear_module_cnt(struct sst_dsp *ctx); #endif /* __SKL_IPC_H */ diff --git a/sound/soc/intel/skylake/skl-sst-utils.c b/sound/soc/intel/skylake/skl-sst-utils.c new file mode 100644 index 000000000000..25fcb796bd86 --- /dev/null +++ b/sound/soc/intel/skylake/skl-sst-utils.c @@ -0,0 +1,256 @@ +/* + * skl-sst-utils.c - SKL sst utils functions + * + * Copyright (C) 2016 Intel Corp + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as version 2, as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + */ + +#include <linux/device.h> +#include <linux/slab.h> +#include <linux/uuid.h> +#include "skl-sst-dsp.h" +#include "../common/sst-dsp.h" +#include "../common/sst-dsp-priv.h" +#include "skl-sst-ipc.h" + + +#define UUID_STR_SIZE 37 +#define DEFAULT_HASH_SHA256_LEN 32 + +/* FW Extended Manifest Header id = $AE1 */ +#define SKL_EXT_MANIFEST_HEADER_MAGIC 0x31454124 + +struct skl_dfw_module_mod { + char name[100]; + struct skl_dfw_module skl_dfw_mod; +}; + +struct UUID { + u8 id[16]; +}; + +union seg_flags { + u32 ul; + struct { + u32 contents : 1; + u32 alloc : 1; + u32 load : 1; + u32 read_only : 1; + u32 code : 1; + u32 data : 1; + u32 _rsvd0 : 2; + u32 type : 4; + u32 _rsvd1 : 4; + u32 length : 16; + } r; +} __packed; + +struct segment_desc { + union seg_flags flags; + u32 v_base_addr; + u32 file_offset; +}; + +struct module_type { + u32 load_type : 4; + u32 auto_start : 1; + u32 domain_ll : 1; + u32 domain_dp : 1; + u32 rsvd : 25; +} __packed; + +struct adsp_module_entry { + u32 struct_id; + u8 name[8]; + struct UUID uuid; + struct module_type type; + u8 hash1[DEFAULT_HASH_SHA256_LEN]; + u32 entry_point; + u16 cfg_offset; + u16 cfg_count; + u32 affinity_mask; + u16 instance_max_count; + u16 instance_bss_size; + struct segment_desc segments[3]; +} __packed; + +struct adsp_fw_hdr { + u32 id; + u32 len; + u8 name[8]; + u32 preload_page_count; + u32 fw_image_flags; + u32 feature_mask; + u16 major; + u16 minor; + u16 hotfix; + u16 build; + u32 num_modules; + u32 hw_buf_base; + u32 hw_buf_length; + u32 load_offset; +} __packed; + +struct uuid_module { + uuid_le uuid; + int id; + int is_loadable; + + struct list_head list; +}; + +struct skl_ext_manifest_hdr { + u32 id; + u32 len; + u16 version_major; + u16 version_minor; + u32 entries; +}; + +int snd_skl_get_module_info(struct skl_sst *ctx, u8 *uuid, + struct skl_dfw_module *dfw_config) +{ + struct uuid_module *module; + uuid_le *uuid_mod; + + uuid_mod = (uuid_le *)uuid; + + list_for_each_entry(module, &ctx->uuid_list, list) { + if (uuid_le_cmp(*uuid_mod, module->uuid) == 0) { + dfw_config->module_id = module->id; + dfw_config->is_loadable = module->is_loadable; + + return 0; + } + } + + return -EINVAL; +} +EXPORT_SYMBOL_GPL(snd_skl_get_module_info); + +/* + * Parse the firmware binary to get the UUID, module id + * and loadable flags + */ +int snd_skl_parse_uuids(struct sst_dsp *ctx, unsigned int offset) +{ + struct adsp_fw_hdr *adsp_hdr; + struct adsp_module_entry *mod_entry; + int i, num_entry; + uuid_le *uuid_bin; + const char *buf; + struct skl_sst *skl = ctx->thread_context; + struct uuid_module *module; + struct firmware stripped_fw; + unsigned int safe_file; + + /* Get the FW pointer to derive ADSP header */ + stripped_fw.data = ctx->fw->data; + stripped_fw.size = ctx->fw->size; + + skl_dsp_strip_extended_manifest(&stripped_fw); + + buf = stripped_fw.data; + + /* check if we have enough space in file to move to header */ + safe_file = sizeof(*adsp_hdr) + offset; + if (stripped_fw.size <= safe_file) { + dev_err(ctx->dev, "Small fw file size, No space for hdr\n"); + return -EINVAL; + } + + adsp_hdr = (struct adsp_fw_hdr *)(buf + offset); + + /* check 1st module entry is in file */ + safe_file += adsp_hdr->len + sizeof(*mod_entry); + if (stripped_fw.size <= safe_file) { + dev_err(ctx->dev, "Small fw file size, No module entry\n"); + return -EINVAL; + } + + mod_entry = (struct adsp_module_entry *) + (buf + offset + adsp_hdr->len); + + num_entry = adsp_hdr->num_modules; + + /* check all entries are in file */ + safe_file += num_entry * sizeof(*mod_entry); + if (stripped_fw.size <= safe_file) { + dev_err(ctx->dev, "Small fw file size, No modules\n"); + return -EINVAL; + } + + + /* + * Read the UUID(GUID) from FW Manifest. + * + * The 16 byte UUID format is: XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXX + * Populate the UUID table to store module_id and loadable flags + * for the module. + */ + + for (i = 0; i < num_entry; i++, mod_entry++) { + module = kzalloc(sizeof(*module), GFP_KERNEL); + if (!module) + return -ENOMEM; + + uuid_bin = (uuid_le *)mod_entry->uuid.id; + memcpy(&module->uuid, uuid_bin, sizeof(module->uuid)); + + module->id = i; + module->is_loadable = mod_entry->type.load_type; + + list_add_tail(&module->list, &skl->uuid_list); + + dev_dbg(ctx->dev, + "Adding uuid :%pUL mod id: %d Loadable: %d\n", + &module->uuid, module->id, module->is_loadable); + } + + return 0; +} + +void skl_freeup_uuid_list(struct skl_sst *ctx) +{ + struct uuid_module *uuid, *_uuid; + + list_for_each_entry_safe(uuid, _uuid, &ctx->uuid_list, list) { + list_del(&uuid->list); + kfree(uuid); + } +} + +/* + * some firmware binary contains some extended manifest. This needs + * to be stripped in that case before we load and use that image. + * + * Get the module id for the module by checking + * the table for the UUID for the module + */ +int skl_dsp_strip_extended_manifest(struct firmware *fw) +{ + struct skl_ext_manifest_hdr *hdr; + + /* check if fw file is greater than header we are looking */ + if (fw->size < sizeof(hdr)) { + pr_err("%s: Firmware file small, no hdr\n", __func__); + return -EINVAL; + } + + hdr = (struct skl_ext_manifest_hdr *)fw->data; + + if (hdr->id == SKL_EXT_MANIFEST_HEADER_MAGIC) { + fw->size -= hdr->len; + fw->data += hdr->len; + } + + return 0; +} diff --git a/sound/soc/intel/skylake/skl-sst.c b/sound/soc/intel/skylake/skl-sst.c index 13ec8d53b526..588f899ceb65 100644 --- a/sound/soc/intel/skylake/skl-sst.c +++ b/sound/soc/intel/skylake/skl-sst.c @@ -68,10 +68,13 @@ static int skl_transfer_firmware(struct sst_dsp *ctx, return ret; } +#define SKL_ADSP_FW_BIN_HDR_OFFSET 0x284 + static int skl_load_base_firmware(struct sst_dsp *ctx) { int ret = 0, i; struct skl_sst *skl = ctx->thread_context; + struct firmware stripped_fw; u32 reg; skl->boot_complete = false; @@ -81,11 +84,25 @@ static int skl_load_base_firmware(struct sst_dsp *ctx) ret = request_firmware(&ctx->fw, ctx->fw_name, ctx->dev); if (ret < 0) { dev_err(ctx->dev, "Request firmware failed %d\n", ret); - skl_dsp_disable_core(ctx); return -EIO; } } + ret = snd_skl_parse_uuids(ctx, SKL_ADSP_FW_BIN_HDR_OFFSET); + if (ret < 0) { + dev_err(ctx->dev, + "UUID parsing err: %d\n", ret); + release_firmware(ctx->fw); + skl_dsp_disable_core(ctx, SKL_DSP_CORE0_MASK); + return ret; + } + + /* check for extended manifest */ + stripped_fw.data = ctx->fw->data; + stripped_fw.size = ctx->fw->size; + + skl_dsp_strip_extended_manifest(&stripped_fw); + ret = skl_dsp_boot(ctx); if (ret < 0) { dev_err(ctx->dev, "Boot dsp core failed ret: %d", ret); @@ -119,7 +136,7 @@ static int skl_load_base_firmware(struct sst_dsp *ctx) goto transfer_firmware_failed; } - ret = skl_transfer_firmware(ctx, ctx->fw->data, ctx->fw->size); + ret = skl_transfer_firmware(ctx, stripped_fw.data, stripped_fw.size); if (ret < 0) { dev_err(ctx->dev, "Transfer firmware failed%d\n", ret); goto transfer_firmware_failed; @@ -133,67 +150,87 @@ static int skl_load_base_firmware(struct sst_dsp *ctx) } dev_dbg(ctx->dev, "Download firmware successful%d\n", ret); - skl_dsp_set_state_locked(ctx, SKL_DSP_RUNNING); + skl->fw_loaded = true; } return 0; transfer_firmware_failed: ctx->cl_dev.ops.cl_cleanup_controller(ctx); skl_load_base_firmware_failed: - skl_dsp_disable_core(ctx); + skl_dsp_disable_core(ctx, SKL_DSP_CORE0_MASK); release_firmware(ctx->fw); ctx->fw = NULL; return ret; } -static int skl_set_dsp_D0(struct sst_dsp *ctx) +static int skl_set_dsp_D0(struct sst_dsp *ctx, unsigned int core_id) { int ret; + struct skl_ipc_dxstate_info dx; + struct skl_sst *skl = ctx->thread_context; + unsigned int core_mask = SKL_DSP_CORE_MASK(core_id); - ret = skl_load_base_firmware(ctx); - if (ret < 0) { - dev_err(ctx->dev, "unable to load firmware\n"); - return ret; + /* If core0 is being turned on, we need to load the FW */ + if (core_id == SKL_DSP_CORE0_ID) { + ret = skl_load_base_firmware(ctx); + if (ret < 0) { + dev_err(ctx->dev, "unable to load firmware\n"); + return ret; + } } - skl_dsp_set_state_locked(ctx, SKL_DSP_RUNNING); + /* + * If any core other than core 0 is being moved to D0, enable the + * core and send the set dx IPC for the core. + */ + if (core_id != SKL_DSP_CORE0_ID) { + ret = skl_dsp_enable_core(ctx, core_mask); + if (ret < 0) + return ret; + + dx.core_mask = core_mask; + dx.dx_mask = core_mask; + + ret = skl_ipc_set_dx(&skl->ipc, SKL_INSTANCE_ID, + SKL_BASE_FW_MODULE_ID, &dx); + if (ret < 0) { + dev_err(ctx->dev, "Failed to set dsp to D0:core id= %d\n", + core_id); + skl_dsp_disable_core(ctx, core_mask); + } + } + + skl->cores.state[core_id] = SKL_DSP_RUNNING; return ret; } -static int skl_set_dsp_D3(struct sst_dsp *ctx) +static int skl_set_dsp_D3(struct sst_dsp *ctx, unsigned int core_id) { int ret; struct skl_ipc_dxstate_info dx; struct skl_sst *skl = ctx->thread_context; + unsigned int core_mask = SKL_DSP_CORE_MASK(core_id); - dev_dbg(ctx->dev, "In %s:\n", __func__); - mutex_lock(&ctx->mutex); - if (!is_skl_dsp_running(ctx)) { - mutex_unlock(&ctx->mutex); - return 0; - } - mutex_unlock(&ctx->mutex); - - dx.core_mask = SKL_DSP_CORE0_MASK; + dx.core_mask = core_mask; dx.dx_mask = SKL_IPC_D3_MASK; + ret = skl_ipc_set_dx(&skl->ipc, SKL_INSTANCE_ID, SKL_BASE_FW_MODULE_ID, &dx); if (ret < 0) - dev_err(ctx->dev, - "D3 request to FW failed, continuing reset: %d", ret); - - /* disable Interrupt */ - ctx->cl_dev.ops.cl_cleanup_controller(ctx); - skl_cldma_int_disable(ctx); - skl_ipc_op_int_disable(ctx); - skl_ipc_int_disable(ctx); - - ret = skl_dsp_disable_core(ctx); - if (ret < 0) { - dev_err(ctx->dev, "disable dsp core failed ret: %d\n", ret); - ret = -EIO; + dev_err(ctx->dev, "set Dx core %d fail: %d\n", core_id, ret); + + if (core_id == SKL_DSP_CORE0_ID) { + /* disable Interrupt */ + ctx->cl_dev.ops.cl_cleanup_controller(ctx); + skl_cldma_int_disable(ctx); + skl_ipc_op_int_disable(ctx); + skl_ipc_int_disable(ctx); } - skl_dsp_set_state_locked(ctx, SKL_DSP_RESET); + ret = skl_dsp_disable_core(ctx, core_mask); + if (ret < 0) + return ret; + + skl->cores.state[core_id] = SKL_DSP_RESET; return ret; } @@ -360,6 +397,19 @@ static int skl_unload_module(struct sst_dsp *ctx, u16 mod_id) return ret; } +void skl_clear_module_cnt(struct sst_dsp *ctx) +{ + struct skl_module_table *module; + + if (list_empty(&ctx->module_list)) + return; + + list_for_each_entry(module, &ctx->module_list, list) { + module->usage_cnt = 0; + } +} +EXPORT_SYMBOL_GPL(skl_clear_module_cnt); + static void skl_clear_module_table(struct sst_dsp *ctx) { struct skl_module_table *module, *tmp; @@ -409,6 +459,7 @@ int skl_sst_dsp_init(struct device *dev, void __iomem *mmio_base, int irq, skl->dev = dev; skl_dev.thread_context = skl; + INIT_LIST_HEAD(&skl->uuid_list); skl->dsp = skl_dsp_ctx_init(dev, &skl_dev, irq); if (!skl->dsp) { @@ -432,12 +483,16 @@ int skl_sst_dsp_init(struct device *dev, void __iomem *mmio_base, int irq, if (ret) return ret; + skl->cores.count = 2; + ret = sst->fw_ops.load_fw(sst); if (ret < 0) { dev_err(dev, "Load base fw failed : %d", ret); goto cleanup; } + skl_dsp_init_core_state(sst); + if (dsp) *dsp = skl; @@ -452,6 +507,7 @@ EXPORT_SYMBOL_GPL(skl_sst_dsp_init); void skl_sst_dsp_cleanup(struct device *dev, struct skl_sst *ctx) { skl_clear_module_table(ctx->dsp); + skl_freeup_uuid_list(ctx); skl_ipc_free(&ctx->ipc); ctx->dsp->ops->free(ctx->dsp); if (ctx->boot_complete) { diff --git a/sound/soc/intel/skylake/skl-topology.c b/sound/soc/intel/skylake/skl-topology.c index c15b7f8962b3..cc0150fc2601 100644 --- a/sound/soc/intel/skylake/skl-topology.c +++ b/sound/soc/intel/skylake/skl-topology.c @@ -379,43 +379,6 @@ static void skl_tplg_update_module_params(struct snd_soc_dapm_widget *w, } /* - * A pipe can have multiple modules, each of them will be a DAPM widget as - * well. While managing a pipeline we need to get the list of all the - * widgets in a pipelines, so this helper - skl_tplg_get_pipe_widget() helps - * to get the SKL type widgets in that pipeline - */ -static int skl_tplg_alloc_pipe_widget(struct device *dev, - struct snd_soc_dapm_widget *w, struct skl_pipe *pipe) -{ - struct skl_module_cfg *src_module = NULL; - struct snd_soc_dapm_path *p = NULL; - struct skl_pipe_module *p_module = NULL; - - p_module = devm_kzalloc(dev, sizeof(*p_module), GFP_KERNEL); - if (!p_module) - return -ENOMEM; - - p_module->w = w; - list_add_tail(&p_module->node, &pipe->w_list); - - snd_soc_dapm_widget_for_each_sink_path(w, p) { - if ((p->sink->priv == NULL) - && (!is_skl_dsp_widget_type(w))) - continue; - - if ((p->sink->priv != NULL) && p->connect - && is_skl_dsp_widget_type(p->sink)) { - - src_module = p->sink->priv; - if (pipe->ppl_id == src_module->pipe->ppl_id) - skl_tplg_alloc_pipe_widget(dev, - p->sink, pipe); - } - } - return 0; -} - -/* * some modules can have multiple params set from user control and * need to be set after module is initialized. If set_param flag is * set module params will be done after module is initialised. @@ -514,8 +477,6 @@ skl_tplg_init_pipe_modules(struct skl *skl, struct skl_pipe *pipe) if (!skl_is_pipe_mcps_avail(skl, mconfig)) return -ENOMEM; - skl_tplg_alloc_pipe_mcps(skl, mconfig); - if (mconfig->is_loadable && ctx->dsp->fw_ops.load_mod) { ret = ctx->dsp->fw_ops.load_mod(ctx->dsp, mconfig->id.module_id, mconfig->guid); @@ -539,6 +500,7 @@ skl_tplg_init_pipe_modules(struct skl *skl, struct skl_pipe *pipe) if (ret < 0) return ret; + skl_tplg_alloc_pipe_mcps(skl, mconfig); ret = skl_tplg_set_module_params(w, ctx); if (ret < 0) return ret; @@ -591,9 +553,6 @@ static int skl_tplg_mixer_dapm_pre_pmu_event(struct snd_soc_dapm_widget *w, if (!skl_is_pipe_mem_avail(skl, mconfig)) return -ENOMEM; - skl_tplg_alloc_pipe_mem(skl, mconfig); - skl_tplg_alloc_pipe_mcps(skl, mconfig); - /* * Create a list of modules for pipe. * This list contains modules from source to sink @@ -602,19 +561,8 @@ static int skl_tplg_mixer_dapm_pre_pmu_event(struct snd_soc_dapm_widget *w, if (ret < 0) return ret; - /* - * we create a w_list of all widgets in that pipe. This list is not - * freed on PMD event as widgets within a pipe are static. This - * saves us cycles to get widgets in pipe every time. - * - * So if we have already initialized all the widgets of a pipeline - * we skip, so check for list_empty and create the list if empty - */ - if (list_empty(&s_pipe->w_list)) { - ret = skl_tplg_alloc_pipe_widget(ctx->dev, w, s_pipe); - if (ret < 0) - return ret; - } + skl_tplg_alloc_pipe_mem(skl, mconfig); + skl_tplg_alloc_pipe_mcps(skl, mconfig); /* Init all pipe modules from source to sink */ ret = skl_tplg_init_pipe_modules(skl, s_pipe); @@ -949,13 +897,17 @@ static int skl_tplg_mixer_dapm_post_pmd_event(struct snd_soc_dapm_widget *w, struct skl_pipe *s_pipe = mconfig->pipe; int ret = 0; + if (s_pipe->state == SKL_PIPE_INVALID) + return -EINVAL; + skl_tplg_free_pipe_mcps(skl, mconfig); skl_tplg_free_pipe_mem(skl, mconfig); list_for_each_entry(w_module, &s_pipe->w_list, node) { dst_module = w_module->w->priv; - skl_tplg_free_pipe_mcps(skl, dst_module); + if (mconfig->m_state >= SKL_MODULE_INIT_DONE) + skl_tplg_free_pipe_mcps(skl, dst_module); if (src_module == NULL) { src_module = dst_module; continue; @@ -1163,6 +1115,39 @@ static int skl_tplg_tlv_control_set(struct snd_kcontrol *kcontrol, } /* + * Fill the dma id for host and link. In case of passthrough + * pipeline, this will both host and link in the same + * pipeline, so need to copy the link and host based on dev_type + */ +static void skl_tplg_fill_dma_id(struct skl_module_cfg *mcfg, + struct skl_pipe_params *params) +{ + struct skl_pipe *pipe = mcfg->pipe; + + if (pipe->passthru) { + switch (mcfg->dev_type) { + case SKL_DEVICE_HDALINK: + pipe->p_params->link_dma_id = params->link_dma_id; + break; + + case SKL_DEVICE_HDAHOST: + pipe->p_params->host_dma_id = params->host_dma_id; + break; + + default: + break; + } + pipe->p_params->s_fmt = params->s_fmt; + pipe->p_params->ch = params->ch; + pipe->p_params->s_freq = params->s_freq; + pipe->p_params->stream = params->stream; + + } else { + memcpy(pipe->p_params, params, sizeof(*params)); + } +} + +/* * The FE params are passed by hw_params of the DAI. * On hw_params, the params are stored in Gateway module of the FE and we * need to calculate the format in DSP module configuration, that @@ -1172,10 +1157,9 @@ int skl_tplg_update_pipe_params(struct device *dev, struct skl_module_cfg *mconfig, struct skl_pipe_params *params) { - struct skl_pipe *pipe = mconfig->pipe; struct skl_module_fmt *format = NULL; - memcpy(pipe->p_params, params, sizeof(*params)); + skl_tplg_fill_dma_id(mconfig, params); if (params->stream == SNDRV_PCM_STREAM_PLAYBACK) format = &mconfig->in_fmt[0]; @@ -1362,12 +1346,11 @@ static int skl_tplg_be_fill_pipe_params(struct snd_soc_dai *dai, struct skl_module_cfg *mconfig, struct skl_pipe_params *params) { - struct skl_pipe *pipe = mconfig->pipe; struct nhlt_specific_cfg *cfg; struct skl *skl = get_skl_ctx(dai->dev); int link_type = skl_tplg_be_link_type(mconfig->dev_type); - memcpy(pipe->p_params, params, sizeof(*params)); + skl_tplg_fill_dma_id(mconfig, params); if (link_type == NHLT_LINK_HDA) return 0; @@ -1558,6 +1541,55 @@ static void skl_tplg_fill_fmt(struct skl_module_fmt *dst_fmt, } } +static void skl_clear_pin_config(struct snd_soc_platform *platform, + struct snd_soc_dapm_widget *w) +{ + int i; + struct skl_module_cfg *mconfig; + struct skl_pipe *pipe; + + if (!strncmp(w->dapm->component->name, platform->component.name, + strlen(platform->component.name))) { + mconfig = w->priv; + pipe = mconfig->pipe; + for (i = 0; i < mconfig->max_in_queue; i++) { + mconfig->m_in_pin[i].in_use = false; + mconfig->m_in_pin[i].pin_state = SKL_PIN_UNBIND; + } + for (i = 0; i < mconfig->max_out_queue; i++) { + mconfig->m_out_pin[i].in_use = false; + mconfig->m_out_pin[i].pin_state = SKL_PIN_UNBIND; + } + pipe->state = SKL_PIPE_INVALID; + mconfig->m_state = SKL_MODULE_UNINIT; + } +} + +void skl_cleanup_resources(struct skl *skl) +{ + struct skl_sst *ctx = skl->skl_sst; + struct snd_soc_platform *soc_platform = skl->platform; + struct snd_soc_dapm_widget *w; + struct snd_soc_card *card; + + if (soc_platform == NULL) + return; + + card = soc_platform->component.card; + if (!card || !card->instantiated) + return; + + skl->resource.mem = 0; + skl->resource.mcps = 0; + + list_for_each_entry(w, &card->widgets, list) { + if (is_skl_dsp_widget_type(w) && (w->priv != NULL)) + skl_clear_pin_config(soc_platform, w); + } + + skl_clear_module_cnt(ctx->dsp); +} + /* * Topology core widget load callback * @@ -1589,6 +1621,10 @@ static int skl_tplg_widget_load(struct snd_soc_component *cmpnt, w->priv = mconfig; memcpy(&mconfig->guid, &dfw_config->uuid, 16); + ret = snd_skl_get_module_info(skl->skl_sst, mconfig->guid, dfw_config); + if (ret < 0) + return ret; + mconfig->id.module_id = dfw_config->module_id; mconfig->id.instance_id = dfw_config->instance_id; mconfig->mcps = dfw_config->max_mcps; @@ -1738,6 +1774,60 @@ static struct snd_soc_tplg_ops skl_tplg_ops = { .bytes_ext_ops_count = ARRAY_SIZE(skl_tlv_ops), }; +/* + * A pipe can have multiple modules, each of them will be a DAPM widget as + * well. While managing a pipeline we need to get the list of all the + * widgets in a pipelines, so this helper - skl_tplg_create_pipe_widget_list() + * helps to get the SKL type widgets in that pipeline + */ +static int skl_tplg_create_pipe_widget_list(struct snd_soc_platform *platform) +{ + struct snd_soc_dapm_widget *w; + struct skl_module_cfg *mcfg = NULL; + struct skl_pipe_module *p_module = NULL; + struct skl_pipe *pipe; + + list_for_each_entry(w, &platform->component.card->widgets, list) { + if (is_skl_dsp_widget_type(w) && w->priv != NULL) { + mcfg = w->priv; + pipe = mcfg->pipe; + + p_module = devm_kzalloc(platform->dev, + sizeof(*p_module), GFP_KERNEL); + if (!p_module) + return -ENOMEM; + + p_module->w = w; + list_add_tail(&p_module->node, &pipe->w_list); + } + } + + return 0; +} + +static void skl_tplg_set_pipe_type(struct skl *skl, struct skl_pipe *pipe) +{ + struct skl_pipe_module *w_module; + struct snd_soc_dapm_widget *w; + struct skl_module_cfg *mconfig; + bool host_found = false, link_found = false; + + list_for_each_entry(w_module, &pipe->w_list, node) { + w = w_module->w; + mconfig = w->priv; + + if (mconfig->dev_type == SKL_DEVICE_HDAHOST) + host_found = true; + else if (mconfig->dev_type != SKL_DEVICE_NONE) + link_found = true; + } + + if (host_found && link_found) + pipe->passthru = true; + else + pipe->passthru = false; +} + /* This will be read from topology manifest, currently defined here */ #define SKL_MAX_MCPS 30000000 #define SKL_FW_MAX_MEM 1000000 @@ -1751,6 +1841,7 @@ int skl_tplg_init(struct snd_soc_platform *platform, struct hdac_ext_bus *ebus) const struct firmware *fw; struct hdac_bus *bus = ebus_to_hbus(ebus); struct skl *skl = ebus_to_skl(ebus); + struct skl_pipeline *ppl; ret = request_firmware(&fw, skl->tplg_name, bus->dev); if (ret < 0) { @@ -1780,6 +1871,12 @@ int skl_tplg_init(struct snd_soc_platform *platform, struct hdac_ext_bus *ebus) skl->resource.max_mem = SKL_FW_MAX_MEM; skl->tplg = fw; + ret = skl_tplg_create_pipe_widget_list(platform); + if (ret < 0) + return ret; + + list_for_each_entry(ppl, &skl->ppl_list, node) + skl_tplg_set_pipe_type(skl, ppl->pipe); return 0; } diff --git a/sound/soc/intel/skylake/skl-topology.h b/sound/soc/intel/skylake/skl-topology.h index 28d1d2c68528..22d3ef83817d 100644 --- a/sound/soc/intel/skylake/skl-topology.h +++ b/sound/soc/intel/skylake/skl-topology.h @@ -244,7 +244,8 @@ enum skl_pipe_state { SKL_PIPE_INVALID = 0, SKL_PIPE_CREATED = 1, SKL_PIPE_PAUSED = 2, - SKL_PIPE_STARTED = 3 + SKL_PIPE_STARTED = 3, + SKL_PIPE_RESET = 4 }; struct skl_pipe_module { @@ -270,6 +271,7 @@ struct skl_pipe { struct skl_pipe_params *p_params; enum skl_pipe_state state; struct list_head w_list; + bool passthru; }; enum skl_module_state { @@ -358,6 +360,8 @@ int skl_delete_pipe(struct skl_sst *ctx, struct skl_pipe *pipe); int skl_stop_pipe(struct skl_sst *ctx, struct skl_pipe *pipe); +int skl_reset_pipe(struct skl_sst *ctx, struct skl_pipe *pipe); + int skl_init_module(struct skl_sst *ctx, struct skl_module_cfg *module_config); int skl_bind_modules(struct skl_sst *ctx, struct skl_module_cfg diff --git a/sound/soc/intel/skylake/skl.c b/sound/soc/intel/skylake/skl.c index 06d8c263c68f..cd59536a761d 100644 --- a/sound/soc/intel/skylake/skl.c +++ b/sound/soc/intel/skylake/skl.c @@ -35,6 +35,8 @@ #include "skl-sst-dsp.h" #include "skl-sst-ipc.h" +static struct skl_machine_pdata skl_dmic_data; + /* * initialize the PCI registers */ @@ -184,6 +186,7 @@ static int _skl_suspend(struct hdac_ext_bus *ebus) { struct skl *skl = ebus_to_skl(ebus); struct hdac_bus *bus = ebus_to_hbus(ebus); + struct pci_dev *pci = to_pci_dev(bus->dev); int ret; snd_hdac_ext_bus_link_power_down_all(ebus); @@ -193,9 +196,12 @@ static int _skl_suspend(struct hdac_ext_bus *ebus) return ret; snd_hdac_bus_stop_chip(bus); + update_pci_dword(pci, AZX_PCIREG_PGCTL, + AZX_PGCTL_LSRMD_MASK, AZX_PGCTL_LSRMD_MASK); skl_enable_miscbdcge(bus->dev, false); snd_hdac_bus_enter_link_reset(bus); skl_enable_miscbdcge(bus->dev, true); + skl_cleanup_resources(skl); return 0; } @@ -242,6 +248,7 @@ static int skl_suspend(struct device *dev) ret = _skl_suspend(ebus); if (ret < 0) return ret; + skl->skl_sst->fw_loaded = false; } if (IS_ENABLED(CONFIG_SND_SOC_HDAC_HDMI)) { @@ -397,6 +404,10 @@ static int skl_machine_device_register(struct skl *skl, void *driver_data) platform_device_put(pdev); return -EIO; } + + if (mach->pdata) + dev_set_drvdata(&pdev->dev, mach->pdata); + skl->i2s_dev = pdev; return 0; @@ -657,6 +668,8 @@ static int skl_probe(struct pci_dev *pci, skl->pci_id = pci->device; + device_disable_async_suspend(bus->dev); + skl->nhlt = skl_nhlt_init(bus->dev); if (skl->nhlt == NULL) @@ -666,6 +679,8 @@ static int skl_probe(struct pci_dev *pci, pci_set_drvdata(skl->pci, ebus); + skl_dmic_data.dmic_num = skl_get_dmic_geo(skl); + /* check if dsp is there */ if (ebus->ppcap) { err = skl_machine_device_register(skl, @@ -713,7 +728,7 @@ static int skl_probe(struct pci_dev *pci, list_for_each_entry(hlink, &ebus->hlink_list, list) snd_hdac_ext_bus_link_put(ebus, hlink); - /*configure PM */ + /* configure PM */ pm_runtime_put_noidle(bus->dev); pm_runtime_allow(bus->dev); @@ -766,8 +781,7 @@ static void skl_remove(struct pci_dev *pci) struct hdac_ext_bus *ebus = pci_get_drvdata(pci); struct skl *skl = ebus_to_skl(ebus); - if (skl->tplg) - release_firmware(skl->tplg); + release_firmware(skl->tplg); if (pci_dev_run_wake(pci)) pm_runtime_get_noresume(&pci->dev); @@ -786,15 +800,23 @@ static void skl_remove(struct pci_dev *pci) static struct sst_acpi_mach sst_skl_devdata[] = { { "INT343A", "skl_alc286s_i2s", "intel/dsp_fw_release.bin", NULL, NULL, NULL }, - { "INT343B", "skl_nau88l25_ssm4567_i2s", "intel/dsp_fw_release.bin", - NULL, NULL, NULL }, - { "MX98357A", "skl_nau88l25_max98357a_i2s", "intel/dsp_fw_release.bin", - NULL, NULL, NULL }, + { "INT343B", "skl_n88l25_s4567", "intel/dsp_fw_release.bin", + NULL, NULL, &skl_dmic_data }, + { "MX98357A", "skl_n88l25_m98357a", "intel/dsp_fw_release.bin", + NULL, NULL, &skl_dmic_data }, {} }; static struct sst_acpi_mach sst_bxtp_devdata[] = { { "INT343A", "bxt_alc298s_i2s", "intel/dsp_fw_bxtn.bin", NULL, NULL, NULL }, + { "DLGS7219", "bxt_da7219_max98357a_i2s", "intel/dsp_fw_bxtn.bin", NULL, NULL, NULL }, +}; + +static struct sst_acpi_mach sst_kbl_devdata[] = { + { "INT343A", "kbl_alc286s_i2s", "intel/dsp_fw_kbl.bin", NULL, NULL, NULL }, + { "INT343B", "kbl_n88l25_s4567", "intel/dsp_fw_kbl.bin", NULL, NULL, &skl_dmic_data }, + { "MX98357A", "kbl_n88l25_m98357a", "intel/dsp_fw_kbl.bin", NULL, NULL, &skl_dmic_data }, + {} }; /* PCI IDs */ @@ -805,6 +827,9 @@ static const struct pci_device_id skl_ids[] = { /* BXT-P */ { PCI_DEVICE(0x8086, 0x5a98), .driver_data = (unsigned long)&sst_bxtp_devdata}, + /* KBL */ + { PCI_DEVICE(0x8086, 0x9D71), + .driver_data = (unsigned long)&sst_kbl_devdata}, { 0, } }; MODULE_DEVICE_TABLE(pci, skl_ids); diff --git a/sound/soc/intel/skylake/skl.h b/sound/soc/intel/skylake/skl.h index 4b4b3876aea9..9064e5b0d676 100644 --- a/sound/soc/intel/skylake/skl.h +++ b/sound/soc/intel/skylake/skl.h @@ -48,6 +48,8 @@ #define AZX_REG_VS_SDXEFIFOS_XBASE 0x1094 #define AZX_REG_VS_SDXEFIFOS_XINTERVAL 0x20 +#define AZX_PCIREG_PGCTL 0x44 +#define AZX_PGCTL_LSRMD_MASK (1 << 4) #define AZX_PCIREG_CGCTL 0x48 #define AZX_CGCTL_MISCBDCGE_MASK (1 << 6) @@ -65,6 +67,7 @@ struct skl { unsigned int init_failed:1; /* delayed init failed */ struct platform_device *dmic_dev; struct platform_device *i2s_dev; + struct snd_soc_platform *platform; struct nhlt_acpi_table *nhlt; /* nhlt ptr */ struct skl_sst *skl_sst; /* sst skl ctx */ @@ -90,6 +93,11 @@ struct skl_dma_params { u8 stream_tag; }; +/* to pass dmic data */ +struct skl_machine_pdata { + u32 dmic_num; +}; + struct skl_dsp_ops { int id; struct skl_dsp_loader_ops (*loader_ops)(void); @@ -108,9 +116,11 @@ void skl_nhlt_free(struct nhlt_acpi_table *addr); struct nhlt_specific_cfg *skl_get_ep_blob(struct skl *skl, u32 instance, u8 link_type, u8 s_fmt, u8 no_ch, u32 s_rate, u8 dirn); +int skl_get_dmic_geo(struct skl *skl); int skl_nhlt_update_topology_bin(struct skl *skl); int skl_init_dsp(struct skl *skl); int skl_free_dsp(struct skl *skl); int skl_suspend_dsp(struct skl *skl); int skl_resume_dsp(struct skl *skl); +void skl_cleanup_resources(struct skl *skl); #endif /* __SOUND_SOC_SKL_H */ |