From 02a513acbd1bbb6d7b519fc66f5523be70fc496a Mon Sep 17 00:00:00 2001 From: Kirill Marinushkin Date: Wed, 4 Apr 2018 06:19:37 +0200 Subject: [PATCH 001/107] ASoC: topology: Fix bclk and fsync inversion in set_link_hw_format() The values of bclk and fsync are inverted WRT the codec. But the existing solution already works for Broadwell, see the alsa-lib config: `alsa-lib/src/conf/topology/broadwell/broadwell.conf` This commit provides the backwards-compatible solution to fix this misuse. Signed-off-by: Kirill Marinushkin Reviewed-by: Pierre-Louis Bossart Tested-by: Pan Xiuli Tested-by: Pierre-Louis Bossart Cc: Jaroslav Kysela Cc: Takashi Iwai Cc: Mark Brown Cc: Liam Girdwood Cc: linux-kernel@vger.kernel.org Cc: alsa-devel@alsa-project.org Signed-off-by: Mark Brown (cherry picked from commit a941e2fab3207cb0d57dc4ec47b1b12c8ea78b84) --- include/uapi/sound/asoc.h | 16 ++++++++++++++-- sound/soc/soc-topology.c | 12 +++++++----- 2 files changed, 21 insertions(+), 7 deletions(-) diff --git a/include/uapi/sound/asoc.h b/include/uapi/sound/asoc.h index 69c37ecbff7ee3..f0e5e21efa540d 100644 --- a/include/uapi/sound/asoc.h +++ b/include/uapi/sound/asoc.h @@ -160,6 +160,18 @@ #define SND_SOC_TPLG_LNK_FLGBIT_SYMMETRIC_SAMPLEBITS (1 << 2) #define SND_SOC_TPLG_LNK_FLGBIT_VOICE_WAKEUP (1 << 3) +/* DAI topology BCLK parameter + * For the backwards capability, by default codec is bclk master + */ +#define SND_SOC_TPLG_BCLK_CM 0 /* codec is bclk master */ +#define SND_SOC_TPLG_BCLK_CS 1 /* codec is bclk slave */ + +/* DAI topology FSYNC parameter + * For the backwards capability, by default codec is fsync master + */ +#define SND_SOC_TPLG_FSYNC_CM 0 /* codec is fsync master */ +#define SND_SOC_TPLG_FSYNC_CS 1 /* codec is fsync slave */ + /* * Block Header. * This header precedes all object and object arrays below. @@ -315,8 +327,8 @@ struct snd_soc_tplg_hw_config { __u8 clock_gated; /* 1 if clock can be gated to save power */ __u8 invert_bclk; /* 1 for inverted BCLK, 0 for normal */ __u8 invert_fsync; /* 1 for inverted frame clock, 0 for normal */ - __u8 bclk_master; /* 1 for master of BCLK, 0 for slave */ - __u8 fsync_master; /* 1 for master of FSYNC, 0 for slave */ + __u8 bclk_master; /* SND_SOC_TPLG_BCLK_ value */ + __u8 fsync_master; /* SND_SOC_TPLG_FSYNC_ value */ __u8 mclk_direction; /* 0 for input, 1 for output */ __le16 reserved; /* for 32bit alignment */ __le32 mclk_rate; /* MCLK or SYSCLK freqency in Hz */ diff --git a/sound/soc/soc-topology.c b/sound/soc/soc-topology.c index 986b8b2f90fba5..82db57868d4fea 100644 --- a/sound/soc/soc-topology.c +++ b/sound/soc/soc-topology.c @@ -2019,13 +2019,15 @@ static void set_link_hw_format(struct snd_soc_dai_link *link, link->dai_fmt |= SND_SOC_DAIFMT_IB_IF; /* clock masters */ - bclk_master = hw_config->bclk_master; - fsync_master = hw_config->fsync_master; - if (!bclk_master && !fsync_master) + bclk_master = (hw_config->bclk_master == + SND_SOC_TPLG_BCLK_CM); + fsync_master = (hw_config->fsync_master == + SND_SOC_TPLG_FSYNC_CM); + if (bclk_master && fsync_master) link->dai_fmt |= SND_SOC_DAIFMT_CBM_CFM; - else if (bclk_master && !fsync_master) - link->dai_fmt |= SND_SOC_DAIFMT_CBS_CFM; else if (!bclk_master && fsync_master) + link->dai_fmt |= SND_SOC_DAIFMT_CBS_CFM; + else if (bclk_master && !fsync_master) link->dai_fmt |= SND_SOC_DAIFMT_CBM_CFS; else link->dai_fmt |= SND_SOC_DAIFMT_CBS_CFS; From cba633966cdba72289a56fb559cc0de1851ad3c6 Mon Sep 17 00:00:00 2001 From: Kirill Marinushkin Date: Wed, 4 Apr 2018 06:19:38 +0200 Subject: [PATCH 002/107] ASoC: topology: Add missing clock gating parameter when parsing hw_configs Clock gating parameter is a part of `dai_fmt`. It is supported by `alsa-lib` when creating a topology binary file, but ignored by kernel when loading this topology file. After applying this commit, the clock gating parameter is not ignored any more. This solution is backwards compatible. The existing behaviour is not broken, because by default the parameter value is 0 and is ignored. snd_soc_tplg_hw_config.clock_gated = 0 => no effect snd_soc_tplg_hw_config.clock_gated = 1 => SND_SOC_DAIFMT_GATED snd_soc_tplg_hw_config.clock_gated = 2 => SND_SOC_DAIFMT_CONT For example, the following config, based on alsa-lib/src/conf/topology/broadwell/broadwell.conf, is now supported: ~~~~ SectionHWConfig."CodecHWConfig" { id "1" format "I2S" # physical audio format. pm_gate_clocks "true" # clock can be gated } SectionLink."Codec" { # used for binding to the physical link id "0" hw_configs [ "CodecHWConfig" ] default_hw_conf_id "1" } ~~~~ Signed-off-by: Kirill Marinushkin Reviewed-by: Pierre-Louis Bossart Cc: Jaroslav Kysela Cc: Takashi Iwai Cc: Mark Brown Cc: Pan Xiuli Cc: Liam Girdwood Cc: linux-kernel@vger.kernel.org Cc: alsa-devel@alsa-project.org Signed-off-by: Mark Brown (cherry picked from commit 933e1c4a667103c4d10ebdc9505a0a6abd8c3fbd) --- include/uapi/sound/asoc.h | 7 ++++++- sound/soc/soc-topology.c | 7 +++++++ 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/include/uapi/sound/asoc.h b/include/uapi/sound/asoc.h index f0e5e21efa540d..f3c4b46e39d8b0 100644 --- a/include/uapi/sound/asoc.h +++ b/include/uapi/sound/asoc.h @@ -139,6 +139,11 @@ #define SND_SOC_TPLG_DAI_FLGBIT_SYMMETRIC_CHANNELS (1 << 1) #define SND_SOC_TPLG_DAI_FLGBIT_SYMMETRIC_SAMPLEBITS (1 << 2) +/* DAI clock gating */ +#define SND_SOC_TPLG_DAI_CLK_GATE_UNDEFINED 0 +#define SND_SOC_TPLG_DAI_CLK_GATE_GATED 1 +#define SND_SOC_TPLG_DAI_CLK_GATE_CONT 2 + /* DAI physical PCM data formats. * Add new formats to the end of the list. */ @@ -324,7 +329,7 @@ struct snd_soc_tplg_hw_config { __le32 size; /* in bytes of this structure */ __le32 id; /* unique ID - - used to match */ __le32 fmt; /* SND_SOC_DAI_FORMAT_ format value */ - __u8 clock_gated; /* 1 if clock can be gated to save power */ + __u8 clock_gated; /* SND_SOC_TPLG_DAI_CLK_GATE_ value */ __u8 invert_bclk; /* 1 for inverted BCLK, 0 for normal */ __u8 invert_fsync; /* 1 for inverted frame clock, 0 for normal */ __u8 bclk_master; /* SND_SOC_TPLG_BCLK_ value */ diff --git a/sound/soc/soc-topology.c b/sound/soc/soc-topology.c index 82db57868d4fea..f1b4e30995130e 100644 --- a/sound/soc/soc-topology.c +++ b/sound/soc/soc-topology.c @@ -2006,6 +2006,13 @@ static void set_link_hw_format(struct snd_soc_dai_link *link, link->dai_fmt = hw_config->fmt & SND_SOC_DAIFMT_FORMAT_MASK; + /* clock gating */ + if (hw_config->clock_gated == SND_SOC_TPLG_DAI_CLK_GATE_GATED) + link->dai_fmt |= SND_SOC_DAIFMT_GATED; + else if (hw_config->clock_gated == + SND_SOC_TPLG_DAI_CLK_GATE_CONT) + link->dai_fmt |= SND_SOC_DAIFMT_CONT; + /* clock signal polarity */ invert_bclk = hw_config->invert_bclk; invert_fsync = hw_config->invert_fsync; From fdab57554be01e096ce8ded2a9d162710d5fd1cf Mon Sep 17 00:00:00 2001 From: Kirill Marinushkin Date: Wed, 4 Apr 2018 06:19:39 +0200 Subject: [PATCH 003/107] ASoC: topology: Add definitions for mclk_direction values Current comment makes not clear the direction of mclk. Previously, similar description caused a misunderstanding for bclk_master and fsync_master. This commit solves the potential confusion the same way it is solved for bclk_master and fsync_master. Signed-off-by: Kirill Marinushkin Acked-by: Pierre-Louis Bossart Cc: Jaroslav Kysela Cc: Takashi Iwai Cc: Mark Brown Cc: Pan Xiuli Cc: Liam Girdwood Cc: linux-kernel@vger.kernel.org Cc: alsa-devel@alsa-project.org Signed-off-by: Mark Brown (cherry picked from commit e590522a06adce8ca2eb47e77d80616cd1542d91) --- include/uapi/sound/asoc.h | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/include/uapi/sound/asoc.h b/include/uapi/sound/asoc.h index f3c4b46e39d8b0..b901cdbe532a07 100644 --- a/include/uapi/sound/asoc.h +++ b/include/uapi/sound/asoc.h @@ -144,6 +144,10 @@ #define SND_SOC_TPLG_DAI_CLK_GATE_GATED 1 #define SND_SOC_TPLG_DAI_CLK_GATE_CONT 2 +/* DAI mclk_direction */ +#define SND_SOC_TPLG_MCLK_CO 0 /* for codec, mclk is output */ +#define SND_SOC_TPLG_MCLK_CI 1 /* for codec, mclk is input */ + /* DAI physical PCM data formats. * Add new formats to the end of the list. */ @@ -334,7 +338,7 @@ struct snd_soc_tplg_hw_config { __u8 invert_fsync; /* 1 for inverted frame clock, 0 for normal */ __u8 bclk_master; /* SND_SOC_TPLG_BCLK_ value */ __u8 fsync_master; /* SND_SOC_TPLG_FSYNC_ value */ - __u8 mclk_direction; /* 0 for input, 1 for output */ + __u8 mclk_direction; /* SND_SOC_TPLG_MCLK_ value */ __le16 reserved; /* for 32bit alignment */ __le32 mclk_rate; /* MCLK or SYSCLK freqency in Hz */ __le32 bclk_rate; /* BCLK freqency in Hz */ From 5625100d83602bab12e67174de5d46bfcf69ea54 Mon Sep 17 00:00:00 2001 From: Liam Girdwood Date: Tue, 27 Mar 2018 14:30:45 +0100 Subject: [PATCH 004/107] ASoC: topology: Add support for compressed PCMs Register a compressed PCM if topology defines one. Signed-off-by: Liam Girdwood Signed-off-by: Mark Brown (cherry picked from commit 5db6aab6f36f7560dc95f7ca340d5632b7a3be6a) --- sound/soc/soc-topology.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/sound/soc/soc-topology.c b/sound/soc/soc-topology.c index f1b4e30995130e..8097ddbf67730b 100644 --- a/sound/soc/soc-topology.c +++ b/sound/soc/soc-topology.c @@ -1754,6 +1754,9 @@ static int soc_tplg_dai_create(struct soc_tplg *tplg, set_stream_info(stream, caps); } + if (pcm->compress) + dai_drv->compress_new = snd_soc_new_compress; + /* pass control to component driver for optional further init */ ret = soc_tplg_dai_load(tplg, dai_drv); if (ret < 0) { From 64859fb736fc325398a8146e19583b5c5863253a Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Tue, 29 May 2018 18:30:02 -0500 Subject: [PATCH 005/107] ASoC: fix 0-day warnings with snd_soc_new_compress() All conditionally-defined routines in include/sound/soc.h expose a static inline fallback to avoid 0-day warnings and compilation issues, except snd_soc_new_compress(). Fixes: 5db6aab6f36f ('ASoC: topology: Add support for compressed PCMs') Signed-off-by: Pierre-Louis Bossart Signed-off-by: Mark Brown (cherry picked from commit 0b014d72ebae14c0c6ab3fb36a442fda91e1a1b3) --- include/sound/soc.h | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/include/sound/soc.h b/include/sound/soc.h index ad266d7e95537c..fc2e83e7caa24f 100644 --- a/include/sound/soc.h +++ b/include/sound/soc.h @@ -492,6 +492,11 @@ int snd_soc_platform_write(struct snd_soc_platform *platform, int soc_new_pcm(struct snd_soc_pcm_runtime *rtd, int num); #ifdef CONFIG_SND_SOC_COMPRESS int snd_soc_new_compress(struct snd_soc_pcm_runtime *rtd, int num); +#else +static inline int snd_soc_new_compress(struct snd_soc_pcm_runtime *rtd, int num) +{ + return 0; +} #endif void snd_soc_disconnect_sync(struct device *dev); From e022fa0892d833084450c686421c7b1c196f4e94 Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Fri, 27 Apr 2018 16:36:00 -0500 Subject: [PATCH 006/107] ASoC: Intel: cht-bsw-rt5672: allow for topology-defined codec-dai setup Hard-coded setups conflict with topology defined ones. Move this code to codec_fixup so that SOF can override codec dai settings, e.g. to only use 2 channels. Signed-off-by: Pierre-Louis Bossart Acked-by: Vinod Koul Signed-off-by: Mark Brown (cherry picked from commit bf14adcc4ddd101088237179bee6daa19bac1669) --- sound/soc/intel/boards/cht_bsw_rt5672.c | 30 +++++++++++++++++-------- 1 file changed, 21 insertions(+), 9 deletions(-) diff --git a/sound/soc/intel/boards/cht_bsw_rt5672.c b/sound/soc/intel/boards/cht_bsw_rt5672.c index e68ec32720a4ad..e5aa13058dd738 100644 --- a/sound/soc/intel/boards/cht_bsw_rt5672.c +++ b/sound/soc/intel/boards/cht_bsw_rt5672.c @@ -189,13 +189,6 @@ static int cht_codec_init(struct snd_soc_pcm_runtime *runtime) if (devm_acpi_dev_add_driver_gpios(component->dev, cht_rt5672_gpios)) dev_warn(runtime->dev, "Unable to add GPIO mapping table\n"); - /* TDM 4 slots 24 bit, set Rx & Tx bitmask to 4 active slots */ - ret = snd_soc_dai_set_tdm_slot(codec_dai, 0xF, 0xF, 4, 24); - if (ret < 0) { - dev_err(runtime->dev, "can't set codec TDM slot %d\n", ret); - return ret; - } - /* Select codec ASRC clock source to track I2S1 clock, because codec * is in slave mode and 100fs I2S format (BCLK = 100 * LRCLK) cannot * be supported by RT5672. Otherwise, ASRC will be disabled and cause @@ -252,6 +245,7 @@ static int cht_codec_fixup(struct snd_soc_pcm_runtime *rtd, SNDRV_PCM_HW_PARAM_RATE); struct snd_interval *channels = hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS); + int ret; /* The DSP will covert the FE rate to 48k, stereo, 24bits */ rate->min = rate->max = 48000; @@ -259,6 +253,26 @@ static int cht_codec_fixup(struct snd_soc_pcm_runtime *rtd, /* set SSP2 to 24-bit */ params_set_format(params, SNDRV_PCM_FORMAT_S24_LE); + + /* + * Default mode for SSP configuration is TDM 4 slot + */ + ret = snd_soc_dai_set_fmt(rtd->codec_dai, + SND_SOC_DAIFMT_DSP_B | + SND_SOC_DAIFMT_IB_NF | + SND_SOC_DAIFMT_CBS_CFS); + if (ret < 0) { + dev_err(rtd->dev, "can't set format to TDM %d\n", ret); + return ret; + } + + /* TDM 4 slots 24 bit, set Rx & Tx bitmask to 4 active slots */ + ret = snd_soc_dai_set_tdm_slot(rtd->codec_dai, 0xF, 0xF, 4, 24); + if (ret < 0) { + dev_err(rtd->dev, "can't set codec TDM slot %d\n", ret); + return ret; + } + return 0; } @@ -315,8 +329,6 @@ static struct snd_soc_dai_link cht_dailink[] = { .nonatomic = true, .codec_dai_name = "rt5670-aif1", .codec_name = "i2c-10EC5670:00", - .dai_fmt = SND_SOC_DAIFMT_DSP_B | SND_SOC_DAIFMT_IB_NF - | SND_SOC_DAIFMT_CBS_CFS, .init = cht_codec_init, .be_hw_params_fixup = cht_codec_fixup, .dpcm_playback = 1, From 6bde10d618f4bbf257d5164b7bd2db09ba38cec2 Mon Sep 17 00:00:00 2001 From: Ranjani Sridharan Date: Wed, 9 May 2018 17:53:04 -0700 Subject: [PATCH 007/107] ALSA: core api: define offsets for TLV items Currently, there are no pre-defined accessors for the elements in topology TLV data. In the absence of such offsets, the tlv data will have to be decoded using hardwired offset numbers 0-N depending on the type of TLV. This patch defines accessor offsets for the type, length, min and mute/step items in TLV data for DB_SCALE type tlv's. These will be used by drivers to decode the TLV data while loading topology thereby improving code readability. The type and len offsets are common for all TLV types. The min and step/mute offsets are specific to DB_SCALE tlv type. Signed-off-by: Ranjani Sridharan Reviewed-by: Takashi Sakamoto Signed-off-by: Takashi Iwai (cherry picked from commit 08f9f4485f2158de0fa77506687a073cb869e803) --- include/uapi/sound/tlv.h | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/include/uapi/sound/tlv.h b/include/uapi/sound/tlv.h index be5371f09a626b..e3437e96519a8a 100644 --- a/include/uapi/sound/tlv.h +++ b/include/uapi/sound/tlv.h @@ -42,6 +42,10 @@ #define SNDRV_CTL_TLVD_LENGTH(...) \ ((unsigned int)sizeof((const unsigned int[]) { __VA_ARGS__ })) +/* Accessor offsets for TLV data items */ +#define SNDRV_CTL_TLVO_TYPE 0 +#define SNDRV_CTL_TLVO_LEN 1 + #define SNDRV_CTL_TLVD_CONTAINER_ITEM(...) \ SNDRV_CTL_TLVD_ITEM(SNDRV_CTL_TLVT_CONTAINER, __VA_ARGS__) #define SNDRV_CTL_TLVD_DECLARE_CONTAINER(name, ...) \ @@ -61,6 +65,10 @@ SNDRV_CTL_TLVD_DB_SCALE_ITEM(min, step, mute) \ } +/* Accessor offsets for min, mute and step items in dB scale type TLV */ +#define SNDRV_CTL_TLVO_DB_SCALE_MIN 2 +#define SNDRV_CTL_TLVO_DB_SCALE_MUTE_AND_STEP 3 + /* dB scale specified with min/max values instead of step */ #define SNDRV_CTL_TLVD_DB_MINMAX_ITEM(min_dB, max_dB) \ SNDRV_CTL_TLVD_ITEM(SNDRV_CTL_TLVT_DB_MINMAX, (min_dB), (max_dB)) From 4c685a7d8e3b62d364f0d678e9b1fb9a7bafe913 Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Thu, 17 May 2018 17:53:26 -0500 Subject: [PATCH 008/107] ASoC: pcm512x: Add ACPI support HID is assumed to be made of TI PCI ID (0x104C) + part number, so all four 104C5121, 104C5122, 104C5141 104C5142 are valid. Signed-off-by: Pierre-Louis Bossart Signed-off-by: Mark Brown (cherry picked from commit b84f48d18124da49a06e5d4ba6525b2955f15899) --- sound/soc/codecs/pcm512x-i2c.c | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/sound/soc/codecs/pcm512x-i2c.c b/sound/soc/codecs/pcm512x-i2c.c index 5f9c069569d5a9..0fe5ced841a383 100644 --- a/sound/soc/codecs/pcm512x-i2c.c +++ b/sound/soc/codecs/pcm512x-i2c.c @@ -17,6 +17,7 @@ #include #include #include +#include #include "pcm512x.h" @@ -52,6 +53,7 @@ static const struct i2c_device_id pcm512x_i2c_id[] = { }; MODULE_DEVICE_TABLE(i2c, pcm512x_i2c_id); +#if defined(CONFIG_OF) static const struct of_device_id pcm512x_of_match[] = { { .compatible = "ti,pcm5121", }, { .compatible = "ti,pcm5122", }, @@ -60,6 +62,18 @@ static const struct of_device_id pcm512x_of_match[] = { { } }; MODULE_DEVICE_TABLE(of, pcm512x_of_match); +#endif + +#ifdef CONFIG_ACPI +static const struct acpi_device_id pcm512x_acpi_match[] = { + { "104C5121", 0 }, + { "104C5122", 0 }, + { "104C5141", 0 }, + { "104C5142", 0 }, + { }, +}; +MODULE_DEVICE_TABLE(acpi, pcm512x_acpi_match); +#endif static struct i2c_driver pcm512x_i2c_driver = { .probe = pcm512x_i2c_probe, @@ -67,7 +81,8 @@ static struct i2c_driver pcm512x_i2c_driver = { .id_table = pcm512x_i2c_id, .driver = { .name = "pcm512x", - .of_match_table = pcm512x_of_match, + .of_match_table = of_match_ptr(pcm512x_of_match), + .acpi_match_table = ACPI_PTR(pcm512x_acpi_match), .pm = &pcm512x_pm_ops, }, }; From 9f2ec80d821a8d54a62ab9ab16ce6c44a5513997 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Fri, 13 Apr 2018 14:45:37 +0200 Subject: [PATCH 009/107] ASoC: Intel: Disable SND_SOC_INTEL_BAYTRAIL when SND_SST_ATOM_HIFI2_PLATFORM is enabled The sound/soc/intel/common/sst-acpi.c code only tries to load the "baytrail-pcm-audio" driver (and supporting board drivers) when SND_SST_ATOM_HIFI2_PLATFORM is not enabled, since otherwise these are handled by snd-soc-sst-atom-hifi2-platform.ko. Since these thus will never be used when SND_SST_ATOM_HIFI2_PLATFORM is enabled, building these drivers when it is enabled is useless. Add a Kconfig dependency to reflect this, so that SND_SOC_INTEL_BAYTRAIL cannot be enabled when SND_SST_ATOM_HIFI2_PLATFORM is also enabled. Signed-off-by: Hans de Goede Acked-by: Pierre-Louis Bossart Signed-off-by: Mark Brown (cherry picked from commit ed55fe24d7cb6fdc391ab808f22f163bd28928be) --- sound/soc/intel/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sound/soc/intel/Kconfig b/sound/soc/intel/Kconfig index addac2a8e52a57..0caa1f4eb94d7d 100644 --- a/sound/soc/intel/Kconfig +++ b/sound/soc/intel/Kconfig @@ -61,7 +61,7 @@ config SND_SOC_INTEL_HASWELL config SND_SOC_INTEL_BAYTRAIL tristate "Baytrail (legacy) Platforms" - depends on DMADEVICES && ACPI + depends on DMADEVICES && ACPI && SND_SST_ATOM_HIFI2_PLATFORM=n select SND_SOC_INTEL_SST select SND_SOC_INTEL_SST_ACPI select SND_SOC_INTEL_SST_FIRMWARE From 28fe6c4fddc935576bcb962238a665b53884c350 Mon Sep 17 00:00:00 2001 From: Liam Girdwood Date: Thu, 14 Jun 2018 20:50:37 +0100 Subject: [PATCH 010/107] ASoC: topology: Give more data to clients via callbacks Give topology clients more access to the topology data by passing index, pcm, link_config and dai_driver to clients. This allows clients to fully instantiate and track topology objects. The SOF driver is the first user of these new APIs and needs them to build component topology driver and FW objects. Signed-off-by: Liam Girdwood Signed-off-by: Mark Brown (cherry picked from commit c60b613a7097cff20fdd05e2891ce69542f0d5a3) --- include/sound/soc-topology.h | 23 +++++++++++-------- sound/soc/intel/skylake/skl-pcm.c | 7 +++--- sound/soc/intel/skylake/skl-topology.c | 5 +++-- sound/soc/intel/skylake/skl-topology.h | 5 +++-- sound/soc/soc-topology.c | 31 +++++++++++++++----------- 5 files changed, 42 insertions(+), 29 deletions(-) diff --git a/include/sound/soc-topology.h b/include/sound/soc-topology.h index f552c3f56368c4..e1f265e21ee128 100644 --- a/include/sound/soc-topology.h +++ b/include/sound/soc-topology.h @@ -30,6 +30,8 @@ struct snd_soc_dapm_context; struct snd_soc_card; struct snd_kcontrol_new; struct snd_soc_dai_link; +struct snd_soc_dai_driver; +struct snd_soc_dai; /* object scan be loaded and unloaded in groups with identfying indexes */ #define SND_SOC_TPLG_INDEX_ALL 0 /* ID that matches all FW objects */ @@ -109,35 +111,38 @@ struct snd_soc_tplg_widget_events { struct snd_soc_tplg_ops { /* external kcontrol init - used for any driver specific init */ - int (*control_load)(struct snd_soc_component *, + int (*control_load)(struct snd_soc_component *, int index, struct snd_kcontrol_new *, struct snd_soc_tplg_ctl_hdr *); int (*control_unload)(struct snd_soc_component *, struct snd_soc_dobj *); /* external widget init - used for any driver specific init */ - int (*widget_load)(struct snd_soc_component *, + int (*widget_load)(struct snd_soc_component *, int index, struct snd_soc_dapm_widget *, struct snd_soc_tplg_dapm_widget *); - int (*widget_ready)(struct snd_soc_component *, + int (*widget_ready)(struct snd_soc_component *, int index, struct snd_soc_dapm_widget *, struct snd_soc_tplg_dapm_widget *); int (*widget_unload)(struct snd_soc_component *, struct snd_soc_dobj *); /* FE DAI - used for any driver specific init */ - int (*dai_load)(struct snd_soc_component *, - struct snd_soc_dai_driver *dai_drv); + int (*dai_load)(struct snd_soc_component *, int index, + struct snd_soc_dai_driver *dai_drv, + struct snd_soc_tplg_pcm *pcm, struct snd_soc_dai *dai); + int (*dai_unload)(struct snd_soc_component *, struct snd_soc_dobj *); /* DAI link - used for any driver specific init */ - int (*link_load)(struct snd_soc_component *, - struct snd_soc_dai_link *link); + int (*link_load)(struct snd_soc_component *, int index, + struct snd_soc_dai_link *link, + struct snd_soc_tplg_link_config *cfg); int (*link_unload)(struct snd_soc_component *, struct snd_soc_dobj *); /* callback to handle vendor bespoke data */ - int (*vendor_load)(struct snd_soc_component *, + int (*vendor_load)(struct snd_soc_component *, int index, struct snd_soc_tplg_hdr *); int (*vendor_unload)(struct snd_soc_component *, struct snd_soc_tplg_hdr *); @@ -146,7 +151,7 @@ struct snd_soc_tplg_ops { void (*complete)(struct snd_soc_component *); /* manifest - optional to inform component of manifest */ - int (*manifest)(struct snd_soc_component *, + int (*manifest)(struct snd_soc_component *, int index, struct snd_soc_tplg_manifest *); /* vendor specific kcontrol handlers available for binding */ diff --git a/sound/soc/intel/skylake/skl-pcm.c b/sound/soc/intel/skylake/skl-pcm.c index 15cb8ac3e374b8..c466265a2fbbc5 100644 --- a/sound/soc/intel/skylake/skl-pcm.c +++ b/sound/soc/intel/skylake/skl-pcm.c @@ -989,10 +989,11 @@ static struct snd_soc_dai_driver skl_platform_dai[] = { }, }; -int skl_dai_load(struct snd_soc_component *cmp, - struct snd_soc_dai_driver *pcm_dai) +int skl_dai_load(struct snd_soc_component *cmp, int index, + struct snd_soc_dai_driver *dai_drv, + struct snd_soc_tplg_pcm *pcm, struct snd_soc_dai *dai) { - pcm_dai->ops = &skl_pcm_dai_ops; + dai_drv->ops = &skl_pcm_dai_ops; return 0; } diff --git a/sound/soc/intel/skylake/skl-topology.c b/sound/soc/intel/skylake/skl-topology.c index 3b1dca419883b9..6ac081f1f215df 100644 --- a/sound/soc/intel/skylake/skl-topology.c +++ b/sound/soc/intel/skylake/skl-topology.c @@ -2851,7 +2851,7 @@ void skl_cleanup_resources(struct skl *skl) * information to the driver about module and pipeline parameters which DSP * FW expects like ids, resource values, formats etc */ -static int skl_tplg_widget_load(struct snd_soc_component *cmpnt, +static int skl_tplg_widget_load(struct snd_soc_component *cmpnt, int index, struct snd_soc_dapm_widget *w, struct snd_soc_tplg_dapm_widget *tplg_w) { @@ -2958,6 +2958,7 @@ static int skl_init_enum_data(struct device *dev, struct soc_enum *se, } static int skl_tplg_control_load(struct snd_soc_component *cmpnt, + int index, struct snd_kcontrol_new *kctl, struct snd_soc_tplg_ctl_hdr *hdr) { @@ -3446,7 +3447,7 @@ static int skl_tplg_get_manifest_data(struct snd_soc_tplg_manifest *manifest, return 0; } -static int skl_manifest_load(struct snd_soc_component *cmpnt, +static int skl_manifest_load(struct snd_soc_component *cmpnt, int index, struct snd_soc_tplg_manifest *manifest) { struct hdac_ext_bus *ebus = snd_soc_component_get_drvdata(cmpnt); diff --git a/sound/soc/intel/skylake/skl-topology.h b/sound/soc/intel/skylake/skl-topology.h index b1e0667c0ae0a7..8fcba4cfca6cfc 100644 --- a/sound/soc/intel/skylake/skl-topology.h +++ b/sound/soc/intel/skylake/skl-topology.h @@ -512,8 +512,9 @@ int skl_pcm_host_dma_prepare(struct device *dev, int skl_pcm_link_dma_prepare(struct device *dev, struct skl_pipe_params *params); -int skl_dai_load(struct snd_soc_component *cmp, - struct snd_soc_dai_driver *pcm_dai); +int skl_dai_load(struct snd_soc_component *cmp, int index, + struct snd_soc_dai_driver *dai_drv, + struct snd_soc_tplg_pcm *pcm, struct snd_soc_dai *dai); void skl_tplg_add_moduleid_in_bind_params(struct skl *skl, struct snd_soc_dapm_widget *w); #endif diff --git a/sound/soc/soc-topology.c b/sound/soc/soc-topology.c index 8097ddbf67730b..f1c27a7be7ca8f 100644 --- a/sound/soc/soc-topology.c +++ b/sound/soc/soc-topology.c @@ -315,7 +315,7 @@ static int soc_tplg_vendor_load_(struct soc_tplg *tplg, int ret = 0; if (tplg->comp && tplg->ops && tplg->ops->vendor_load) - ret = tplg->ops->vendor_load(tplg->comp, hdr); + ret = tplg->ops->vendor_load(tplg->comp, tplg->index, hdr); else { dev_err(tplg->dev, "ASoC: no vendor load callback for ID %d\n", hdr->vendor_type); @@ -347,7 +347,8 @@ static int soc_tplg_widget_load(struct soc_tplg *tplg, struct snd_soc_dapm_widget *w, struct snd_soc_tplg_dapm_widget *tplg_w) { if (tplg->comp && tplg->ops && tplg->ops->widget_load) - return tplg->ops->widget_load(tplg->comp, w, tplg_w); + return tplg->ops->widget_load(tplg->comp, tplg->index, w, + tplg_w); return 0; } @@ -358,27 +359,30 @@ static int soc_tplg_widget_ready(struct soc_tplg *tplg, struct snd_soc_dapm_widget *w, struct snd_soc_tplg_dapm_widget *tplg_w) { if (tplg->comp && tplg->ops && tplg->ops->widget_ready) - return tplg->ops->widget_ready(tplg->comp, w, tplg_w); + return tplg->ops->widget_ready(tplg->comp, tplg->index, w, + tplg_w); return 0; } /* pass DAI configurations to component driver for extra initialization */ static int soc_tplg_dai_load(struct soc_tplg *tplg, - struct snd_soc_dai_driver *dai_drv) + struct snd_soc_dai_driver *dai_drv, + struct snd_soc_tplg_pcm *pcm, struct snd_soc_dai *dai) { if (tplg->comp && tplg->ops && tplg->ops->dai_load) - return tplg->ops->dai_load(tplg->comp, dai_drv); + return tplg->ops->dai_load(tplg->comp, tplg->index, dai_drv, + pcm, dai); return 0; } /* pass link configurations to component driver for extra initialization */ static int soc_tplg_dai_link_load(struct soc_tplg *tplg, - struct snd_soc_dai_link *link) + struct snd_soc_dai_link *link, struct snd_soc_tplg_link_config *cfg) { if (tplg->comp && tplg->ops && tplg->ops->link_load) - return tplg->ops->link_load(tplg->comp, link); + return tplg->ops->link_load(tplg->comp, tplg->index, link, cfg); return 0; } @@ -699,7 +703,8 @@ static int soc_tplg_init_kcontrol(struct soc_tplg *tplg, struct snd_kcontrol_new *k, struct snd_soc_tplg_ctl_hdr *hdr) { if (tplg->comp && tplg->ops && tplg->ops->control_load) - return tplg->ops->control_load(tplg->comp, k, hdr); + return tplg->ops->control_load(tplg->comp, tplg->index, k, + hdr); return 0; } @@ -1758,7 +1763,7 @@ static int soc_tplg_dai_create(struct soc_tplg *tplg, dai_drv->compress_new = snd_soc_new_compress; /* pass control to component driver for optional further init */ - ret = soc_tplg_dai_load(tplg, dai_drv); + ret = soc_tplg_dai_load(tplg, dai_drv, pcm, NULL); if (ret < 0) { dev_err(tplg->comp->dev, "ASoC: DAI loading failed\n"); kfree(dai_drv); @@ -1828,7 +1833,7 @@ static int soc_tplg_fe_link_create(struct soc_tplg *tplg, set_link_flags(link, pcm->flag_mask, pcm->flags); /* pass control to component driver for optional further init */ - ret = soc_tplg_dai_link_load(tplg, link); + ret = soc_tplg_dai_link_load(tplg, link, NULL); if (ret < 0) { dev_err(tplg->comp->dev, "ASoC: FE link loading failed\n"); kfree(link); @@ -2128,7 +2133,7 @@ static int soc_tplg_link_config(struct soc_tplg *tplg, set_link_flags(link, cfg->flag_mask, cfg->flags); /* pass control to component driver for optional further init */ - ret = soc_tplg_dai_link_load(tplg, link); + ret = soc_tplg_dai_link_load(tplg, link, cfg); if (ret < 0) { dev_err(tplg->dev, "ASoC: physical link loading failed\n"); return ret; @@ -2250,7 +2255,7 @@ static int soc_tplg_dai_config(struct soc_tplg *tplg, set_dai_flags(dai_drv, d->flag_mask, d->flags); /* pass control to component driver for optional further init */ - ret = soc_tplg_dai_load(tplg, dai_drv); + ret = soc_tplg_dai_load(tplg, dai_drv, NULL, dai); if (ret < 0) { dev_err(tplg->comp->dev, "ASoC: DAI loading failed\n"); return ret; @@ -2356,7 +2361,7 @@ static int soc_tplg_manifest_load(struct soc_tplg *tplg, /* pass control to component driver for optional further init */ if (tplg->comp && tplg->ops && tplg->ops->manifest) - return tplg->ops->manifest(tplg->comp, _manifest); + return tplg->ops->manifest(tplg->comp, tplg->index, _manifest); if (!abi_match) /* free the duplicated one */ kfree(_manifest); From 4c8ac45f0f150b4e3ec46afd5aa9cb2bf9b163d1 Mon Sep 17 00:00:00 2001 From: Liam Girdwood Date: Thu, 14 Jun 2018 20:53:59 +0100 Subject: [PATCH 011/107] ASoC: topology: Add callback for DAPM route load/unload Add a callback fro clients for notification about DAPM route loading and unloading. Signed-off-by: Liam Girdwood Signed-off-by: Mark Brown (cherry picked from commit 503e79b793fea5de626db73accf8e8994bc4289d) --- include/sound/soc-topology.h | 7 +++++++ sound/soc/soc-topology.c | 13 +++++++++++++ 2 files changed, 20 insertions(+) diff --git a/include/sound/soc-topology.h b/include/sound/soc-topology.h index e1f265e21ee128..401ef2c45d6c76 100644 --- a/include/sound/soc-topology.h +++ b/include/sound/soc-topology.h @@ -32,6 +32,7 @@ struct snd_kcontrol_new; struct snd_soc_dai_link; struct snd_soc_dai_driver; struct snd_soc_dai; +struct snd_soc_dapm_route; /* object scan be loaded and unloaded in groups with identfying indexes */ #define SND_SOC_TPLG_INDEX_ALL 0 /* ID that matches all FW objects */ @@ -116,6 +117,12 @@ struct snd_soc_tplg_ops { int (*control_unload)(struct snd_soc_component *, struct snd_soc_dobj *); + /* DAPM graph route element loading and unloading */ + int (*dapm_route_load)(struct snd_soc_component *, int index, + struct snd_soc_dapm_route *route); + int (*dapm_route_unload)(struct snd_soc_component *, + struct snd_soc_dobj *); + /* external widget init - used for any driver specific init */ int (*widget_load)(struct snd_soc_component *, int index, struct snd_soc_dapm_widget *, diff --git a/sound/soc/soc-topology.c b/sound/soc/soc-topology.c index f1c27a7be7ca8f..1dfd2782225a58 100644 --- a/sound/soc/soc-topology.c +++ b/sound/soc/soc-topology.c @@ -1161,6 +1161,17 @@ static int soc_tplg_kcontrol_elems_load(struct soc_tplg *tplg, return 0; } +/* optionally pass new dynamic kcontrol to component driver. */ +static int soc_tplg_add_route(struct soc_tplg *tplg, + struct snd_soc_dapm_route *route) +{ + if (tplg->comp && tplg->ops && tplg->ops->dapm_route_load) + return tplg->ops->dapm_route_load(tplg->comp, tplg->index, + route); + + return 0; +} + static int soc_tplg_dapm_graph_elems_load(struct soc_tplg *tplg, struct snd_soc_tplg_hdr *hdr) { @@ -1209,6 +1220,8 @@ static int soc_tplg_dapm_graph_elems_load(struct soc_tplg *tplg, else route.control = elem->control; + soc_tplg_add_route(tplg, &route); + /* add route, but keep going if some fail */ snd_soc_dapm_add_routes(dapm, &route, 1); } From 2695cf512c5c5971828f39d36e192740e34def8a Mon Sep 17 00:00:00 2001 From: Liam Girdwood Date: Thu, 14 Jun 2018 20:26:42 +0100 Subject: [PATCH 012/107] ASoC: dapm: Fix potential DAI widget pointer deref when linking DAIs Sometime a component or topology may configure a DAI widget with no private data leading to a dev_dbg() dereferencne of this data. Fix this to check for non NULL private data and let users know if widget is missing DAI. Signed-off-by: Liam Girdwood Signed-off-by: Mark Brown (cherry picked from commit e01b4f624278d5efe5fb5da585ca371947b16680) --- sound/soc/soc-dapm.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/sound/soc/soc-dapm.c b/sound/soc/soc-dapm.c index 2d9709104ec55e..6d54128e44b46b 100644 --- a/sound/soc/soc-dapm.c +++ b/sound/soc/soc-dapm.c @@ -4075,6 +4075,13 @@ int snd_soc_dapm_link_dai_widgets(struct snd_soc_card *card) continue; } + /* let users know there is no DAI to link */ + if (!dai_w->priv) { + dev_dbg(card->dev, "dai widget %s has no DAI\n", + dai_w->name); + continue; + } + dai = dai_w->priv; /* ...find all widgets with the same stream and link them */ From 02dd6a5758675f833ff9b9183586301355766e98 Mon Sep 17 00:00:00 2001 From: Naveen Manohar Date: Mon, 18 Jun 2018 13:29:35 -0500 Subject: [PATCH 013/107] ASoC: Intel: broxton: reduce machine name for bxt_da7219_max98357a Use truncated names in bxt id table and bxt_da7219_max98357a machine as platform device id table expects names to be less then 20chars. Signed-off-by: Naveen Manohar Signed-off-by: Pierre-Louis Bossart Signed-off-by: Mark Brown (cherry picked from commit 95555f580dca21fac5ea35c10fa92fa034bd403f) --- sound/soc/intel/boards/bxt_da7219_max98357a.c | 4 ++-- sound/soc/intel/skylake/skl.c | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/sound/soc/intel/boards/bxt_da7219_max98357a.c b/sound/soc/intel/boards/bxt_da7219_max98357a.c index 668c0934e942ba..ce021a3efc534d 100644 --- a/sound/soc/intel/boards/bxt_da7219_max98357a.c +++ b/sound/soc/intel/boards/bxt_da7219_max98357a.c @@ -586,7 +586,7 @@ static int broxton_audio_probe(struct platform_device *pdev) static struct platform_driver broxton_audio = { .probe = broxton_audio_probe, .driver = { - .name = "bxt_da7219_max98357a_i2s", + .name = "bxt_da7219_max98357a", .pm = &snd_soc_pm_ops, }, }; @@ -599,4 +599,4 @@ MODULE_AUTHOR("Rohit Ainapure "); MODULE_AUTHOR("Harsha Priya "); MODULE_AUTHOR("Conrad Cooke "); MODULE_LICENSE("GPL v2"); -MODULE_ALIAS("platform:bxt_da7219_max98357a_i2s"); +MODULE_ALIAS("platform:bxt_da7219_max98357a"); diff --git a/sound/soc/intel/skylake/skl.c b/sound/soc/intel/skylake/skl.c index abf324747b2901..d50c2eed46deaa 100644 --- a/sound/soc/intel/skylake/skl.c +++ b/sound/soc/intel/skylake/skl.c @@ -1086,7 +1086,7 @@ static struct snd_soc_acpi_mach sst_bxtp_devdata[] = { }, { .id = "DLGS7219", - .drv_name = "bxt_da7219_max98357a_i2s", + .drv_name = "bxt_da7219_max98357a", .fw_filename = "intel/dsp_fw_bxtn.bin", .machine_quirk = snd_soc_acpi_codec_list, .quirk_data = &bxt_codecs, From 3c71f58ad503db20420dbec734e18da014dc23a0 Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Mon, 18 Jun 2018 13:29:36 -0500 Subject: [PATCH 014/107] ASoC: Intel: Skylake: cleanup before moving ACPI tables There is no need to deal with DMICs if the DSP is not present and there is no ACPI machine ID found. Simplify before moving these ACPI tables to sound/soc/intel/common Signed-off-by: Pierre-Louis Bossart Signed-off-by: Mark Brown (cherry picked from commit 5f15f267daf81a4c7c2a1cd2a0d6743ec7fc8b59) --- sound/soc/intel/skylake/skl.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/sound/soc/intel/skylake/skl.c b/sound/soc/intel/skylake/skl.c index d50c2eed46deaa..f1692178666d3b 100644 --- a/sound/soc/intel/skylake/skl.c +++ b/sound/soc/intel/skylake/skl.c @@ -493,10 +493,12 @@ static int skl_find_machine(struct skl *skl, void *driver_data) skl->mach = mach; skl->fw_name = mach->fw_filename; - pdata = skl->mach->pdata; + pdata = mach->pdata; - if (mach->pdata) + if (pdata) { skl->use_tplg_pcm = pdata->use_tplg_pcm; + pdata->dmic_num = skl_get_dmic_geo(skl); + } return 0; } @@ -923,8 +925,6 @@ 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 (bus->ppcap) { /* create device for dsp clk */ From 7a3d05442197e5d9a5a2471ebb539282565444ea Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Mon, 18 Jun 2018 13:29:37 -0500 Subject: [PATCH 015/107] ASoC: Intel: move SKL+ codec ACPI tables to common directory No functionality change, just move to common tables to make it easier to deal with SOF and share the same machine drivers - as done previously for BYT/CHT/HSW/BDW. Signed-off-by: Pierre-Louis Bossart Signed-off-by: Mark Brown (cherry picked from commit cbaa7f0bdbee1969bb311c641abbd0d2af6ba861) --- include/sound/soc-acpi-intel-match.h | 5 + sound/soc/intel/common/Makefile | 6 +- .../intel/common/soc-acpi-intel-bxt-match.c | 35 ++++ .../intel/common/soc-acpi-intel-cnl-match.c | 29 ++++ .../intel/common/soc-acpi-intel-glk-match.c | 23 +++ .../intel/common/soc-acpi-intel-kbl-match.c | 91 ++++++++++ .../intel/common/soc-acpi-intel-skl-match.c | 47 +++++ sound/soc/intel/skylake/skl.c | 162 +----------------- 8 files changed, 241 insertions(+), 157 deletions(-) create mode 100644 sound/soc/intel/common/soc-acpi-intel-bxt-match.c create mode 100644 sound/soc/intel/common/soc-acpi-intel-cnl-match.c create mode 100644 sound/soc/intel/common/soc-acpi-intel-glk-match.c create mode 100644 sound/soc/intel/common/soc-acpi-intel-kbl-match.c create mode 100644 sound/soc/intel/common/soc-acpi-intel-skl-match.c diff --git a/include/sound/soc-acpi-intel-match.h b/include/sound/soc-acpi-intel-match.h index 9da6388c20a1cc..917ddd0f2762ea 100644 --- a/include/sound/soc-acpi-intel-match.h +++ b/include/sound/soc-acpi-intel-match.h @@ -29,5 +29,10 @@ extern struct snd_soc_acpi_mach snd_soc_acpi_intel_broadwell_machines[]; extern struct snd_soc_acpi_mach snd_soc_acpi_intel_baytrail_legacy_machines[]; extern struct snd_soc_acpi_mach snd_soc_acpi_intel_baytrail_machines[]; extern struct snd_soc_acpi_mach snd_soc_acpi_intel_cherrytrail_machines[]; +extern struct snd_soc_acpi_mach snd_soc_acpi_intel_skl_machines[]; +extern struct snd_soc_acpi_mach snd_soc_acpi_intel_kbl_machines[]; +extern struct snd_soc_acpi_mach snd_soc_acpi_intel_bxt_machines[]; +extern struct snd_soc_acpi_mach snd_soc_acpi_intel_glk_machines[]; +extern struct snd_soc_acpi_mach snd_soc_acpi_intel_cnl_machines[]; #endif diff --git a/sound/soc/intel/common/Makefile b/sound/soc/intel/common/Makefile index 7379d8830c391e..915a34cdc8acbb 100644 --- a/sound/soc/intel/common/Makefile +++ b/sound/soc/intel/common/Makefile @@ -3,7 +3,11 @@ snd-soc-sst-dsp-objs := sst-dsp.o snd-soc-sst-acpi-objs := sst-acpi.o snd-soc-sst-ipc-objs := sst-ipc.o snd-soc-sst-firmware-objs := sst-firmware.o -snd-soc-acpi-intel-match-objs := soc-acpi-intel-byt-match.o soc-acpi-intel-cht-match.o soc-acpi-intel-hsw-bdw-match.o +snd-soc-acpi-intel-match-objs := soc-acpi-intel-byt-match.o soc-acpi-intel-cht-match.o \ + soc-acpi-intel-hsw-bdw-match.o \ + soc-acpi-intel-skl-match.o soc-acpi-intel-kbl-match.o \ + soc-acpi-intel-bxt-match.o soc-acpi-intel-glk-match.o \ + soc-acpi-intel-cnl-match.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 diff --git a/sound/soc/intel/common/soc-acpi-intel-bxt-match.c b/sound/soc/intel/common/soc-acpi-intel-bxt-match.c new file mode 100644 index 00000000000000..569f1de97e82da --- /dev/null +++ b/sound/soc/intel/common/soc-acpi-intel-bxt-match.c @@ -0,0 +1,35 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * soc-apci-intel-bxt-match.c - tables and support for BXT ACPI enumeration. + * + * Copyright (c) 2018, Intel Corporation. + * + */ + +#include +#include + +static struct snd_soc_acpi_codecs bxt_codecs = { + .num_codecs = 1, + .codecs = {"MX98357A"} +}; + +struct snd_soc_acpi_mach snd_soc_acpi_intel_bxt_machines[] = { + { + .id = "INT343A", + .drv_name = "bxt_alc298s_i2s", + .fw_filename = "intel/dsp_fw_bxtn.bin", + }, + { + .id = "DLGS7219", + .drv_name = "bxt_da7219_max98357a", + .fw_filename = "intel/dsp_fw_bxtn.bin", + .machine_quirk = snd_soc_acpi_codec_list, + .quirk_data = &bxt_codecs, + }, + {}, +}; +EXPORT_SYMBOL_GPL(snd_soc_acpi_intel_bxt_machines); + +MODULE_LICENSE("GPL v2"); +MODULE_DESCRIPTION("Intel Common ACPI Match module"); diff --git a/sound/soc/intel/common/soc-acpi-intel-cnl-match.c b/sound/soc/intel/common/soc-acpi-intel-cnl-match.c new file mode 100644 index 00000000000000..b83ee053d41767 --- /dev/null +++ b/sound/soc/intel/common/soc-acpi-intel-cnl-match.c @@ -0,0 +1,29 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * soc-apci-intel-cnl-match.c - tables and support for CNL ACPI enumeration. + * + * Copyright (c) 2018, Intel Corporation. + * + */ + +#include +#include +#include "../skylake/skl.h" + +static struct skl_machine_pdata cnl_pdata = { + .use_tplg_pcm = true, +}; + +struct snd_soc_acpi_mach snd_soc_acpi_intel_cnl_machines[] = { + { + .id = "INT34C2", + .drv_name = "cnl_rt274", + .fw_filename = "intel/dsp_fw_cnl.bin", + .pdata = &cnl_pdata, + }, + {}, +}; +EXPORT_SYMBOL_GPL(snd_soc_acpi_intel_cnl_machines); + +MODULE_LICENSE("GPL v2"); +MODULE_DESCRIPTION("Intel Common ACPI Match module"); diff --git a/sound/soc/intel/common/soc-acpi-intel-glk-match.c b/sound/soc/intel/common/soc-acpi-intel-glk-match.c new file mode 100644 index 00000000000000..dee09439e7cb16 --- /dev/null +++ b/sound/soc/intel/common/soc-acpi-intel-glk-match.c @@ -0,0 +1,23 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * soc-apci-intel-glk-match.c - tables and support for GLK ACPI enumeration. + * + * Copyright (c) 2018, Intel Corporation. + * + */ + +#include +#include + +struct snd_soc_acpi_mach snd_soc_acpi_intel_glk_machines[] = { + { + .id = "INT343A", + .drv_name = "glk_alc298s_i2s", + .fw_filename = "intel/dsp_fw_glk.bin", + }, + {}, +}; +EXPORT_SYMBOL_GPL(snd_soc_acpi_intel_glk_machines); + +MODULE_LICENSE("GPL v2"); +MODULE_DESCRIPTION("Intel Common ACPI Match module"); diff --git a/sound/soc/intel/common/soc-acpi-intel-kbl-match.c b/sound/soc/intel/common/soc-acpi-intel-kbl-match.c new file mode 100644 index 00000000000000..0ee173ca437dd3 --- /dev/null +++ b/sound/soc/intel/common/soc-acpi-intel-kbl-match.c @@ -0,0 +1,91 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * soc-apci-intel-kbl-match.c - tables and support for KBL ACPI enumeration. + * + * Copyright (c) 2018, Intel Corporation. + * + */ + +#include +#include +#include "../skylake/skl.h" + +static struct skl_machine_pdata skl_dmic_data; + +static struct snd_soc_acpi_codecs kbl_codecs = { + .num_codecs = 1, + .codecs = {"10508825"} +}; + +static struct snd_soc_acpi_codecs kbl_poppy_codecs = { + .num_codecs = 1, + .codecs = {"10EC5663"} +}; + +static struct snd_soc_acpi_codecs kbl_5663_5514_codecs = { + .num_codecs = 2, + .codecs = {"10EC5663", "10EC5514"} +}; + +static struct snd_soc_acpi_codecs kbl_7219_98357_codecs = { + .num_codecs = 1, + .codecs = {"MX98357A"} +}; + +struct snd_soc_acpi_mach snd_soc_acpi_intel_kbl_machines[] = { + { + .id = "INT343A", + .drv_name = "kbl_alc286s_i2s", + .fw_filename = "intel/dsp_fw_kbl.bin", + }, + { + .id = "INT343B", + .drv_name = "kbl_n88l25_s4567", + .fw_filename = "intel/dsp_fw_kbl.bin", + .machine_quirk = snd_soc_acpi_codec_list, + .quirk_data = &kbl_codecs, + .pdata = &skl_dmic_data, + }, + { + .id = "MX98357A", + .drv_name = "kbl_n88l25_m98357a", + .fw_filename = "intel/dsp_fw_kbl.bin", + .machine_quirk = snd_soc_acpi_codec_list, + .quirk_data = &kbl_codecs, + .pdata = &skl_dmic_data, + }, + { + .id = "MX98927", + .drv_name = "kbl_r5514_5663_max", + .fw_filename = "intel/dsp_fw_kbl.bin", + .machine_quirk = snd_soc_acpi_codec_list, + .quirk_data = &kbl_5663_5514_codecs, + .pdata = &skl_dmic_data, + }, + { + .id = "MX98927", + .drv_name = "kbl_rt5663_m98927", + .fw_filename = "intel/dsp_fw_kbl.bin", + .machine_quirk = snd_soc_acpi_codec_list, + .quirk_data = &kbl_poppy_codecs, + .pdata = &skl_dmic_data, + }, + { + .id = "10EC5663", + .drv_name = "kbl_rt5663", + .fw_filename = "intel/dsp_fw_kbl.bin", + }, + { + .id = "DLGS7219", + .drv_name = "kbl_da7219_max98357a", + .fw_filename = "intel/dsp_fw_kbl.bin", + .machine_quirk = snd_soc_acpi_codec_list, + .quirk_data = &kbl_7219_98357_codecs, + .pdata = &skl_dmic_data, + }, + {}, +}; +EXPORT_SYMBOL_GPL(snd_soc_acpi_intel_kbl_machines); + +MODULE_LICENSE("GPL v2"); +MODULE_DESCRIPTION("Intel Common ACPI Match module"); diff --git a/sound/soc/intel/common/soc-acpi-intel-skl-match.c b/sound/soc/intel/common/soc-acpi-intel-skl-match.c new file mode 100644 index 00000000000000..0c9c0edd35b34d --- /dev/null +++ b/sound/soc/intel/common/soc-acpi-intel-skl-match.c @@ -0,0 +1,47 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * soc-apci-intel-skl-match.c - tables and support for SKL ACPI enumeration. + * + * Copyright (c) 2018, Intel Corporation. + * + */ + +#include +#include +#include "../skylake/skl.h" + +static struct skl_machine_pdata skl_dmic_data; + +static struct snd_soc_acpi_codecs skl_codecs = { + .num_codecs = 1, + .codecs = {"10508825"} +}; + +struct snd_soc_acpi_mach snd_soc_acpi_intel_skl_machines[] = { + { + .id = "INT343A", + .drv_name = "skl_alc286s_i2s", + .fw_filename = "intel/dsp_fw_release.bin", + }, + { + .id = "INT343B", + .drv_name = "skl_n88l25_s4567", + .fw_filename = "intel/dsp_fw_release.bin", + .machine_quirk = snd_soc_acpi_codec_list, + .quirk_data = &skl_codecs, + .pdata = &skl_dmic_data, + }, + { + .id = "MX98357A", + .drv_name = "skl_n88l25_m98357a", + .fw_filename = "intel/dsp_fw_release.bin", + .machine_quirk = snd_soc_acpi_codec_list, + .quirk_data = &skl_codecs, + .pdata = &skl_dmic_data, + }, + {}, +}; +EXPORT_SYMBOL_GPL(snd_soc_acpi_intel_skl_machines); + +MODULE_LICENSE("GPL v2"); +MODULE_DESCRIPTION("Intel Common ACPI Match module"); diff --git a/sound/soc/intel/skylake/skl.c b/sound/soc/intel/skylake/skl.c index f1692178666d3b..8def7cc58586e7 100644 --- a/sound/soc/intel/skylake/skl.c +++ b/sound/soc/intel/skylake/skl.c @@ -29,6 +29,7 @@ #include #include #include +#include #include #include #include @@ -36,8 +37,6 @@ #include "skl-sst-dsp.h" #include "skl-sst-ipc.h" -static struct skl_machine_pdata skl_dmic_data; - /* * initialize the PCI registers */ @@ -1019,172 +1018,23 @@ static void skl_remove(struct pci_dev *pci) dev_set_drvdata(&pci->dev, NULL); } -static struct snd_soc_acpi_codecs skl_codecs = { - .num_codecs = 1, - .codecs = {"10508825"} -}; - -static struct snd_soc_acpi_codecs kbl_codecs = { - .num_codecs = 1, - .codecs = {"10508825"} -}; - -static struct snd_soc_acpi_codecs bxt_codecs = { - .num_codecs = 1, - .codecs = {"MX98357A"} -}; - -static struct snd_soc_acpi_codecs kbl_poppy_codecs = { - .num_codecs = 1, - .codecs = {"10EC5663"} -}; - -static struct snd_soc_acpi_codecs kbl_5663_5514_codecs = { - .num_codecs = 2, - .codecs = {"10EC5663", "10EC5514"} -}; - -static struct snd_soc_acpi_codecs kbl_7219_98357_codecs = { - .num_codecs = 1, - .codecs = {"MX98357A"} -}; - -static struct skl_machine_pdata cnl_pdata = { - .use_tplg_pcm = true, -}; - -static struct snd_soc_acpi_mach sst_skl_devdata[] = { - { - .id = "INT343A", - .drv_name = "skl_alc286s_i2s", - .fw_filename = "intel/dsp_fw_release.bin", - }, - { - .id = "INT343B", - .drv_name = "skl_n88l25_s4567", - .fw_filename = "intel/dsp_fw_release.bin", - .machine_quirk = snd_soc_acpi_codec_list, - .quirk_data = &skl_codecs, - .pdata = &skl_dmic_data - }, - { - .id = "MX98357A", - .drv_name = "skl_n88l25_m98357a", - .fw_filename = "intel/dsp_fw_release.bin", - .machine_quirk = snd_soc_acpi_codec_list, - .quirk_data = &skl_codecs, - .pdata = &skl_dmic_data - }, - {} -}; - -static struct snd_soc_acpi_mach sst_bxtp_devdata[] = { - { - .id = "INT343A", - .drv_name = "bxt_alc298s_i2s", - .fw_filename = "intel/dsp_fw_bxtn.bin", - }, - { - .id = "DLGS7219", - .drv_name = "bxt_da7219_max98357a", - .fw_filename = "intel/dsp_fw_bxtn.bin", - .machine_quirk = snd_soc_acpi_codec_list, - .quirk_data = &bxt_codecs, - }, - {} -}; - -static struct snd_soc_acpi_mach sst_kbl_devdata[] = { - { - .id = "INT343A", - .drv_name = "kbl_alc286s_i2s", - .fw_filename = "intel/dsp_fw_kbl.bin", - }, - { - .id = "INT343B", - .drv_name = "kbl_n88l25_s4567", - .fw_filename = "intel/dsp_fw_kbl.bin", - .machine_quirk = snd_soc_acpi_codec_list, - .quirk_data = &kbl_codecs, - .pdata = &skl_dmic_data - }, - { - .id = "MX98357A", - .drv_name = "kbl_n88l25_m98357a", - .fw_filename = "intel/dsp_fw_kbl.bin", - .machine_quirk = snd_soc_acpi_codec_list, - .quirk_data = &kbl_codecs, - .pdata = &skl_dmic_data - }, - { - .id = "MX98927", - .drv_name = "kbl_r5514_5663_max", - .fw_filename = "intel/dsp_fw_kbl.bin", - .machine_quirk = snd_soc_acpi_codec_list, - .quirk_data = &kbl_5663_5514_codecs, - .pdata = &skl_dmic_data - }, - { - .id = "MX98927", - .drv_name = "kbl_rt5663_m98927", - .fw_filename = "intel/dsp_fw_kbl.bin", - .machine_quirk = snd_soc_acpi_codec_list, - .quirk_data = &kbl_poppy_codecs, - .pdata = &skl_dmic_data - }, - { - .id = "10EC5663", - .drv_name = "kbl_rt5663", - .fw_filename = "intel/dsp_fw_kbl.bin", - }, - { - .id = "DLGS7219", - .drv_name = "kbl_da7219_max98357a", - .fw_filename = "intel/dsp_fw_kbl.bin", - .machine_quirk = snd_soc_acpi_codec_list, - .quirk_data = &kbl_7219_98357_codecs, - .pdata = &skl_dmic_data - }, - - {} -}; - -static struct snd_soc_acpi_mach sst_glk_devdata[] = { - { - .id = "INT343A", - .drv_name = "glk_alc298s_i2s", - .fw_filename = "intel/dsp_fw_glk.bin", - }, - {} -}; - -static const struct snd_soc_acpi_mach sst_cnl_devdata[] = { - { - .id = "INT34C2", - .drv_name = "cnl_rt274", - .fw_filename = "intel/dsp_fw_cnl.bin", - .pdata = &cnl_pdata, - }, - {} -}; - /* PCI IDs */ static const struct pci_device_id skl_ids[] = { /* Sunrise Point-LP */ { PCI_DEVICE(0x8086, 0x9d70), - .driver_data = (unsigned long)&sst_skl_devdata}, + .driver_data = (unsigned long)&snd_soc_acpi_intel_skl_machines}, /* BXT-P */ { PCI_DEVICE(0x8086, 0x5a98), - .driver_data = (unsigned long)&sst_bxtp_devdata}, + .driver_data = (unsigned long)&snd_soc_acpi_intel_bxt_machines}, /* KBL */ { PCI_DEVICE(0x8086, 0x9D71), - .driver_data = (unsigned long)&sst_kbl_devdata}, + .driver_data = (unsigned long)&snd_soc_acpi_intel_kbl_machines}, /* GLK */ { PCI_DEVICE(0x8086, 0x3198), - .driver_data = (unsigned long)&sst_glk_devdata}, + .driver_data = (unsigned long)&snd_soc_acpi_intel_glk_machines}, /* CNL */ { PCI_DEVICE(0x8086, 0x9dc8), - .driver_data = (unsigned long)&sst_cnl_devdata}, + .driver_data = (unsigned long)&snd_soc_acpi_intel_cnl_machines}, { 0, } }; MODULE_DEVICE_TABLE(pci, skl_ids); From 674ee8abd9bf7615dbc070f1923f5f5d534096c1 Mon Sep 17 00:00:00 2001 From: Naveen Manohar Date: Mon, 18 Jun 2018 13:29:38 -0500 Subject: [PATCH 016/107] ASoC: Intel: common: Add Geminilake Dialog+Maxim machine driver entry This patch adds da7219_max98357a machine driver entry into machine table Signed-off-by: Naveen Manohar Signed-off-by: Pierre-Louis Bossart Signed-off-by: Mark Brown (cherry picked from commit 65a33883c778befcb85ef45285763fd8ac1b2ba3) --- sound/soc/intel/common/soc-acpi-intel-glk-match.c | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/sound/soc/intel/common/soc-acpi-intel-glk-match.c b/sound/soc/intel/common/soc-acpi-intel-glk-match.c index dee09439e7cb16..5902aa1d0ee38f 100644 --- a/sound/soc/intel/common/soc-acpi-intel-glk-match.c +++ b/sound/soc/intel/common/soc-acpi-intel-glk-match.c @@ -9,12 +9,24 @@ #include #include +static struct snd_soc_acpi_codecs glk_codecs = { + .num_codecs = 1, + .codecs = {"MX98357A"} +}; + struct snd_soc_acpi_mach snd_soc_acpi_intel_glk_machines[] = { { .id = "INT343A", .drv_name = "glk_alc298s_i2s", .fw_filename = "intel/dsp_fw_glk.bin", }, + { + .id = "DLGS7219", + .drv_name = "glk_da7219_max98357a", + .fw_filename = "intel/dsp_fw_glk.bin", + .machine_quirk = snd_soc_acpi_codec_list, + .quirk_data = &glk_codecs, + }, {}, }; EXPORT_SYMBOL_GPL(snd_soc_acpi_intel_glk_machines); From 19d4dd5df40f2f76c2ec4b81930957159bda06f4 Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Mon, 18 Jun 2018 13:29:39 -0500 Subject: [PATCH 017/107] ASoC: Intel: common: add firmware/topology information for SOF No functionality change for Skylake driver, add relevant names needed by SOF for BXT/APL, GLK and CNL. Signed-off-by: Pierre-Louis Bossart Signed-off-by: Mark Brown (cherry picked from commit e6d298fd4a4454dd121343323e3f00a27f8819a4) --- sound/soc/intel/common/soc-acpi-intel-bxt-match.c | 3 +++ sound/soc/intel/common/soc-acpi-intel-cnl-match.c | 3 +++ sound/soc/intel/common/soc-acpi-intel-glk-match.c | 6 ++++++ 3 files changed, 12 insertions(+) diff --git a/sound/soc/intel/common/soc-acpi-intel-bxt-match.c b/sound/soc/intel/common/soc-acpi-intel-bxt-match.c index 569f1de97e82da..50869eddbb3b00 100644 --- a/sound/soc/intel/common/soc-acpi-intel-bxt-match.c +++ b/sound/soc/intel/common/soc-acpi-intel-bxt-match.c @@ -26,6 +26,9 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_bxt_machines[] = { .fw_filename = "intel/dsp_fw_bxtn.bin", .machine_quirk = snd_soc_acpi_codec_list, .quirk_data = &bxt_codecs, + .sof_fw_filename = "intel/sof-apl.ri", + .sof_tplg_filename = "intel/sof-apl-da7219.tplg", + .asoc_plat_name = "0000:00:0e.0", }, {}, }; diff --git a/sound/soc/intel/common/soc-acpi-intel-cnl-match.c b/sound/soc/intel/common/soc-acpi-intel-cnl-match.c index b83ee053d41767..ec8e28e7b937aa 100644 --- a/sound/soc/intel/common/soc-acpi-intel-cnl-match.c +++ b/sound/soc/intel/common/soc-acpi-intel-cnl-match.c @@ -20,6 +20,9 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_cnl_machines[] = { .drv_name = "cnl_rt274", .fw_filename = "intel/dsp_fw_cnl.bin", .pdata = &cnl_pdata, + .sof_fw_filename = "intel/sof-cnl.ri", + .sof_tplg_filename = "intel/sof-cnl-rt274.tplg", + .asoc_plat_name = "0000:00:1f.3", }, {}, }; diff --git a/sound/soc/intel/common/soc-acpi-intel-glk-match.c b/sound/soc/intel/common/soc-acpi-intel-glk-match.c index 5902aa1d0ee38f..305875af71caee 100644 --- a/sound/soc/intel/common/soc-acpi-intel-glk-match.c +++ b/sound/soc/intel/common/soc-acpi-intel-glk-match.c @@ -19,6 +19,9 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_glk_machines[] = { .id = "INT343A", .drv_name = "glk_alc298s_i2s", .fw_filename = "intel/dsp_fw_glk.bin", + .sof_fw_filename = "intel/sof-glk.ri", + .sof_tplg_filename = "intel/sof-glk-alc298.tplg", + .asoc_plat_name = "0000:00:0e.0", }, { .id = "DLGS7219", @@ -26,6 +29,9 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_glk_machines[] = { .fw_filename = "intel/dsp_fw_glk.bin", .machine_quirk = snd_soc_acpi_codec_list, .quirk_data = &glk_codecs, + .sof_fw_filename = "intel/sof-glk.ri", + .sof_tplg_filename = "intel/sof-glk-da7219.tplg", + .asoc_plat_name = "0000:00:0e.0", }, {}, }; From 82e7ce8b7e6b30bef30316be335524449cc25962 Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Mon, 18 Jun 2018 13:29:41 -0500 Subject: [PATCH 018/107] ASoC: Intel: common: add entries for SOF-based machine drivers While we are at it, add entries for machine drivers that are used on SOF-based platforms. The drivers will be submitted upstream after the core SOF patches, but there's no harm in adding these references now. Signed-off-by: Pierre-Louis Bossart Signed-off-by: Mark Brown (cherry picked from commit b45350135b9241b64cc91ccc8dddca2ee4dc25d7) --- .../intel/common/soc-acpi-intel-bxt-match.c | 21 +++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/sound/soc/intel/common/soc-acpi-intel-bxt-match.c b/sound/soc/intel/common/soc-acpi-intel-bxt-match.c index 50869eddbb3b00..f39386e540d322 100644 --- a/sound/soc/intel/common/soc-acpi-intel-bxt-match.c +++ b/sound/soc/intel/common/soc-acpi-intel-bxt-match.c @@ -30,6 +30,27 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_bxt_machines[] = { .sof_tplg_filename = "intel/sof-apl-da7219.tplg", .asoc_plat_name = "0000:00:0e.0", }, + { + .id = "104C5122", + .drv_name = "bxt-pcm512x", + .sof_fw_filename = "intel/sof-apl.ri", + .sof_tplg_filename = "intel/sof-apl-pcm512x.tplg", + .asoc_plat_name = "0000:00:0e.0", + }, + { + .id = "1AEC8804", + .drv_name = "bxt-wm8804", + .sof_fw_filename = "intel/sof-apl.ri", + .sof_tplg_filename = "intel/sof-apl-wm8804.tplg", + .asoc_plat_name = "0000:00:0e.0", + }, + { + .id = "INT34C3", + .drv_name = "bxt_tdf8532", + .sof_fw_filename = "intel/sof-apl.ri", + .sof_tplg_filename = "intel/sof-apl-tdf8532.tplg", + .asoc_plat_name = "0000:00:0e.0", + }, {}, }; EXPORT_SYMBOL_GPL(snd_soc_acpi_intel_bxt_machines); From c94ec7c2daa5fa0e6c37224a13b03632a010f7e7 Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Mon, 18 Jun 2018 13:29:42 -0500 Subject: [PATCH 019/107] ASoC: Intel: common: fix copy/paste issue with SOF/broadwell topology file There are two commercially-available Broadwell platforms based on I2S (Dell XPS13 and 'Samus' Pixel 2015 Chromebook). Fix a copy/paste issue to allow each platform to enable different features if needed when SOF is enabled Signed-off-by: Pierre-Louis Bossart Signed-off-by: Mark Brown (cherry picked from commit f0d9034b290d8bad590e843c2a1081eb47d813ad) --- sound/soc/intel/common/soc-acpi-intel-hsw-bdw-match.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sound/soc/intel/common/soc-acpi-intel-hsw-bdw-match.c b/sound/soc/intel/common/soc-acpi-intel-hsw-bdw-match.c index e0e8c8c27528cb..53e7acdfbb81ac 100644 --- a/sound/soc/intel/common/soc-acpi-intel-hsw-bdw-match.c +++ b/sound/soc/intel/common/soc-acpi-intel-hsw-bdw-match.c @@ -45,7 +45,7 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_broadwell_machines[] = { .drv_name = "bdw-rt5677", .fw_filename = "intel/IntcSST2.bin", .sof_fw_filename = "intel/reef-bdw.ri", - .sof_tplg_filename = "intel/reef-bdw-rt286.tplg", + .sof_tplg_filename = "intel/reef-bdw-rt5677.tplg", .asoc_plat_name = "haswell-pcm-audio", }, { From 453d69143b3cf295b23b99414aa2bbcd9bb9fa8c Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Mon, 12 Mar 2018 15:35:11 -0500 Subject: [PATCH 020/107] ASoC: Intel: common: rename 'reef' to 'sof' in ACPI matching table Align with firmware tools, no functionality change Signed-off-by: Pierre-Louis Bossart --- .../intel/common/soc-acpi-intel-byt-match.c | 40 +++++++------- .../intel/common/soc-acpi-intel-cht-match.c | 52 +++++++++---------- .../common/soc-acpi-intel-hsw-bdw-match.c | 16 +++--- 3 files changed, 54 insertions(+), 54 deletions(-) diff --git a/sound/soc/intel/common/soc-acpi-intel-byt-match.c b/sound/soc/intel/common/soc-acpi-intel-byt-match.c index bfe1ca68a54234..4daa8a4f0c0c69 100644 --- a/sound/soc/intel/common/soc-acpi-intel-byt-match.c +++ b/sound/soc/intel/common/soc-acpi-intel-byt-match.c @@ -59,8 +59,8 @@ static struct snd_soc_acpi_mach byt_thinkpad_10 = { .drv_name = "cht-bsw-rt5672", .fw_filename = "intel/fw_sst_0f28.bin", .board = "cht-bsw", - .sof_fw_filename = "intel/reef-byt.ri", - .sof_tplg_filename = "intel/reef-byt-rt5670.tplg", + .sof_fw_filename = "intel/sof-byt.ri", + .sof_tplg_filename = "intel/sof-byt-rt5670.tplg", .asoc_plat_name = "sst-mfld-platform", }; @@ -98,8 +98,8 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_baytrail_machines[] = { .fw_filename = "intel/fw_sst_0f28.bin", .board = "bytcr_rt5640", .machine_quirk = byt_quirk, - .sof_fw_filename = "intel/reef-byt.ri", - .sof_tplg_filename = "intel/reef-byt-rt5640.tplg", + .sof_fw_filename = "intel/sof-byt.ri", + .sof_tplg_filename = "intel/sof-byt-rt5640.tplg", .asoc_plat_name = "sst-mfld-platform", }, { @@ -107,8 +107,8 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_baytrail_machines[] = { .drv_name = "bytcr_rt5640", .fw_filename = "intel/fw_sst_0f28.bin", .board = "bytcr_rt5640", - .sof_fw_filename = "intel/reef-byt.ri", - .sof_tplg_filename = "intel/reef-byt-rt5640.tplg", + .sof_fw_filename = "intel/sof-byt.ri", + .sof_tplg_filename = "intel/sof-byt-rt5640.tplg", .asoc_plat_name = "sst-mfld-platform", }, { @@ -116,8 +116,8 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_baytrail_machines[] = { .drv_name = "bytcr_rt5640", .fw_filename = "intel/fw_sst_0f28.bin", .board = "bytcr_rt5640", - .sof_fw_filename = "intel/reef-byt.ri", - .sof_tplg_filename = "intel/reef-byt-rt5640.tplg", + .sof_fw_filename = "intel/sof-byt.ri", + .sof_tplg_filename = "intel/sof-byt-rt5640.tplg", .asoc_plat_name = "sst-mfld-platform", }, { @@ -125,8 +125,8 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_baytrail_machines[] = { .drv_name = "bytcr_rt5651", .fw_filename = "intel/fw_sst_0f28.bin", .board = "bytcr_rt5651", - .sof_fw_filename = "intel/reef-byt.ri", - .sof_tplg_filename = "intel/reef-byt-rt5651.tplg", + .sof_fw_filename = "intel/sof-byt.ri", + .sof_tplg_filename = "intel/sof-byt-rt5651.tplg", .asoc_plat_name = "sst-mfld-platform", }, { @@ -134,8 +134,8 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_baytrail_machines[] = { .drv_name = "bytcht_da7213", .fw_filename = "intel/fw_sst_0f28.bin", .board = "bytcht_da7213", - .sof_fw_filename = "intel/reef-byt.ri", - .sof_tplg_filename = "intel/reef-byt-da7213.tplg", + .sof_fw_filename = "intel/sof-byt.ri", + .sof_tplg_filename = "intel/sof-byt-da7213.tplg", .asoc_plat_name = "sst-mfld-platform", }, { @@ -143,8 +143,8 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_baytrail_machines[] = { .drv_name = "bytcht_da7213", .fw_filename = "intel/fw_sst_0f28.bin", .board = "bytcht_da7213", - .sof_fw_filename = "intel/reef-byt.ri", - .sof_tplg_filename = "intel/reef-byt-da7213.tplg", + .sof_fw_filename = "intel/sof-byt.ri", + .sof_tplg_filename = "intel/sof-byt-da7213.tplg", .asoc_plat_name = "sst-mfld-platform", }, /* some Baytrail platforms rely on RT5645, use CHT machine driver */ @@ -153,8 +153,8 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_baytrail_machines[] = { .drv_name = "cht-bsw-rt5645", .fw_filename = "intel/fw_sst_0f28.bin", .board = "cht-bsw", - .sof_fw_filename = "intel/reef-byt.ri", - .sof_tplg_filename = "intel/reef-byt-rt5645.tplg", + .sof_fw_filename = "intel/sof-byt.ri", + .sof_tplg_filename = "intel/sof-byt-rt5645.tplg", .asoc_plat_name = "sst-mfld-platform", }, { @@ -162,8 +162,8 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_baytrail_machines[] = { .drv_name = "cht-bsw-rt5645", .fw_filename = "intel/fw_sst_0f28.bin", .board = "cht-bsw", - .sof_fw_filename = "intel/reef-byt.ri", - .sof_tplg_filename = "intel/reef-byt-rt5645.tplg", + .sof_fw_filename = "intel/sof-byt.ri", + .sof_tplg_filename = "intel/sof-byt-rt5645.tplg", .asoc_plat_name = "sst-mfld-platform", }, /* use CHT driver to Baytrail Chromebooks */ @@ -172,8 +172,8 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_baytrail_machines[] = { .drv_name = "cht-bsw-max98090", .fw_filename = "intel/fw_sst_0f28.bin", .board = "cht-bsw", - .sof_fw_filename = "intel/reef-byt.ri", - .sof_tplg_filename = "intel/reef-byt-max98090.tplg", + .sof_fw_filename = "intel/sof-byt.ri", + .sof_tplg_filename = "intel/sof-byt-max98090.tplg", .asoc_plat_name = "sst-mfld-platform", }, #if IS_ENABLED(CONFIG_SND_SOC_INTEL_BYT_CHT_NOCODEC_MACH) diff --git a/sound/soc/intel/common/soc-acpi-intel-cht-match.c b/sound/soc/intel/common/soc-acpi-intel-cht-match.c index ad1eb2d644beab..3c3f2f8585d719 100644 --- a/sound/soc/intel/common/soc-acpi-intel-cht-match.c +++ b/sound/soc/intel/common/soc-acpi-intel-cht-match.c @@ -44,8 +44,8 @@ static struct snd_soc_acpi_mach cht_surface_mach = { .drv_name = "cht-bsw-rt5645", .fw_filename = "intel/fw_sst_22a8.bin", .board = "cht-bsw", - .sof_fw_filename = "intel/reef-cht.ri", - .sof_tplg_filename = "intel/reef-cht-rt5645.tplg", + .sof_fw_filename = "intel/sof-cht.ri", + .sof_tplg_filename = "intel/sof-cht-rt5645.tplg", .asoc_plat_name = "sst-mfld-platform", }; @@ -68,8 +68,8 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_cherrytrail_machines[] = { .drv_name = "cht-bsw-rt5672", .fw_filename = "intel/fw_sst_22a8.bin", .board = "cht-bsw", - .sof_fw_filename = "intel/reef-cht.ri", - .sof_tplg_filename = "intel/reef-cht-rt5670.tplg", + .sof_fw_filename = "intel/sof-cht.ri", + .sof_tplg_filename = "intel/sof-cht-rt5670.tplg", .asoc_plat_name = "sst-mfld-platform", }, { @@ -77,8 +77,8 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_cherrytrail_machines[] = { .drv_name = "cht-bsw-rt5672", .fw_filename = "intel/fw_sst_22a8.bin", .board = "cht-bsw", - .sof_fw_filename = "intel/reef-cht.ri", - .sof_tplg_filename = "intel/reef-cht-rt5670.tplg", + .sof_fw_filename = "intel/sof-cht.ri", + .sof_tplg_filename = "intel/sof-cht-rt5670.tplg", .asoc_plat_name = "sst-mfld-platform", }, { @@ -86,8 +86,8 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_cherrytrail_machines[] = { .drv_name = "cht-bsw-rt5645", .fw_filename = "intel/fw_sst_22a8.bin", .board = "cht-bsw", - .sof_fw_filename = "intel/reef-cht.ri", - .sof_tplg_filename = "intel/reef-cht-rt5645.tplg", + .sof_fw_filename = "intel/sof-cht.ri", + .sof_tplg_filename = "intel/sof-cht-rt5645.tplg", .asoc_plat_name = "sst-mfld-platform", }, { @@ -95,8 +95,8 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_cherrytrail_machines[] = { .drv_name = "cht-bsw-rt5645", .fw_filename = "intel/fw_sst_22a8.bin", .board = "cht-bsw", - .sof_fw_filename = "intel/reef-cht.ri", - .sof_tplg_filename = "intel/reef-cht-rt5645.tplg", + .sof_fw_filename = "intel/sof-cht.ri", + .sof_tplg_filename = "intel/sof-cht-rt5645.tplg", .asoc_plat_name = "sst-mfld-platform", }, { @@ -104,8 +104,8 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_cherrytrail_machines[] = { .drv_name = "cht-bsw-rt5645", .fw_filename = "intel/fw_sst_22a8.bin", .board = "cht-bsw", - .sof_fw_filename = "intel/reef-cht.ri", - .sof_tplg_filename = "intel/reef-cht-rt5645.tplg", + .sof_fw_filename = "intel/sof-cht.ri", + .sof_tplg_filename = "intel/sof-cht-rt5645.tplg", .asoc_plat_name = "sst-mfld-platform", }, { @@ -113,8 +113,8 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_cherrytrail_machines[] = { .drv_name = "cht-bsw-max98090", .fw_filename = "intel/fw_sst_22a8.bin", .board = "cht-bsw", - .sof_fw_filename = "intel/reef-cht.ri", - .sof_tplg_filename = "intel/reef-cht-max98090.tplg", + .sof_fw_filename = "intel/sof-cht.ri", + .sof_tplg_filename = "intel/sof-cht-max98090.tplg", .asoc_plat_name = "sst-mfld-platform", }, { @@ -131,8 +131,8 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_cherrytrail_machines[] = { .drv_name = "bytcht_da7213", .fw_filename = "intel/fw_sst_22a8.bin", .board = "bytcht_da7213", - .sof_fw_filename = "intel/reef-cht.ri", - .sof_tplg_filename = "intel/reef-cht-da7213.tplg", + .sof_fw_filename = "intel/sof-cht.ri", + .sof_tplg_filename = "intel/sof-cht-da7213.tplg", .asoc_plat_name = "sst-mfld-platform", }, { @@ -140,8 +140,8 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_cherrytrail_machines[] = { .drv_name = "bytcht_da7213", .fw_filename = "intel/fw_sst_22a8.bin", .board = "bytcht_da7213", - .sof_fw_filename = "intel/reef-cht.ri", - .sof_tplg_filename = "intel/reef-cht-da7213.tplg", + .sof_fw_filename = "intel/sof-cht.ri", + .sof_tplg_filename = "intel/sof-cht-da7213.tplg", .asoc_plat_name = "sst-mfld-platform", }, { @@ -149,8 +149,8 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_cherrytrail_machines[] = { .drv_name = "bytcht_es8316", .fw_filename = "intel/fw_sst_22a8.bin", .board = "bytcht_es8316", - .sof_fw_filename = "intel/reef-cht.ri", - .sof_tplg_filename = "intel/reef-cht-es8316.tplg", + .sof_fw_filename = "intel/sof-cht.ri", + .sof_tplg_filename = "intel/sof-cht-es8316.tplg", .asoc_plat_name = "sst-mfld-platform", }, /* some CHT-T platforms rely on RT5640, use Baytrail machine driver */ @@ -160,8 +160,8 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_cherrytrail_machines[] = { .fw_filename = "intel/fw_sst_22a8.bin", .board = "bytcr_rt5640", .machine_quirk = cht_quirk, - .sof_fw_filename = "intel/reef-cht.ri", - .sof_tplg_filename = "intel/reef-cht-rt5640.tplg", + .sof_fw_filename = "intel/sof-cht.ri", + .sof_tplg_filename = "intel/sof-cht-rt5640.tplg", .asoc_plat_name = "sst-mfld-platform", }, { @@ -169,8 +169,8 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_cherrytrail_machines[] = { .drv_name = "bytcr_rt5640", .fw_filename = "intel/fw_sst_22a8.bin", .board = "bytcr_rt5640", - .sof_fw_filename = "intel/reef-cht.ri", - .sof_tplg_filename = "intel/reef-cht-rt5640.tplg", + .sof_fw_filename = "intel/sof-cht.ri", + .sof_tplg_filename = "intel/sof-cht-rt5640.tplg", .asoc_plat_name = "sst-mfld-platform", }, /* some CHT-T platforms rely on RT5651, use Baytrail machine driver */ @@ -179,8 +179,8 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_cherrytrail_machines[] = { .drv_name = "bytcr_rt5651", .fw_filename = "intel/fw_sst_22a8.bin", .board = "bytcr_rt5651", - .sof_fw_filename = "intel/reef-cht.ri", - .sof_tplg_filename = "intel/reef-cht-rt5651.tplg", + .sof_fw_filename = "intel/sof-cht.ri", + .sof_tplg_filename = "intel/sof-cht-rt5651.tplg", .asoc_plat_name = "sst-mfld-platform", }, #if IS_ENABLED(CONFIG_SND_SOC_INTEL_BYT_CHT_NOCODEC_MACH) diff --git a/sound/soc/intel/common/soc-acpi-intel-hsw-bdw-match.c b/sound/soc/intel/common/soc-acpi-intel-hsw-bdw-match.c index 53e7acdfbb81ac..494a0ea9b02903 100644 --- a/sound/soc/intel/common/soc-acpi-intel-hsw-bdw-match.c +++ b/sound/soc/intel/common/soc-acpi-intel-hsw-bdw-match.c @@ -23,8 +23,8 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_haswell_machines[] = { .id = "INT33CA", .drv_name = "haswell-audio", .fw_filename = "intel/IntcSST1.bin", - .sof_fw_filename = "intel/reef-hsw.ri", - .sof_tplg_filename = "intel/reef-hsw.tplg", + .sof_fw_filename = "intel/sof-hsw.ri", + .sof_tplg_filename = "intel/sof-hsw.tplg", .asoc_plat_name = "haswell-pcm-audio", }, {} @@ -36,24 +36,24 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_broadwell_machines[] = { .id = "INT343A", .drv_name = "broadwell-audio", .fw_filename = "intel/IntcSST2.bin", - .sof_fw_filename = "intel/reef-bdw.ri", - .sof_tplg_filename = "intel/reef-bdw-rt286.tplg", + .sof_fw_filename = "intel/sof-bdw.ri", + .sof_tplg_filename = "intel/sof-bdw-rt286.tplg", .asoc_plat_name = "haswell-pcm-audio", }, { .id = "RT5677CE", .drv_name = "bdw-rt5677", .fw_filename = "intel/IntcSST2.bin", - .sof_fw_filename = "intel/reef-bdw.ri", - .sof_tplg_filename = "intel/reef-bdw-rt5677.tplg", + .sof_fw_filename = "intel/sof-bdw.ri", + .sof_tplg_filename = "intel/sof-bdw-rt5677.tplg", .asoc_plat_name = "haswell-pcm-audio", }, { .id = "INT33CA", .drv_name = "haswell-audio", .fw_filename = "intel/IntcSST2.bin", - .sof_fw_filename = "intel/reef-bdw.ri", - .sof_tplg_filename = "intel/reef-bdw-rt5640.tplg", + .sof_fw_filename = "intel/sof-bdw.ri", + .sof_tplg_filename = "intel/sof-bdw-rt5640.tplg", .asoc_plat_name = "haswell-pcm-audio", }, {} From f4cd2bce71d35dfc9959f998047fa2bb3ea570ed Mon Sep 17 00:00:00 2001 From: Liam Girdwood Date: Mon, 8 Jan 2018 14:54:53 +0000 Subject: [PATCH 021/107] ASoC: core: Allow topology to override machine driver FE DAI link config. Machine drivers statically define a number of DAI links that currently cannot be changed or removed by topology. This means PCMs and platform components cannot be changed by topology at runtime AND machine drivers are tightly coupled to topology. This patch allows topology to override the machine driver DAI link config in order to reuse machine drivers with different topologies and platform components. The patch supports :- 1) create new FE PCMs with a topology defined PCM ID. 2) destroy existing static FE PCMs 3) change the platform component driver. 4) assign any new HW params fixups. The patch requires no changes to the machine drivers, but does add some platform component flags that the platform component driver can assign before loading topologies. Signed-off-by: Liam Girdwood --- include/sound/soc.h | 10 ++++++ sound/soc/soc-core.c | 79 ++++++++++++++++++++++++++++++++++++++++++-- sound/soc/soc-pcm.c | 12 +++++++ 3 files changed, 98 insertions(+), 3 deletions(-) diff --git a/include/sound/soc.h b/include/sound/soc.h index fc2e83e7caa24f..4da57378c3cb72 100644 --- a/include/sound/soc.h +++ b/include/sound/soc.h @@ -1017,6 +1017,13 @@ struct snd_soc_platform_driver { /* platform stream compress ops */ const struct snd_compr_ops *compr_ops; + + /* this platform uses topology and ignore machine driver FEs */ + const char *ignore_machine; + int (*be_hw_params_fixup)(struct snd_soc_pcm_runtime *rtd, + struct snd_pcm_hw_params *params); + bool use_dai_pcm_id; /* use the DAI link PCM ID as PCM device number */ + int be_pcm_base; /* base device ID for all BE PCMs */ }; struct snd_soc_dai_link_component { @@ -1123,6 +1130,9 @@ struct snd_soc_dai_link { /* pmdown_time is ignored at stop */ unsigned int ignore_pmdown_time:1; + /* Do not create a PCM for this DAI link (Backend link) */ + unsigned int ignore:1; + struct list_head list; /* DAI link list of the soc card */ struct snd_soc_dobj dobj; /* For topology */ }; diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c index bf7ca32ab31fca..efbe4eea1e8a98 100644 --- a/sound/soc/soc-core.c +++ b/sound/soc/soc-core.c @@ -1050,6 +1050,9 @@ static int soc_bind_dai_link(struct snd_soc_card *card, const char *platform_name; int i; + if (dai_link->ignore) + return 0; + dev_dbg(card->dev, "ASoC: binding %s\n", dai_link->name); if (soc_is_dai_link_bound(card, dai_link)) { @@ -1672,7 +1675,7 @@ static int soc_probe_link_dais(struct snd_soc_card *card, { struct snd_soc_dai_link *dai_link = rtd->dai_link; struct snd_soc_dai *cpu_dai = rtd->cpu_dai; - int i, ret; + int i, ret, num; dev_dbg(card->dev, "ASoC: probe %s dai link %d late %d\n", card->name, rtd->num, order); @@ -1718,9 +1721,23 @@ static int soc_probe_link_dais(struct snd_soc_card *card, soc_dpcm_debugfs_add(rtd); #endif + /* + * most drivers will register their PCMs using DAI link ordering but + * topology based drivers can use the DAI link id field to set PCM + * device number and then use rtd + a base offset of the BEs. + */ + if (rtd->platform->driver->use_dai_pcm_id) { + if (rtd->dai_link->no_pcm) + num = rtd->platform->driver->be_pcm_base + rtd->num; + else + num = rtd->dai_link->id; + } else { + num = rtd->num; + } + if (cpu_dai->driver->compress_new) { /*create compress_device"*/ - ret = cpu_dai->driver->compress_new(rtd, rtd->num); + ret = cpu_dai->driver->compress_new(rtd, num); if (ret < 0) { dev_err(card->dev, "ASoC: can't create compress %s\n", dai_link->stream_name); @@ -1730,7 +1747,7 @@ static int soc_probe_link_dais(struct snd_soc_card *card, if (!dai_link->params) { /* create the pcm */ - ret = soc_new_pcm(rtd, rtd->num); + ret = soc_new_pcm(rtd, num); if (ret < 0) { dev_err(card->dev, "ASoC: can't create pcm %s :%d\n", dai_link->stream_name, ret); @@ -2076,6 +2093,59 @@ int snd_soc_set_dmi_name(struct snd_soc_card *card, const char *flavour) EXPORT_SYMBOL_GPL(snd_soc_set_dmi_name); #endif /* CONFIG_DMI */ +static void soc_check_tplg_fes(struct snd_soc_card *card) +{ + struct snd_soc_platform *platform; + struct snd_soc_dai_link *dai_link; + int i; + + list_for_each_entry(platform, &platform_list, list) { + + /* does this platform override FEs ? */ + if (!platform->driver->ignore_machine) + continue; + + /* for this machine ? */ + if (strcmp(platform->driver->ignore_machine, + card->dev->driver->name)) + continue; + + /* machine matches, so override the rtd data */ + for (i = 0; i < card->num_links; i++) { + + dai_link = &card->dai_link[i]; + + /* ignore this FE */ + if (dai_link->dynamic) { + dai_link->ignore = true; + continue; + } + + dev_info(card->dev, "info: override FE DAI link %s\n", + card->dai_link[i].name); + + /* override platform */ + dai_link->platform_name = platform->component.name; + dai_link->cpu_dai_name = platform->component.name; + + /* convert non BE into BE */ + dai_link->no_pcm = 1; + dai_link->dpcm_playback = 1; + dai_link->dpcm_capture = 1; + + /* override any BE fixups */ + dai_link->be_hw_params_fixup = + platform->driver->be_hw_params_fixup; + + /* most BE links don't set stream name, so set it to + * dai link name if it's NULL to help bind widgets. + */ + if (!dai_link->stream_name) + dai_link->stream_name = dai_link->name; + } + } +} + static int snd_soc_instantiate_card(struct snd_soc_card *card) { struct snd_soc_codec *codec; @@ -2086,6 +2156,9 @@ static int snd_soc_instantiate_card(struct snd_soc_card *card) mutex_lock(&client_mutex); mutex_lock_nested(&card->mutex, SND_SOC_CARD_CLASS_INIT); + /* check whether any platform is ignore machine FE and using topology */ + soc_check_tplg_fes(card); + /* bind DAIs */ for (i = 0; i < card->num_links; i++) { ret = soc_bind_dai_link(card, &card->dai_link[i]); diff --git a/sound/soc/soc-pcm.c b/sound/soc/soc-pcm.c index 68d9dc93009636..4ce489165a6d9b 100644 --- a/sound/soc/soc-pcm.c +++ b/sound/soc/soc-pcm.c @@ -909,8 +909,20 @@ int soc_dai_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params, struct snd_soc_dai *dai) { + struct snd_soc_pcm_runtime *rtd = substream->private_data; int ret; + /* perform any topology hw_params fixups before DAI */ + if (rtd->dai_link->be_hw_params_fixup) { + ret = rtd->dai_link->be_hw_params_fixup(rtd, params); + if (ret < 0) { + dev_err(rtd->dev, + "ASoC: hw_params topology fixup failed %d\n", + ret); + return ret; + } + } + if (dai->driver->ops->hw_params) { ret = dai->driver->ops->hw_params(substream, params, dai); if (ret < 0) { From ca845055f78216acad6400e0f5e30b6ab5e09c8e Mon Sep 17 00:00:00 2001 From: Liam Girdwood Date: Thu, 8 Mar 2018 18:23:38 -0600 Subject: [PATCH 022/107] [SQUASHME?] ASoC: core: Add name prefix for machines with topology rewrites Signed-off-by: Liam Girdwood --- include/sound/soc.h | 2 ++ sound/soc/soc-core.c | 8 ++++++++ 2 files changed, 10 insertions(+) diff --git a/include/sound/soc.h b/include/sound/soc.h index 4da57378c3cb72..649dd7d3187581 100644 --- a/include/sound/soc.h +++ b/include/sound/soc.h @@ -1020,6 +1020,7 @@ struct snd_soc_platform_driver { /* this platform uses topology and ignore machine driver FEs */ const char *ignore_machine; + const char *topology_name_prefix; int (*be_hw_params_fixup)(struct snd_soc_pcm_runtime *rtd, struct snd_pcm_hw_params *params); bool use_dai_pcm_id; /* use the DAI link PCM ID as PCM device number */ @@ -1172,6 +1173,7 @@ struct snd_soc_card { const char *long_name; const char *driver_name; char dmi_longname[80]; + char topology_shortname[32]; struct device *dev; struct snd_card *snd_card; diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c index efbe4eea1e8a98..a0160a409e4094 100644 --- a/sound/soc/soc-core.c +++ b/sound/soc/soc-core.c @@ -2143,6 +2143,14 @@ static void soc_check_tplg_fes(struct snd_soc_card *card) if (!dai_link->stream_name) dai_link->stream_name = dai_link->name; } + + /* Inform userspace we are using alternate topology */ + if (platform->driver->topology_name_prefix) { + snprintf(card->topology_shortname, 32, "%s-%s", + platform->driver->topology_name_prefix, + card->name); + card->name = card->topology_shortname; + } } } From 51f05df2e5e9cf7832a22044f95dfd70cbef8c35 Mon Sep 17 00:00:00 2001 From: Liam Girdwood Date: Mon, 8 Jan 2018 20:15:35 +0000 Subject: [PATCH 023/107] ALSA: core: Allow drivers to set R/W wait time. Currently ALSA core blocks userspace for about 10 seconds for PCM R/W IO. This needs to be configurable for modern hardware like DSPs where no pointer update in milliseconds can indicate terminal DSP errors. Add a substream variable to set the wait time in ms. This allows userspace and drivers to recover more quickly from terminal DSP errors. Signed-off-by: Liam Girdwood --- include/sound/pcm.h | 6 ++++++ sound/core/pcm_lib.c | 15 ++++++++++----- 2 files changed, 16 insertions(+), 5 deletions(-) diff --git a/include/sound/pcm.h b/include/sound/pcm.h index e054c583d3b339..e4694684c524d1 100644 --- a/include/sound/pcm.h +++ b/include/sound/pcm.h @@ -462,6 +462,7 @@ struct snd_pcm_substream { /* -- timer section -- */ struct snd_timer *timer; /* timer */ unsigned timer_running: 1; /* time is running */ + unsigned wait_time; /* time in ms for R/W to wait for avail */ /* -- next substream -- */ struct snd_pcm_substream *next; /* -- linked substreams -- */ @@ -579,6 +580,11 @@ int snd_pcm_start(struct snd_pcm_substream *substream); int snd_pcm_stop(struct snd_pcm_substream *substream, snd_pcm_state_t status); int snd_pcm_drain_done(struct snd_pcm_substream *substream); int snd_pcm_stop_xrun(struct snd_pcm_substream *substream); +static inline void snd_pcm_wait_time(struct snd_pcm_substream *substream, + unsigned wait_time) +{ + substream->wait_time = wait_time; +} #ifdef CONFIG_PM int snd_pcm_suspend(struct snd_pcm_substream *substream); int snd_pcm_suspend_all(struct snd_pcm *pcm); diff --git a/sound/core/pcm_lib.c b/sound/core/pcm_lib.c index f4a19509cccf3c..6b8e48d48e111b 100644 --- a/sound/core/pcm_lib.c +++ b/sound/core/pcm_lib.c @@ -1835,12 +1835,17 @@ static int wait_for_avail(struct snd_pcm_substream *substream, if (runtime->no_period_wakeup) wait_time = MAX_SCHEDULE_TIMEOUT; else { - wait_time = 10; - if (runtime->rate) { - long t = runtime->period_size * 2 / runtime->rate; - wait_time = max(t, wait_time); + /* use wait time from substream if available */ + if (substream->wait_time) { + wait_time = msecs_to_jiffies(substream->wait_time); + } else { + wait_time = 10; + if (runtime->rate) { + long t = runtime->period_size * 2 / runtime->rate; + wait_time = max(t, wait_time); + } + wait_time = msecs_to_jiffies(wait_time * 1000); } - wait_time = msecs_to_jiffies(wait_time * 1000); } for (;;) { From d4da47509021f16d726dc6a5df21bc5f7912bc26 Mon Sep 17 00:00:00 2001 From: Liam Girdwood Date: Mon, 8 Jan 2018 20:30:59 +0000 Subject: [PATCH 024/107] ASoC: SOF: Add Sound Open Firmware driver core The Sound Open Firmware driver core is a generic architecture independent layer that allows SOF to be used on many different different architectures and platforms. It abstracts DSP operations and IO methods so that the target DSP can be an internal memory mapped or external SPI or I2C based device. This abstraction also allows SOF to be run on many different VMs on the same physical HW. SOF also requires some data in ASoC PCM runtime data for looking up SOF data during ASoC PCM operations. Signed-off-by: Liam Girdwood --- include/sound/soc.h | 1 + include/sound/sof.h | 76 +++++++ sound/soc/sof/core.c | 422 +++++++++++++++++++++++++++++++++++++ sound/soc/sof/sof-priv.h | 436 +++++++++++++++++++++++++++++++++++++++ 4 files changed, 935 insertions(+) create mode 100644 include/sound/sof.h create mode 100644 sound/soc/sof/core.c create mode 100644 sound/soc/sof/sof-priv.h diff --git a/include/sound/soc.h b/include/sound/soc.h index 649dd7d3187581..71a745fa8131d4 100644 --- a/include/sound/soc.h +++ b/include/sound/soc.h @@ -1293,6 +1293,7 @@ struct snd_soc_pcm_runtime { /* runtime devices */ struct snd_pcm *pcm; struct snd_compr *compr; + struct snd_sof_pcm *sof; struct snd_soc_codec *codec; struct snd_soc_platform *platform; /* will be removed */ struct snd_soc_dai *codec_dai; diff --git a/include/sound/sof.h b/include/sound/sof.h new file mode 100644 index 00000000000000..7767ec8ce188d6 --- /dev/null +++ b/include/sound/sof.h @@ -0,0 +1,76 @@ +/* SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause) */ +/* + * This file is provided under a dual BSD/GPLv2 license. When using or + * redistributing this file, you may do so under either license. + * + * Copyright(c) 2017 Intel Corporation. All rights reserved. + * + * Author: Liam Girdwood + */ + +#ifndef __INCLUDE_SOUND_SOF_H +#define __INCLUDE_SOUND_SOF_H + +#include +#include +#include +#include +#include +#include +#include +#include + +struct snd_sof_dsp_ops; + +/* + * SOF Platform data. + */ +struct snd_sof_pdata { + u32 id; /* PCI/ACPI ID */ + const struct firmware *fw; + const char *drv_name; + const char *name; + + /* parent devices */ + struct device *dev; + struct pci_dev *pci; + struct platform_device *pdev; + + /* descriptor */ + const struct sof_dev_desc *desc; + + /* machine */ + struct platform_device *pdev_mach; + const struct snd_soc_acpi_mach *machine; +}; + +/* + * Descriptor used for setting up SOF platform data. This is used when + * ACPI/PCI data is missing or mapped differently. + */ +struct sof_dev_desc { + /* list of machines using this configuration */ + struct snd_soc_acpi_mach *machines; + + /* Platform resource indexes in BAR / ACPI resources. */ + /* Must set to -1 if not used - add new items to end */ + int resindex_lpe_base; + int resindex_pcicfg_base; + int resindex_imr_base; + int irqindex_host_ipc; + int resindex_dma_base; + + /* DMA only valid when resindex_dma_base != -1*/ + int dma_engine; + int dma_size; + + /* IPC timeouts in ms */ + int ipc_timeout; + int boot_timeout; + + /* defaults for no codec mode */ + const char *nocodec_fw_filename; + const char *nocodec_tplg_filename; +}; + +#endif diff --git a/sound/soc/sof/core.c b/sound/soc/sof/core.c new file mode 100644 index 00000000000000..e15c393cd57476 --- /dev/null +++ b/sound/soc/sof/core.c @@ -0,0 +1,422 @@ +// SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause) +/* + * This file is provided under a dual BSD/GPLv2 license. When using or + * redistributing this file, you may do so under either license. + * + * Copyright(c) 2017 Intel Corporation. All rights reserved. + * + * Author: Liam Girdwood + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include "sof-priv.h" +#include "ops.h" + +#define TIMEOUT_IPC 5 +#define TIMEOUT_BOOT 100 + +struct snd_sof_pcm *snd_sof_find_spcm_dai(struct snd_sof_dev *sdev, + struct snd_soc_pcm_runtime *rtd) +{ + struct snd_sof_pcm *spcm = NULL; + + list_for_each_entry(spcm, &sdev->pcm_list, list) { + if (spcm->pcm.dai_id == rtd->dai_link->id) + return spcm; + } + + return NULL; +} + +struct snd_sof_pcm *snd_sof_find_spcm_name(struct snd_sof_dev *sdev, + char *name) +{ + struct snd_sof_pcm *spcm = NULL; + + list_for_each_entry(spcm, &sdev->pcm_list, list) { + if (strcmp(spcm->pcm.dai_name, name) == 0) + return spcm; + + if (strcmp(spcm->pcm.caps[0].name, name) == 0) + return spcm; + + if (strcmp(spcm->pcm.caps[1].name, name) == 0) + return spcm; + } + + return NULL; +} + +struct snd_sof_pcm *snd_sof_find_spcm_comp(struct snd_sof_dev *sdev, + unsigned int comp_id, + int *direction) +{ + struct snd_sof_pcm *spcm = NULL; + + list_for_each_entry(spcm, &sdev->pcm_list, list) { + if (spcm->stream[SNDRV_PCM_STREAM_PLAYBACK].comp_id == + comp_id) { + *direction = SNDRV_PCM_STREAM_PLAYBACK; + return spcm; + } + if (spcm->stream[SNDRV_PCM_STREAM_CAPTURE].comp_id == comp_id) { + *direction = SNDRV_PCM_STREAM_CAPTURE; + return spcm; + } + } + + return NULL; +} + +struct snd_sof_pcm *snd_sof_find_spcm_pcm_id(struct snd_sof_dev *sdev, + unsigned int pcm_id) +{ + struct snd_sof_pcm *spcm = NULL; + + list_for_each_entry(spcm, &sdev->pcm_list, list) { + if (spcm->pcm.pcm_id == pcm_id) + return spcm; + } + + return NULL; +} + +struct snd_sof_widget *snd_sof_find_swidget(struct snd_sof_dev *sdev, + char *name) +{ + struct snd_sof_widget *swidget = NULL; + + list_for_each_entry(swidget, &sdev->widget_list, list) { + if (strcmp(name, swidget->widget->name) == 0) + return swidget; + } + + return NULL; +} + +struct snd_sof_dai *snd_sof_find_dai(struct snd_sof_dev *sdev, + char *name) +{ + struct snd_sof_dai *dai = NULL; + + list_for_each_entry(dai, &sdev->dai_list, list) { + if (!dai->name) + continue; + + if (strcmp(name, dai->name) == 0) + return dai; + } + + return NULL; +} + +static inline unsigned int sof_get_pages(size_t size) +{ + return (size + PAGE_SIZE - 1) >> PAGE_SHIFT; +} + +struct sof_panic_msg { + u32 id; + const char *msg; +}; + +static const struct sof_panic_msg panic_msg[] = { + {SOF_IPC_PANIC_MEM, "out of memory"}, + {SOF_IPC_PANIC_WORK, "work subsystem init failed"}, + {SOF_IPC_PANIC_IPC, "IPC subsystem init failed"}, + {SOF_IPC_PANIC_ARCH, "arch init failed"}, + {SOF_IPC_PANIC_PLATFORM, "platform init failed"}, + {SOF_IPC_PANIC_TASK, "scheduler init failed"}, + {SOF_IPC_PANIC_EXCEPTION, "runtime exception"}, + {SOF_IPC_PANIC_DEADLOCK, "deadlock"}, + {SOF_IPC_PANIC_STACK, "stack overflow"}, + {SOF_IPC_PANIC_IDLE, "can't enter idle"}, +}; + +/* only need xtensa atm */ +static void sof_arch_dsp_oops(struct snd_sof_dev *sdev, void *oops) +{ + struct sof_ipc_dsp_oops_xtensa *xoops = oops; + + dev_err(sdev->dev, "error: DSP Firmware Oops\n"); + dev_err(sdev->dev, "EXCCAUSE 0x%8.8x EXCVADDR 0x%8.8x PS 0x%8.8x SAR 0x%8.8x\n", + xoops->exccause, xoops->excvaddr, xoops->ps, xoops->sar); + dev_err(sdev->dev, "EPC1 0x%8.8x EPC2 0x%8.8x EPC3 0x%8.8x EPC4 0x%8.8x", + xoops->epc1, xoops->epc2, xoops->epc3, xoops->epc4); + dev_err(sdev->dev, "EPC5 0x%8.8x EPC6 0x%8.8x EPC7 0x%8.8x DEPC 0x%8.8x", + xoops->epc5, xoops->epc6, xoops->epc7, xoops->depc); + dev_err(sdev->dev, "EPS2 0x%8.8x EPS3 0x%8.8x EPS4 0x%8.8x EPS5 0x%8.8x", + xoops->eps2, xoops->eps3, xoops->eps4, xoops->eps5); + dev_err(sdev->dev, "EPS6 0x%8.8x EPS7 0x%8.8x INTENABL 0x%8.8x INTERRU 0x%8.8x", + xoops->eps6, xoops->eps7, xoops->intenable, xoops->interrupt); +} + +static void sof_dsp_dump_stack(struct snd_sof_dev *sdev, void *oops, + u32 *stack, u32 stack_words) +{ + struct sof_ipc_dsp_oops_xtensa *xoops = oops; + u32 stack_ptr = xoops->stack; + int i; + + dev_err(sdev->dev, "stack dump from 0x%8.8x\n", stack_ptr); + + for (i = 0; i <= stack_words - 4; i += 4) { + dev_err(sdev->dev, "0x%8.8x: 0x%8.8x 0x%8.8x 0x%8.8x 0x%8.8x\n", + stack_ptr + i, stack[i], stack[i + 1], stack[i + 2], + stack[i + 3]); + } + + /* deal with any remaining words */ + switch (stack_words - i) { + case 0: + break; + case 1: + dev_err(sdev->dev, "0x%8.8x: 0x%8.8x\n", + stack_ptr + stack_words - 1, stack[stack_words - 1]); + break; + case 2: + dev_err(sdev->dev, "0x%8.8x: 0x%8.8x 0x%8.8x\n", + stack_ptr + stack_words - 2, stack[stack_words - 2], + stack[stack_words - 1]); + break; + case 3: + dev_err(sdev->dev, "0x%8.8x: 0x%8.8x 0x%8.8x 0x%8.8x\n", + stack_ptr + stack_words - 3, stack[stack_words - 3], + stack[stack_words - 2], stack[stack_words - 1]); + break; + default: + break; + } +} + +int snd_sof_get_status(struct snd_sof_dev *sdev, u32 panic_code, + u32 tracep_code, void *oops, void *stack, + size_t stack_words) +{ + u32 code; + int i; + + /* is firmware dead ? */ + if ((panic_code & SOF_IPC_PANIC_MAGIC_MASK) != SOF_IPC_PANIC_MAGIC) { + dev_err(sdev->dev, "error: unexpected fault 0x%8.8x trace 0x%8.8x\n", + panic_code, tracep_code); + return 0; /* no fault ? */ + } + + code = panic_code & + (SOF_IPC_PANIC_MAGIC_MASK | SOF_IPC_PANIC_CODE_MASK); + + for (i = 0; i < ARRAY_SIZE(panic_msg); i++) { + if (panic_msg[i].id == code) { + dev_err(sdev->dev, "error: %s\n", panic_msg[i].msg); + dev_err(sdev->dev, "error: trace point %8.8x\n", + tracep_code); + goto out; + } + } + + /* unknown error */ + dev_err(sdev->dev, "error: unknown reason %8.8x\n", panic_code); + dev_err(sdev->dev, "error: trace point %8.8x\n", tracep_code); + +out: + sof_arch_dsp_oops(sdev, oops); + sof_dsp_dump_stack(sdev, oops, stack, stack_words); + return -EFAULT; +} +EXPORT_SYMBOL(snd_sof_get_status); + +static int sof_probe(struct platform_device *pdev) +{ + struct snd_sof_pdata *plat_data = dev_get_platdata(&pdev->dev); + struct snd_sof_dev *sdev; + int ret; + + sdev = devm_kzalloc(&pdev->dev, sizeof(*sdev), GFP_KERNEL); + if (!sdev) + return -ENOMEM; + + dev_dbg(&pdev->dev, "probing SOF DSP device....\n"); + + /* initialize sof device */ + sdev->dev = &pdev->dev; + if (plat_data->pci) { + sdev->pci = plat_data->pci; + sdev->parent = &plat_data->pci->dev; + } else if (plat_data->pdev) { + sdev->parent = &plat_data->pdev->dev; + } else { + sdev->parent = plat_data->dev; + } + sdev->ops = plat_data->machine->pdata; + + sdev->pdata = plat_data; + INIT_LIST_HEAD(&sdev->pcm_list); + INIT_LIST_HEAD(&sdev->kcontrol_list); + INIT_LIST_HEAD(&sdev->widget_list); + INIT_LIST_HEAD(&sdev->dai_list); + dev_set_drvdata(&pdev->dev, sdev); + spin_lock_init(&sdev->ipc_lock); + spin_lock_init(&sdev->hw_lock); + + /* set up platform and component drivers */ + snd_sof_new_platform_drv(sdev); + snd_sof_new_dai_drv(sdev); + + /* set default timeouts if none provided */ + if (plat_data->desc->ipc_timeout == 0) + sdev->ipc_timeout = TIMEOUT_IPC; + else + sdev->ipc_timeout = plat_data->desc->ipc_timeout; + if (plat_data->desc->boot_timeout == 0) + sdev->boot_timeout = TIMEOUT_BOOT; + else + sdev->boot_timeout = plat_data->desc->boot_timeout; + + /* probe the DSP hardware */ + ret = snd_sof_probe(sdev); + if (ret < 0) { + dev_err(sdev->dev, "error: failed to probe DSP %d\n", ret); + return ret; + } + + /* register any debug/trace capabilities */ + ret = snd_sof_dbg_init(sdev); + if (ret < 0) { + dev_err(sdev->dev, "error: failed to init DSP trace/debug %d\n", + ret); + goto dbg_err; + } + + /* init the IPC */ + sdev->ipc = snd_sof_ipc_init(sdev); + if (!sdev->ipc) { + dev_err(sdev->dev, "error: failed to init DSP IPC %d\n", ret); + goto ipc_err; + } + + /* load the firmware */ + ret = snd_sof_load_firmware(sdev, plat_data->fw); + if (ret < 0) { + dev_err(sdev->dev, "error: failed to load DSP firmware %d\n", + ret); + goto fw_load_err; + } + + /* boot the firmware */ + ret = snd_sof_run_firmware(sdev); + if (ret < 0) { + dev_err(sdev->dev, "error: failed to boot DSP firmware %d\n", + ret); + goto fw_run_err; + } + + /* now register audio DSP platform driver */ + ret = snd_soc_register_platform(&pdev->dev, &sdev->plat_drv); + if (ret < 0) { + dev_err(sdev->dev, + "error: failed to register DSP platform driver %d\n", + ret); + goto fw_run_err; + } + + ret = snd_soc_register_component(&pdev->dev, sdev->cmpnt_drv, + &sdev->dai_drv, sdev->num_dai); + if (ret < 0) { + dev_err(sdev->dev, + "error: failed to register DSP DAI driver %d\n", ret); + goto comp_err; + } + + ret = snd_sof_init_trace(sdev); + if (ret < 0) { + dev_warn(sdev->dev, + "warning: failed to initialize trace %d\n", ret); + } + + return 0; + +comp_err: + snd_soc_unregister_component(&pdev->dev); + snd_sof_free_topology(sdev); +fw_run_err: + snd_sof_fw_unload(sdev); +fw_load_err: + snd_sof_ipc_free(sdev); +ipc_err: + snd_sof_free_debug(sdev); +dbg_err: + snd_sof_remove(sdev); + + return ret; +} + +static int sof_remove(struct platform_device *pdev) +{ + struct snd_sof_dev *sdev = dev_get_drvdata(&pdev->dev); + + snd_soc_unregister_platform(&pdev->dev); + snd_soc_unregister_component(&pdev->dev); + snd_sof_fw_unload(sdev); + snd_sof_ipc_free(sdev); + snd_sof_free_debug(sdev); + snd_sof_release_trace(sdev); + snd_sof_remove(sdev); + return 0; +} + +void snd_sof_shutdown(struct device *dev) +{ +} +EXPORT_SYMBOL(snd_sof_shutdown); + +int snd_sof_create_page_table(struct snd_sof_dev *sdev, + struct snd_dma_buffer *dmab, + unsigned char *page_table, size_t size) +{ + int i, pages; + + pages = sof_get_pages(size); + + dev_dbg(sdev->dev, "generating page table for %p size 0x%zx pages %d\n", + dmab->area, size, pages); + + for (i = 0; i < pages; i++) { + u32 idx = (((i << 2) + i)) >> 1; + u32 pfn = snd_sgbuf_get_addr(dmab, i * PAGE_SIZE) >> PAGE_SHIFT; + u32 *pg_table; + + dev_dbg(sdev->dev, "pfn i %i idx %d pfn %x\n", i, idx, pfn); + + pg_table = (u32 *)(page_table + idx); + + if (i & 1) + *pg_table |= (pfn << 4); + else + *pg_table |= pfn; + } + + return pages; +} + +static struct platform_driver sof_driver = { + .driver = { + .name = "sof-audio", + }, + + .probe = sof_probe, + .remove = sof_remove, +}; +module_platform_driver(sof_driver); + +MODULE_AUTHOR("Liam Girdwood"); +MODULE_DESCRIPTION("Sound Open Firmware (SOF) Core"); +MODULE_LICENSE("Dual BSD/GPL"); +MODULE_ALIAS("platform:sof-audio"); diff --git a/sound/soc/sof/sof-priv.h b/sound/soc/sof/sof-priv.h new file mode 100644 index 00000000000000..0f9b659461d94e --- /dev/null +++ b/sound/soc/sof/sof-priv.h @@ -0,0 +1,436 @@ +/* SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause) */ +/* + * This file is provided under a dual BSD/GPLv2 license. When using or + * redistributing this file, you may do so under either license. + * + * Copyright(c) 2017 Intel Corporation. All rights reserved. + * + * Author: Liam Girdwood + */ + +#ifndef __SOUND_SOC_SOF_PRIV_H +#define __SOUND_SOC_SOF_PRIV_H + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* debug flags */ +#define SOF_DBG_REGS BIT(1) +#define SOF_DBG_MBOX BIT(2) +#define SOF_DBG_TEXT BIT(3) +#define SOF_DBG_PCI BIT(4) + +/* max BARs mmaped devices can use */ +#define SND_SOF_BARS 8 + +/* time in ms for runtime suspend delay */ +#define SND_SOF_SUSPEND_DELAY 2000 + +/* DMA buffer size for trace */ +#define DMA_BUF_SIZE_FOR_TRACE (PAGE_SIZE * 16) + +/* max number of FE PCMs before BEs */ +#define SOF_BE_PCM_BASE 16 + +struct snd_sof_dev; +struct snd_sof_ipc_msg; +struct snd_sof_ipc; +struct snd_sof_debugfs_map; +struct snd_soc_tplg_ops; +struct snd_soc_component; + +struct snd_sof_dsp_ops { + /* probe and remove */ + int (*remove)(struct snd_sof_dev *sof_dev); + int (*probe)(struct snd_sof_dev *sof_dev); + + /* DSP core boot / reset */ + int (*run)(struct snd_sof_dev *sof_dev); + int (*stall)(struct snd_sof_dev *sof_dev); + int (*reset)(struct snd_sof_dev *sof_dev); + + /* DSP PM */ + int (*suspend)(struct snd_sof_dev *sof_dev, int state); + int (*resume)(struct snd_sof_dev *sof_dev); + + /* DSP clocking */ + int (*set_clk)(struct snd_sof_dev *sof_dev, u32 freq); + + /* Register IO */ + void (*write)(struct snd_sof_dev *sof_dev, void __iomem *addr, + u32 value); + u32 (*read)(struct snd_sof_dev *sof_dev, void __iomem *addr); + void (*write64)(struct snd_sof_dev *sof_dev, void __iomem *addr, + u64 value); + u64 (*read64)(struct snd_sof_dev *sof_dev, void __iomem *addr); + + /* memcpy IO */ + void (*block_read)(struct snd_sof_dev *sof_dev, + u32 offset, void *dest, size_t size); + void (*block_write)(struct snd_sof_dev *sof_dev, + u32 offset, void *src, size_t size); + + /* doorbell */ + irqreturn_t (*irq_handler)(int irq, void *context); + irqreturn_t (*irq_thread)(int irq, void *context); + + /* mailbox */ + void (*mailbox_read)(struct snd_sof_dev *sof_dev, u32 offset, + void __iomem *addr, size_t bytes); + void (*mailbox_write)(struct snd_sof_dev *sof_dev, u32 offset, + void __iomem *addr, size_t bytes); + + /* ipc */ + int (*send_msg)(struct snd_sof_dev *sof_dev, + struct snd_sof_ipc_msg *msg); + int (*get_reply)(struct snd_sof_dev *sof_dev, + struct snd_sof_ipc_msg *msg); + int (*is_ready)(struct snd_sof_dev *sof_dev); + int (*cmd_done)(struct snd_sof_dev *sof_dev); + + /* debug */ + const struct snd_sof_debugfs_map *debug_map; + int debug_map_count; + void (*dbg_dump)(struct snd_sof_dev *sof_dev, u32 flags); + + /* connect pcm substream to a host stream */ + int (*host_stream_open)(struct snd_sof_dev *sdev, + struct snd_pcm_substream *substream); + /* disconnect pcm substream to a host stream */ + int (*host_stream_close)(struct snd_sof_dev *sdev, + struct snd_pcm_substream *substream); + + /* host stream hw params */ + int (*host_stream_hw_params)(struct snd_sof_dev *sdev, + struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params); + + /* host stream trigger */ + int (*host_stream_trigger)(struct snd_sof_dev *sdev, + struct snd_pcm_substream *substream, + int cmd); + + /* FW loading */ + int (*load_firmware)(struct snd_sof_dev *sof_dev, + const struct firmware *fw); + int (*load_module)(struct snd_sof_dev *sof_dev, + struct snd_sof_mod_hdr *hdr); + int (*fw_ready)(struct snd_sof_dev *sdev, u32 msg_id); + + /* host DMA trace initialization */ + int (*trace_init)(struct snd_sof_dev *sdev, u32 *stream_tag); + int (*trace_release)(struct snd_sof_dev *sdev); + int (*trace_trigger)(struct snd_sof_dev *sdev, int cmd); +}; + +struct snd_sof_pdata; + +struct sof_ops_table { + const struct sof_dev_desc *desc; + struct snd_sof_dsp_ops *ops; + struct platform_device *(*new_data)(struct snd_sof_pdata *pdata); +}; + +struct snd_sof_dfsentry { + struct dentry *dfsentry; + size_t size; + void *buf; + struct snd_sof_dev *sdev; +}; + +struct snd_sof_debugfs_map { + const char *name; + u32 bar; + u32 offset; + u32 size; +}; + +struct snd_sof_mailbox { + u32 offset; + size_t size; +}; + +struct snd_sof_pcm_stream { + u32 comp_id; + struct snd_dma_buffer page_table; + struct sof_ipc_stream_posn posn; + struct snd_pcm_substream *substream; +}; + +struct snd_sof_pcm { + struct snd_sof_dev *sdev; + struct snd_soc_tplg_pcm pcm; + struct snd_sof_pcm_stream stream[2]; + u32 posn_offset[2]; + struct mutex mutex; + struct list_head list; /* list in sdev pcm list */ +}; + +struct snd_sof_control { + struct snd_sof_dev *sdev; + int comp_id; + int num_channels; + u32 readback_offset; /* offset to mmaped data if used */ + struct sof_ipc_ctrl_data *control_data; + u32 size; /* cdata size */ + enum sof_ipc_ctrl_cmd cmd; + + struct mutex mutex; + struct list_head list; /* list in sdev control list */ +}; + +struct snd_sof_widget { + struct snd_sof_dev *sdev; + int comp_id; + int pipeline_id; + int complete; + int id; + + struct snd_soc_dapm_widget *widget; + struct mutex mutex; + struct list_head list; /* list in sdev widget list */ + + void *private; /* core does not touch this */ +}; + +struct snd_sof_dai { + struct snd_sof_dev *sdev; + const char *name; + + struct sof_ipc_comp_dai comp_dai; + struct sof_ipc_dai_config dai_config; + struct list_head list; /* list in sdev dai list */ +}; + +struct snd_sof_ipc_msg { + struct list_head list; + + /* message data */ + u32 header; + void *msg_data; + void *reply_data; + size_t msg_size; + size_t reply_size; + + wait_queue_head_t waitq; + bool complete; +}; + +struct sof_intel_hda_dev; + +/* + * SOF Device Level. + */ +struct snd_sof_dev { + struct device *dev; + struct device *parent; + spinlock_t ipc_lock; /* lock for IPC users */ + spinlock_t hw_lock; /* lock for HW IO access */ + struct pci_dev *pci; + + /* ASoC components */ + struct snd_soc_platform_driver plat_drv; + const struct snd_soc_component_driver *cmpnt_drv; + struct snd_soc_dai_driver dai_drv; + int num_dai; + + /* DSP firmware boot */ + wait_queue_head_t boot_wait; + bool boot_complete; + + /* DSP HW differentiation */ + struct snd_sof_pdata *pdata; + const struct snd_sof_dsp_ops *ops; + struct sof_intel_hda_dev *hda; /* for HDA based DSP HW */ + + /* IPC */ + struct snd_sof_ipc *ipc; + struct snd_sof_mailbox dsp_box; /* DSP initiated IPC */ + struct snd_sof_mailbox host_box; /* Host initiated IPC */ + struct snd_sof_mailbox stream_box; /* Stream position update */ + u64 irq_status; + int ipc_irq; + u32 next_comp_id; /* monotonic - reset during S3 */ + + /* memory bases for mmaped DSPs - set by dsp_init() */ + void __iomem *bar[SND_SOF_BARS]; /* DSP base address */ + int mmio_bar; + int mailbox_bar; + size_t dsp_oops_offset; + + /* debug */ + struct dentry *debugfs_root; + + /* firmware loader */ + int cl_bar; + struct snd_dma_buffer dmab; + struct sof_ipc_fw_ready fw_ready; + + /* topology */ + struct snd_soc_tplg_ops *tplg_ops; + struct list_head pcm_list; + struct list_head kcontrol_list; + struct list_head widget_list; + struct list_head dai_list; + struct snd_soc_component *component; + + /* FW configuration */ + struct sof_ipc_dma_buffer_data *info_buffer; + struct sof_ipc_window *info_window; + + /* IPC timeouts in ms */ + int ipc_timeout; + int boot_timeout; + + /* Wait queue for code loading */ + wait_queue_head_t waitq; + int code_loading; + + /* DMA for Trace */ + struct snd_dma_buffer dmatb; + struct snd_dma_buffer dmatp; + int dma_trace_pages; + wait_queue_head_t trace_sleep; + u32 host_offset; + bool dtrace_is_enabled; + + void *private; /* core does not touch this */ +}; + +/* + * Device Level. + */ +void snd_sof_shutdown(struct device *dev); +int snd_sof_runtime_suspend(struct device *dev); +int snd_sof_runtime_resume(struct device *dev); +int snd_sof_resume(struct device *dev); +int snd_sof_suspend(struct device *dev); +int snd_sof_suspend_late(struct device *dev); + +void snd_sof_new_platform_drv(struct snd_sof_dev *sdev); +void snd_sof_new_dai_drv(struct snd_sof_dev *sdev); + +int snd_sof_create_page_table(struct snd_sof_dev *sdev, + struct snd_dma_buffer *dmab, + unsigned char *page_table, size_t size); + +/* + * Firmware loading. + */ +int snd_sof_load_firmware(struct snd_sof_dev *sdev, + const struct firmware *fw); +int snd_sof_load_firmware_memcpy(struct snd_sof_dev *sdev, + const struct firmware *fw); +int snd_sof_run_firmware(struct snd_sof_dev *sdev); +int snd_sof_parse_module_memcpy(struct snd_sof_dev *sdev, + struct snd_sof_mod_hdr *module); +void snd_sof_fw_unload(struct snd_sof_dev *sdev); +int snd_sof_fw_parse_ext_data(struct snd_sof_dev *sdev, u32 offset); + +/* + * IPC low level APIs. + */ +struct snd_sof_ipc *snd_sof_ipc_init(struct snd_sof_dev *sdev); +void snd_sof_ipc_free(struct snd_sof_dev *sdev); +void snd_sof_ipc_reply(struct snd_sof_dev *sdev, u32 msg_id); +void snd_sof_ipc_msgs_rx(struct snd_sof_dev *sdev); +void snd_sof_ipc_msgs_tx(struct snd_sof_dev *sdev); +int snd_sof_ipc_stream_pcm_params(struct snd_sof_dev *sdev, + struct sof_ipc_pcm_params *params); +int snd_sof_dsp_mailbox_init(struct snd_sof_dev *sdev, u32 dspbox, + size_t dspbox_size, u32 hostbox, + size_t hostbox_size); +int sof_ipc_tx_message(struct snd_sof_ipc *ipc, u32 header, void *tx_data, + size_t tx_bytes, void *rx_data, size_t rx_bytes); +struct snd_sof_widget *snd_sof_find_swidget(struct snd_sof_dev *sdev, + char *name); +struct snd_sof_dai *snd_sof_find_dai(struct snd_sof_dev *sdev, + char *name); +struct snd_sof_pcm *snd_sof_find_spcm_dai(struct snd_sof_dev *sdev, + struct snd_soc_pcm_runtime *rtd); +struct snd_sof_pcm *snd_sof_find_spcm_name(struct snd_sof_dev *sdev, + char *name); +struct snd_sof_pcm *snd_sof_find_spcm_comp(struct snd_sof_dev *sdev, + unsigned int comp_id, + int *direction); +struct snd_sof_pcm *snd_sof_find_spcm_pcm_id(struct snd_sof_dev *sdev, + unsigned int pcm_id); + +/* + * Stream IPC + */ +int snd_sof_ipc_stream_posn(struct snd_sof_dev *sdev, + struct snd_sof_pcm *spcm, int direction, + struct sof_ipc_stream_posn *posn); + +/* + * Mixer IPC + */ +int snd_sof_ipc_set_comp_data(struct snd_sof_ipc *ipc, + struct snd_sof_control *scontrol, u32 ipc_cmd, + enum sof_ipc_ctrl_type ctrl_type, + enum sof_ipc_ctrl_cmd ctrl_cmd); +int snd_sof_ipc_get_comp_data(struct snd_sof_ipc *ipc, + struct snd_sof_control *scontrol, u32 ipc_cmd, + enum sof_ipc_ctrl_type ctrl_type, + enum sof_ipc_ctrl_cmd ctrl_cmd); + +/* + * Topology. + */ +int snd_sof_init_topology(struct snd_sof_dev *sdev, + struct snd_soc_tplg_ops *ops); +int snd_sof_load_topology(struct snd_sof_dev *sdev, const char *file); +void snd_sof_free_topology(struct snd_sof_dev *sdev); + +/* + * Trace/debug + */ +int snd_sof_init_trace(struct snd_sof_dev *sdev); +void snd_sof_release_trace(struct snd_sof_dev *sdev); +int snd_sof_dbg_init(struct snd_sof_dev *sdev); +void snd_sof_free_debug(struct snd_sof_dev *sdev); +int snd_sof_debugfs_create_item(struct snd_sof_dev *sdev, + void __iomem *base, size_t size, + const char *name); +int snd_sof_trace_update_pos(struct snd_sof_dev *sdev, + struct sof_ipc_dma_trace_posn *posn); +void snd_sof_trace_notify_for_error(struct snd_sof_dev *sdev); +int snd_sof_get_status(struct snd_sof_dev *sdev, u32 panic_code, + u32 tracep_code, void *oops, void *stack, + size_t stack_size); + +/* + * Platform specific ops. + */ +extern struct snd_compr_ops sof_compressed_ops; + +/* + * Kcontrols. + */ + +int snd_sof_volume_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol); +int snd_sof_volume_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol); +int snd_sof_enum_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol); +int snd_sof_enum_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol); +int snd_sof_bytes_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol); +int snd_sof_bytes_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol); + +#endif From d2ee6b199d6a5fdcba4e63ae4539162bac4bcf4a Mon Sep 17 00:00:00 2001 From: Liam Girdwood Date: Mon, 8 Jan 2018 20:31:31 +0000 Subject: [PATCH 025/107] ASoC: SOF: Add Sound Open Firmware KControl support SOF exposes regular ALSA Kcontrols that are defined by topology. This patch converts the Kcontrol IO to DSP IPC. Signed-off-by: Liam Girdwood --- sound/soc/sof/control.c | 197 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 197 insertions(+) create mode 100644 sound/soc/sof/control.c diff --git a/sound/soc/sof/control.c b/sound/soc/sof/control.c new file mode 100644 index 00000000000000..db09ba63154b69 --- /dev/null +++ b/sound/soc/sof/control.c @@ -0,0 +1,197 @@ +// SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause) +/* + * This file is provided under a dual BSD/GPLv2 license. When using or + * redistributing this file, you may do so under either license. + * + * Copyright(c) 2017 Intel Corporation. All rights reserved. + * + * Author: Liam Girdwood + */ + +/* Mixer Controls */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include "sof-priv.h" + +static inline u32 mixer_to_ipc(unsigned int value, u32 *volume_map, int size) +{ + if (value >= size) + return volume_map[size - 1]; + else + return volume_map[value]; +} + +static inline u32 ipc_to_mixer(u32 value, u32 *volume_map, int size) +{ + int i; + + for (i = 0; i < size; i++) { + if (volume_map[i] >= value) + return i; + } + + return i - 1; +} + +int snd_sof_volume_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct soc_mixer_control *sm = + (struct soc_mixer_control *)kcontrol->private_value; + struct snd_sof_control *scontrol = sm->dobj.private; + struct snd_sof_dev *sdev = scontrol->sdev; + struct sof_ipc_ctrl_data *cdata = scontrol->control_data; + unsigned int i, channels = scontrol->num_channels; + + pm_runtime_get_sync(sdev->dev); + + /* get all the mixer data from DSP */ + snd_sof_ipc_get_comp_data(sdev->ipc, scontrol, SOF_IPC_COMP_GET_VALUE, + SOF_CTRL_TYPE_VALUE_CHAN_GET, + SOF_CTRL_CMD_VOLUME); + + /* read back each channel */ + for (i = 0; i < channels; i++) + ucontrol->value.integer.value[i] = + ipc_to_mixer(cdata->chanv[i].value, + scontrol->volume_table, sm->max + 1); + + pm_runtime_mark_last_busy(sdev->dev); + pm_runtime_put_autosuspend(sdev->dev); + return 0; +} + +int snd_sof_volume_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct soc_mixer_control *sm = + (struct soc_mixer_control *)kcontrol->private_value; + struct snd_sof_control *scontrol = sm->dobj.private; + struct snd_sof_dev *sdev = scontrol->sdev; + struct sof_ipc_ctrl_data *cdata = scontrol->control_data; + unsigned int i, channels = scontrol->num_channels; + + pm_runtime_get_sync(sdev->dev); + + /* update each channel */ + for (i = 0; i < channels; i++) { + cdata->chanv[i].value = + mixer_to_ipc(ucontrol->value.integer.value[i], + scontrol->volume_table, sm->max + 1); + cdata->chanv[i].channel = i; + } + + /* notify DSP of mixer updates */ + snd_sof_ipc_set_comp_data(sdev->ipc, scontrol, SOF_IPC_COMP_SET_VALUE, + SOF_CTRL_TYPE_VALUE_CHAN_GET, + SOF_CTRL_CMD_VOLUME); + + pm_runtime_mark_last_busy(sdev->dev); + pm_runtime_put_autosuspend(sdev->dev); + return 0; +} + +int snd_sof_enum_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct soc_enum *se = + (struct soc_enum *)kcontrol->private_value; + struct snd_sof_control *scontrol = se->dobj.private; + struct snd_sof_dev *sdev = scontrol->sdev; + struct sof_ipc_ctrl_data *cdata = scontrol->control_data; + unsigned int i, channels = scontrol->num_channels; + + pm_runtime_get_sync(sdev->dev); + + /* get all the mixer data from DSP */ + snd_sof_ipc_get_comp_data(sdev->ipc, scontrol, SOF_IPC_COMP_GET_VALUE, + SOF_CTRL_TYPE_VALUE_CHAN_GET, + SOF_CTRL_CMD_ENUM); + + /* read back each channel */ + for (i = 0; i < channels; i++) + ucontrol->value.integer.value[i] = cdata->chanv[i].value; + + pm_runtime_mark_last_busy(sdev->dev); + pm_runtime_put_autosuspend(sdev->dev); + return 0; +} + +int snd_sof_enum_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct soc_enum *se = + (struct soc_enum *)kcontrol->private_value; + struct snd_sof_control *scontrol = se->dobj.private; + struct snd_sof_dev *sdev = scontrol->sdev; + struct sof_ipc_ctrl_data *cdata = scontrol->control_data; + unsigned int i, channels = scontrol->num_channels; + + pm_runtime_get_sync(sdev->dev); + + /* update each channel */ + for (i = 0; i < channels; i++) + cdata->chanv[i].value = ucontrol->value.integer.value[i]; + + /* notify DSP of mixer updates */ + snd_sof_ipc_set_comp_data(sdev->ipc, scontrol, SOF_IPC_COMP_SET_VALUE, + SOF_CTRL_TYPE_VALUE_CHAN_SET, + SOF_CTRL_CMD_ENUM); + + pm_runtime_mark_last_busy(sdev->dev); + pm_runtime_put_autosuspend(sdev->dev); + return 0; +} + +int snd_sof_bytes_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct soc_bytes_ext *be = + (struct soc_bytes_ext *)kcontrol->private_value; + struct snd_sof_control *scontrol = be->dobj.private; + struct snd_sof_dev *sdev = scontrol->sdev; + //struct sof_ipc_ctrl_data *cdata = scontrol->control_data; + //unsigned int i, channels = scontrol->num_channels; + + pm_runtime_get_sync(sdev->dev); + + /* get all the mixer data from DSP */ + snd_sof_ipc_get_comp_data(sdev->ipc, scontrol, SOF_IPC_COMP_GET_DATA, + SOF_CTRL_TYPE_DATA_GET, scontrol->cmd); + + /* TODO: copy back to userspace */ + + pm_runtime_mark_last_busy(sdev->dev); + pm_runtime_put_autosuspend(sdev->dev); + return 0; +} + +int snd_sof_bytes_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct soc_bytes_ext *be = + (struct soc_bytes_ext *)kcontrol->private_value; + struct snd_sof_control *scontrol = be->dobj.private; + struct snd_sof_dev *sdev = scontrol->sdev; + //struct sof_ipc_ctrl_data *cdata = scontrol->control_data; + //unsigned int i, channels = scontrol->num_channels; + + pm_runtime_get_sync(sdev->dev); + + /* TODO: copy from userspace */ + + /* notify DSP of mixer updates */ + snd_sof_ipc_set_comp_data(sdev->ipc, scontrol, SOF_IPC_COMP_SET_DATA, + SOF_CTRL_TYPE_DATA_SET, scontrol->cmd); + + pm_runtime_mark_last_busy(sdev->dev); + pm_runtime_put_autosuspend(sdev->dev); + return 0; +} From 4c649d0447a74d8ae4139f78054425a59874bca5 Mon Sep 17 00:00:00 2001 From: Liam Girdwood Date: Mon, 8 Jan 2018 20:31:44 +0000 Subject: [PATCH 026/107] ASoC: SOF: Add driver debug support. Add debugFS files that can be used to expose DSP memories and and peripherals to userspace to assist with firmware debugging. Signed-off-by: Liam Girdwood --- sound/soc/sof/debug.c | 137 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 137 insertions(+) create mode 100644 sound/soc/sof/debug.c diff --git a/sound/soc/sof/debug.c b/sound/soc/sof/debug.c new file mode 100644 index 00000000000000..c1580a17d2f2d2 --- /dev/null +++ b/sound/soc/sof/debug.c @@ -0,0 +1,137 @@ +// SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause) +/* + * This file is provided under a dual BSD/GPLv2 license. When using or + * redistributing this file, you may do so under either license. + * + * Copyright(c) 2017 Intel Corporation. All rights reserved. + * + * Author: Liam Girdwood + * Yan Wang + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "sof-priv.h" +#include "ops.h" + +static int sof_dfsentry_open(struct inode *inode, struct file *file) +{ + file->private_data = inode->i_private; + + return 0; +} + +static ssize_t sof_dfsentry_read(struct file *file, char __user *buffer, + size_t count, loff_t *ppos) +{ + struct snd_sof_dfsentry *dfse = file->private_data; + struct snd_sof_dev *sdev = dfse->sdev; + int size; + u32 *buf; + loff_t pos = *ppos; + size_t ret; + + size = dfse->size; + + if (pos < 0) + return -EINVAL; + if (pos >= size || !count) + return 0; + if (count > size - pos) + count = size - pos; + + size = (count + 3) & ~3; + buf = kzalloc(size, GFP_KERNEL); + if (!buf) + return -ENOMEM; + + pm_runtime_get(sdev->dev); + memcpy_fromio(buf, dfse->buf + pos, size); + pm_runtime_put(sdev->dev); + + ret = copy_to_user(buffer, buf, count); + kfree(buf); + + if (ret == count) + return -EFAULT; + count -= ret; + *ppos = pos + count; + + return count; +} + +static const struct file_operations sof_dfs_fops = { + .open = sof_dfsentry_open, + .read = sof_dfsentry_read, + .llseek = default_llseek, +}; + +int snd_sof_debugfs_create_item(struct snd_sof_dev *sdev, + void __iomem *base, size_t size, + const char *name) +{ + struct snd_sof_dfsentry *dfse; + + if (!sdev) + return -EINVAL; + + dfse = kzalloc(sizeof(*dfse), GFP_KERNEL); + if (!dfse) + return -ENOMEM; + + dfse->buf = base; + dfse->size = size; + dfse->sdev = sdev; + + dfse->dfsentry = debugfs_create_file(name, 0444, sdev->debugfs_root, + dfse, &sof_dfs_fops); + if (!dfse->dfsentry) { + dev_err(sdev->dev, "cannot create debugfs entry.\n"); + kfree(dfse); + return -ENODEV; + } + + return 0; +} +EXPORT_SYMBOL(snd_sof_debugfs_create_item); + +int snd_sof_dbg_init(struct snd_sof_dev *sdev) +{ + const struct snd_sof_dsp_ops *ops = sdev->ops; + const struct snd_sof_debugfs_map *map; + int err = 0, i; + + sdev->debugfs_root = debugfs_create_dir("sof", NULL); + if (IS_ERR_OR_NULL(sdev->debugfs_root)) { + dev_err(sdev->dev, "error: failed to create debugfs directory\n"); + return -EINVAL; + } + + for (i = 0; i < ops->debug_map_count; i++) { + map = &ops->debug_map[i]; + + err = snd_sof_debugfs_create_item(sdev, sdev->bar[map->bar] + + map->offset, map->size, + map->name); + if (err < 0) + dev_err(sdev->dev, "cannot create debugfs for %s\n", + map->name); + } + + return err; +} +EXPORT_SYMBOL(snd_sof_dbg_init); + +void snd_sof_free_debug(struct snd_sof_dev *sdev) +{ + debugfs_remove_recursive(sdev->debugfs_root); +} +EXPORT_SYMBOL(snd_sof_free_debug); From ce4ffebccad797d12e7171da1933a79ecd64b5df Mon Sep 17 00:00:00 2001 From: Liam Girdwood Date: Mon, 8 Jan 2018 20:32:12 +0000 Subject: [PATCH 027/107] ASoC: SOF: Add support for IPC IO between DSP and Host Define an IPC ABI for all host <--> DSP communication. This ABI should be transport agnostic. i.e. it should work on MMIO and SPI/I2C style interfaces. Signed-off-by: Liam Girdwood --- include/uapi/sound/sof-ipc.h | 860 +++++++++++++++++++++++++++++++++++ sound/soc/sof/ipc.c | 609 +++++++++++++++++++++++++ 2 files changed, 1469 insertions(+) create mode 100644 include/uapi/sound/sof-ipc.h create mode 100644 sound/soc/sof/ipc.c diff --git a/include/uapi/sound/sof-ipc.h b/include/uapi/sound/sof-ipc.h new file mode 100644 index 00000000000000..9b33691ac8a94b --- /dev/null +++ b/include/uapi/sound/sof-ipc.h @@ -0,0 +1,860 @@ +/* SPDX-License-Identifier: ((GPL-2.0 WITH Linux-syscall-note)) OR BSD-3-Clause) */ +/* + * This file is provided under a dual BSD/GPLv2 license. When using or + * redistributing this file, you may do so under either license. + * + * Copyright(c) 2017 Intel Corporation. All rights reserved. + * + * Author: Liam Girdwood + * Keyon Jie + */ + +#ifndef __INCLUDE_UAPI_SOF_IPC_H__ +#define __INCLUDE_UAPI_SOF_IPC_H__ + +#include + +/* + * IPC messages have a prefixed 32 bit identifier made up as follows :- + * + * 0xGCCCNNNN where + * G is global cmd type (4 bits) + * C is command type (12 bits) + * I is the ID number (16 bits) - monotonic and overflows + * + * This is sent at the start of the IPM message in the mailbox. Messages should + * not be sent in the doorbell (special exceptions for firmware . + */ + +/* Global Message - Generic */ +#define SOF_GLB_TYPE_SHIFT 28 +#define SOF_GLB_TYPE_MASK (0xf << SOF_GLB_TYPE_SHIFT) +#define SOF_GLB_TYPE(x) ((x) << SOF_GLB_TYPE_SHIFT) + +/* Command Message - Generic */ +#define SOF_CMD_TYPE_SHIFT 16 +#define SOF_CMD_TYPE_MASK (0xfff << SOF_CMD_TYPE_SHIFT) +#define SOF_CMD_TYPE(x) ((x) << SOF_CMD_TYPE_SHIFT) + +/* Global Message Types */ +#define SOF_IPC_GLB_REPLY SOF_GLB_TYPE(0x1U) +#define SOF_IPC_GLB_COMPOUND SOF_GLB_TYPE(0x2U) +#define SOF_IPC_GLB_TPLG_MSG SOF_GLB_TYPE(0x3U) +#define SOF_IPC_GLB_PM_MSG SOF_GLB_TYPE(0x4U) +#define SOF_IPC_GLB_COMP_MSG SOF_GLB_TYPE(0x5U) +#define SOF_IPC_GLB_STREAM_MSG SOF_GLB_TYPE(0x6U) +#define SOF_IPC_FW_READY SOF_GLB_TYPE(0x7U) +#define SOF_IPC_GLB_DAI_MSG SOF_GLB_TYPE(0x8U) +#define SOF_IPC_GLB_TRACE_MSG SOF_GLB_TYPE(0x9U) + +/* + * DSP Command Message Types + */ + +/* topology */ +#define SOF_IPC_TPLG_COMP_NEW SOF_CMD_TYPE(0x001) +#define SOF_IPC_TPLG_COMP_FREE SOF_CMD_TYPE(0x002) +#define SOF_IPC_TPLG_COMP_CONNECT SOF_CMD_TYPE(0x003) +#define SOF_IPC_TPLG_PIPE_NEW SOF_CMD_TYPE(0x010) +#define SOF_IPC_TPLG_PIPE_FREE SOF_CMD_TYPE(0x011) +#define SOF_IPC_TPLG_PIPE_CONNECT SOF_CMD_TYPE(0x012) +#define SOF_IPC_TPLG_PIPE_COMPLETE SOF_CMD_TYPE(0x013) +#define SOF_IPC_TPLG_BUFFER_NEW SOF_CMD_TYPE(0x020) +#define SOF_IPC_TPLG_BUFFER_FREE SOF_CMD_TYPE(0x021) + +/* PM */ +#define SOF_IPC_PM_CTX_SAVE SOF_CMD_TYPE(0x001) +#define SOF_IPC_PM_CTX_RESTORE SOF_CMD_TYPE(0x002) +#define SOF_IPC_PM_CTX_SIZE SOF_CMD_TYPE(0x003) +#define SOF_IPC_PM_CLK_SET SOF_CMD_TYPE(0x004) +#define SOF_IPC_PM_CLK_GET SOF_CMD_TYPE(0x005) +#define SOF_IPC_PM_CLK_REQ SOF_CMD_TYPE(0x006) + +/* component runtime config - multiple different types */ +#define SOF_IPC_COMP_SET_VALUE SOF_CMD_TYPE(0x001) +#define SOF_IPC_COMP_GET_VALUE SOF_CMD_TYPE(0x002) +#define SOF_IPC_COMP_SET_DATA SOF_CMD_TYPE(0x003) +#define SOF_IPC_COMP_GET_DATA SOF_CMD_TYPE(0x004) + +/* DAI messages */ +#define SOF_IPC_DAI_CONFIG SOF_CMD_TYPE(0x001) +#define SOF_IPC_DAI_LOOPBACK SOF_CMD_TYPE(0x002) + +/* stream */ +#define SOF_IPC_STREAM_PCM_PARAMS SOF_CMD_TYPE(0x001) +#define SOF_IPC_STREAM_PCM_PARAMS_REPLY SOF_CMD_TYPE(0x002) +#define SOF_IPC_STREAM_PCM_FREE SOF_CMD_TYPE(0x003) +#define SOF_IPC_STREAM_TRIG_START SOF_CMD_TYPE(0x004) +#define SOF_IPC_STREAM_TRIG_STOP SOF_CMD_TYPE(0x005) +#define SOF_IPC_STREAM_TRIG_PAUSE SOF_CMD_TYPE(0x006) +#define SOF_IPC_STREAM_TRIG_RELEASE SOF_CMD_TYPE(0x007) +#define SOF_IPC_STREAM_TRIG_DRAIN SOF_CMD_TYPE(0x008) +#define SOF_IPC_STREAM_TRIG_XRUN SOF_CMD_TYPE(0x009) +#define SOF_IPC_STREAM_POSITION SOF_CMD_TYPE(0x00a) +#define SOF_IPC_STREAM_VORBIS_PARAMS SOF_CMD_TYPE(0x010) +#define SOF_IPC_STREAM_VORBIS_FREE SOF_CMD_TYPE(0x011) + +/* trace and debug */ +#define SOF_IPC_TRACE_DMA_PARAMS SOF_CMD_TYPE(0x001) +#define SOF_IPC_TRACE_DMA_POSITION SOF_CMD_TYPE(0x002) + +/* Get message component id */ +#define SOF_IPC_MESSAGE_ID(x) ((x) & 0xffff) + +/* maximum message size for mailbox Tx/Rx */ +#define SOF_IPC_MSG_MAX_SIZE 128 + +/* + * SOF panic codes + */ +#define SOF_IPC_PANIC_MAGIC 0x0dead000 +#define SOF_IPC_PANIC_MAGIC_MASK 0x0ffff000 +#define SOF_IPC_PANIC_CODE_MASK 0x00000fff +#define SOF_IPC_PANIC_MEM (SOF_IPC_PANIC_MAGIC | 0) +#define SOF_IPC_PANIC_WORK (SOF_IPC_PANIC_MAGIC | 1) +#define SOF_IPC_PANIC_IPC (SOF_IPC_PANIC_MAGIC | 2) +#define SOF_IPC_PANIC_ARCH (SOF_IPC_PANIC_MAGIC | 3) +#define SOF_IPC_PANIC_PLATFORM (SOF_IPC_PANIC_MAGIC | 4) +#define SOF_IPC_PANIC_TASK (SOF_IPC_PANIC_MAGIC | 5) +#define SOF_IPC_PANIC_EXCEPTION (SOF_IPC_PANIC_MAGIC | 6) +#define SOF_IPC_PANIC_DEADLOCK (SOF_IPC_PANIC_MAGIC | 7) +#define SOF_IPC_PANIC_STACK (SOF_IPC_PANIC_MAGIC | 8) +#define SOF_IPC_PANIC_IDLE (SOF_IPC_PANIC_MAGIC | 9) + +/* + * SOF memory capabilities, add new ones at the end + */ +#define SOF_MEM_CAPS_RAM (1 << 0) +#define SOF_MEM_CAPS_ROM (1 << 1) +#define SOF_MEM_CAPS_EXT (1 << 2) /* external */ +#define SOF_MEM_CAPS_LP (1 << 3) /* low power */ +#define SOF_MEM_CAPS_HP (1 << 4) /* high performance */ +#define SOF_MEM_CAPS_DMA (1 << 5) /* DMA'able */ +#define SOF_MEM_CAPS_CACHE (1 << 6) /* cacheable */ + +/* + * Command Header - Header for all IPC. Identifies IPC message. + * The size can be greater than the structure size and that means there is + * extended bespoke data beyond the end of the structure including variable + * arrays. + */ + +struct sof_ipc_hdr { + uint32_t cmd; /* SOF_IPC_GLB_ + cmd */ + uint32_t size; /* size of structure */ +} __attribute__((packed)); + +/* + * Generic reply message. Some commands override this with their own reply + * types that must include this at start. + */ +struct sof_ipc_reply { + struct sof_ipc_hdr hdr; + int32_t error; /* negative error numbers */ +} __attribute__((packed)); + +/* + * Compound commands - SOF_IPC_GLB_COMPOUND. + * + * Compound commands are sent to the DSP as a single IPC operation. The + * commands are split into blocks and each block has a header. This header + * identifies the command type and the number of commands before the next + * header. + */ + +struct sof_ipc_compound_hdr { + struct sof_ipc_hdr hdr; + uint32_t count; /* count of 0 means end of compound sequence */ +} __attribute__((packed)); + +/* + * DAI Configuration. + * + * Each different DAI type will have it's own structure and IPC cmd. + */ + +#define SOF_DAI_FMT_I2S 1 /* I2S mode */ +#define SOF_DAI_FMT_RIGHT_J 2 /* Right Justified mode */ +#define SOF_DAI_FMT_LEFT_J 3 /* Left Justified mode */ +#define SOF_DAI_FMT_DSP_A 4 /* L data MSB after FRM LRC */ +#define SOF_DAI_FMT_DSP_B 5 /* L data MSB during FRM LRC */ +#define SOF_DAI_FMT_PDM 6 /* Pulse density modulation */ + +#define SOF_DAI_FMT_CONT (1 << 4) /* continuous clock */ +#define SOF_DAI_FMT_GATED (0 << 4) /* clock is gated */ + +#define SOF_DAI_FMT_NB_NF (0 << 8) /* normal bit clock + frame */ +#define SOF_DAI_FMT_NB_IF (2 << 8) /* normal BCLK + inv FRM */ +#define SOF_DAI_FMT_IB_NF (3 << 8) /* invert BCLK + nor FRM */ +#define SOF_DAI_FMT_IB_IF (4 << 8) /* invert BCLK + FRM */ + +#define SOF_DAI_FMT_CBM_CFM (0 << 12) /* codec clk & FRM master */ +#define SOF_DAI_FMT_CBS_CFM (2 << 12) /* codec clk slave & FRM master */ +#define SOF_DAI_FMT_CBM_CFS (3 << 12) /* codec clk master & frame slave */ +#define SOF_DAI_FMT_CBS_CFS (4 << 12) /* codec clk & FRM slave */ + +#define SOF_DAI_FMT_FORMAT_MASK 0x000f +#define SOF_DAI_FMT_CLOCK_MASK 0x00f0 +#define SOF_DAI_FMT_INV_MASK 0x0f00 +#define SOF_DAI_FMT_MASTER_MASK 0xf000 + +/* types of DAI */ +enum sof_ipc_dai_type { + SOF_DAI_INTEL_NONE = 0, + SOF_DAI_INTEL_SSP, + SOF_DAI_INTEL_DMIC, + SOF_DAI_INTEL_HDA, +}; + +/* SSP Configuration Request - SOF_IPC_DAI_SSP_CONFIG */ +struct sof_ipc_dai_ssp_params { + uint16_t mode; // FIXME: do we need this? + uint16_t clk_id; // FIXME: do we need this? + + uint32_t mclk_rate; /* mclk frequency in Hz */ + uint32_t fsync_rate; /* fsync frequency in Hz */ + uint32_t bclk_rate; /* bclk frequency in Hz */ + + /* TDM */ + uint32_t tdm_slots; + uint32_t rx_slots; + uint32_t tx_slots; + + /* data */ + uint32_t sample_valid_bits; + uint16_t tdm_slot_width; + uint16_t reserved2; /* alignment */ + + /* MCLK */ + uint32_t mclk_direction; + uint32_t mclk_keep_active; + uint32_t bclk_keep_active; + uint32_t fs_keep_active; + + //uint32_t quirks; // FIXME: is 32 bits enough ? + + /* private data, e.g. for quirks */ + //uint32_t pdata[10]; // FIXME: would really need ~16 u32 +} __attribute__((packed)); + +/* HDA Configuration Request - SOF_IPC_DAI_HDA_CONFIG */ +struct sof_ipc_dai_hda_params { + struct sof_ipc_hdr hdr; + /* TODO */ +} __attribute__((packed)); + +/* DMIC Configuration Request - SOF_IPC_DAI_DMIC_CONFIG */ +struct sof_ipc_dai_dmic_params { + struct sof_ipc_hdr hdr; + /* TODO */ +} __attribute__((packed)); + +/* general purpose DAI configuration */ +struct sof_ipc_dai_config { + struct sof_ipc_hdr hdr; + enum sof_ipc_dai_type type; + uint32_t id; /* physical number if more than 1 of this type */ + + /* physical protocol and clocking */ + uint16_t format; /* SOF_DAI_FMT_ */ + uint16_t reserved; /* alignment */ + + /* HW specific data */ + union { + struct sof_ipc_dai_ssp_params ssp; + struct sof_ipc_dai_hda_params hda; + struct sof_ipc_dai_dmic_params dmic; + }; +}; + +/* + * Stream configuration. + */ + +#define SOF_IPC_MAX_CHANNELS 8 + +/* channel positions - uses same values as ALSA */ +enum sof_ipc_chmap { + SOF_CHMAP_UNKNOWN = 0, + SOF_CHMAP_NA, /* N/A, silent */ + SOF_CHMAP_MONO, /* mono stream */ + SOF_CHMAP_FL, /* front left */ + SOF_CHMAP_FR, /* front right */ + SOF_CHMAP_RL, /* rear left */ + SOF_CHMAP_RR, /* rear right */ + SOF_CHMAP_FC, /* front centre */ + SOF_CHMAP_LFE, /* LFE */ + SOF_CHMAP_SL, /* side left */ + SOF_CHMAP_SR, /* side right */ + SOF_CHMAP_RC, /* rear centre */ + SOF_CHMAP_FLC, /* front left centre */ + SOF_CHMAP_FRC, /* front right centre */ + SOF_CHMAP_RLC, /* rear left centre */ + SOF_CHMAP_RRC, /* rear right centre */ + SOF_CHMAP_FLW, /* front left wide */ + SOF_CHMAP_FRW, /* front right wide */ + SOF_CHMAP_FLH, /* front left high */ + SOF_CHMAP_FCH, /* front centre high */ + SOF_CHMAP_FRH, /* front right high */ + SOF_CHMAP_TC, /* top centre */ + SOF_CHMAP_TFL, /* top front left */ + SOF_CHMAP_TFR, /* top front right */ + SOF_CHMAP_TFC, /* top front centre */ + SOF_CHMAP_TRL, /* top rear left */ + SOF_CHMAP_TRR, /* top rear right */ + SOF_CHMAP_TRC, /* top rear centre */ + SOF_CHMAP_TFLC, /* top front left centre */ + SOF_CHMAP_TFRC, /* top front right centre */ + SOF_CHMAP_TSL, /* top side left */ + SOF_CHMAP_TSR, /* top side right */ + SOF_CHMAP_LLFE, /* left LFE */ + SOF_CHMAP_RLFE, /* right LFE */ + SOF_CHMAP_BC, /* bottom centre */ + SOF_CHMAP_BLC, /* bottom left centre */ + SOF_CHMAP_BRC, /* bottom right centre */ + SOF_CHMAP_LAST = SOF_CHMAP_BRC, +}; + +/* common sample rates for use in masks */ +#define SOF_RATE_8000 (1 << 0) /* 8000Hz */ +#define SOF_RATE_11025 (1 << 1) /* 11025Hz */ +#define SOF_RATE_12000 (1 << 2) /* 12000Hz */ +#define SOF_RATE_16000 (1 << 3) /* 16000Hz */ +#define SOF_RATE_22050 (1 << 4) /* 22050Hz */ +#define SOF_RATE_24000 (1 << 5) /* 24000Hz */ +#define SOF_RATE_32000 (1 << 6) /* 32000Hz */ +#define SOF_RATE_44100 (1 << 7) /* 44100Hz */ +#define SOF_RATE_48000 (1 << 8) /* 48000Hz */ +#define SOF_RATE_64000 (1 << 9) /* 64000Hz */ +#define SOF_RATE_88200 (1 << 10) /* 88200Hz */ +#define SOF_RATE_96000 (1 << 11) /* 96000Hz */ +#define SOF_RATE_176400 (1 << 12) /* 176400Hz */ +#define SOF_RATE_192000 (1 << 13) /* 192000Hz */ + +/* continuous and non-standard rates for flexibility */ +#define SOF_RATE_CONTINUOUS (1 << 30) /* range */ +#define SOF_RATE_KNOT (1 << 31) /* non-continuous */ + +/* stream PCM frame format */ +enum sof_ipc_frame { + SOF_IPC_FRAME_S16_LE = 0, + SOF_IPC_FRAME_S24_4LE, + SOF_IPC_FRAME_S32_LE, + SOF_IPC_FRAME_FLOAT, + /* other formats here */ +}; + +/* stream buffer format */ +enum sof_ipc_buffer_format { + SOF_IPC_BUFFER_INTERLEAVED, + SOF_IPC_BUFFER_NONINTERLEAVED, + /* other formats here */ +}; + +/* stream direction */ +enum sof_ipc_stream_direction { + SOF_IPC_STREAM_PLAYBACK = 0, + SOF_IPC_STREAM_CAPTURE, +}; + +/* stream ring info */ +struct sof_ipc_host_buffer { + uint32_t phy_addr; + uint32_t pages; + uint32_t size; + uint32_t offset; +} __attribute__((packed)); + +struct sof_ipc_stream_params { + struct sof_ipc_host_buffer buffer; + enum sof_ipc_stream_direction direction; + enum sof_ipc_frame frame_fmt; + enum sof_ipc_buffer_format buffer_fmt; + uint32_t stream_tag; + uint32_t rate; + uint32_t channels; + uint32_t sample_valid_bytes; + uint32_t sample_container_bytes; + /* for notifying host period has completed - 0 means no period IRQ */ + uint32_t host_period_bytes; + enum sof_ipc_chmap chmap[SOF_IPC_MAX_CHANNELS]; /* channel map */ +} __attribute__((packed)); + +/* PCM params info - SOF_IPC_STREAM_PCM_PARAMS */ +struct sof_ipc_pcm_params { + struct sof_ipc_hdr hdr; + uint32_t comp_id; + struct sof_ipc_stream_params params; +} __attribute__((packed)); + +/* PCM params info reply - SOF_IPC_STREAM_PCM_PARAMS_REPLY */ +struct sof_ipc_pcm_params_reply { + struct sof_ipc_reply rhdr; + uint32_t comp_id; + uint32_t posn_offset; +} __attribute__((packed)); + +/* compressed vorbis params - SOF_IPC_STREAM_VORBIS_PARAMS */ +struct sof_ipc_vorbis_params { + struct sof_ipc_hdr hdr; + uint32_t comp_id; + struct sof_ipc_stream_params params; + /* TODO */ +} __attribute__((packed)); + +/* free stream - SOF_IPC_STREAM_PCM_PARAMS */ +struct sof_ipc_stream { + struct sof_ipc_hdr hdr; + uint32_t comp_id; +} __attribute__((packed)); + +/* flags indicating which time stamps are in sync with each other */ +#define SOF_TIME_HOST_SYNC (1 << 0) +#define SOF_TIME_DAI_SYNC (1 << 1) +#define SOF_TIME_WALL_SYNC (1 << 2) +#define SOF_TIME_STAMP_SYNC (1 << 3) + +/* flags indicating which time stamps are valid */ +#define SOF_TIME_HOST_VALID (1 << 8) +#define SOF_TIME_DAI_VALID (1 << 9) +#define SOF_TIME_WALL_VALID (1 << 10) +#define SOF_TIME_STAMP_VALID (1 << 11) + +/* flags indicating time stamps are 64bit else 3use low 32bit */ +#define SOF_TIME_HOST_64 (1 << 16) +#define SOF_TIME_DAI_64 (1 << 17) +#define SOF_TIME_WALL_64 (1 << 18) +#define SOF_TIME_STAMP_64 (1 << 19) + +struct sof_ipc_stream_posn { + struct sof_ipc_reply rhdr; + uint32_t comp_id; /* host component ID */ + uint32_t flags; /* SOF_TIME_ */ + uint32_t wallclock_hz; /* frequency of wallclock in Hz */ + uint32_t timestamp_ns; /* resolution of timestamp in ns */ + uint64_t host_posn; /* host DMA position in bytes */ + uint64_t dai_posn; /* DAI DMA position in bytes */ + uint64_t comp_posn; /* comp position in bytes */ + uint64_t wallclock; /* audio wall clock */ + uint64_t timestamp; /* system time stamp */ + uint32_t xrun_comp_id; /* comp ID of XRUN component */ + int32_t xrun_size; /* XRUN size in bytes */ +} __attribute__((packed)); + +/* + * Component Mixers and Controls + */ + +/* control data type and direction */ +enum sof_ipc_ctrl_type { + /* per channel data - uses struct sof_ipc_ctrl_value_chan */ + SOF_CTRL_TYPE_VALUE_CHAN_GET = 0, + SOF_CTRL_TYPE_VALUE_CHAN_SET, + /* component data - uses struct sof_ipc_ctrl_value_comp */ + SOF_CTRL_TYPE_VALUE_COMP_GET, + SOF_CTRL_TYPE_VALUE_COMP_SET, + /* bespoke data - struct struct sof_abi_hdr */ + SOF_CTRL_TYPE_DATA_GET, + SOF_CTRL_TYPE_DATA_SET, +}; + +/* control command type */ +enum sof_ipc_ctrl_cmd { + SOF_CTRL_CMD_VOLUME = 0, /* maps to ALSA volume style controls */ + SOF_CTRL_CMD_ENUM, /* maps to ALSA enum style controls */ + SOF_CTRL_CMD_SWITCH, /* maps to ALSA switch style controls */ + SOF_CTRL_CMD_BINARY, /* maps to ALSA binary style controls */ +}; + +/* generic channel mapped value data */ +struct sof_ipc_ctrl_value_chan { + enum sof_ipc_chmap channel; + uint32_t value; +} __attribute__((packed)); + +/* generic component mapped value data */ +struct sof_ipc_ctrl_value_comp { + uint32_t index; /* component source/sink/control index in control */ + union { + uint32_t uvalue; + int32_t svalue; + }; +} __attribute__((packed)); + +/* generic control data */ +struct sof_ipc_ctrl_data { + struct sof_ipc_reply rhdr; + uint32_t comp_id; + + /* control access and data type */ + enum sof_ipc_ctrl_type type; + enum sof_ipc_ctrl_cmd cmd; + uint32_t index; /* control index for comps > 1 control */ + + /* control data - can either be appended or DMAed from host */ + struct sof_ipc_host_buffer buffer; + uint32_t num_elems; /* in array elems or bytes */ + + /* control data - add new types if needed */ + union { + /* channel values can be used by volume type controls */ + struct sof_ipc_ctrl_value_chan chanv[0]; + /* component values used by routing controls like mux, mixer */ + struct sof_ipc_ctrl_value_comp compv[0]; + /* data can be used by binary controls */ + struct sof_abi_hdr data[0]; + }; +} __attribute__((packed)); + +/* + * Component + */ + +/* types of component */ +enum sof_comp_type { + SOF_COMP_NONE = 0, + SOF_COMP_HOST, + SOF_COMP_DAI, + SOF_COMP_SG_HOST, /* scatter gather variant */ + SOF_COMP_SG_DAI, /* scatter gather variant */ + SOF_COMP_VOLUME, + SOF_COMP_MIXER, + SOF_COMP_MUX, + SOF_COMP_SRC, + SOF_COMP_SPLITTER, + SOF_COMP_TONE, + SOF_COMP_SWITCH, + SOF_COMP_BUFFER, + SOF_COMP_EQ_IIR, + SOF_COMP_EQ_FIR, + SOF_COMP_FILEREAD, /* host test based file IO */ + SOF_COMP_FILEWRITE, /* host test based file IO */ +}; + +/* XRUN action for component */ +#define SOF_XRUN_STOP 1 /* stop stream */ +#define SOF_XRUN_UNDER_ZERO 2 /* send 0s to sink */ +#define SOF_XRUN_OVER_NULL 4 /* send data to NULL */ + +/* create new generic component - SOF_IPC_TPLG_COMP_NEW */ +struct sof_ipc_comp { + struct sof_ipc_hdr hdr; + uint32_t id; + enum sof_comp_type type; + uint32_t pipeline_id; +} __attribute__((packed)); + +/* + * Component Buffers + */ + +/* create new component buffer - SOF_IPC_TPLG_BUFFER_NEW */ +struct sof_ipc_buffer { + struct sof_ipc_comp comp; + uint32_t size; /* buffer size in bytes */ + uint32_t caps; /* SOF_MEM_CAPS_ */ +} __attribute__((packed)); + +/* generic component config data - must always be after struct sof_ipc_comp */ +struct sof_ipc_comp_config { + uint32_t periods_sink; /* 0 means variable */ + uint32_t periods_source; /* 0 means variable */ + uint32_t preload_count; /* how many periods to preload */ + enum sof_ipc_frame frame_fmt; + uint32_t xrun_action; +} __attribute__((packed)); + +/* generic host component */ +struct sof_ipc_comp_host { + struct sof_ipc_comp comp; + struct sof_ipc_comp_config config; + enum sof_ipc_stream_direction direction; + uint32_t no_irq; /* don't send periodic IRQ to host/DSP */ + uint32_t dmac_id; + uint32_t dmac_chan; + uint32_t dmac_config; /* DMA engine specific */ +} __attribute__((packed)); + +/* generic DAI component */ +struct sof_ipc_comp_dai { + struct sof_ipc_comp comp; + struct sof_ipc_comp_config config; + enum sof_ipc_stream_direction direction; + uint32_t index; + enum sof_ipc_dai_type type; + uint32_t dmac_id; + uint32_t dmac_chan; + uint32_t dmac_config; /* DMA engine specific */ +} __attribute__((packed)); + +/* generic mixer component */ +struct sof_ipc_comp_mixer { + struct sof_ipc_comp comp; + struct sof_ipc_comp_config config; +} __attribute__((packed)); + +/* volume ramping types */ +enum sof_volume_ramp { + SOF_VOLUME_LINEAR = 0, + SOF_VOLUME_LOG, + SOF_VOLUME_LINEAR_ZC, + SOF_VOLUME_LOG_ZC, +}; + +/* generic volume component */ +struct sof_ipc_comp_volume { + struct sof_ipc_comp comp; + struct sof_ipc_comp_config config; + uint32_t channels; + int32_t min_value; + int32_t max_value; + enum sof_volume_ramp ramp; + uint32_t initial_ramp; /* ramp space in ms */ +} __attribute__((packed)); + +/* generic SRC component */ +struct sof_ipc_comp_src { + struct sof_ipc_comp comp; + struct sof_ipc_comp_config config; + /* either source or sink rate must be non zero */ + uint32_t source_rate; /* source rate or 0 for variable */ + uint32_t sink_rate; /* sink rate or 0 for variable */ + uint32_t rate_mask; /* SOF_RATE_ supported rates */ +} __attribute__((packed)); + +/* generic MUX component */ +struct sof_ipc_comp_mux { + struct sof_ipc_comp comp; + struct sof_ipc_comp_config config; +} __attribute__((packed)); + +/* generic tone generator component */ +struct sof_ipc_comp_tone { + struct sof_ipc_comp comp; + struct sof_ipc_comp_config config; + int32_t frequency; + int32_t amplitude; + int32_t freq_mult; + int32_t ampl_mult; + int32_t length; + int32_t period; + int32_t repeats; + int32_t ramp_step; +} __attribute__((packed)); + +/* FIR equalizer component */ +struct sof_ipc_comp_eq_fir { + struct sof_ipc_comp comp; + struct sof_ipc_comp_config config; +} __attribute__((packed)); + +/* IIR equalizer component */ +struct sof_ipc_comp_eq_iir { + struct sof_ipc_comp comp; + struct sof_ipc_comp_config config; +} __attribute__((packed)); + +/* frees components, buffers and pipelines + * SOF_IPC_TPLG_COMP_FREE, SOF_IPC_TPLG_PIPE_FREE, SOF_IPC_TPLG_BUFFER_FREE + */ +struct sof_ipc_free { + struct sof_ipc_hdr hdr; + uint32_t id; +} __attribute__((packed)); + +struct sof_ipc_comp_reply { + struct sof_ipc_reply rhdr; + uint32_t id; + uint32_t offset; +} __attribute__((packed)); + +/* + * Pipeline + */ + +/* new pipeline - SOF_IPC_TPLG_PIPE_NEW */ +struct sof_ipc_pipe_new { + struct sof_ipc_hdr hdr; + uint32_t comp_id; /* component id for pipeline */ + uint32_t pipeline_id; /* pipeline id */ + uint32_t sched_id; /* sheduling component id */ + uint32_t core; /* core we run on */ + uint32_t deadline; /* execution completion deadline in us*/ + uint32_t priority; /* priority level 0 (low) to 10 (max) */ + uint32_t mips; /* worst case instruction count per period */ + uint32_t frames_per_sched;/* output frames of pipeline, 0 is variable */ + uint32_t xrun_limit_usecs; /* report xruns greater than limit */ + uint32_t timer;/* non zero if timer scheduled otherwise DAI scheduled */ +} __attribute__((packed)); + +/* pipeline construction complete - SOF_IPC_TPLG_PIPE_COMPLETE */ +struct sof_ipc_pipe_ready { + struct sof_ipc_hdr hdr; + uint32_t comp_id; +} __attribute__((packed)); + +struct sof_ipc_pipe_free { + struct sof_ipc_hdr hdr; + uint32_t comp_id; +} __attribute__((packed)); + +/* connect two components in pipeline - SOF_IPC_TPLG_COMP_CONNECT */ +struct sof_ipc_pipe_comp_connect { + struct sof_ipc_hdr hdr; + uint32_t source_id; + uint32_t sink_id; +} __attribute__((packed)); + +/* + * PM + */ + +/* PM context element */ +struct sof_ipc_pm_ctx_elem { + uint32_t type; + uint32_t size; + uint64_t addr; +} __attribute__((packed)); + +/* + * PM context - SOF_IPC_PM_CTX_SAVE, SOF_IPC_PM_CTX_RESTORE, + * SOF_IPC_PM_CTX_SIZE + */ +struct sof_ipc_pm_ctx { + struct sof_ipc_hdr hdr; + struct sof_ipc_host_buffer buffer; + uint32_t num_elems; + uint32_t size; + struct sof_ipc_pm_ctx_elem elems[]; +}; + +/* + * Firmware boot and version + */ + +#define SOF_IPC_MAX_ELEMS 16 + +/* extended data types that can be appended onto end of sof_ipc_fw_ready */ +enum sof_ipc_ext_data { + SOF_IPC_EXT_DMA_BUFFER = 0, + SOF_IPC_EXT_WINDOW, +}; + +/* FW version - SOF_IPC_GLB_VERSION */ +struct sof_ipc_fw_version { + uint16_t major; + uint16_t minor; + uint16_t build; + uint8_t date[12]; + uint8_t time[10]; + uint8_t tag[6]; +} __attribute__((packed)); + +/* FW ready Message - sent by firmware when boot has completed */ +struct sof_ipc_fw_ready { + struct sof_ipc_hdr hdr; + uint32_t dspbox_offset; /* dsp initiated IPC mailbox */ + uint32_t hostbox_offset; /* host initiated IPC mailbox */ + uint32_t dspbox_size; + uint32_t hostbox_size; + struct sof_ipc_fw_version version; +} __attribute__((packed)); + +/* + * Extended Firmware data. All optional, depends on platform/arch. + */ + +enum sof_ipc_region { + SOF_IPC_REGION_DOWNBOX = 0, + SOF_IPC_REGION_UPBOX, + SOF_IPC_REGION_TRACE, + SOF_IPC_REGION_DEBUG, + SOF_IPC_REGION_STREAM, + SOF_IPC_REGION_REGS, + SOF_IPC_REGION_EXCEPTION, +}; + +struct sof_ipc_ext_data_hdr { + struct sof_ipc_hdr hdr; + enum sof_ipc_ext_data type; /* SOF_IPC_EXT_ */ +}; + +struct sof_ipc_dma_buffer_elem { + enum sof_ipc_region type; + uint32_t id; /* platform specific - used to map to host memory */ + struct sof_ipc_host_buffer buffer; +}; + +/* extended data DMA buffers for IPC, trace and debug */ +struct sof_ipc_dma_buffer_data { + struct sof_ipc_ext_data_hdr ext_hdr; + uint32_t num_buffers; + /* host files in buffer[n].buffer */ + struct sof_ipc_dma_buffer_elem buffer[]; +} __attribute__((packed)); + +struct sof_ipc_window_elem { + enum sof_ipc_region type; + uint32_t id; /* platform specific - used to map to host memory */ + uint32_t flags; /* R, W, RW, etc - to define */ + uint32_t size; /* size of region in bytes */ + /* offset in window region as windows can be partitioned */ + uint32_t offset; +}; + +/* extended data memory windows for IPC, trace and debug */ +struct sof_ipc_window { + struct sof_ipc_ext_data_hdr ext_hdr; + uint32_t num_windows; + struct sof_ipc_window_elem window[]; +} __attribute__((packed)); + +/* + * DMA for Trace + */ + +/* DMA for Trace params info - SOF_IPC_DEBUG_DMA_PARAMS */ +struct sof_ipc_dma_trace_params { + struct sof_ipc_hdr hdr; + struct sof_ipc_host_buffer buffer; + uint32_t stream_tag; +} __attribute__((packed)); + +/* DMA for Trace params info - SOF_IPC_DEBUG_DMA_PARAMS */ +struct sof_ipc_dma_trace_posn { + struct sof_ipc_reply rhdr; + uint32_t host_offset; /* Offset of DMA host buffer */ + uint32_t overflow; /* overflow bytes if any */ + uint32_t messages; /* total trace messages */ +} __attribute__((packed)); + +/* + * Architecture specific debug + */ + +/* Xtensa Firmware Oops data */ +struct sof_ipc_dsp_oops_xtensa { + uint32_t exccause; + uint32_t excvaddr; + uint32_t ps; + uint32_t epc1; + uint32_t epc2; + uint32_t epc3; + uint32_t epc4; + uint32_t epc5; + uint32_t epc6; + uint32_t epc7; + uint32_t eps2; + uint32_t eps3; + uint32_t eps4; + uint32_t eps5; + uint32_t eps6; + uint32_t eps7; + uint32_t depc; + uint32_t intenable; + uint32_t interrupt; + uint32_t sar; + uint32_t stack; +} __attribute__((packed)); + +#endif diff --git a/sound/soc/sof/ipc.c b/sound/soc/sof/ipc.c new file mode 100644 index 00000000000000..bd646c23cf9fd2 --- /dev/null +++ b/sound/soc/sof/ipc.c @@ -0,0 +1,609 @@ +// SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause) +/* + * This file is provided under a dual BSD/GPLv2 license. When using or + * redistributing this file, you may do so under either license. + * + * Copyright(c) 2017 Intel Corporation. All rights reserved. + * + * Author: Liam Girdwood + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "sof-priv.h" +#include "ops.h" + +/* IPC message timeout (msecs) */ +#define IPC_TIMEOUT_MSECS 300 + +#define IPC_EMPTY_LIST_SIZE 8 + +/* SOF generic IPC data */ +struct snd_sof_ipc { + struct snd_sof_dev *sdev; + + /* TX message work and status */ + wait_queue_head_t wait_txq; + struct work_struct tx_kwork; + bool msg_pending; + + /* Rx Message work and status */ + struct work_struct rx_kwork; + + /* lists */ + struct list_head tx_list; + struct list_head reply_list; + struct list_head empty_list; +}; + +/* locks held by caller */ +static struct snd_sof_ipc_msg *msg_get_empty(struct snd_sof_ipc *ipc) +{ + struct snd_sof_ipc_msg *msg = NULL; + + if (!list_empty(&ipc->empty_list)) { + msg = list_first_entry(&ipc->empty_list, struct snd_sof_ipc_msg, + list); + list_del(&msg->list); + } + + return msg; +} + +static int tx_wait_done(struct snd_sof_ipc *ipc, struct snd_sof_ipc_msg *msg, + void *reply_data) +{ + struct snd_sof_dev *sdev = ipc->sdev; + struct sof_ipc_hdr *hdr = (struct sof_ipc_hdr *)msg->msg_data; + unsigned long flags; + int ret; + + /* wait for DSP IPC completion */ + ret = wait_event_timeout(msg->waitq, msg->complete, + msecs_to_jiffies(IPC_TIMEOUT_MSECS)); + + spin_lock_irqsave(&sdev->ipc_lock, flags); + + if (ret == 0) { + dev_err(sdev->dev, "error: ipc timed out for 0x%x size 0x%x\n", + hdr->cmd, hdr->size); + snd_sof_dsp_dbg_dump(ipc->sdev, SOF_DBG_REGS | SOF_DBG_MBOX); + snd_sof_trace_notify_for_error(ipc->sdev); + ret = -ETIMEDOUT; + } else { + /* copy the data returned from DSP */ + ret = snd_sof_dsp_get_reply(sdev, msg); + if (msg->reply_size) + memcpy(reply_data, msg->reply_data, msg->reply_size); + if (ret < 0) + dev_err(sdev->dev, "error: ipc error for 0x%x size 0x%zx\n", + hdr->cmd, msg->reply_size); + else + dev_dbg(sdev->dev, "ipc: 0x%x succeeded\n", hdr->cmd); + } + + /* return message body to empty list */ + list_move(&msg->list, &ipc->empty_list); + + spin_unlock_irqrestore(&sdev->ipc_lock, flags); + + /* continue to schedule any remaining messages... */ + snd_sof_ipc_msgs_tx(sdev); + + return ret; +} + +int sof_ipc_tx_message(struct snd_sof_ipc *ipc, u32 header, + void *msg_data, size_t msg_bytes, void *reply_data, + size_t reply_bytes) +{ + struct snd_sof_dev *sdev = ipc->sdev; + struct snd_sof_ipc_msg *msg; + unsigned long flags; + + spin_lock_irqsave(&sdev->ipc_lock, flags); + + msg = msg_get_empty(ipc); + if (!msg) { + spin_unlock_irqrestore(&sdev->ipc_lock, flags); + return -EBUSY; + } + + msg->header = header; + msg->msg_size = msg_bytes; + msg->reply_size = reply_bytes; + msg->complete = false; + + if (msg_bytes) + memcpy(msg->msg_data, msg_data, msg_bytes); + + list_add_tail(&msg->list, &ipc->tx_list); + + /* schedule the messgae if not busy */ + if (snd_sof_dsp_is_ready(sdev)) + schedule_work(&ipc->tx_kwork); + + spin_unlock_irqrestore(&sdev->ipc_lock, flags); + + return tx_wait_done(ipc, msg, reply_data); +} +EXPORT_SYMBOL(sof_ipc_tx_message); + +static void ipc_tx_next_msg(struct work_struct *work) +{ + struct snd_sof_ipc *ipc = + container_of(work, struct snd_sof_ipc, tx_kwork); + struct snd_sof_dev *sdev = ipc->sdev; + struct snd_sof_ipc_msg *msg; + + spin_lock_irq(&sdev->ipc_lock); + + if (list_empty(&ipc->tx_list)) + goto out; + + msg = list_first_entry(&ipc->tx_list, struct snd_sof_ipc_msg, list); + list_move(&msg->list, &ipc->reply_list); + + snd_sof_dsp_send_msg(sdev, msg); + dev_dbg(sdev->dev, "ipc: send 0x%x\n", msg->header); + +out: + spin_unlock_irq(&sdev->ipc_lock); +} + +struct snd_sof_ipc_msg *sof_ipc_reply_find_msg(struct snd_sof_ipc *ipc, + u32 header) +{ + struct snd_sof_dev *sdev = ipc->sdev; + struct snd_sof_ipc_msg *msg; + + header = SOF_IPC_MESSAGE_ID(header); + + if (list_empty(&ipc->reply_list)) + goto err; + + list_for_each_entry(msg, &ipc->reply_list, list) { + if (SOF_IPC_MESSAGE_ID(msg->header) == header) + return msg; + } + +err: + dev_err(sdev->dev, "error: rx list empty but received 0x%x\n", + header); + return NULL; +} +EXPORT_SYMBOL(sof_ipc_reply_find_msg); + +/* locks held by caller */ +void sof_ipc_tx_msg_reply_complete(struct snd_sof_ipc *ipc, + struct snd_sof_ipc_msg *msg) +{ + msg->complete = true; + wake_up(&msg->waitq); +} + +void sof_ipc_drop_all(struct snd_sof_ipc *ipc) +{ + struct snd_sof_dev *sdev = ipc->sdev; + struct snd_sof_ipc_msg *msg, *tmp; + unsigned long flags; + + /* drop all TX and Rx messages before we stall + reset DSP */ + spin_lock_irqsave(&sdev->ipc_lock, flags); + + list_for_each_entry_safe(msg, tmp, &ipc->tx_list, list) { + list_move(&msg->list, &ipc->empty_list); + dev_err(sdev->dev, "error: dropped msg %d\n", msg->header); + } + + list_for_each_entry_safe(msg, tmp, &ipc->reply_list, list) { + list_move(&msg->list, &ipc->empty_list); + dev_err(sdev->dev, "error: dropped reply %d\n", msg->header); + } + + spin_unlock_irqrestore(&sdev->ipc_lock, flags); +} +EXPORT_SYMBOL(sof_ipc_drop_all); + +void snd_sof_ipc_reply(struct snd_sof_dev *sdev, u32 msg_id) +{ + struct snd_sof_ipc_msg *msg; + + msg = sof_ipc_reply_find_msg(sdev->ipc, msg_id); + if (!msg) { + dev_err(sdev->dev, "error: can't find message header 0x%x", + msg_id); + return; + } + + /* wake up and return the error if we have waiters on this message ? */ + sof_ipc_tx_msg_reply_complete(sdev->ipc, msg); +} +EXPORT_SYMBOL(snd_sof_ipc_reply); + +int snd_sof_dsp_mailbox_init(struct snd_sof_dev *sdev, u32 dspbox, + size_t dspbox_size, u32 hostbox, + size_t hostbox_size) +{ + sdev->dsp_box.offset = dspbox; + sdev->dsp_box.size = dspbox_size; + sdev->host_box.offset = hostbox; + sdev->host_box.size = hostbox_size; + return 0; +} +EXPORT_SYMBOL(snd_sof_dsp_mailbox_init); + +static void ipc_period_elapsed(struct snd_sof_dev *sdev, u32 msg_id) +{ + struct sof_ipc_stream_posn posn; + struct snd_sof_pcm *spcm; + u32 posn_offset; + int direction; + + /* check if we have stream box */ + if (sdev->stream_box.size == 0) { + /* read back full message */ + snd_sof_dsp_mailbox_read(sdev, sdev->dsp_box.offset, &posn, + sizeof(posn)); + + spcm = snd_sof_find_spcm_comp(sdev, posn.comp_id, &direction); + } else { + spcm = snd_sof_find_spcm_comp(sdev, msg_id, &direction); + } + + if (!spcm) { + dev_err(sdev->dev, + "period elapsed for unknown stream, msg_id %d\n", + msg_id); + return; + } + + /* have stream box read from stream box */ + if (sdev->stream_box.size != 0) { + posn_offset = spcm->posn_offset[direction]; + snd_sof_dsp_mailbox_read(sdev, posn_offset, &posn, + sizeof(posn)); + + dev_dbg(sdev->dev, "posn mailbox: posn offset is 0x%x", + posn_offset); + } + + dev_dbg(sdev->dev, "posn : host 0x%llx dai 0x%llx wall 0x%llx\n", + posn.host_posn, posn.dai_posn, posn.wallclock); + + memcpy(&spcm->stream[direction].posn, &posn, sizeof(posn)); + snd_pcm_period_elapsed(spcm->stream[direction].substream); +} + +static void ipc_xrun(struct snd_sof_dev *sdev, u32 msg_id) +{ + struct sof_ipc_stream_posn posn; + struct snd_sof_pcm *spcm; + u32 posn_offset; + int direction; + + /* check if we have stream box */ + if (sdev->stream_box.size == 0) { + /* read back full message */ + snd_sof_dsp_mailbox_read(sdev, sdev->dsp_box.offset, &posn, + sizeof(posn)); + + spcm = snd_sof_find_spcm_comp(sdev, posn.comp_id, &direction); + } else { + spcm = snd_sof_find_spcm_comp(sdev, msg_id, &direction); + } + + if (!spcm) { + dev_err(sdev->dev, "XRUN for unknown stream, msg_id %d\n", + msg_id); + return; + } + + /* have stream box read from stream box */ + if (sdev->stream_box.size != 0) { + posn_offset = spcm->posn_offset[direction]; + snd_sof_dsp_mailbox_read(sdev, posn_offset, &posn, + sizeof(posn)); + + dev_dbg(sdev->dev, "posn mailbox: posn offset is 0x%x", + posn_offset); + } + + dev_dbg(sdev->dev, "posn XRUN: host %llx comp %d size %d\n", + posn.host_posn, posn.xrun_comp_id, posn.xrun_size); + + return; /* TODO: don't do anything yet until preload is working */ + + memcpy(&spcm->stream[direction].posn, &posn, sizeof(posn)); + snd_pcm_stop_xrun(spcm->stream[direction].substream); +} + +static void ipc_stream_message(struct snd_sof_dev *sdev, u32 msg_cmd) +{ + /* get msg cmd type and msd id */ + u32 msg_type = msg_cmd & SOF_CMD_TYPE_MASK; + u32 msg_id = SOF_IPC_MESSAGE_ID(msg_cmd); + + switch (msg_type) { + case SOF_IPC_STREAM_POSITION: + ipc_period_elapsed(sdev, msg_id); + break; + case SOF_IPC_STREAM_TRIG_XRUN: + ipc_xrun(sdev, msg_id); + break; + default: + dev_err(sdev->dev, "error: unhandled stream message %x\n", + msg_id); + break; + } +} + +static void ipc_trace_message(struct snd_sof_dev *sdev, u32 msg_id) +{ + struct sof_ipc_dma_trace_posn posn; + + switch (msg_id) { + case SOF_IPC_TRACE_DMA_POSITION: + /* read back full message */ + snd_sof_dsp_mailbox_read(sdev, sdev->dsp_box.offset, &posn, + sizeof(posn)); + snd_sof_trace_update_pos(sdev, &posn); + break; + default: + dev_err(sdev->dev, "error: unhandled trace message %x\n", + msg_id); + break; + } +} + +/* DSP firmware has sent host a message */ +static void ipc_msgs_rx(struct work_struct *work) +{ + struct snd_sof_ipc *ipc = + container_of(work, struct snd_sof_ipc, rx_kwork); + struct snd_sof_dev *sdev = ipc->sdev; + struct sof_ipc_hdr hdr; + u32 cmd, type; + int err = -EINVAL; + + /* read back header */ + snd_sof_dsp_mailbox_read(sdev, sdev->dsp_box.offset, &hdr, sizeof(hdr)); + + cmd = hdr.cmd & SOF_GLB_TYPE_MASK; + type = hdr.cmd & SOF_CMD_TYPE_MASK; + + switch (cmd) { + case SOF_IPC_GLB_REPLY: + dev_err(sdev->dev, "error: ipc reply unknown\n"); + break; + case SOF_IPC_FW_READY: + /* check for FW boot completion */ + if (!sdev->boot_complete) { + if (sdev->ops->fw_ready) + err = sdev->ops->fw_ready(sdev, cmd); + if (err < 0) { + dev_err(sdev->dev, "DSP firmware boot timeout %d\n", + err); + } else { + /* firmware boot completed OK */ + sdev->boot_complete = true; + dev_dbg(sdev->dev, "booting DSP firmware completed\n"); + wake_up(&sdev->boot_wait); + } + } + break; + case SOF_IPC_GLB_COMPOUND: + case SOF_IPC_GLB_TPLG_MSG: + case SOF_IPC_GLB_PM_MSG: + case SOF_IPC_GLB_COMP_MSG: + break; + case SOF_IPC_GLB_STREAM_MSG: + /* need to pass msg id into the function */ + ipc_stream_message(sdev, hdr.cmd); + break; + case SOF_IPC_GLB_TRACE_MSG: + ipc_trace_message(sdev, type); + break; + default: + dev_err(sdev->dev, "unknown DSP message 0x%x\n", cmd); + break; + } + + dev_dbg(sdev->dev, "ipc rx: 0x%x done\n", hdr.cmd); + + snd_sof_dsp_cmd_done(sdev); +} + +void snd_sof_ipc_msgs_tx(struct snd_sof_dev *sdev) +{ + schedule_work(&sdev->ipc->tx_kwork); +} +EXPORT_SYMBOL(snd_sof_ipc_msgs_tx); + +void snd_sof_ipc_msgs_rx(struct snd_sof_dev *sdev) +{ + schedule_work(&sdev->ipc->rx_kwork); +} +EXPORT_SYMBOL(snd_sof_ipc_msgs_rx); + +struct snd_sof_ipc *snd_sof_ipc_init(struct snd_sof_dev *sdev) +{ + struct snd_sof_ipc *ipc; + struct snd_sof_ipc_msg *msg; + int i; + + ipc = devm_kzalloc(sdev->dev, sizeof(*ipc), GFP_KERNEL); + if (!ipc) + return NULL; + + INIT_LIST_HEAD(&ipc->tx_list); + INIT_LIST_HEAD(&ipc->reply_list); + INIT_LIST_HEAD(&ipc->empty_list); + init_waitqueue_head(&ipc->wait_txq); + INIT_WORK(&ipc->tx_kwork, ipc_tx_next_msg); + INIT_WORK(&ipc->rx_kwork, ipc_msgs_rx); + ipc->sdev = sdev; + + /* pre-allocate messages */ + dev_dbg(sdev->dev, "pre-allocate %d IPC messages\n", + IPC_EMPTY_LIST_SIZE); + msg = devm_kzalloc(sdev->dev, sizeof(struct snd_sof_ipc_msg) * + IPC_EMPTY_LIST_SIZE, GFP_KERNEL); + if (!msg) + return NULL; + + /* pre-allocate message data */ + for (i = 0; i < IPC_EMPTY_LIST_SIZE; i++) { + msg->msg_data = devm_kzalloc(sdev->dev, PAGE_SIZE, GFP_KERNEL); + if (!msg->msg_data) + return NULL; + + msg->reply_data = devm_kzalloc(sdev->dev, PAGE_SIZE, + GFP_KERNEL); + if (!msg->reply_data) + return NULL; + + init_waitqueue_head(&msg->waitq); + list_add(&msg->list, &ipc->empty_list); + msg++; + } + + return ipc; +} +EXPORT_SYMBOL(snd_sof_ipc_init); + +void snd_sof_ipc_free(struct snd_sof_dev *sdev) +{ + /* TODO: send IPC to prepare DSP for shutdown */ + cancel_work_sync(&sdev->ipc->tx_kwork); + cancel_work_sync(&sdev->ipc->rx_kwork); +} +EXPORT_SYMBOL(snd_sof_ipc_free); + +int snd_sof_ipc_stream_posn(struct snd_sof_dev *sdev, + struct snd_sof_pcm *spcm, int direction, + struct sof_ipc_stream_posn *posn) +{ + struct sof_ipc_stream stream; + int err; + + /* read position via slower IPC */ + stream.hdr.size = sizeof(stream); + stream.hdr.cmd = SOF_IPC_GLB_STREAM_MSG | SOF_IPC_STREAM_POSITION; + stream.comp_id = spcm->stream[direction].comp_id; + + /* send IPC to the DSP */ + err = sof_ipc_tx_message(sdev->ipc, + stream.hdr.cmd, &stream, sizeof(stream), &posn, + sizeof(*posn)); + if (err < 0) { + dev_err(sdev->dev, "error: failed to get stream %d position\n", + stream.comp_id); + return err; + } + + return 0; +} +EXPORT_SYMBOL(snd_sof_ipc_stream_posn); + +int snd_sof_ipc_set_comp_data(struct snd_sof_ipc *ipc, + struct snd_sof_control *scontrol, u32 ipc_cmd, + enum sof_ipc_ctrl_type ctrl_type, + enum sof_ipc_ctrl_cmd ctrl_cmd) +{ + struct snd_sof_dev *sdev = ipc->sdev; + struct sof_ipc_ctrl_data *cdata = scontrol->control_data; + int err; + + /* read firmware volume */ + if (scontrol->readback_offset != 0) { + /* we can read value header via mmaped region */ + snd_sof_dsp_block_write(sdev, scontrol->readback_offset, + cdata->chanv, + sizeof(struct sof_ipc_ctrl_value_chan) * + cdata->num_elems); + + } else { + /* write value via slower IPC */ + cdata->rhdr.hdr.cmd = SOF_IPC_GLB_COMP_MSG | ipc_cmd; + cdata->cmd = ctrl_cmd; + cdata->type = ctrl_type; + cdata->rhdr.hdr.size = scontrol->size; + cdata->comp_id = scontrol->comp_id; + cdata->num_elems = scontrol->num_channels; + + /* send IPC to the DSP */ + err = sof_ipc_tx_message(sdev->ipc, + cdata->rhdr.hdr.cmd, cdata, + cdata->rhdr.hdr.size, + cdata, cdata->rhdr.hdr.size); + if (err < 0) { + dev_err(sdev->dev, "error: failed to set control %d values\n", + cdata->comp_id); + return err; + } + } + + return 0; +} +EXPORT_SYMBOL(snd_sof_ipc_set_comp_data); + +int snd_sof_ipc_get_comp_data(struct snd_sof_ipc *ipc, + struct snd_sof_control *scontrol, u32 ipc_cmd, + enum sof_ipc_ctrl_type ctrl_type, + enum sof_ipc_ctrl_cmd ctrl_cmd) +{ + struct snd_sof_dev *sdev = ipc->sdev; + struct sof_ipc_ctrl_data *cdata = scontrol->control_data; + int err; + + /* read firmware byte counters */ + if (scontrol->readback_offset != 0) { + /* we can read values via mmaped region */ + snd_sof_dsp_block_read(sdev, scontrol->readback_offset, + cdata->chanv, + sizeof(struct sof_ipc_ctrl_value_chan) * + cdata->num_elems); + + } else { + /* read position via slower IPC */ + cdata->rhdr.hdr.cmd = SOF_IPC_GLB_COMP_MSG | ipc_cmd; + cdata->cmd = ctrl_cmd; + cdata->type = ctrl_type; + cdata->rhdr.hdr.size = scontrol->size; + cdata->comp_id = scontrol->comp_id; + cdata->num_elems = scontrol->num_channels; + + /* send IPC to the DSP */ + err = sof_ipc_tx_message(sdev->ipc, + cdata->rhdr.hdr.cmd, cdata, + cdata->rhdr.hdr.size, + cdata, cdata->rhdr.hdr.size); + if (err < 0) { + dev_err(sdev->dev, "error: failed to get control %d values\n", + cdata->comp_id); + return err; + } + } + + return 0; +} +EXPORT_SYMBOL(snd_sof_ipc_get_comp_data); From 9c9d49ddf89370f3886ea1e9ca9d1d96d7003762 Mon Sep 17 00:00:00 2001 From: Liam Girdwood Date: Mon, 8 Jan 2018 20:32:38 +0000 Subject: [PATCH 028/107] ASoC: SOF: Add PCM operations support Add support for exposing PCMs to userspace. PCMs are defined by topology and the operations in this patch map to SOF IPC calls. Signed-off-by: Liam Girdwood --- sound/soc/sof/pcm.c | 645 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 645 insertions(+) create mode 100644 sound/soc/sof/pcm.c diff --git a/sound/soc/sof/pcm.c b/sound/soc/sof/pcm.c new file mode 100644 index 00000000000000..32d114ee748f7e --- /dev/null +++ b/sound/soc/sof/pcm.c @@ -0,0 +1,645 @@ +// SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause) +/* + * This file is provided under a dual BSD/GPLv2 license. When using or + * redistributing this file, you may do so under either license. + * + * Copyright(c) 2017 Intel Corporation. All rights reserved. + * + * Author: Liam Girdwood + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "sof-priv.h" + +/* Create DMA buffer page table for DSP */ +static int create_page_table(struct snd_pcm_substream *substream, + unsigned char *dma_area, size_t size) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_sof_dev *sdev = + snd_soc_platform_get_drvdata(rtd->platform); + struct snd_sof_pcm *spcm = rtd->sof; + struct snd_dma_buffer *dmab = snd_pcm_get_dma_buf(substream); + int stream = substream->stream; + + return snd_sof_create_page_table(sdev, dmab, + spcm->stream[stream].page_table.area, size); +} + +/* this may get called several times by oss emulation */ +static int sof_pcm_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_pcm_runtime *runtime = substream->runtime; + struct snd_sof_dev *sdev = + snd_soc_platform_get_drvdata(rtd->platform); + const struct snd_sof_dsp_ops *ops = sdev->ops; + struct snd_sof_pcm *spcm = rtd->sof; + struct sof_ipc_pcm_params pcm; + struct sof_ipc_pcm_params_reply ipc_params_reply; + int posn_offset; + int ret; + + /* nothing todo for BE */ + if (rtd->dai_link->no_pcm) + return 0; + + dev_dbg(sdev->dev, "pcm: hw params stream %d dir %d\n", + spcm->pcm.pcm_id, substream->stream); + + memset(&pcm, 0, sizeof(pcm)); + + /* allocate audio buffer pages */ + ret = snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(params)); + if (ret < 0) { + dev_err(sdev->dev, "error: could not allocate %d bytes for PCM %d\n", + params_buffer_bytes(params), ret); + return ret; + } + + /* craete compressed page table for audio firmware */ + ret = create_page_table(substream, runtime->dma_area, + runtime->dma_bytes); + if (ret < 0) + return ret; + + /* number of pages should be rounded up */ + if (runtime->dma_bytes % PAGE_SIZE) + pcm.params.buffer.pages = (runtime->dma_bytes / PAGE_SIZE) + 1; + else + pcm.params.buffer.pages = runtime->dma_bytes / PAGE_SIZE; + + /* set IPC PCM parameters */ + pcm.hdr.size = sizeof(pcm); + pcm.hdr.cmd = SOF_IPC_GLB_STREAM_MSG | SOF_IPC_STREAM_PCM_PARAMS; + pcm.comp_id = spcm->stream[substream->stream].comp_id; + pcm.params.buffer.phy_addr = + spcm->stream[substream->stream].page_table.addr; + pcm.params.buffer.size = runtime->dma_bytes; + pcm.params.buffer.offset = 0; + pcm.params.direction = substream->stream; + pcm.params.sample_valid_bytes = params_width(params) >> 3; + pcm.params.buffer_fmt = SOF_IPC_BUFFER_INTERLEAVED; + pcm.params.rate = params_rate(params); + pcm.params.channels = params_channels(params); + pcm.params.host_period_bytes = params_period_bytes(params); + + /* container size */ + switch (params_width(params)) { + case 16: + pcm.params.sample_container_bytes = 2; + break; + case 24: + pcm.params.sample_container_bytes = 4; + break; + case 32: + pcm.params.sample_container_bytes = 4; + break; + default: + return -EINVAL; + } + + /* format */ + switch (params_format(params)) { + case SNDRV_PCM_FORMAT_S16: + pcm.params.frame_fmt = SOF_IPC_FRAME_S16_LE; + break; + case SNDRV_PCM_FORMAT_S24: + pcm.params.frame_fmt = SOF_IPC_FRAME_S24_4LE; + break; + case SNDRV_PCM_FORMAT_S32: + pcm.params.frame_fmt = SOF_IPC_FRAME_S32_LE; + break; + case SNDRV_PCM_FORMAT_FLOAT: + pcm.params.frame_fmt = SOF_IPC_FRAME_FLOAT; + break; + default: + return -EINVAL; + } + + /* firmware already configured host stream */ + if (ops && ops->host_stream_hw_params) { + pcm.params.stream_tag = + ops->host_stream_hw_params(sdev, substream, params); + dev_dbg(sdev->dev, "stream_tag %d", pcm.params.stream_tag); + } + + /* send IPC to the DSP */ + ret = sof_ipc_tx_message(sdev->ipc, pcm.hdr.cmd, &pcm, sizeof(pcm), + &ipc_params_reply, sizeof(ipc_params_reply)); + + /* validate offset */ + posn_offset = ipc_params_reply.posn_offset; + + /* check if offset is overflow or it is not aligned */ + if (posn_offset > sdev->stream_box.size || + posn_offset % sizeof(struct sof_ipc_stream_posn) != 0) { + dev_err(sdev->dev, "error: got wrong posn offset 0x%x for PCM %d\n", + posn_offset, ret); + return ret; + } + spcm->posn_offset[substream->stream] = + sdev->stream_box.offset + posn_offset; + + return ret; +} + +static int sof_pcm_hw_free(struct snd_pcm_substream *substream) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_sof_dev *sdev = + snd_soc_platform_get_drvdata(rtd->platform); + struct snd_sof_pcm *spcm = rtd->sof; + struct sof_ipc_stream stream; + struct sof_ipc_reply reply; + int ret; + + /* nothing todo for BE */ + if (rtd->dai_link->no_pcm) + return 0; + + dev_dbg(sdev->dev, "pcm: free stream %d dir %d\n", spcm->pcm.pcm_id, + substream->stream); + + stream.hdr.size = sizeof(stream); + stream.hdr.cmd = SOF_IPC_GLB_STREAM_MSG | SOF_IPC_STREAM_PCM_FREE; + stream.comp_id = spcm->stream[substream->stream].comp_id; + + /* send IPC to the DSP */ + ret = sof_ipc_tx_message(sdev->ipc, stream.hdr.cmd, &stream, + sizeof(stream), &reply, sizeof(reply)); + + snd_pcm_lib_free_pages(substream); + return ret; +} + +static int sof_pcm_trigger(struct snd_pcm_substream *substream, int cmd) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_sof_dev *sdev = + snd_soc_platform_get_drvdata(rtd->platform); + struct snd_sof_pcm *spcm = rtd->sof; + struct sof_ipc_stream stream; + struct sof_ipc_reply reply; + const struct snd_sof_dsp_ops *ops = sdev->ops; + int ret = 0; + + /* nothing todo for BE */ + if (rtd->dai_link->no_pcm) + return 0; + + dev_dbg(sdev->dev, "pcm: trigger stream %d dir %d cmd %d\n", + spcm->pcm.pcm_id, substream->stream, cmd); + + stream.hdr.size = sizeof(stream); + stream.hdr.cmd = SOF_IPC_GLB_STREAM_MSG; + stream.comp_id = spcm->stream[substream->stream].comp_id; + + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + stream.hdr.cmd |= SOF_IPC_STREAM_TRIG_START; + break; + case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: + stream.hdr.cmd |= SOF_IPC_STREAM_TRIG_RELEASE; + break; + case SNDRV_PCM_TRIGGER_STOP: + stream.hdr.cmd |= SOF_IPC_STREAM_TRIG_STOP; + break; + case SNDRV_PCM_TRIGGER_PAUSE_PUSH: + stream.hdr.cmd |= SOF_IPC_STREAM_TRIG_PAUSE; + break; + case SNDRV_PCM_TRIGGER_RESUME: + case SNDRV_PCM_TRIGGER_SUSPEND: + break; + default: + dev_err(sdev->dev, "error: unhandled trigger cmd %d\n", cmd); + return -EINVAL; + } + + /* send IPC to the DSP */ + ret = sof_ipc_tx_message(sdev->ipc, stream.hdr.cmd, &stream, + sizeof(stream), &reply, sizeof(reply)); + + if (ops && ops->host_stream_trigger) + ret = ops->host_stream_trigger(sdev, substream, cmd); + + return ret; +} + +static snd_pcm_uframes_t sof_pcm_pointer(struct snd_pcm_substream *substream) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_sof_dev *sdev = + snd_soc_platform_get_drvdata(rtd->platform); + struct snd_sof_pcm *spcm = rtd->sof; + snd_pcm_uframes_t host = 0, dai = 0; + + /* nothing todo for BE */ + if (rtd->dai_link->no_pcm) + return 0; + + /* TODO: call HW position callback */ + host = bytes_to_frames(substream->runtime, + spcm->stream[substream->stream].posn.host_posn); + dai = bytes_to_frames(substream->runtime, + spcm->stream[substream->stream].posn.dai_posn); + + dev_dbg(sdev->dev, "PCM: stream %d dir %d DMA position %lu DAI position %lu\n", + spcm->pcm.pcm_id, substream->stream, host, dai); + + return host; +} + +static int sof_pcm_open(struct snd_pcm_substream *substream) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_pcm_runtime *runtime = substream->runtime; + struct snd_sof_dev *sdev = + snd_soc_platform_get_drvdata(rtd->platform); + struct snd_sof_pcm *spcm = rtd->sof; + struct snd_soc_tplg_stream_caps *caps = + &spcm->pcm.caps[substream->stream]; + const struct snd_sof_dsp_ops *ops = sdev->ops; + + /* nothing todo for BE */ + if (rtd->dai_link->no_pcm) + return 0; + + dev_dbg(sdev->dev, "pcm: open stream %d dir %d\n", spcm->pcm.pcm_id, + substream->stream); + + mutex_lock(&spcm->mutex); + + pm_runtime_get_sync(sdev->dev); + + /* set any runtime constraints based on topology */ + snd_pcm_hw_constraint_step(substream->runtime, 0, + SNDRV_PCM_HW_PARAM_BUFFER_SIZE, + caps->period_size_min); + snd_pcm_hw_constraint_step(substream->runtime, 0, + SNDRV_PCM_HW_PARAM_PERIOD_SIZE, + caps->period_size_min); + + /* set runtime config */ + runtime->hw.info = SNDRV_PCM_INFO_MMAP | + SNDRV_PCM_INFO_MMAP_VALID | + SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_PAUSE | + SNDRV_PCM_INFO_RESUME | + SNDRV_PCM_INFO_NO_PERIOD_WAKEUP; + runtime->hw.formats = caps->formats; + runtime->hw.period_bytes_min = caps->period_size_min; + runtime->hw.period_bytes_max = caps->period_size_max; + runtime->hw.periods_min = caps->periods_min; + runtime->hw.periods_max = caps->periods_max; + runtime->hw.buffer_bytes_max = caps->buffer_size_max; + + dev_dbg(sdev->dev, "period min %zd max %zd bytes\n", + runtime->hw.period_bytes_min, + runtime->hw.period_bytes_max); + dev_dbg(sdev->dev, "period count %d max %d\n", + runtime->hw.periods_min, + runtime->hw.periods_max); + dev_dbg(sdev->dev, "buffer max %zd bytes\n", + runtime->hw.buffer_bytes_max); + + // TODO: create IPC to get this from DSP pipeline + //runtime->hw.fifo_size = hw->fifo_size; + + /* set wait time - TODO: come from topology */ + snd_pcm_wait_time(substream, 500); + + spcm->stream[substream->stream].posn.host_posn = 0; + spcm->stream[substream->stream].posn.dai_posn = 0; + spcm->stream[substream->stream].substream = substream; + + if (ops && ops->host_stream_open) + ops->host_stream_open(sdev, substream); + + mutex_unlock(&spcm->mutex); + return 0; +} + +static int sof_pcm_close(struct snd_pcm_substream *substream) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_sof_dev *sdev = + snd_soc_platform_get_drvdata(rtd->platform); + struct snd_sof_pcm *spcm = rtd->sof; + const struct snd_sof_dsp_ops *ops = sdev->ops; + + /* nothing todo for BE */ + if (rtd->dai_link->no_pcm) + return 0; + + dev_dbg(sdev->dev, "pcm: close stream %d dir %d\n", spcm->pcm.pcm_id, + substream->stream); + + if (ops && ops->host_stream_close) + ops->host_stream_close(sdev, substream); + + mutex_lock(&spcm->mutex); + pm_runtime_mark_last_busy(sdev->dev); + pm_runtime_put_autosuspend(sdev->dev); + mutex_unlock(&spcm->mutex); + return 0; +} + +static struct snd_pcm_ops sof_pcm_ops = { + .open = sof_pcm_open, + .close = sof_pcm_close, + .ioctl = snd_pcm_lib_ioctl, + .hw_params = sof_pcm_hw_params, + .hw_free = sof_pcm_hw_free, + .trigger = sof_pcm_trigger, + .pointer = sof_pcm_pointer, + .page = snd_pcm_sgbuf_ops_page, +}; + +static int sof_pcm_new(struct snd_soc_pcm_runtime *rtd) +{ + struct snd_sof_dev *sdev = + snd_soc_platform_get_drvdata(rtd->platform); + struct snd_sof_pcm *spcm; + struct snd_pcm *pcm = rtd->pcm; + struct snd_soc_tplg_stream_caps *caps; + int ret = 0, stream = SNDRV_PCM_STREAM_PLAYBACK; + + spcm = snd_sof_find_spcm_dai(sdev, rtd); + + if (!spcm) { + dev_warn(sdev->dev, "warn: can't find PCM with DAI ID %d\n", + rtd->dai_link->id); + return 0; + } + rtd->sof = spcm; + + dev_dbg(sdev->dev, "creating new PCM %s\n", spcm->pcm.pcm_name); + + /* do we need to allocate playback PCM DMA pages */ + if (!spcm->pcm.playback) + goto capture; + + caps = &spcm->pcm.caps[stream]; + + /* pre-allocate playback audio buffer pages */ + dev_dbg(sdev->dev, "spcm: allocate %s playback DMA buffer size 0x%x max 0x%x\n", + caps->name, caps->buffer_size_min, caps->buffer_size_max); + + ret = snd_pcm_lib_preallocate_pages(pcm->streams[stream].substream, + SNDRV_DMA_TYPE_DEV_SG, sdev->parent, + caps->buffer_size_min, + caps->buffer_size_max); + if (ret) { + dev_err(sdev->dev, "error: can't alloc DMA buffer size 0x%x/0x%x for %s %d\n", + caps->buffer_size_min, caps->buffer_size_max, + caps->name, ret); + return ret; + } + + /* allocate playback page table buffer */ + ret = snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, sdev->parent, + PAGE_SIZE, &spcm->stream[stream].page_table); + if (ret < 0) { + dev_err(sdev->dev, "error: can't alloc page table for %s %d\n", + caps->name, ret); + return ret; + } + +capture: + stream = SNDRV_PCM_STREAM_CAPTURE; + + /* do we need to allocate capture PCM DMA pages */ + if (!spcm->pcm.capture) + return ret; + + caps = &spcm->pcm.caps[stream]; + + /* pre-allocate capture audio buffer pages */ + dev_dbg(sdev->dev, "spcm: allocate %s capture DMA buffer size 0x%x max 0x%x\n", + caps->name, caps->buffer_size_min, caps->buffer_size_max); + + ret = snd_pcm_lib_preallocate_pages(pcm->streams[stream].substream, + SNDRV_DMA_TYPE_DEV_SG, sdev->parent, + caps->buffer_size_min, + caps->buffer_size_max); + if (ret) { + dev_err(sdev->dev, "error: can't alloc DMA buffer size 0x%x/0x%x for %s %d\n", + caps->buffer_size_min, caps->buffer_size_max, + caps->name, ret); + snd_dma_free_pages(&spcm->stream[stream].page_table); + return ret; + } + + /* allocate capture page table buffer */ + ret = snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, sdev->parent, + PAGE_SIZE, &spcm->stream[stream].page_table); + if (ret < 0) { + dev_err(sdev->dev, "error: can't alloc page table for %s %d\n", + caps->name, ret); + snd_dma_free_pages(&spcm->stream[stream].page_table); + return ret; + } + + /* TODO: assign channel maps from topology */ + + return ret; +} + +static void sof_pcm_free(struct snd_pcm *pcm) +{ + struct snd_sof_pcm *spcm; + struct snd_soc_pcm_runtime *rtd = pcm->private_data; + struct snd_sof_dev *sdev = + snd_soc_platform_get_drvdata(rtd->platform); + + spcm = snd_sof_find_spcm_dai(sdev, rtd); + if (!spcm) { + dev_warn(sdev->dev, "warn: can't find PCM with DAI ID %d\n", + rtd->dai_link->id); + return; + } + + if (spcm->pcm.playback) + snd_dma_free_pages(&spcm->stream[SNDRV_PCM_STREAM_PLAYBACK].page_table); + + if (spcm->pcm.capture) + snd_dma_free_pages(&spcm->stream[SNDRV_PCM_STREAM_CAPTURE].page_table); + + snd_sof_free_topology(sdev); +} + +static int sof_pcm_dai_link_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); + struct snd_sof_dev *sdev = + snd_soc_platform_get_drvdata(rtd->platform); + struct snd_sof_dai *dai = + snd_sof_find_dai(sdev, (char *)rtd->dai_link->name); + + if (!dai) { + dev_err(sdev->dev, "No DAI is found!\n"); + + /* set 48k, stereo, 16bits by default */ + rate->min = 48000; + rate->max = 48000; + + channels->min = 2; + channels->max = 2; + + snd_mask_none(fmt); + snd_mask_set(fmt, SNDRV_PCM_FORMAT_S16_LE); + + return -EINVAL; + } + + /* read format from topology */ + snd_mask_none(fmt); + + switch (dai->comp_dai.config.frame_fmt) { + case SOF_IPC_FRAME_S16_LE: + snd_mask_set(fmt, SNDRV_PCM_FORMAT_S16_LE); + break; + case SOF_IPC_FRAME_S24_4LE: + snd_mask_set(fmt, SNDRV_PCM_FORMAT_S24_LE); + break; + case SOF_IPC_FRAME_S32_LE: + snd_mask_set(fmt, SNDRV_PCM_FORMAT_S32_LE); + break; + default: + dev_err(sdev->dev, "No available DAI format!\n"); + return -EINVAL; + } + + /* read rate and channels from topology */ + switch (dai->dai_config.type) { + case SOF_DAI_INTEL_SSP: + rate->min = dai->dai_config.ssp.fsync_rate; + rate->max = dai->dai_config.ssp.fsync_rate; + channels->min = dai->dai_config.ssp.tdm_slots; + channels->max = dai->dai_config.ssp.tdm_slots; + + dev_dbg(sdev->dev, + "rate_min: %d rate_max: %d\n", rate->min, rate->max); + dev_dbg(sdev->dev, + "channels_min: %d channels_max: %d\n", + channels->min, channels->max); + + break; + case SOF_DAI_INTEL_DMIC: + /* fallthrough */ + case SOF_DAI_INTEL_HDA: + /* fallthrough */ + default: + dev_err(sdev->dev, "error: invalid DAI type %d\n", + dai->dai_config.type); + break; + } + + return 0; +} + +static int sof_pcm_probe(struct snd_soc_platform *platform) +{ + struct snd_sof_dev *sdev = + snd_soc_platform_get_drvdata(platform); + struct snd_sof_pdata *plat_data = dev_get_platdata(platform->dev); + int ret; + + /* load the default topology */ + sdev->component = &platform->component; + ret = snd_sof_load_topology(sdev, + plat_data->machine->sof_tplg_filename); + if (ret < 0) { + dev_err(sdev->dev, "error: failed to load DSP topology %d\n", + ret); + goto err; + } + + /* enable runtime PM with auto suspend */ + pm_runtime_set_autosuspend_delay(platform->dev, + SND_SOF_SUSPEND_DELAY); + pm_runtime_use_autosuspend(platform->dev); + pm_runtime_enable(platform->dev); + pm_runtime_idle(platform->dev); + +err: + return ret; +} + +static int sof_pcm_remove(struct snd_soc_platform *platform) +{ + pm_runtime_disable(platform->dev); + + return 0; +} + +void snd_sof_new_platform_drv(struct snd_sof_dev *sdev) +{ + struct snd_soc_platform_driver *pd = &sdev->plat_drv; + struct snd_sof_pdata *plat_data = sdev->pdata; + + dev_dbg(sdev->dev, "using platform alias %s\n", + plat_data->machine->asoc_plat_name); + + pd->probe = sof_pcm_probe; + pd->remove = sof_pcm_remove; + pd->ops = &sof_pcm_ops; + pd->compr_ops = &sof_compressed_ops; + pd->pcm_new = sof_pcm_new; + pd->pcm_free = sof_pcm_free; + pd->ignore_machine = plat_data->machine->drv_name; + pd->be_hw_params_fixup = sof_pcm_dai_link_fixup; + pd->be_pcm_base = SOF_BE_PCM_BASE; + pd->use_dai_pcm_id = true; + pd->topology_name_prefix = "sof"; +} + +static const struct snd_soc_dai_ops sof_dai_ops = { +}; + +static const struct snd_soc_component_driver sof_dai_component = { + .name = "sof-dai", +}; + +#define SOF_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE | \ + SNDRV_PCM_FMTBIT_S32_LE | SNDRV_PCM_FMTBIT_FLOAT) + +void snd_sof_new_dai_drv(struct snd_sof_dev *sdev) +{ + struct snd_soc_dai_driver *dd = &sdev->dai_drv; + //struct snd_sof_pdata *plat_data = sdev->pdata; + + sdev->cmpnt_drv = &sof_dai_component; + dd->playback.channels_min = 1; + dd->playback.channels_max = 16; + dd->playback.rates = SNDRV_PCM_RATE_8000_192000; + dd->playback.formats = SOF_FORMATS; + dd->capture.channels_min = 1; + dd->capture.channels_max = 16; + dd->capture.rates = SNDRV_PCM_RATE_8000_192000; + dd->capture.formats = SOF_FORMATS; + dd->ops = &sof_dai_ops; + sdev->num_dai = 1; +} From 95545bc033178c58ea641e67ce5c873916caa6e2 Mon Sep 17 00:00:00 2001 From: Liam Girdwood Date: Mon, 8 Jan 2018 20:33:05 +0000 Subject: [PATCH 029/107] ASoC: SOF: Add support for loading topologies SOF uses topology to define the DAPM graphs and widgets, DAIs, PCMs and set parameters for init and run time usage. This patch loads topology and maps it to IPC commands that are build the topology on the DSP. Signed-off-by: Liam Girdwood --- include/uapi/sound/sof-topology.h | 76 ++ sound/soc/sof/sof-priv.h | 1 + sound/soc/sof/topology.c | 1825 +++++++++++++++++++++++++++++ 3 files changed, 1902 insertions(+) create mode 100644 include/uapi/sound/sof-topology.h create mode 100644 sound/soc/sof/topology.c diff --git a/include/uapi/sound/sof-topology.h b/include/uapi/sound/sof-topology.h new file mode 100644 index 00000000000000..62c3f242509701 --- /dev/null +++ b/include/uapi/sound/sof-topology.h @@ -0,0 +1,76 @@ +/* SPDX-License-Identifier: ((GPL-2.0 WITH Linux-syscall-note)) OR BSD-3-Clause) */ +/* + * This file is provided under a dual BSD/GPLv2 license. When using or + * redistributing this file, you may do so under either license. + * + * Copyright(c) 2017 Intel Corporation. All rights reserved. + * + * Author: Liam Girdwood + * Keyon Jie + */ + +/* + * Topology IDs and tokens. + * + * ** MUST BE ALIGNED WITH TOPOLOGY CONFIGURATION TOKEN VALUES ** + */ + +#ifndef __INCLUDE_UAPI_SOF_TOPOLGOY_H__ +#define __INCLUDE_UAPI_SOF_TOPOLOGY_H__ + +/* + * Kcontrol IDs + */ +#define SOF_TPLG_KCTL_VOL_ID 256 +#define SOF_TPLG_KCTL_ENUM_ID 257 +#define SOF_TPLG_KCTL_BYTES_ID 258 + +/* + * Tokens - must match values in topology configurations + */ + +/* buffers */ +#define SOF_TKN_BUF_SIZE 100 +#define SOF_TKN_BUF_CAPS 101 + +/* DAI */ +#define SOF_TKN_DAI_DMAC 151 +#define SOF_TKN_DAI_DMAC_CHAN 152 +#define SOF_TKN_DAI_DMAC_CONFIG 153 +#define SOF_TKN_DAI_TYPE 154 +#define SOF_TKN_DAI_INDEX 155 +#define SOF_TKN_DAI_SAMPLE_BITS 156 + +/* scheduling */ +#define SOF_TKN_SCHED_DEADLINE 200 +#define SOF_TKN_SCHED_PRIORITY 201 +#define SOF_TKN_SCHED_MIPS 202 +#define SOF_TKN_SCHED_CORE 203 +#define SOF_TKN_SCHED_FRAMES 204 +#define SOF_TKN_SCHED_TIMER 205 + +/* volume */ +#define SOF_TKN_VOLUME_RAMP_STEP_TYPE 250 +#define SOF_TKN_VOLUME_RAMP_STEP_MS 251 + +/* SRC */ +#define SOF_TKN_SRC_RATE_IN 300 +#define SOF_TKN_SRC_RATE_OUT 301 + +/* PCM */ +#define SOF_TKN_PCM_DMAC 351 +#define SOF_TKN_PCM_DMAC_CHAN 352 +#define SOF_TKN_PCM_DMAC_CONFIG 353 + +/* Generic components */ +#define SOF_TKN_COMP_PERIOD_SINK_COUNT 400 +#define SOF_TKN_COMP_PERIOD_SOURCE_COUNT 401 +#define SOF_TKN_COMP_FORMAT 402 +#define SOF_TKN_COMP_PRELOAD_COUNT 403 + +/* SSP */ +#define SOF_TKN_INTEL_SSP_MCLK_KEEP_ACTIVE 500 +#define SOF_TKN_INTEL_SSP_BCLK_KEEP_ACTIVE 501 +#define SOF_TKN_INTEL_SSP_FS_KEEP_ACTIVE 502 + +#endif diff --git a/sound/soc/sof/sof-priv.h b/sound/soc/sof/sof-priv.h index 0f9b659461d94e..bde985f6f438be 100644 --- a/sound/soc/sof/sof-priv.h +++ b/sound/soc/sof/sof-priv.h @@ -185,6 +185,7 @@ struct snd_sof_control { struct sof_ipc_ctrl_data *control_data; u32 size; /* cdata size */ enum sof_ipc_ctrl_cmd cmd; + u32 *volume_table; /* volume table computed from tlv data*/ struct mutex mutex; struct list_head list; /* list in sdev control list */ diff --git a/sound/soc/sof/topology.c b/sound/soc/sof/topology.c new file mode 100644 index 00000000000000..5fe74fa41e423f --- /dev/null +++ b/sound/soc/sof/topology.c @@ -0,0 +1,1825 @@ +// SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause) +/* + * This file is provided under a dual BSD/GPLv2 license. When using or + * redistributing this file, you may do so under either license. + * + * Copyright(c) 2017 Intel Corporation. All rights reserved. + * + * Author: Liam Girdwood + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "sof-priv.h" + +#define COMP_ID_UNASSIGNED 0xffffffff +/* Constants used in the computation of linear volume gain from dB gain */ +/* 20th root of 10 in Q1.16 fixed-point notation*/ +#define VOL_TWENTIETH_ROOT_OF_TEN 73533 +/* 40th root of 10 in Q1.16 fixed-point notation*/ +#define VOL_FORTIETH_ROOT_OF_TEN 69419 +/* Volume fractional word length */ +#define VOLUME_FWL 16 +/* 0.5 dB step value in topology TLV */ +#define VOL_HALF_DB_STEP 50 + +/* TLV data items */ +#define TLV_ITEMS 3 +#define TLV_MIN 0 +#define TLV_STEP 1 +#define TLV_MUTE 2 + +static inline int get_tlv_data(const int *p, int tlv[TLV_ITEMS]) +{ + /* we only support dB scale TLV type at the moment */ + if ((int)p[SNDRV_CTL_TLVO_TYPE] != SNDRV_CTL_TLVT_DB_SCALE) + return -EINVAL; + + /* min value in topology tlv data is multiplied by 100 */ + tlv[TLV_MIN] = (int)p[SNDRV_CTL_TLVO_DB_SCALE_MIN] / 100; + + /* volume steps */ + tlv[TLV_STEP] = (int)(p[SNDRV_CTL_TLVO_DB_SCALE_MUTE_AND_STEP] & + TLV_DB_SCALE_MASK); + + /* mute ON/OFF */ + if ((p[SNDRV_CTL_TLVO_DB_SCALE_MUTE_AND_STEP] & + TLV_DB_SCALE_MUTE) == 0) + tlv[TLV_MUTE] = 0; + else + tlv[TLV_MUTE] = 1; + + return 0; +} + +/* Function to truncate an unsigned 64-bit number + * by x bits and return 32-bit unsigned number + * This function also takes care of rounding while truncating + */ +static inline u32 vol_shift_64(u64 i, u32 x) +{ + /* do not truncate more than 32 bits */ + if (x > 32) + x = 32; + + if (x == 0) + return (u32)i; + + return (u32)(((i >> (x - 1)) + 1) >> 1); +} + +/* Function to compute a ^ exp where, + * a is a fractional number represented by a fixed-point integer + * with a fractional world length of "fwl" + * exp is an integer + * fwl is the fractional word length + * Return value is a fractional number represented by a fixed-point + * integer with a fractional word length of "fwl" + */ +static u32 vol_pow32(u32 a, int exp, u32 fwl) +{ + int i, iter; + u32 power = 1 << fwl; + u64 numerator; + + /* if exponent is 0, return 1 */ + if (exp == 0) + return power; + + /* determine the number of iterations based on the exponent */ + if (exp < 0) + iter = exp * -1; + else + iter = exp; + + /* mutiply a "iter" times to compute power */ + for (i = 0; i < iter; i++) { + /* Product of 2 Qx.fwl fixed-point numbers yields a Q2*x.2*fwl + * Truncate product back to fwl fractional bits with rounding + */ + power = vol_shift_64((u64)power * a, fwl); + } + + if (exp > 0) { + /* if exp is positive, return the result */ + return power; + } + + /* if exp is negative, return the multiplicative inverse */ + numerator = (u64)1 << (fwl << 1); + do_div(numerator, power); + + return (u32)numerator; +} + +/* Function to calculate volume gain from TLV data + * This function can only handle gain steps that are multiples of 0.5 dB + */ +static u32 vol_compute_gain(u32 value, int *tlv) +{ + int dB_gain; + u32 linear_gain; + int f_step; + + /* mute volume */ + if (value == 0 && tlv[TLV_MUTE]) + return 0; + + /* compute dB gain from tlv + * tlv_step in topology is multiplied by 100 + */ + dB_gain = tlv[TLV_MIN] + (value * tlv[TLV_STEP]) / 100; + + /* compute linear gain + * represented by fixed-point int with VOLUME_FWL fractional bits + */ + linear_gain = vol_pow32(VOL_TWENTIETH_ROOT_OF_TEN, dB_gain, VOLUME_FWL); + + /* extract the fractional part of volume step */ + f_step = tlv[TLV_STEP] - (tlv[TLV_STEP] / 100); + + /* if volume step is an odd multiple of 0.5 dB */ + if (f_step == VOL_HALF_DB_STEP && (value & 1)) + linear_gain = vol_shift_64((u64)linear_gain * + VOL_FORTIETH_ROOT_OF_TEN, + VOLUME_FWL); + + return linear_gain; +} + +/* Set up volume table for kcontrols from tlv data + * "size" specifies the number of entries in the table + */ +static int set_up_volume_table(struct snd_sof_control *scontrol, + int tlv[TLV_ITEMS], int size) +{ + int j; + + /* init the volume table */ + scontrol->volume_table = kcalloc(size, sizeof(u32), GFP_KERNEL); + if (!scontrol->volume_table) + return -ENOMEM; + + /* populate the volume table */ + for (j = 0; j < size ; j++) + scontrol->volume_table[j] = vol_compute_gain(j, tlv); + + return 0; +} + +struct sof_dai_types { + const char *name; + enum sof_ipc_dai_type type; +}; + +static const struct sof_dai_types sof_dais[] = { + {"SSP", SOF_DAI_INTEL_SSP}, + {"HDA", SOF_DAI_INTEL_HDA}, + {"DMIC", SOF_DAI_INTEL_DMIC}, +}; + +static enum sof_ipc_dai_type find_dai(const char *name) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(sof_dais); i++) { + if (strcmp(name, sof_dais[i].name) == 0) + return sof_dais[i].type; + } + + return SOF_DAI_INTEL_NONE; +} + +struct sof_frame_types { + const char *name; + enum sof_ipc_frame frame; +}; + +static const struct sof_frame_types sof_frames[] = { + {"s16le", SOF_IPC_FRAME_S16_LE}, + {"s24le", SOF_IPC_FRAME_S24_4LE}, + {"s32le", SOF_IPC_FRAME_S32_LE}, + {"float", SOF_IPC_FRAME_FLOAT}, +}; + +static enum sof_ipc_dai_type find_format(const char *name) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(sof_frames); i++) { + if (strcmp(name, sof_frames[i].name) == 0) + return sof_frames[i].frame; + } + + /* use s32le if nothing is specified */ + return SOF_IPC_FRAME_S32_LE; +} + +/* + * Standard Kcontrols. + */ + +static int sof_control_load_volume(struct snd_soc_component *scomp, + struct snd_sof_control *scontrol, + struct snd_kcontrol_new *kc, + struct snd_soc_tplg_ctl_hdr *hdr) +{ + struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp); + struct snd_soc_tplg_mixer_control *mc = + (struct snd_soc_tplg_mixer_control *)hdr; + struct sof_ipc_ctrl_data *cdata; + + /* validate topology data */ + if (mc->num_channels >= SND_SOC_TPLG_MAX_CHAN) + return -EINVAL; + + /* init the volume get/put data */ + scontrol->size = sizeof(struct sof_ipc_ctrl_data) + + sizeof(struct sof_ipc_ctrl_value_chan) * mc->num_channels; + scontrol->control_data = kzalloc(scontrol->size, GFP_KERNEL); + cdata = scontrol->control_data; + if (!scontrol->control_data) + return -ENOMEM; + + scontrol->comp_id = sdev->next_comp_id; + scontrol->num_channels = mc->num_channels; + + dev_dbg(sdev->dev, "tplg: load kcontrol index %d chans %d\n", + scontrol->comp_id, scontrol->num_channels); + + return 0; + /* configure channel IDs */ + //for (i = 0; i < mc->num_channels; i++) { + // v.pcm.chmap[i] = mc->channel[i].id; + //} +} + +struct sof_topology_token { + u32 token; + u32 type; + int (*get_token)(void *elem, void *object, u32 offset, u32 size); + u32 offset; + u32 size; +}; + +static int get_token_u32(void *elem, void *object, u32 offset, u32 size) +{ + struct snd_soc_tplg_vendor_value_elem *velem = elem; + u32 *val = object + offset; + + *val = velem->value; + return 0; +} + +static int get_token_comp_format(void *elem, void *object, u32 offset, u32 size) +{ + struct snd_soc_tplg_vendor_string_elem *velem = elem; + u32 *val = object + offset; + + *val = find_format(velem->string); + return 0; +} + +static int get_token_dai_type(void *elem, void *object, u32 offset, u32 size) +{ + struct snd_soc_tplg_vendor_string_elem *velem = elem; + u32 *val = object + offset; + + *val = find_dai(velem->string); + return 0; +} + +/* Buffers */ +static const struct sof_topology_token buffer_tokens[] = { + {SOF_TKN_BUF_SIZE, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32, + offsetof(struct sof_ipc_buffer, size), 0}, + {SOF_TKN_BUF_CAPS, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32, + offsetof(struct sof_ipc_buffer, caps), 0}, +}; + +/* DAI */ +static const struct sof_topology_token dai_tokens[] = { + {SOF_TKN_DAI_DMAC, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32, + offsetof(struct sof_ipc_comp_dai, dmac_id), 0}, + {SOF_TKN_DAI_DMAC_CHAN, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32, + offsetof(struct sof_ipc_comp_dai, dmac_chan), 0}, + {SOF_TKN_DAI_DMAC_CONFIG, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32, + offsetof(struct sof_ipc_comp_dai, dmac_config), 0}, + {SOF_TKN_DAI_TYPE, SND_SOC_TPLG_TUPLE_TYPE_STRING, get_token_dai_type, + offsetof(struct sof_ipc_comp_dai, type), 0}, + {SOF_TKN_DAI_INDEX, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32, + offsetof(struct sof_ipc_comp_dai, index), 0}, +}; + +/* BE DAI link */ +static const struct sof_topology_token dai_link_tokens[] = { + {SOF_TKN_DAI_TYPE, SND_SOC_TPLG_TUPLE_TYPE_STRING, get_token_dai_type, + offsetof(struct sof_ipc_dai_config, type), 0}, +}; + +static const struct sof_topology_token dai_ssp_link_tokens[] = { + {SOF_TKN_DAI_SAMPLE_BITS, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32, + offsetof(struct sof_ipc_dai_ssp_params, sample_valid_bits), 0}, +}; + +/* scheduling */ +static const struct sof_topology_token sched_tokens[] = { + {SOF_TKN_SCHED_DEADLINE, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32, + offsetof(struct sof_ipc_pipe_new, deadline), 0}, + {SOF_TKN_SCHED_PRIORITY, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32, + offsetof(struct sof_ipc_pipe_new, priority), 0}, + {SOF_TKN_SCHED_MIPS, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32, + offsetof(struct sof_ipc_pipe_new, mips), 0}, + {SOF_TKN_SCHED_CORE, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32, + offsetof(struct sof_ipc_pipe_new, core), 0}, + {SOF_TKN_SCHED_FRAMES, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32, + offsetof(struct sof_ipc_pipe_new, frames_per_sched), 0}, + {SOF_TKN_SCHED_TIMER, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32, + offsetof(struct sof_ipc_pipe_new, timer), 0}, +}; + +/* volume */ +static const struct sof_topology_token volume_tokens[] = { + {SOF_TKN_VOLUME_RAMP_STEP_TYPE, SND_SOC_TPLG_TUPLE_TYPE_WORD, + get_token_u32, offsetof(struct sof_ipc_comp_volume, ramp), 0}, + {SOF_TKN_VOLUME_RAMP_STEP_MS, + SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32, + offsetof(struct sof_ipc_comp_volume, initial_ramp), 0}, +}; + +/* SRC */ +static const struct sof_topology_token src_tokens[] = { + {SOF_TKN_SRC_RATE_IN, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32, + offsetof(struct sof_ipc_comp_src, source_rate), 0}, + {SOF_TKN_SRC_RATE_OUT, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32, + offsetof(struct sof_ipc_comp_src, sink_rate), 0}, +}; + +/* Tone */ +static const struct sof_topology_token tone_tokens[] = { +}; + +/* PCM */ +static const struct sof_topology_token pcm_tokens[] = { + {SOF_TKN_PCM_DMAC, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32, + offsetof(struct sof_ipc_comp_host, dmac_id), 0}, + {SOF_TKN_PCM_DMAC_CHAN, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32, + offsetof(struct sof_ipc_comp_host, dmac_chan), 0}, + {SOF_TKN_PCM_DMAC_CONFIG, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32, + offsetof(struct sof_ipc_comp_host, dmac_config), 0}, +}; + +/* Generic components */ +static const struct sof_topology_token comp_tokens[] = { + {SOF_TKN_COMP_PERIOD_SINK_COUNT, + SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32, + offsetof(struct sof_ipc_comp_config, periods_sink), 0}, + {SOF_TKN_COMP_PERIOD_SOURCE_COUNT, + SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32, + offsetof(struct sof_ipc_comp_config, periods_source), 0}, + {SOF_TKN_COMP_FORMAT, + SND_SOC_TPLG_TUPLE_TYPE_STRING, get_token_comp_format, + offsetof(struct sof_ipc_comp_config, frame_fmt), 0}, + {SOF_TKN_COMP_PRELOAD_COUNT, + SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32, + offsetof(struct sof_ipc_comp_config, preload_count), 0}, +}; + +/* SSP */ +static const struct sof_topology_token ssp_tokens[] = { + {SOF_TKN_INTEL_SSP_MCLK_KEEP_ACTIVE, + SND_SOC_TPLG_TUPLE_TYPE_BOOL, get_token_u32, + offsetof(struct sof_ipc_dai_ssp_params, mclk_keep_active), 0}, + {SOF_TKN_INTEL_SSP_BCLK_KEEP_ACTIVE, + SND_SOC_TPLG_TUPLE_TYPE_BOOL, get_token_u32, + offsetof(struct sof_ipc_dai_ssp_params, bclk_keep_active), 0}, + {SOF_TKN_INTEL_SSP_FS_KEEP_ACTIVE, + SND_SOC_TPLG_TUPLE_TYPE_BOOL, get_token_u32, + offsetof(struct sof_ipc_dai_ssp_params, fs_keep_active), 0}, +}; + +/* DMIC */ +static const struct sof_topology_token dmic_tokens[] = { +}; + +/* HDA */ +static const struct sof_topology_token hda_tokens[] = { +}; + +static void sof_parse_uuid_tokens(struct snd_soc_component *scomp, + void *object, + const struct sof_topology_token *tokens, + int count, + struct snd_soc_tplg_vendor_array *array) +{ + struct snd_soc_tplg_vendor_uuid_elem *elem; + int i, j; + + /* parse element by element */ + for (i = 0; i < array->num_elems; i++) { + elem = &array->uuid[i]; + + /* search for token */ + for (j = 0; j < count; j++) { + /* match token type */ + if (tokens[j].type != SND_SOC_TPLG_TUPLE_TYPE_UUID) + continue; + + /* match token id */ + if (tokens[j].token != elem->token) + continue; + + /* matched - now load token */ + tokens[j].get_token(elem, object, tokens[j].offset, + tokens[j].size); + } + } +} + +static void sof_parse_string_tokens(struct snd_soc_component *scomp, + void *object, + const struct sof_topology_token *tokens, + int count, + struct snd_soc_tplg_vendor_array *array) +{ + struct snd_soc_tplg_vendor_string_elem *elem; + int i, j; + + /* parse element by element */ + for (i = 0; i < array->num_elems; i++) { + elem = &array->string[i]; + + /* search for token */ + for (j = 0; j < count; j++) { + /* match token type */ + if (tokens[j].type != SND_SOC_TPLG_TUPLE_TYPE_STRING) + continue; + + /* match token id */ + if (tokens[j].token != elem->token) + continue; + + /* matched - now load token */ + tokens[j].get_token(elem, object, tokens[j].offset, + tokens[j].size); + } + } +} + +static void sof_parse_word_tokens(struct snd_soc_component *scomp, + void *object, + const struct sof_topology_token *tokens, + int count, + struct snd_soc_tplg_vendor_array *array) +{ + struct snd_soc_tplg_vendor_value_elem *elem; + int i, j; + + /* parse element by element */ + for (i = 0; i < array->num_elems; i++) { + elem = &array->value[i]; + + /* search for token */ + for (j = 0; j < count; j++) { + /* match token type */ + if (tokens[j].type != SND_SOC_TPLG_TUPLE_TYPE_WORD) + continue; + + /* match token id */ + if (tokens[j].token != elem->token) + continue; + + /* matched - now load token */ + tokens[j].get_token(elem, object, tokens[j].offset, + tokens[j].size); + } + } +} + +static int sof_parse_tokens(struct snd_soc_component *scomp, + void *object, + const struct sof_topology_token *tokens, + int count, + struct snd_soc_tplg_vendor_array *array, + int priv_size) +{ + struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp); + int asize; + + while (priv_size > 0) { + asize = array->size; + + /* validate asize */ + if (asize < 0) { /* FIXME: A zero-size array makes no sense */ + dev_err(sdev->dev, "error: invalid array size 0x%x\n", + asize); + return -EINVAL; + } + + /* make sure there is enough data before parsing */ + priv_size -= asize; + if (priv_size < 0) { + dev_err(sdev->dev, "error: invalid array size 0x%x\n", + asize); + return -EINVAL; + } + + /* call correct parser depending on type */ + switch (array->type) { + case SND_SOC_TPLG_TUPLE_TYPE_UUID: + sof_parse_uuid_tokens(scomp, object, tokens, count, + array); + break; + case SND_SOC_TPLG_TUPLE_TYPE_STRING: + sof_parse_string_tokens(scomp, object, tokens, count, + array); + break; + case SND_SOC_TPLG_TUPLE_TYPE_BOOL: + case SND_SOC_TPLG_TUPLE_TYPE_BYTE: + case SND_SOC_TPLG_TUPLE_TYPE_WORD: + case SND_SOC_TPLG_TUPLE_TYPE_SHORT: + sof_parse_word_tokens(scomp, object, tokens, count, + array); + break; + default: + dev_err(sdev->dev, "error: unknown token type %d\n", + array->type); + return -EINVAL; + } + + /* next array */ + array = (void *)array + asize; + } + return 0; +} + +static void sof_dbg_comp_config(struct snd_soc_component *scomp, + struct sof_ipc_comp_config *config) +{ + struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp); + + dev_dbg(sdev->dev, " config: periods snk %d src %d fmt %d\n", + config->periods_sink, config->periods_source, + config->frame_fmt); +} + +/* external kcontrol init - used for any driver specific init */ +static int sof_control_load(struct snd_soc_component *scomp, int index, + struct snd_kcontrol_new *kc, + struct snd_soc_tplg_ctl_hdr *hdr) +{ + struct soc_mixer_control *sm; + struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp); + struct snd_soc_dobj *dobj = NULL; + struct snd_sof_control *scontrol; + int ret = -EINVAL; + + dev_dbg(sdev->dev, "tplg: load control type %d name : %s\n", + hdr->type, hdr->name); + + scontrol = kzalloc(sizeof(*scontrol), GFP_KERNEL); + if (!scontrol) + return -ENOMEM; + + scontrol->sdev = sdev; + mutex_init(&scontrol->mutex); + + switch (hdr->ops.info) { + case SND_SOC_TPLG_CTL_VOLSW: + case SND_SOC_TPLG_CTL_VOLSW_SX: + case SND_SOC_TPLG_CTL_VOLSW_XR_SX: + sm = (struct soc_mixer_control *)kc->private_value; + dobj = &sm->dobj; + ret = sof_control_load_volume(scomp, scontrol, kc, hdr); + break; + case SND_SOC_TPLG_CTL_ENUM: + case SND_SOC_TPLG_CTL_BYTES: + case SND_SOC_TPLG_CTL_ENUM_VALUE: + case SND_SOC_TPLG_CTL_RANGE: + case SND_SOC_TPLG_CTL_STROBE: + case SND_SOC_TPLG_DAPM_CTL_VOLSW: + case SND_SOC_TPLG_DAPM_CTL_ENUM_DOUBLE: + case SND_SOC_TPLG_DAPM_CTL_ENUM_VIRT: + case SND_SOC_TPLG_DAPM_CTL_ENUM_VALUE: + case SND_SOC_TPLG_DAPM_CTL_PIN: + default: + dev_warn(sdev->dev, "control type not supported %d:%d:%d\n", + hdr->ops.get, hdr->ops.put, hdr->ops.info); + return 0; + } + + dobj->private = scontrol; + list_add(&scontrol->list, &sdev->kcontrol_list); + return ret; +} + +static int sof_control_unload(struct snd_soc_component *scomp, + struct snd_soc_dobj *dobj) +{ + struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp); + struct sof_ipc_free fcomp; + struct snd_sof_control *scontrol = dobj->private; + + dev_dbg(sdev->dev, "tplg: unload control name : %s\n", scomp->name); + + fcomp.hdr.cmd = SOF_IPC_GLB_TPLG_MSG | SOF_IPC_TPLG_COMP_FREE; + fcomp.hdr.size = sizeof(fcomp); + fcomp.id = scontrol->comp_id; + + /* send IPC to the DSP */ + return sof_ipc_tx_message(sdev->ipc, + fcomp.hdr.cmd, &fcomp, sizeof(fcomp), + NULL, 0); +} + +/* + * DAI Topology + */ + +static int sof_connect_dai_widget(struct snd_soc_component *scomp, + struct snd_soc_dapm_widget *w, + struct snd_soc_tplg_dapm_widget *tw, + struct snd_sof_dai *dai) +{ + struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp); + struct snd_soc_card *card = scomp->card; + struct snd_soc_pcm_runtime *rtd; + + list_for_each_entry(rtd, &card->rtd_list, list) { + dev_dbg(sdev->dev, "tplg: check widget: %s stream: %s dai stream: %s\n", + w->name, w->sname, rtd->dai_link->stream_name); + + if (!w->sname || !rtd->dai_link->stream_name) + continue; + + /* does stream match DAI link ? */ + if (strcmp(w->sname, rtd->dai_link->stream_name)) + continue; + + switch (w->id) { + case snd_soc_dapm_dai_out: + rtd->cpu_dai->capture_widget = w; + if (dai) + dai->name = rtd->dai_link->name; + dev_dbg(sdev->dev, "tplg: connected widget %s -> DAI link %s\n", + w->name, rtd->dai_link->name); + break; + case snd_soc_dapm_dai_in: + rtd->cpu_dai->playback_widget = w; + if (dai) + dai->name = rtd->dai_link->name; + dev_dbg(sdev->dev, "tplg: connected widget %s -> DAI link %s\n", + w->name, rtd->dai_link->name); + break; + default: + break; + } + } + + /* check we have a connection */ + if (!dai->name) { + dev_err(sdev->dev, "error: can't connect DAI %s stream %s\n", + w->name, w->sname); + return -EINVAL; + } + + return 0; +} + +static int sof_widget_load_dai(struct snd_soc_component *scomp, int index, + struct snd_sof_widget *swidget, + struct snd_soc_tplg_dapm_widget *tw, + struct sof_ipc_comp_reply *r, + struct snd_sof_dai *dai) +{ + struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp); + struct snd_soc_tplg_private *private = &tw->priv; + struct sof_ipc_comp_dai comp_dai; + int ret; + + /* configure dai IPC message */ + memset(&comp_dai, 0, sizeof(comp_dai)); + comp_dai.comp.hdr.size = sizeof(comp_dai); + comp_dai.comp.hdr.cmd = SOF_IPC_GLB_TPLG_MSG | SOF_IPC_TPLG_COMP_NEW; + comp_dai.comp.id = swidget->comp_id; + comp_dai.comp.type = SOF_COMP_DAI; + comp_dai.comp.pipeline_id = index; + + ret = sof_parse_tokens(scomp, &comp_dai, dai_tokens, + ARRAY_SIZE(dai_tokens), private->array, + private->size); + if (ret != 0) { + dev_err(sdev->dev, "error: parse dai tokens failed %d\n", + private->size); + return ret; + } + + ret = sof_parse_tokens(scomp, &comp_dai.config, comp_tokens, + ARRAY_SIZE(comp_tokens), private->array, + private->size); + if (ret != 0) { + dev_err(sdev->dev, "error: parse dai.cfg tokens failed %d\n", + private->size); + return ret; + } + + dev_dbg(sdev->dev, "dai %s: dmac %d chan %d type %d index %d\n", + swidget->widget->name, comp_dai.dmac_id, comp_dai.dmac_chan, + comp_dai.type, comp_dai.index); + sof_dbg_comp_config(scomp, &comp_dai.config); + + ret = sof_ipc_tx_message(sdev->ipc, comp_dai.comp.hdr.cmd, + &comp_dai, sizeof(comp_dai), r, sizeof(*r)); + + if (ret == 0 && dai) { + dai->sdev = sdev; + memcpy(&dai->comp_dai, &comp_dai, sizeof(comp_dai)); + } + + return ret; +} + +/* + * Buffer topology + */ + +static int sof_widget_load_buffer(struct snd_soc_component *scomp, int index, + struct snd_sof_widget *swidget, + struct snd_soc_tplg_dapm_widget *tw, + struct sof_ipc_comp_reply *r) +{ + struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp); + struct snd_soc_tplg_private *private = &tw->priv; + struct sof_ipc_buffer buffer; + int ret; + + /* configure dai IPC message */ + memset(&buffer, 0, sizeof(buffer)); + buffer.comp.hdr.size = sizeof(buffer); + buffer.comp.hdr.cmd = SOF_IPC_GLB_TPLG_MSG | SOF_IPC_TPLG_BUFFER_NEW; + buffer.comp.id = swidget->comp_id; + buffer.comp.type = SOF_COMP_BUFFER; + buffer.comp.pipeline_id = index; + + ret = sof_parse_tokens(scomp, &buffer, buffer_tokens, + ARRAY_SIZE(buffer_tokens), private->array, + private->size); + if (ret != 0) { + dev_err(sdev->dev, "error: parse buffer tokens failed %d\n", + private->size); + return ret; + } + + dev_dbg(sdev->dev, "buffer %s: size %d caps 0x%x\n", + swidget->widget->name, buffer.size, buffer.caps); + + return sof_ipc_tx_message(sdev->ipc, + buffer.comp.hdr.cmd, &buffer, sizeof(buffer), + r, sizeof(*r)); +} + +/* + * PCM Topology + */ + +static int sof_widget_load_pcm(struct snd_soc_component *scomp, int index, + struct snd_sof_widget *swidget, + enum sof_ipc_stream_direction dir, + struct snd_soc_tplg_dapm_widget *tw, + struct sof_ipc_comp_reply *r) +{ + struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp); + struct snd_soc_tplg_private *private = &tw->priv; + struct sof_ipc_comp_host host; + int ret; + + /* configure mixer IPC message */ + memset(&host, 0, sizeof(host)); + host.comp.hdr.size = sizeof(host); + host.comp.hdr.cmd = SOF_IPC_GLB_TPLG_MSG | SOF_IPC_TPLG_COMP_NEW; + host.comp.id = swidget->comp_id; + host.comp.type = SOF_COMP_HOST; + host.comp.pipeline_id = index; + host.direction = dir; + + ret = sof_parse_tokens(scomp, &host, pcm_tokens, + ARRAY_SIZE(pcm_tokens), private->array, + private->size); + if (ret != 0) { + dev_err(sdev->dev, "error: parse host tokens failed %d\n", + private->size); + return ret; + } + + ret = sof_parse_tokens(scomp, &host.config, comp_tokens, + ARRAY_SIZE(comp_tokens), private->array, + private->size); + if (ret != 0) { + dev_err(sdev->dev, "error: parse host.cfg tokens failed %d\n", + private->size); + return ret; + } + + dev_dbg(sdev->dev, "host %s: dmac %d chan %d\n", + swidget->widget->name, host.dmac_id, host.dmac_chan); + sof_dbg_comp_config(scomp, &host.config); + + return sof_ipc_tx_message(sdev->ipc, + host.comp.hdr.cmd, &host, sizeof(host), r, + sizeof(*r)); +} + +/* + * Pipeline Topology + */ + +static int sof_widget_load_pipeline(struct snd_soc_component *scomp, + int index, struct snd_sof_widget *swidget, + struct snd_soc_tplg_dapm_widget *tw, + struct sof_ipc_comp_reply *r) +{ + struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp); + struct snd_soc_tplg_private *private = &tw->priv; + struct sof_ipc_pipe_new pipeline; + struct snd_sof_widget *comp_swidget; + int ret; + + /* configure dai IPC message */ + memset(&pipeline, 0, sizeof(pipeline)); + pipeline.hdr.size = sizeof(pipeline); + pipeline.hdr.cmd = SOF_IPC_GLB_TPLG_MSG | SOF_IPC_TPLG_PIPE_NEW; + pipeline.pipeline_id = index; + pipeline.comp_id = swidget->comp_id; + + /* component at start of pipeline is our stream id */ + comp_swidget = snd_sof_find_swidget(sdev, tw->sname); + if (!comp_swidget) { + dev_err(sdev->dev, "error: widget %s refers to non existent widget %s\n", + tw->name, tw->sname); + return -EINVAL; + } + + pipeline.sched_id = comp_swidget->comp_id; + + dev_dbg(sdev->dev, "tplg: pipeline id %d comp %d scheduling comp id %d\n", + pipeline.pipeline_id, pipeline.comp_id, pipeline.sched_id); + + ret = sof_parse_tokens(scomp, &pipeline, sched_tokens, + ARRAY_SIZE(sched_tokens), private->array, + private->size); + if (ret != 0) { + dev_err(sdev->dev, "error: parse pipeline tokens failed %d\n", + private->size); + return ret; + } + + dev_dbg(sdev->dev, "pipeline %s: deadline %d pri %d mips %d core %d frames %d\n", + swidget->widget->name, pipeline.deadline, pipeline.priority, + pipeline.mips, pipeline.core, pipeline.frames_per_sched); + + return sof_ipc_tx_message(sdev->ipc, + pipeline.hdr.cmd, &pipeline, sizeof(pipeline), + r, sizeof(*r)); +} + +/* + * Mixer topology + */ + +static int sof_widget_load_mixer(struct snd_soc_component *scomp, int index, + struct snd_sof_widget *swidget, + struct snd_soc_tplg_dapm_widget *tw, + struct sof_ipc_comp_reply *r) +{ + struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp); + struct snd_soc_tplg_private *private = &tw->priv; + struct sof_ipc_comp_mixer mixer; + int ret; + + /* configure mixer IPC message */ + memset(&mixer, 0, sizeof(mixer)); + mixer.comp.hdr.size = sizeof(mixer); + mixer.comp.hdr.cmd = SOF_IPC_GLB_TPLG_MSG | SOF_IPC_TPLG_COMP_NEW; + mixer.comp.id = swidget->comp_id; + mixer.comp.type = SOF_COMP_MIXER; + mixer.comp.pipeline_id = index; + + ret = sof_parse_tokens(scomp, &mixer.config, comp_tokens, + ARRAY_SIZE(comp_tokens), private->array, + private->size); + if (ret != 0) { + dev_err(sdev->dev, "error: parse mixer.cfg tokens failed %d\n", + private->size); + return ret; + } + + sof_dbg_comp_config(scomp, &mixer.config); + return sof_ipc_tx_message(sdev->ipc, + mixer.comp.hdr.cmd, &mixer, sizeof(mixer), r, + sizeof(*r)); +} + +/* + * PGA Topology + */ + +static int sof_widget_load_pga(struct snd_soc_component *scomp, int index, + struct snd_sof_widget *swidget, + struct snd_soc_tplg_dapm_widget *tw, + struct sof_ipc_comp_reply *r) +{ + struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp); + struct snd_soc_tplg_private *private = &tw->priv; + struct sof_ipc_comp_volume volume; + struct snd_soc_dapm_widget *widget = swidget->widget; + const struct snd_kcontrol_new *kc = NULL; + struct soc_mixer_control *sm; + struct snd_sof_control *scontrol; + const unsigned int *p; + int ret, tlv[TLV_ITEMS]; + + if (tw->num_kcontrols != 1) { + dev_err(sdev->dev, "error: invalid kcontrol count %d for volume\n", + tw->num_kcontrols); + return -EINVAL; + } + + /* set up volume gain tables for kcontrol */ + kc = &widget->kcontrol_news[0]; + sm = (struct soc_mixer_control *)kc->private_value; + + /* get volume control */ + scontrol = sm->dobj.private; + + /* get topology tlv data */ + p = kc->tlv.p; + + /* extract tlv data */ + if (get_tlv_data(p, tlv) < 0) { + dev_err(sdev->dev, "error: invalid TLV data\n"); + return -EINVAL; + } + + /* set up volume table */ + if (set_up_volume_table(scontrol, tlv, sm->max + 1) < 0) { + dev_err(sdev->dev, "error: setting up volume table\n"); + return -ENOMEM; + } + + /* configure dai IPC message */ + memset(&volume, 0, sizeof(volume)); + volume.comp.hdr.size = sizeof(volume); + volume.comp.hdr.cmd = SOF_IPC_GLB_TPLG_MSG | SOF_IPC_TPLG_COMP_NEW; + volume.comp.id = swidget->comp_id; + volume.comp.type = SOF_COMP_VOLUME; + volume.comp.pipeline_id = index; + + ret = sof_parse_tokens(scomp, &volume, volume_tokens, + ARRAY_SIZE(volume_tokens), private->array, + private->size); + if (ret != 0) { + dev_err(sdev->dev, "error: parse volume tokens failed %d\n", + private->size); + return ret; + } + ret = sof_parse_tokens(scomp, &volume.config, comp_tokens, + ARRAY_SIZE(comp_tokens), private->array, + private->size); + if (ret != 0) { + dev_err(sdev->dev, "error: parse volume.cfg tokens failed %d\n", + private->size); + return ret; + } + + sof_dbg_comp_config(scomp, &volume.config); + + return sof_ipc_tx_message(sdev->ipc, + volume.comp.hdr.cmd, &volume, sizeof(volume), + r, sizeof(*r)); +} + +/* + * SRC Topology + */ + +static int sof_widget_load_src(struct snd_soc_component *scomp, int index, + struct snd_sof_widget *swidget, + struct snd_soc_tplg_dapm_widget *tw, + struct sof_ipc_comp_reply *r) +{ + struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp); + struct snd_soc_tplg_private *private = &tw->priv; + struct sof_ipc_comp_src src; + int ret; + + /* configure mixer IPC message */ + memset(&src, 0, sizeof(src)); + src.comp.hdr.size = sizeof(src); + src.comp.hdr.cmd = SOF_IPC_GLB_TPLG_MSG | SOF_IPC_TPLG_COMP_NEW; + src.comp.id = swidget->comp_id; + src.comp.type = SOF_COMP_SRC; + src.comp.pipeline_id = index; + + ret = sof_parse_tokens(scomp, &src, src_tokens, + ARRAY_SIZE(src_tokens), private->array, + private->size); + if (ret != 0) { + dev_err(sdev->dev, "error: parse src tokens failed %d\n", + private->size); + return ret; + } + + ret = sof_parse_tokens(scomp, &src.config, comp_tokens, + ARRAY_SIZE(comp_tokens), private->array, + private->size); + if (ret != 0) { + dev_err(sdev->dev, "error: parse src.cfg tokens failed %d\n", + private->size); + return ret; + } + + dev_dbg(sdev->dev, "src %s: source rate %d sink rate %d\n", + swidget->widget->name, src.source_rate, src.sink_rate); + sof_dbg_comp_config(scomp, &src.config); + + return sof_ipc_tx_message(sdev->ipc, + src.comp.hdr.cmd, &src, sizeof(src), r, + sizeof(*r)); +} + +/* + * Signal Generator Topology + */ + +static int sof_widget_load_siggen(struct snd_soc_component *scomp, int index, + struct snd_sof_widget *swidget, + struct snd_soc_tplg_dapm_widget *tw, + struct sof_ipc_comp_reply *r) +{ + struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp); + struct snd_soc_tplg_private *private = &tw->priv; + struct sof_ipc_comp_tone tone; + int ret; + + /* configure mixer IPC message */ + memset(&tone, 0, sizeof(tone)); + tone.comp.hdr.size = sizeof(tone); + tone.comp.hdr.cmd = SOF_IPC_GLB_TPLG_MSG | SOF_IPC_TPLG_COMP_NEW; + tone.comp.id = swidget->comp_id; + tone.comp.type = SOF_COMP_TONE; + tone.comp.pipeline_id = index; + + ret = sof_parse_tokens(scomp, &tone, tone_tokens, + ARRAY_SIZE(tone_tokens), private->array, + private->size); + if (ret != 0) { + dev_err(sdev->dev, "error: parse tone tokens failed %d\n", + private->size); + return ret; + } + + ret = sof_parse_tokens(scomp, &tone.config, comp_tokens, + ARRAY_SIZE(comp_tokens), private->array, + private->size); + if (ret != 0) { + dev_err(sdev->dev, "error: parse tone.cfg tokens failed %d\n", + private->size); + return ret; + } + + dev_dbg(sdev->dev, "tone %s: frequency %d amplitude %d\n", + swidget->widget->name, tone.frequency, tone.amplitude); + sof_dbg_comp_config(scomp, &tone.config); + + return sof_ipc_tx_message(sdev->ipc, + tone.comp.hdr.cmd, &tone, sizeof(tone), r, + sizeof(*r)); +} + +/* + * Generic widget loader. + */ + +static int sof_widget_load(struct snd_soc_component *scomp, int index, + struct snd_soc_dapm_widget *w, + struct snd_soc_tplg_dapm_widget *tw) +{ + return 0; +} + +/* external widget init - used for any driver specific init */ +static int sof_widget_ready(struct snd_soc_component *scomp, int index, + struct snd_soc_dapm_widget *w, + struct snd_soc_tplg_dapm_widget *tw) +{ + struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp); + struct snd_sof_widget *swidget; + struct snd_sof_dai *dai; + struct sof_ipc_comp_reply reply; + struct snd_sof_control *scontrol = NULL; + int ret = 0; + + swidget = kzalloc(sizeof(*swidget), GFP_KERNEL); + if (!swidget) + return -ENOMEM; + + swidget->sdev = sdev; + swidget->widget = w; + swidget->comp_id = sdev->next_comp_id++; + swidget->complete = 0; + swidget->id = w->id; + swidget->pipeline_id = index; + swidget->private = NULL; + memset(&reply, 0, sizeof(reply)); + + dev_dbg(sdev->dev, "tplg: ready widget id %d pipe %d type %d name : %s stream %s\n", + swidget->comp_id, index, swidget->id, tw->name, + tw->sname ? tw->sname : "none"); + + /* handle any special case widgets */ + switch (w->id) { + case snd_soc_dapm_dai_in: + case snd_soc_dapm_dai_out: + dai = kzalloc(sizeof(*dai), GFP_KERNEL); + if (!dai) + return -ENOMEM; + + ret = sof_widget_load_dai(scomp, index, swidget, tw, &reply, + dai); + if (ret == 0) { + sof_connect_dai_widget(scomp, w, tw, dai); + list_add(&dai->list, &sdev->dai_list); + swidget->private = dai; + } else { + kfree(dai); + } + break; + case snd_soc_dapm_mixer: + ret = sof_widget_load_mixer(scomp, index, swidget, tw, &reply); + break; + case snd_soc_dapm_pga: + ret = sof_widget_load_pga(scomp, index, swidget, tw, &reply); + /* Find scontrol for this pga and set readback offset*/ + list_for_each_entry(scontrol, &sdev->kcontrol_list, list) { + if (scontrol->comp_id == swidget->comp_id) { + scontrol->readback_offset = reply.offset; + break; + } + } + break; + case snd_soc_dapm_buffer: + ret = sof_widget_load_buffer(scomp, index, swidget, tw, &reply); + break; + case snd_soc_dapm_scheduler: + ret = sof_widget_load_pipeline(scomp, index, swidget, tw, + &reply); + break; + case snd_soc_dapm_aif_out: + ret = sof_widget_load_pcm(scomp, index, swidget, + SOF_IPC_STREAM_CAPTURE, tw, &reply); + break; + case snd_soc_dapm_aif_in: + ret = sof_widget_load_pcm(scomp, index, swidget, + SOF_IPC_STREAM_PLAYBACK, tw, &reply); + break; + case snd_soc_dapm_src: + ret = sof_widget_load_src(scomp, index, swidget, tw, &reply); + break; + case snd_soc_dapm_siggen: + ret = sof_widget_load_siggen(scomp, index, swidget, tw, &reply); + break; + case snd_soc_dapm_mux: + case snd_soc_dapm_demux: + case snd_soc_dapm_switch: + case snd_soc_dapm_dai_link: + case snd_soc_dapm_kcontrol: + case snd_soc_dapm_effect: + default: + dev_warn(sdev->dev, "warning: widget type %d name %s not handled\n", + swidget->id, tw->name); + break; + } + + /* check IPC reply */ + if (ret < 0 || reply.rhdr.error < 0) { + dev_err(sdev->dev, + "error: DSP failed to add widget id %d type %d name : %s stream %s reply %d\n", + tw->shift, swidget->id, tw->name, + tw->sname ? tw->sname : "none", reply.rhdr.error); + return ret; + } + + w->dobj.private = swidget; + mutex_init(&swidget->mutex); + list_add(&swidget->list, &sdev->widget_list); + return ret; +} + +static int sof_widget_unload(struct snd_soc_component *scomp, + struct snd_soc_dobj *dobj) +{ + struct snd_sof_widget *swidget; + struct snd_sof_dai *dai; + + swidget = dobj->private; + if (!swidget) + return 0; + + dai = swidget->private; + + /* remove and free dai object */ + if (dai) { + list_del(&dai->list); + kfree(dai); + } + + /* remove and free swidget object */ + list_del(&swidget->list); + kfree(swidget); + + return 0; +} + +/* FE DAI - used for any driver specific init */ +static int sof_dai_load(struct snd_soc_component *scomp, int index, + struct snd_soc_dai_driver *dai_drv, + struct snd_soc_tplg_pcm *pcm, struct snd_soc_dai *dai) +{ + struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp); + struct snd_sof_pcm *spcm; + + /* don't need to do anything for BEs atm */ + if (!pcm) + return 0; + + spcm = kzalloc(sizeof(*spcm), GFP_KERNEL); + if (!spcm) + return -ENOMEM; + + spcm->sdev = sdev; + spcm->stream[SNDRV_PCM_STREAM_PLAYBACK].comp_id = COMP_ID_UNASSIGNED; + spcm->stream[SNDRV_PCM_STREAM_CAPTURE].comp_id = COMP_ID_UNASSIGNED; + if (pcm) { + spcm->pcm = *pcm; + dev_dbg(sdev->dev, "tplg: load pcm %s\n", pcm->dai_name); + } + dai_drv->dobj.private = spcm; + mutex_init(&spcm->mutex); + list_add(&spcm->list, &sdev->pcm_list); + + return 0; +} + +static int sof_dai_unload(struct snd_soc_component *scomp, + struct snd_soc_dobj *dobj) +{ + struct snd_sof_pcm *spcm = dobj->private; + + list_del(&spcm->list); + kfree(spcm); + + return 0; +} + +static void sof_dai_set_format(struct snd_soc_tplg_hw_config *hw_config, + struct sof_ipc_dai_config *config) +{ + /* clock directions wrt codec */ + if (hw_config->bclk_master == SND_SOC_TPLG_BCLK_CM) { + /* codec is bclk master */ + if (hw_config->fsync_master == SND_SOC_TPLG_FSYNC_CM) + config->format |= SOF_DAI_FMT_CBM_CFM; + else + config->format |= SOF_DAI_FMT_CBM_CFS; + } else { + /* codec is bclk slave */ + if (hw_config->fsync_master == SND_SOC_TPLG_FSYNC_CM) + config->format |= SOF_DAI_FMT_CBS_CFM; + else + config->format |= SOF_DAI_FMT_CBS_CFS; + } + + /* inverted clocks ? */ + if (hw_config->invert_bclk) { + if (hw_config->invert_fsync) + config->format |= SOF_DAI_FMT_IB_IF; + else + config->format |= SOF_DAI_FMT_IB_NF; + } else { + if (hw_config->invert_fsync) + config->format |= SOF_DAI_FMT_NB_IF; + else + config->format |= SOF_DAI_FMT_NB_NF; + } +} + +static int sof_link_ssp_load(struct snd_soc_component *scomp, int index, + struct snd_soc_dai_link *link, + struct snd_soc_tplg_link_config *cfg, + struct snd_soc_tplg_hw_config *hw_config, + struct sof_ipc_dai_config *config) +{ + struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp); + struct snd_soc_tplg_private *private = &cfg->priv; + struct sof_ipc_reply reply; + u32 size = sizeof(*config); + int ret; + + /* handle master/slave and inverted clocks */ + sof_dai_set_format(hw_config, config); + + /* init IPC */ + memset(&config->ssp, 0, sizeof(struct sof_ipc_dai_ssp_params)); + config->hdr.size = size; + + /* get any bespoke DAI tokens */ + ret = sof_parse_tokens(scomp, &config->ssp, dai_ssp_link_tokens, + ARRAY_SIZE(dai_ssp_link_tokens), + private->array, private->size); + if (ret != 0) { + dev_err(sdev->dev, "error: parse ssp link tokens failed %d\n", + private->size); + return ret; + } + + ret = sof_parse_tokens(scomp, config, ssp_tokens, + ARRAY_SIZE(ssp_tokens), private->array, + private->size); + if (ret != 0) { + dev_err(sdev->dev, "error: parse ssp tokens failed %d\n", + private->size); + return ret; + } + + config->ssp.mclk_rate = hw_config->mclk_rate; + config->ssp.bclk_rate = hw_config->bclk_rate; + config->ssp.fsync_rate = hw_config->fsync_rate; + config->ssp.tdm_slots = hw_config->tdm_slots; + config->ssp.tdm_slot_width = hw_config->tdm_slot_width; + config->ssp.mclk_direction = hw_config->mclk_direction; + config->ssp.rx_slots = hw_config->rx_slots; + config->ssp.tx_slots = hw_config->tx_slots; + + dev_dbg(sdev->dev, "tplg: config SSP%d fmt 0x%x mclk %d bclk %d fclk %d width (%d)%d slots %d\n", + config->id, config->format, + config->ssp.mclk_rate, config->ssp.bclk_rate, + config->ssp.fsync_rate, config->ssp.sample_valid_bits, + config->ssp.tdm_slot_width, config->ssp.tdm_slots); + + /* send message to DSP */ + ret = sof_ipc_tx_message(sdev->ipc, + config->hdr.cmd, config, size, &reply, + sizeof(reply)); + + if (ret < 0) + dev_err(sdev->dev, "error: failed to set DAI config for SSP%d\n", + config->id); + + return ret; +} + +static int sof_link_dmic_load(struct snd_soc_component *scomp, int index, + struct snd_soc_dai_link *link, + struct snd_soc_tplg_link_config *cfg, + struct snd_soc_tplg_hw_config *hw_config, + struct sof_ipc_dai_config *config) +{ + struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp); + struct snd_soc_tplg_private *private = &cfg->priv; + struct sof_ipc_reply reply; + u32 size = sizeof(*config); + int ret; + + /* init IPC */ + memset(&config->dmic, 0, sizeof(struct sof_ipc_dai_dmic_params)); + config->hdr.size = size; + + /* get any bespoke DAI tokens */ + ret = sof_parse_tokens(scomp, config, dmic_tokens, + ARRAY_SIZE(dmic_tokens), private->array, + private->size); + if (ret != 0) { + dev_err(sdev->dev, "error: parse dmic tokens failed %d\n", + private->size); + return ret; + } + + dev_dbg(sdev->dev, "tplg: config DMIC%d fmt 0x%x\n", + config->id, config->format); + + /* send message to DSP */ + ret = sof_ipc_tx_message(sdev->ipc, + config->hdr.cmd, config, size, &reply, + sizeof(reply)); + + if (ret < 0) + dev_err(sdev->dev, "error: failed to set DAI config for DMIC%d\n", + config->id); + + return ret; +} + +static int sof_link_hda_load(struct snd_soc_component *scomp, int index, + struct snd_soc_dai_link *link, + struct snd_soc_tplg_link_config *cfg, + struct snd_soc_tplg_hw_config *hw_config, + struct sof_ipc_dai_config *config) +{ + struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp); + struct snd_soc_tplg_private *private = &cfg->priv; + struct sof_ipc_reply reply; + u32 size = sizeof(*config); + int ret; + + /* init IPC */ + memset(&config->hda, 0, sizeof(struct sof_ipc_dai_hda_params)); + config->hdr.size = size; + + /* get any bespoke DAI tokens */ + ret = sof_parse_tokens(scomp, config, hda_tokens, + ARRAY_SIZE(hda_tokens), private->array, + private->size); + if (ret != 0) { + dev_err(sdev->dev, "error: parse hda tokens failed %d\n", + private->size); + return ret; + } + + dev_dbg(sdev->dev, "tplg: config HDA%d fmt 0x%x\n", + config->id, config->format); + + /* send message to DSP */ + ret = sof_ipc_tx_message(sdev->ipc, + config->hdr.cmd, config, size, &reply, + sizeof(reply)); + + if (ret < 0) + dev_err(sdev->dev, "error: failed to set DAI config for HDA%d\n", + config->id); + + return ret; +} + +/* DAI link - used for any driver specific init */ +static int sof_link_load(struct snd_soc_component *scomp, int index, + struct snd_soc_dai_link *link, + struct snd_soc_tplg_link_config *cfg) +{ + struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp); + struct snd_soc_tplg_private *private = &cfg->priv; + struct sof_ipc_dai_config config; + struct snd_soc_tplg_hw_config *hw_config; + struct snd_sof_dai *dai; + int ret = 0; + + link->platform_name = "sof-audio"; + link->nonatomic = true; + + /* send BE configurations to DSP */ + if (!link->no_pcm) + return 0; + + /* only support 1 config atm */ + if (cfg->num_hw_configs != 1) { + dev_err(sdev->dev, "error: unexpected DAI config count %d\n", + cfg->num_hw_configs); + return -EINVAL; + } + + /* check we have some tokens - we need at least DAI type */ + if (private->size == 0) { + dev_err(sdev->dev, "error: expected tokens for DAI, none found\n"); + return -EINVAL; + } + + memset(&config, 0, sizeof(config)); + + /* get any common DAI tokens */ + ret = sof_parse_tokens(scomp, &config, dai_link_tokens, + ARRAY_SIZE(dai_link_tokens), private->array, + private->size); + if (ret != 0) { + dev_err(sdev->dev, "error: parse link tokens failed %d\n", + private->size); + return ret; + } + + /* configure dai IPC message */ + hw_config = &cfg->hw_config[0]; + + config.hdr.cmd = SOF_IPC_GLB_DAI_MSG | SOF_IPC_DAI_CONFIG; + config.id = hw_config->id; + config.format = hw_config->fmt; + + /* now load DAI specific data and send IPC - type comes from token */ + switch (config.type) { + case SOF_DAI_INTEL_SSP: + ret = sof_link_ssp_load(scomp, index, link, cfg, hw_config, + &config); + break; + case SOF_DAI_INTEL_DMIC: + ret = sof_link_dmic_load(scomp, index, link, cfg, hw_config, + &config); + break; + case SOF_DAI_INTEL_HDA: + ret = sof_link_hda_load(scomp, index, link, cfg, hw_config, + &config); + break; + default: + dev_err(sdev->dev, "error: invalid DAI type %d\n", config.type); + ret = -EINVAL; + break; + } + + dai = snd_sof_find_dai(sdev, (char *)link->name); + if (dai) + memcpy(&dai->dai_config, &config, + sizeof(struct sof_ipc_dai_config)); + + return 0; +} + +static int sof_link_unload(struct snd_soc_component *scomp, + struct snd_soc_dobj *dobj) +{ + return 0; +} + +/* bind PCM ID to host component ID */ +static int spcm_bind(struct snd_soc_component *scomp, struct snd_sof_pcm *spcm, + const char *host) +{ + struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp); + struct snd_sof_widget *host_widget; + + host_widget = snd_sof_find_swidget(sdev, (char *)host); + if (!host_widget) { + dev_err(sdev->dev, "error: can't find host component %s\n", + host); + return -ENODEV; + } + + switch (host_widget->id) { + case snd_soc_dapm_aif_in: + spcm->stream[SNDRV_PCM_STREAM_PLAYBACK].comp_id = + host_widget->comp_id; + break; + case snd_soc_dapm_aif_out: + spcm->stream[SNDRV_PCM_STREAM_CAPTURE].comp_id = + host_widget->comp_id; + break; + default: + dev_err(sdev->dev, "error: host is wrong type %d\n", + host_widget->id); + return -EINVAL; + } + + return 0; +} + +/* DAI link - used for any driver specific init */ +static int sof_route_load(struct snd_soc_component *scomp, int index, + struct snd_soc_dapm_route *route) +{ + struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp); + struct sof_ipc_pipe_comp_connect connect; + struct snd_sof_widget *source_swidget, *sink_swidget; + struct snd_sof_pcm *spcm; + struct sof_ipc_reply reply; + int ret; + + memset(&connect, 0, sizeof(connect)); + connect.hdr.size = sizeof(connect); + connect.hdr.cmd = SOF_IPC_GLB_TPLG_MSG | SOF_IPC_TPLG_COMP_CONNECT; + + dev_dbg(sdev->dev, "sink %s control %s source %s\n", + route->sink, route->control ? route->control : "none", + route->source); + + /* source component */ + source_swidget = snd_sof_find_swidget(sdev, (char *)route->source); + if (!source_swidget) { + /* don't send any routes to DSP that include a driver PCM */ + spcm = snd_sof_find_spcm_name(sdev, (char *)route->source); + if (spcm) + return spcm_bind(scomp, spcm, route->sink); + + dev_err(sdev->dev, "error: source %s not found\n", + route->source); + return -EINVAL; + } + + connect.source_id = source_swidget->comp_id; + + /* sink component */ + sink_swidget = snd_sof_find_swidget(sdev, (char *)route->sink); + if (!sink_swidget) { + /* don't send any routes to DSP that include a driver PCM */ + spcm = snd_sof_find_spcm_name(sdev, (char *)route->sink); + if (spcm) + return spcm_bind(scomp, spcm, route->source); + + dev_err(sdev->dev, "error: sink %s not found\n", + route->sink); + return -EINVAL; + } + + connect.sink_id = sink_swidget->comp_id; + + ret = sof_ipc_tx_message(sdev->ipc, + connect.hdr.cmd, &connect, sizeof(connect), + &reply, sizeof(reply)); + + /* check IPC return value */ + if (ret < 0) { + dev_err(sdev->dev, "error: failed to add route sink %s control %s source %s\n", + route->sink, route->control ? route->control : "none", + route->source); + return ret; + } + + /* check IPC reply */ + if (reply.error < 0) { + dev_err(sdev->dev, "error: DSP failed to add route sink %s control %s source %s result %d\n", + route->sink, route->control ? route->control : "none", + route->source, reply.error); + //return ret; // TODO: + } + + return ret; +} + +static int sof_route_unload(struct snd_soc_component *scomp, + struct snd_soc_dobj *dobj) +{ + return 0; +} + +static int sof_complete_pipeline(struct snd_soc_component *scomp, + struct snd_sof_widget *swidget) +{ + struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp); + struct sof_ipc_pipe_ready ready; + struct sof_ipc_reply reply; + int ret; + + dev_dbg(sdev->dev, "tplg: complete pipeline %s id %d\n", + swidget->widget->name, swidget->comp_id); + + memset(&ready, 0, sizeof(ready)); + ready.hdr.size = sizeof(ready); + ready.hdr.cmd = SOF_IPC_GLB_TPLG_MSG | SOF_IPC_TPLG_PIPE_COMPLETE; + ready.comp_id = swidget->comp_id; + + ret = sof_ipc_tx_message(sdev->ipc, + ready.hdr.cmd, &ready, sizeof(ready), &reply, + sizeof(reply)); + if (ret < 0) + return ret; + return 1; +} + +/* completion - called at completion of firmware loading */ +static void sof_complete(struct snd_soc_component *scomp) +{ + struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp); + struct snd_sof_widget *swidget; + + /* some widget types require completion notificattion */ + list_for_each_entry(swidget, &sdev->widget_list, list) { + if (swidget->complete) + continue; + + switch (swidget->id) { + case snd_soc_dapm_scheduler: + swidget->complete = + sof_complete_pipeline(scomp, swidget); + break; + default: + break; + } + } +} + +/* manifest - optional to inform component of manifest */ +static int sof_manifest(struct snd_soc_component *scomp, int index, + struct snd_soc_tplg_manifest *man) +{ + return 0; +} + +/* vendor specific kcontrol handlers available for binding */ +static const struct snd_soc_tplg_kcontrol_ops sof_io_ops[] = { + {SOF_TPLG_KCTL_VOL_ID, snd_sof_volume_get, snd_sof_volume_put}, + {SOF_TPLG_KCTL_ENUM_ID, snd_sof_enum_get, snd_sof_enum_put}, + {SOF_TPLG_KCTL_BYTES_ID, snd_sof_bytes_get, snd_sof_bytes_put}, +}; + +/* vendor specific bytes ext handlers available for binding */ +static const struct snd_soc_tplg_bytes_ext_ops sof_bytes_ext_ops[] = { +{}, +}; + +static struct snd_soc_tplg_ops sof_tplg_ops = { + /* external kcontrol init - used for any driver specific init */ + .control_load = sof_control_load, + .control_unload = sof_control_unload, + + /* external kcontrol init - used for any driver specific init */ + .dapm_route_load = sof_route_load, + .dapm_route_unload = sof_route_unload, + + /* external widget init - used for any driver specific init */ + .widget_load = sof_widget_load, + .widget_ready = sof_widget_ready, + .widget_unload = sof_widget_unload, + + /* FE DAI - used for any driver specific init */ + .dai_load = sof_dai_load, + .dai_unload = sof_dai_unload, + + /* DAI link - used for any driver specific init */ + .link_load = sof_link_load, + .link_unload = sof_link_unload, + + /* completion - called at completion of firmware loading */ + .complete = sof_complete, + + /* manifest - optional to inform component of manifest */ + .manifest = sof_manifest, + + /* vendor specific kcontrol handlers available for binding */ + .io_ops = sof_io_ops, + .io_ops_count = ARRAY_SIZE(sof_io_ops), + + /* vendor specific bytes ext handlers available for binding */ + .bytes_ext_ops = sof_bytes_ext_ops, + .bytes_ext_ops_count = ARRAY_SIZE(sof_bytes_ext_ops), +}; + +int snd_sof_init_topology(struct snd_sof_dev *sdev, + struct snd_soc_tplg_ops *ops) +{ + /* TODO: support linked list of topologies */ + sdev->tplg_ops = ops; + return 0; +} +EXPORT_SYMBOL(snd_sof_init_topology); + +int snd_sof_load_topology(struct snd_sof_dev *sdev, const char *file) +{ + const struct firmware *fw; + struct snd_soc_tplg_hdr *hdr; + int ret; + + dev_dbg(sdev->dev, "loading topology:%s\n", file); + + ret = request_firmware(&fw, file, sdev->dev); + if (ret < 0) { + dev_err(sdev->dev, "error: tplg %s load failed with %d\n", + file, ret); + return ret; + } + + hdr = (struct snd_soc_tplg_hdr *)fw->data; + ret = snd_soc_tplg_component_load(sdev->component, + &sof_tplg_ops, fw, + SND_SOC_TPLG_INDEX_ALL); + if (ret < 0) { + dev_err(sdev->dev, "error: tplg component load failed %d\n", + ret); + ret = -EINVAL; + } + + release_firmware(fw); + return ret; +} +EXPORT_SYMBOL(snd_sof_load_topology); + +void snd_sof_free_topology(struct snd_sof_dev *sdev) +{ + int ret; + + dev_dbg(sdev->dev, "free topology...\n"); + + ret = snd_soc_tplg_component_remove(sdev->component, + SND_SOC_TPLG_INDEX_ALL); + if (ret < 0) + dev_err(sdev->dev, + "error: tplg component free failed %d\n", ret); +} +EXPORT_SYMBOL(snd_sof_free_topology); From 496c29a4d647a5b548342877febcb94351035a20 Mon Sep 17 00:00:00 2001 From: Liam Girdwood Date: Mon, 8 Jan 2018 20:33:15 +0000 Subject: [PATCH 030/107] ASoC: SOF: Add DSP firmware trace event support Add a trace event buffer that can be used by userspace to read DSP runtime trace events alongside bespoke trace data in realtime for firmware debug. Signed-off-by: Liam Girdwood --- sound/soc/sof/trace.c | 290 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 290 insertions(+) create mode 100644 sound/soc/sof/trace.c diff --git a/sound/soc/sof/trace.c b/sound/soc/sof/trace.c new file mode 100644 index 00000000000000..dc452ad6109359 --- /dev/null +++ b/sound/soc/sof/trace.c @@ -0,0 +1,290 @@ +// SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause) +/* + * This file is provided under a dual BSD/GPLv2 license. When using or + * redistributing this file, you may do so under either license. + * + * Copyright(c) 2017 Intel Corporation. All rights reserved. + * + * Author: Liam Girdwood + * Yan Wang + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "sof-priv.h" +#include "ops.h" + +static int sof_wait_trace_avail(struct snd_sof_dev *sdev, size_t *count, + loff_t pos, size_t size) +{ + size_t avail; + wait_queue_entry_t wait; + + /* + * If host offset is less than local pos, it means write pointer of + * host DMA buffer has been wrapped. We should output the trace data + * at the end of host DMA buffer at first. + */ + if (sdev->host_offset < pos) { + avail = size - pos; + goto _host_end; + } + + /* If there is available trace data now, it is unnecessary to wait. */ + if (sdev->host_offset > pos) + goto _endcheck; + + /* wait for available trace data from FW */ + init_waitqueue_entry(&wait, current); + set_current_state(TASK_INTERRUPTIBLE); + add_wait_queue(&sdev->trace_sleep, &wait); + + if (signal_pending(current)) { + remove_wait_queue(&sdev->trace_sleep, &wait); + goto _endcheck; + } + + /* set timeout to max value, no error code */ + schedule_timeout(MAX_SCHEDULE_TIMEOUT); + remove_wait_queue(&sdev->trace_sleep, &wait); + +_endcheck: + /* calculate the available count */ + avail = sdev->host_offset - pos; + +_host_end: + /* return min value between available and request count */ + *count = avail < *count ? avail : *count; + + return 0; +} + +static ssize_t sof_dfsentry_trace_read(struct file *file, char __user *buffer, + size_t count, loff_t *ppos) +{ + struct snd_sof_dfsentry *dfse = file->private_data; + struct snd_sof_dev *sdev = dfse->sdev; + int err; + loff_t pos = *ppos; + loff_t lpos = pos; + size_t ret, size; + + size = dfse->size; + + /* check pos and count */ + if (pos < 0) + return -EINVAL; + if (!count) + return 0; + + /* + * If pos exceeds size, it means host DMA buffer has been wrapped. So + * local pos will be truncated from global pos. It is possible to wrap + * host DMA buffer multiply times when keep output long time, so we + * need one loop to process it. + */ + while (lpos >= size) + lpos -= size; + + if (count > size - lpos) + count = size - lpos; + + /* get available count based on current host offset */ + err = sof_wait_trace_avail(sdev, &count, lpos, size); + if (err < 0) { + dev_err(sdev->dev, + "error: can't get more trace %d\n", err); + return 0; + } + + /* copy available trace data to debugfs */ + ret = copy_to_user(buffer, dfse->buf + lpos, count); + + if (ret == count) + return -EFAULT; + count -= ret; + + /* move debugfs reading position */ + *ppos = pos + count; + + return count; +} + +static const struct file_operations sof_dfs_trace_fops = { + .open = simple_open, + .read = sof_dfsentry_trace_read, + .llseek = default_llseek, +}; + +static int trace_debugfs_create(struct snd_sof_dev *sdev) +{ + struct snd_sof_dfsentry *dfse; + + if (!sdev) + return -EINVAL; + + dfse = kzalloc(sizeof(*dfse), GFP_KERNEL); + if (!dfse) + return -ENOMEM; + + dfse->buf = sdev->dmatb.area; + dfse->size = sdev->dmatb.bytes; + dfse->sdev = sdev; + + dfse->dfsentry = debugfs_create_file("trace", 0444, sdev->debugfs_root, + dfse, &sof_dfs_trace_fops); + if (!dfse->dfsentry) { + dev_err(sdev->dev, + "error: cannot create debugfs entry for trace\n"); + kfree(dfse); + return -ENODEV; + } + + return 0; +} + +int snd_sof_init_trace(struct snd_sof_dev *sdev) +{ + struct sof_ipc_dma_trace_params params; + struct sof_ipc_reply ipc_reply; + int ret; + + /* set false before start initialization */ + sdev->dtrace_is_enabled = false; + + /* allocate trace page table buffer */ + ret = snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, sdev->parent, + PAGE_SIZE, &sdev->dmatp); + if (ret < 0) { + dev_err(sdev->dev, + "error: can't alloc page table for trace %d\n", ret); + return ret; + } + + /* allocate trace data buffer */ + ret = snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV_SG, sdev->parent, + DMA_BUF_SIZE_FOR_TRACE, &sdev->dmatb); + if (ret < 0) { + dev_err(sdev->dev, + "error: can't alloc buffer for trace%d\n", ret); + goto page_err; + } + + /* craete compressed page table for audio firmware */ + ret = snd_sof_create_page_table(sdev, &sdev->dmatb, sdev->dmatp.area, + sdev->dmatb.bytes); + if (ret < 0) + goto table_err; + + sdev->dma_trace_pages = ret; + dev_dbg(sdev->dev, "dma_trace_pages: %d\n", sdev->dma_trace_pages); + + ret = trace_debugfs_create(sdev); + if (ret < 0) + goto table_err; + + /* set IPC parameters */ + params.hdr.size = sizeof(params); + params.hdr.cmd = SOF_IPC_GLB_TRACE_MSG | SOF_IPC_TRACE_DMA_PARAMS; + params.buffer.phy_addr = sdev->dmatp.addr; + params.buffer.size = sdev->dmatb.bytes; + params.buffer.offset = 0; + params.buffer.pages = sdev->dma_trace_pages; + + init_waitqueue_head(&sdev->trace_sleep); + sdev->host_offset = 0; + + ret = snd_sof_dma_trace_init(sdev, ¶ms.stream_tag); + if (ret < 0) { + dev_err(sdev->dev, + "error: fail in snd_sof_dma_trace_init %d\n", ret); + goto table_err; + } + dev_dbg(sdev->dev, "stream_tag: %d\n", params.stream_tag); + + /* send IPC to the DSP */ + ret = sof_ipc_tx_message(sdev->ipc, + params.hdr.cmd, ¶ms, sizeof(params), + &ipc_reply, sizeof(ipc_reply)); + if (ret < 0) { + dev_err(sdev->dev, + "error: can't set params for DMA for trace %d\n", ret); + goto table_err; + } + + ret = snd_sof_dma_trace_trigger(sdev, SNDRV_PCM_TRIGGER_START); + if (ret < 0) { + dev_err(sdev->dev, + "error: snd_sof_dma_trace_trigger: start: %d\n", ret); + goto table_err; + } + + sdev->dtrace_is_enabled = true; + return 0; + +table_err: + snd_dma_free_pages(&sdev->dmatb); +page_err: + snd_dma_free_pages(&sdev->dmatp); + return ret; +} +EXPORT_SYMBOL(snd_sof_init_trace); + +int snd_sof_trace_update_pos(struct snd_sof_dev *sdev, + struct sof_ipc_dma_trace_posn *posn) +{ + if (sdev->dtrace_is_enabled && sdev->host_offset != posn->host_offset) { + sdev->host_offset = posn->host_offset; + wake_up(&sdev->trace_sleep); + } + + if (posn->overflow != 0) + dev_err(sdev->dev, + "error: DSP trace buffer overflow %u bytes. Total messages %d\n", + posn->overflow, posn->messages); + + return 0; +} + +void snd_sof_trace_notify_for_error(struct snd_sof_dev *sdev) +{ + if (sdev->dtrace_is_enabled) { + dev_err(sdev->dev, "error: waking up any trace sleepers\n"); + wake_up(&sdev->trace_sleep); + } +} +EXPORT_SYMBOL(snd_sof_trace_notify_for_error); + +void snd_sof_release_trace(struct snd_sof_dev *sdev) +{ + int ret; + + if (!sdev->dtrace_is_enabled) + return; + + ret = snd_sof_dma_trace_trigger(sdev, SNDRV_PCM_TRIGGER_STOP); + if (ret < 0) + dev_err(sdev->dev, + "error: snd_sof_dma_trace_trigger: stop: %d\n", ret); + + ret = snd_sof_dma_trace_release(sdev); + if (ret < 0) + dev_err(sdev->dev, + "error: fail in snd_sof_dma_trace_release %d\n", ret); + + snd_dma_free_pages(&sdev->dmatb); + snd_dma_free_pages(&sdev->dmatp); +} +EXPORT_SYMBOL(snd_sof_release_trace); From 95b1b4eb36f764b864fcd122610d36c636008d1a Mon Sep 17 00:00:00 2001 From: Liam Girdwood Date: Mon, 8 Jan 2018 20:33:43 +0000 Subject: [PATCH 031/107] ASoC: SOF: Add DSP HW abstraction operations Add operation pointers that can be called by core to control a wide variety of DSP targets. The DSP HW drivers will fill in these operations. Signed-off-by: Liam Girdwood --- sound/soc/sof/ops.c | 210 ++++++++++++++++++++++++++++++++++++ sound/soc/sof/ops.h | 252 ++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 462 insertions(+) create mode 100644 sound/soc/sof/ops.c create mode 100644 sound/soc/sof/ops.h diff --git a/sound/soc/sof/ops.c b/sound/soc/sof/ops.c new file mode 100644 index 00000000000000..a3b1aebb0fd14a --- /dev/null +++ b/sound/soc/sof/ops.c @@ -0,0 +1,210 @@ +// SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause) +/* + * This file is provided under a dual BSD/GPLv2 license. When using or + * redistributing this file, you may do so under either license. + * + * Copyright(c) 2017 Intel Corporation. All rights reserved. + * + * Author: Liam Girdwood + */ + +#include +#include +#include +#include +#include +#include +#include +#include "ops.h" +#include "sof-priv.h" + +int snd_sof_pci_update_bits_unlocked(struct snd_sof_dev *sdev, u32 offset, + u32 mask, u32 value) +{ + bool change; + unsigned int old, new; + u32 ret; + + pci_read_config_dword(sdev->pci, offset, &ret); + dev_dbg(sdev->dev, "Debug PCIR: %8.8x at %8.8x\n", + pci_read_config_dword(sdev->pci, offset, &ret), offset); + + old = ret; + new = (old & (~mask)) | (value & mask); + + change = (old != new); + if (change) { + pci_write_config_dword(sdev->pci, offset, new); + dev_dbg(sdev->dev, "Debug PCIW: %8.8x at %8.8x\n", value, + offset); + } + + return change; +} +EXPORT_SYMBOL(snd_sof_pci_update_bits_unlocked); + +int snd_sof_pci_update_bits(struct snd_sof_dev *sdev, u32 offset, + u32 mask, u32 value) +{ + unsigned long flags; + bool change; + + spin_lock_irqsave(&sdev->hw_lock, flags); + change = snd_sof_pci_update_bits_unlocked(sdev, offset, mask, value); + spin_unlock_irqrestore(&sdev->hw_lock, flags); + return change; +} +EXPORT_SYMBOL(snd_sof_pci_update_bits); + +int snd_sof_dsp_update_bits_unlocked(struct snd_sof_dev *sdev, u32 bar, + u32 offset, u32 mask, u32 value) +{ + bool change; + unsigned int old, new; + u32 ret; + + ret = snd_sof_dsp_read(sdev, bar, offset); + + old = ret; + new = (old & (~mask)) | (value & mask); + + change = (old != new); + if (change) + snd_sof_dsp_write(sdev, bar, offset, new); + + return change; +} +EXPORT_SYMBOL(snd_sof_dsp_update_bits_unlocked); + +int snd_sof_dsp_update_bits64_unlocked(struct snd_sof_dev *sdev, u32 bar, + u32 offset, u64 mask, u64 value) +{ + bool change; + u64 old, new; + + old = snd_sof_dsp_read64(sdev, bar, offset); + + new = (old & (~mask)) | (value & mask); + + change = (old != new); + if (change) + snd_sof_dsp_write64(sdev, bar, offset, new); + + return change; +} +EXPORT_SYMBOL(snd_sof_dsp_update_bits64_unlocked); + +/* This is for registers bits with attribute RWC */ +void snd_sof_dsp_update_bits_forced_unlocked(struct snd_sof_dev *sdev, u32 bar, + u32 offset, u32 mask, u32 value) +{ + unsigned int old, new; + u32 ret; + + ret = snd_sof_dsp_read(sdev, bar, offset); + + old = ret; + new = (old & (~mask)) | (value & mask); + + snd_sof_dsp_write(sdev, bar, offset, new); +} +EXPORT_SYMBOL(snd_sof_dsp_update_bits_forced_unlocked); + +int snd_sof_dsp_update_bits(struct snd_sof_dev *sdev, u32 bar, u32 offset, + u32 mask, u32 value) +{ + unsigned long flags; + bool change; + + spin_lock_irqsave(&sdev->hw_lock, flags); + change = snd_sof_dsp_update_bits_unlocked(sdev, bar, offset, mask, + value); + spin_unlock_irqrestore(&sdev->hw_lock, flags); + return change; +} +EXPORT_SYMBOL(snd_sof_dsp_update_bits); + +int snd_sof_dsp_update_bits64(struct snd_sof_dev *sdev, u32 bar, u32 offset, + u64 mask, u64 value) +{ + unsigned long flags; + bool change; + + spin_lock_irqsave(&sdev->hw_lock, flags); + change = snd_sof_dsp_update_bits64_unlocked(sdev, bar, offset, mask, + value); + spin_unlock_irqrestore(&sdev->hw_lock, flags); + return change; +} +EXPORT_SYMBOL(snd_sof_dsp_update_bits64); + +/* This is for registers bits with attribute RWC */ +void snd_sof_dsp_update_bits_forced(struct snd_sof_dev *sdev, u32 bar, + u32 offset, u32 mask, u32 value) +{ + unsigned long flags; + + spin_lock_irqsave(&sdev->hw_lock, flags); + snd_sof_dsp_update_bits_forced_unlocked(sdev, bar, offset, mask, value); + spin_unlock_irqrestore(&sdev->hw_lock, flags); +} +EXPORT_SYMBOL(snd_sof_dsp_update_bits_forced); + +int snd_sof_dsp_register_poll(struct snd_sof_dev *sdev, u32 bar, u32 offset, + u32 mask, u32 target, u32 timeout) +{ + int time, ret; + bool done = false; + + /* + * we will poll for couple of ms using mdelay, if not successful + * then go to longer sleep using usleep_range + */ + + /* check if set state successful */ + for (time = 0; time < 5; time++) { + if ((snd_sof_dsp_read(sdev, bar, offset) & mask) == target) { + done = true; + break; + } + msleep(20); + } + + if (!done) { + /* sleeping in 10ms steps so adjust timeout value */ + timeout /= 10; + + for (time = 0; time < timeout; time++) { + if ((snd_sof_dsp_read(sdev, bar, offset) & mask) == + target) + break; + + usleep_range(5000, 10000); + } + } + + ret = time < timeout ? 0 : -ETIME; + + return ret; +} +EXPORT_SYMBOL(snd_sof_dsp_register_poll); + +void snd_sof_dsp_panic(struct snd_sof_dev *sdev, u32 offset) +{ + dev_err(sdev->dev, "error : DSP panic!\n"); + + /* check if DSP is not ready and did not set the dsp_oops_offset. + * if the dsp_oops_offset is not set, set it from the panic message. + * Also add a check to memory window setting with panic message. + */ + if (!sdev->dsp_oops_offset) + sdev->dsp_oops_offset = offset; + else + dev_dbg(sdev->dev, "panic: dsp_oops_offset %zu offset %d\n", + sdev->dsp_oops_offset, offset); + + snd_sof_dsp_dbg_dump(sdev, SOF_DBG_REGS | SOF_DBG_MBOX); + snd_sof_trace_notify_for_error(sdev); + snd_sof_dsp_cmd_done(sdev); +} +EXPORT_SYMBOL(snd_sof_dsp_panic); diff --git a/sound/soc/sof/ops.h b/sound/soc/sof/ops.h new file mode 100644 index 00000000000000..1a00a4b7d8037f --- /dev/null +++ b/sound/soc/sof/ops.h @@ -0,0 +1,252 @@ +/* SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause) */ +/* + * This file is provided under a dual BSD/GPLv2 license. When using or + * redistributing this file, you may do so under either license. + * + * Copyright(c) 2017 Intel Corporation. All rights reserved. + * + * Author: Liam Girdwood + */ + +#ifndef __SOUND_SOC_SOF_IO_H +#define __SOUND_SOC_SOF_IO_H + +#include +#include +#include +#include +#include +#include "sof-priv.h" + +/* init */ +static inline int snd_sof_probe(struct snd_sof_dev *sdev) +{ + if (sdev->ops->probe) + return sdev->ops->probe(sdev); + else + return 0; +} + +static inline int snd_sof_remove(struct snd_sof_dev *sdev) +{ + if (sdev->ops->remove) + return sdev->ops->remove(sdev); + else + return 0; +} + +/* control */ +static inline int snd_sof_dsp_run(struct snd_sof_dev *sdev) +{ + if (sdev->ops->run) + return sdev->ops->run(sdev); + else + return 0; +} + +static inline int snd_sof_dsp_stall(struct snd_sof_dev *sdev) +{ + if (sdev->ops->stall) + return sdev->ops->stall(sdev); + else + return 0; +} + +static inline int snd_sof_dsp_reset(struct snd_sof_dev *sdev) +{ + if (sdev->ops->reset) + return sdev->ops->reset(sdev); + else + return 0; +} + +/* power management */ +static inline int snd_sof_dsp_resume(struct snd_sof_dev *sdev) +{ + if (sdev->ops->resume) + return sdev->ops->resume(sdev); + else + return 0; +} + +static inline int snd_sof_dsp_suspend(struct snd_sof_dev *sdev, int state) +{ + if (sdev->ops->suspend) + return sdev->ops->suspend(sdev, state); + else + return 0; +} + +static inline int snd_sof_dsp_set_clk(struct snd_sof_dev *sdev, u32 freq) +{ + if (sdev->ops->set_clk) + return sdev->ops->set_clk(sdev, freq); + else + return 0; +} + +/* debug */ +static inline void snd_sof_dsp_dbg_dump(struct snd_sof_dev *sdev, u32 flags) +{ + if (sdev->ops->dbg_dump) + return sdev->ops->dbg_dump(sdev, flags); +} + +/* register IO */ +static inline void snd_sof_dsp_write(struct snd_sof_dev *sdev, u32 bar, + u32 offset, u32 value) +{ + if (sdev->ops->write) + sdev->ops->write(sdev, sdev->bar[bar] + offset, value); +} + +static inline void snd_sof_dsp_write64(struct snd_sof_dev *sdev, u32 bar, + u32 offset, u64 value) +{ + if (sdev->ops->write64) + sdev->ops->write64(sdev, + sdev->bar[bar] + offset, value); +} + +static inline u32 snd_sof_dsp_read(struct snd_sof_dev *sdev, u32 bar, + u32 offset) +{ + if (sdev->ops->read) + return sdev->ops->read(sdev, sdev->bar[bar] + offset); + else + return 0; +} + +static inline u64 snd_sof_dsp_read64(struct snd_sof_dev *sdev, u32 bar, + u32 offset) +{ + if (sdev->ops->read64) + return sdev->ops->read64(sdev, sdev->bar[bar] + offset); + else + return 0; +} + +/* block IO */ +static inline void snd_sof_dsp_block_read(struct snd_sof_dev *sdev, + u32 offset, void *dest, size_t bytes) +{ + if (sdev->ops->block_read) + sdev->ops->block_read(sdev, offset, dest, bytes); +} + +static inline void snd_sof_dsp_block_write(struct snd_sof_dev *sdev, + u32 offset, void *src, size_t bytes) +{ + if (sdev->ops->block_write) + sdev->ops->block_write(sdev, offset, src, bytes); +} + +/* mailbox */ +static inline void snd_sof_dsp_mailbox_read(struct snd_sof_dev *sdev, + u32 offset, void *message, + size_t bytes) +{ + if (sdev->ops->mailbox_read) + sdev->ops->mailbox_read(sdev, offset, message, bytes); +} + +static inline void snd_sof_dsp_mailbox_write(struct snd_sof_dev *sdev, + u32 offset, void *message, + size_t bytes) +{ + if (sdev->ops->mailbox_write) + sdev->ops->mailbox_write(sdev, offset, message, bytes); +} + +/* ipc */ +static inline int snd_sof_dsp_send_msg(struct snd_sof_dev *sdev, + struct snd_sof_ipc_msg *msg) +{ + if (sdev->ops->send_msg) + return sdev->ops->send_msg(sdev, msg); + else + return 0; +} + +static inline int snd_sof_dsp_get_reply(struct snd_sof_dev *sdev, + struct snd_sof_ipc_msg *msg) +{ + if (sdev->ops->get_reply) + return sdev->ops->get_reply(sdev, msg); + else + return 0; +} + +static inline int snd_sof_dsp_is_ready(struct snd_sof_dev *sdev) +{ + if (sdev->ops->is_ready) + return sdev->ops->is_ready(sdev); + else + return 0; +} + +static inline int snd_sof_dsp_cmd_done(struct snd_sof_dev *sdev) +{ + if (sdev->ops->cmd_done) + return sdev->ops->cmd_done(sdev); + else + return 0; +} + +/* host DMA trace */ +static inline int snd_sof_dma_trace_init(struct snd_sof_dev *sdev, + u32 *stream_tag) +{ + if (sdev->ops->trace_init) + return sdev->ops->trace_init(sdev, stream_tag); + else + return 0; +} + +static inline int snd_sof_dma_trace_release(struct snd_sof_dev *sdev) +{ + if (sdev->ops->trace_release) + return sdev->ops->trace_release(sdev); + else + return 0; +} + +static inline int snd_sof_dma_trace_trigger(struct snd_sof_dev *sdev, int cmd) +{ + if (sdev->ops->trace_trigger) + return sdev->ops->trace_trigger(sdev, cmd); + else + return 0; +} + +int snd_sof_dsp_update_bits_unlocked(struct snd_sof_dev *sdev, u32 bar, + u32 offset, u32 mask, u32 value); + +int snd_sof_dsp_update_bits64_unlocked(struct snd_sof_dev *sdev, u32 bar, + u32 offset, u64 mask, u64 value); + +/* This is for registers bits with attribute RWC */ +void snd_sof_dsp_update_bits_forced_unlocked(struct snd_sof_dev *sdev, u32 bar, + u32 offset, u32 mask, u32 value); + +int snd_sof_dsp_update_bits(struct snd_sof_dev *sdev, u32 bar, u32 offset, + u32 mask, u32 value); + +int snd_sof_dsp_update_bits64(struct snd_sof_dev *sdev, u32 bar, + u32 offset, u64 mask, u64 value); + +/* This is for registers bits with attribute RWC */ +void snd_sof_dsp_update_bits_forced(struct snd_sof_dev *sdev, u32 bar, + u32 offset, u32 mask, u32 value); + +int snd_sof_pci_update_bits_unlocked(struct snd_sof_dev *sdev, u32 offset, + u32 mask, u32 value); + +int snd_sof_pci_update_bits(struct snd_sof_dev *sdev, u32 offset, + u32 mask, u32 value); + +int snd_sof_dsp_register_poll(struct snd_sof_dev *sdev, u32 bar, u32 offset, + u32 mask, u32 target, u32 timeout); + +void snd_sof_dsp_panic(struct snd_sof_dev *sdev, u32 offset); +#endif From 41ff0a488b22099476b53e9096d5bf66aa1e5e83 Mon Sep 17 00:00:00 2001 From: Liam Girdwood Date: Mon, 8 Jan 2018 20:34:01 +0000 Subject: [PATCH 032/107] ASoC: SOF: Add firmware loader support The firmware loader exports APIs that can be called by core to load and process multiple different file formats. Signed-off-by: Liam Girdwood --- sound/soc/sof/loader.c | 283 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 283 insertions(+) create mode 100644 sound/soc/sof/loader.c diff --git a/sound/soc/sof/loader.c b/sound/soc/sof/loader.c new file mode 100644 index 00000000000000..82d6c6ccc65fb2 --- /dev/null +++ b/sound/soc/sof/loader.c @@ -0,0 +1,283 @@ +// SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause) +/* + * This file is provided under a dual BSD/GPLv2 license. When using or + * redistributing this file, you may do so under either license. + * + * Copyright(c) 2017 Intel Corporation. All rights reserved. + * + * Author: Liam Girdwood + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "sof-priv.h" +#include "ops.h" + +static int get_ext_windows(struct snd_sof_dev *sdev, + struct sof_ipc_ext_data_hdr *ext_hdr) +{ + struct sof_ipc_window *w = (struct sof_ipc_window *)ext_hdr; + + int ret = 0; + size_t size; + + if (w->num_windows == 0 || w->num_windows > SOF_IPC_MAX_ELEMS) + return -EINVAL; + + size = sizeof(*w) + sizeof(struct sof_ipc_window_elem) * w->num_windows; + + /* keep a local copy of the data */ + sdev->info_window = kmemdup(w, size, GFP_KERNEL); + if (!sdev->info_window) + return -ENOMEM; + + return ret; +} + +int snd_sof_fw_parse_ext_data(struct snd_sof_dev *sdev, u32 offset) +{ + struct sof_ipc_ext_data_hdr *ext_hdr; + void *ext_data; + int ret = 0; + + ext_data = kzalloc(PAGE_SIZE, GFP_KERNEL); + if (!ext_data) + return -ENOMEM; + + /* get first header */ + snd_sof_dsp_block_read(sdev, offset, ext_data, sizeof(*ext_hdr)); + ext_hdr = (struct sof_ipc_ext_data_hdr *)ext_data; + + while (ext_hdr->hdr.cmd == SOF_IPC_FW_READY) { + /* read in ext structure */ + offset += sizeof(*ext_hdr); + snd_sof_dsp_block_read(sdev, offset, + ext_data + sizeof(*ext_hdr), + ext_hdr->hdr.size - sizeof(*ext_hdr)); + + dev_dbg(sdev->dev, "found ext header type %d size 0x%x\n", + ext_hdr->type, ext_hdr->hdr.size); + + /* process structure data */ + switch (ext_hdr->type) { + case SOF_IPC_EXT_DMA_BUFFER: + break; + case SOF_IPC_EXT_WINDOW: + ret = get_ext_windows(sdev, ext_hdr); + break; + default: + break; + } + + if (ret < 0) { + dev_err(sdev->dev, "error: failed to parse ext data type %d\n", + ext_hdr->type); + } + + /* move to next header */ + offset += ext_hdr->hdr.size; + snd_sof_dsp_block_read(sdev, offset, ext_data, + sizeof(*ext_hdr)); + ext_hdr = (struct sof_ipc_ext_data_hdr *)ext_data; + } + + kfree(ext_data); + return ret; +} +EXPORT_SYMBOL(snd_sof_fw_parse_ext_data); + +/* generic module parser for mmaped DSPs */ +int snd_sof_parse_module_memcpy(struct snd_sof_dev *sdev, + struct snd_sof_mod_hdr *module) +{ + struct snd_sof_blk_hdr *block; + int count; + u32 offset; + + dev_dbg(sdev->dev, "new module size 0x%x blocks 0x%x type 0x%x\n", + module->size, module->num_blocks, module->type); + + block = (void *)module + sizeof(*module); + + for (count = 0; count < module->num_blocks; count++) { + if (block->size == 0) { + dev_warn(sdev->dev, + "warning: block %d size zero\n", count); + dev_warn(sdev->dev, " type 0x%x offset 0x%x\n", + block->type, block->offset); + continue; + } + + switch (block->type) { + case SOF_BLK_IMAGE: + case SOF_BLK_CACHE: + case SOF_BLK_REGS: + case SOF_BLK_SIG: + case SOF_BLK_ROM: + continue; /* not handled atm */ + case SOF_BLK_TEXT: + case SOF_BLK_DATA: + offset = block->offset; + break; + default: + dev_err(sdev->dev, "error: bad type 0x%x for block 0x%x\n", + block->type, count); + return -EINVAL; + } + + dev_dbg(sdev->dev, + "block %d type 0x%x size 0x%x ==> offset 0x%x\n", + count, block->type, block->size, offset); + + snd_sof_dsp_block_write(sdev, offset, + (void *)block + sizeof(*block), + block->size); + + /* next block */ + block = (void *)block + sizeof(*block) + block->size; + } + + return 0; +} +EXPORT_SYMBOL(snd_sof_parse_module_memcpy); + +static int check_header(struct snd_sof_dev *sdev, const struct firmware *fw) +{ + struct snd_sof_fw_header *header; + + /* Read the header information from the data pointer */ + header = (struct snd_sof_fw_header *)fw->data; + + /* verify FW sig */ + if (strncmp(header->sig, SND_SOF_FW_SIG, SND_SOF_FW_SIG_SIZE) != 0) { + dev_err(sdev->dev, "error: invalid firmware signature\n"); + return -EINVAL; + } + + /* check size is valid */ + if (fw->size != header->file_size + sizeof(*header)) { + dev_err(sdev->dev, "error: invalid filesize mismatch got 0x%zx expected 0x%zx\n", + fw->size, header->file_size + sizeof(*header)); + return -EINVAL; + } + + dev_dbg(sdev->dev, "header size=0x%x modules=0x%x abi=0x%x size=%zu\n", + header->file_size, header->num_modules, + header->abi, sizeof(*header)); + + return 0; +} + +static int load_modules(struct snd_sof_dev *sdev, const struct firmware *fw) +{ + struct snd_sof_fw_header *header; + struct snd_sof_mod_hdr *module; + int (*load_module)(struct snd_sof_dev *sof_dev, + struct snd_sof_mod_hdr *hdr); + int ret, count; + + header = (struct snd_sof_fw_header *)fw->data; + load_module = sdev->ops->load_module; + if (!load_module) + return -EINVAL; + + /* parse each module */ + module = (void *)fw->data + sizeof(*header); + for (count = 0; count < header->num_modules; count++) { + /* module */ + ret = load_module(sdev, module); + if (ret < 0) { + dev_err(sdev->dev, "error: invalid module %d\n", count); + return ret; + } + module = (void *)module + sizeof(*module) + module->size; + } + + return 0; +} + +int snd_sof_load_firmware_memcpy(struct snd_sof_dev *sdev, + const struct firmware *fw) +{ + int ret; + + /* make sure the FW header and file is valid */ + ret = check_header(sdev, fw); + if (ret < 0) { + dev_err(sdev->dev, "error: invalid FW header\n"); + return ret; + } + + /* prepare the DSP for FW loading */ + ret = snd_sof_dsp_reset(sdev); + if (ret < 0) { + dev_err(sdev->dev, "error: failed to reset DSP\n"); + return ret; + } + + /* parse and load firmware modules to DSP */ + ret = load_modules(sdev, fw); + if (ret < 0) { + dev_err(sdev->dev, "error: invalid FW modules\n"); + return ret; + } + + return ret; +} +EXPORT_SYMBOL(snd_sof_load_firmware_memcpy); + +int snd_sof_load_firmware(struct snd_sof_dev *sdev, + const struct firmware *fw) +{ + dev_dbg(sdev->dev, "loading firmware\n"); + + if (sdev->ops->load_firmware) + return sdev->ops->load_firmware(sdev, fw); + return 0; +} +EXPORT_SYMBOL(snd_sof_load_firmware); + +int snd_sof_run_firmware(struct snd_sof_dev *sdev) +{ + int ret; + + init_waitqueue_head(&sdev->boot_wait); + sdev->boot_complete = false; + + dev_dbg(sdev->dev, "booting DSP firmware\n"); + + /* boot the firmware on the DSP */ + ret = snd_sof_dsp_run(sdev); + if (ret < 0) { + dev_err(sdev->dev, "error: failed to reset DSP\n"); + return ret; + } + + /* now wait for the DSP to boot */ + ret = wait_event_timeout(sdev->boot_wait, sdev->boot_complete, + msecs_to_jiffies(sdev->boot_timeout)); + if (ret == 0) { + dev_err(sdev->dev, "error: firmware boot timeout\n"); + snd_sof_dsp_dbg_dump(sdev, SOF_DBG_REGS | SOF_DBG_MBOX | + SOF_DBG_TEXT | SOF_DBG_PCI); + return -EIO; + } + + dev_info(sdev->dev, "firmware boot complete\n"); + + return 0; +} +EXPORT_SYMBOL(snd_sof_run_firmware); + +void snd_sof_fw_unload(struct snd_sof_dev *sdev) +{ +} +EXPORT_SYMBOL(snd_sof_fw_unload); From a8711d68ea39f819dabb44cb8fd13d3d4564b138 Mon Sep 17 00:00:00 2001 From: Liam Girdwood Date: Mon, 8 Jan 2018 20:34:16 +0000 Subject: [PATCH 033/107] ASoC: SOF: Add compressed PCM support Add support for compressed audio playback/capture in SOF. TODO: to be completed. Signed-off-by: Liam Girdwood --- sound/soc/sof/compressed.c | 168 +++++++++++++++++++++++++++++++++++++ 1 file changed, 168 insertions(+) create mode 100644 sound/soc/sof/compressed.c diff --git a/sound/soc/sof/compressed.c b/sound/soc/sof/compressed.c new file mode 100644 index 00000000000000..64b35fb0e6e5ed --- /dev/null +++ b/sound/soc/sof/compressed.c @@ -0,0 +1,168 @@ +// SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause) +/* + * This file is provided under a dual BSD/GPLv2 license. When using or + * redistributing this file, you may do so under either license. + * + * Copyright(c) 2017 Intel Corporation. All rights reserved. + * + * Author: Liam Girdwood + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "sof-priv.h" + +static int sof_compressed_open(struct snd_compr_stream *cstream) +{ + struct snd_soc_pcm_runtime *rtd = cstream->private_data; + struct snd_sof_dev *sdev = + snd_soc_platform_get_drvdata(rtd->platform); + struct snd_sof_pcm *spcm = rtd->sof; + + mutex_lock(&spcm->mutex); + pm_runtime_get_sync(sdev->dev); + mutex_unlock(&spcm->mutex); + return 0; +} + +static int sof_compressed_free(struct snd_compr_stream *cstream) +{ + struct snd_soc_pcm_runtime *rtd = cstream->private_data; + struct snd_sof_dev *sdev = + snd_soc_platform_get_drvdata(rtd->platform); + struct snd_sof_pcm *spcm = rtd->sof; + + mutex_lock(&spcm->mutex); + pm_runtime_put(sdev->dev); + mutex_unlock(&spcm->mutex); + return 0; +} + +static int sof_vorbis_set_params(struct snd_compr_stream *cstream, + struct snd_compr_params *params) +{ + return 0; +} + +static int sof_mp3_set_params(struct snd_compr_stream *cstream, + struct snd_compr_params *params) +{ + return 0; +} + +static int sof_compressed_set_params(struct snd_compr_stream *cstream, + struct snd_compr_params *params) +{ + struct snd_soc_pcm_runtime *rtd = cstream->private_data; + struct snd_sof_dev *sdev = + snd_soc_platform_get_drvdata(rtd->platform); + + switch (params->codec.id) { + case SND_AUDIOCODEC_VORBIS: + return sof_vorbis_set_params(cstream, params); + case SND_AUDIOCODEC_MP3: + return sof_mp3_set_params(cstream, params); + default: + dev_err(sdev->dev, "error: codec id %d not supported\n", + params->codec.id); + return -EINVAL; + } +} + +static int sof_compressed_trigger(struct snd_compr_stream *cstream, int cmd) +{ + struct snd_soc_pcm_runtime *rtd = cstream->private_data; + struct snd_sof_dev *sdev = + snd_soc_platform_get_drvdata(rtd->platform); + struct snd_sof_pcm *spcm = rtd->sof; + struct sof_ipc_stream stream; + struct sof_ipc_reply reply; + + stream.hdr.size = sizeof(stream); + stream.hdr.cmd = SOF_IPC_GLB_STREAM_MSG; + stream.comp_id = spcm->stream[cstream->direction].comp_id; + + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + stream.hdr.cmd |= SOF_IPC_STREAM_TRIG_START; + break; + case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: + stream.hdr.cmd |= SOF_IPC_STREAM_TRIG_RELEASE; + break; + case SNDRV_PCM_TRIGGER_STOP: + stream.hdr.cmd |= SOF_IPC_STREAM_TRIG_STOP; + break; + case SNDRV_PCM_TRIGGER_PAUSE_PUSH: + stream.hdr.cmd |= SOF_IPC_STREAM_TRIG_PAUSE; + break; + case SNDRV_PCM_TRIGGER_SUSPEND: + case SNDRV_PCM_TRIGGER_RESUME: + default: + break; + } + + /* send IPC to the DSP */ + return sof_ipc_tx_message(sdev->ipc, stream.hdr.cmd, &stream, + sizeof(stream), &reply, sizeof(reply)); +} + +static int sof_compressed_pointer(struct snd_compr_stream *cstream, + struct snd_compr_tstamp *tstamp) +{ + struct snd_soc_pcm_runtime *rtd = cstream->private_data; + struct snd_sof_dev *sdev = + snd_soc_platform_get_drvdata(rtd->platform); + struct sof_ipc_stream_posn posn; + struct snd_sof_pcm *spcm = rtd->sof; + + snd_sof_ipc_stream_posn(sdev, spcm, cstream->direction, &posn); + + dev_vdbg(sdev->dev, "CPCM: DMA position %llu DAI position %llu\n", + posn.host_posn, posn.dai_posn); + + return 0; +} + +static int sof_compressed_ack(struct snd_compr_stream *cstream, + size_t bytes) +{ + return 0; +} + +static int sof_compressed_get_caps(struct snd_compr_stream *cstream, + struct snd_compr_caps *caps) +{ + return 0; +} + +static int sof_compressed_get_codec_caps(struct snd_compr_stream *cstream, + struct snd_compr_codec_caps *codec) +{ + return 0; +} + +static int sof_compressed_set_metadata(struct snd_compr_stream *cstream, + struct snd_compr_metadata *metadata) +{ + return 0; +} + +struct snd_compr_ops sof_compressed_ops = { + .open = sof_compressed_open, + .free = sof_compressed_free, + .set_params = sof_compressed_set_params, + .set_metadata = sof_compressed_set_metadata, + .trigger = sof_compressed_trigger, + .pointer = sof_compressed_pointer, + .ack = sof_compressed_ack, + .get_caps = sof_compressed_get_caps, + .get_codec_caps = sof_compressed_get_codec_caps, +}; From 7c7884f7270f872af709eb49f76a431a13fe5cda Mon Sep 17 00:00:00 2001 From: Liam Girdwood Date: Tue, 16 Jan 2018 15:36:57 +0000 Subject: [PATCH 034/107] ASoC: SOF: Add PM support Add support for saving and restoring DSP context in D3 to host DDR. Signed-off-by: Liam Girdwood --- sound/soc/sof/pm.c | 48 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 48 insertions(+) create mode 100644 sound/soc/sof/pm.c diff --git a/sound/soc/sof/pm.c b/sound/soc/sof/pm.c new file mode 100644 index 00000000000000..d825ff402fc66a --- /dev/null +++ b/sound/soc/sof/pm.c @@ -0,0 +1,48 @@ +// SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause) +/* + * This file is provided under a dual BSD/GPLv2 license. When using or + * redistributing this file, you may do so under either license. + * + * Copyright(c) 2017 Intel Corporation. All rights reserved. + * + * Author: Liam Girdwood + */ + +#include +#include +#include +#include +#include +#include +#include +#include "sof-priv.h" + +int snd_sof_runtime_suspend(struct device *dev) +{ + return 0; +} +EXPORT_SYMBOL(snd_sof_runtime_suspend); + +int snd_sof_runtime_resume(struct device *dev) +{ + return 0; +} +EXPORT_SYMBOL(snd_sof_runtime_resume); + +int snd_sof_resume(struct device *dev) +{ + return 0; +} +EXPORT_SYMBOL(snd_sof_resume); + +int snd_sof_suspend(struct device *dev) +{ + return 0; +} +EXPORT_SYMBOL(snd_sof_suspend); + +int snd_sof_suspend_late(struct device *dev) +{ + return 0; +} +EXPORT_SYMBOL(snd_sof_suspend_late); From 543caaf6ef64b52cdce9bc91a05d30eff7de723a Mon Sep 17 00:00:00 2001 From: Liam Girdwood Date: Mon, 8 Jan 2018 20:37:08 +0000 Subject: [PATCH 035/107] ASoC: SOF: Add Nocodec machine driver support Add a simple "fallback" machine driver that can be used to enable SOF on boards with no codec device. This machine driver can also be forced for debug/development. Signed-off-by: Liam Girdwood --- include/sound/sof.h | 5 ++ sound/soc/sof/nocodec.c | 114 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 119 insertions(+) create mode 100644 sound/soc/sof/nocodec.c diff --git a/include/sound/sof.h b/include/sound/sof.h index 7767ec8ce188d6..f6056247c3a75f 100644 --- a/include/sound/sof.h +++ b/include/sound/sof.h @@ -73,4 +73,9 @@ struct sof_dev_desc { const char *nocodec_tplg_filename; }; +int sof_nocodec_setup(struct device *dev, + struct snd_sof_pdata *sof_pdata, + struct snd_soc_acpi_mach *mach, + const struct sof_dev_desc *desc); + #endif diff --git a/sound/soc/sof/nocodec.c b/sound/soc/sof/nocodec.c new file mode 100644 index 00000000000000..0685d29f9d28b1 --- /dev/null +++ b/sound/soc/sof/nocodec.c @@ -0,0 +1,114 @@ +// SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause) +/* + * This file is provided under a dual BSD/GPLv2 license. When using or + * redistributing this file, you may do so under either license. + * + * Copyright(c) 2017 Intel Corporation. All rights reserved. + * + * Author: Liam Girdwood + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +int sof_nocodec_setup(struct device *dev, + struct snd_sof_pdata *sof_pdata, + struct snd_soc_acpi_mach *mach, + const struct sof_dev_desc *desc) +{ + if (!mach) + return -EINVAL; + + sof_pdata->drv_name = "sof-nocodec"; + + mach->drv_name = "sof-nocodec"; + mach->sof_fw_filename = desc->nocodec_fw_filename; + mach->sof_tplg_filename = desc->nocodec_tplg_filename; + + return 0; +} +EXPORT_SYMBOL(sof_nocodec_setup); + +static int sof_nocodec_codec_fixup(struct snd_soc_pcm_runtime *rtd, + struct snd_pcm_hw_params *params) +{ + // TODO: read this from topology + return 0; +} + +static struct snd_soc_ops sof_nocodec_ops = {}; + +static int nocodec_rtd_init(struct snd_soc_pcm_runtime *rtd) +{ + snd_soc_set_dmi_name(rtd->card, NULL); + + return 0; +} + +/* we just set some BEs - FE provided by topology */ +static struct snd_soc_dai_link sof_nocodec_dais[] = { + /* Back End DAI links */ + { + /* SSP0 - Codec */ + .name = "NoCodec", + .id = 0, + .init = nocodec_rtd_init, + .cpu_dai_name = "sof-audio", + .platform_name = "sof-audio", + .no_pcm = 1, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + .ops = &sof_nocodec_ops, + .dai_fmt = SND_SOC_DAIFMT_DSP_B | SND_SOC_DAIFMT_NB_NF | + SND_SOC_DAIFMT_CBS_CFS, + .ignore_suspend = 1, + .be_hw_params_fixup = sof_nocodec_codec_fixup, + .dpcm_playback = 1, + .dpcm_capture = 1, + }, +}; + +static struct snd_soc_card sof_nocodec_card = { + .name = "sof-nocodec", + .dai_link = sof_nocodec_dais, + .num_links = ARRAY_SIZE(sof_nocodec_dais), +}; + +static int sof_nocodec_probe(struct platform_device *pdev) +{ + struct snd_soc_card *card = &sof_nocodec_card; + + card->dev = &pdev->dev; + + return devm_snd_soc_register_card(&pdev->dev, card); +} + +static int sof_nocodec_remove(struct platform_device *pdev) +{ + return 0; +} + +static struct platform_driver sof_nocodec_audio = { + .probe = sof_nocodec_probe, + .remove = sof_nocodec_remove, + .driver = { + .name = "sof-nocodec", + .pm = &snd_soc_pm_ops, + }, +}; +module_platform_driver(sof_nocodec_audio) + +MODULE_DESCRIPTION("ASoC sof nocodec"); +MODULE_AUTHOR("Liam Girdwood"); +MODULE_LICENSE("Dual BSD/GPL"); +MODULE_ALIAS("platform:sof-nocodec"); From 8c5bb0fade1c8e6605bec547d67ea28cc3e18071 Mon Sep 17 00:00:00 2001 From: Liam Girdwood Date: Mon, 8 Jan 2018 20:37:40 +0000 Subject: [PATCH 036/107] ASoC: SOF: Intel: Add BYT, CHT and BSW DSP HW support. Add support for the audio DSP hardware found on Intel Baytrail, Cherrytrail and Braswell based devices. Signed-off-by: Liam Girdwood --- sound/soc/sof/intel/byt.c | 846 +++++++++++++++++++++++++++++++++++++ sound/soc/sof/intel/shim.h | 157 +++++++ 2 files changed, 1003 insertions(+) create mode 100644 sound/soc/sof/intel/byt.c create mode 100644 sound/soc/sof/intel/shim.h diff --git a/sound/soc/sof/intel/byt.c b/sound/soc/sof/intel/byt.c new file mode 100644 index 00000000000000..3103987207e4ae --- /dev/null +++ b/sound/soc/sof/intel/byt.c @@ -0,0 +1,846 @@ +// SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause) +/* + * This file is provided under a dual BSD/GPLv2 license. When using or + * redistributing this file, you may do so under either license. + * + * Copyright(c) 2017 Intel Corporation. All rights reserved. + * + * Author: Liam Girdwood + */ + +/* + * Hardware interface for audio DSP on Baytrail, Braswell and Cherrytrail. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "../sof-priv.h" +#include "../ops.h" +#include "shim.h" + +/* DSP memories */ +#define IRAM_OFFSET 0x0C0000 +#define IRAM_SIZE (80 * 1024) +#define DRAM_OFFSET 0x100000 +#define DRAM_SIZE (160 * 1024) +#define SHIM_OFFSET 0x140000 +#define SHIM_SIZE 0x100 +#define MBOX_OFFSET 0x144000 +#define MBOX_SIZE 0x1000 +#define EXCEPT_OFFSET 0x800 + +/* DSP peripherals */ +#define DMAC0_OFFSET 0x098000 +#define DMAC1_OFFSET 0x09c000 +#define DMAC2_OFFSET 0x094000 +#define DMAC_SIZE 0x420 +#define SSP0_OFFSET 0x0a0000 +#define SSP1_OFFSET 0x0a1000 +#define SSP2_OFFSET 0x0a2000 +#define SSP3_OFFSET 0x0a4000 +#define SSP4_OFFSET 0x0a5000 +#define SSP5_OFFSET 0x0a6000 +#define SSP_SIZE 0x100 + +#define BYT_STACK_DUMP_SIZE 32 + +#define BYT_PCI_BAR_SIZE 0x200000 + +#define BYT_PANIC_OFFSET(x) (((x) & (0xFFFFll << 32)) >> 32) + +/* + * Debug + */ + +#define MBOX_DUMP_SIZE 0x30 + +/* BARs */ +#define BYT_DSP_BAR 0 +#define BYT_PCI_BAR 1 +#define BYT_IMR_BAR 2 + +static const struct snd_sof_debugfs_map byt_debugfs[] = { + {"dmac0", BYT_DSP_BAR, DMAC0_OFFSET, DMAC_SIZE}, + {"dmac1", BYT_DSP_BAR, DMAC1_OFFSET, DMAC_SIZE}, + {"ssp0", BYT_DSP_BAR, SSP0_OFFSET, SSP_SIZE}, + {"ssp1", BYT_DSP_BAR, SSP1_OFFSET, SSP_SIZE}, + {"ssp2", BYT_DSP_BAR, SSP2_OFFSET, SSP_SIZE}, + {"iram", BYT_DSP_BAR, IRAM_OFFSET, IRAM_SIZE}, + {"dram", BYT_DSP_BAR, DRAM_OFFSET, DRAM_SIZE}, + {"shim", BYT_DSP_BAR, SHIM_OFFSET, SHIM_SIZE}, +}; + +static const struct snd_sof_debugfs_map cht_debugfs[] = { + {"dmac0", BYT_DSP_BAR, DMAC0_OFFSET, DMAC_SIZE}, + {"dmac1", BYT_DSP_BAR, DMAC1_OFFSET, DMAC_SIZE}, + {"dmac2", BYT_DSP_BAR, DMAC2_OFFSET, DMAC_SIZE}, + {"ssp0", BYT_DSP_BAR, SSP0_OFFSET, SSP_SIZE}, + {"ssp1", BYT_DSP_BAR, SSP1_OFFSET, SSP_SIZE}, + {"ssp2", BYT_DSP_BAR, SSP2_OFFSET, SSP_SIZE}, + {"ssp3", BYT_DSP_BAR, SSP3_OFFSET, SSP_SIZE}, + {"ssp4", BYT_DSP_BAR, SSP4_OFFSET, SSP_SIZE}, + {"ssp5", BYT_DSP_BAR, SSP5_OFFSET, SSP_SIZE}, + {"iram", BYT_DSP_BAR, IRAM_OFFSET, IRAM_SIZE}, + {"dram", BYT_DSP_BAR, DRAM_OFFSET, DRAM_SIZE}, + {"shim", BYT_DSP_BAR, SHIM_OFFSET, SHIM_SIZE}, +}; + +/* + * Register IO + */ + +static void byt_write(struct snd_sof_dev *sdev, void __iomem *addr, + u32 value) +{ + writel(value, addr); +} + +static u32 byt_read(struct snd_sof_dev *sdev, void __iomem *addr) +{ + return readl(addr); +} + +static void byt_write64(struct snd_sof_dev *sdev, void __iomem *addr, + u64 value) +{ + memcpy_toio(addr, &value, sizeof(value)); +} + +static u64 byt_read64(struct snd_sof_dev *sdev, void __iomem *addr) +{ + u64 val; + + memcpy_fromio(&val, addr, sizeof(val)); + return val; +} + +/* + * Memory copy. + */ + +static void byt_block_write(struct snd_sof_dev *sdev, u32 offset, void *src, + size_t size) +{ + void __iomem *dest = sdev->bar[sdev->mmio_bar] + offset; + u32 tmp = 0; + int i, m, n; + const u8 *src_byte = src; + + m = size / 4; + n = size % 4; + + /* __iowrite32_copy use 32bit size values so divide by 4 */ + __iowrite32_copy((void *)dest, src, m); + + if (n) { + for (i = 0; i < n; i++) + tmp |= (u32)*(src_byte + m * 4 + i) << (i * 8); + __iowrite32_copy((void *)(dest + m * 4), &tmp, 1); + } +} + +static void byt_block_read(struct snd_sof_dev *sdev, u32 offset, void *dest, + size_t size) +{ + void __iomem *src = sdev->bar[sdev->mmio_bar] + offset; + + memcpy_fromio(dest, src, size); +} + +/* + * IPC Firmware ready. + */ +static void byt_get_windows(struct snd_sof_dev *sdev) +{ + struct sof_ipc_window_elem *elem; + u32 outbox_offset = 0; + u32 stream_offset = 0; + u32 inbox_offset = 0; + u32 outbox_size = 0; + u32 stream_size = 0; + u32 inbox_size = 0; + int i; + + if (!sdev->info_window) { + dev_err(sdev->dev, "error: have no window info\n"); + return; + } + + for (i = 0; i < sdev->info_window->num_windows; i++) { + elem = &sdev->info_window->window[i]; + + switch (elem->type) { + case SOF_IPC_REGION_UPBOX: + inbox_offset = elem->offset + MBOX_OFFSET; + inbox_size = elem->size; + snd_sof_debugfs_create_item(sdev, + sdev->bar[BYT_DSP_BAR] + + inbox_offset, + elem->size, "inbox"); + break; + case SOF_IPC_REGION_DOWNBOX: + outbox_offset = elem->offset + MBOX_OFFSET; + outbox_size = elem->size; + snd_sof_debugfs_create_item(sdev, + sdev->bar[BYT_DSP_BAR] + + outbox_offset, + elem->size, "outbox"); + break; + case SOF_IPC_REGION_TRACE: + snd_sof_debugfs_create_item(sdev, + sdev->bar[BYT_DSP_BAR] + + elem->offset + MBOX_OFFSET, + elem->size, "etrace"); + break; + case SOF_IPC_REGION_DEBUG: + snd_sof_debugfs_create_item(sdev, + sdev->bar[BYT_DSP_BAR] + + elem->offset + MBOX_OFFSET, + elem->size, "debug"); + break; + case SOF_IPC_REGION_STREAM: + stream_offset = elem->offset + MBOX_OFFSET; + stream_size = elem->size; + snd_sof_debugfs_create_item(sdev, + sdev->bar[BYT_DSP_BAR] + + stream_offset, + elem->size, "stream"); + break; + case SOF_IPC_REGION_REGS: + snd_sof_debugfs_create_item(sdev, + sdev->bar[BYT_DSP_BAR] + + elem->offset + MBOX_OFFSET, + elem->size, "regs"); + break; + case SOF_IPC_REGION_EXCEPTION: + sdev->dsp_oops_offset = elem->offset + MBOX_OFFSET; + snd_sof_debugfs_create_item(sdev, + sdev->bar[BYT_DSP_BAR] + + elem->offset + MBOX_OFFSET, + elem->size, "exception"); + break; + default: + dev_err(sdev->dev, "error: get illegal window info\n"); + return; + } + } + + if (outbox_size == 0 || inbox_size == 0) { + dev_err(sdev->dev, "error: get illegal mailbox window\n"); + return; + } + + snd_sof_dsp_mailbox_init(sdev, inbox_offset, inbox_size, + outbox_offset, outbox_size); + sdev->stream_box.offset = stream_offset; + sdev->stream_box.size = stream_size; + + dev_dbg(sdev->dev, " mailbox upstream 0x%x - size 0x%x\n", + inbox_offset, inbox_size); + dev_dbg(sdev->dev, " mailbox downstream 0x%x - size 0x%x\n", + outbox_offset, outbox_size); + dev_dbg(sdev->dev, " stream region 0x%x - size 0x%x\n", + stream_offset, stream_size); +} + +static int byt_fw_ready(struct snd_sof_dev *sdev, u32 msg_id) +{ + struct sof_ipc_fw_ready *fw_ready = &sdev->fw_ready; + struct sof_ipc_fw_version *v = &fw_ready->version; + u32 offset; + + /* mailbox must be on 4k boundary */ + offset = MBOX_OFFSET; + + dev_dbg(sdev->dev, "ipc: DSP is ready 0x%8.8x offset 0x%x\n", + msg_id, offset); + + /* copy data from the DSP FW ready offset */ + byt_block_read(sdev, offset, fw_ready, sizeof(*fw_ready)); + + snd_sof_dsp_mailbox_init(sdev, fw_ready->dspbox_offset, + fw_ready->dspbox_size, + fw_ready->hostbox_offset, + fw_ready->hostbox_size); + + dev_info(sdev->dev, + " Firmware info: version %d:%d-%s build %d on %s:%s\n", + v->major, v->minor, v->tag, v->build, v->date, v->time); + + /* now check for extended data */ + snd_sof_fw_parse_ext_data(sdev, MBOX_OFFSET + + sizeof(struct sof_ipc_fw_ready)); + + byt_get_windows(sdev); + + return 0; +} + +/* + * IPC Mailbox IO + */ + +static void byt_mailbox_write(struct snd_sof_dev *sdev, u32 offset, + void *message, size_t bytes) +{ + void __iomem *dest = sdev->bar[sdev->mailbox_bar] + offset; + + memcpy_toio(dest, message, bytes); +} + +static void byt_mailbox_read(struct snd_sof_dev *sdev, u32 offset, + void *message, size_t bytes) +{ + void __iomem *src = sdev->bar[sdev->mailbox_bar] + offset; + + memcpy_fromio(message, src, bytes); +} + +/* + * Debug + */ + +static void byt_get_registers(struct snd_sof_dev *sdev, + struct sof_ipc_dsp_oops_xtensa *xoops, + u32 *stack, size_t stack_words) +{ + /* first read regsisters */ + byt_mailbox_read(sdev, sdev->dsp_oops_offset, xoops, sizeof(*xoops)); + + /* the get the stack */ + byt_mailbox_read(sdev, sdev->dsp_oops_offset + sizeof(*xoops), stack, + stack_words * sizeof(u32)); +} + +static void byt_dump(struct snd_sof_dev *sdev, u32 flags) +{ + struct sof_ipc_dsp_oops_xtensa xoops; + u32 stack[BYT_STACK_DUMP_SIZE]; + u32 status, panic; + + /* now try generic SOF status messages */ + status = snd_sof_dsp_read(sdev, BYT_DSP_BAR, SHIM_IPCD); + panic = snd_sof_dsp_read(sdev, BYT_DSP_BAR, SHIM_IPCX); + byt_get_registers(sdev, &xoops, stack, BYT_STACK_DUMP_SIZE); + snd_sof_get_status(sdev, status, panic, &xoops, stack, + BYT_STACK_DUMP_SIZE); +} + +/* + * IPC Doorbell IRQ handler and thread. + */ + +static irqreturn_t byt_irq_handler(int irq, void *context) +{ + struct snd_sof_dev *sdev = (struct snd_sof_dev *)context; + u64 isr; + int ret = IRQ_NONE; + + /* Interrupt arrived, check src */ + isr = snd_sof_dsp_read64(sdev, BYT_DSP_BAR, SHIM_ISRX); + if (isr & SHIM_ISRX_DONE) { + /* Mask Done interrupt before return */ + snd_sof_dsp_update_bits64_unlocked(sdev, BYT_DSP_BAR, SHIM_IMRX, + SHIM_IMRX_DONE, + SHIM_IMRX_DONE); + ret = IRQ_WAKE_THREAD; + } + + if (isr & SHIM_ISRX_BUSY) { + /* Mask Busy interrupt before return */ + snd_sof_dsp_update_bits64_unlocked(sdev, BYT_DSP_BAR, SHIM_IMRX, + SHIM_IMRX_BUSY, + SHIM_IMRX_BUSY); + ret = IRQ_WAKE_THREAD; + } + + return ret; +} + +static irqreturn_t byt_irq_thread(int irq, void *context) +{ + struct snd_sof_dev *sdev = (struct snd_sof_dev *)context; + u64 ipcx, ipcd; + + ipcx = snd_sof_dsp_read64(sdev, BYT_DSP_BAR, SHIM_IPCX); + + /* reply message from DSP */ + if (ipcx & SHIM_BYT_IPCX_DONE) { + /* Handle Immediate reply from DSP Core */ + snd_sof_ipc_reply(sdev, ipcx); + + /* clear DONE bit - tell DSP we have completed */ + snd_sof_dsp_update_bits64_unlocked(sdev, BYT_DSP_BAR, SHIM_IPCX, + SHIM_BYT_IPCX_DONE, 0); + + /* unmask Done interrupt */ + snd_sof_dsp_update_bits64_unlocked(sdev, BYT_DSP_BAR, SHIM_IMRX, + SHIM_IMRX_DONE, 0); + } + + /* new message from DSP */ + ipcd = snd_sof_dsp_read64(sdev, BYT_DSP_BAR, SHIM_IPCD); + if (ipcd & SHIM_BYT_IPCD_BUSY) { + /* Handle messages from DSP Core */ + if ((ipcd & SOF_IPC_PANIC_MAGIC_MASK) == SOF_IPC_PANIC_MAGIC) { + snd_sof_dsp_panic(sdev, BYT_PANIC_OFFSET(ipcd) + + MBOX_OFFSET); + } else { + snd_sof_ipc_msgs_rx(sdev); + } + } + + return IRQ_HANDLED; +} + +static int byt_is_ready(struct snd_sof_dev *sdev) +{ + u64 imrx; + + imrx = snd_sof_dsp_read64(sdev, BYT_DSP_BAR, SHIM_IMRX); + if (imrx & SHIM_IMRX_DONE) + return 0; + + return 1; +} + +static int byt_send_msg(struct snd_sof_dev *sdev, struct snd_sof_ipc_msg *msg) +{ + u64 cmd = msg->header; + + /* send the message */ + byt_mailbox_write(sdev, sdev->host_box.offset, msg->msg_data, + msg->msg_size); + snd_sof_dsp_write64(sdev, BYT_DSP_BAR, SHIM_IPCX, + cmd | SHIM_BYT_IPCX_BUSY); + + return 0; +} + +static int byt_get_reply(struct snd_sof_dev *sdev, struct snd_sof_ipc_msg *msg) +{ + struct sof_ipc_reply reply; + int ret = 0; + u32 size; + + /* get reply */ + byt_mailbox_read(sdev, sdev->host_box.offset, &reply, sizeof(reply)); + if (reply.error < 0) { + size = sizeof(reply); + ret = reply.error; + } else { + /* reply correct size ? */ + if (reply.hdr.size != msg->reply_size) { + dev_err(sdev->dev, "error: reply expected 0x%zx got 0x%x bytes\n", + msg->reply_size, reply.hdr.size); + size = msg->reply_size; + ret = -EINVAL; + } else { + size = reply.hdr.size; + } + } + + /* read the message */ + if (msg->msg_data && size > 0) + byt_mailbox_read(sdev, sdev->host_box.offset, msg->reply_data, + size); + + return ret; +} + +static int byt_cmd_done(struct snd_sof_dev *sdev) +{ + /* clear BUSY bit and set DONE bit - accept new messages */ + snd_sof_dsp_update_bits64_unlocked(sdev, BYT_DSP_BAR, SHIM_IPCD, + SHIM_BYT_IPCD_BUSY | + SHIM_BYT_IPCD_DONE, + SHIM_BYT_IPCD_DONE); + + /* unmask busy interrupt */ + snd_sof_dsp_update_bits64_unlocked(sdev, BYT_DSP_BAR, SHIM_IMRX, + SHIM_IMRX_BUSY, 0); + + return 0; +} + +/* + * DSP control. + */ + +static int byt_run(struct snd_sof_dev *sdev) +{ + int tries = 10; + + /* release stall and wait to unstall */ + snd_sof_dsp_update_bits64(sdev, BYT_DSP_BAR, SHIM_CSR, + SHIM_BYT_CSR_STALL, 0x0); + while (tries--) { + if (!(snd_sof_dsp_read64(sdev, BYT_DSP_BAR, SHIM_CSR) & + SHIM_BYT_CSR_PWAITMODE)) + break; + msleep(100); + } + if (tries < 0) { + dev_err(sdev->dev, "error: unable to run DSP firmware\n"); + byt_dump(sdev, SOF_DBG_REGS | SOF_DBG_MBOX); + return -ENODEV; + } + + return 0; +} + +static int byt_reset(struct snd_sof_dev *sdev) +{ + /* put DSP into reset, set reset vector and stall */ + snd_sof_dsp_update_bits64(sdev, BYT_DSP_BAR, SHIM_CSR, + SHIM_BYT_CSR_RST | SHIM_BYT_CSR_VECTOR_SEL | + SHIM_BYT_CSR_STALL, + SHIM_BYT_CSR_RST | SHIM_BYT_CSR_VECTOR_SEL | + SHIM_BYT_CSR_STALL); + + usleep_range(10, 15); + + /* take DSP out of reset and keep stalled for FW loading */ + snd_sof_dsp_update_bits64(sdev, BYT_DSP_BAR, SHIM_CSR, + SHIM_BYT_CSR_RST, 0); + + return 0; +} + +/* + * Probe and remove. + */ + +static int byt_acpi_probe(struct snd_sof_dev *sdev) +{ + struct snd_sof_pdata *pdata = sdev->pdata; + const struct sof_dev_desc *desc = pdata->desc; + struct platform_device *pdev = + container_of(sdev->parent, struct platform_device, dev); + struct resource *mmio; + u32 base, size; + int ret = 0; + + /* DSP DMA can only access low 31 bits of host memory */ + ret = dma_coerce_mask_and_coherent(sdev->dev, DMA_BIT_MASK(31)); + if (ret < 0) { + dev_err(sdev->dev, "error: failed to set DMA mask %d\n", ret); + return ret; + } + + /* LPE base */ + mmio = platform_get_resource(pdev, IORESOURCE_MEM, + desc->resindex_lpe_base); + if (mmio) { + base = mmio->start; + size = resource_size(mmio); + } else { + dev_err(sdev->dev, "error: failed to get LPE base at idx %d\n", + desc->resindex_lpe_base); + return -EINVAL; + } + + dev_dbg(sdev->dev, "LPE PHY base at 0x%x size 0x%x", base, size); + sdev->bar[BYT_DSP_BAR] = ioremap(base, size); + if (!sdev->bar[BYT_DSP_BAR]) { + dev_err(sdev->dev, "error: failed to ioremap LPE base 0x%x size 0x%x\n", + base, size); + return -ENODEV; + } + dev_dbg(sdev->dev, "LPE VADDR %p\n", sdev->bar[BYT_DSP_BAR]); + + /* TODO: add offsets */ + sdev->mmio_bar = BYT_DSP_BAR; + sdev->mailbox_bar = BYT_DSP_BAR; + + /* IMR base - optional */ + if (desc->resindex_imr_base == -1) + goto irq; + + mmio = platform_get_resource(pdev, IORESOURCE_MEM, + desc->resindex_imr_base); + if (mmio) { + base = mmio->start; + size = resource_size(mmio); + } else { + dev_err(sdev->dev, "error: failed to get IMR base at idx %d\n", + desc->resindex_imr_base); + ret = -ENODEV; + goto imr_err; + } + + /* some BIOSes don't map IMR */ + if (base == 0x55aa55aa || base == 0x0) { + dev_info(sdev->dev, "IMR not set by BIOS. Ignoring\n"); + goto irq; + } + + dev_dbg(sdev->dev, "IMR base at 0x%x size 0x%x", base, size); + sdev->bar[BYT_IMR_BAR] = ioremap(base, size); + if (!sdev->bar[BYT_IMR_BAR]) { + dev_err(sdev->dev, "error: failed to ioremap IMR base 0x%x size 0x%x\n", + base, size); + ret = -ENODEV; + goto imr_err; + } + dev_dbg(sdev->dev, "IMR VADDR %p\n", sdev->bar[BYT_IMR_BAR]); + +irq: + /* register our IRQ */ + sdev->ipc_irq = platform_get_irq(pdev, desc->irqindex_host_ipc); + if (sdev->ipc_irq < 0) { + dev_err(sdev->dev, "error: failed to get IRQ at index %d\n", + desc->irqindex_host_ipc); + ret = sdev->ipc_irq; + goto irq_err; + } + + dev_dbg(sdev->dev, "using IRQ %d\n", sdev->ipc_irq); + ret = request_threaded_irq(sdev->ipc_irq, byt_irq_handler, + byt_irq_thread, IRQF_SHARED, "AudioDSP", + sdev); + if (ret < 0) { + dev_err(sdev->dev, "error: failed to register IRQ %d\n", + sdev->ipc_irq); + goto irq_err; + } + + /* enable Interrupt from both sides */ + snd_sof_dsp_update_bits64(sdev, BYT_DSP_BAR, SHIM_IMRX, 0x3, 0x0); + snd_sof_dsp_update_bits64(sdev, BYT_DSP_BAR, SHIM_IMRD, 0x3, 0x0); + + /* set BARS */ + sdev->cl_bar = BYT_DSP_BAR; + + /* set default mailbox offset for FW ready message */ + sdev->dsp_box.offset = MBOX_OFFSET; + + return ret; + +irq_err: + iounmap(sdev->bar[BYT_IMR_BAR]); +imr_err: + iounmap(sdev->bar[BYT_DSP_BAR]); + return ret; +} + +static int byt_pci_probe(struct snd_sof_dev *sdev) +{ + struct snd_sof_pdata *pdata = sdev->pdata; + const struct sof_dev_desc *desc = pdata->desc; + struct pci_dev *pci = sdev->pci; + u32 base, size; + int ret = 0; + + /* DSP DMA can only access low 31 bits of host memory */ + ret = dma_coerce_mask_and_coherent(&pci->dev, DMA_BIT_MASK(31)); + if (ret < 0) { + dev_err(sdev->dev, "error: failed to set DMA mask %d\n", ret); + return ret; + } + + /* LPE base */ + base = pci_resource_start(pci, desc->resindex_lpe_base) - IRAM_OFFSET; + size = BYT_PCI_BAR_SIZE; + + dev_dbg(sdev->dev, "LPE PHY base at 0x%x size 0x%x", base, size); + sdev->bar[BYT_DSP_BAR] = ioremap(base, size); + if (!sdev->bar[BYT_DSP_BAR]) { + dev_err(sdev->dev, "error: failed to ioremap LPE base 0x%x size 0x%x\n", + base, size); + return -ENODEV; + } + dev_dbg(sdev->dev, "LPE VADDR %p\n", sdev->bar[BYT_DSP_BAR]); + + /* IMR base - optional */ + if (desc->resindex_imr_base == -1) + goto irq; + + base = pci_resource_start(pci, desc->resindex_imr_base); + size = pci_resource_len(pci, desc->resindex_imr_base); + + /* some BIOSes don't map IMR */ + if (base == 0x55aa55aa || base == 0x0) { + dev_info(sdev->dev, "IMR not set by BIOS. Ignoring\n"); + goto irq; + } + + dev_dbg(sdev->dev, "IMR base at 0x%x size 0x%x", base, size); + sdev->bar[BYT_IMR_BAR] = ioremap(base, size); + if (!sdev->bar[BYT_IMR_BAR]) { + dev_err(sdev->dev, "error: failed to ioremap IMR base 0x%x size 0x%x\n", + base, size); + ret = -ENODEV; + goto imr_err; + } + dev_dbg(sdev->dev, "IMR VADDR %p\n", sdev->bar[BYT_IMR_BAR]); + +irq: + /* register our IRQ */ + sdev->ipc_irq = pci->irq; + dev_dbg(sdev->dev, "using IRQ %d\n", sdev->ipc_irq); + ret = request_threaded_irq(sdev->ipc_irq, byt_irq_handler, + byt_irq_thread, 0, "AudioDSP", sdev); + if (ret < 0) { + dev_err(sdev->dev, "error: failed to register IRQ %d\n", + sdev->ipc_irq); + goto irq_err; + } + + /* enable Interrupt from both sides */ + snd_sof_dsp_update_bits64(sdev, BYT_DSP_BAR, SHIM_IMRX, 0x3, 0x0); + snd_sof_dsp_update_bits64(sdev, BYT_DSP_BAR, SHIM_IMRD, 0x3, 0x0); + + /* set BARS */ + sdev->cl_bar = BYT_DSP_BAR; + + /* set default mailbox offset for FW ready message */ + sdev->dsp_box.offset = MBOX_OFFSET; + + return ret; + +irq_err: + iounmap(sdev->bar[BYT_IMR_BAR]); +imr_err: + iounmap(sdev->bar[BYT_DSP_BAR]); + return ret; +} + +static int byt_probe(struct snd_sof_dev *sdev) +{ + if (sdev->pci) + return byt_pci_probe(sdev); + else + return byt_acpi_probe(sdev); +} + +static int byt_acpi_remove(struct snd_sof_dev *sdev) +{ + iounmap(sdev->bar[BYT_DSP_BAR]); + iounmap(sdev->bar[BYT_PCI_BAR]); + iounmap(sdev->bar[BYT_IMR_BAR]); + free_irq(sdev->ipc_irq, sdev); + return 0; +} + +static int byt_pci_remove(struct snd_sof_dev *sdev) +{ + free_irq(sdev->ipc_irq, sdev); + return 0; +} + +static int byt_remove(struct snd_sof_dev *sdev) +{ + if (sdev->pci) + return byt_pci_remove(sdev); + else + return byt_acpi_remove(sdev); +} + +/* baytrail ops */ +struct snd_sof_dsp_ops sof_byt_ops = { + /* device init */ + .probe = byt_probe, + .remove = byt_remove, + + /* DSP core boot / reset */ + .run = byt_run, + .reset = byt_reset, + + /* Register IO */ + .write = byt_write, + .read = byt_read, + .write64 = byt_write64, + .read64 = byt_read64, + + /* Block IO */ + .block_read = byt_block_read, + .block_write = byt_block_write, + + /* doorbell */ + .irq_handler = byt_irq_handler, + .irq_thread = byt_irq_thread, + + /* mailbox */ + .mailbox_read = byt_mailbox_read, + .mailbox_write = byt_mailbox_write, + + /* ipc */ + .send_msg = byt_send_msg, + .get_reply = byt_get_reply, + .fw_ready = byt_fw_ready, + .is_ready = byt_is_ready, + .cmd_done = byt_cmd_done, + + /* debug */ + .debug_map = byt_debugfs, + .debug_map_count = ARRAY_SIZE(byt_debugfs), + .dbg_dump = byt_dump, + + /* module loading */ + .load_module = snd_sof_parse_module_memcpy, + + /*Firmware loading */ + .load_firmware = snd_sof_load_firmware_memcpy, +}; +EXPORT_SYMBOL(sof_byt_ops); + +/* cherrytrail and braswell ops */ +struct snd_sof_dsp_ops sof_cht_ops = { + /* device init */ + .probe = byt_probe, + .remove = byt_remove, + + /* DSP core boot / reset */ + .run = byt_run, + .reset = byt_reset, + + /* Register IO */ + .write = byt_write, + .read = byt_read, + .write64 = byt_write64, + .read64 = byt_read64, + + /* Block IO */ + .block_read = byt_block_read, + .block_write = byt_block_write, + + /* doorbell */ + .irq_handler = byt_irq_handler, + .irq_thread = byt_irq_thread, + + /* mailbox */ + .mailbox_read = byt_mailbox_read, + .mailbox_write = byt_mailbox_write, + + /* ipc */ + .send_msg = byt_send_msg, + .get_reply = byt_get_reply, + .fw_ready = byt_fw_ready, + .is_ready = byt_is_ready, + .cmd_done = byt_cmd_done, + + /* debug */ + .debug_map = cht_debugfs, + .debug_map_count = ARRAY_SIZE(cht_debugfs), + .dbg_dump = byt_dump, + + /* module loading */ + .load_module = snd_sof_parse_module_memcpy, + + /*Firmware loading */ + .load_firmware = snd_sof_load_firmware_memcpy, +}; +EXPORT_SYMBOL(sof_cht_ops); + +MODULE_LICENSE("Dual BSD/GPL"); diff --git a/sound/soc/sof/intel/shim.h b/sound/soc/sof/intel/shim.h new file mode 100644 index 00000000000000..512441387b1e32 --- /dev/null +++ b/sound/soc/sof/intel/shim.h @@ -0,0 +1,157 @@ +/* SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause) */ +/* + * This file is provided under a dual BSD/GPLv2 license. When using or + * redistributing this file, you may do so under either license. + * + * Copyright(c) 2017 Intel Corporation. All rights reserved. + * + * Author: Liam Girdwood + */ + +#ifndef __SOF_INTEL_SHIM_H +#define __SOF_INTEL_SHIM_H + +/* + * SHIM registers for BYT, BSW, CHT, HSW, BDW + */ + +#define SHIM_CSR (SHIM_OFFSET + 0x00) +#define SHIM_PISR (SHIM_OFFSET + 0x08) +#define SHIM_PIMR (SHIM_OFFSET + 0x10) +#define SHIM_ISRX (SHIM_OFFSET + 0x18) +#define SHIM_ISRD (SHIM_OFFSET + 0x20) +#define SHIM_IMRX (SHIM_OFFSET + 0x28) +#define SHIM_IMRD (SHIM_OFFSET + 0x30) +#define SHIM_IPCX (SHIM_OFFSET + 0x38) +#define SHIM_IPCD (SHIM_OFFSET + 0x40) +#define SHIM_ISRSC (SHIM_OFFSET + 0x48) +#define SHIM_ISRLPESC (SHIM_OFFSET + 0x50) +#define SHIM_IMRSC (SHIM_OFFSET + 0x58) +#define SHIM_IMRLPESC (SHIM_OFFSET + 0x60) +#define SHIM_IPCSC (SHIM_OFFSET + 0x68) +#define SHIM_IPCLPESC (SHIM_OFFSET + 0x70) +#define SHIM_CLKCTL (SHIM_OFFSET + 0x78) +#define SHIM_CSR2 (SHIM_OFFSET + 0x80) +#define SHIM_LTRC (SHIM_OFFSET + 0xE0) +#define SHIM_HMDC (SHIM_OFFSET + 0xE8) + +#define SHIM_PWMCTRL 0x1000 + +/* + * SST SHIM register bits for BYT, BSW, CHT HSW, BDW + * Register bit naming and functionaility can differ between devices. + */ + +/* CSR / CS */ +#define SHIM_CSR_RST (0x1 << 1) +#define SHIM_CSR_SBCS0 (0x1 << 2) +#define SHIM_CSR_SBCS1 (0x1 << 3) +#define SHIM_CSR_DCS(x) (x << 4) +#define SHIM_CSR_DCS_MASK (0x7 << 4) +#define SHIM_CSR_STALL (0x1 << 10) +#define SHIM_CSR_S0IOCS (0x1 << 21) +#define SHIM_CSR_S1IOCS (0x1 << 23) +#define SHIM_CSR_LPCS (0x1 << 31) +#define SHIM_CSR_24MHZ_LPCS \ + (SHIM_CSR_SBCS0 | SHIM_CSR_SBCS1 | SHIM_CSR_LPCS) +#define SHIM_CSR_24MHZ_NO_LPCS (SHIM_CSR_SBCS0 | SHIM_CSR_SBCS1) +#define SHIM_BYT_CSR_RST (0x1 << 0) +#define SHIM_BYT_CSR_VECTOR_SEL (0x1 << 1) +#define SHIM_BYT_CSR_STALL (0x1 << 2) +#define SHIM_BYT_CSR_PWAITMODE (0x1 << 3) + +/* ISRX / ISC */ +#define SHIM_ISRX_BUSY (0x1 << 1) +#define SHIM_ISRX_DONE (0x1 << 0) +#define SHIM_BYT_ISRX_REQUEST (0x1 << 1) + +/* ISRD / ISD */ +#define SHIM_ISRD_BUSY (0x1 << 1) +#define SHIM_ISRD_DONE (0x1 << 0) + +/* IMRX / IMC */ +#define SHIM_IMRX_BUSY (0x1 << 1) +#define SHIM_IMRX_DONE (0x1 << 0) +#define SHIM_BYT_IMRX_REQUEST (0x1 << 1) + +/* IMRD / IMD */ +#define SHIM_IMRD_DONE (0x1 << 0) +#define SHIM_IMRD_BUSY (0x1 << 1) +#define SHIM_IMRD_SSP0 (0x1 << 16) +#define SHIM_IMRD_DMAC0 (0x1 << 21) +#define SHIM_IMRD_DMAC1 (0x1 << 22) +#define SHIM_IMRD_DMAC (SHIM_IMRD_DMAC0 | SHIM_IMRD_DMAC1) + +/* IPCX / IPCC */ +#define SHIM_IPCX_DONE (0x1 << 30) +#define SHIM_IPCX_BUSY (0x1 << 31) +#define SHIM_BYT_IPCX_DONE ((u64)0x1 << 62) +#define SHIM_BYT_IPCX_BUSY ((u64)0x1 << 63) + +/* IPCD */ +#define SHIM_IPCD_DONE (0x1 << 30) +#define SHIM_IPCD_BUSY (0x1 << 31) +#define SHIM_BYT_IPCD_DONE ((u64)0x1 << 62) +#define SHIM_BYT_IPCD_BUSY ((u64)0x1 << 63) + +/* CLKCTL */ +#define SHIM_CLKCTL_SMOS(x) (x << 24) +#define SHIM_CLKCTL_MASK (3 << 24) +#define SHIM_CLKCTL_DCPLCG BIT(18) +#define SHIM_CLKCTL_SCOE1 BIT(17) +#define SHIM_CLKCTL_SCOE0 BIT(16) + +/* CSR2 / CS2 */ +#define SHIM_CSR2_SDFD_SSP0 BIT(1) +#define SHIM_CSR2_SDFD_SSP1 BIT(2) + +/* LTRC */ +#define SHIM_LTRC_VAL(x) (x << 0) + +/* HMDC */ +#define SHIM_HMDC_HDDA0(x) (x << 0) +#define SHIM_HMDC_HDDA1(x) (x << 7) +#define SHIM_HMDC_HDDA_E0_CH0 1 +#define SHIM_HMDC_HDDA_E0_CH1 2 +#define SHIM_HMDC_HDDA_E0_CH2 4 +#define SHIM_HMDC_HDDA_E0_CH3 8 +#define SHIM_HMDC_HDDA_E1_CH0 SHIM_HMDC_HDDA1(SHIM_HMDC_HDDA_E0_CH0) +#define SHIM_HMDC_HDDA_E1_CH1 SHIM_HMDC_HDDA1(SHIM_HMDC_HDDA_E0_CH1) +#define SHIM_HMDC_HDDA_E1_CH2 SHIM_HMDC_HDDA1(SHIM_HMDC_HDDA_E0_CH2) +#define SHIM_HMDC_HDDA_E1_CH3 SHIM_HMDC_HDDA1(SHIM_HMDC_HDDA_E0_CH3) +#define SHIM_HMDC_HDDA_E0_ALLCH \ + (SHIM_HMDC_HDDA_E0_CH0 | SHIM_HMDC_HDDA_E0_CH1 | \ + SHIM_HMDC_HDDA_E0_CH2 | SHIM_HMDC_HDDA_E0_CH3) +#define SHIM_HMDC_HDDA_E1_ALLCH \ + (SHIM_HMDC_HDDA_E1_CH0 | SHIM_HMDC_HDDA_E1_CH1 | \ + SHIM_HMDC_HDDA_E1_CH2 | SHIM_HMDC_HDDA_E1_CH3) + +/* Audio DSP PCI registers */ +#define PCI_VDRTCTL0 0xa0 +#define PCI_VDRTCTL1 0xa4 +#define PCI_VDRTCTL2 0xa8 +#define PCI_VDRTCTL3 0xaC + +/* VDRTCTL0 */ +#define PCI_VDRTCL0_D3PGD BIT(0) +#define PCI_VDRTCL0_D3SRAMPGD BIT(1) +#define PCI_VDRTCL0_DSRAMPGE_SHIFT 12 +#define PCI_VDRTCL0_DSRAMPGE_MASK (0xfffff << PCI_VDRTCL0_DSRAMPGE_SHIFT) +#define PCI_VDRTCL0_ISRAMPGE_SHIFT 2 +#define PCI_VDRTCL0_ISRAMPGE_MASK (0x3ff << PCI_VDRTCL0_ISRAMPGE_SHIFT) + +/* VDRTCTL2 */ +#define PCI_VDRTCL2_DCLCGE BIT(1) +#define PCI_VDRTCL2_DTCGE BIT(10) +#define PCI_VDRTCL2_APLLSE_MASK BIT(31) + +/* PMCS */ +#define PCI_PMCS 0x84 +#define PCI_PMCS_PS_MASK 0x3 + +extern struct snd_sof_dsp_ops sof_byt_ops; +extern struct snd_sof_dsp_ops sof_cht_ops; +extern struct snd_sof_dsp_ops sof_hsw_ops; +extern struct snd_sof_dsp_ops sof_bdw_ops; + +#endif From a289d928a2f5e3e3ef54eab45ba70b912c01b5f6 Mon Sep 17 00:00:00 2001 From: Liam Girdwood Date: Mon, 8 Jan 2018 20:37:51 +0000 Subject: [PATCH 037/107] ASoC: SOF: Intel: Add HSW HW DSP support Add DSP hardware support for Intel Haswell based devices. Signed-off-by: Liam Girdwood --- sound/soc/sof/intel/hsw.c | 754 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 754 insertions(+) create mode 100644 sound/soc/sof/intel/hsw.c diff --git a/sound/soc/sof/intel/hsw.c b/sound/soc/sof/intel/hsw.c new file mode 100644 index 00000000000000..b9a51021550790 --- /dev/null +++ b/sound/soc/sof/intel/hsw.c @@ -0,0 +1,754 @@ +// SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause) +/* + * This file is provided under a dual BSD/GPLv2 license. When using or + * redistributing this file, you may do so under either license. + * + * Copyright(c) 2017 Intel Corporation. All rights reserved. + * + * Author: Liam Girdwood + */ + +/* + * Hardwre interface for audio DSP on Haswell + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "../sof-priv.h" +#include "../ops.h" +#include "shim.h" + +/* BARs */ +#define HSW_DSP_BAR 0 +#define HSW_PCI_BAR 1 + +/* + * Debug + */ + +/* DSP memories for HSW */ +#define IRAM_OFFSET 0x80000 +#define HSW_IRAM_SIZE (10 * 32 * 1024) +#define DRAM_OFFSET 0x00000 +#define HSW_DRAM_SIZE (16 * 32 * 1024) +#define SHIM_OFFSET 0xE7000 +#define SHIM_SIZE 0x100 +#define MBOX_OFFSET 0x7E000 +#define MBOX_SIZE 0x1000 +#define MBOX_DUMP_SIZE 0x30 +#define EXCEPT_OFFSET 0x800 + +/* DSP peripherals */ +#define DMAC0_OFFSET 0xFE000 +#define DMAC1_OFFSET 0xFF000 +#define DMAC_SIZE 0x420 +#define SSP0_OFFSET 0xFC000 +#define SSP1_OFFSET 0xFD000 +#define SSP_SIZE 0x100 + +#define HSW_STACK_DUMP_SIZE 32 + +#define HSW_PANIC_OFFSET(x) ((x) & 0xFFFF) + +static const struct snd_sof_debugfs_map hsw_debugfs[] = { + {"dmac0", HSW_DSP_BAR, DMAC0_OFFSET, DMAC_SIZE}, + {"dmac1", HSW_DSP_BAR, DMAC1_OFFSET, DMAC_SIZE}, + {"ssp0", HSW_DSP_BAR, SSP0_OFFSET, SSP_SIZE}, + {"ssp1", HSW_DSP_BAR, SSP1_OFFSET, SSP_SIZE}, + {"iram", HSW_DSP_BAR, IRAM_OFFSET, HSW_IRAM_SIZE}, + {"dram", HSW_DSP_BAR, DRAM_OFFSET, HSW_DRAM_SIZE}, + {"shim", HSW_DSP_BAR, SHIM_OFFSET, SHIM_SIZE}, +}; + +/* + * Memory copy. + */ + +/* write has to deal with copying non 32 bit sized data */ +static void hsw_block_write(struct snd_sof_dev *sdev, u32 offset, void *src, + size_t size) +{ + void __iomem *dest = sdev->bar[sdev->mmio_bar] + offset; + u32 tmp = 0; + int i, m, n; + const u8 *src_byte = src; + + m = size / 4; + n = size % 4; + + /* __iowrite32_copy use 32bit size values so divide by 4 */ + __iowrite32_copy((void *)dest, src, m); + + if (n) { + for (i = 0; i < n; i++) + tmp |= (u32)*(src_byte + m * 4 + i) << (i * 8); + __iowrite32_copy((void *)(dest + m * 4), &tmp, 1); + } +} + +static void hsw_block_read(struct snd_sof_dev *sdev, u32 offset, void *dest, + size_t size) +{ + void __iomem *src = sdev->bar[sdev->mmio_bar] + offset; + + memcpy_fromio(dest, src, size); +} + +static void hsw_mailbox_write(struct snd_sof_dev *sdev, u32 offset, + void *message, size_t bytes) +{ + void __iomem *dest = sdev->bar[sdev->mailbox_bar] + offset; + + memcpy_toio(dest, message, bytes); +} + +static void hsw_mailbox_read(struct snd_sof_dev *sdev, u32 offset, + void *message, size_t bytes) +{ + void __iomem *src = sdev->bar[sdev->mailbox_bar] + offset; + + memcpy_fromio(message, src, bytes); +} + +/* + * Register IO + */ + +static void hsw_write(struct snd_sof_dev *sdev, void __iomem *addr, + u32 value) +{ + writel(value, addr); +} + +static u32 hsw_read(struct snd_sof_dev *sdev, void __iomem *addr) +{ + return readl(addr); +} + +static void hsw_write64(struct snd_sof_dev *sdev, void __iomem *addr, + u64 value) +{ + memcpy_toio(addr, &value, sizeof(value)); +} + +static u64 hsw_read64(struct snd_sof_dev *sdev, void __iomem *addr) +{ + u64 val; + + memcpy_fromio(&val, addr, sizeof(val)); + return val; +} + +/* + * DSP Control. + */ + +static int hsw_run(struct snd_sof_dev *sdev) +{ + /* set oportunistic mode on engine 0,1 for all channels */ + snd_sof_dsp_update_bits(sdev, HSW_DSP_BAR, SHIM_HMDC, + SHIM_HMDC_HDDA_E0_ALLCH | + SHIM_HMDC_HDDA_E1_ALLCH, 0); + + /* set DSP to RUN */ + snd_sof_dsp_update_bits_unlocked(sdev, HSW_DSP_BAR, SHIM_CSR, + SHIM_CSR_STALL, 0x0); + + return 0; //TODO: Fix return value +} + +static int hsw_reset(struct snd_sof_dev *sdev) +{ + /* put DSP into reset and stall */ + snd_sof_dsp_update_bits_unlocked(sdev, HSW_DSP_BAR, SHIM_CSR, + SHIM_CSR_RST | SHIM_CSR_STALL, + SHIM_CSR_RST | SHIM_CSR_STALL); + + /* keep in reset for 10ms */ + mdelay(10); + + /* take DSP out of reset and keep stalled for FW loading */ + snd_sof_dsp_update_bits_unlocked(sdev, HSW_DSP_BAR, SHIM_CSR, + SHIM_CSR_RST | SHIM_CSR_STALL, + SHIM_CSR_STALL); + + return 0; //TODO: Fix return value +} + +static int hsw_set_dsp_D0(struct snd_sof_dev *sdev) +{ + int tries = 10; + u32 reg; + + /* Disable core clock gating (VDRTCTL2.DCLCGE = 0) */ + snd_sof_dsp_update_bits_unlocked(sdev, HSW_PCI_BAR, PCI_VDRTCTL2, + PCI_VDRTCL2_DCLCGE | PCI_VDRTCL2_DTCGE, + 0); + + /* Disable D3PG (VDRTCTL0.D3PGD = 1) */ + snd_sof_dsp_update_bits_unlocked(sdev, HSW_PCI_BAR, PCI_VDRTCTL0, + PCI_VDRTCL0_D3PGD, PCI_VDRTCL0_D3PGD); + + /* Set D0 state */ + snd_sof_dsp_update_bits_unlocked(sdev, HSW_PCI_BAR, PCI_PMCS, + PCI_PMCS_PS_MASK, 0); + + /* check that ADSP shim is enabled */ + while (tries--) { + reg = readl(sdev->bar[HSW_PCI_BAR] + PCI_PMCS) + & PCI_PMCS_PS_MASK; + if (reg == 0) + goto finish; + + msleep(20); + } + + return -ENODEV; + +finish: + /* + * select SSP1 19.2MHz base clock, SSP clock 0, + * turn off Low Power Clock + */ + snd_sof_dsp_update_bits_unlocked(sdev, HSW_DSP_BAR, SHIM_CSR, + SHIM_CSR_S1IOCS | SHIM_CSR_SBCS1 | + SHIM_CSR_LPCS, 0x0); + + /* stall DSP core, set clk to 192/96Mhz */ + snd_sof_dsp_update_bits_unlocked(sdev, HSW_DSP_BAR, + SHIM_CSR, SHIM_CSR_STALL | + SHIM_CSR_DCS_MASK, + SHIM_CSR_STALL | SHIM_CSR_DCS(4)); + + /* Set 24MHz MCLK, prevent local clock gating, enable SSP0 clock */ + snd_sof_dsp_update_bits_unlocked(sdev, HSW_DSP_BAR, SHIM_CLKCTL, + SHIM_CLKCTL_MASK | SHIM_CLKCTL_DCPLCG | + SHIM_CLKCTL_SCOE0, + SHIM_CLKCTL_MASK | SHIM_CLKCTL_DCPLCG | + SHIM_CLKCTL_SCOE0); + + /* Stall and reset core, set CSR */ + hsw_reset(sdev); + + /* Enable core clock gating (VDRTCTL2.DCLCGE = 1), delay 50 us */ + snd_sof_dsp_update_bits_unlocked(sdev, HSW_PCI_BAR, PCI_VDRTCTL2, + PCI_VDRTCL2_DCLCGE | + PCI_VDRTCL2_DTCGE, + PCI_VDRTCL2_DCLCGE | + PCI_VDRTCL2_DTCGE); + + usleep_range(50, 55); + + /* switch on audio PLL */ + snd_sof_dsp_update_bits_unlocked(sdev, HSW_PCI_BAR, PCI_VDRTCTL2, + PCI_VDRTCL2_APLLSE_MASK, 0); + + /* + * set default power gating control, enable power gating control for + * all blocks. that is, can't be accessed, please enable each block + * before accessing. + */ + snd_sof_dsp_update_bits_unlocked(sdev, HSW_PCI_BAR, PCI_VDRTCTL0, + PCI_VDRTCL0_DSRAMPGE_MASK | + PCI_VDRTCL0_ISRAMPGE_MASK, 0); + + /* disable DMA finish function for SSP0 & SSP1 */ + snd_sof_dsp_update_bits_unlocked(sdev, HSW_DSP_BAR, SHIM_CSR2, + SHIM_CSR2_SDFD_SSP1, + SHIM_CSR2_SDFD_SSP1); + + /* set on-demond mode on engine 0,1 for all channels */ + snd_sof_dsp_update_bits(sdev, HSW_DSP_BAR, SHIM_HMDC, + SHIM_HMDC_HDDA_E0_ALLCH | + SHIM_HMDC_HDDA_E1_ALLCH, + SHIM_HMDC_HDDA_E0_ALLCH | + SHIM_HMDC_HDDA_E1_ALLCH); + + /* Enable Interrupt from both sides */ + snd_sof_dsp_update_bits(sdev, HSW_DSP_BAR, SHIM_IMRX, + (SHIM_IMRX_BUSY | SHIM_IMRX_DONE), 0x0); + snd_sof_dsp_update_bits(sdev, HSW_DSP_BAR, SHIM_IMRD, + (SHIM_IMRD_DONE | SHIM_IMRD_BUSY | + SHIM_IMRD_SSP0 | SHIM_IMRD_DMAC), 0x0); + + /* clear IPC registers */ + snd_sof_dsp_write(sdev, HSW_DSP_BAR, SHIM_IPCX, 0x0); + snd_sof_dsp_write(sdev, HSW_DSP_BAR, SHIM_IPCD, 0x0); + snd_sof_dsp_write(sdev, HSW_DSP_BAR, 0x80, 0x6); + snd_sof_dsp_write(sdev, HSW_DSP_BAR, 0xe0, 0x300a); + + return 0; +} + +static void hsw_get_registers(struct snd_sof_dev *sdev, + struct sof_ipc_dsp_oops_xtensa *xoops, + u32 *stack, size_t stack_words) +{ + /* first read regsisters */ + hsw_mailbox_read(sdev, sdev->dsp_oops_offset, xoops, sizeof(*xoops)); + + /* the get the stack */ + hsw_mailbox_read(sdev, sdev->dsp_oops_offset + sizeof(*xoops), stack, + stack_words * sizeof(u32)); +} + +static void hsw_dump(struct snd_sof_dev *sdev, u32 flags) +{ + struct sof_ipc_dsp_oops_xtensa xoops; + u32 stack[HSW_STACK_DUMP_SIZE]; + u32 status, panic; + + /* now try generic SOF status messages */ + status = snd_sof_dsp_read(sdev, HSW_DSP_BAR, SHIM_IPCD); + panic = snd_sof_dsp_read(sdev, HSW_DSP_BAR, SHIM_IPCX); + hsw_get_registers(sdev, &xoops, stack, HSW_STACK_DUMP_SIZE); + snd_sof_get_status(sdev, status, panic, &xoops, stack, + HSW_STACK_DUMP_SIZE); +} + +/* + * IPC Doorbell IRQ handler and thread. + */ + +static irqreturn_t hsw_irq_handler(int irq, void *context) +{ + struct snd_sof_dev *sdev = (struct snd_sof_dev *)context; + u32 isr; + int ret = IRQ_NONE; + + spin_lock(&sdev->hw_lock); + + /* Interrupt arrived, check src */ + isr = snd_sof_dsp_read(sdev, HSW_DSP_BAR, SHIM_ISRX); + if (isr & SHIM_ISRX_DONE) { + /* Mask Done interrupt before return */ + snd_sof_dsp_update_bits_unlocked(sdev, HSW_DSP_BAR, SHIM_IMRX, + SHIM_IMRX_DONE, + SHIM_IMRX_DONE); + ret = IRQ_WAKE_THREAD; + } + + if (isr & SHIM_ISRX_BUSY) { + /* Mask Busy interrupt before return */ + snd_sof_dsp_update_bits_unlocked(sdev, HSW_DSP_BAR, SHIM_IMRX, + SHIM_IMRX_BUSY, + SHIM_IMRX_BUSY); + ret = IRQ_WAKE_THREAD; + } + + spin_unlock(&sdev->hw_lock); + return ret; +} + +static irqreturn_t hsw_irq_thread(int irq, void *context) +{ + struct snd_sof_dev *sdev = (struct snd_sof_dev *)context; + u32 ipcx, ipcd, hdr; + + ipcx = snd_sof_dsp_read(sdev, HSW_DSP_BAR, SHIM_IPCX); + + /* reply message from DSP */ + if (ipcx & SHIM_IPCX_DONE) { + /* Handle Immediate reply from DSP Core */ + hsw_mailbox_read(sdev, sdev->host_box.offset, &hdr, + sizeof(hdr)); + snd_sof_ipc_reply(sdev, hdr); + + /* clear DONE bit - tell DSP we have completed */ + snd_sof_dsp_update_bits_unlocked(sdev, HSW_DSP_BAR, SHIM_IPCX, + SHIM_IPCX_DONE, 0); + + /* unmask Done interrupt */ + snd_sof_dsp_update_bits_unlocked(sdev, HSW_DSP_BAR, SHIM_IMRX, + SHIM_IMRX_DONE, 0); + } + + ipcd = snd_sof_dsp_read(sdev, HSW_DSP_BAR, SHIM_IPCD); + + /* new message from DSP */ + if (ipcd & SHIM_IPCD_BUSY) { + /* Handle messages from DSP Core */ + if ((ipcd & SOF_IPC_PANIC_MAGIC_MASK) == SOF_IPC_PANIC_MAGIC) { + snd_sof_dsp_panic(sdev, HSW_PANIC_OFFSET(ipcx) + + MBOX_OFFSET); + } else { + snd_sof_ipc_msgs_rx(sdev); + } + } + + return IRQ_HANDLED; +} + +/* + * IPC Firmware ready. + */ +static void hsw_get_windows(struct snd_sof_dev *sdev) +{ + struct sof_ipc_window_elem *elem; + u32 outbox_offset = 0; + u32 stream_offset = 0; + u32 inbox_offset = 0; + u32 outbox_size = 0; + u32 stream_size = 0; + u32 inbox_size = 0; + int i; + + if (!sdev->info_window) { + dev_err(sdev->dev, "error: have no window info\n"); + return; + } + + for (i = 0; i < sdev->info_window->num_windows; i++) { + elem = &sdev->info_window->window[i]; + + switch (elem->type) { + case SOF_IPC_REGION_UPBOX: + inbox_offset = elem->offset + MBOX_OFFSET; + inbox_size = elem->size; + snd_sof_debugfs_create_item(sdev, + sdev->bar[HSW_DSP_BAR] + + inbox_offset, + elem->size, "inbox"); + break; + case SOF_IPC_REGION_DOWNBOX: + outbox_offset = elem->offset + MBOX_OFFSET; + outbox_size = elem->size; + snd_sof_debugfs_create_item(sdev, + sdev->bar[HSW_DSP_BAR] + + outbox_offset, + elem->size, "outbox"); + break; + case SOF_IPC_REGION_TRACE: + snd_sof_debugfs_create_item(sdev, + sdev->bar[HSW_DSP_BAR] + + elem->offset + MBOX_OFFSET, + elem->size, "etrace"); + break; + case SOF_IPC_REGION_DEBUG: + snd_sof_debugfs_create_item(sdev, + sdev->bar[HSW_DSP_BAR] + + elem->offset + MBOX_OFFSET, + elem->size, "debug"); + break; + case SOF_IPC_REGION_STREAM: + stream_offset = elem->offset + MBOX_OFFSET; + stream_size = elem->size; + snd_sof_debugfs_create_item(sdev, + sdev->bar[HSW_DSP_BAR] + + stream_offset, + elem->size, "stream"); + break; + case SOF_IPC_REGION_REGS: + snd_sof_debugfs_create_item(sdev, + sdev->bar[HSW_DSP_BAR] + + elem->offset + MBOX_OFFSET, + elem->size, "regs"); + break; + case SOF_IPC_REGION_EXCEPTION: + sdev->dsp_oops_offset = elem->offset + MBOX_OFFSET; + snd_sof_debugfs_create_item(sdev, + sdev->bar[HSW_DSP_BAR] + + elem->offset + MBOX_OFFSET, + elem->size, "exception"); + break; + default: + dev_err(sdev->dev, "error: get illegal window info\n"); + return; + } + } + + if (outbox_size == 0 || inbox_size == 0) { + dev_err(sdev->dev, "error: get illegal mailbox window\n"); + return; + } + + snd_sof_dsp_mailbox_init(sdev, inbox_offset, inbox_size, + outbox_offset, outbox_size); + sdev->stream_box.offset = stream_offset; + sdev->stream_box.size = stream_size; + + dev_dbg(sdev->dev, " mailbox upstream 0x%x - size 0x%x\n", + inbox_offset, inbox_size); + dev_dbg(sdev->dev, " mailbox downstream 0x%x - size 0x%x\n", + outbox_offset, outbox_size); + dev_dbg(sdev->dev, " stream region 0x%x - size 0x%x\n", + stream_offset, stream_size); +} + +static int hsw_fw_ready(struct snd_sof_dev *sdev, u32 msg_id) +{ + struct sof_ipc_fw_ready *fw_ready = &sdev->fw_ready; + struct sof_ipc_fw_version *v = &fw_ready->version; + u32 offset; + + /* mailbox must be on 4k boundary */ + offset = MBOX_OFFSET; + + dev_dbg(sdev->dev, "ipc: DSP is ready 0x%8.8x offset %d\n", + msg_id, offset); + + /* copy data from the DSP FW ready offset */ + hsw_block_read(sdev, offset, fw_ready, sizeof(*fw_ready)); + + snd_sof_dsp_mailbox_init(sdev, fw_ready->dspbox_offset, + fw_ready->dspbox_size, + fw_ready->hostbox_offset, + fw_ready->hostbox_size); + + dev_info(sdev->dev, + " Firmware info: version %d:%d-%s build %d on %s:%s\n", + v->major, v->minor, v->tag, v->build, v->date, v->time); + + /* now check for extended data */ + snd_sof_fw_parse_ext_data(sdev, MBOX_OFFSET + + sizeof(struct sof_ipc_fw_ready)); + + hsw_get_windows(sdev); + + return 0; +} + +/* + * IPC Mailbox IO + */ + +static int hsw_is_ready(struct snd_sof_dev *sdev) +{ + u32 val; + + val = snd_sof_dsp_read(sdev, HSW_DSP_BAR, SHIM_IPCX); + if (val & SHIM_IPCX_BUSY) + return 0; + + return 1; +} + +static int hsw_send_msg(struct snd_sof_dev *sdev, struct snd_sof_ipc_msg *msg) +{ + /* send the message */ + hsw_mailbox_write(sdev, sdev->host_box.offset, msg->msg_data, + msg->msg_size); + snd_sof_dsp_write(sdev, HSW_DSP_BAR, SHIM_IPCX, SHIM_IPCX_BUSY); + + return 0; +} + +static int hsw_get_reply(struct snd_sof_dev *sdev, struct snd_sof_ipc_msg *msg) +{ + struct sof_ipc_reply reply; + int ret = 0; + u32 size; + + /* get reply */ + hsw_mailbox_read(sdev, sdev->host_box.offset, &reply, sizeof(reply)); + if (reply.error < 0) { + size = sizeof(reply); + ret = reply.error; + } else { + /* reply correct size ? */ + if (reply.hdr.size != msg->reply_size) { + dev_err(sdev->dev, "error: reply expected 0x%zx got 0x%x bytes\n", + msg->reply_size, reply.hdr.size); + size = msg->reply_size; + ret = -EINVAL; + } else { + size = reply.hdr.size; + } + } + + /* read the message */ + if (msg->msg_data && size > 0) + hsw_mailbox_read(sdev, sdev->host_box.offset, msg->reply_data, + size); + return ret; +} + +static int hsw_cmd_done(struct snd_sof_dev *sdev) +{ + /* clear BUSY bit and set DONE bit - accept new messages */ + snd_sof_dsp_update_bits_unlocked(sdev, HSW_DSP_BAR, SHIM_IPCD, + SHIM_IPCD_BUSY | SHIM_IPCD_DONE, + SHIM_IPCD_DONE); + + /* unmask busy interrupt */ + snd_sof_dsp_update_bits_unlocked(sdev, HSW_DSP_BAR, SHIM_IMRX, + SHIM_IMRX_BUSY, 0); + + return 0; +} + +/* + * Probe and remove. + */ +static int hsw_probe(struct snd_sof_dev *sdev) +{ + struct snd_sof_pdata *pdata = sdev->pdata; + const struct sof_dev_desc *desc = pdata->desc; + struct platform_device *pdev = + container_of(sdev->parent, struct platform_device, dev); + struct resource *mmio; + u32 base, size; + int ret = 0; + + /* LPE base */ + mmio = platform_get_resource(pdev, IORESOURCE_MEM, + desc->resindex_lpe_base); + if (mmio) { + base = mmio->start; + size = resource_size(mmio); + } else { + dev_err(sdev->dev, "error: failed to get LPE base at idx %d\n", + desc->resindex_lpe_base); + return -EINVAL; + } + + dev_dbg(sdev->dev, "LPE PHY base at 0x%x size 0x%x", base, size); + sdev->bar[HSW_DSP_BAR] = ioremap(base, size); + if (!sdev->bar[HSW_DSP_BAR]) { + dev_err(sdev->dev, + "error: failed to ioremap LPE base 0x%x size 0x%x\n", + base, size); + return -ENODEV; + } + dev_dbg(sdev->dev, "LPE VADDR %p\n", sdev->bar[HSW_DSP_BAR]); + + /* TODO: add offsets */ + sdev->mmio_bar = HSW_DSP_BAR; + sdev->mailbox_bar = HSW_DSP_BAR; + + /* PCI base */ + mmio = platform_get_resource(pdev, IORESOURCE_MEM, + desc->resindex_pcicfg_base); + if (mmio) { + base = mmio->start; + size = resource_size(mmio); + } else { + dev_err(sdev->dev, "error: failed to get PCI base at idx %d\n", + desc->resindex_pcicfg_base); + ret = -ENODEV; + goto pci_err; + } + + dev_dbg(sdev->dev, "PCI base at 0x%x size 0x%x", base, size); + sdev->bar[HSW_PCI_BAR] = ioremap(base, size); + if (!sdev->bar[HSW_PCI_BAR]) { + dev_err(sdev->dev, + "error: failed to ioremap PCI base 0x%x size 0x%x\n", + base, size); + ret = -ENODEV; + goto pci_err; + } + dev_dbg(sdev->dev, "PCI VADDR %p\n", sdev->bar[HSW_PCI_BAR]); + + /* register our IRQ */ + sdev->ipc_irq = platform_get_irq(pdev, desc->irqindex_host_ipc); + if (sdev->ipc_irq < 0) { + dev_err(sdev->dev, "error: failed to get IRQ at index %d\n", + desc->irqindex_host_ipc); + ret = sdev->ipc_irq; + goto irq_err; + } + + dev_dbg(sdev->dev, "using IRQ %d\n", sdev->ipc_irq); + ret = request_threaded_irq(sdev->ipc_irq, hsw_irq_handler, + hsw_irq_thread, 0, "AudioDSP", sdev); + if (ret < 0) { + dev_err(sdev->dev, "error: failed to register IRQ %d\n", + sdev->ipc_irq); + goto irq_err; + } + + /* enable the DSP SHIM */ + ret = hsw_set_dsp_D0(sdev); + if (ret < 0) { + dev_err(sdev->dev, "error: failed to set DSP D0\n"); + return ret; + } + + /* DSP DMA can only access low 31 bits of host memory */ + ret = dma_coerce_mask_and_coherent(sdev->dev, DMA_BIT_MASK(31)); + if (ret < 0) { + dev_err(sdev->dev, "error: failed to set DMA mask %d\n", ret); + return ret; + } + + /* set BARS */ + sdev->cl_bar = HSW_DSP_BAR; + + /* set default mailbox */ + snd_sof_dsp_mailbox_init(sdev, MBOX_OFFSET, MBOX_SIZE, 0, 0); + + return ret; + +irq_err: + iounmap(sdev->bar[HSW_DSP_BAR]); +pci_err: + iounmap(sdev->bar[HSW_PCI_BAR]); + return ret; +} + +static int hsw_remove(struct snd_sof_dev *sdev) +{ + iounmap(sdev->bar[HSW_DSP_BAR]); + iounmap(sdev->bar[HSW_PCI_BAR]); + free_irq(sdev->ipc_irq, sdev); + return 0; +} + +/* haswell ops */ +struct snd_sof_dsp_ops sof_hsw_ops = { + /*Device init */ + .probe = hsw_probe, + .remove = hsw_remove, + + /* DSP Core Control */ + .run = hsw_run, + .reset = hsw_reset, + + /* Register IO */ + .read = hsw_read, + .write = hsw_write, + .read64 = hsw_read64, + .write64 = hsw_write64, + + /* Block IO */ + .block_read = hsw_block_read, + .block_write = hsw_block_write, + + /* mailbox */ + .mailbox_read = hsw_mailbox_read, + .mailbox_write = hsw_mailbox_write, + + /* ipc */ + .send_msg = hsw_send_msg, + .get_reply = hsw_get_reply, + .fw_ready = hsw_fw_ready, + .is_ready = hsw_is_ready, + .cmd_done = hsw_cmd_done, + + /* debug */ + .debug_map = hsw_debugfs, + .debug_map_count = ARRAY_SIZE(hsw_debugfs), + .dbg_dump = hsw_dump, + + /* Module loading */ + .load_module = snd_sof_parse_module_memcpy, + + /*Firmware loading */ + .load_firmware = snd_sof_load_firmware_memcpy, + +}; +EXPORT_SYMBOL(sof_hsw_ops); + +MODULE_LICENSE("Dual BSD/GPL"); From 80c1ab57da6ead608e272542c7714789c2cb92ac Mon Sep 17 00:00:00 2001 From: Liam Girdwood Date: Mon, 8 Jan 2018 20:38:14 +0000 Subject: [PATCH 038/107] ASoC: SOF: Intel: Add support for BDW HW DSP support Add SOF support for Intel Broadwell based devices. Signed-off-by: Liam Girdwood --- sound/soc/sof/intel/bdw.c | 753 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 753 insertions(+) create mode 100644 sound/soc/sof/intel/bdw.c diff --git a/sound/soc/sof/intel/bdw.c b/sound/soc/sof/intel/bdw.c new file mode 100644 index 00000000000000..d1dbef55e27059 --- /dev/null +++ b/sound/soc/sof/intel/bdw.c @@ -0,0 +1,753 @@ +// SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause) +/* + * This file is provided under a dual BSD/GPLv2 license. When using or + * redistributing this file, you may do so under either license. + * + * Copyright(c) 2017 Intel Corporation. All rights reserved. + * + * Author: Liam Girdwood + */ + +/* + * Hardwre interface for audio DSP on Haswell and Broadwell + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include "../sof-priv.h" +#include "../ops.h" +#include "shim.h" + +/* BARs */ +#define BDW_DSP_BAR 0 +#define BDW_PCI_BAR 1 + +/* + * Debug + */ + +/* DSP memories for BDW */ +#define IRAM_OFFSET 0xA0000 +#define BDW_IRAM_SIZE (10 * 32 * 1024) +#define DRAM_OFFSET 0x00000 +#define BDW_DRAM_SIZE (20 * 32 * 1024) +#define SHIM_OFFSET 0xFB000 +#define SHIM_SIZE 0x100 +#define MBOX_OFFSET 0x9E000 +#define MBOX_SIZE 0x1000 +#define MBOX_DUMP_SIZE 0x30 +#define EXCEPT_OFFSET 0x800 + +/* DSP peripherals */ +#define DMAC0_OFFSET 0xFE000 +#define DMAC1_OFFSET 0xFF000 +#define DMAC_SIZE 0x420 +#define SSP0_OFFSET 0xFC000 +#define SSP1_OFFSET 0xFD000 +#define SSP_SIZE 0x100 + +#define BDW_STACK_DUMP_SIZE 32 + +#define BDW_PANIC_OFFSET(x) ((x) & 0xFFFF) + +static const struct snd_sof_debugfs_map bdw_debugfs[] = { + {"dmac0", BDW_DSP_BAR, DMAC0_OFFSET, DMAC_SIZE}, + {"dmac1", BDW_DSP_BAR, DMAC1_OFFSET, DMAC_SIZE}, + {"ssp0", BDW_DSP_BAR, SSP0_OFFSET, SSP_SIZE}, + {"ssp1", BDW_DSP_BAR, SSP1_OFFSET, SSP_SIZE}, + {"iram", BDW_DSP_BAR, IRAM_OFFSET, BDW_IRAM_SIZE}, + {"dram", BDW_DSP_BAR, DRAM_OFFSET, BDW_DRAM_SIZE}, + {"shim", BDW_DSP_BAR, SHIM_OFFSET, SHIM_SIZE}, +}; + +/* + * Memory copy. + */ + +/* write has to deal with copying non 32 bit sized data */ +static void bdw_block_write(struct snd_sof_dev *sdev, u32 offset, void *src, + size_t size) +{ + void __iomem *dest = sdev->bar[sdev->mmio_bar] + offset; + u32 tmp = 0; + int i, m, n; + const u8 *src_byte = src; + + m = size / 4; + n = size % 4; + + /* __iowrite32_copy use 32bit size values so divide by 4 */ + __iowrite32_copy((void *)dest, src, m); + + if (n) { + for (i = 0; i < n; i++) + tmp |= (u32)*(src_byte + m * 4 + i) << (i * 8); + __iowrite32_copy((void *)(dest + m * 4), &tmp, 1); + } +} + +static void bdw_block_read(struct snd_sof_dev *sdev, u32 offset, void *dest, + size_t size) +{ + void __iomem *src = sdev->bar[sdev->mmio_bar] + offset; + + memcpy_fromio(dest, src, size); +} + +static void bdw_mailbox_write(struct snd_sof_dev *sdev, u32 offset, + void *message, size_t bytes) +{ + void __iomem *dest = sdev->bar[sdev->mailbox_bar] + offset; + + memcpy_toio(dest, message, bytes); +} + +static void bdw_mailbox_read(struct snd_sof_dev *sdev, u32 offset, + void *message, size_t bytes) +{ + void __iomem *src = sdev->bar[sdev->mailbox_bar] + offset; + + memcpy_fromio(message, src, bytes); +} + +/* + * Register IO + */ + +static void bdw_write(struct snd_sof_dev *sdev, void __iomem *addr, + u32 value) +{ + writel(value, addr); +} + +static u32 bdw_read(struct snd_sof_dev *sdev, void __iomem *addr) +{ + return readl(addr); +} + +static void bdw_write64(struct snd_sof_dev *sdev, void __iomem *addr, + u64 value) +{ + memcpy_toio(addr, &value, sizeof(value)); +} + +static u64 bdw_read64(struct snd_sof_dev *sdev, void __iomem *addr) +{ + u64 val; + + memcpy_fromio(&val, addr, sizeof(val)); + return val; +} + +/* + * DSP Control. + */ + +static int bdw_run(struct snd_sof_dev *sdev) +{ + /* set oportunistic mode on engine 0,1 for all channels */ + snd_sof_dsp_update_bits(sdev, BDW_DSP_BAR, SHIM_HMDC, + SHIM_HMDC_HDDA_E0_ALLCH | + SHIM_HMDC_HDDA_E1_ALLCH, 0); + + /* set DSP to RUN */ + snd_sof_dsp_update_bits_unlocked(sdev, BDW_DSP_BAR, SHIM_CSR, + SHIM_CSR_STALL, 0x0); + + return 0; +} + +static int bdw_reset(struct snd_sof_dev *sdev) +{ + /* put DSP into reset and stall */ + snd_sof_dsp_update_bits_unlocked(sdev, BDW_DSP_BAR, SHIM_CSR, + SHIM_CSR_RST | SHIM_CSR_STALL, + SHIM_CSR_RST | SHIM_CSR_STALL); + + /* keep in reset for 10ms */ + mdelay(10); + + /* take DSP out of reset and keep stalled for FW loading */ + snd_sof_dsp_update_bits_unlocked(sdev, BDW_DSP_BAR, SHIM_CSR, + SHIM_CSR_RST | SHIM_CSR_STALL, + SHIM_CSR_STALL); + + return 0; +} + +static int bdw_set_dsp_D0(struct snd_sof_dev *sdev) +{ + int tries = 10; + u32 reg; + + /* Disable core clock gating (VDRTCTL2.DCLCGE = 0) */ + snd_sof_dsp_update_bits_unlocked(sdev, BDW_PCI_BAR, PCI_VDRTCTL2, + PCI_VDRTCL2_DCLCGE | + PCI_VDRTCL2_DTCGE, 0); + + /* Disable D3PG (VDRTCTL0.D3PGD = 1) */ + snd_sof_dsp_update_bits_unlocked(sdev, BDW_PCI_BAR, PCI_VDRTCTL0, + PCI_VDRTCL0_D3PGD, PCI_VDRTCL0_D3PGD); + + /* Set D0 state */ + snd_sof_dsp_update_bits_unlocked(sdev, BDW_PCI_BAR, PCI_PMCS, + PCI_PMCS_PS_MASK, 0); + + /* check that ADSP shim is enabled */ + while (tries--) { + reg = readl(sdev->bar[BDW_PCI_BAR] + PCI_PMCS) + & PCI_PMCS_PS_MASK; + if (reg == 0) + goto finish; + + msleep(20); + } + + return -ENODEV; + +finish: + /* + * select SSP1 19.2MHz base clock, SSP clock 0, + * turn off Low Power Clock + */ + snd_sof_dsp_update_bits_unlocked(sdev, BDW_DSP_BAR, SHIM_CSR, + SHIM_CSR_S1IOCS | SHIM_CSR_SBCS1 | + SHIM_CSR_LPCS, 0x0); + + /* stall DSP core, set clk to 192/96Mhz */ + snd_sof_dsp_update_bits_unlocked(sdev, BDW_DSP_BAR, + SHIM_CSR, SHIM_CSR_STALL | + SHIM_CSR_DCS_MASK, + SHIM_CSR_STALL | + SHIM_CSR_DCS(4)); + + /* Set 24MHz MCLK, prevent local clock gating, enable SSP0 clock */ + snd_sof_dsp_update_bits_unlocked(sdev, BDW_DSP_BAR, SHIM_CLKCTL, + SHIM_CLKCTL_MASK | + SHIM_CLKCTL_DCPLCG | + SHIM_CLKCTL_SCOE0, + SHIM_CLKCTL_MASK | + SHIM_CLKCTL_DCPLCG | + SHIM_CLKCTL_SCOE0); + + /* Stall and reset core, set CSR */ + bdw_reset(sdev); + + /* Enable core clock gating (VDRTCTL2.DCLCGE = 1), delay 50 us */ + snd_sof_dsp_update_bits_unlocked(sdev, BDW_PCI_BAR, PCI_VDRTCTL2, + PCI_VDRTCL2_DCLCGE | + PCI_VDRTCL2_DTCGE, + PCI_VDRTCL2_DCLCGE | + PCI_VDRTCL2_DTCGE); + + usleep_range(50, 55); + + /* switch on audio PLL */ + snd_sof_dsp_update_bits_unlocked(sdev, BDW_PCI_BAR, PCI_VDRTCTL2, + PCI_VDRTCL2_APLLSE_MASK, 0); + + /* + * set default power gating control, enable power gating control for + * all blocks. that is, can't be accessed, please enable each block + * before accessing. + */ + snd_sof_dsp_update_bits_unlocked(sdev, BDW_PCI_BAR, PCI_VDRTCTL0, + 0xfffffffC, 0x0); + + /* disable DMA finish function for SSP0 & SSP1 */ + snd_sof_dsp_update_bits_unlocked(sdev, BDW_DSP_BAR, SHIM_CSR2, + SHIM_CSR2_SDFD_SSP1, + SHIM_CSR2_SDFD_SSP1); + + /* set on-demond mode on engine 0,1 for all channels */ + snd_sof_dsp_update_bits(sdev, BDW_DSP_BAR, SHIM_HMDC, + SHIM_HMDC_HDDA_E0_ALLCH | + SHIM_HMDC_HDDA_E1_ALLCH, + SHIM_HMDC_HDDA_E0_ALLCH | + SHIM_HMDC_HDDA_E1_ALLCH); + + /* Enable Interrupt from both sides */ + snd_sof_dsp_update_bits(sdev, BDW_DSP_BAR, SHIM_IMRX, + (SHIM_IMRX_BUSY | SHIM_IMRX_DONE), 0x0); + snd_sof_dsp_update_bits(sdev, BDW_DSP_BAR, SHIM_IMRD, + (SHIM_IMRD_DONE | SHIM_IMRD_BUSY | + SHIM_IMRD_SSP0 | SHIM_IMRD_DMAC), 0x0); + + /* clear IPC registers */ + snd_sof_dsp_write(sdev, BDW_DSP_BAR, SHIM_IPCX, 0x0); + snd_sof_dsp_write(sdev, BDW_DSP_BAR, SHIM_IPCD, 0x0); + snd_sof_dsp_write(sdev, BDW_DSP_BAR, 0x80, 0x6); + snd_sof_dsp_write(sdev, BDW_DSP_BAR, 0xe0, 0x300a); + + return 0; +} + +static void bdw_get_registers(struct snd_sof_dev *sdev, + struct sof_ipc_dsp_oops_xtensa *xoops, + u32 *stack, size_t stack_words) +{ + /* first read regsisters */ + bdw_mailbox_read(sdev, sdev->dsp_oops_offset, xoops, sizeof(*xoops)); + + /* the get the stack */ + bdw_mailbox_read(sdev, sdev->dsp_oops_offset + sizeof(*xoops), stack, + stack_words * sizeof(u32)); +} + +static void bdw_dump(struct snd_sof_dev *sdev, u32 flags) +{ + struct sof_ipc_dsp_oops_xtensa xoops; + u32 stack[BDW_STACK_DUMP_SIZE]; + u32 status, panic; + + /* now try generic SOF status messages */ + status = snd_sof_dsp_read(sdev, BDW_DSP_BAR, SHIM_IPCD); + panic = snd_sof_dsp_read(sdev, BDW_DSP_BAR, SHIM_IPCX); + bdw_get_registers(sdev, &xoops, stack, BDW_STACK_DUMP_SIZE); + snd_sof_get_status(sdev, status, panic, &xoops, stack, + BDW_STACK_DUMP_SIZE); +} + +/* + * IPC Doorbell IRQ handler and thread. + */ + +static irqreturn_t bdw_irq_handler(int irq, void *context) +{ + struct snd_sof_dev *sdev = (struct snd_sof_dev *)context; + u32 isr; + int ret = IRQ_NONE; + + /* Interrupt arrived, check src */ + isr = snd_sof_dsp_read(sdev, BDW_DSP_BAR, SHIM_ISRX); + if (isr & SHIM_ISRX_DONE) { + /* Mask Done interrupt before return */ + snd_sof_dsp_update_bits_unlocked(sdev, BDW_DSP_BAR, + SHIM_IMRX, SHIM_IMRX_DONE, + SHIM_IMRX_DONE); + ret = IRQ_WAKE_THREAD; + } + + if (isr & SHIM_ISRX_BUSY) { + /* Mask Busy interrupt before return */ + snd_sof_dsp_update_bits_unlocked(sdev, BDW_DSP_BAR, + SHIM_IMRX, SHIM_IMRX_BUSY, + SHIM_IMRX_BUSY); + ret = IRQ_WAKE_THREAD; + } + + return ret; +} + +static irqreturn_t bdw_irq_thread(int irq, void *context) +{ + struct snd_sof_dev *sdev = (struct snd_sof_dev *)context; + u32 ipcx, ipcd, hdr; + + ipcx = snd_sof_dsp_read(sdev, BDW_DSP_BAR, SHIM_IPCX); + + /* reply message from DSP */ + if (ipcx & SHIM_IPCX_DONE) { + /* Handle Immediate reply from DSP Core */ + bdw_mailbox_read(sdev, sdev->host_box.offset, &hdr, + sizeof(hdr)); + snd_sof_ipc_reply(sdev, hdr); + + /* clear DONE bit - tell DSP we have completed */ + snd_sof_dsp_update_bits_unlocked(sdev, BDW_DSP_BAR, SHIM_IPCX, + SHIM_IPCX_DONE, 0); + + /* unmask Done interrupt */ + snd_sof_dsp_update_bits_unlocked(sdev, BDW_DSP_BAR, SHIM_IMRX, + SHIM_IMRX_DONE, 0); + } + + ipcd = snd_sof_dsp_read(sdev, BDW_DSP_BAR, SHIM_IPCD); + + /* new message from DSP */ + if (ipcd & SHIM_IPCD_BUSY) { + /* Handle messages from DSP Core */ + if ((ipcd & SOF_IPC_PANIC_MAGIC_MASK) == SOF_IPC_PANIC_MAGIC) { + snd_sof_dsp_panic(sdev, BDW_PANIC_OFFSET(ipcx) + + MBOX_OFFSET); + } else { + snd_sof_ipc_msgs_rx(sdev); + } + } + + return IRQ_HANDLED; +} + +/* + * IPC Firmware ready. + */ +static void bdw_get_windows(struct snd_sof_dev *sdev) +{ + struct sof_ipc_window_elem *elem; + u32 outbox_offset = 0; + u32 stream_offset = 0; + u32 inbox_offset = 0; + u32 outbox_size = 0; + u32 stream_size = 0; + u32 inbox_size = 0; + int i; + + if (!sdev->info_window) { + dev_err(sdev->dev, "error: have no window info\n"); + return; + } + + for (i = 0; i < sdev->info_window->num_windows; i++) { + elem = &sdev->info_window->window[i]; + + switch (elem->type) { + case SOF_IPC_REGION_UPBOX: + inbox_offset = elem->offset + MBOX_OFFSET; + inbox_size = elem->size; + snd_sof_debugfs_create_item(sdev, + sdev->bar[BDW_DSP_BAR] + + inbox_offset, + elem->size, "inbox"); + break; + case SOF_IPC_REGION_DOWNBOX: + outbox_offset = elem->offset + MBOX_OFFSET; + outbox_size = elem->size; + snd_sof_debugfs_create_item(sdev, + sdev->bar[BDW_DSP_BAR] + + outbox_offset, + elem->size, "outbox"); + break; + case SOF_IPC_REGION_TRACE: + snd_sof_debugfs_create_item(sdev, + sdev->bar[BDW_DSP_BAR] + + elem->offset + MBOX_OFFSET, + elem->size, "etrace"); + break; + case SOF_IPC_REGION_DEBUG: + snd_sof_debugfs_create_item(sdev, + sdev->bar[BDW_DSP_BAR] + + elem->offset + MBOX_OFFSET, + elem->size, "debug"); + break; + case SOF_IPC_REGION_STREAM: + stream_offset = elem->offset + MBOX_OFFSET; + stream_size = elem->size; + snd_sof_debugfs_create_item(sdev, + sdev->bar[BDW_DSP_BAR] + + stream_offset, + elem->size, "stream"); + break; + case SOF_IPC_REGION_REGS: + snd_sof_debugfs_create_item(sdev, + sdev->bar[BDW_DSP_BAR] + + elem->offset + MBOX_OFFSET, + elem->size, "regs"); + break; + case SOF_IPC_REGION_EXCEPTION: + sdev->dsp_oops_offset = elem->offset + MBOX_OFFSET; + snd_sof_debugfs_create_item(sdev, + sdev->bar[BDW_DSP_BAR] + + elem->offset + MBOX_OFFSET, + elem->size, "exception"); + break; + default: + dev_err(sdev->dev, "error: get illegal window info\n"); + return; + } + } + + if (outbox_size == 0 || inbox_size == 0) { + dev_err(sdev->dev, "error: get illegal mailbox window\n"); + return; + } + + snd_sof_dsp_mailbox_init(sdev, inbox_offset, inbox_size, + outbox_offset, outbox_size); + sdev->stream_box.offset = stream_offset; + sdev->stream_box.size = stream_size; + + dev_dbg(sdev->dev, " mailbox upstream 0x%x - size 0x%x\n", + inbox_offset, inbox_size); + dev_dbg(sdev->dev, " mailbox downstream 0x%x - size 0x%x\n", + outbox_offset, outbox_size); + dev_dbg(sdev->dev, " stream region 0x%x - size 0x%x\n", + stream_offset, stream_size); +} + +static int bdw_fw_ready(struct snd_sof_dev *sdev, u32 msg_id) +{ + struct sof_ipc_fw_ready *fw_ready = &sdev->fw_ready; + struct sof_ipc_fw_version *v = &fw_ready->version; + u32 offset; + + /* mailbox must be on 4k boundary */ + offset = MBOX_OFFSET; + + dev_dbg(sdev->dev, "ipc: DSP is ready 0x%8.8x offset %d\n", + msg_id, offset); + + /* copy data from the DSP FW ready offset */ + bdw_block_read(sdev, offset, fw_ready, sizeof(*fw_ready)); + + snd_sof_dsp_mailbox_init(sdev, fw_ready->dspbox_offset, + fw_ready->dspbox_size, + fw_ready->hostbox_offset, + fw_ready->hostbox_size); + + dev_info(sdev->dev, + " Firmware info: version %d:%d-%s build %d on %s:%s\n", + v->major, v->minor, v->tag, v->build, v->date, v->time); + + /* now check for extended data */ + snd_sof_fw_parse_ext_data(sdev, MBOX_OFFSET + + sizeof(struct sof_ipc_fw_ready)); + + bdw_get_windows(sdev); + + return 0; +} + +/* + * IPC Mailbox IO + */ + +static int bdw_is_ready(struct snd_sof_dev *sdev) +{ + u32 val; + + val = snd_sof_dsp_read(sdev, BDW_DSP_BAR, SHIM_IPCX); + if (val & SHIM_IPCX_BUSY) + return 0; + + return 1; +} + +static int bdw_send_msg(struct snd_sof_dev *sdev, struct snd_sof_ipc_msg *msg) +{ + /* send the message */ + bdw_mailbox_write(sdev, sdev->host_box.offset, msg->msg_data, + msg->msg_size); + snd_sof_dsp_write(sdev, BDW_DSP_BAR, SHIM_IPCX, SHIM_IPCX_BUSY); + + return 0; +} + +static int bdw_get_reply(struct snd_sof_dev *sdev, struct snd_sof_ipc_msg *msg) +{ + struct sof_ipc_reply reply; + int ret = 0; + u32 size; + + /* get reply */ + bdw_mailbox_read(sdev, sdev->host_box.offset, &reply, sizeof(reply)); + if (reply.error < 0) { + size = sizeof(reply); + ret = reply.error; + } else { + /* reply correct size ? */ + if (reply.hdr.size != msg->reply_size) { + dev_err(sdev->dev, "error: reply expected 0x%zx got 0x%x bytes\n", + msg->reply_size, reply.hdr.size); + size = msg->reply_size; + ret = -EINVAL; + } else { + size = reply.hdr.size; + } + } + + /* read the message */ + if (msg->msg_data && size > 0) + bdw_mailbox_read(sdev, sdev->host_box.offset, msg->reply_data, + size); + + return ret; +} + +static int bdw_cmd_done(struct snd_sof_dev *sdev) +{ + /* clear BUSY bit and set DONE bit - accept new messages */ + snd_sof_dsp_update_bits_unlocked(sdev, BDW_DSP_BAR, SHIM_IPCD, + SHIM_IPCD_BUSY | SHIM_IPCD_DONE, + SHIM_IPCD_DONE); + + /* unmask busy interrupt */ + snd_sof_dsp_update_bits_unlocked(sdev, BDW_DSP_BAR, SHIM_IMRX, + SHIM_IMRX_BUSY, 0); + + return 0; +} + +/* + * Probe and remove. + */ +static int bdw_probe(struct snd_sof_dev *sdev) +{ + struct snd_sof_pdata *pdata = sdev->pdata; + const struct sof_dev_desc *desc = pdata->desc; + struct platform_device *pdev = + container_of(sdev->parent, struct platform_device, dev); + struct resource *mmio; + u32 base, size; + int ret = 0; + + /* LPE base */ + mmio = platform_get_resource(pdev, IORESOURCE_MEM, + desc->resindex_lpe_base); + if (mmio) { + base = mmio->start; + size = resource_size(mmio); + } else { + dev_err(sdev->dev, "error: failed to get LPE base at idx %d\n", + desc->resindex_lpe_base); + return -EINVAL; + } + + dev_dbg(sdev->dev, "LPE PHY base at 0x%x size 0x%x", base, size); + sdev->bar[BDW_DSP_BAR] = ioremap(base, size); + if (!sdev->bar[BDW_DSP_BAR]) { + dev_err(sdev->dev, + "error: failed to ioremap LPE base 0x%x size 0x%x\n", + base, size); + return -ENODEV; + } + dev_dbg(sdev->dev, "LPE VADDR %p\n", sdev->bar[BDW_DSP_BAR]); + + /* TODO: add offsets */ + sdev->mmio_bar = BDW_DSP_BAR; + sdev->mailbox_bar = BDW_DSP_BAR; + + /* PCI base */ + mmio = platform_get_resource(pdev, IORESOURCE_MEM, + desc->resindex_pcicfg_base); + if (mmio) { + base = mmio->start; + size = resource_size(mmio); + } else { + dev_err(sdev->dev, "error: failed to get PCI base at idx %d\n", + desc->resindex_pcicfg_base); + ret = -ENODEV; + goto pci_err; + } + + dev_dbg(sdev->dev, "PCI base at 0x%x size 0x%x", base, size); + sdev->bar[BDW_PCI_BAR] = ioremap(base, size); + if (!sdev->bar[BDW_PCI_BAR]) { + dev_err(sdev->dev, + "error: failed to ioremap PCI base 0x%x size 0x%x\n", + base, size); + ret = -ENODEV; + goto pci_err; + } + dev_dbg(sdev->dev, "PCI VADDR %p\n", sdev->bar[BDW_PCI_BAR]); + + /* register our IRQ */ + sdev->ipc_irq = platform_get_irq(pdev, desc->irqindex_host_ipc); + if (sdev->ipc_irq < 0) { + dev_err(sdev->dev, "error: failed to get IRQ at index %d\n", + desc->irqindex_host_ipc); + ret = sdev->ipc_irq; + goto irq_err; + } + + dev_dbg(sdev->dev, "using IRQ %d\n", sdev->ipc_irq); + ret = request_threaded_irq(sdev->ipc_irq, bdw_irq_handler, + bdw_irq_thread, IRQF_SHARED, "AudioDSP", + sdev); + if (ret < 0) { + dev_err(sdev->dev, "error: failed to register IRQ %d\n", + sdev->ipc_irq); + goto irq_err; + } + + /* enable the DSP SHIM */ + ret = bdw_set_dsp_D0(sdev); + if (ret < 0) { + dev_err(sdev->dev, "error: failed to set DSP D0\n"); + return ret; + } + + /* DSP DMA can only access low 31 bits of host memory */ + ret = dma_coerce_mask_and_coherent(sdev->dev, DMA_BIT_MASK(31)); + if (ret < 0) { + dev_err(sdev->dev, "error: failed to set DMA mask %d\n", ret); + return ret; + } + + /* set BARS */ + sdev->cl_bar = BDW_DSP_BAR; + + /* set default mailbox */ + snd_sof_dsp_mailbox_init(sdev, MBOX_OFFSET, MBOX_SIZE, 0, 0); + + return ret; + +irq_err: + iounmap(sdev->bar[BDW_DSP_BAR]); +pci_err: + iounmap(sdev->bar[BDW_PCI_BAR]); + return ret; +} + +static int bdw_remove(struct snd_sof_dev *sdev) +{ + iounmap(sdev->bar[BDW_DSP_BAR]); + iounmap(sdev->bar[BDW_PCI_BAR]); + free_irq(sdev->ipc_irq, sdev); + return 0; +} + +/* broadwell ops */ +struct snd_sof_dsp_ops sof_bdw_ops = { + /*Device init */ + .probe = bdw_probe, + .remove = bdw_remove, + + /* DSP Core Control */ + .run = bdw_run, + .reset = bdw_reset, + + /* Register IO */ + .read = bdw_read, + .write = bdw_write, + .read64 = bdw_read64, + .write64 = bdw_write64, + + /* Block IO */ + .block_read = bdw_block_read, + .block_write = bdw_block_write, + + /* mailbox */ + .mailbox_read = bdw_mailbox_read, + .mailbox_write = bdw_mailbox_write, + + /* ipc */ + .send_msg = bdw_send_msg, + .get_reply = bdw_get_reply, + .fw_ready = bdw_fw_ready, + .is_ready = bdw_is_ready, + .cmd_done = bdw_cmd_done, + + /* debug */ + .debug_map = bdw_debugfs, + .debug_map_count = ARRAY_SIZE(bdw_debugfs), + .dbg_dump = bdw_dump, + + /* Module loading */ + .load_module = snd_sof_parse_module_memcpy, + + /*Firmware loading */ + .load_firmware = snd_sof_load_firmware_memcpy, +}; +EXPORT_SYMBOL(sof_bdw_ops); + +MODULE_LICENSE("Dual BSD/GPL"); From d3be805cf71b09efd9216dcdbc0edcc3581fc280 Mon Sep 17 00:00:00 2001 From: Liam Girdwood Date: Mon, 8 Jan 2018 20:38:27 +0000 Subject: [PATCH 039/107] ASoC: SOF: Intel: Add APL/CNL HW DSP support Add SOF hardware DSP support for Intel Apollolake and Cannonlake based devices. Signed-off-by: Liam Girdwood --- sound/soc/sof/intel/hda.c | 531 ++++++++++++++++++++++++++++++++++++++ sound/soc/sof/intel/hda.h | 504 ++++++++++++++++++++++++++++++++++++ 2 files changed, 1035 insertions(+) create mode 100644 sound/soc/sof/intel/hda.c create mode 100644 sound/soc/sof/intel/hda.h diff --git a/sound/soc/sof/intel/hda.c b/sound/soc/sof/intel/hda.c new file mode 100644 index 00000000000000..de138cfaa08d85 --- /dev/null +++ b/sound/soc/sof/intel/hda.c @@ -0,0 +1,531 @@ +// SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause) +/* + * This file is provided under a dual BSD/GPLv2 license. When using or + * redistributing this file, you may do so under either license. + * + * Copyright(c) 2017 Intel Corporation. All rights reserved. + * + * Authors: Liam Girdwood + * Ranjani Sridharan + * Jeeja KP + * Rander Wang + * Keyon Jie + */ + +/* + * Hardware interface for generic Intel audio DSP HDA IP + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "../sof-priv.h" +#include "../ops.h" +#include "hda.h" + +/* + * Register IO + */ + +void hda_dsp_write(struct snd_sof_dev *sdev, void __iomem *addr, u32 value) +{ + writel(value, addr); +} + +u32 hda_dsp_read(struct snd_sof_dev *sdev, void __iomem *addr) +{ + return readl(addr); +} + +void hda_dsp_write64(struct snd_sof_dev *sdev, void __iomem *addr, u64 value) +{ + memcpy_toio(addr, &value, sizeof(value)); +} + +u64 hda_dsp_read64(struct snd_sof_dev *sdev, void __iomem *addr) +{ + u64 val; + + memcpy_fromio(&val, addr, sizeof(val)); + return val; +} + +/* + * Memory copy. + */ + +void hda_dsp_block_write(struct snd_sof_dev *sdev, u32 offset, void *src, + size_t size) +{ + void __iomem *dest = sdev->bar[sdev->mmio_bar] + offset; + u32 tmp = 0; + int i, m, n; + const u8 *src_byte = src; + + m = size / 4; + n = size % 4; + + /* __iowrite32_copy use 32bit size values so divide by 4 */ + __iowrite32_copy((void *)dest, src, m); + + if (n) { + for (i = 0; i < n; i++) + tmp |= (u32)*(src_byte + m * 4 + i) << (i * 8); + __iowrite32_copy((void *)(dest + m * 4), &tmp, 1); + } +} + +void hda_dsp_block_read(struct snd_sof_dev *sdev, u32 offset, void *dest, + size_t size) +{ + void __iomem *src = sdev->bar[sdev->mmio_bar] + offset; + + memcpy_fromio(dest, src, size); +} + +/* + * Debug + */ + +struct hda_dsp_msg_code { + u32 code; + const char *msg; +}; + +static const struct hda_dsp_msg_code hda_dsp_rom_msg[] = { + {HDA_DSP_ROM_FW_MANIFEST_LOADED, "status: manifest loaded"}, + {HDA_DSP_ROM_FW_FW_LOADED, "status: fw loaded"}, + {HDA_DSP_ROM_FW_ENTERED, "status: fw entered"}, + {HDA_DSP_ROM_CSE_ERROR, "error: cse error"}, + {HDA_DSP_ROM_CSE_WRONG_RESPONSE, "error: cse wrong response"}, + {HDA_DSP_ROM_IMR_TO_SMALL, "error: IMR too small"}, + {HDA_DSP_ROM_BASE_FW_NOT_FOUND, "error: base fw not found"}, + {HDA_DSP_ROM_CSE_VALIDATION_FAILED, "error: signature verification failed"}, + {HDA_DSP_ROM_IPC_FATAL_ERROR, "error: ipc fatal error"}, + {HDA_DSP_ROM_L2_CACHE_ERROR, "error: L2 cache error"}, + {HDA_DSP_ROM_LOAD_OFFSET_TO_SMALL, "error: load offset too small"}, + {HDA_DSP_ROM_API_PTR_INVALID, "error: API ptr invalid"}, + {HDA_DSP_ROM_BASEFW_INCOMPAT, "error: base fw incompatble"}, + {HDA_DSP_ROM_UNHANDLED_INTERRUPT, "error: unhandled interrupt"}, + {HDA_DSP_ROM_MEMORY_HOLE_ECC, "error: ECC memory hole"}, + {HDA_DSP_ROM_KERNEL_EXCEPTION, "error: kernel exception"}, + {HDA_DSP_ROM_USER_EXCEPTION, "error: user exception"}, + {HDA_DSP_ROM_UNEXPECTED_RESET, "error: unexpected reset"}, + {HDA_DSP_ROM_NULL_FW_ENTRY, "error: null FW entry point"}, +}; + +static void hda_dsp_get_status(struct snd_sof_dev *sdev) +{ + u32 status; + int i; + + status = snd_sof_dsp_read(sdev, HDA_DSP_BAR, + HDA_DSP_SRAM_REG_ROM_STATUS); + + for (i = 0; i < ARRAY_SIZE(hda_dsp_rom_msg); i++) { + if (status == hda_dsp_rom_msg[i].code) { + dev_err(sdev->dev, "%s - code %8.8x\n", + hda_dsp_rom_msg[i].msg, status); + return; + } + } + + /* not for us, must be generic sof message */ + dev_dbg(sdev->dev, "unknown ROM status value %8.8x\n", status); +} + +static void hda_dsp_get_registers(struct snd_sof_dev *sdev, + struct sof_ipc_dsp_oops_xtensa *xoops, + u32 *stack, size_t stack_words) +{ + /* first read registers */ + hda_dsp_block_read(sdev, sdev->dsp_oops_offset, xoops, sizeof(*xoops)); + + /* then get the stack */ + hda_dsp_block_read(sdev, sdev->dsp_oops_offset + sizeof(*xoops), stack, + stack_words * sizeof(u32)); +} + +void hda_dsp_dump(struct snd_sof_dev *sdev, u32 flags) +{ + struct sof_ipc_dsp_oops_xtensa xoops; + u32 stack[HDA_DSP_STACK_DUMP_SIZE]; + u32 status, panic; + + /* try APL specific status message types first */ + hda_dsp_get_status(sdev); + + /* now try generic SOF status messages */ + status = snd_sof_dsp_read(sdev, HDA_DSP_BAR, + HDA_DSP_SRAM_REG_FW_STATUS); + panic = snd_sof_dsp_read(sdev, HDA_DSP_BAR, HDA_DSP_SRAM_REG_FW_TRACEP); + + if (sdev->boot_complete) { + hda_dsp_get_registers(sdev, &xoops, stack, + HDA_DSP_STACK_DUMP_SIZE); + snd_sof_get_status(sdev, status, panic, &xoops, stack, + HDA_DSP_STACK_DUMP_SIZE); + } else { + dev_err(sdev->dev, "error: status = 0x%8.8x panic = 0x%8.8x\n", + status, panic); + hda_dsp_get_status(sdev); + } +} + +/* + * IPC Mailbox IO + */ + +void hda_dsp_mailbox_write(struct snd_sof_dev *sdev, u32 offset, + void *message, size_t bytes) +{ + void __iomem *dest = sdev->bar[sdev->mailbox_bar] + offset; + + memcpy_toio(dest, message, bytes); +} + +void hda_dsp_mailbox_read(struct snd_sof_dev *sdev, u32 offset, + void *message, size_t bytes) +{ + void __iomem *src = sdev->bar[sdev->mailbox_bar] + offset; + + memcpy_fromio(message, src, bytes); +} + +/* + * Supported devices. + */ + +static const struct sof_intel_dsp_desc chip_info[] = { +{ + /* Skylake */ + .id = 0x9d70, + .cores_num = 2, + .cores_mask = HDA_DSP_CORE_MASK(0) | HDA_DSP_CORE_MASK(1), + .ipc_req = HDA_DSP_REG_HIPCI, + .ipc_req_mask = HDA_DSP_REG_HIPCI_BUSY, + .ipc_ack = HDA_DSP_REG_HIPCIE, + .ipc_ack_mask = HDA_DSP_REG_HIPCIE_DONE, + .ipc_ctl = HDA_DSP_REG_HIPCCTL, + .ops = &sof_skl_ops, +}, +{ + /* Kabylake */ + .id = 0x9d71, + .cores_num = 2, + .cores_mask = HDA_DSP_CORE_MASK(0) | HDA_DSP_CORE_MASK(1), + .ipc_req = HDA_DSP_REG_HIPCI, + .ipc_req_mask = HDA_DSP_REG_HIPCI_BUSY, + .ipc_ack = HDA_DSP_REG_HIPCIE, + .ipc_ack_mask = HDA_DSP_REG_HIPCIE_DONE, + .ipc_ctl = HDA_DSP_REG_HIPCCTL, + .ops = &sof_skl_ops, +}, +{ + /* Apollolake - BXT-P */ + .id = 0x5a98, + .cores_num = 2, + .cores_mask = HDA_DSP_CORE_MASK(0) | HDA_DSP_CORE_MASK(1), + .ipc_req = HDA_DSP_REG_HIPCI, + .ipc_req_mask = HDA_DSP_REG_HIPCI_BUSY, + .ipc_ack = HDA_DSP_REG_HIPCIE, + .ipc_ack_mask = HDA_DSP_REG_HIPCIE_DONE, + .ipc_ctl = HDA_DSP_REG_HIPCCTL, + .ops = &sof_apl_ops, +}, +{ + /* BXT-M */ + .id = 0x1a98, + .cores_num = 2, + .cores_mask = HDA_DSP_CORE_MASK(0) | HDA_DSP_CORE_MASK(1), + .ipc_req = HDA_DSP_REG_HIPCI, + .ipc_req_mask = HDA_DSP_REG_HIPCI_BUSY, + .ipc_ack = HDA_DSP_REG_HIPCIE, + .ipc_ack_mask = HDA_DSP_REG_HIPCIE_DONE, + .ipc_ctl = HDA_DSP_REG_HIPCCTL, + .ops = &sof_apl_ops, +}, +{ + /* GeminiLake */ + .id = 0x3198, + .cores_num = 2, + .cores_mask = HDA_DSP_CORE_MASK(0) | HDA_DSP_CORE_MASK(1), + .ipc_req = HDA_DSP_REG_HIPCI, + .ipc_req_mask = HDA_DSP_REG_HIPCI_BUSY, + .ipc_ack = HDA_DSP_REG_HIPCIE, + .ipc_ack_mask = HDA_DSP_REG_HIPCIE_DONE, + .ipc_ctl = HDA_DSP_REG_HIPCCTL, + .ops = &sof_apl_ops, +}, +{ + /* Cannonlake */ + .id = 0x9dc8, + .cores_num = 4, + .cores_mask = HDA_DSP_CORE_MASK(0) | + HDA_DSP_CORE_MASK(1) | + HDA_DSP_CORE_MASK(2) | + HDA_DSP_CORE_MASK(3), + .ipc_req = CNL_DSP_REG_HIPCIDR, + .ipc_req_mask = CNL_DSP_REG_HIPCIDR_BUSY, + .ipc_ack = CNL_DSP_REG_HIPCIDA, + .ipc_ack_mask = CNL_DSP_REG_HIPCIDA_DONE, + .ipc_ctl = CNL_DSP_REG_HIPCCTL, + .ops = &sof_cnl_ops, +}, +}; + +static const struct sof_intel_dsp_desc *get_chip_info(int pci_id) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(chip_info); i++) { + if (chip_info[i].id == pci_id) + return &chip_info[i]; + } + + return NULL; +} + +/* + * We don't need to do a full HDA codec probe as external HDA codec mode is + * considered legacy and will not be supported under SOF. HDMI/DP HDA will + * be supported in the DSP. + */ +int hda_dsp_probe(struct snd_sof_dev *sdev) +{ + struct pci_dev *pci = sdev->pci; + struct sof_intel_hda_dev *hdev; + struct sof_intel_hda_stream *stream; + const struct sof_intel_dsp_desc *chip; + int i; + int ret = 0; + + chip = get_chip_info(pci->device); + if (!chip) { + dev_err(sdev->dev, "no such device supported, chip id:%x\n", + pci->device); + ret = -EIO; + goto err; + } + + hdev = devm_kzalloc(&pci->dev, sizeof(*hdev), GFP_KERNEL); + if (!hdev) + return -ENOMEM; + sdev->hda = hdev; + hdev->desc = chip; + + /* HDA base */ + sdev->bar[HDA_DSP_HDA_BAR] = pci_ioremap_bar(pci, HDA_DSP_HDA_BAR); + if (!sdev->bar[HDA_DSP_HDA_BAR]) { + dev_err(&pci->dev, "error: ioremap error\n"); + /* + * FIXME: why do we return directly, + * should we have a goto err here? + * or should all these gotos be replaced + * by a return? + */ + return -ENXIO; + } + + /* DSP base */ + sdev->bar[HDA_DSP_BAR] = pci_ioremap_bar(pci, HDA_DSP_BAR); + if (!sdev->bar[HDA_DSP_BAR]) { + dev_err(&pci->dev, "error: ioremap error\n"); + ret = -ENXIO; + goto err; + } + + sdev->mmio_bar = HDA_DSP_BAR; + sdev->mailbox_bar = HDA_DSP_BAR; + + pci_set_master(pci); + synchronize_irq(pci->irq); + + /* allow 64bit DMA address if supported by H/W */ + if (!dma_set_mask(&pci->dev, DMA_BIT_MASK(64))) { + dev_dbg(&pci->dev, "DMA mask is 64 bit\n"); + dma_set_coherent_mask(&pci->dev, DMA_BIT_MASK(64)); + } else { + dev_dbg(&pci->dev, "DMA mask is 32 bit\n"); + dma_set_mask(&pci->dev, DMA_BIT_MASK(32)); + dma_set_coherent_mask(&pci->dev, DMA_BIT_MASK(32)); + } + + /* get controller capabilities */ + ret = hda_dsp_ctrl_get_caps(sdev); + if (ret < 0) { + dev_err(&pci->dev, "error: failed to find DSP capability\n"); + goto err; + } + + /* init streams */ + ret = hda_dsp_stream_init(sdev); + if (ret < 0) { + dev_err(&pci->dev, "error: failed to init streams\n"); + /* + * not all errors are due to memory issues, but trying + * to free everything does not harm + */ + goto stream_err; + } + + /* + * clear bits 0-2 of PCI register TCSEL (at offset 0x44) + * TCSEL == Traffic Class Select Register, which sets PCI express QOS + * Ensuring these bits are 0 clears playback static on some HD Audio + * codecs. PCI register TCSEL is defined in the Intel manuals. + */ + snd_sof_pci_update_bits(sdev, PCI_TCSEL, 0x07, 0); + + /* + * while performing reset, controller may not come back properly causing + * issues, so recommendation is to set CGCTL.MISCBDCGE to 0 then do + * reset (init chip) and then again set CGCTL.MISCBDCGE to 1 + */ + snd_sof_pci_update_bits(sdev, PCI_CGCTL, + PCI_CGCTL_MISCBDCGE_MASK, 0); + + /* clear WAKESTS */ + snd_sof_dsp_update_bits(sdev, HDA_DSP_HDA_BAR, SOF_HDA_WAKESTS, + SOF_HDA_WAKESTS_INT_MASK, + SOF_HDA_WAKESTS_INT_MASK); + + /* reset HDA controller */ + ret = hda_dsp_ctrl_link_reset(sdev); + if (ret < 0) { + dev_err(&pci->dev, "error: failed to reset HDA controller\n"); + goto stream_err; + } + + /* clear stream status */ + for (i = 0 ; i < hdev->num_capture ; i++) { + stream = &hdev->cstream[i]; + if (stream) + snd_sof_dsp_update_bits(sdev, HDA_DSP_HDA_BAR, + stream->sd_offset + + SOF_HDA_ADSP_REG_CL_SD_STS, + SOF_HDA_CL_DMA_SD_INT_MASK, + SOF_HDA_CL_DMA_SD_INT_MASK); + } + + for (i = 0 ; i < hdev->num_playback ; i++) { + stream = &hdev->pstream[i]; + if (stream) + snd_sof_dsp_update_bits(sdev, HDA_DSP_HDA_BAR, + stream->sd_offset + + SOF_HDA_ADSP_REG_CL_SD_STS, + SOF_HDA_CL_DMA_SD_INT_MASK, + SOF_HDA_CL_DMA_SD_INT_MASK); + } + + /* clear WAKESTS */ + snd_sof_dsp_update_bits(sdev, HDA_DSP_HDA_BAR, SOF_HDA_WAKESTS, + SOF_HDA_WAKESTS_INT_MASK, + SOF_HDA_WAKESTS_INT_MASK); + + /* clear interrupt status register */ + snd_sof_dsp_write(sdev, HDA_DSP_HDA_BAR, SOF_HDA_INTSTS, + SOF_HDA_INT_CTRL_EN | SOF_HDA_INT_ALL_STREAM); + + /* enable CIE and GIE interrupts */ + snd_sof_dsp_update_bits(sdev, HDA_DSP_HDA_BAR, SOF_HDA_INTCTL, + SOF_HDA_INT_CTRL_EN | SOF_HDA_INT_GLOBAL_EN, + SOF_HDA_INT_CTRL_EN | SOF_HDA_INT_GLOBAL_EN); + + dev_dbg(sdev->dev, "using PCI IRQ %d\n", pci->irq); + + /* register our IRQ */ + ret = request_threaded_irq(pci->irq, hda_dsp_stream_interrupt, + hda_dsp_stream_threaded_handler, + IRQF_SHARED, "AudioHDA", sdev); + if (ret < 0) { + dev_err(sdev->dev, "error: failed to register HDA IRQ %d\n", + sdev->ipc_irq); + goto stream_err; + } + sdev->hda->irq = pci->irq; + + sdev->ipc_irq = pci->irq; + dev_dbg(sdev->dev, "using IPC IRQ %d\n", sdev->ipc_irq); + ret = request_threaded_irq(sdev->ipc_irq, hda_dsp_ipc_irq_handler, + chip->ops->irq_thread, IRQF_SHARED, + "AudioDSP", sdev); + if (ret < 0) { + dev_err(sdev->dev, "error: failed to register IPC IRQ %d\n", + sdev->ipc_irq); + goto irq_err; + } + + /* re-enable CGCTL.MISCBDCGE after reset */ + snd_sof_pci_update_bits(sdev, PCI_CGCTL, + PCI_CGCTL_MISCBDCGE_MASK, + PCI_CGCTL_MISCBDCGE_MASK); + + device_disable_async_suspend(&pci->dev); + + /* enable DSP features */ + snd_sof_dsp_update_bits(sdev, HDA_DSP_PP_BAR, SOF_HDA_REG_PP_PPCTL, + SOF_HDA_PPCTL_GPROCEN, SOF_HDA_PPCTL_GPROCEN); + + /* enable DSP IRQ */ + snd_sof_dsp_update_bits(sdev, HDA_DSP_PP_BAR, SOF_HDA_REG_PP_PPCTL, + SOF_HDA_PPCTL_PIE, SOF_HDA_PPCTL_PIE); + + /* initialize waitq for code loading */ + init_waitqueue_head(&sdev->waitq); + + /* set default mailbox offset for FW ready message */ + sdev->dsp_box.offset = HDA_DSP_MBOX_UPLINK_OFFSET; + + return 0; + +irq_err: + free_irq(pci->irq, sdev); +stream_err: + hda_dsp_stream_free(sdev); +err: + /* disable DSP */ + snd_sof_dsp_update_bits(sdev, HDA_DSP_PP_BAR, SOF_HDA_REG_PP_PPCTL, + SOF_HDA_PPCTL_GPROCEN, 0); + return ret; +} + +int hda_dsp_remove(struct snd_sof_dev *sdev) +{ + const struct sof_intel_dsp_desc *chip = sdev->hda->desc; + + /* disable DSP IRQ */ + snd_sof_dsp_update_bits(sdev, HDA_DSP_PP_BAR, SOF_HDA_REG_PP_PPCTL, + SOF_HDA_PPCTL_PIE, 0); + + /* disable CIE and GIE interrupts */ + snd_sof_dsp_update_bits(sdev, HDA_DSP_HDA_BAR, SOF_HDA_INTCTL, + SOF_HDA_INT_CTRL_EN | SOF_HDA_INT_GLOBAL_EN, 0); + + /* disable cores */ + if (chip) + hda_dsp_core_reset_power_down(sdev, chip->cores_mask); + + /* disable DSP */ + snd_sof_dsp_update_bits(sdev, HDA_DSP_PP_BAR, SOF_HDA_REG_PP_PPCTL, + SOF_HDA_PPCTL_GPROCEN, 0); + + free_irq(sdev->ipc_irq, sdev); + free_irq(sdev->pci->irq, sdev); + + hda_dsp_stream_free(sdev); + return 0; +} + +MODULE_LICENSE("Dual BSD/GPL"); diff --git a/sound/soc/sof/intel/hda.h b/sound/soc/sof/intel/hda.h new file mode 100644 index 00000000000000..5eb677c601dd4f --- /dev/null +++ b/sound/soc/sof/intel/hda.h @@ -0,0 +1,504 @@ +/* SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause) */ +/* + * This file is provided under a dual BSD/GPLv2 license. When using or + * redistributing this file, you may do so under either license. + * + * Copyright(c) 2017 Intel Corporation. All rights reserved. + * + * Author: Liam Girdwood + */ + +#ifndef __SOF_INTEL_HDA_H +#define __SOF_INTEL_HDA_H + +/* PCI registers */ +#define PCI_TCSEL 0x44 +#define PCI_CGCTL 0x48 + +/* PCI_CGCTL bits */ +#define PCI_CGCTL_MISCBDCGE_MASK BIT(6) + +/* Legacy HDA registers and bits used - widths are variable */ +#define SOF_HDA_GCAP 0x0 +#define SOF_HDA_GCTL 0x8 +/* accept unsol. response enable */ +#define SOF_HDA_GCTL_UNSOL BIT(8) +#define SOF_HDA_LLCH 0x14 +#define SOF_HDA_INTCTL 0x20 +#define SOF_HDA_INTSTS 0x24 +#define SOF_HDA_WAKESTS 0x0E +#define SOF_HDA_WAKESTS_INT_MASK ((1 << 8) - 1) + +/* SOF_HDA_GCTL register bist */ +#define SOF_HDA_GCTL_RESET BIT(0) + +/* SOF_HDA_INCTL and SOF_HDA_INTSTS regs */ +#define SOF_HDA_INT_GLOBAL_EN BIT(31) +#define SOF_HDA_INT_CTRL_EN BIT(30) +#define SOF_HDA_INT_ALL_STREAM 0xff + +#define SOF_HDA_MAX_CAPS 10 +#define SOF_HDA_CAP_ID_OFF 16 +#define SOF_HDA_CAP_ID_MASK (0xFFF << SOF_HDA_CAP_ID_OFF) +#define SOF_HDA_CAP_NEXT_MASK 0xFFFF + +#define SOF_HDA_PP_CAP_ID 0x3 +#define SOF_HDA_REG_PP_PPCH 0x10 +#define SOF_HDA_REG_PP_PPCTL 0x04 +#define SOF_HDA_PPCTL_PIE BIT(31) +#define SOF_HDA_PPCTL_GPROCEN BIT(30) + +#define SOF_HDA_SPIB_CAP_ID 0x4 +#define SOF_HDA_DRSM_CAP_ID 0x5 + +#define SOF_HDA_SPIB_BASE 0x08 +#define SOF_HDA_SPIB_INTERVAL 0x08 +#define SOF_HDA_SPIB_SPIB 0x00 +#define SOF_HDA_SPIB_MAXFIFO 0x04 + +#define SOF_HDA_PPHC_BASE 0x10 +#define SOF_HDA_PPHC_INTERVAL 0x10 + +#define SOF_HDA_PPLC_BASE 0x10 +#define SOF_HDA_PPLC_MULTI 0x10 +#define SOF_HDA_PPLC_INTERVAL 0x10 + +#define SOF_HDA_DRSM_BASE 0x08 +#define SOF_HDA_DRSM_INTERVAL 0x08 + +/* Descriptor error interrupt */ +#define SOF_HDA_CL_DMA_SD_INT_DESC_ERR 0x10 + +/* FIFO error interrupt */ +#define SOF_HDA_CL_DMA_SD_INT_FIFO_ERR 0x08 + +/* Buffer completion interrupt */ +#define SOF_HDA_CL_DMA_SD_INT_COMPLETE 0x04 + +#define SOF_HDA_CL_DMA_SD_INT_MASK \ + (SOF_HDA_CL_DMA_SD_INT_DESC_ERR | \ + SOF_HDA_CL_DMA_SD_INT_FIFO_ERR | \ + SOF_HDA_CL_DMA_SD_INT_COMPLETE) +#define SOF_HDA_SD_CTL_DMA_START 0x02 /* Stream DMA start bit */ + +/* Intel HD Audio Code Loader DMA Registers */ +#define SOF_HDA_ADSP_LOADER_BASE 0x80 +#define SOF_HDA_ADSP_DPLBASE 0x70 +#define SOF_HDA_ADSP_DPUBASE 0x74 +#define SOF_HDA_ADSP_DPLBASE_ENABLE 0x01 + +/* Stream Registers */ +#define SOF_HDA_ADSP_REG_CL_SD_CTL 0x00 +#define SOF_HDA_ADSP_REG_CL_SD_STS 0x03 +#define SOF_HDA_ADSP_REG_CL_SD_LPIB 0x04 +#define SOF_HDA_ADSP_REG_CL_SD_CBL 0x08 +#define SOF_HDA_ADSP_REG_CL_SD_LVI 0x0C +#define SOF_HDA_ADSP_REG_CL_SD_FIFOW 0x0E +#define SOF_HDA_ADSP_REG_CL_SD_FIFOSIZE 0x10 +#define SOF_HDA_ADSP_REG_CL_SD_FORMAT 0x12 +#define SOF_HDA_ADSP_REG_CL_SD_FIFOL 0x14 +#define SOF_HDA_ADSP_REG_CL_SD_BDLPL 0x18 +#define SOF_HDA_ADSP_REG_CL_SD_BDLPU 0x1C + +/* CL: Software Position Based FIFO Capability Registers */ +#define SOF_DSP_REG_CL_SPBFIFO \ + (SOF_HDA_ADSP_LOADER_BASE + 0x20) +#define SOF_HDA_ADSP_REG_CL_SPBFIFO_SPBFCH 0x0 +#define SOF_HDA_ADSP_REG_CL_SPBFIFO_SPBFCCTL 0x4 +#define SOF_HDA_ADSP_REG_CL_SPBFIFO_SPIB 0x8 +#define SOF_HDA_ADSP_REG_CL_SPBFIFO_MAXFIFOS 0xc + +/* Stream Number */ +#define SOF_HDA_CL_SD_CTL_STREAM_TAG_SHIFT 20 +#define SOF_HDA_CL_SD_CTL_STREAM_TAG_MASK \ + (0xf << SOF_HDA_CL_SD_CTL_STREAM_TAG_SHIFT) + +#define HDA_DSP_HDA_BAR 0 +#define HDA_DSP_PP_BAR 1 +#define HDA_DSP_SPIB_BAR 2 +#define HDA_DSP_DRSM_BAR 3 +#define HDA_DSP_BAR 4 + +#define SRAM_WINDOW_OFFSET(x) (0x80000 + x * 0x20000) + +#define HDA_DSP_MBOX_OFFSET SRAM_WINDOW_OFFSET(0) + +#define HDA_DSP_PANIC_OFFSET(x) \ + (((x) & 0xFFFFFF) + HDA_DSP_MBOX_OFFSET) + +/* SRAM window 0 FW "registers" */ +#define HDA_DSP_SRAM_REG_ROM_STATUS (HDA_DSP_MBOX_OFFSET + 0x0) +#define HDA_DSP_SRAM_REG_ROM_ERROR (HDA_DSP_MBOX_OFFSET + 0x4) +/* FW and ROM share offset 4 */ +#define HDA_DSP_SRAM_REG_FW_STATUS (HDA_DSP_MBOX_OFFSET + 0x4) +#define HDA_DSP_SRAM_REG_FW_TRACEP (HDA_DSP_MBOX_OFFSET + 0x8) +#define HDA_DSP_SRAM_REG_FW_END (HDA_DSP_MBOX_OFFSET + 0xc) + +#define HDA_DSP_MBOX_UPLINK_OFFSET 0x81000 + +#define HDA_DSP_STREAM_RESET_TIMEOUT 300 +#define HDA_DSP_CL_TRIGGER_TIMEOUT 300 + +#define HDA_DSP_SPIB_ENABLE 1 +#define HDA_DSP_SPIB_DISABLE 0 + +#define SOF_HDA_MAX_BUFFER_SIZE (32 * PAGE_SIZE) + +#define HDA_DSP_STACK_DUMP_SIZE 32 + +/* ROM status/error values */ +#define HDA_DSP_ROM_STS_MASK 0xf +#define HDA_DSP_ROM_INIT 0x1 +#define HDA_DSP_ROM_FW_MANIFEST_LOADED 0x3 +#define HDA_DSP_ROM_FW_FW_LOADED 0x4 +#define HDA_DSP_ROM_FW_ENTERED 0x5 +#define HDA_DSP_ROM_RFW_START 0xf +#define HDA_DSP_ROM_CSE_ERROR 40 +#define HDA_DSP_ROM_CSE_WRONG_RESPONSE 41 +#define HDA_DSP_ROM_IMR_TO_SMALL 42 +#define HDA_DSP_ROM_BASE_FW_NOT_FOUND 43 +#define HDA_DSP_ROM_CSE_VALIDATION_FAILED 44 +#define HDA_DSP_ROM_IPC_FATAL_ERROR 45 +#define HDA_DSP_ROM_L2_CACHE_ERROR 46 +#define HDA_DSP_ROM_LOAD_OFFSET_TO_SMALL 47 +#define HDA_DSP_ROM_API_PTR_INVALID 50 +#define HDA_DSP_ROM_BASEFW_INCOMPAT 51 +#define HDA_DSP_ROM_UNHANDLED_INTERRUPT 0xBEE00000 +#define HDA_DSP_ROM_MEMORY_HOLE_ECC 0xECC00000 +#define HDA_DSP_ROM_KERNEL_EXCEPTION 0xCAFE0000 +#define HDA_DSP_ROM_USER_EXCEPTION 0xBEEF0000 +#define HDA_DSP_ROM_UNEXPECTED_RESET 0xDECAF000 +#define HDA_DSP_ROM_NULL_FW_ENTRY 0x4c4c4e55 +#define HDA_DSP_IPC_PURGE_FW 0x01004000 + +/* various timeout values */ +#define HDA_DSP_PU_TIMEOUT 50 +#define HDA_DSP_PD_TIMEOUT 50 +#define HDA_DSP_RESET_TIMEOUT 50 +#define HDA_DSP_BASEFW_TIMEOUT 3000 +#define HDA_DSP_INIT_TIMEOUT 500 +#define HDA_DSP_CTRL_RESET_TIMEOUT 100 +#define HDA_DSP_WAIT_TIMEOUT 500 /* 500 msec */ + +#define HDA_DSP_ADSPIC_IPC 1 +#define HDA_DSP_ADSPIS_IPC 1 + +/* Intel HD Audio General DSP Registers */ +#define HDA_DSP_GEN_BASE 0x0 +#define HDA_DSP_REG_ADSPCS (HDA_DSP_GEN_BASE + 0x04) +#define HDA_DSP_REG_ADSPIC (HDA_DSP_GEN_BASE + 0x08) +#define HDA_DSP_REG_ADSPIS (HDA_DSP_GEN_BASE + 0x0C) +#define HDA_DSP_REG_ADSPIC2 (HDA_DSP_GEN_BASE + 0x10) +#define HDA_DSP_REG_ADSPIS2 (HDA_DSP_GEN_BASE + 0x14) + +/* Intel HD Audio Inter-Processor Communication Registers */ +#define HDA_DSP_IPC_BASE 0x40 +#define HDA_DSP_REG_HIPCT (HDA_DSP_IPC_BASE + 0x00) +#define HDA_DSP_REG_HIPCTE (HDA_DSP_IPC_BASE + 0x04) +#define HDA_DSP_REG_HIPCI (HDA_DSP_IPC_BASE + 0x08) +#define HDA_DSP_REG_HIPCIE (HDA_DSP_IPC_BASE + 0x0C) +#define HDA_DSP_REG_HIPCCTL (HDA_DSP_IPC_BASE + 0x10) + +/* HIPCI */ +#define HDA_DSP_REG_HIPCI_BUSY BIT(31) +#define HDA_DSP_REG_HIPCI_MSG_MASK 0x7FFFFFFF + +/* HIPCIE */ +#define HDA_DSP_REG_HIPCIE_DONE BIT(30) +#define HDA_DSP_REG_HIPCIE_MSG_MASK 0x3FFFFFFF + +/* HIPCCTL */ +#define HDA_DSP_REG_HIPCCTL_DONE BIT(1) +#define HDA_DSP_REG_HIPCCTL_BUSY BIT(0) + +/* HIPCT */ +#define HDA_DSP_REG_HIPCT_BUSY BIT(31) +#define HDA_DSP_REG_HIPCT_MSG_MASK 0x7FFFFFFF + +/* HIPCTE */ +#define HDA_DSP_REG_HIPCTE_MSG_MASK 0x3FFFFFFF + +#define HDA_DSP_ADSPIC_CL_DMA 0x2 +#define HDA_DSP_ADSPIS_CL_DMA 0x2 + +/* Delay before scheduling D0i3 entry */ +#define BXT_D0I3_DELAY 5000 + +#define FW_CL_STREAM_NUMBER 0x1 + +/* ADSPCS - Audio DSP Control & Status */ + +/* + * Core Reset - asserted high + * CRST Mask for a given core mask pattern, cm + */ +#define HDA_DSP_ADSPCS_CRST_SHIFT 0 +#define HDA_DSP_ADSPCS_CRST_MASK(cm) ((cm) << HDA_DSP_ADSPCS_CRST_SHIFT) + +/* + * Core run/stall - when set to '1' core is stalled + * CSTALL Mask for a given core mask pattern, cm + */ +#define HDA_DSP_ADSPCS_CSTALL_SHIFT 8 +#define HDA_DSP_ADSPCS_CSTALL_MASK(cm) ((cm) << HDA_DSP_ADSPCS_CSTALL_SHIFT) + +/* + * Set Power Active - when set to '1' turn cores on + * SPA Mask for a given core mask pattern, cm + */ +#define HDA_DSP_ADSPCS_SPA_SHIFT 16 +#define HDA_DSP_ADSPCS_SPA_MASK(cm) ((cm) << HDA_DSP_ADSPCS_SPA_SHIFT) + +/* + * Current Power Active - power status of cores, set by hardware + * CPA Mask for a given core mask pattern, cm + */ +#define HDA_DSP_ADSPCS_CPA_SHIFT 24 +#define HDA_DSP_ADSPCS_CPA_MASK(cm) ((cm) << HDA_DSP_ADSPCS_CPA_SHIFT) + +#define HDA_DSP_ADSPIC_CL_DMA 0x2 +#define HDA_DSP_ADSPIS_CL_DMA 0x2 + +/* Mask for a given core index, c = 0.. number of supported cores - 1 */ +#define HDA_DSP_CORE_MASK(c) BIT(c) + +/* + * Mask for a given number of cores + * nc = number of supported cores + */ +#define SOF_DSP_CORES_MASK(nc) GENMASK((nc - 1), 0) + +/* Intel HD Audio Inter-Processor Communication Registers for Cannonlake*/ +#define CNL_DSP_IPC_BASE 0xc0 +#define CNL_DSP_REG_HIPCTDR (CNL_DSP_IPC_BASE + 0x00) +#define CNL_DSP_REG_HIPCTDA (CNL_DSP_IPC_BASE + 0x04) +#define CNL_DSP_REG_HIPCTDD (CNL_DSP_IPC_BASE + 0x08) +#define CNL_DSP_REG_HIPCIDR (CNL_DSP_IPC_BASE + 0x10) +#define CNL_DSP_REG_HIPCIDA (CNL_DSP_IPC_BASE + 0x14) +#define CNL_DSP_REG_HIPCCTL (CNL_DSP_IPC_BASE + 0x28) + +/* HIPCI */ +#define CNL_DSP_REG_HIPCIDR_BUSY BIT(31) +#define CNL_DSP_REG_HIPCIDR_MSG_MASK 0x7FFFFFFF + +/* HIPCIE */ +#define CNL_DSP_REG_HIPCIDA_DONE BIT(31) +#define CNL_DSP_REG_HIPCIDA_MSG_MASK 0x7FFFFFFF + +/* HIPCCTL */ +#define CNL_DSP_REG_HIPCCTL_DONE BIT(1) +#define CNL_DSP_REG_HIPCCTL_BUSY BIT(0) + +/* HIPCT */ +#define CNL_DSP_REG_HIPCTDR_BUSY BIT(31) +#define CNL_DSP_REG_HIPCTDR_MSG_MASK 0x7FFFFFFF + +/* HIPCTDA */ +#define CNL_DSP_REG_HIPCTDA_DONE BIT(31) +#define CNL_DSP_REG_HIPCTDA_MSG_MASK 0x7FFFFFFF + +/* HIPCTDD */ +#define CNL_DSP_REG_HIPCTDD_MSG_MASK 0x7FFFFFFF + +/* BDL */ +#define HDA_DSP_BDL_SIZE 4096 +#define HDA_DSP_MAX_BDL_ENTRIES \ + (HDA_DSP_BDL_SIZE / sizeof(struct sof_intel_dsp_bdl)) + +struct sof_intel_dsp_bdl { + u32 addr_l; + u32 addr_h; + u32 size; + u32 ioc; +} __attribute((packed)); + +/* DSP hardware descriptor */ +struct sof_intel_dsp_desc { + int id; + int cores_num; + int cores_mask; + int ipc_req; + int ipc_req_mask; + int ipc_ack; + int ipc_ack_mask; + int ipc_ctl; + struct snd_sof_dsp_ops *ops; +}; + +/* per stream data for HDA DSP Frontend */ +struct sof_intel_hda_stream { + + /* addresses for stream HDA functions */ + void __iomem *pphc_addr; + void __iomem *pplc_addr; + void __iomem *spib_addr; + void __iomem *fifo_addr; + void __iomem *drsm_addr; + + /* runtime state */ + u32 dpib; + u32 lpib; + int tag; + int direction; + bool open; + bool running; + u32 index; + + /* buffer & descriptors */ + struct snd_dma_buffer bdl; + void __iomem *sd_addr; /* stream descriptor pointer */ + int sd_offset; /* Stream descriptor offset */ + unsigned int bufsize; /* size of the play buffer in bytes */ + unsigned int fifo_size; /* FIFO size */ + + __le32 *posbuf; /* position buffer pointer */ + unsigned int frags; /* number for period in the play buffer */ + unsigned int config; /* format config value */ + + /* PCM */ + struct snd_pcm_substream *substream; +}; + +#define SOF_HDA_PLAYBACK_STREAMS 16 +#define SOF_HDA_CAPTURE_STREAMS 16 +#define SOF_HDA_PLAYBACK 0 +#define SOF_HDA_CAPTURE 1 + +/* represents DSP HDA controller frontend - i.e. host facing control */ +struct sof_intel_hda_dev { + + /* hw config */ + const struct sof_intel_dsp_desc *desc; + + /* streams */ + struct sof_intel_hda_stream pstream[SOF_HDA_PLAYBACK_STREAMS]; + struct sof_intel_hda_stream cstream[SOF_HDA_CAPTURE_STREAMS]; + int num_capture; + int num_playback; + + /* position buffers */ + struct snd_dma_buffer posbuffer; + + /*trace */ + struct sof_intel_hda_stream *dtrace_stream; + + int irq; +}; + +/* + * DSP Core services. + */ +int hda_dsp_probe(struct snd_sof_dev *sdev); +int hda_dsp_remove(struct snd_sof_dev *sdev); +int hda_dsp_core_reset_enter(struct snd_sof_dev *sdev, + unsigned int core_mask); +int hda_dsp_core_reset_leave(struct snd_sof_dev *sdev, + unsigned int core_mask); +int hda_dsp_core_stall_reset(struct snd_sof_dev *sdev, unsigned int core_mask); +int hda_dsp_core_run(struct snd_sof_dev *sdev, unsigned int core_mask); +int hda_dsp_core_power_up(struct snd_sof_dev *sdev, unsigned int core_mask); +int hda_dsp_core_power_down(struct snd_sof_dev *sdev, unsigned int core_mask); +bool hda_dsp_core_is_enabled(struct snd_sof_dev *sdev, + unsigned int core_mask); +int hda_dsp_core_reset_power_down(struct snd_sof_dev *sdev, + unsigned int core_mask); +void hda_dsp_dump(struct snd_sof_dev *sdev, u32 flags); + +/* + * DSP IO + */ +void hda_dsp_write(struct snd_sof_dev *sdev, void __iomem *addr, u32 value); +u32 hda_dsp_read(struct snd_sof_dev *sdev, void __iomem *addr); +void hda_dsp_write64(struct snd_sof_dev *sdev, void __iomem *addr, u64 value); +u64 hda_dsp_read64(struct snd_sof_dev *sdev, void __iomem *addr); +void hda_dsp_block_write(struct snd_sof_dev *sdev, u32 offset, void *src, + size_t size); +void hda_dsp_block_read(struct snd_sof_dev *sdev, u32 offset, void *dest, + size_t size); +void hda_dsp_mailbox_write(struct snd_sof_dev *sdev, u32 offset, + void *message, size_t bytes); +void hda_dsp_mailbox_read(struct snd_sof_dev *sdev, u32 offset, + void *message, size_t bytes); + +/* + * DSP PCM Operations. + */ +int hda_dsp_pcm_open(struct snd_sof_dev *sdev, + struct snd_pcm_substream *substream); +int hda_dsp_pcm_close(struct snd_sof_dev *sdev, + struct snd_pcm_substream *substream); +int hda_dsp_pcm_hw_params(struct snd_sof_dev *sdev, + struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params); +int hda_dsp_pcm_trigger(struct snd_sof_dev *sdev, + struct snd_pcm_substream *substream, int cmd); + +/* + * DSP Stream Operations. + */ + +int hda_dsp_stream_init(struct snd_sof_dev *sdev); +void hda_dsp_stream_free(struct snd_sof_dev *sdev); +int hda_dsp_stream_hw_params(struct snd_sof_dev *sdev, + struct sof_intel_hda_stream *stream, + struct snd_dma_buffer *dmab, + struct snd_pcm_hw_params *params); +int hda_dsp_stream_trigger(struct snd_sof_dev *sdev, + struct sof_intel_hda_stream *stream, int cmd); +irqreturn_t hda_dsp_stream_interrupt(int irq, void *context); +irqreturn_t hda_dsp_stream_threaded_handler(int irq, void *context); +int hda_dsp_stream_setup_bdl(struct snd_sof_dev *sdev, + struct snd_dma_buffer *dmab, + struct sof_intel_hda_stream *stream, + struct sof_intel_dsp_bdl *bdl, int size, + struct snd_pcm_hw_params *params); +struct sof_intel_hda_stream * + hda_dsp_stream_get_cstream(struct snd_sof_dev *sdev); +struct sof_intel_hda_stream * + hda_dsp_stream_get_pstream(struct snd_sof_dev *sdev); +int hda_dsp_stream_put_pstream(struct snd_sof_dev *sdev, int stream_tag); +int hda_dsp_stream_put_cstream(struct snd_sof_dev *sdev, int stream_tag); +int hda_dsp_stream_spib_config(struct snd_sof_dev *sdev, + struct sof_intel_hda_stream *stream, + int enable, u32 size); + +/* + * DSP IPC Operations. + */ +int hda_dsp_ipc_is_ready(struct snd_sof_dev *sdev); +int hda_dsp_ipc_send_msg(struct snd_sof_dev *sdev, + struct snd_sof_ipc_msg *msg); +int hda_dsp_ipc_get_reply(struct snd_sof_dev *sdev, + struct snd_sof_ipc_msg *msg); +int hda_dsp_ipc_fw_ready(struct snd_sof_dev *sdev, u32 msg_id); +irqreturn_t hda_dsp_ipc_irq_handler(int irq, void *context); +irqreturn_t hda_dsp_ipc_irq_thread(int irq, void *context); +int hda_dsp_ipc_cmd_done(struct snd_sof_dev *sdev); + +/* + * DSP Code loader. + */ +int hda_dsp_cl_load_fw(struct snd_sof_dev *sdev, const struct firmware *fw); +int hda_dsp_cl_boot_firmware(struct snd_sof_dev *sdev); + +/* + * HDA Controller Operations. + */ +int hda_dsp_ctrl_get_caps(struct snd_sof_dev *sdev); +int hda_dsp_ctrl_link_reset(struct snd_sof_dev *sdev); + +/* + * Trace Control. + */ +int hda_dsp_trace_init(struct snd_sof_dev *sdev, u32 *stream_tag); +int hda_dsp_trace_release(struct snd_sof_dev *sdev); +int hda_dsp_trace_trigger(struct snd_sof_dev *sdev, int cmd); + +/* + * Platform Specific HW abstraction Ops. + */ +extern struct snd_sof_dsp_ops sof_apl_ops; +extern struct snd_sof_dsp_ops sof_cnl_ops; +extern struct snd_sof_dsp_ops sof_skl_ops; + +#endif From 7f48b6b01cc1834fc952e7a1507ff1be714192e6 Mon Sep 17 00:00:00 2001 From: Liam Girdwood Date: Wed, 9 May 2018 17:14:11 +0100 Subject: [PATCH 040/107] ASoC: SOF: Intel: Add HDA controller for Intel DSP Support HDA controller operations for DSP and provide space for future DSP HDA FW integration. Signed-off-by: Liam Girdwood --- sound/soc/sof/intel/hda-ctrl.c | 133 +++++++++++++++++++++++++++++++++ 1 file changed, 133 insertions(+) create mode 100644 sound/soc/sof/intel/hda-ctrl.c diff --git a/sound/soc/sof/intel/hda-ctrl.c b/sound/soc/sof/intel/hda-ctrl.c new file mode 100644 index 00000000000000..57b877bde8a8f4 --- /dev/null +++ b/sound/soc/sof/intel/hda-ctrl.c @@ -0,0 +1,133 @@ +// SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause) +/* + * This file is provided under a dual BSD/GPLv2 license. When using or + * redistributing this file, you may do so under either license. + * + * Copyright(c) 2017 Intel Corporation. All rights reserved. + * + * Authors: Liam Girdwood + * Ranjani Sridharan + * Jeeja KP + * Rander Wang + * Keyon Jie + */ + +/* + * Hardware interface for generic Intel audio DSP HDA IP + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "../sof-priv.h" +#include "../ops.h" +#include "hda.h" + +/* + * HDA Operations. + */ + +int hda_dsp_ctrl_link_reset(struct snd_sof_dev *sdev) +{ + unsigned long timeout; + u32 gctl = 0; + + /* reset the HDA controller */ + snd_sof_dsp_update_bits(sdev, HDA_DSP_HDA_BAR, SOF_HDA_GCTL, + SOF_HDA_GCTL_RESET, 0); + + /* wait for reset */ + timeout = jiffies + msecs_to_jiffies(HDA_DSP_CTRL_RESET_TIMEOUT); + while (time_before(jiffies, timeout)) { + usleep_range(500, 1000); + gctl = snd_sof_dsp_read(sdev, HDA_DSP_HDA_BAR, SOF_HDA_GCTL); + if ((gctl & SOF_HDA_GCTL_RESET) == 0) + goto clear; + } + + /* reset failed */ + dev_err(sdev->dev, "error: failed to reset HDA controller gctl 0x%x\n", + gctl); + return -EIO; + +clear: + /* wait for codec */ + usleep_range(500, 1000); + + /* now take controller out of reset */ + snd_sof_dsp_update_bits(sdev, HDA_DSP_HDA_BAR, SOF_HDA_GCTL, + SOF_HDA_GCTL_RESET, SOF_HDA_GCTL_RESET); + + /* wait for controller to be ready */ + timeout = jiffies + msecs_to_jiffies(HDA_DSP_CTRL_RESET_TIMEOUT); + while (time_before(jiffies, timeout)) { + gctl = snd_sof_dsp_read(sdev, HDA_DSP_HDA_BAR, SOF_HDA_GCTL); + if ((gctl & SOF_HDA_GCTL_RESET) == 1) + return 0; + usleep_range(500, 1000); + } + + /* reset failed */ + dev_err(sdev->dev, "error: failed to ready HDA controller gctl 0x%x\n", + gctl); + return -EIO; +} + +int hda_dsp_ctrl_get_caps(struct snd_sof_dev *sdev) +{ + u32 cap, offset, feature; + int ret = -ENODEV, count = 0; + + offset = snd_sof_dsp_read(sdev, HDA_DSP_HDA_BAR, SOF_HDA_LLCH); + + do { + cap = snd_sof_dsp_read(sdev, HDA_DSP_HDA_BAR, offset); + + dev_dbg(sdev->dev, "checking for capabilities at offset 0x%x\n", + offset & SOF_HDA_CAP_NEXT_MASK); + + feature = (cap & SOF_HDA_CAP_ID_MASK) >> SOF_HDA_CAP_ID_OFF; + + switch (feature) { + case SOF_HDA_PP_CAP_ID: + dev_dbg(sdev->dev, "found DSP capability at 0x%x\n", + offset); + sdev->bar[HDA_DSP_PP_BAR] = sdev->bar[HDA_DSP_HDA_BAR] + + offset; + ret = 0; + break; + case SOF_HDA_SPIB_CAP_ID: + dev_dbg(sdev->dev, "found SPIB capability at 0x%x\n", + offset); + sdev->bar[HDA_DSP_SPIB_BAR] = + sdev->bar[HDA_DSP_HDA_BAR] + offset; + break; + case SOF_HDA_DRSM_CAP_ID: + dev_dbg(sdev->dev, "found DRSM capability at 0x%x\n", + offset); + sdev->bar[HDA_DSP_DRSM_BAR] = + sdev->bar[HDA_DSP_HDA_BAR] + offset; + break; + default: + dev_vdbg(sdev->dev, "found capability %d at 0x%x\n", + feature, offset); + break; + } + + offset = cap & SOF_HDA_CAP_NEXT_MASK; + } while (count++ <= SOF_HDA_MAX_CAPS && offset); + + return ret; +} + From 5d9b8b00d22fbe4156907c824db60b0e9f0673f3 Mon Sep 17 00:00:00 2001 From: Liam Girdwood Date: Wed, 9 May 2018 17:14:12 +0100 Subject: [PATCH 041/107] ASoC: SOF: Intel: Add Intel specific HDA DSP HW operations Add support for various PM and core reset/run state transitions. Signed-off-by: Liam Girdwood --- sound/soc/sof/intel/hda-dsp.c | 241 ++++++++++++++++++++++++++++++++++ 1 file changed, 241 insertions(+) create mode 100644 sound/soc/sof/intel/hda-dsp.c diff --git a/sound/soc/sof/intel/hda-dsp.c b/sound/soc/sof/intel/hda-dsp.c new file mode 100644 index 00000000000000..82f9ce897137c5 --- /dev/null +++ b/sound/soc/sof/intel/hda-dsp.c @@ -0,0 +1,241 @@ +// SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause) +/* + * This file is provided under a dual BSD/GPLv2 license. When using or + * redistributing this file, you may do so under either license. + * + * Copyright(c) 2017 Intel Corporation. All rights reserved. + * + * Authors: Liam Girdwood + * Ranjani Sridharan + * Jeeja KP + * Rander Wang + * Keyon Jie + */ + +/* + * Hardware interface for generic Intel audio DSP HDA IP + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "../sof-priv.h" +#include "../ops.h" +#include "hda.h" + +/* + * DSP Core control. + */ + +int hda_dsp_core_reset_enter(struct snd_sof_dev *sdev, unsigned int core_mask) +{ + u32 adspcs; + int ret; + + /* set reset bits for cores */ + snd_sof_dsp_update_bits_unlocked(sdev, HDA_DSP_BAR, + HDA_DSP_REG_ADSPCS, + HDA_DSP_ADSPCS_CRST_MASK(core_mask), + HDA_DSP_ADSPCS_CRST_MASK(core_mask)); + + /* poll with timeout to check if operation successful */ + ret = snd_sof_dsp_register_poll(sdev, HDA_DSP_BAR, + HDA_DSP_REG_ADSPCS, + HDA_DSP_ADSPCS_CRST_MASK(core_mask), + HDA_DSP_ADSPCS_CRST_MASK(core_mask), + HDA_DSP_RESET_TIMEOUT); + + /* has core entered reset ? */ + adspcs = snd_sof_dsp_read(sdev, HDA_DSP_BAR, + HDA_DSP_REG_ADSPCS); + if ((adspcs & HDA_DSP_ADSPCS_CRST_MASK(core_mask)) != + HDA_DSP_ADSPCS_CRST_MASK(core_mask)) { + dev_err(sdev->dev, + "error: reset enter failed: core_mask %x adspcs 0x%x\n", + core_mask, adspcs); + ret = -EIO; + } + + return ret; +} + +int hda_dsp_core_reset_leave(struct snd_sof_dev *sdev, unsigned int core_mask) +{ + u32 adspcs; + int ret; + + /* clear reset bits for cores */ + snd_sof_dsp_update_bits_unlocked(sdev, HDA_DSP_BAR, + HDA_DSP_REG_ADSPCS, + HDA_DSP_ADSPCS_CRST_MASK(core_mask), + 0); + + /* poll with timeout to check if operation successful */ + ret = snd_sof_dsp_register_poll(sdev, HDA_DSP_BAR, + HDA_DSP_REG_ADSPCS, + HDA_DSP_ADSPCS_CRST_MASK(core_mask), 0, + HDA_DSP_RESET_TIMEOUT); + + /* has core left reset ? */ + adspcs = snd_sof_dsp_read(sdev, HDA_DSP_BAR, + HDA_DSP_REG_ADSPCS); + if ((adspcs & HDA_DSP_ADSPCS_CRST_MASK(core_mask)) != 0) { + dev_err(sdev->dev, + "error: reset leave failed: core_mask %x adspcs 0x%x\n", + core_mask, adspcs); + ret = -EIO; + } + + return ret; +} + +int hda_dsp_core_stall_reset(struct snd_sof_dev *sdev, unsigned int core_mask) +{ + /* stall core */ + snd_sof_dsp_update_bits_unlocked(sdev, HDA_DSP_HDA_BAR, + HDA_DSP_REG_ADSPCS, + HDA_DSP_ADSPCS_CSTALL_MASK(core_mask), + HDA_DSP_ADSPCS_CSTALL_MASK(core_mask)); + + /* set reset state */ + return hda_dsp_core_reset_enter(sdev, core_mask); +} + +int hda_dsp_core_run(struct snd_sof_dev *sdev, unsigned int core_mask) +{ + int ret; + + /* leave reset state */ + ret = hda_dsp_core_reset_leave(sdev, core_mask); + if (ret < 0) + return ret; + + /* run core */ + dev_dbg(sdev->dev, "unstall/run core: core_mask = %x\n", core_mask); + snd_sof_dsp_update_bits_unlocked(sdev, HDA_DSP_BAR, + HDA_DSP_REG_ADSPCS, + HDA_DSP_ADSPCS_CSTALL_MASK(core_mask), + 0); + + /* is core now running ? */ + if (!hda_dsp_core_is_enabled(sdev, core_mask)) { + hda_dsp_core_stall_reset(sdev, core_mask); + dev_err(sdev->dev, "error: DSP start core failed: core_mask %x\n", + core_mask); + ret = -EIO; + } + + return ret; +} + +/* + * Power Management. + */ + +int hda_dsp_core_power_up(struct snd_sof_dev *sdev, unsigned int core_mask) +{ + u32 adspcs; + int ret; + + /* update bits */ + snd_sof_dsp_update_bits(sdev, HDA_DSP_BAR, HDA_DSP_REG_ADSPCS, + HDA_DSP_ADSPCS_SPA_MASK(core_mask), + HDA_DSP_ADSPCS_SPA_MASK(core_mask)); + + /* poll with timeout to check if operation successful */ + ret = snd_sof_dsp_register_poll(sdev, HDA_DSP_BAR, + HDA_DSP_REG_ADSPCS, + HDA_DSP_ADSPCS_CPA_MASK(core_mask), + HDA_DSP_ADSPCS_CPA_MASK(core_mask), + HDA_DSP_PU_TIMEOUT); + if (ret < 0) + dev_err(sdev->dev, "error: timeout on core powerup\n"); + + /* did core power up ? */ + adspcs = snd_sof_dsp_read(sdev, HDA_DSP_BAR, + HDA_DSP_REG_ADSPCS); + if ((adspcs & HDA_DSP_ADSPCS_CPA_MASK(core_mask)) != + HDA_DSP_ADSPCS_CPA_MASK(core_mask)) { + dev_err(sdev->dev, + "error: power up core failed core_mask %xadspcs 0x%x\n", + core_mask, adspcs); + ret = -EIO; + } + + return ret; +} + +int hda_dsp_core_power_down(struct snd_sof_dev *sdev, unsigned int core_mask) +{ + /* update bits */ + snd_sof_dsp_update_bits_unlocked(sdev, HDA_DSP_BAR, + HDA_DSP_REG_ADSPCS, + HDA_DSP_ADSPCS_SPA_MASK(core_mask), 0); + + /* poll with timeout to check if operation successful */ + return snd_sof_dsp_register_poll(sdev, HDA_DSP_BAR, + HDA_DSP_REG_ADSPCS, HDA_DSP_ADSPCS_CPA_MASK(core_mask), 0, + HDA_DSP_PD_TIMEOUT); +} + +bool hda_dsp_core_is_enabled(struct snd_sof_dev *sdev, + unsigned int core_mask) +{ + int val; + bool is_enable; + + val = snd_sof_dsp_read(sdev, HDA_DSP_BAR, HDA_DSP_REG_ADSPCS); + + is_enable = ((val & HDA_DSP_ADSPCS_CPA_MASK(core_mask)) && + (val & HDA_DSP_ADSPCS_SPA_MASK(core_mask)) && + !(val & HDA_DSP_ADSPCS_CRST_MASK(core_mask)) && + !(val & HDA_DSP_ADSPCS_CSTALL_MASK(core_mask))); + + dev_dbg(sdev->dev, "DSP core(s) enabled? %d : core_mask %x\n", + is_enable, core_mask); + + return is_enable; +} + +int hda_dsp_core_reset_power_down(struct snd_sof_dev *sdev, + unsigned int core_mask) +{ + int ret; + + /* place core in reset prior to power down */ + ret = hda_dsp_core_stall_reset(sdev, core_mask); + if (ret < 0) { + dev_err(sdev->dev, "error: dsp core reset failed: core_mask %x\n", + core_mask); + return ret; + } + + /* power down core */ + ret = hda_dsp_core_power_down(sdev, core_mask); + if (ret < 0) { + dev_err(sdev->dev, "error: dsp core power down fail mask %x: %d\n", + core_mask, ret); + return ret; + } + + /* make sure we are in OFF state */ + if (hda_dsp_core_is_enabled(sdev, core_mask)) { + dev_err(sdev->dev, "error: dsp core disable fail mask %x: %d\n", + core_mask, ret); + ret = -EIO; + } + + return ret; +} + From e10ad951c30386772fcd0cfefa946f46bb9f5437 Mon Sep 17 00:00:00 2001 From: Liam Girdwood Date: Wed, 9 May 2018 17:14:13 +0100 Subject: [PATCH 042/107] ASoC: SOF: Intel: Add Intel specific HDA IPC mechanisms. Add HDA specific IPC mechanism for Intel DSP HW. Signed-off-by: Liam Girdwood --- sound/soc/sof/intel/hda-ipc.c | 367 ++++++++++++++++++++++++++++++++++ 1 file changed, 367 insertions(+) create mode 100644 sound/soc/sof/intel/hda-ipc.c diff --git a/sound/soc/sof/intel/hda-ipc.c b/sound/soc/sof/intel/hda-ipc.c new file mode 100644 index 00000000000000..357d909bfe80d6 --- /dev/null +++ b/sound/soc/sof/intel/hda-ipc.c @@ -0,0 +1,367 @@ +// SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause) +/* + * This file is provided under a dual BSD/GPLv2 license. When using or + * redistributing this file, you may do so under either license. + * + * Copyright(c) 2017 Intel Corporation. All rights reserved. + * + * Authors: Liam Girdwood + * Ranjani Sridharan + * Jeeja KP + * Rander Wang + * Keyon Jie + */ + +/* + * Hardware interface for generic Intel audio DSP HDA IP + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "../sof-priv.h" +#include "../ops.h" +#include "hda.h" + +int hda_dsp_ipc_cmd_done(struct snd_sof_dev *sdev) +{ + /* tell DSP cmd is done - clear busy interrupt */ + snd_sof_dsp_update_bits_forced(sdev, HDA_DSP_BAR, + HDA_DSP_REG_HIPCT, + HDA_DSP_REG_HIPCT_BUSY, + HDA_DSP_REG_HIPCT_BUSY); + return 0; +} + +int hda_dsp_ipc_is_ready(struct snd_sof_dev *sdev) +{ + u64 val; + + /* is DSP ready for next IPC command */ + val = snd_sof_dsp_read(sdev, HDA_DSP_BAR, HDA_DSP_REG_HIPCI); + if (val & HDA_DSP_REG_HIPCI_BUSY) + return 0; + + return 1; +} + +int hda_dsp_ipc_send_msg(struct snd_sof_dev *sdev, struct snd_sof_ipc_msg *msg) +{ + u32 cmd = msg->header; + + /* send IPC message to DSP */ + hda_dsp_mailbox_write(sdev, sdev->host_box.offset, msg->msg_data, + msg->msg_size); + snd_sof_dsp_write(sdev, HDA_DSP_BAR, HDA_DSP_REG_HIPCI, + cmd | HDA_DSP_REG_HIPCI_BUSY); + + return 0; +} + +int hda_dsp_ipc_get_reply(struct snd_sof_dev *sdev, + struct snd_sof_ipc_msg *msg) +{ + struct sof_ipc_reply reply; + int ret = 0; + u32 size; + + /* get IPC reply from DSP in the mailbox */ + hda_dsp_mailbox_read(sdev, sdev->host_box.offset, &reply, + sizeof(reply)); + if (reply.error < 0) { + size = sizeof(reply); + ret = reply.error; + } else { + /* reply correct size ? */ + if (reply.hdr.size != msg->reply_size) { + dev_err(sdev->dev, "error: reply expected 0x%zx got 0x%x bytes\n", + msg->reply_size, reply.hdr.size); + size = msg->reply_size; + ret = -EINVAL; + } else { + size = reply.hdr.size; + } + } + + /* read the message */ + if (msg->msg_data && size > 0) + hda_dsp_mailbox_read(sdev, sdev->host_box.offset, + msg->reply_data, size); + + return ret; +} + +/* IPC handler thread */ +irqreturn_t hda_dsp_ipc_irq_thread(int irq, void *context) +{ + struct snd_sof_dev *sdev = (struct snd_sof_dev *)context; + u32 hipci, hipcie, hipct, hipcte, msg = 0, msg_ext = 0; + irqreturn_t ret = IRQ_NONE; + + /* here we handle IPC interrupts only */ + if (!(sdev->irq_status & HDA_DSP_ADSPIS_IPC)) + return ret; + + /* read IPC status */ + hipcie = snd_sof_dsp_read(sdev, HDA_DSP_BAR, + HDA_DSP_REG_HIPCIE); + hipct = snd_sof_dsp_read(sdev, HDA_DSP_BAR, HDA_DSP_REG_HIPCT); + + /* is this a reply message from the DSP */ + if (hipcie & HDA_DSP_REG_HIPCIE_DONE) { + + hipci = snd_sof_dsp_read(sdev, HDA_DSP_BAR, + HDA_DSP_REG_HIPCI); + msg = hipci & HDA_DSP_REG_HIPCI_MSG_MASK; + msg_ext = hipcie & HDA_DSP_REG_HIPCIE_MSG_MASK; + + dev_vdbg(sdev->dev, + "ipc: firmware response, msg:0x%x, msg_ext:0x%x\n", + msg, msg_ext); + + /* mask Done interrupt */ + snd_sof_dsp_update_bits(sdev, HDA_DSP_BAR, + HDA_DSP_REG_HIPCCTL, + HDA_DSP_REG_HIPCCTL_DONE, 0); + + /* handle immediate reply from DSP core - ignore ROM messages */ + if (msg != 0x1004000) + snd_sof_ipc_reply(sdev, msg); + + /* clear DONE bit - tell DSP we have completed the operation */ + snd_sof_dsp_update_bits_forced(sdev, HDA_DSP_BAR, + HDA_DSP_REG_HIPCIE, + HDA_DSP_REG_HIPCIE_DONE, + HDA_DSP_REG_HIPCIE_DONE); + + /* unmask Done interrupt */ + snd_sof_dsp_update_bits(sdev, HDA_DSP_BAR, + HDA_DSP_REG_HIPCCTL, + HDA_DSP_REG_HIPCCTL_DONE, + HDA_DSP_REG_HIPCCTL_DONE); + + ret = IRQ_HANDLED; + } + + /* is this a new message from DSP */ + if (hipct & HDA_DSP_REG_HIPCT_BUSY) { + + hipcte = snd_sof_dsp_read(sdev, HDA_DSP_BAR, + HDA_DSP_REG_HIPCTE); + msg = hipct & HDA_DSP_REG_HIPCT_MSG_MASK; + msg_ext = hipcte & HDA_DSP_REG_HIPCTE_MSG_MASK; + + dev_vdbg(sdev->dev, + "ipc: firmware initiated, msg:0x%x, msg_ext:0x%x\n", + msg, msg_ext); + + /* handle messages from DSP */ + if ((hipct & SOF_IPC_PANIC_MAGIC_MASK) == SOF_IPC_PANIC_MAGIC) { + /* this is a PANIC message !! */ + snd_sof_dsp_panic(sdev, HDA_DSP_PANIC_OFFSET(msg_ext)); + } else { + /* normal message - process normally*/ + snd_sof_ipc_msgs_rx(sdev); + } + + /* clear busy interrupt */ + snd_sof_dsp_update_bits_forced(sdev, HDA_DSP_BAR, + HDA_DSP_REG_HIPCT, + HDA_DSP_REG_HIPCT_BUSY, + HDA_DSP_REG_HIPCT_BUSY); + ret = IRQ_HANDLED; + } + + if (ret == IRQ_HANDLED) { + /* reenable IPC interrupt */ + snd_sof_dsp_update_bits(sdev, HDA_DSP_BAR, HDA_DSP_REG_ADSPIC, + HDA_DSP_ADSPIC_IPC, HDA_DSP_ADSPIC_IPC); + } + + /* wake up sleeper if we are loading code */ + if (sdev->code_loading) { + sdev->code_loading = 0; + wake_up(&sdev->waitq); + } + + return ret; +} + +/* is this IRQ for ADSP ? - we only care about IPC here */ +irqreturn_t hda_dsp_ipc_irq_handler(int irq, void *context) +{ + struct snd_sof_dev *sdev = (struct snd_sof_dev *)context; + int ret = IRQ_NONE; + + spin_lock(&sdev->hw_lock); + + /* store status */ + sdev->irq_status = snd_sof_dsp_read(sdev, HDA_DSP_BAR, + HDA_DSP_REG_ADSPIS); + + /* invalid message ? */ + if (sdev->irq_status == 0xffffffff) + goto out; + + /* IPC message ? */ + if (sdev->irq_status & HDA_DSP_ADSPIS_IPC) { + /* disable IPC interrupt */ + snd_sof_dsp_update_bits_unlocked(sdev, HDA_DSP_BAR, + HDA_DSP_REG_ADSPIC, + HDA_DSP_ADSPIC_IPC, 0); + ret = IRQ_WAKE_THREAD; + } + +out: + spin_unlock(&sdev->hw_lock); + return ret; +} + +/* + * IPC Firmware ready. + */ + +static void ipc_get_windows(struct snd_sof_dev *sdev) +{ + struct sof_ipc_window_elem *elem; + u32 outbox_offset = 0; + u32 stream_offset = 0; + u32 inbox_offset = 0; + u32 outbox_size = 0; + u32 stream_size = 0; + u32 inbox_size = 0; + int i; + + if (!sdev->info_window) { + dev_err(sdev->dev, "error: have no window info\n"); + return; + } + + for (i = 0; i < sdev->info_window->num_windows; i++) { + elem = &sdev->info_window->window[i]; + + switch (elem->type) { + case SOF_IPC_REGION_UPBOX: + inbox_offset = + elem->offset + SRAM_WINDOW_OFFSET(elem->id); + inbox_size = elem->size; + snd_sof_debugfs_create_item(sdev, + sdev->bar[HDA_DSP_BAR] + + inbox_offset, + elem->size, "inbox"); + break; + case SOF_IPC_REGION_DOWNBOX: + outbox_offset = + elem->offset + SRAM_WINDOW_OFFSET(elem->id); + outbox_size = elem->size; + snd_sof_debugfs_create_item(sdev, + sdev->bar[HDA_DSP_BAR] + + outbox_offset, + elem->size, "outbox"); + break; + case SOF_IPC_REGION_TRACE: + snd_sof_debugfs_create_item(sdev, + sdev->bar[HDA_DSP_BAR] + + elem->offset + + SRAM_WINDOW_OFFSET + (elem->id), + elem->size, "etrace"); + break; + case SOF_IPC_REGION_DEBUG: + snd_sof_debugfs_create_item(sdev, + sdev->bar[HDA_DSP_BAR] + + elem->offset + + SRAM_WINDOW_OFFSET + (elem->id), + elem->size, "debug"); + break; + case SOF_IPC_REGION_STREAM: + stream_offset = + elem->offset + SRAM_WINDOW_OFFSET(elem->id); + stream_size = elem->size; + snd_sof_debugfs_create_item(sdev, + sdev->bar[HDA_DSP_BAR] + + elem->offset + + SRAM_WINDOW_OFFSET + (elem->id), + elem->size, "stream"); + break; + case SOF_IPC_REGION_REGS: + snd_sof_debugfs_create_item(sdev, + sdev->bar[HDA_DSP_BAR] + + elem->offset + + SRAM_WINDOW_OFFSET + (elem->id), + elem->size, "regs"); + break; + case SOF_IPC_REGION_EXCEPTION: + sdev->dsp_oops_offset = elem->offset + + SRAM_WINDOW_OFFSET(elem->id); + snd_sof_debugfs_create_item(sdev, + sdev->bar[HDA_DSP_BAR] + + elem->offset + + SRAM_WINDOW_OFFSET + (elem->id), + elem->size, "exception"); + break; + default: + dev_err(sdev->dev, "error: get illegal window info\n"); + return; + } + } + + if (outbox_size == 0 || inbox_size == 0) { + dev_err(sdev->dev, "error: get illegal mailbox window\n"); + return; + } + + snd_sof_dsp_mailbox_init(sdev, inbox_offset, inbox_size, + outbox_offset, outbox_size); + sdev->stream_box.offset = stream_offset; + sdev->stream_box.size = stream_size; + + dev_dbg(sdev->dev, " mailbox upstream 0x%x - size 0x%x\n", + inbox_offset, inbox_size); + dev_dbg(sdev->dev, " mailbox downstream 0x%x - size 0x%x\n", + outbox_offset, outbox_size); + dev_dbg(sdev->dev, " stream region 0x%x - size 0x%x\n", + stream_offset, stream_size); +} + +int hda_dsp_ipc_fw_ready(struct snd_sof_dev *sdev, u32 msg_id) +{ + struct sof_ipc_fw_ready *fw_ready = &sdev->fw_ready; + struct sof_ipc_fw_version *v = &fw_ready->version; + u32 offset; + + /* mailbox must be on 4k boundary */ + offset = HDA_DSP_MBOX_UPLINK_OFFSET; + + dev_dbg(sdev->dev, "ipc: DSP is ready 0x%8.8x offset 0x%x\n", + msg_id, offset); + + /* copy data from the DSP FW ready offset */ + hda_dsp_block_read(sdev, offset, fw_ready, sizeof(*fw_ready)); + dev_info(sdev->dev, + " Firmware info: version %d.%d-%s build %d on %s:%s\n", + v->major, v->minor, v->tag, v->build, v->date, v->time); + + /* now check for extended data */ + snd_sof_fw_parse_ext_data(sdev, HDA_DSP_MBOX_UPLINK_OFFSET + + sizeof(struct sof_ipc_fw_ready)); + + ipc_get_windows(sdev); + + return 0; +} From 8bb0c744392324d33dba4e72e302915feed5bbe9 Mon Sep 17 00:00:00 2001 From: Liam Girdwood Date: Wed, 9 May 2018 17:14:14 +0100 Subject: [PATCH 043/107] ASoC: SOF: Intel: Add Intel specific HDA firmware loader Add support for loading DSP firmware on Intel HDA based platforms. Signed-off-by: Liam Girdwood --- sound/soc/sof/intel/hda-loader.c | 367 +++++++++++++++++++++++++++++++ 1 file changed, 367 insertions(+) create mode 100644 sound/soc/sof/intel/hda-loader.c diff --git a/sound/soc/sof/intel/hda-loader.c b/sound/soc/sof/intel/hda-loader.c new file mode 100644 index 00000000000000..372ccfaac30647 --- /dev/null +++ b/sound/soc/sof/intel/hda-loader.c @@ -0,0 +1,367 @@ +// SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause) +/* + * This file is provided under a dual BSD/GPLv2 license. When using or + * redistributing this file, you may do so under either license. + * + * Copyright(c) 2017 Intel Corporation. All rights reserved. + * + * Authors: Liam Girdwood + * Ranjani Sridharan + * Jeeja KP + * Rander Wang + * Keyon Jie + */ + +/* + * Hardware interface for HDA DSP code loader + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "../sof-priv.h" +#include "../ops.h" +#include "hda.h" + +static int cl_stream_prepare(struct snd_sof_dev *sdev, unsigned int format, + unsigned int size, struct snd_dma_buffer *dmab, + int direction) +{ + struct sof_intel_hda_stream *stream = NULL; + struct pci_dev *pci = sdev->pci; + int ret; + + if (direction == SNDRV_PCM_STREAM_PLAYBACK) { + stream = hda_dsp_stream_get_pstream(sdev); + } else { + dev_err(sdev->dev, "error: code loading DMA is playback only\n"); + return -EINVAL; + } + + if (!stream) { + dev_err(sdev->dev, "error: no stream available\n"); + return -ENODEV; + } + + /* allocate DMA buffer */ + ret = snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV_SG, &pci->dev, size, dmab); + if (ret < 0) { + dev_err(sdev->dev, "error: memory alloc failed: %x\n", ret); + goto error; + } + + stream->config = format; + stream->bufsize = size; + + ret = hda_dsp_stream_hw_params(sdev, stream, dmab, NULL); + if (ret < 0) { + dev_err(sdev->dev, "error: hdac prepare failed: %x\n", ret); + goto error; + } + + hda_dsp_stream_spib_config(sdev, stream, HDA_DSP_SPIB_ENABLE, size); + + return stream->tag; + +error: + hda_dsp_stream_put_pstream(sdev, stream->tag); + snd_dma_free_pages(dmab); + return ret; +} + +/* + * 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 cl_dsp_init(struct snd_sof_dev *sdev, const void *fwdata, + u32 fwsize) +{ + int tag, ret, i; + u32 hipcie; + const struct sof_intel_dsp_desc *chip = sdev->hda->desc; + + /* prepare DMA for code loader stream */ + tag = cl_stream_prepare(sdev, 0x40, fwsize, &sdev->dmab, + SNDRV_PCM_STREAM_PLAYBACK); + + if (tag <= 0) { + dev_err(sdev->dev, "error: dma prepare for fw loading err: %x\n", + tag); + return tag; + } + + memcpy(sdev->dmab.area, fwdata, fwsize); + + /* step 1: power up corex */ + ret = hda_dsp_core_power_up(sdev, chip->cores_mask); + if (ret < 0) { + dev_err(sdev->dev, "error: dsp core 0/1 power up failed\n"); + goto err; + } + + /* step 2: purge FW request */ + snd_sof_dsp_write(sdev, HDA_DSP_BAR, chip->ipc_req, + chip->ipc_req_mask | (HDA_DSP_IPC_PURGE_FW | + ((tag - 1) << 9))); + + /* step 3: unset core 0 reset state & unstall/run core 0 */ + ret = hda_dsp_core_run(sdev, HDA_DSP_CORE_MASK(0)); + if (ret < 0) { + dev_err(sdev->dev, "error: dsp core start failed %d\n", ret); + ret = -EIO; + goto err; + } + + /* step 4: wait for IPC DONE bit from ROM */ + for (i = HDA_DSP_INIT_TIMEOUT; i > 0; i--) { + hipcie = snd_sof_dsp_read(sdev, HDA_DSP_BAR, + chip->ipc_ack); + + if (hipcie & chip->ipc_ack_mask) { + snd_sof_dsp_update_bits(sdev, HDA_DSP_BAR, + chip->ipc_ack, + chip->ipc_ack_mask, + chip->ipc_ack_mask); + goto step5; + } + mdelay(1); + } + + dev_err(sdev->dev, "error: waiting for HIPCIE done, reg: 0x%x\n", + hipcie); + goto err; + +step5: + /* step 5: power down corex */ + ret = hda_dsp_core_power_down(sdev, + chip->cores_mask & ~(HDA_DSP_CORE_MASK(0))); + if (ret < 0) { + dev_err(sdev->dev, "error: dsp core x power down failed\n"); + goto err; + } + + /* step 6: enable interrupt */ + snd_sof_dsp_update_bits(sdev, HDA_DSP_BAR, HDA_DSP_REG_ADSPIC, + HDA_DSP_ADSPIC_IPC, HDA_DSP_ADSPIC_IPC); + + /* enable IPC DONE interrupt */ + snd_sof_dsp_update_bits(sdev, HDA_DSP_BAR, chip->ipc_ctl, + HDA_DSP_REG_HIPCCTL_DONE, + HDA_DSP_REG_HIPCCTL_DONE); + + /* enable IPC BUSY interrupt */ + snd_sof_dsp_update_bits(sdev, HDA_DSP_BAR, chip->ipc_ctl, + HDA_DSP_REG_HIPCCTL_BUSY, + HDA_DSP_REG_HIPCCTL_BUSY); + + /* step 7: wait for ROM init */ + ret = snd_sof_dsp_register_poll(sdev, HDA_DSP_BAR, + HDA_DSP_SRAM_REG_ROM_STATUS, + HDA_DSP_ROM_STS_MASK, HDA_DSP_ROM_INIT, + HDA_DSP_INIT_TIMEOUT); + if (ret >= 0) + goto out; + + ret = -EIO; + +err: + hda_dsp_dump(sdev, SOF_DBG_REGS | SOF_DBG_PCI | SOF_DBG_MBOX); + //sdev->dsp_ops.cleanup(sdev->dev, &sdev->dmab, tag); + hda_dsp_core_reset_power_down(sdev, HDA_DSP_CORE_MASK(0) | + HDA_DSP_CORE_MASK(1)); + return ret; + +out: + return tag; +} + +static int cl_trigger(struct snd_sof_dev *sdev, + struct sof_intel_hda_stream *stream, int cmd) +{ + /* code loader is special case that reuses stream ops */ + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + wait_event_timeout(sdev->waitq, !sdev->code_loading, + HDA_DSP_CL_TRIGGER_TIMEOUT); + + snd_sof_dsp_update_bits(sdev, HDA_DSP_HDA_BAR, SOF_HDA_INTCTL, + 1 << stream->index, 1 << stream->index); + + snd_sof_dsp_update_bits(sdev, HDA_DSP_HDA_BAR, + stream->sd_offset, + SOF_HDA_SD_CTL_DMA_START | + SOF_HDA_CL_DMA_SD_INT_MASK, + SOF_HDA_SD_CTL_DMA_START | + SOF_HDA_CL_DMA_SD_INT_MASK); + + stream->running = true; + return 0; + default: + return hda_dsp_stream_trigger(sdev, stream, cmd); + } +} + +static int cl_cleanup(struct snd_sof_dev *sdev, struct snd_dma_buffer *dmab, + struct sof_intel_hda_stream *stream) +{ + int ret; + + ret = hda_dsp_stream_spib_config(sdev, stream, HDA_DSP_SPIB_DISABLE, 0); + + /* TODO: spin lock ?*/ + stream->open = 0; + stream->running = 0; + stream->substream = NULL; + + /* reset BDL address */ + snd_sof_dsp_write(sdev, HDA_DSP_HDA_BAR, + stream->sd_offset + SOF_HDA_ADSP_REG_CL_SD_BDLPL, 0); + snd_sof_dsp_write(sdev, HDA_DSP_HDA_BAR, + stream->sd_offset + SOF_HDA_ADSP_REG_CL_SD_BDLPU, 0); + + snd_sof_dsp_write(sdev, HDA_DSP_HDA_BAR, stream->sd_offset, 0); + snd_dma_free_pages(dmab); + dmab->area = NULL; + stream->bufsize = 0; + stream->config = 0; + + return ret; +} + +static int cl_copy_fw(struct snd_sof_dev *sdev, int tag) +{ + struct sof_intel_hda_stream *stream = NULL; + struct sof_intel_hda_dev *hdev = sdev->hda; + int ret, status, i; + + /* get stream with tag */ + for (i = 0; i < hdev->num_playback; i++) { + if (hdev->pstream[i].tag == tag) { + stream = &hdev->pstream[i]; + break; + } + } + if (!stream) { + dev_err(sdev->dev, + "error: could not get stream with stream tag%d\n", + tag); + return -ENODEV; + } + + ret = cl_trigger(sdev, stream, SNDRV_PCM_TRIGGER_START); + if (ret < 0) { + dev_err(sdev->dev, "error: DMA trigger start failed\n"); + return ret; + } + + status = snd_sof_dsp_register_poll(sdev, HDA_DSP_BAR, + HDA_DSP_SRAM_REG_ROM_STATUS, + HDA_DSP_ROM_STS_MASK, + HDA_DSP_ROM_FW_ENTERED, + HDA_DSP_BASEFW_TIMEOUT); + + ret = cl_trigger(sdev, stream, SNDRV_PCM_TRIGGER_STOP); + if (ret < 0) { + dev_err(sdev->dev, "error: DMA trigger stop failed\n"); + return ret; + } + + ret = cl_cleanup(sdev, &sdev->dmab, stream); + if (ret < 0) { + dev_err(sdev->dev, "error: Code loader DSP cleanup failed\n"); + return ret; + } + + return status; +} + +int hda_dsp_cl_load_fw(struct snd_sof_dev *sdev, const struct firmware *fw) +{ + struct snd_sof_pdata *plat_data = dev_get_platdata(sdev->dev); + int ret; + + /* set code loading condition to true */ + sdev->code_loading = 1; + + ret = request_firmware(&plat_data->fw, + plat_data->machine->sof_fw_filename, sdev->dev); + + if (ret < 0) { + dev_err(sdev->dev, "error: request firmware failed err: %d\n", + ret); + return -EINVAL; + } + + if (!plat_data->fw) + return -EINVAL; + + return ret; +} + +int hda_dsp_cl_boot_firmware(struct snd_sof_dev *sdev) +{ + struct snd_sof_pdata *plat_data = dev_get_platdata(sdev->dev); + struct firmware stripped_firmware; + int ret, tag; + + stripped_firmware.data = plat_data->fw->data; + stripped_firmware.size = plat_data->fw->size; + + tag = cl_dsp_init(sdev, stripped_firmware.data, + stripped_firmware.size); + + /* retry enabling core and ROM load. seemed to help */ + if (tag < 0) { + tag = cl_dsp_init(sdev, stripped_firmware.data, + stripped_firmware.size); + if (tag <= 0) { + dev_err(sdev->dev, "Error code=0x%x: FW status=0x%x\n", + snd_sof_dsp_read(sdev, HDA_DSP_BAR, + HDA_DSP_SRAM_REG_ROM_ERROR), + snd_sof_dsp_read(sdev, HDA_DSP_BAR, + HDA_DSP_SRAM_REG_ROM_STATUS)); + dev_err(sdev->dev, "Core En/ROM load fail:%d\n", + tag); + ret = tag; + goto irq_err; + } + } + + /* init for booting wait */ + init_waitqueue_head(&sdev->boot_wait); + sdev->boot_complete = false; + + /* at this point DSP ROM has been initialized and should be ready for + * code loading and firmware boot + */ + ret = cl_copy_fw(sdev, tag); + if (ret < 0) { + dev_err(sdev->dev, "error: load fw failed err: %d\n", ret); + goto irq_err; + } + + dev_dbg(sdev->dev, "Firmware download successful, booting...\n"); + + return ret; + +irq_err: + hda_dsp_dump(sdev, SOF_DBG_REGS | SOF_DBG_PCI | SOF_DBG_MBOX); + + /* disable DSP */ + snd_sof_dsp_update_bits(sdev, HDA_DSP_PP_BAR, SOF_HDA_REG_PP_PPCTL, + SOF_HDA_PPCTL_GPROCEN, 0); + dev_err(sdev->dev, "error: load fw failed err: %d\n", ret); + return ret; +} From 36319f423eb2287b02b1b1f12848908e67e750d3 Mon Sep 17 00:00:00 2001 From: Liam Girdwood Date: Wed, 9 May 2018 17:14:15 +0100 Subject: [PATCH 044/107] ASoC: SOF: Intel: Add Intel specific HDA PCM operations Add PCM operations for Intel HDA based DSPs. Signed-off-by: Liam Girdwood --- sound/soc/sof/intel/hda-pcm.c | 177 ++++++++++++++++++++++++++++++++++ 1 file changed, 177 insertions(+) create mode 100644 sound/soc/sof/intel/hda-pcm.c diff --git a/sound/soc/sof/intel/hda-pcm.c b/sound/soc/sof/intel/hda-pcm.c new file mode 100644 index 00000000000000..0abb2e5778cae2 --- /dev/null +++ b/sound/soc/sof/intel/hda-pcm.c @@ -0,0 +1,177 @@ +// SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause) +/* + * This file is provided under a dual BSD/GPLv2 license. When using or + * redistributing this file, you may do so under either license. + * + * Copyright(c) 2017 Intel Corporation. All rights reserved. + * + * Authors: Liam Girdwood + * Ranjani Sridharan + * Jeeja KP + * Rander Wang + * Keyon Jie + */ + +/* + * Hardware interface for generic Intel audio DSP HDA IP + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "../sof-priv.h" +#include "../ops.h" +#include "hda.h" + +#define SDnFMT_BASE(x) (x << 14) +#define SDnFMT_MULT(x) ((x - 1) << 11) +#define SDnFMT_DIV(x) ((x - 1) << 8) +#define SDnFMT_BITS(x) (x << 4) +#define SDnFMT_CHAN(x) (x << 0) + +static inline u32 get_mult_div(struct snd_sof_dev *sdev, int rate) +{ + switch (rate) { + case 8000: + return SDnFMT_DIV(6); + case 9600: + return SDnFMT_DIV(5); + case 11025: + return SDnFMT_BASE(1) | SDnFMT_DIV(4); + case 16000: + return SDnFMT_DIV(3); + case 22050: + return SDnFMT_BASE(1) | SDnFMT_DIV(2); + case 32000: + return SDnFMT_DIV(3) | SDnFMT_MULT(2); + case 44100: + return SDnFMT_BASE(1); + case 48000: + return 0; + case 88200: + return SDnFMT_BASE(1) | SDnFMT_MULT(2); + case 96000: + return SDnFMT_MULT(2); + case 176400: + return SDnFMT_BASE(1) | SDnFMT_MULT(4); + case 192000: + return SDnFMT_MULT(4); + default: + dev_warn(sdev->dev, "can't find div rate %d using 48kHz\n", + rate); + return 0; /* use 48KHz if not found */ + } +}; + +static inline u32 get_bits(struct snd_sof_dev *sdev, int sample_bits) +{ + switch (sample_bits) { + case 8: + return SDnFMT_BITS(0); + case 16: + return SDnFMT_BITS(1); + case 20: + return SDnFMT_BITS(2); + case 24: + return SDnFMT_BITS(3); + case 32: + return SDnFMT_BITS(4); + default: + dev_warn(sdev->dev, "can't find %d bits using 16bit\n", + sample_bits); + return SDnFMT_BITS(1); /* use 16bits format if not found */ + } +}; + +int hda_dsp_pcm_hw_params(struct snd_sof_dev *sdev, + struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params) +{ + struct sof_intel_hda_stream *stream = substream->runtime->private_data; + struct snd_dma_buffer *dmab; + int ret; + u32 size, rate, bits; + + size = params_buffer_bytes(params); + rate = get_mult_div(sdev, params_rate(params)); + bits = get_bits(sdev, params_width(params)); + + stream->substream = substream; + + dmab = substream->runtime->dma_buffer_p; + + stream->config = rate | bits | (params_channels(params) - 1); + stream->bufsize = size; + + ret = hda_dsp_stream_hw_params(sdev, stream, dmab, params); + if (ret < 0) { + dev_err(sdev->dev, "error: hdac prepare failed: %x\n", ret); + return ret; + } + + /* disable SPIB, to enable buffer wrap for stream */ + hda_dsp_stream_spib_config(sdev, stream, HDA_DSP_SPIB_DISABLE, 0); + + return stream->tag; +} + +int hda_dsp_pcm_trigger(struct snd_sof_dev *sdev, + struct snd_pcm_substream *substream, int cmd) +{ + struct sof_intel_hda_stream *stream = substream->runtime->private_data; + + return hda_dsp_stream_trigger(sdev, stream, cmd); +} + +int hda_dsp_pcm_open(struct snd_sof_dev *sdev, + struct snd_pcm_substream *substream) +{ + struct sof_intel_hda_stream *stream; + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + stream = hda_dsp_stream_get_pstream(sdev); + else + stream = hda_dsp_stream_get_cstream(sdev); + + if (!stream) { + dev_err(sdev->dev, "error: no stream available\n"); + return -ENODEV; + } + + /* binding pcm substream to hda stream */ + substream->runtime->private_data = stream; + return 0; +} + +int hda_dsp_pcm_close(struct snd_sof_dev *sdev, + struct snd_pcm_substream *substream) +{ + struct sof_intel_hda_stream *stream = substream->runtime->private_data; + int ret; + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + ret = hda_dsp_stream_put_pstream(sdev, stream->tag); + else + ret = hda_dsp_stream_put_cstream(sdev, stream->tag); + + if (ret) { + dev_dbg(sdev->dev, "stream %s not opened!\n", substream->name); + return -ENODEV; + } + + /* unbinding pcm substream to hda stream */ + substream->runtime->private_data = NULL; + return 0; +} + From c1946110ae976f6bdcfb056f750f2478f187c9de Mon Sep 17 00:00:00 2001 From: Liam Girdwood Date: Wed, 9 May 2018 17:14:16 +0100 Subject: [PATCH 045/107] ASoC: SOF: Intel: Add Intel specific HDA stream operations Add support or HDA DSP stream operations for Intel HDA DSPs. Signed-off-by: Liam Girdwood --- sound/soc/sof/intel/hda-stream.c | 656 +++++++++++++++++++++++++++++++ 1 file changed, 656 insertions(+) create mode 100644 sound/soc/sof/intel/hda-stream.c diff --git a/sound/soc/sof/intel/hda-stream.c b/sound/soc/sof/intel/hda-stream.c new file mode 100644 index 00000000000000..a9e81a505aa6c6 --- /dev/null +++ b/sound/soc/sof/intel/hda-stream.c @@ -0,0 +1,656 @@ +// SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause) +/* + * This file is provided under a dual BSD/GPLv2 license. When using or + * redistributing this file, you may do so under either license. + * + * Copyright(c) 2017 Intel Corporation. All rights reserved. + * + * Authors: Liam Girdwood + * Ranjani Sridharan + * Jeeja KP + * Rander Wang + * Keyon Jie + */ + +/* + * Hardware interface for generic Intel audio DSP HDA IP + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "../sof-priv.h" +#include "../ops.h" +#include "hda.h" + +/* + * set up Buffer Descriptor List (BDL) for host memory transfer + * BDL describes the location of the individual buffers and is little endian. + */ +int hda_dsp_stream_setup_bdl(struct snd_sof_dev *sdev, + struct snd_dma_buffer *dmab, + struct sof_intel_hda_stream *stream, + struct sof_intel_dsp_bdl *bdl, int size, + struct snd_pcm_hw_params *params) +{ + int offset = 0; + int chunk = PAGE_SIZE, entry_size; + dma_addr_t addr; + + if (stream->substream && params) { + chunk = params_period_bytes(params); + dev_dbg(sdev->dev, "period_bytes:0x%x\n", chunk); + } + + while (size > 0) { + if (stream->frags >= HDA_DSP_MAX_BDL_ENTRIES) { + dev_err(sdev->dev, "error: stream frags exceeded\n"); + return -EINVAL; + } + + addr = snd_sgbuf_get_addr(dmab, offset); + + /* program BDL addr */ + bdl->addr_l = lower_32_bits(addr); + bdl->addr_h = upper_32_bits(addr); + + entry_size = size > chunk ? chunk : size; + + /* program BDL size */ + bdl->size = snd_sgbuf_get_chunk_size(dmab, offset, entry_size); + + /* program the IOC to enable interrupt + * when the whole fragment is processed + */ + size -= entry_size; + if (size) + bdl->ioc = 0; + else + bdl->ioc = 1; + + stream->frags++; + offset += bdl->size; + + dev_vdbg(sdev->dev, "bdl, frags:%d, entry size:0x%x;\n", + stream->frags, entry_size); + + bdl++; + } + + return offset; +} + +int hda_dsp_stream_spib_config(struct snd_sof_dev *sdev, + struct sof_intel_hda_stream *stream, + int enable, u32 size) +{ + u32 mask = 0; + + if (!sdev->bar[HDA_DSP_SPIB_BAR]) { + dev_err(sdev->dev, "error: address of spib capability is NULL\n"); + return -EINVAL; + } + + mask |= (1 << stream->index); + + /* enable/disable SPIB for the stream */ + snd_sof_dsp_update_bits(sdev, HDA_DSP_SPIB_BAR, + SOF_HDA_ADSP_REG_CL_SPBFIFO_SPBFCCTL, mask, + enable << stream->index); + + /* set the SPIB value */ + hda_dsp_write(sdev, stream->spib_addr, size); + + return 0; +} + +/* get next unused playback stream */ +struct sof_intel_hda_stream * +hda_dsp_stream_get_pstream(struct snd_sof_dev *sdev) +{ + struct sof_intel_hda_dev *hdev = sdev->hda; + struct sof_intel_hda_stream *stream = NULL; + int i; + + /* get an unused playback stream */ + for (i = 0; i < hdev->num_playback; i++) { + if (!hdev->pstream[i].open) { + hdev->pstream[i].open = true; + stream = &hdev->pstream[i]; + break; + } + } + + /* stream found ? */ + if (!stream) + dev_err(sdev->dev, "error: no free playback streams\n"); + + return stream; +} + +/* get next unused capture stream */ +struct sof_intel_hda_stream * +hda_dsp_stream_get_cstream(struct snd_sof_dev *sdev) +{ + struct sof_intel_hda_dev *hdev = sdev->hda; + struct sof_intel_hda_stream *stream = NULL; + int i; + + /* get an unused capture stream */ + for (i = 0; i < hdev->num_capture; i++) { + if (!hdev->cstream[i].open) { + hdev->cstream[i].open = true; + stream = &hdev->cstream[i]; + break; + } + } + + /* stream found ? */ + if (!stream) + dev_err(sdev->dev, "error: no free capture streams\n"); + + return stream; +} + +/* free playback stream */ +int hda_dsp_stream_put_pstream(struct snd_sof_dev *sdev, int tag) +{ + struct sof_intel_hda_dev *hdev = sdev->hda; + int i; + + /* find used playback stream */ + for (i = 0; i < hdev->num_playback; i++) { + if (hdev->pstream[i].open && + hdev->pstream[i].tag == tag) { + hdev->pstream[i].open = false; + return 0; + } + } + + dev_dbg(sdev->dev, "tag %d not opened!\n", tag); + return -ENODEV; +} + +/* free capture stream */ +int hda_dsp_stream_put_cstream(struct snd_sof_dev *sdev, int tag) +{ + struct sof_intel_hda_dev *hdev = sdev->hda; + int i; + + /* find used capture stream */ + for (i = 0; i < hdev->num_capture; i++) { + if (hdev->cstream[i].open && + hdev->cstream[i].tag == tag) { + hdev->cstream[i].open = false; + return 0; + } + } + + dev_dbg(sdev->dev, "tag %d not opened!\n", tag); + return -ENODEV; +} + +int hda_dsp_stream_trigger(struct snd_sof_dev *sdev, + struct sof_intel_hda_stream *stream, int cmd) +{ + /* cmd must be for audio stream */ + switch (cmd) { + case SNDRV_PCM_TRIGGER_RESUME: + case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: + case SNDRV_PCM_TRIGGER_START: + snd_sof_dsp_update_bits(sdev, HDA_DSP_HDA_BAR, SOF_HDA_INTCTL, + 1 << stream->index, + 1 << stream->index); + + snd_sof_dsp_update_bits(sdev, HDA_DSP_HDA_BAR, + stream->sd_offset, + SOF_HDA_SD_CTL_DMA_START | + SOF_HDA_CL_DMA_SD_INT_MASK, + SOF_HDA_SD_CTL_DMA_START | + SOF_HDA_CL_DMA_SD_INT_MASK); + + stream->running = true; + break; + case SNDRV_PCM_TRIGGER_SUSPEND: + case SNDRV_PCM_TRIGGER_PAUSE_PUSH: + case SNDRV_PCM_TRIGGER_STOP: + snd_sof_dsp_update_bits(sdev, HDA_DSP_HDA_BAR, + stream->sd_offset, + SOF_HDA_SD_CTL_DMA_START | + SOF_HDA_CL_DMA_SD_INT_MASK, 0x0); + + snd_sof_dsp_write(sdev, HDA_DSP_HDA_BAR, stream->sd_offset + + SOF_HDA_ADSP_REG_CL_SD_STS, + SOF_HDA_CL_DMA_SD_INT_MASK); + + stream->running = false; + snd_sof_dsp_update_bits(sdev, HDA_DSP_HDA_BAR, SOF_HDA_INTCTL, + 1 << stream->index, 0x0); + break; + default: + dev_err(sdev->dev, "error: unknown command: %d\n", cmd); + return -EINVAL; + } + + return 0; +} + +/* + * prepare for common hdac registers settings, for both code loader + * and normal stream. + */ +int hda_dsp_stream_hw_params(struct snd_sof_dev *sdev, + struct sof_intel_hda_stream *stream, + struct snd_dma_buffer *dmab, + struct snd_pcm_hw_params *params) +{ + struct sof_intel_hda_dev *hdev = sdev->hda; + struct sof_intel_dsp_bdl *bdl; + int ret, timeout = HDA_DSP_STREAM_RESET_TIMEOUT; + u32 val, mask; + + if (!stream) { + dev_err(sdev->dev, "error: no stream available\n"); + return -ENODEV; + } + + /* decouple host and link DMA */ + mask = 0x1 << stream->index; + snd_sof_dsp_update_bits(sdev, HDA_DSP_PP_BAR, SOF_HDA_REG_PP_PPCTL, + mask, mask); + + if (!dmab) { + dev_err(sdev->dev, "error: no dma buffer allocated!\n"); + return -ENODEV; + } + + /* clear stream status */ + snd_sof_dsp_update_bits(sdev, HDA_DSP_HDA_BAR, stream->sd_offset, + SOF_HDA_CL_DMA_SD_INT_MASK | + SOF_HDA_SD_CTL_DMA_START, 0); + snd_sof_dsp_update_bits(sdev, HDA_DSP_HDA_BAR, + stream->sd_offset + SOF_HDA_ADSP_REG_CL_SD_STS, + SOF_HDA_CL_DMA_SD_INT_MASK, + SOF_HDA_CL_DMA_SD_INT_MASK); + + /* stream reset */ + snd_sof_dsp_update_bits(sdev, HDA_DSP_HDA_BAR, stream->sd_offset, 0x1, + 0x1); + udelay(3); + do { + val = snd_sof_dsp_read(sdev, HDA_DSP_HDA_BAR, + stream->sd_offset); + if (val & 0x1) + break; + } while (--timeout); + if (timeout == 0) { + dev_err(sdev->dev, "error: stream reset failed\n"); + return -ETIMEDOUT; + } + + timeout = HDA_DSP_STREAM_RESET_TIMEOUT; + snd_sof_dsp_update_bits(sdev, HDA_DSP_HDA_BAR, stream->sd_offset, 0x1, + 0x0); + + /* wait for hardware to report that stream is out of reset */ + udelay(3); + do { + val = snd_sof_dsp_read(sdev, HDA_DSP_HDA_BAR, + stream->sd_offset); + if ((val & 0x1) == 0) + break; + } while (--timeout); + if (timeout == 0) { + dev_err(sdev->dev, "error: timeout waiting for stream reset\n"); + return -ETIMEDOUT; + } + + if (stream->posbuf) + *stream->posbuf = 0; + + /* reset BDL address */ + snd_sof_dsp_write(sdev, HDA_DSP_HDA_BAR, + stream->sd_offset + SOF_HDA_ADSP_REG_CL_SD_BDLPL, + 0x0); + snd_sof_dsp_write(sdev, HDA_DSP_HDA_BAR, + stream->sd_offset + SOF_HDA_ADSP_REG_CL_SD_BDLPU, + 0x0); + + /* clear stream status */ + snd_sof_dsp_update_bits(sdev, HDA_DSP_HDA_BAR, stream->sd_offset, + SOF_HDA_CL_DMA_SD_INT_MASK | + SOF_HDA_SD_CTL_DMA_START, 0); + snd_sof_dsp_update_bits(sdev, HDA_DSP_HDA_BAR, + stream->sd_offset + SOF_HDA_ADSP_REG_CL_SD_STS, + SOF_HDA_CL_DMA_SD_INT_MASK, + SOF_HDA_CL_DMA_SD_INT_MASK); + + stream->frags = 0; + + bdl = (struct sof_intel_dsp_bdl *)stream->bdl.area; + ret = hda_dsp_stream_setup_bdl(sdev, dmab, stream, bdl, + stream->bufsize, params); + if (ret < 0) { + dev_err(sdev->dev, "error: set up of BDL failed\n"); + return ret; + } + + /* set up stream descriptor for DMA */ + /* program stream tag */ + snd_sof_dsp_update_bits(sdev, HDA_DSP_HDA_BAR, stream->sd_offset, + SOF_HDA_CL_SD_CTL_STREAM_TAG_MASK, + stream->tag << + SOF_HDA_CL_SD_CTL_STREAM_TAG_SHIFT); + + /* program cyclic buffer length */ + snd_sof_dsp_write(sdev, HDA_DSP_HDA_BAR, + stream->sd_offset + SOF_HDA_ADSP_REG_CL_SD_CBL, + stream->bufsize); + + /* program stream format */ + snd_sof_dsp_update_bits(sdev, HDA_DSP_HDA_BAR, + stream->sd_offset + + SOF_HDA_ADSP_REG_CL_SD_FORMAT, + 0xffff, stream->config); + + /* program last valid index */ + snd_sof_dsp_update_bits(sdev, HDA_DSP_HDA_BAR, + stream->sd_offset + SOF_HDA_ADSP_REG_CL_SD_LVI, + 0xffff, (stream->frags - 1)); + + /* program BDL address */ + snd_sof_dsp_write(sdev, HDA_DSP_HDA_BAR, + stream->sd_offset + SOF_HDA_ADSP_REG_CL_SD_BDLPL, + (u32)stream->bdl.addr); + snd_sof_dsp_write(sdev, HDA_DSP_HDA_BAR, + stream->sd_offset + SOF_HDA_ADSP_REG_CL_SD_BDLPU, + upper_32_bits(stream->bdl.addr)); + + /* enable position buffer */ + if (!(snd_sof_dsp_read(sdev, HDA_DSP_HDA_BAR, SOF_HDA_ADSP_DPLBASE) + & SOF_HDA_ADSP_DPLBASE_ENABLE)) + snd_sof_dsp_write(sdev, HDA_DSP_HDA_BAR, SOF_HDA_ADSP_DPLBASE, + (u32)hdev->posbuffer.addr | + SOF_HDA_ADSP_DPLBASE_ENABLE); + + /* set interrupt enable bits */ + snd_sof_dsp_update_bits(sdev, HDA_DSP_HDA_BAR, stream->sd_offset, + SOF_HDA_CL_DMA_SD_INT_MASK, + SOF_HDA_CL_DMA_SD_INT_MASK); + + /* read FIFO size */ + if (stream->direction == SNDRV_PCM_STREAM_PLAYBACK) { + stream->fifo_size = + snd_sof_dsp_read(sdev, HDA_DSP_HDA_BAR, + stream->sd_offset + + SOF_HDA_ADSP_REG_CL_SD_FIFOSIZE); + stream->fifo_size &= 0xffff; + stream->fifo_size += 1; + } else { + stream->fifo_size = 0; + } + + return ret; +} + +irqreturn_t hda_dsp_stream_interrupt(int irq, void *context) +{ + struct snd_sof_dev *sdev = (struct snd_sof_dev *)context; + u32 status; + + if (!pm_runtime_active(sdev->dev)) + return IRQ_NONE; + + status = snd_sof_dsp_read(sdev, HDA_DSP_HDA_BAR, SOF_HDA_INTSTS); + + if (status == 0 || status == 0xffffffff) + return IRQ_NONE; + + return status ? IRQ_WAKE_THREAD : IRQ_HANDLED; +} + +irqreturn_t hda_dsp_stream_threaded_handler(int irq, void *context) +{ + struct snd_sof_dev *sdev = (struct snd_sof_dev *)context; + struct sof_intel_hda_dev *hdev = sdev->hda; + u32 status = snd_sof_dsp_read(sdev, HDA_DSP_HDA_BAR, SOF_HDA_INTSTS); + u32 sd_status; + int i; + + /* check playback streams */ + for (i = 0; i < hdev->num_playback; i++) { + /* is IRQ for this stream ? */ + if (status & (1 << hdev->pstream[i].index)) { + sd_status = + snd_sof_dsp_read(sdev, HDA_DSP_HDA_BAR, + hdev->pstream[i].sd_offset + + SOF_HDA_ADSP_REG_CL_SD_STS) & + 0xff; + + dev_dbg(sdev->dev, "pstream %d status 0x%x\n", + i, sd_status); + + snd_sof_dsp_update_bits(sdev, HDA_DSP_HDA_BAR, + hdev->pstream[i].sd_offset + + SOF_HDA_ADSP_REG_CL_SD_STS, + SOF_HDA_CL_DMA_SD_INT_MASK, + SOF_HDA_CL_DMA_SD_INT_MASK); + + if (!hdev->pstream[i].substream || + !hdev->pstream[i].running || + (sd_status & SOF_HDA_CL_DMA_SD_INT_MASK) == 0) + continue; + } + } + + /* check capture streams */ + for (i = 0; i < hdev->num_capture; i++) { + /* is IRQ for this stream ? */ + if (status & (1 << hdev->cstream[i].index)) { + sd_status = + snd_sof_dsp_read(sdev, HDA_DSP_HDA_BAR, + hdev->cstream[i].sd_offset + + SOF_HDA_ADSP_REG_CL_SD_STS) & + 0xff; + + dev_dbg(sdev->dev, "cstream %d status 0x%x\n", + i, sd_status); + + snd_sof_dsp_update_bits(sdev, HDA_DSP_HDA_BAR, + hdev->cstream[i].sd_offset + + SOF_HDA_ADSP_REG_CL_SD_STS, + SOF_HDA_CL_DMA_SD_INT_MASK, + SOF_HDA_CL_DMA_SD_INT_MASK); + + if (!hdev->cstream[i].substream || + !hdev->cstream[i].running || + (sd_status & SOF_HDA_CL_DMA_SD_INT_MASK) == 0) + continue; + } + } + + return IRQ_HANDLED; +} + +int hda_dsp_stream_init(struct snd_sof_dev *sdev) +{ + struct sof_intel_hda_dev *hdev = sdev->hda; + struct sof_intel_hda_stream *stream; + struct pci_dev *pci = sdev->pci; + int i, num_playback, num_capture, num_total, ret; + u32 gcap; + + gcap = snd_sof_dsp_read(sdev, HDA_DSP_HDA_BAR, SOF_HDA_GCAP); + dev_dbg(sdev->dev, "hda global caps = 0x%x\n", gcap); + + /* get stream count from GCAP */ + num_capture = (gcap >> 8) & 0x0f; + num_playback = (gcap >> 12) & 0x0f; + num_total = num_playback + num_capture; + + hdev->num_capture = num_capture; + hdev->num_playback = num_playback; + + dev_dbg(sdev->dev, "detected %d playback and %d capture streams\n", + num_playback, num_capture); + + if (num_playback >= SOF_HDA_PLAYBACK_STREAMS) { + dev_err(sdev->dev, "error: too many playback streams %d\n", + num_playback); + return -EINVAL; + } + + if (num_capture >= SOF_HDA_CAPTURE_STREAMS) { + dev_err(sdev->dev, "error: too many capture streams %d\n", + num_playback); + return -EINVAL; + } + + /* mem alloc for the position buffer */ + ret = snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, &pci->dev, 8, + &hdev->posbuffer); + if (ret < 0) { + dev_err(sdev->dev, "error: posbuffer dma alloc failed\n"); + return -ENOMEM; + } + + /* create capture streams */ + for (i = 0; i < num_capture; i++) { + stream = &hdev->cstream[i]; + + stream->pphc_addr = sdev->bar[HDA_DSP_PP_BAR] + + SOF_HDA_PPHC_BASE + SOF_HDA_PPHC_INTERVAL * i; + + stream->pplc_addr = sdev->bar[HDA_DSP_PP_BAR] + + SOF_HDA_PPLC_BASE + SOF_HDA_PPLC_MULTI * num_total + + SOF_HDA_PPLC_INTERVAL * i; + + /* do we support SPIB */ + if (sdev->bar[HDA_DSP_SPIB_BAR]) { + stream->spib_addr = sdev->bar[HDA_DSP_SPIB_BAR] + + SOF_HDA_SPIB_BASE + SOF_HDA_SPIB_INTERVAL * i + + SOF_HDA_SPIB_SPIB; + + stream->fifo_addr = sdev->bar[HDA_DSP_SPIB_BAR] + + SOF_HDA_SPIB_BASE + SOF_HDA_SPIB_INTERVAL * i + + SOF_HDA_SPIB_MAXFIFO; + } + + /* do we support DRSM */ + if (sdev->bar[HDA_DSP_DRSM_BAR]) + stream->drsm_addr = sdev->bar[HDA_DSP_DRSM_BAR] + + SOF_HDA_DRSM_BASE + SOF_HDA_DRSM_INTERVAL * i; + + stream->sd_offset = 0x20 * i + SOF_HDA_ADSP_LOADER_BASE; + stream->sd_addr = sdev->bar[HDA_DSP_HDA_BAR] + + stream->sd_offset; + + stream->tag = i + 1; + stream->open = false; + stream->running = false; + stream->direction = SNDRV_PCM_STREAM_CAPTURE; + stream->index = i; + + /* memory alloc for stream BDL */ + ret = snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, &pci->dev, + HDA_DSP_BDL_SIZE, &stream->bdl); + if (ret < 0) { + dev_err(sdev->dev, "error: stream bdl dma alloc failed\n"); + return -ENOMEM; + } + stream->posbuf = (__le32 *)(hdev->posbuffer.area + + (stream->index) * 8); + } + + /* create playback streams */ + for (i = num_capture; i < num_total; i++) { + stream = &hdev->pstream[i - num_capture]; + + /* we always have DSP support */ + stream->pphc_addr = sdev->bar[HDA_DSP_PP_BAR] + + SOF_HDA_PPHC_BASE + SOF_HDA_PPHC_INTERVAL * i; + + stream->pplc_addr = sdev->bar[HDA_DSP_PP_BAR] + + SOF_HDA_PPLC_BASE + SOF_HDA_PPLC_MULTI * num_total + + SOF_HDA_PPLC_INTERVAL * i; + + /* do we support SPIB */ + if (sdev->bar[HDA_DSP_SPIB_BAR]) { + stream->spib_addr = sdev->bar[HDA_DSP_SPIB_BAR] + + SOF_HDA_SPIB_BASE + SOF_HDA_SPIB_INTERVAL * i + + SOF_HDA_SPIB_SPIB; + + stream->fifo_addr = sdev->bar[HDA_DSP_SPIB_BAR] + + SOF_HDA_SPIB_BASE + SOF_HDA_SPIB_INTERVAL * i + + SOF_HDA_SPIB_MAXFIFO; + } + + /* do we support DRSM */ + if (sdev->bar[HDA_DSP_DRSM_BAR]) + stream->drsm_addr = sdev->bar[HDA_DSP_DRSM_BAR] + + SOF_HDA_DRSM_BASE + SOF_HDA_DRSM_INTERVAL * i; + + stream->sd_offset = 0x20 * i + SOF_HDA_ADSP_LOADER_BASE; + stream->sd_addr = sdev->bar[HDA_DSP_HDA_BAR] + + stream->sd_offset; + stream->tag = i - num_capture + 1; + stream->open = false; + stream->running = false; + stream->direction = SNDRV_PCM_STREAM_PLAYBACK; + stream->index = i; + + /* mem alloc for stream BDL */ + ret = snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, &pci->dev, + HDA_DSP_BDL_SIZE, &stream->bdl); + if (ret < 0) { + dev_err(sdev->dev, "error: stream bdl dma alloc failed\n"); + return -ENOMEM; + } + + stream->posbuf = (__le32 *)(hdev->posbuffer.area + + (stream->index) * 8); + } + + return 0; +} + +void hda_dsp_stream_free(struct snd_sof_dev *sdev) +{ + struct sof_intel_hda_dev *hdev = sdev->hda; + struct sof_intel_hda_stream *stream; + int i; + + /* free position buffer */ + if (hdev->posbuffer.area) + snd_dma_free_pages(&hdev->posbuffer); + + /* free capture streams */ + for (i = 0; i < hdev->num_capture; i++) { + stream = &hdev->cstream[i]; + + /* free bdl buffer */ + if (stream->bdl.area) + snd_dma_free_pages(&stream->bdl); + } + + /* free playback streams */ + for (i = 0; i < hdev->num_playback; i++) { + stream = &hdev->pstream[i]; + + /* free bdl buffer */ + if (stream->bdl.area) + snd_dma_free_pages(&stream->bdl); + } +} + From 0489f5af0a066a52f6ec9c08224a93bc2463456c Mon Sep 17 00:00:00 2001 From: Liam Girdwood Date: Wed, 9 May 2018 17:14:17 +0100 Subject: [PATCH 046/107] ASoC: SOF: Intel: Add Intel specific HDA trace operations Add trace operations for Intel based HDA DSPs Signed-off-by: Liam Girdwood --- sound/soc/sof/intel/hda-trace.c | 88 +++++++++++++++++++++++++++++++++ 1 file changed, 88 insertions(+) create mode 100644 sound/soc/sof/intel/hda-trace.c diff --git a/sound/soc/sof/intel/hda-trace.c b/sound/soc/sof/intel/hda-trace.c new file mode 100644 index 00000000000000..2b88768cb903bb --- /dev/null +++ b/sound/soc/sof/intel/hda-trace.c @@ -0,0 +1,88 @@ +// SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause) +/* + * This file is provided under a dual BSD/GPLv2 license. When using or + * redistributing this file, you may do so under either license. + * + * Copyright(c) 2017 Intel Corporation. All rights reserved. + * + * Authors: Liam Girdwood + * Ranjani Sridharan + * Jeeja KP + * Rander Wang + * Keyon Jie + */ + +/* + * Hardware interface for generic Intel audio DSP HDA IP + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "../sof-priv.h" +#include "../ops.h" +#include "hda.h" + +static int hda_dsp_trace_prepare(struct snd_sof_dev *sdev) +{ + struct sof_intel_hda_stream *stream = sdev->hda->dtrace_stream; + struct snd_dma_buffer *dmab = &sdev->dmatb; + int ret; + + stream->bufsize = sdev->dmatb.bytes; + + ret = hda_dsp_stream_hw_params(sdev, stream, dmab, NULL); + if (ret < 0) + dev_err(sdev->dev, "error: hdac prepare failed: %x\n", ret); + + return ret; +} + +int hda_dsp_trace_init(struct snd_sof_dev *sdev, u32 *tag) +{ + sdev->hda->dtrace_stream = hda_dsp_stream_get_cstream(sdev); + + if (!sdev->hda->dtrace_stream) { + dev_err(sdev->dev, + "error: no available capture stream for DMA trace\n"); + return -ENODEV; + } + + *tag = sdev->hda->dtrace_stream->tag; + + /* + * initialize capture stream, set BDL address and return corresponding + * stream tag which will be sent to the firmware by IPC message. + */ + return hda_dsp_trace_prepare(sdev); +} + +int hda_dsp_trace_release(struct snd_sof_dev *sdev) +{ + if (sdev->hda->dtrace_stream) { + sdev->hda->dtrace_stream->open = false; + hda_dsp_stream_put_cstream(sdev, + sdev->hda->dtrace_stream->tag); + sdev->hda->dtrace_stream = NULL; + return 0; + } + + dev_dbg(sdev->dev, "DMA trace stream is not opened!\n"); + return -ENODEV; +} + +int hda_dsp_trace_trigger(struct snd_sof_dev *sdev, int cmd) +{ + return hda_dsp_stream_trigger(sdev, sdev->hda->dtrace_stream, cmd); +} From db1915c88f789390b8a81a00e597b3fc34e4d56e Mon Sep 17 00:00:00 2001 From: Liam Girdwood Date: Wed, 9 May 2018 17:14:18 +0100 Subject: [PATCH 047/107] ASoC: SOF: Intel: Add platform differentiation for SKL, APL and CNL Add platform differentiation operations for different Intel HDA DSP platforms. Signed-off-by: Liam Girdwood --- sound/soc/sof/intel/apl.c | 96 ++++++++++++++++ sound/soc/sof/intel/cnl.c | 224 ++++++++++++++++++++++++++++++++++++++ sound/soc/sof/intel/skl.c | 101 +++++++++++++++++ 3 files changed, 421 insertions(+) create mode 100644 sound/soc/sof/intel/apl.c create mode 100644 sound/soc/sof/intel/cnl.c create mode 100644 sound/soc/sof/intel/skl.c diff --git a/sound/soc/sof/intel/apl.c b/sound/soc/sof/intel/apl.c new file mode 100644 index 00000000000000..d36e30d2a8b388 --- /dev/null +++ b/sound/soc/sof/intel/apl.c @@ -0,0 +1,96 @@ +// SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause) +/* + * This file is provided under a dual BSD/GPLv2 license. When using or + * redistributing this file, you may do so under either license. + * + * Copyright(c) 2017 Intel Corporation. All rights reserved. + * + * Authors: Liam Girdwood + * Ranjani Sridharan + * Jeeja KP + * Rander Wang + * Keyon Jie + */ + +/* + * Hardware interface for audio DSP on Apollolake and GeminiLake + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "../sof-priv.h" +#include "../ops.h" +#include "hda.h" + +static const struct snd_sof_debugfs_map apl_dsp_debugfs[] = { + {"hda", HDA_DSP_HDA_BAR, 0, 0x4000}, + {"pp", HDA_DSP_PP_BAR, 0, 0x1000}, + {"dsp", HDA_DSP_BAR, 0, 0x10000}, +}; + +/* apollolake ops */ +struct snd_sof_dsp_ops sof_apl_ops = { + /* probe and remove */ + .probe = hda_dsp_probe, + .remove = hda_dsp_remove, + + /* Register IO */ + .write = hda_dsp_write, + .read = hda_dsp_read, + .write64 = hda_dsp_write64, + .read64 = hda_dsp_read64, + + /* Block IO */ + .block_read = hda_dsp_block_read, + .block_write = hda_dsp_block_write, + + /* doorbell */ + .irq_handler = hda_dsp_ipc_irq_handler, + .irq_thread = hda_dsp_ipc_irq_thread, + + /* mailbox */ + .mailbox_read = hda_dsp_mailbox_read, + .mailbox_write = hda_dsp_mailbox_write, + + /* ipc */ + .send_msg = hda_dsp_ipc_send_msg, + .get_reply = hda_dsp_ipc_get_reply, + .fw_ready = hda_dsp_ipc_fw_ready, + .is_ready = hda_dsp_ipc_is_ready, + .cmd_done = hda_dsp_ipc_cmd_done, + + /* debug */ + .debug_map = apl_dsp_debugfs, + .debug_map_count = ARRAY_SIZE(apl_dsp_debugfs), + .dbg_dump = hda_dsp_dump, + + /* stream callbacks */ + .host_stream_open = hda_dsp_pcm_open, + .host_stream_close = hda_dsp_pcm_close, + .host_stream_hw_params = hda_dsp_pcm_hw_params, + .host_stream_trigger = hda_dsp_pcm_trigger, + + /* firmware loading */ + .load_firmware = hda_dsp_cl_load_fw, + + /* firmware run */ + .run = hda_dsp_cl_boot_firmware, + + /* trace callback */ + .trace_init = hda_dsp_trace_init, + .trace_release = hda_dsp_trace_release, + .trace_trigger = hda_dsp_trace_trigger, +}; +EXPORT_SYMBOL(sof_apl_ops); diff --git a/sound/soc/sof/intel/cnl.c b/sound/soc/sof/intel/cnl.c new file mode 100644 index 00000000000000..80d6cc772fd74a --- /dev/null +++ b/sound/soc/sof/intel/cnl.c @@ -0,0 +1,224 @@ +// SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause) +/* + * This file is provided under a dual BSD/GPLv2 license. When using or + * redistributing this file, you may do so under either license. + * + * Copyright(c) 2017 Intel Corporation. All rights reserved. + * + * Authors: Liam Girdwood + * Ranjani Sridharan + * Jeeja KP + * Rander Wang + * Keyon Jie + */ + +/* + * Hardware interface for audio DSP on Cannonlake. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "../sof-priv.h" +#include "../ops.h" +#include "hda.h" + +static const struct snd_sof_debugfs_map cnl_dsp_debugfs[] = { + {"hda", HDA_DSP_HDA_BAR, 0, 0x4000}, + {"pp", HDA_DSP_PP_BAR, 0, 0x1000}, + {"dsp", HDA_DSP_BAR, 0, 0x10000}, +}; + +static irqreturn_t cnl_ipc_irq_thread(int irq, void *context) +{ + struct snd_sof_dev *sdev = (struct snd_sof_dev *)context; + u32 hipci, hipcida, hipctdr, hipctdd, msg = 0, msg_ext = 0; + irqreturn_t ret = IRQ_NONE; + + /* here we handle IPC interrupts only */ + if (!(sdev->irq_status & HDA_DSP_ADSPIS_IPC)) + return ret; + + hipcida = snd_sof_dsp_read(sdev, HDA_DSP_BAR, CNL_DSP_REG_HIPCIDA); + hipctdr = snd_sof_dsp_read(sdev, HDA_DSP_BAR, CNL_DSP_REG_HIPCTDR); + + /* reply message from DSP */ + if (hipcida & CNL_DSP_REG_HIPCIDA_DONE) { + hipci = snd_sof_dsp_read(sdev, HDA_DSP_BAR, + CNL_DSP_REG_HIPCIDR); + msg_ext = hipci & CNL_DSP_REG_HIPCIDR_MSG_MASK; + msg = hipcida & CNL_DSP_REG_HIPCIDA_MSG_MASK; + + dev_vdbg(sdev->dev, + "ipc: firmware response, msg:0x%x, msg_ext:0x%x\n", + msg, msg_ext); + + /* mask Done interrupt */ + snd_sof_dsp_update_bits(sdev, HDA_DSP_BAR, + CNL_DSP_REG_HIPCCTL, + CNL_DSP_REG_HIPCCTL_DONE, 0); + + /* handle immediate reply from DSP core */ + snd_sof_ipc_reply(sdev, msg); + + /* clear DONE bit - tell DSP we have completed the operation */ + snd_sof_dsp_update_bits_forced(sdev, HDA_DSP_BAR, + CNL_DSP_REG_HIPCIDA, + CNL_DSP_REG_HIPCIDA_DONE, + CNL_DSP_REG_HIPCIDA_DONE); + + /* unmask Done interrupt */ + snd_sof_dsp_update_bits(sdev, HDA_DSP_BAR, + CNL_DSP_REG_HIPCCTL, + CNL_DSP_REG_HIPCCTL_DONE, + CNL_DSP_REG_HIPCCTL_DONE); + + ret = IRQ_HANDLED; + } + + hipctdr = snd_sof_dsp_read(sdev, HDA_DSP_BAR, CNL_DSP_REG_HIPCTDR); + + /* new message from DSP */ + if (hipctdr & CNL_DSP_REG_HIPCTDR_BUSY) { + hipctdd = snd_sof_dsp_read(sdev, HDA_DSP_BAR, + CNL_DSP_REG_HIPCTDD); + msg = hipctdr & CNL_DSP_REG_HIPCTDR_MSG_MASK; + msg_ext = hipctdd & CNL_DSP_REG_HIPCTDD_MSG_MASK; + + dev_vdbg(sdev->dev, + "ipc: firmware initiated, msg:0x%x, msg_ext:0x%x\n", + msg, msg_ext); + + /* handle messages from DSP */ + if ((hipctdr & SOF_IPC_PANIC_MAGIC_MASK) == + SOF_IPC_PANIC_MAGIC) { + snd_sof_dsp_panic(sdev, HDA_DSP_PANIC_OFFSET(msg_ext)); + } else { + snd_sof_ipc_msgs_rx(sdev); + } + + /* clear busy interrupt to tell dsp controller this */ + /* interrupt has been accepted, not trigger it again */ + snd_sof_dsp_update_bits_forced(sdev, HDA_DSP_BAR, + CNL_DSP_REG_HIPCTDR, + CNL_DSP_REG_HIPCTDR_BUSY, + CNL_DSP_REG_HIPCTDR_BUSY); + + ret = IRQ_HANDLED; + } + + if (ret == IRQ_HANDLED) { + /* reenable IPC interrupt */ + snd_sof_dsp_update_bits(sdev, HDA_DSP_BAR, HDA_DSP_REG_ADSPIC, + HDA_DSP_ADSPIC_IPC, HDA_DSP_ADSPIC_IPC); + } + + if (sdev->code_loading) { + sdev->code_loading = 0; + wake_up(&sdev->waitq); + } + + return ret; +} + +static int cnl_ipc_cmd_done(struct snd_sof_dev *sdev) +{ + /* set done bit to ack dsp the msg has been processed */ + snd_sof_dsp_update_bits_forced(sdev, HDA_DSP_BAR, + CNL_DSP_REG_HIPCTDA, + CNL_DSP_REG_HIPCTDA_DONE, + CNL_DSP_REG_HIPCTDA_DONE); + + return 0; +} + +static int cnl_ipc_is_ready(struct snd_sof_dev *sdev) +{ + u64 val; + + val = snd_sof_dsp_read(sdev, HDA_DSP_BAR, CNL_DSP_REG_HIPCIDR); + if (val & CNL_DSP_REG_HIPCIDR_BUSY) + return 0; + + return 1; +} + +static int cnl_ipc_send_msg(struct snd_sof_dev *sdev, + struct snd_sof_ipc_msg *msg) +{ + u32 cmd = msg->header; + + /* send the message */ + hda_dsp_mailbox_write(sdev, sdev->host_box.offset, msg->msg_data, + msg->msg_size); + snd_sof_dsp_write(sdev, HDA_DSP_BAR, CNL_DSP_REG_HIPCIDR, + cmd | CNL_DSP_REG_HIPCIDR_BUSY); + + return 0; +} + +/* cannonlake ops */ +struct snd_sof_dsp_ops sof_cnl_ops = { + /* probe and remove */ + .probe = hda_dsp_probe, + .remove = hda_dsp_remove, + + /* Register IO */ + .write = hda_dsp_write, + .read = hda_dsp_read, + .write64 = hda_dsp_write64, + .read64 = hda_dsp_read64, + + /* Block IO */ + .block_read = hda_dsp_block_read, + .block_write = hda_dsp_block_write, + + /* doorbell */ + .irq_handler = hda_dsp_ipc_irq_handler, + .irq_thread = cnl_ipc_irq_thread, + + /* mailbox */ + .mailbox_read = hda_dsp_mailbox_read, + .mailbox_write = hda_dsp_mailbox_write, + + /* ipc */ + .send_msg = cnl_ipc_send_msg, + .get_reply = hda_dsp_ipc_get_reply, + .fw_ready = hda_dsp_ipc_fw_ready, + .is_ready = cnl_ipc_is_ready, + .cmd_done = cnl_ipc_cmd_done, + + /* debug */ + .debug_map = cnl_dsp_debugfs, + .debug_map_count = ARRAY_SIZE(cnl_dsp_debugfs), + .dbg_dump = hda_dsp_dump, + + /* stream callbacks */ + .host_stream_open = hda_dsp_pcm_open, + .host_stream_close = hda_dsp_pcm_close, + .host_stream_hw_params = hda_dsp_pcm_hw_params, + .host_stream_trigger = hda_dsp_pcm_trigger, + + /* firmware loading */ + .load_firmware = hda_dsp_cl_load_fw, + + /* firmware run */ + .run = hda_dsp_cl_boot_firmware, + + /* trace callback */ + .trace_init = hda_dsp_trace_init, + .trace_release = hda_dsp_trace_release, + .trace_trigger = hda_dsp_trace_trigger, +}; +EXPORT_SYMBOL(sof_cnl_ops); diff --git a/sound/soc/sof/intel/skl.c b/sound/soc/sof/intel/skl.c new file mode 100644 index 00000000000000..d4b6de626456b1 --- /dev/null +++ b/sound/soc/sof/intel/skl.c @@ -0,0 +1,101 @@ +// SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause) +/* + * This file is provided under a dual BSD/GPLv2 license. When using or + * redistributing this file, you may do so under either license. + * + * Copyright(c) 2017 Intel Corporation. All rights reserved. + * + * Authors: Liam Girdwood + * Ranjani Sridharan + * Jeeja KP + * Rander Wang + * Keyon Jie + */ + +/* + * Hardware interface for audio DSP on Skylake and Kabylake. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "../sof-priv.h" +#include "../ops.h" +#include "hda.h" + +static const struct snd_sof_debugfs_map skl_dsp_debugfs[] = { + {"hda", HDA_DSP_HDA_BAR, 0, 0x4000}, + {"pp", HDA_DSP_PP_BAR, 0, 0x1000}, + {"dsp", HDA_DSP_BAR, 0, 0x10000}, +}; + +int skl_run_firmware(struct snd_sof_dev *sdev) +{ + return 0; +} + +/* skylake ops */ +struct snd_sof_dsp_ops sof_skl_ops = { + /* probe and remove */ + .probe = hda_dsp_probe, + .remove = hda_dsp_remove, + + /* Register IO */ + .write = hda_dsp_write, + .read = hda_dsp_read, + .write64 = hda_dsp_write64, + .read64 = hda_dsp_read64, + + /* Block IO */ + .block_read = hda_dsp_block_read, + .block_write = hda_dsp_block_write, + + /* doorbell */ + .irq_handler = hda_dsp_ipc_irq_handler, + .irq_thread = hda_dsp_ipc_irq_thread, + + /* mailbox */ + .mailbox_read = hda_dsp_mailbox_read, + .mailbox_write = hda_dsp_mailbox_write, + + /* ipc */ + .send_msg = hda_dsp_ipc_send_msg, + .get_reply = hda_dsp_ipc_get_reply, + .fw_ready = hda_dsp_ipc_fw_ready, + .is_ready = hda_dsp_ipc_is_ready, + .cmd_done = hda_dsp_ipc_cmd_done, + + /* debug */ + .debug_map = skl_dsp_debugfs, + .debug_map_count = ARRAY_SIZE(skl_dsp_debugfs), + .dbg_dump = hda_dsp_dump, + + /* stream callbacks */ + .host_stream_open = hda_dsp_pcm_open, + .host_stream_close = hda_dsp_pcm_close, + .host_stream_hw_params = hda_dsp_pcm_hw_params, + .host_stream_trigger = hda_dsp_pcm_trigger, + + /* firmware loading */ + .load_firmware = hda_dsp_cl_load_fw, + + /* firmware run */ + .run = hda_dsp_cl_boot_firmware, + + /* trace callback */ + .trace_init = hda_dsp_trace_init, + .trace_release = hda_dsp_trace_release, + .trace_trigger = hda_dsp_trace_trigger, +}; +EXPORT_SYMBOL(sof_skl_ops); From 843579f3cbe843843670ffd9b03414be16075e6b Mon Sep 17 00:00:00 2001 From: Liam Girdwood Date: Mon, 8 Jan 2018 20:39:03 +0000 Subject: [PATCH 048/107] ASoC: SOF: Add userspace ABI support Add userspace ABI for audio userspace application IO outside of regular ALSA PCM and kcontrols. This is intended to be used to format coefficients and data for custom processing components. Signed-off-by: Liam Girdwood --- include/uapi/sound/sof-abi.h | 29 ++++++++++ include/uapi/sound/sof-eq.h | 102 ++++++++++++++++++++++++++++++++++ include/uapi/sound/sof-fw.h | 69 +++++++++++++++++++++++ include/uapi/sound/sof-tone.h | 28 ++++++++++ 4 files changed, 228 insertions(+) create mode 100644 include/uapi/sound/sof-abi.h create mode 100644 include/uapi/sound/sof-eq.h create mode 100644 include/uapi/sound/sof-fw.h create mode 100644 include/uapi/sound/sof-tone.h diff --git a/include/uapi/sound/sof-abi.h b/include/uapi/sound/sof-abi.h new file mode 100644 index 00000000000000..4e78fa14465e35 --- /dev/null +++ b/include/uapi/sound/sof-abi.h @@ -0,0 +1,29 @@ +/* SPDX-License-Identifier: ((GPL-2.0 WITH Linux-syscall-note)) OR BSD-3-Clause) */ +/* + * This file is provided under a dual BSD/GPLv2 license. When using or + * redistributing this file, you may do so under either license. + * + * Copyright(c) 2017 Intel Corporation. All rights reserved. + */ + +#ifndef __INCLUDE_UAPI_ABI_H__ +#define __INCLUDE_UAPI_ABI_H__ + +#define SOF_ABI_VERSION 1 +#define SOF_ABI_MAGIC 0x00464F53 /* "SOF\0" */ + +/* + * Header for all non IPC ABI data. Identifies data type, size and ABI. + * Used by any bespoke component data structures or binary blobs. + */ + +struct sof_abi_hdr { + uint32_t magic; /* 'S', 'O', 'F', '\0' */ + uint32_t type; /* component specific type */ + uint32_t size; /* size in bytes of data excluding this struct */ + uint32_t abi; /* SOF ABI version */ + uint32_t comp_abi; /* component specific ABI version */ + char data[0]; +} __attribute__((packed)); + +#endif diff --git a/include/uapi/sound/sof-eq.h b/include/uapi/sound/sof-eq.h new file mode 100644 index 00000000000000..96aefd1e1950b9 --- /dev/null +++ b/include/uapi/sound/sof-eq.h @@ -0,0 +1,102 @@ +/* SPDX-License-Identifier: ((GPL-2.0 WITH Linux-syscall-note)) OR BSD-3-Clause) */ +/* + * This file is provided under a dual BSD/GPLv2 license. When using or + * redistributing this file, you may do so under either license. + * + * Copyright(c) 2017 Intel Corporation. All rights reserved. + * + * Author: Seppo Ingalsuo + */ + +#ifndef EQ_H +#define EQ_H + +/* FIR EQ type */ + +/* Component will reject non-matching configuration. The version number need + * to be incremented with any ABI changes in function fir_cmd(). + */ +#define SOF_EQ_FIR_ABI_VERSION 1 + +#define SOF_EQ_FIR_IDX_SWITCH 0 + +#define SOF_EQ_FIR_MAX_SIZE 4096 /* Max size allowed for coef data in bytes */ + +/* + * eq_fir_configuration data structure contains this information + * uint16_t channels_in_config + * This describes the number of channels in this EQ config data. It + * can be different from PLATFORM_MAX_CHANNELS. + * uint16_t number_of_responses + * 0=no responses, 1=one response defined, 2=two responses defined, etc. + * int16_t data[] + * assign_response[STREAM_MAX_CHANNELS] + * -1 = not defined, 0 = use first response, 1 = use 2nd response, etc. + * E.g. {0, 0, 0, 0, -1, -1, -1, -1} would apply to channels 0-3 the + * same first defined response and leave channels 4-7 unequalized. + * coef_data[] + * Repeated data { filter_length, input_shift, output_shift, h[] } + * for every EQ response defined where vector h has filter_length + * number of coefficients. Coefficients in h[] are in Q1.15 format. + * E.g. 16384 (Q1.15) = 0.5. The shifts are number of right shifts. + */ + +struct sof_eq_fir_config { + uint16_t channels_in_config; + uint16_t number_of_responses; + int16_t data[]; +}; + +/* IIR EQ type */ + +/* Component will reject non-matching configuration. The version number need + * to be incremented with any ABI changes in function fir_cmd(). + */ +#define SOF_EQ_FIR_ABI_VERSION 1 + +#define SOF_EQ_IIR_IDX_SWITCH 0 + +#define SOF_EQ_IIR_MAX_SIZE 1024 /* Max size allowed for coef data in bytes */ + +/* eq_iir_configuration + * uint32_t channels_in_config + * This describes the number of channels in this EQ config data. It + * can be different from PLATFORM_MAX_CHANNELS. + * uint32_t number_of_responses_defined + * 0=no responses, 1=one response defined, 2=two responses defined, etc. + * int32_t data[] + * Data consist of two parts. First is the response assign vector that + * has length of channels_in_config. The latter part is coefficient + * data. + * uint32_t assign_response[channels_in_config] + * -1 = not defined, 0 = use first response, 1 = use 2nd, etc. + * E.g. {0, 0, 0, 0, -1, -1, -1, -1} would apply to channels 0-3 the + * same first defined response and leave channels 4-7 unequalized. + * coefficient_data[] + * <1st EQ> + * uint32_t num_biquads + * uint32_t num_biquads_in_series + * <1st biquad> + * int32_t coef_a2 Q2.30 format + * int32_t coef_a1 Q2.30 format + * int32_t coef_b2 Q2.30 format + * int32_t coef_b1 Q2.30 format + * int32_t coef_b0 Q2.30 format + * int32_t output_shift number of shifts right, shift left is negative + * int32_t output_gain Q2.14 format + * <2nd biquad> + * ... + * <2nd EQ> + * + * Note: A flat response biquad can be made with a section set to + * b0 = 1.0, gain = 1.0, and other parameters set to 0 + * {0, 0, 0, 0, 1073741824, 0, 16484} + */ + +struct sof_eq_iir_config { + uint32_t channels_in_config; + uint32_t number_of_responses; + int32_t data[]; +}; + +#endif /* EQ_H */ diff --git a/include/uapi/sound/sof-fw.h b/include/uapi/sound/sof-fw.h new file mode 100644 index 00000000000000..2f9bd827d2f02d --- /dev/null +++ b/include/uapi/sound/sof-fw.h @@ -0,0 +1,69 @@ +/* SPDX-License-Identifier: ((GPL-2.0 WITH Linux-syscall-note)) OR BSD-3-Clause) */ +/* + * This file is provided under a dual BSD/GPLv2 license. When using or + * redistributing this file, you may do so under either license. + * + * Copyright(c) 2017 Intel Corporation. All rights reserved. + * + * Author: Liam Girdwood + */ + +/* + * Firmware file format . + */ + +#ifndef __INCLUDE_UAPI_SOF_FW_H__ +#define __INCLUDE_UAPI_SOF_FW_H__ + +#define SND_SOF_FW_SIG_SIZE 4 +#define SND_SOF_FW_ABI 1 +#define SND_SOF_FW_SIG "Reef" + +/* + * Firmware module is made up of 1 . N blocks of different types. The + * Block header is used to determine where and how block is to be copied in the + * DSP/host memory space. + */ +enum snd_sof_fw_blk_type { + SOF_BLK_IMAGE = 0, /* whole image - parsed by ROMs */ + SOF_BLK_TEXT = 1, + SOF_BLK_DATA = 2, + SOF_BLK_CACHE = 3, + SOF_BLK_REGS = 4, + SOF_BLK_SIG = 5, + SOF_BLK_ROM = 6, + /* add new block types here */ +}; + +struct snd_sof_blk_hdr { + enum snd_sof_fw_blk_type type; + uint32_t size; /* bytes minus this header */ + uint32_t offset; /* offset from base */ +} __attribute__((packed)); + +/* + * Firmware file is made up of 1 .. N different modules types. The module + * type is used to determine how to load and parse the module. + */ +enum snd_sof_fw_mod_type { + SOF_FW_BASE = 0, /* base firmware image */ + SOF_FW_MODULE = 1, /* firmware module */ +}; + +struct snd_sof_mod_hdr { + enum snd_sof_fw_mod_type type; + uint32_t size; /* bytes minus this header */ + uint32_t num_blocks; /* number of blocks */ +} __attribute__((packed)); + +/* + * Firmware file header. + */ +struct snd_sof_fw_header { + unsigned char sig[SND_SOF_FW_SIG_SIZE]; /* "Reef" */ + uint32_t file_size; /* size of file minus this header */ + uint32_t num_modules; /* number of modules */ + uint32_t abi; /* version of header format */ +} __attribute__((packed)); + +#endif diff --git a/include/uapi/sound/sof-tone.h b/include/uapi/sound/sof-tone.h new file mode 100644 index 00000000000000..f9c9bf5c482d85 --- /dev/null +++ b/include/uapi/sound/sof-tone.h @@ -0,0 +1,28 @@ +/* SPDX-License-Identifier: ((GPL-2.0 WITH Linux-syscall-note)) OR BSD-3-Clause) */ +/* + * This file is provided under a dual BSD/GPLv2 license. When using or + * redistributing this file, you may do so under either license. + * + * Copyright(c) 2017 Intel Corporation. All rights reserved. + * + * Author: Seppo Ingalsuo + */ + +#ifndef TONE_H +#define TONE_H + +/* Component will reject non-matching configuration. The version number need + * to be incremented with any ABI changes in function fir_cmd(). + */ +#define SOF_TONE_ABI_VERSION 1 + +#define SOF_TONE_IDX_FREQUENCY 0 +#define SOF_TONE_IDX_AMPLITUDE 1 +#define SOF_TONE_IDX_FREQ_MULT 2 +#define SOF_TONE_IDX_AMPL_MULT 3 +#define SOF_TONE_IDX_LENGTH 4 +#define SOF_TONE_IDX_PERIOD 5 +#define SOF_TONE_IDX_REPEATS 6 +#define SOF_TONE_IDX_LIN_RAMP_STEP 7 + +#endif /* TONE_ABI_H */ From 4078be48bb29c524a888d1ab82c73044ddef9cfa Mon Sep 17 00:00:00 2001 From: Liam Girdwood Date: Mon, 8 Jan 2018 20:35:47 +0000 Subject: [PATCH 049/107] ASoC: SOF: Add VirtIO support Add support for running SOF on multiple VMs within the same HW. Signed-off-by: Liam Girdwood --- sound/soc/sof/virtio-be.c | 126 ++++++++++++++++++++++++++++++++++++ sound/soc/sof/virtio-fe.c | 130 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 256 insertions(+) create mode 100644 sound/soc/sof/virtio-be.c create mode 100644 sound/soc/sof/virtio-fe.c diff --git a/sound/soc/sof/virtio-be.c b/sound/soc/sof/virtio-be.c new file mode 100644 index 00000000000000..4c07caeba41b0c --- /dev/null +++ b/sound/soc/sof/virtio-be.c @@ -0,0 +1,126 @@ +// SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause) +/* + * This file is provided under a dual BSD/GPLv2 license. When using or + * redistributing this file, you may do so under either license. + * + * Copyright(c) 2017 Intel Corporation. All rights reserved. + * + * Author: Luo Xionghu + * Liam Girdwood + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "sof-priv.h" +#include "ops.h" + +/* BE driver + * + * This driver will create IO Queues for communition from FE drivers. + * The FE driver will send real IPC structures over the queue and then + * the BE driver will send the structures directlt to the DSP. The BE will + * get the IPC reply from the DSP and send it back to the FE over the queue. + * + * The virt IO message Q handlers in this file will :- + * + * 1) Check that the message is valid and not for any componenets that don't + * belong to the guest. + * + * 2) Call snd_sof_dsp_tx_msg(struct snd_sof_dev *sdev, + * struct snd_sof_ipc_msg *msg) to send the message to the DSP. + * + * Replies will be sent back using a similar method. + */ + +static int sof_virtio_validate(struct virtio_device *dev) +{ + /* do we need this func ?? */ + return 0; +} + +static int sof_virtio_probe(struct virtio_device *dev) +{ + /* register fe device with sof core */ + //snd_sof_virtio_register_fe(dev); + + /* create our virtqueues */s + + /* send topology data to fe via virtq */ + + return 0; +} + +static void sof_virtio_remove(struct virtio_device *dev) +{ + /* remove topology from fe via virtqueue */ + + /* destroy virtqueue */ +} + +#ifdef CONFIG_PM +static int sof_virtio_freeze(struct virtio_device *dev) +{ + /* pause and suspend any streams for this FE */ + return 0; +} + +static int sof_virtio_restore(struct virtio_device *dev) +{ + /* restore and unpause any streams for this FE */ + return 0; +} +#endif + +/* IDs of FEs */ +static const struct virtio_device_id *fe_id_table[] + { +}; + +static struct virtio_driver sof_be_virtio_driver = { + .driver = { + .name = "sof-virtio-be", + .owner = THIS_MODULE, + }, + + .id_table = fe_id_table, + + //const unsigned int *feature_table; + //unsigned int feature_table_size; + //const unsigned int *feature_table_legacy; + //unsigned int feature_table_size_legacy; + + validate = sof_virtio_validate, + probe = sof_virtio_probe, + remove = sof_virtio_remove, + +#ifdef CONFIG_PM + freeze = sof_virtio_freeze, + restore = sof_virtio_restore, +#endif +}; + +/* this will be called by sof core when core is ready */ +int sof_virtio_register(struct snd_sof_dev *sdev) +{ + int ret; + + ret = register_virtio_driver(&sof_be_virtio_driver); + /* do we need to do anythig else here */ + return ret; +} + +/* called by sof core when driver is removed */ +void sof_virtio_unregister(struct snd_sof_dev *sdev) +{ + unregister_virtio_driver(&sof_be_virtio_driver); + /* do we need to do anythig else here */ +} diff --git a/sound/soc/sof/virtio-fe.c b/sound/soc/sof/virtio-fe.c new file mode 100644 index 00000000000000..1019e65876b923 --- /dev/null +++ b/sound/soc/sof/virtio-fe.c @@ -0,0 +1,130 @@ +// SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause) +/* + * This file is provided under a dual BSD/GPLv2 license. When using or + * redistributing this file, you may do so under either license. + * + * Copyright(c) 2017 Intel Corporation. All rights reserved. + * + * Author: Luo Xionghu + * Liam Girdwood + */ + +/* + * virt IO FE driver + * + * The SOF driver thinks this driver is another audio DSP, however the calls + * made by the SOF driver core do not directly go to HW, but over a virtIO + * message Q to the virtIO BE driver. + * + * The virtIO message Q will use the *exact* same IPC structures as we currently + * use in the mailbox. + * + * Guest OS SOF core -> SOF FE -> virtIO Q -> SOF BE -> + * System OS SOF core -> DSP + * + * The mailbox IO and TX/RX msg functions below will do IO on the virt IO Q. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "sof-priv.h" +#include "ops.h" +#include "intel.h" + +/* + * IPC Firmware ready. + */ +static int virtio_fe_fw_ready(struct snd_sof_dev *sdev, u32 msg_id) +{ + /* not needed for FE ? */ + return 0; +} + +/* + * IPC Mailbox IO + */ + +static void virtio_fe_mailbox_write(struct snd_sof_dev *sdev, u32 offset, + void *message, size_t bytes) +{ + /* write data to message Q buffer before sending message */ +} + +static void virtio_fe_mailbox_read(struct snd_sof_dev *sdev, u32 offset, + void *message, size_t bytes) +{ + /* read data from message Q buffer after receiving message */ +} + +static int virtio_fe_tx_busy(struct snd_sof_dev *sdev) +{ + /* return 1 if tx message Q is busy */ +} + +static int virtio_fe_tx_msg(struct snd_sof_dev *sdev, + struct snd_sof_ipc_msg *msg) +{ + /* write msg to the virtio queue message for BE */ + + return 0; +} + +static int virtio_fe_rx_msg(struct snd_sof_dev *sdev, + struct snd_sof_ipc_msg *msg) +{ + /* read the virtio queue message from BE and copy to msg */ + return 0; +} + +/* + * Probe and remove. + */ + +static int virtio_fe_probe(struct snd_sof_dev *sdev) +{ + /* register virtio device */ + + /* conenct virt queues to BE */ +} + +static int virtio_fe_remove(struct snd_sof_dev *sdev) +{ + /* free virtio resurces and unregister device */ +} + +/* baytrail ops */ +struct snd_sof_dsp_ops snd_sof_virtio_fe_ops = { + /* device init */ + .probe = virtio_fe_probe, + .remove = virtio_fe_remove, + + /* mailbox */ + .mailbox_read = virtio_fe_mailbox_read, + .mailbox_write = virtio_fe_mailbox_write, + + /* ipc */ + .tx_msg = virtio_fe_tx_msg, + .rx_msg = virtio_fe_rx_msg, + .fw_ready = virtio_fe_fw_ready, + .tx_busy = virtio_fe_tx_busy, + + /* module loading */ +// .load_module = snd_sof_parse_module_memcpy, + + /*Firmware loading */ + .load_firmware = snd_sof_load_firmware_memcpy, +}; +EXPORT_SYMBOL(snd_sof_virtio_fe_ops); + +MODULE_LICENSE("Dual BSD/GPL"); From 361530c1f55d5339c5001790584cb14b275cee2a Mon Sep 17 00:00:00 2001 From: Liam Girdwood Date: Mon, 8 Jan 2018 20:36:46 +0000 Subject: [PATCH 050/107] ASoC: SOF: Add SPI device support Add support for SPI based DSP devices. Signed-off-by: Liam Girdwood --- sound/soc/sof/hw-spi.c | 250 ++++++++++++++++++++++++++++++++++++ sound/soc/sof/sof-spi-dev.c | 167 ++++++++++++++++++++++++ 2 files changed, 417 insertions(+) create mode 100644 sound/soc/sof/hw-spi.c create mode 100644 sound/soc/sof/sof-spi-dev.c diff --git a/sound/soc/sof/hw-spi.c b/sound/soc/sof/hw-spi.c new file mode 100644 index 00000000000000..e20682da09202c --- /dev/null +++ b/sound/soc/sof/hw-spi.c @@ -0,0 +1,250 @@ +// SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause) +/* + * This file is provided under a dual BSD/GPLv2 license. When using or + * redistributing this file, you may do so under either license. + * + * Copyright(c) 2017 Intel Corporation. All rights reserved. + * + * Author: Liam Girdwood + */ + +/* + * Hardware interface for audio DSPs via SPI + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "sof-priv.h" +#include "ops.h" +#include "intel.h" + +/* + * Memory copy. + */ + +static void spi_block_write(struct snd_sof_dev *sdev, u32 offset, void *src, + size_t size) +{ + // use spi_write() to copy data to DSP +} + +static void spi_block_read(struct snd_sof_dev *sdev, u32 offset, void *dest, + size_t size) +{ + // use spi_read() to copy data from DSP +} + +/* + * IPC Firmware ready. + */ +static int spi_fw_ready(struct snd_sof_dev *sdev, u32 msg_id) +{ + struct sof_ipc_fw_ready *fw_ready = &sdev->fw_ready; + struct sof_ipc_fw_version *v = &fw_ready->version; + + dev_dbg(sdev->dev, "ipc: DSP is ready 0x%8.8x\n", msg_id); + + // read local buffer with SPI data + + dev_info(sdev->dev, " Firmware info: version %d:%d-%s build %d on %s:%s\n", + v->major, v->minor, v->tag, v->build, v->date, v->time); + + return 0; +} + +/* + * IPC Mailbox IO + */ + +static void spi_mailbox_write(struct snd_sof_dev *sdev, u32 offset, + void *message, size_t bytes) +{ + void __iomem *dest = sdev->bar[sdev->mailbox_bar] + offset; + + //memcpy_toio(dest, message, bytes); + /* + * this will copy to a local memory buffer that will be sent to DSP via + * SPI at next IPC + */ +} + +static void spi_mailbox_read(struct snd_sof_dev *sdev, u32 offset, + void *message, size_t bytes) +{ + void __iomem *src = sdev->bar[sdev->mailbox_bar] + offset; + + //memcpy_fromio(message, src, bytes); + /* + * this will copy from a local memory buffer that will be received from + * DSP via SPI at last IPC + */ +} + +/* + * IPC Doorbell IRQ handler and thread. + */ + +static irqreturn_t spi_irq_handler(int irq, void *context) +{ + struct snd_sof_dev *sdev = (struct snd_sof_dev *)context; + int ret = IRQ_NONE; + + // on SPI based devices this will likely come via a SoC GPIO IRQ + + // check if GPIO is assetred and if so run thread. + + return ret; +} + +static irqreturn_t spi_irq_thread(int irq, void *context) +{ + struct snd_sof_dev *sdev = (struct snd_sof_dev *)context; + + // read SPI data into local buffer and determine IPC cmd or reply + + /* + * if reply. Handle Immediate reply from DSP Core and set DSP + * state to ready + */ + //snd_sof_ipc_reply(sdev, ipcx); + + /* if cmd, Handle messages from DSP Core */ + //snd_sof_ipc_msgs_rx(sdev); + + return IRQ_HANDLED; +} + +static int spi_is_ready(struct snd_sof_dev *sdev) +{ + // use local variable to store DSP command state. either DSP is ready + // for new cmd or still processing current cmd. + + return 1; +} + +static int spi_send_msg(struct snd_sof_dev *sdev, struct snd_sof_ipc_msg *msg) +{ + u64 cmd = msg->header; + + /* send the message */ + spi_mailbox_write(sdev, sdev->host_box.offset, msg->msg_data, + msg->msg_size); + + return 0; +} + +static int spi_get_reply(struct snd_sof_dev *sdev, struct snd_sof_ipc_msg *msg) +{ + struct sof_ipc_reply reply; + int ret = 0; + u32 size; + + /* get reply */ + spi_mailbox_read(sdev, sdev->host_box.offset, &reply, sizeof(reply)); + if (reply.error < 0) { + size = sizeof(reply); + ret = reply.error; + } else { + /* reply correct size ? */ + if (reply.hdr.size != msg->reply_size) { + dev_err(sdev->dev, "error: reply expected 0x%lx got 0x%x bytes\n", + msg->reply_size, reply.hdr.size); + size = msg->reply_size; + ret = -EINVAL; + } else { + size = reply.hdr.size; + } + } + + /* read the message */ + if (msg->msg_data && size > 0) + spi_mailbox_read(sdev, sdev->host_box.offset, msg->reply_data, + size); + + return ret; +} + +/* + * Probe and remove. + */ + +static int spi_sof_probe(struct snd_sof_dev *sdev) +{ + struct snd_sof_pdata *pdata = sdev->pdata; + const struct sof_dev_desc *desc = pdata->desc; + struct platform_device *pdev = + container_of(sdev->parent, struct platform_device, dev); + int ret = 0; + + /* get IRQ from Device tree or ACPI - register our IRQ */ + dev_dbg(sdev->dev, "using IRQ %d\n", sdev->ipc_irq); + ret = request_threaded_irq(sdev->ipc_irq, spi_irq_handler, + spi_irq_thread, IRQF_SHARED, "AudioDSP", + sdev); + if (ret < 0) { + dev_err(sdev->dev, "error: failed to register IRQ %d\n", + sdev->ipc_irq); + goto irq_err; + } + + return ret; +} + +static int spi_sof_remove(struct snd_sof_dev *sdev) +{ + free_irq(sdev->ipc_irq, sdev); + return 0; +} + +/* baytrail ops */ +struct snd_sof_dsp_ops snd_sof_spi_ops = { + /* device init */ + .probe = spi_sof_probe, + .remove = spi_sof_remove, + + /* Block IO */ + .block_read = spi_block_read, + .block_write = spi_block_write, + + /* doorbell */ + .irq_handler = spi_irq_handler, + .irq_thread = spi_irq_thread, + + /* mailbox */ + .mailbox_read = spi_mailbox_read, + .mailbox_write = spi_mailbox_write, + + /* ipc */ + .send_msg = spi_send_msg, + .get_reply = spi_get_reply, + .fw_ready = spi_fw_ready, + .is_ready = spi_is_ready, + .cmd_done = spi_cmd_done, + + /* debug */ + .debug_map = spi_debugfs, + .debug_map_count = ARRAY_SIZE(spi_debugfs), + .dbg_dump = spi_dump, + + /* module loading */ + .load_module = snd_sof_parse_module_memcpy, + + /*Firmware loading */ + .load_firmware = snd_sof_load_firmware_memcpy, +}; +EXPORT_SYMBOL(snd_sof_spi_ops); + +MODULE_LICENSE("Dual BSD/GPL"); diff --git a/sound/soc/sof/sof-spi-dev.c b/sound/soc/sof/sof-spi-dev.c new file mode 100644 index 00000000000000..b3811db6d74650 --- /dev/null +++ b/sound/soc/sof/sof-spi-dev.c @@ -0,0 +1,167 @@ +// SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause) +/* + * This file is provided under a dual BSD/GPLv2 license. When using or + * redistributing this file, you may do so under either license. + * + * Copyright(c) 2017 Intel Corporation. All rights reserved. + * + * Author: Liam Girdwood + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "sof-priv.h" + +struct sof_spi_priv { + struct snd_sof_pdata *sof_pdata; + struct platform_device *pdev_pcm; +}; + +static void sof_spi_fw_cb(const struct firmware *fw, void *context) +{ + struct sof_spi_priv *priv = context; + struct snd_sof_pdata *sof_pdata = priv->sof_pdata; + const struct snd_sof_machine *mach = sof_pdata->machine; + struct device *dev = sof_pdata->dev; + + sof_pdata->fw = fw; + if (!fw) { + dev_err(dev, "Cannot load firmware %s\n", + mach->sof_fw_filename); + return; + } + + /* register PCM and DAI driver */ + priv->pdev_pcm = + platform_device_register_data(dev, "sof-audio", -1, + sof_pdata, sizeof(*sof_pdata)); + if (IS_ERR(priv->pdev_pcm)) { + dev_err(dev, "Cannot register device sof-audio. Error %d\n", + (int)PTR_ERR(priv->pdev_pcm)); + } +} + +static const struct dev_pm_ops sof_spi_pm = { + SET_SYSTEM_SLEEP_PM_OPS(snd_sof_suspend, snd_sof_resume) + SET_RUNTIME_PM_OPS(snd_sof_runtime_suspend, snd_sof_runtime_resume, + NULL) + .suspend_late = snd_sof_suspend_late, +}; + +static int sof_spi_probe(struct spi_device *spi) +{ + struct device *dev = &spi->dev; + const struct snd_sof_machine *mach; + struct snd_sof_machine *m; + struct snd_sof_pdata *sof_pdata; + struct sof_spi_priv *priv; + int ret = 0; + + dev_dbg(&spi->dev, "SPI DSP detected"); + + priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + spi_set_drvdata(spi, priv); + + sof_pdata = devm_kzalloc(dev, sizeof(*sof_pdata), GFP_KERNEL); + if (!sof_pdata) + return -ENOMEM; + + /* use nocodec machine atm */ + dev_err(dev, "No matching ASoC machine driver found - using nocodec\n"); + sof_pdata->drv_name = "sof-nocodec"; + m = devm_kzalloc(dev, sizeof(*mach), GFP_KERNEL); + if (!m) + return -ENOMEM; + + m->drv_name = "sof-nocodec"; + m->sof_fw_filename = desc->nocodec_fw_filename; + m->sof_tplg_filename = desc->nocodec_tplg_filename; + m->ops = desc->machines[0].ops; + m->asoc_plat_name = "sof-platform"; + mach = m; + + sof_pdata->id = pci_id->device; + sof_pdata->name = spi_name(spi); + sof_pdata->machine = mach; + sof_pdata->desc = (struct sof_dev_desc *)pci_id->driver_data; + priv->sof_pdata = sof_pdata; + sof_pdata->spi = spi; + sof_pdata->dev = dev; + + /* register machine driver */ + sof_pdata->pdev_mach = + platform_device_register_data(dev, mach->drv_name, -1, + sof_pdata, sizeof(*sof_pdata)); + if (IS_ERR(sof_pdata->pdev_mach)) + return PTR_ERR(sof_pdata->pdev_mach); + dev_dbg(dev, "created machine %s\n", + dev_name(&sof_pdata->pdev_mach->dev)); + + /* continue probing after firmware is loaded */ + ret = request_firmware_nowait(THIS_MODULE, true, mach->sof_fw_filename, + dev, GFP_KERNEL, priv, sof_spi_fw_cb); + if (ret) + platform_device_unregister(sof_pdata->pdev_mach); + + return ret; +} + +static int sof_spi_remove(struct spi_device *spi) +{ + struct sof_spi_priv *priv = spi_get_drvdata(spi); + struct snd_sof_pdata *sof_pdata = priv->sof_pdata; + + platform_device_unregister(sof_pdata->pdev_mach); + if (!IS_ERR_OR_NULL(priv->pdev_pcm)) + platform_device_unregister(priv->pdev_pcm); + release_firmware(sof_pdata->fw); +} + +static struct spi_driver wm8731_spi_driver = { + .driver = { + .name = "sof-spi-dev", + .of_match_table = sof_of_match, + }, + .probe = sof_spi_probe, + .remove = sof_spi_remove, +}; + +static const struct snd_sof_machine sof_spi_machines[] = { + { "INT343A", "bxt_alc298s_i2s", "intel/sof-spi.ri", + "intel/sof-spi.tplg", "0000:00:0e.0", &snd_sof_spi_ops }, +}; + +static const struct sof_dev_desc spi_desc = { + .machines = sof_spi_machines, + .nocodec_fw_filename = "intel/sof-spi.ri", + .nocodec_tplg_filename = "intel/sof-spi.tplg" +}; + +static int __init sof_spi_modinit(void) +{ + int ret; + + ret = spi_register_driver(&sof_spi_driver); + if (ret != 0) + pr_err("Failed to register SOF SPI driver: %d\n", ret); + + return ret; +} +module_init(sof_spi_modinit); + +static void __exit sof_spi_modexit(void) +{ + spi_unregister_driver(&sof_spi_driver); +} +module_exit(sof_spi_modexit); + +MODULE_LICENSE("Dual BSD/GPL"); From 4021d605afdc80fbd1911c704dee89f1f15f0a97 Mon Sep 17 00:00:00 2001 From: Liam Girdwood Date: Mon, 8 Jan 2018 20:36:02 +0000 Subject: [PATCH 051/107] ASoC: SOF: Add ACPI device support Add support ACPI based SOF DSP devices. Signed-off-by: Liam Girdwood --- sound/soc/sof/sof-acpi-dev.c | 357 +++++++++++++++++++++++++++++++++++ 1 file changed, 357 insertions(+) create mode 100644 sound/soc/sof/sof-acpi-dev.c diff --git a/sound/soc/sof/sof-acpi-dev.c b/sound/soc/sof/sof-acpi-dev.c new file mode 100644 index 00000000000000..dd71297e8c2558 --- /dev/null +++ b/sound/soc/sof/sof-acpi-dev.c @@ -0,0 +1,357 @@ +// SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause) +/* + * This file is provided under a dual BSD/GPLv2 license. When using or + * redistributing this file, you may do so under either license. + * + * Copyright(c) 2017 Intel Corporation. All rights reserved. + * + * Author: Liam Girdwood + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "sof-priv.h" + +/* platform specific devices */ +#include "intel/shim.h" +#include "intel/hda.h" + +#if IS_ENABLED(CONFIG_SND_SOC_SOF_HASWELL) +static struct sof_dev_desc sof_acpi_haswell_desc = { + .machines = snd_soc_acpi_intel_haswell_machines, + .resindex_lpe_base = 0, + .resindex_pcicfg_base = 1, + .resindex_imr_base = -1, + .irqindex_host_ipc = 0, + .nocodec_fw_filename = "intel/sof-hsw.ri", + .nocodec_tplg_filename = "intel/sof-hsw-nocodec.tplg" +}; +#endif + +#if IS_ENABLED(CONFIG_SND_SOC_SOF_BROADWELL) +static struct sof_dev_desc sof_acpi_broadwell_desc = { + .machines = snd_soc_acpi_intel_broadwell_machines, + .resindex_lpe_base = 0, + .resindex_pcicfg_base = 1, + .resindex_imr_base = -1, + .irqindex_host_ipc = 0, + .nocodec_fw_filename = "intel/sof-bdw.ri", + .nocodec_tplg_filename = "intel/sof-bdw-nocodec.tplg" +}; +#endif + +#if IS_ENABLED(CONFIG_SND_SOC_SOF_BAYTRAIL) + +/* BYTCR uses different IRQ index */ +static struct sof_dev_desc sof_acpi_baytrailcr_desc = { + .machines = snd_soc_acpi_intel_baytrail_machines, + .resindex_lpe_base = 0, + .resindex_pcicfg_base = 1, + .resindex_imr_base = 2, + .irqindex_host_ipc = 0, + .nocodec_fw_filename = "intel/sof-byt.ri", + .nocodec_tplg_filename = "intel/sof-byt-nocodec.tplg" +}; + +static struct sof_dev_desc sof_acpi_baytrail_desc = { + .machines = snd_soc_acpi_intel_baytrail_machines, + .resindex_lpe_base = 0, + .resindex_pcicfg_base = 1, + .resindex_imr_base = 2, + .irqindex_host_ipc = 5, + .nocodec_fw_filename = "intel/sof-byt.ri", + .nocodec_tplg_filename = "intel/sof-byt-nocodec.tplg" +}; + +static int is_byt_cr(struct device *dev) +{ + u32 bios_status; + int status; + + if (!IS_ENABLED(CONFIG_IOSF_MBI) || !iosf_mbi_available()) { + dev_info(dev, "IOSF_MBI not enabled - can't determine CPU variant\n"); + return -EIO; + } + + status = iosf_mbi_read(BT_MBI_UNIT_PMC, /* 0x04 PUNIT */ + MBI_REG_READ, /* 0x10 */ + 0x006, /* BIOS_CONFIG */ + &bios_status); + + if (status) { + dev_err(dev, "error: could not read PUNIT BIOS_CONFIG\n"); + return -EIO; + } + + /* bits 26:27 mirror PMIC options */ + bios_status = (bios_status >> 26) & 3; + + if (bios_status == 1 || bios_status == 3) { + dev_info(dev, "BYT-CR detected\n"); + return 1; + } + + dev_info(dev, "BYT-CR not detected\n"); + return 0; +} + +static struct sof_dev_desc sof_acpi_cherrytrail_desc = { + .machines = snd_soc_acpi_intel_cherrytrail_machines, + .resindex_lpe_base = 0, + .resindex_pcicfg_base = 1, + .resindex_imr_base = 2, + .irqindex_host_ipc = 5, + .nocodec_fw_filename = "intel/sof-cht.ri", + .nocodec_tplg_filename = "intel/sof-cht-nocodec.tplg" +}; +#endif + +static struct platform_device * + mfld_new_mach_data(struct snd_sof_pdata *sof_pdata) +{ + struct snd_soc_acpi_mach pmach; + struct device *dev = &sof_pdata->pdev->dev; + const struct snd_soc_acpi_mach *mach = sof_pdata->machine; + struct platform_device *pdev = NULL; + + memset(&pmach, 0, sizeof(pmach)); + memcpy((void *)pmach.id, mach->id, ACPI_ID_LEN); + pmach.drv_name = mach->drv_name; + + pdev = platform_device_register_data(dev, mach->drv_name, -1, + &pmach, sizeof(pmach)); + return pdev; +} + +struct sof_acpi_priv { + struct snd_sof_pdata *sof_pdata; + struct platform_device *pdev_pcm; +}; + +static void sof_acpi_fw_cb(const struct firmware *fw, void *context) +{ + struct sof_acpi_priv *priv = context; + struct snd_sof_pdata *sof_pdata = priv->sof_pdata; + const struct snd_soc_acpi_mach *mach = sof_pdata->machine; + struct device *dev = &sof_pdata->pdev->dev; + + sof_pdata->fw = fw; + if (!fw) { + dev_err(dev, "Cannot load firmware %s\n", + mach->sof_fw_filename); + return; + } + + /* register PCM and DAI driver */ + priv->pdev_pcm = + platform_device_register_data(dev, "sof-audio", -1, + sof_pdata, sizeof(*sof_pdata)); + if (IS_ERR(priv->pdev_pcm)) { + dev_err(dev, "Cannot register device sof-audio. Error %d\n", + (int)PTR_ERR(priv->pdev_pcm)); + } +} + +static const struct dev_pm_ops sof_acpi_pm = { + SET_SYSTEM_SLEEP_PM_OPS(snd_sof_suspend, snd_sof_resume) + SET_RUNTIME_PM_OPS(snd_sof_runtime_suspend, snd_sof_runtime_resume, + NULL) + .suspend_late = snd_sof_suspend_late, +}; + +static const struct sof_ops_table mach_ops[] = { +#if IS_ENABLED(CONFIG_SND_SOC_SOF_HASWELL) + {&sof_acpi_haswell_desc, &sof_hsw_ops}, +#endif +#if IS_ENABLED(CONFIG_SND_SOC_SOF_BROADWELL) + {&sof_acpi_broadwell_desc, &sof_bdw_ops}, +#endif +#if IS_ENABLED(CONFIG_SND_SOC_SOF_BAYTRAIL) + {&sof_acpi_baytrail_desc, &sof_byt_ops, mfld_new_mach_data}, + {&sof_acpi_baytrailcr_desc, &sof_byt_ops, mfld_new_mach_data}, + {&sof_acpi_cherrytrail_desc, &sof_cht_ops, mfld_new_mach_data}, +#endif +}; + +static struct snd_sof_dsp_ops * + sof_acpi_get_ops(const struct sof_dev_desc *d, + struct platform_device *(**new_mach_data) + (struct snd_sof_pdata *)) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(mach_ops); i++) { + if (d == mach_ops[i].desc) { + *new_mach_data = mach_ops[i].new_data; + return mach_ops[i].ops; + } + } + + /* not found */ + return NULL; +} + +static int sof_acpi_probe(struct platform_device *pdev) +{ + const struct acpi_device_id *id; + struct device *dev = &pdev->dev; + const struct sof_dev_desc *desc; + struct snd_soc_acpi_mach *mach; + struct snd_sof_pdata *sof_pdata; + struct sof_acpi_priv *priv; + struct snd_sof_dsp_ops *ops; + struct platform_device *(*new_mach_data)(struct snd_sof_pdata *pdata); + int ret = 0; + + dev_dbg(&pdev->dev, "ACPI DSP detected"); + + priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + sof_pdata = devm_kzalloc(dev, sizeof(*sof_pdata), GFP_KERNEL); + if (!sof_pdata) + return -ENOMEM; + + id = acpi_match_device(dev->driver->acpi_match_table, dev); + if (!id) + return -ENODEV; + + desc = (const struct sof_dev_desc *)id->driver_data; +#if IS_ENABLED(CONFIG_SND_SOC_SOF_BAYTRAIL) + if (desc == &sof_acpi_baytrail_desc && is_byt_cr(dev)) + desc = &sof_acpi_baytrailcr_desc; +#endif + + /* get ops for platform */ + new_mach_data = NULL; + ops = sof_acpi_get_ops(desc, &new_mach_data); + if (!ops) { + dev_err(dev, "error: no matching ACPI descriptor ops\n"); + return -ENODEV; + } + +#if IS_ENABLED(CONFIG_SND_SOC_SOF_FORCE_NOCODEC_MODE) + /* force nocodec mode */ + dev_warn(dev, "Force to use nocodec mode\n"); + mach = devm_kzalloc(dev, sizeof(*mach), GFP_KERNEL); + ret = sof_nocodec_setup(dev, sof_pdata, mach, desc); + if (ret < 0) + return ret; +#else + /* find machine */ + mach = snd_soc_acpi_find_machine(desc->machines); + if (!mach) { +#if IS_ENABLED(CONFIG_SND_SOC_SOF_NOCODEC) + /* fallback to nocodec mode */ + dev_warn(dev, "No matching ASoC machine driver found - using nocodec\n"); + mach = devm_kzalloc(dev, sizeof(*mach), GFP_KERNEL); + ret = sof_nocodec_setup(dev, sof_pdata, mach, desc); + if (ret < 0) + return ret; +#else + dev_err(dev, "No matching ASoC machine driver found - aborting probe\n"); + return -ENODEV; +#endif + } +#endif + + mach->pdata = ops; + mach->new_mach_data = (struct platform_device * + (*)(void *pdata)) new_mach_data; + + sof_pdata->machine = mach; + /* + * FIXME, this can't work for baytrail cr: + * sof_pdata->desc = (struct sof_dev_desc*) id->driver_data; + */ + sof_pdata->desc = desc; + priv->sof_pdata = sof_pdata; + sof_pdata->pdev = pdev; + dev_set_drvdata(&pdev->dev, priv); + + /* do we need to generate any machine plat data ? */ + if (mach->new_mach_data) + sof_pdata->pdev_mach = mach->new_mach_data(sof_pdata); + else + /* register machine driver, pass machine info as pdata */ + sof_pdata->pdev_mach = + platform_device_register_data(dev, mach->drv_name, -1, + (const void *)mach, + sizeof(*mach)); + if (IS_ERR(sof_pdata->pdev_mach)) + return PTR_ERR(sof_pdata->pdev_mach); + dev_dbg(dev, "created machine %s\n", + dev_name(&sof_pdata->pdev_mach->dev)); + + /* continue SST probing after firmware is loaded */ + dev_info(dev, "info: loading firmware %s\n", mach->sof_fw_filename); + ret = request_firmware_nowait(THIS_MODULE, true, mach->sof_fw_filename, + dev, GFP_KERNEL, priv, sof_acpi_fw_cb); + if (ret) { + platform_device_unregister(sof_pdata->pdev_mach); + dev_err(dev, "error: failed to load firmware %s\n", + mach->sof_fw_filename); + } + + return ret; +} + +static void sof_acpi_shutdown(struct platform_device *pdev) +{ + snd_sof_shutdown(&pdev->dev); +} + +static int sof_acpi_remove(struct platform_device *pdev) +{ + struct sof_acpi_priv *priv = dev_get_drvdata(&pdev->dev); + struct snd_sof_pdata *sof_pdata = priv->sof_pdata; + + platform_device_unregister(sof_pdata->pdev_mach); + if (!IS_ERR_OR_NULL(priv->pdev_pcm)) + platform_device_unregister(priv->pdev_pcm); + release_firmware(sof_pdata->fw); + + return 0; +} + +static const struct acpi_device_id sof_acpi_match[] = { +#if IS_ENABLED(CONFIG_SND_SOC_SOF_HASWELL) + { "INT33C8", (unsigned long)&sof_acpi_haswell_desc }, +#endif +#if IS_ENABLED(CONFIG_SND_SOC_SOF_BROADWELL) + { "INT3438", (unsigned long)&sof_acpi_broadwell_desc }, +#endif +#if IS_ENABLED(CONFIG_SND_SOC_SOF_BAYTRAIL) + { "80860F28", (unsigned long)&sof_acpi_baytrail_desc }, + { "808622A8", (unsigned long)&sof_acpi_cherrytrail_desc }, +#endif + { } +}; +MODULE_DEVICE_TABLE(acpi, sof_acpi_match); + +/* acpi_driver definition */ +static struct platform_driver snd_sof_acpi_driver = { + .probe = sof_acpi_probe, + .remove = sof_acpi_remove, + .shutdown = sof_acpi_shutdown, + .driver = { + .name = "sof-audio-acpi", + .pm = &sof_acpi_pm, + .acpi_match_table = ACPI_PTR(sof_acpi_match), + }, +}; +module_platform_driver(snd_sof_acpi_driver); + +MODULE_LICENSE("Dual BSD/GPL"); From 7bdc920308586b628829f7002ff93abaeee3ad11 Mon Sep 17 00:00:00 2001 From: Liam Girdwood Date: Mon, 8 Jan 2018 20:36:19 +0000 Subject: [PATCH 052/107] ASoC: SOF: Add PCI device support Add support for PCI based DSP devices. Signed-off-by: Liam Girdwood --- sound/soc/sof/sof-pci-dev.c | 341 ++++++++++++++++++++++++++++++++++++ 1 file changed, 341 insertions(+) create mode 100644 sound/soc/sof/sof-pci-dev.c diff --git a/sound/soc/sof/sof-pci-dev.c b/sound/soc/sof/sof-pci-dev.c new file mode 100644 index 00000000000000..7a2d48e0d34764 --- /dev/null +++ b/sound/soc/sof/sof-pci-dev.c @@ -0,0 +1,341 @@ +// SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause) +/* + * This file is provided under a dual BSD/GPLv2 license. When using or + * redistributing this file, you may do so under either license. + * + * Copyright(c) 2017 Intel Corporation. All rights reserved. + * + * Author: Liam Girdwood + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "sof-priv.h" + +/* platform specific devices */ +#include "intel/shim.h" +#include "intel/hda.h" + +#if IS_ENABLED(CONFIG_SND_SOC_SOF_APOLLOLAKE) +static struct sof_dev_desc bxt_desc = { + .machines = snd_soc_acpi_intel_bxt_machines, + .resindex_lpe_base = 0, + .resindex_pcicfg_base = -1, + .resindex_imr_base = -1, + .irqindex_host_ipc = -1, + .resindex_dma_base = -1, + .nocodec_fw_filename = "intel/sof-apl.ri", + .nocodec_tplg_filename = "intel/sof-apl-nocodec.tplg" +}; +#endif + +#if IS_ENABLED(CONFIG_SND_SOC_SOF_GEMINILAKE) +static struct sof_dev_desc glk_desc = { + .machines = snd_soc_acpi_intel_glk_machines, + .resindex_lpe_base = 0, + .resindex_pcicfg_base = -1, + .resindex_imr_base = -1, + .irqindex_host_ipc = -1, + .resindex_dma_base = -1, + .nocodec_fw_filename = "intel/sof-glk.ri", + .nocodec_tplg_filename = "intel/sof-glk-nocodec.tplg" +}; +#endif + +#if IS_ENABLED(CONFIG_SND_SOC_SOF_BAYTRAIL) +static struct snd_soc_acpi_mach sof_byt_machines[] = { + { + .id = "INT343A", + .drv_name = "edison", + .sof_fw_filename = "intel/sof-byt.ri", + .sof_tplg_filename = "intel/sof-byt.tplg", + .asoc_plat_name = "baytrail-pcm-audio", + }, + {} +}; + +static const struct sof_dev_desc byt_desc = { + .machines = sof_byt_machines, + .resindex_lpe_base = 3, /* IRAM, but subtract IRAM offset */ + .resindex_pcicfg_base = -1, + .resindex_imr_base = 0, + .irqindex_host_ipc = -1, + .resindex_dma_base = -1, + .nocodec_fw_filename = "intel/sof-byt.ri", + .nocodec_tplg_filename = "intel/sof-byt.tplg" +}; +#endif + +#if IS_ENABLED(CONFIG_SND_SOC_SOF_CANNONLAKE) +static const struct sof_dev_desc cnl_desc = { + .machines = snd_soc_acpi_intel_cnl_machines, + .resindex_lpe_base = 0, + .resindex_pcicfg_base = -1, + .resindex_imr_base = -1, + .irqindex_host_ipc = -1, + .resindex_dma_base = -1, + .nocodec_fw_filename = "intel/sof-cnl.ri", + .nocodec_tplg_filename = "intel/sof-cnl.tplg" +}; +#endif + +#if IS_ENABLED(CONFIG_SND_SOC_SOF_SKYLAKE) +static struct sof_dev_desc skl_desc = { + .machines = snd_soc_acpi_intel_skl_machines, + .resindex_lpe_base = 0, + .resindex_pcicfg_base = -1, + .resindex_imr_base = -1, + .irqindex_host_ipc = -1, + .resindex_dma_base = -1, + .nocodec_fw_filename = "intel/sof-skl.ri", + .nocodec_tplg_filename = "intel/sof-skl-nocodec.tplg" +}; +#endif + +#if IS_ENABLED(CONFIG_SND_SOC_SOF_KABYLAKE) +static struct sof_dev_desc kbl_desc = { + .machines = snd_soc_acpi_intel_kbl_machines, + .resindex_lpe_base = 0, + .resindex_pcicfg_base = -1, + .resindex_imr_base = -1, + .irqindex_host_ipc = -1, + .resindex_dma_base = -1, + .nocodec_fw_filename = "intel/sof-kbl.ri", + .nocodec_tplg_filename = "intel/sof-kbl-nocodec.tplg" +}; +#endif + +struct sof_pci_priv { + struct snd_sof_pdata *sof_pdata; + struct platform_device *pdev_pcm; +}; + +static void sof_pci_fw_cb(const struct firmware *fw, void *context) +{ + struct sof_pci_priv *priv = context; + struct snd_sof_pdata *sof_pdata = priv->sof_pdata; + const struct snd_soc_acpi_mach *mach = sof_pdata->machine; + struct device *dev = sof_pdata->dev; + + sof_pdata->fw = fw; + if (!fw) { + dev_err(dev, "Cannot load firmware %s\n", + mach->sof_fw_filename); + return; + } + + /* register PCM and DAI driver */ + priv->pdev_pcm = + platform_device_register_data(dev, "sof-audio", -1, + sof_pdata, sizeof(*sof_pdata)); + if (IS_ERR(priv->pdev_pcm)) { + dev_err(dev, "Cannot register device sof-audio. Error %d\n", + (int)PTR_ERR(priv->pdev_pcm)); + } +} + +static const struct dev_pm_ops sof_pci_pm = { + SET_SYSTEM_SLEEP_PM_OPS(snd_sof_suspend, snd_sof_resume) + SET_RUNTIME_PM_OPS(snd_sof_runtime_suspend, snd_sof_runtime_resume, + NULL) + .suspend_late = snd_sof_suspend_late, +}; + +static const struct sof_ops_table mach_ops[] = { +#if IS_ENABLED(CONFIG_SND_SOC_SOF_APOLLOLAKE) + {&bxt_desc, &sof_apl_ops}, +#endif +#if IS_ENABLED(CONFIG_SND_SOC_SOF_GEMINILAKE) + {&glk_desc, &sof_apl_ops}, +#endif +#if IS_ENABLED(CONFIG_SND_SOC_SOF_BAYTRAIL) + {&byt_desc, &sof_byt_ops}, +#endif +#if IS_ENABLED(CONFIG_SND_SOC_SOF_CANNONLAKE) + {&cnl_desc, &sof_cnl_ops}, +#endif +#if IS_ENABLED(CONFIG_SND_SOC_SOF_SKYLAKE) + {&skl_desc, &sof_apl_ops}, +#endif +#if IS_ENABLED(CONFIG_SND_SOC_SOF_KABYLAKE) + {&kbl_desc, &sof_apl_ops}, +#endif +}; + +static struct snd_sof_dsp_ops *sof_pci_get_ops(const struct sof_dev_desc *d) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(mach_ops); i++) { + if (d == mach_ops[i].desc) + return mach_ops[i].ops; + } + + /* not found */ + return NULL; +} + +static int sof_pci_probe(struct pci_dev *pci, + const struct pci_device_id *pci_id) +{ + struct device *dev = &pci->dev; + const struct sof_dev_desc *desc = + (const struct sof_dev_desc *)pci_id->driver_data; + struct snd_soc_acpi_mach *mach; + struct snd_sof_pdata *sof_pdata; + struct sof_pci_priv *priv; + struct snd_sof_dsp_ops *ops; + int ret = 0; + + dev_dbg(&pci->dev, "PCI DSP detected"); + + priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + pci_set_drvdata(pci, priv); + + sof_pdata = devm_kzalloc(dev, sizeof(*sof_pdata), GFP_KERNEL); + if (!sof_pdata) + return -ENOMEM; + + ret = pci_enable_device(pci); + if (ret < 0) + return ret; + + ret = pci_request_regions(pci, "Audio DSP"); + if (ret < 0) + return ret; + + /* get ops for platform */ + ops = sof_pci_get_ops(desc); + if (!ops) { + dev_err(dev, "error: no matching PCI descriptor ops\n"); + return -ENODEV; + } + +#if IS_ENABLED(CONFIG_SND_SOC_SOF_FORCE_NOCODEC_MODE) + /* force nocodec mode */ + dev_warn(dev, "Force to use nocodec mode\n"); + mach = devm_kzalloc(dev, sizeof(*mach), GFP_KERNEL); + ret = sof_nocodec_setup(dev, sof_pdata, mach, desc); + if (ret < 0) + return ret; +#else + /* find machine */ + mach = snd_soc_acpi_find_machine(desc->machines); + if (!mach) { +#if IS_ENABLED(CONFIG_SND_SOC_SOF_NOCODEC) + /* fallback to nocodec mode */ + dev_warn(dev, "No matching ASoC machine driver found - using nocodec\n"); + mach = devm_kzalloc(dev, sizeof(*mach), GFP_KERNEL); + ret = sof_nocodec_setup(dev, sof_pdata, mach, desc); + if (ret < 0) + return ret; +#else + dev_err(dev, "No matching ASoC machine driver found - aborting probe\n"); + return -ENODEV; +#endif + } +#endif + + mach->pdata = ops; + + sof_pdata->id = pci_id->device; + sof_pdata->name = pci_name(pci); + sof_pdata->machine = mach; + sof_pdata->desc = (struct sof_dev_desc *)pci_id->driver_data; + priv->sof_pdata = sof_pdata; + sof_pdata->pci = pci; + sof_pdata->dev = &pci->dev; + + /* register machine driver */ + sof_pdata->pdev_mach = + platform_device_register_data(dev, mach->drv_name, -1, + sof_pdata, sizeof(*sof_pdata)); + if (IS_ERR(sof_pdata->pdev_mach)) + return PTR_ERR(sof_pdata->pdev_mach); + dev_dbg(dev, "created machine %s\n", + dev_name(&sof_pdata->pdev_mach->dev)); + + /* continue probing after firmware is loaded */ + dev_info(dev, "info: loading firmware %s\n", mach->sof_fw_filename); + ret = request_firmware_nowait(THIS_MODULE, true, mach->sof_fw_filename, + dev, GFP_KERNEL, priv, sof_pci_fw_cb); + if (ret) { + platform_device_unregister(sof_pdata->pdev_mach); + dev_err(dev, "error: failed to load firmware %s\n", + mach->sof_fw_filename); + } + + return ret; +} + +static void sof_pci_shutdown(struct pci_dev *pci) +{ + snd_sof_shutdown(&pci->dev); +} + +static void sof_pci_remove(struct pci_dev *pci) +{ + struct sof_pci_priv *priv = pci_get_drvdata(pci); + struct snd_sof_pdata *sof_pdata = priv->sof_pdata; + + platform_device_unregister(sof_pdata->pdev_mach); + if (!IS_ERR_OR_NULL(priv->pdev_pcm)) + platform_device_unregister(priv->pdev_pcm); + release_firmware(sof_pdata->fw); + pci_release_regions(pci); +} + +/* PCI IDs */ +static const struct pci_device_id sof_pci_ids[] = { +#if IS_ENABLED(CONFIG_SND_SOC_SOF_APOLLOLAKE) + /* BXT-P & Apollolake */ + { PCI_DEVICE(0x8086, 0x5a98), + .driver_data = (unsigned long)&bxt_desc}, + { PCI_DEVICE(0x8086, 0x1a98), + .driver_data = (unsigned long)&bxt_desc}, +#endif +#if IS_ENABLED(CONFIG_SND_SOC_SOF_BAYTRAIL) + { PCI_DEVICE(0x8086, 0x119a), + .driver_data = (unsigned long)&byt_desc}, +#endif +#if IS_ENABLED(CONFIG_SND_SOC_SOF_CANNONLAKE) + { PCI_DEVICE(0x8086, 0x9dc8), + .driver_data = (unsigned long)&cnl_desc}, +#endif +#if IS_ENABLED(CONFIG_SND_SOC_SOF_KABYLAKE) + { PCI_DEVICE(0x8086, 0x9d71), + .driver_data = (unsigned long)&kbl_desc}, +#endif +#if IS_ENABLED(CONFIG_SND_SOC_SOF_SKYLAKE) + { PCI_DEVICE(0x8086, 0x9d70), + .driver_data = (unsigned long)&skl_desc}, +#endif + { 0, } +}; +MODULE_DEVICE_TABLE(pci, sof_pci_ids); + +/* pci_driver definition */ +static struct pci_driver snd_sof_pci_driver = { + .name = "sof-audio-pci", + .id_table = sof_pci_ids, + .probe = sof_pci_probe, + .remove = sof_pci_remove, + .shutdown = sof_pci_shutdown, + .driver = { + .pm = &sof_pci_pm, + }, +}; +module_pci_driver(snd_sof_pci_driver); + +MODULE_LICENSE("Dual BSD/GPL"); From bea036a44ca72bb8c69f8c94327edee4cad3b08a Mon Sep 17 00:00:00 2001 From: Liam Girdwood Date: Mon, 8 Jan 2018 20:40:02 +0000 Subject: [PATCH 053/107] ASoC: SOF: Add Build support for SOF core and Intel drivers Build SOF core and Intel-specific drivers. Signed-off-by: Liam Girdwood --- sound/soc/Kconfig | 1 + sound/soc/Makefile | 1 + sound/soc/sof/Kconfig | 44 +++++++++++++++++ sound/soc/sof/Makefile | 22 +++++++++ sound/soc/sof/intel/Kconfig | 94 ++++++++++++++++++++++++++++++++++++ sound/soc/sof/intel/Makefile | 15 ++++++ 6 files changed, 177 insertions(+) create mode 100644 sound/soc/sof/Kconfig create mode 100644 sound/soc/sof/Makefile create mode 100644 sound/soc/sof/intel/Kconfig create mode 100644 sound/soc/sof/intel/Makefile diff --git a/sound/soc/Kconfig b/sound/soc/Kconfig index 41af6b9cc3500b..2c0e6e5c7d7eb8 100644 --- a/sound/soc/Kconfig +++ b/sound/soc/Kconfig @@ -64,6 +64,7 @@ source "sound/soc/rockchip/Kconfig" source "sound/soc/samsung/Kconfig" source "sound/soc/sh/Kconfig" source "sound/soc/sirf/Kconfig" +source "sound/soc/sof/Kconfig" source "sound/soc/spear/Kconfig" source "sound/soc/sti/Kconfig" source "sound/soc/stm/Kconfig" diff --git a/sound/soc/Makefile b/sound/soc/Makefile index 8d92492183d28c..916aee8c5a8961 100644 --- a/sound/soc/Makefile +++ b/sound/soc/Makefile @@ -48,6 +48,7 @@ obj-$(CONFIG_SND_SOC) += rockchip/ obj-$(CONFIG_SND_SOC) += samsung/ obj-$(CONFIG_SND_SOC) += sh/ obj-$(CONFIG_SND_SOC) += sirf/ +obj-$(CONFIG_SND_SOC) += sof/ obj-$(CONFIG_SND_SOC) += spear/ obj-$(CONFIG_SND_SOC) += sti/ obj-$(CONFIG_SND_SOC) += stm/ diff --git a/sound/soc/sof/Kconfig b/sound/soc/sof/Kconfig new file mode 100644 index 00000000000000..0b2e7a5fd31e81 --- /dev/null +++ b/sound/soc/sof/Kconfig @@ -0,0 +1,44 @@ +config SND_SOC_SOF_PCI + tristate + +config SND_SOC_SOF_ACPI + tristate + +config SND_SOC_SOF_PLATFORM + tristate + + +config SND_SOC_SOF + tristate "Sound Open Firmware Support" + default m + select SND_SOC_TOPOLOGY + select SND_SOC_COMPRESS + help + This adds support for SOF + Say Y if you have such a device. + If unsure select "N". + +config SND_SOC_SOF_NOCODEC + tristate "SOF nocodec mode Support" + depends on SND_SOC_SOF + help + This adds support for a dummy/nocodec machine driver fallback + option if no known codec is detected. This is typically only + enabled for developers or devices where the sound card is + controlled externally + Say Y if you need this nocodec fallback option + If unsure select "N". + +config SND_SOC_SOF_FORCE_NOCODEC_MODE + tristate "SOF force nocodec Mode" + depends on SND_SOC_SOF_NOCODEC + help + This forces SOF to use dummy/nocodec as machine driver, even + though there is a codec detected on the real platform. This is + typically only enabled for developers for debug purposes, before + codec/machine driver is ready, or to exclude the impact of those + drivers + Say Y if you need this force nocodec mode option + If unsure select "N". + +source "sound/soc/sof/intel/Kconfig" diff --git a/sound/soc/sof/Makefile b/sound/soc/sof/Makefile new file mode 100644 index 00000000000000..ece93000a1974d --- /dev/null +++ b/sound/soc/sof/Makefile @@ -0,0 +1,22 @@ +# SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause) + +snd-sof-objs := core.o ops.o loader.o ipc.o pcm.o pm.o debug.o topology.o\ + control.o trace.o compressed.o +snd-sof-spi-objs := hw-spi.o + +snd-sof-pci-objs := sof-pci-dev.o +snd-sof-acpi-objs := sof-acpi-dev.o +snd-sof-platform-objs := sof-platform-dev.o +snd-sof-nocodec-objs := nocodec.o + +obj-$(CONFIG_SND_SOC_SOF) += snd-sof.o +obj-$(CONFIG_SND_SOC_SOF_NOCODEC) += snd-sof-nocodec.o + +obj-$(CONFIG_SND_SOC_SOF_ACPI) += sof-acpi-dev.o +obj-$(CONFIG_SND_SOC_SOF_PCI) += sof-pci-dev.o +obj-$(CONFIG_SND_SOC_SOF_SPI) += sof-spi-dev.o +obj-$(SND_SOC_SOF_PLATFORM) += sof-platform-dev.o + +obj-$(CONFIG_SND_SOC_SOF_SPIDSP) += snd-sof-spi.o + +obj-$(CONFIG_SND_SOC_SOF_INTEL) += intel/ diff --git a/sound/soc/sof/intel/Kconfig b/sound/soc/sof/intel/Kconfig new file mode 100644 index 00000000000000..2780750d6289d7 --- /dev/null +++ b/sound/soc/sof/intel/Kconfig @@ -0,0 +1,94 @@ +config SND_SOC_SOF_INTEL + tristate "SOF support for Intel audio DSPs" + depends on SND_SOC_SOF + depends on SND_DMA_SGBUF + select SND_SOC_INTEL_MACH + help + This adds support for Sound Open Firmware for Intel(R) platforms. + Say Y if you have such a device. + If unsure select "N". + +if SND_SOC_SOF_INTEL + +config SND_SOC_SOF_BAYTRAIL + tristate "SOF support for Baytrail, Braswell and Cherrytrail" + select SND_SOC_SOF_ACPI + select SND_SOC_ACPI_INTEL_MATCH + help + This adds support for Sound Open Firmware for Intel(R) platforms + using the Baytrail, Braswell or Cherrytrail processors. + Say Y if you have such a device. + If unsure select "N". + +config SND_SOC_SOF_HASWELL + tristate "SOF support for Haswell" + select SND_SOC_SOF_ACPI + select SND_SOC_ACPI_INTEL_MATCH + help + This adds support for Sound Open Firmware for Intel(R) platforms + using the Haswell processors. + Say Y if you have such a device. + If unsure select "N". + +config SND_SOC_SOF_BROADWELL + tristate "SOF support for Broadwell" + depends on SND_SOC_SOF_INTEL + select SND_SOC_SOF_ACPI + select SND_SOC_ACPI_INTEL_MATCH + help + This adds support for Sound Open Firmware for Intel(R) platforms + using the Broadwell processors. + Say Y if you have such a device. + If unsure select "N". + +config SND_SOC_SOF_APOLLOLAKE + tristate "SOF support for Apollolake" + select SND_SOC_SOF_HDA_COMMON + help + This adds support for Sound Open Firmware for Intel(R) platforms + using the Apollolake processors. + Say Y if you have such a device. + If unsure select "N". + +config SND_SOC_SOF_GEMINILAKE + tristate "SOF support for GeminiLake" + select SND_SOC_SOF_HDA_COMMON + help + This adds support for Sound Open Firmware for Intel(R) platforms + using the Geminilake processors. + Say Y if you have such a device. + If unsure select "N". + +config SND_SOC_SOF_CANNONLAKE + tristate "SOF support for Cannonlake" + select SND_SOC_SOF_HDA_COMMON + help + This adds support for Sound Open Firmware for Intel(R) platforms + using the Cannonlake processors. + Say Y if you have such a device. + If unsure select "N". + +config SND_SOC_SOF_KABYLAKE + tristate "SOF support for Kabylake" + select SND_SOC_SOF_HDA_COMMON + help + This adds support for Sound Open Firmware for Intel(R) platforms + using the Kabylake processors. + Say Y if you have such a device. + If unsure select "N". + +config SND_SOC_SOF_SKYLAKE + tristate "SOF support for Skylake" + select SND_SOC_SOF_HDA_COMMON + help + This adds support for Sound Open Firmware for Intel(R) platforms + using the Skylake processors. + Say Y if you have such a device. + If unsure select "N". + +config SND_SOC_SOF_HDA_COMMON + tristate + select SND_SOC_SOF_PCI + select SND_SOC_ACPI_INTEL_MATCH + +endif ## SND_SOC_SOF_INTEL diff --git a/sound/soc/sof/intel/Makefile b/sound/soc/sof/intel/Makefile new file mode 100644 index 00000000000000..dfeb4fcf217b90 --- /dev/null +++ b/sound/soc/sof/intel/Makefile @@ -0,0 +1,15 @@ +# SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause) + +ccflags-y += -DDEBUG + +snd-sof-intel-byt-objs := byt.o +snd-sof-intel-hsw-objs := hsw.o +snd-sof-intel-bdw-objs := bdw.o +snd-sof-intel-hda-common-objs := hda.o hda-loader.o hda-stream.o hda-trace.o \ + hda-dsp.o hda-ipc.o hda-ctrl.o hda-pcm.o \ + skl.o apl.o cnl.o + +obj-$(CONFIG_SND_SOC_SOF_BAYTRAIL) += snd-sof-intel-byt.o +obj-$(CONFIG_SND_SOC_SOF_HASWELL) += snd-sof-intel-hsw.o +obj-$(CONFIG_SND_SOC_SOF_BROADWELL) += snd-sof-intel-bdw.o +obj-$(CONFIG_SND_SOC_SOF_HDA_COMMON) += snd-sof-intel-hda-common.o From 2c48caba707c5e1c8bc6083d0f02b616c688f4fe Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Thu, 31 May 2018 17:35:22 -0500 Subject: [PATCH 054/107] ASoC: Intel: Kconfig: disable SST and legacy drivers when SOF is selected Make sure distros don't run into issues with multiple incompatible drivers selected for the same hardware. Signed-off-by: Pierre-Louis Bossart --- sound/soc/intel/Kconfig | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sound/soc/intel/Kconfig b/sound/soc/intel/Kconfig index 0caa1f4eb94d7d..08f00058700023 100644 --- a/sound/soc/intel/Kconfig +++ b/sound/soc/intel/Kconfig @@ -61,7 +61,7 @@ config SND_SOC_INTEL_HASWELL config SND_SOC_INTEL_BAYTRAIL tristate "Baytrail (legacy) Platforms" - depends on DMADEVICES && ACPI && SND_SST_ATOM_HIFI2_PLATFORM=n + depends on DMADEVICES && ACPI && SND_SST_ATOM_HIFI2_PLATFORM=n && SND_SOC_SOF_BAYTRAIL=n select SND_SOC_INTEL_SST select SND_SOC_INTEL_SST_ACPI select SND_SOC_INTEL_SST_FIRMWARE @@ -91,7 +91,7 @@ config SND_SST_ATOM_HIFI2_PLATFORM_PCI config SND_SST_ATOM_HIFI2_PLATFORM_ACPI tristate "ACPI HiFi2 (Baytrail, Cherrytrail) Platforms" default ACPI - depends on X86 && ACPI + depends on X86 && ACPI && SND_SOC_SOF_BAYTRAIL=n select SND_SST_IPC_ACPI select SND_SST_ATOM_HIFI2_PLATFORM select SND_SOC_ACPI_INTEL_MATCH From 5119fdb26647c81b736cd760dccc8aa596d819e9 Mon Sep 17 00:00:00 2001 From: Rander Wang Date: Fri, 25 May 2018 16:29:46 +0800 Subject: [PATCH 055/107] ASoC: SOF: refine dapm route loading in sof Some virtual routes and widgets may been added in topology to reusing machine driver in Linux kernel. For virtual routes, both sink and source are not buffer. Since only buffer linked to component is supported by FW, others are reported as error, add check in route function, do not send it to FW when both source and sink are not buffer Signed-off-by: Rander Wang Signed-off-by: Pierre-Louis Bossart --- sound/soc/sof/topology.c | 51 ++++++++++++++++++++++++++-------------- 1 file changed, 33 insertions(+), 18 deletions(-) diff --git a/sound/soc/sof/topology.c b/sound/soc/sof/topology.c index 5fe74fa41e423f..b98078fe49987c 100644 --- a/sound/soc/sof/topology.c +++ b/sound/soc/sof/topology.c @@ -1600,7 +1600,7 @@ static int sof_route_load(struct snd_soc_component *scomp, int index, struct snd_sof_widget *source_swidget, *sink_swidget; struct snd_sof_pcm *spcm; struct sof_ipc_reply reply; - int ret; + int ret = 0; memset(&connect, 0, sizeof(connect)); connect.hdr.size = sizeof(connect); @@ -1640,24 +1640,39 @@ static int sof_route_load(struct snd_soc_component *scomp, int index, connect.sink_id = sink_swidget->comp_id; - ret = sof_ipc_tx_message(sdev->ipc, - connect.hdr.cmd, &connect, sizeof(connect), - &reply, sizeof(reply)); - - /* check IPC return value */ - if (ret < 0) { - dev_err(sdev->dev, "error: failed to add route sink %s control %s source %s\n", - route->sink, route->control ? route->control : "none", - route->source); - return ret; - } + /* Some virtual routes and widgets may been added in topology for + * compatibility. For virtual routes, both sink and source are not + * buffer. Since only buffer linked to component is supported by + * FW, others are reported as error, add check in route function, + * do not send it to FW when both source and sink are not buffer + */ + if (source_swidget->id != snd_soc_dapm_buffer && + sink_swidget->id != snd_soc_dapm_buffer) { + dev_dbg(sdev->dev, "warning: neither Linked source component %s nor sink component %s is of buffer type, ignoring link\n", + route->source, route->sink); + } else { + ret = sof_ipc_tx_message(sdev->ipc, + connect.hdr.cmd, + &connect, sizeof(connect), + &reply, sizeof(reply)); + + /* check IPC return value */ + if (ret < 0) { + dev_err(sdev->dev, "error: failed to add route sink %s control %s source %s\n", + route->sink, + route->control ? route->control : "none", + route->source); + return ret; + } - /* check IPC reply */ - if (reply.error < 0) { - dev_err(sdev->dev, "error: DSP failed to add route sink %s control %s source %s result %d\n", - route->sink, route->control ? route->control : "none", - route->source, reply.error); - //return ret; // TODO: + /* check IPC reply */ + if (reply.error < 0) { + dev_err(sdev->dev, "error: DSP failed to add route sink %s control %s source %s result %d\n", + route->sink, + route->control ? route->control : "none", + route->source, reply.error); + return reply.error; + } } return ret; From 92e8a5070497bd2ef11633b89adbb5bda31584b8 Mon Sep 17 00:00:00 2001 From: Pan Xiuli Date: Fri, 25 May 2018 13:34:43 +0800 Subject: [PATCH 056/107] ASoC: SOF: parse Xtensa exception causes Xtensa DSP panic will have a EXCCAUSE contains the exception cause. Parse the EXCCAUSE into readable message from Xtensa Instruction Set Architecture (ISA) Reference Manual Signed-off-by: Pan Xiuli --- sound/soc/sof/core.c | 74 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 74 insertions(+) diff --git a/sound/soc/sof/core.c b/sound/soc/sof/core.c index e15c393cd57476..9e0e34075f4f07 100644 --- a/sound/soc/sof/core.c +++ b/sound/soc/sof/core.c @@ -140,12 +140,86 @@ static const struct sof_panic_msg panic_msg[] = { {SOF_IPC_PANIC_IDLE, "can't enter idle"}, }; +struct sof_exception_cause { + u32 id; + const char *msg; + const char *description; +}; + +/* From 4.4.1.5 table 4-64 Exception Causes of + * Xtensa Instruction Set Architecture (ISA) Reference Manual + */ +static const struct sof_exception_cause xtensa_exception_causes[] = { + {0, "IllegalInstructionCause", "Illegal instruction"}, + {1, "SyscallCause", "SYSCALL instruction"}, + {2, "InstructionFetchErrorCause", + "Processor internal physical address or data error during instruction fetch"}, + {3, "LoadStoreErrorCause", + "Processor internal physical address or data error during load or store"}, + {4, "Level1InterruptCause", + "Level-1 interrupt as indicated by set level-1 bits in the INTERRUPT register"}, + {5, "AllocaCause", + "MOVSP instruction, if caller’s registers are not in the register file"}, + {6, "IntegerDivideByZeroCause", + "QUOS, QUOU, REMS, or REMU divisor operand is zero"}, + {8, "PrivilegedCause", + "Attempt to execute a privileged operation when CRING ? 0"}, + {9, "LoadStoreAlignmentCause", "Load or store to an unaligned address"}, + {12, "InstrPIFDataErrorCause", + "PIF data error during instruction fetch"}, + {13, "LoadStorePIFDataErrorCause", + "Synchronous PIF data error during LoadStore access"}, + {14, "InstrPIFAddrErrorCause", + "PIF address error during instruction fetch"}, + {15, "LoadStorePIFAddrErrorCause", + "Synchronous PIF address error during LoadStore access"}, + {16, "InstTLBMissCause", "Error during Instruction TLB refill"}, + {17, "InstTLBMultiHitCause", + "Multiple instruction TLB entries matched"}, + {18, "InstFetchPrivilegeCause", + "An instruction fetch referenced a virtual address at a ring level less than CRING"}, + {20, "InstFetchProhibitedCause", + "An instruction fetch referenced a page mapped with an attribute that does not permit instruction fetch"}, + {24, "LoadStoreTLBMissCause", + "Error during TLB refill for a load or store"}, + {25, "LoadStoreTLBMultiHitCause", + "Multiple TLB entries matched for a load or store"}, + {26, "LoadStorePrivilegeCause", + "A load or store referenced a virtual address at a ring level less than CRING"}, + {28, "LoadProhibitedCause", + "A load referenced a page mapped with an attribute that does not permit loads"}, + {32, "Coprocessor0Disabled", + "Coprocessor 0 instruction when cp0 disabled"}, + {33, "Coprocessor1Disabled", + "Coprocessor 1 instruction when cp1 disabled"}, + {34, "Coprocessor2Disabled", + "Coprocessor 2 instruction when cp2 disabled"}, + {35, "Coprocessor3Disabled", + "Coprocessor 3 instruction when cp3 disabled"}, + {36, "Coprocessor4Disabled", + "Coprocessor 4 instruction when cp4 disabled"}, + {37, "Coprocessor5Disabled", + "Coprocessor 5 instruction when cp5 disabled"}, + {38, "Coprocessor6Disabled", + "Coprocessor 6 instruction when cp6 disabled"}, + {39, "Coprocessor7Disabled", + "Coprocessor 7 instruction when cp7 disabled"}, +}; + /* only need xtensa atm */ static void sof_arch_dsp_oops(struct snd_sof_dev *sdev, void *oops) { struct sof_ipc_dsp_oops_xtensa *xoops = oops; + int i; dev_err(sdev->dev, "error: DSP Firmware Oops\n"); + for (i = 0; i < ARRAY_SIZE(xtensa_exception_causes); i++) { + if (xtensa_exception_causes[i].id == xoops->exccause) { + dev_err(sdev->dev, "error: Exception Cause: %s, %s\n", + xtensa_exception_causes[i].msg, + xtensa_exception_causes[i].description); + } + } dev_err(sdev->dev, "EXCCAUSE 0x%8.8x EXCVADDR 0x%8.8x PS 0x%8.8x SAR 0x%8.8x\n", xoops->exccause, xoops->excvaddr, xoops->ps, xoops->sar); dev_err(sdev->dev, "EPC1 0x%8.8x EPC2 0x%8.8x EPC3 0x%8.8x EPC4 0x%8.8x", From 507d6f9bbcfe98678d3b8c5577a0f04866f6a654 Mon Sep 17 00:00:00 2001 From: Ranjani Sridharan Date: Sat, 26 May 2018 23:29:09 -0700 Subject: [PATCH 057/107] ASoC: uapi: sof: Add ipc config params and topology tokens for DMIC DAI type This patch adds the ipc config params and the topology tokens for SOF DMIC DAI type. Signed-off-by: Ranjani Sridharan --- include/uapi/sound/sof-ipc.h | 25 +++++++++++++++++++++++-- include/uapi/sound/sof-topology.h | 19 +++++++++++++++++++ 2 files changed, 42 insertions(+), 2 deletions(-) diff --git a/include/uapi/sound/sof-ipc.h b/include/uapi/sound/sof-ipc.h index 9b33691ac8a94b..d1c972abd03357 100644 --- a/include/uapi/sound/sof-ipc.h +++ b/include/uapi/sound/sof-ipc.h @@ -243,10 +243,31 @@ struct sof_ipc_dai_hda_params { /* TODO */ } __attribute__((packed)); +/* PDM controller configuration parameters */ +struct sof_ipc_dai_dmic_pdm_ctrl { + uint16_t id; /* pdm controller id */ + uint16_t enable_mic_a; /* Use A (left) channel mic (0 or 1)*/ + uint16_t enable_mic_b; /* Use B (right) channel mic (0 or 1)*/ + uint16_t polarity_mic_a; /* Optionally invert mic A signal (0 or 1) */ + uint16_t polarity_mic_b; /* Optionally invert mic B signal (0 or 1) */ + uint16_t clk_edge; /* Optionally swap data clock edge (0 or 1) */ + uint16_t skew; /* Adjust PDM data sampling vs. clock (0..15) */ +} __attribute__((packed)); + /* DMIC Configuration Request - SOF_IPC_DAI_DMIC_CONFIG */ struct sof_ipc_dai_dmic_params { - struct sof_ipc_hdr hdr; - /* TODO */ + uint32_t driver_ipc_version; + uint32_t pdmclk_min; /* Minimum microphone clock in Hz (100000..N) */ + uint32_t pdmclk_max; /* Maximum microphone clock in Hz (min...N) */ + uint32_t fifo_fs_a; /* FIFO A sample rate in Hz (8000..96000) */ + uint32_t fifo_fs_b; /* FIFO B sample rate in Hz (8000..96000) */ + uint16_t fifo_bits_a; /* FIFO A word length (16 or 24) */ + uint16_t fifo_bits_b; /* FIFO B word length (16 or 24) */ + uint16_t duty_min; /* Min. mic clock duty cycle in % (20..80) */ + uint16_t duty_max; /* Max. mic clock duty cycle in % (min..80) */ + uint32_t num_pdm_active; /* Number of active pdm controllers */ + /* variable number of pdm controller config */ + struct sof_ipc_dai_dmic_pdm_ctrl pdm[0]; } __attribute__((packed)); /* general purpose DAI configuration */ diff --git a/include/uapi/sound/sof-topology.h b/include/uapi/sound/sof-topology.h index 62c3f242509701..5941c4cb8623a3 100644 --- a/include/uapi/sound/sof-topology.h +++ b/include/uapi/sound/sof-topology.h @@ -73,4 +73,23 @@ #define SOF_TKN_INTEL_SSP_BCLK_KEEP_ACTIVE 501 #define SOF_TKN_INTEL_SSP_FS_KEEP_ACTIVE 502 +/* DMIC */ +#define SOF_TKN_INTEL_DMIC_DRIVER_VERSION 600 +#define SOF_TKN_INTEL_DMIC_CLK_MIN 601 +#define SOF_TKN_INTEL_DMIC_CLK_MAX 602 +#define SOF_TKN_INTEL_DMIC_DUTY_MIN 603 +#define SOF_TKN_INTEL_DMIC_DUTY_MAX 604 +#define SOF_TKN_INTEL_DMIC_NUM_PDM_ACTIVE 605 +#define SOF_TKN_INTEL_DMIC_SAMPLE_RATE 608 +#define SOF_TKN_INTEL_DMIC_FIFO_WORD_LENGTH 609 + +/* DMIC PDM */ +#define SOF_TKN_INTEL_DMIC_PDM_CTRL_ID 700 +#define SOF_TKN_INTEL_DMIC_PDM_MIC_A_Enable 701 +#define SOF_TKN_INTEL_DMIC_PDM_MIC_B_Enable 702 +#define SOF_TKN_INTEL_DMIC_PDM_POLARITY_A 703 +#define SOF_TKN_INTEL_DMIC_PDM_POLARITY_B 704 +#define SOF_TKN_INTEL_DMIC_PDM_CLK_EDGE 705 +#define SOF_TKN_INTEL_DMIC_PDM_SKEW 706 + #endif From 49427039740abef976f5a0a89784f1009febc420 Mon Sep 17 00:00:00 2001 From: Ranjani Sridharan Date: Sat, 26 May 2018 23:29:10 -0700 Subject: [PATCH 058/107] ASoC: SOF: Add support for parsing DMIC specific tokens from topology Update dmic specific tokens in topology parser and configure ipc params for DMIC DAI type. Signed-off-by: Ranjani Sridharan --- sound/soc/sof/topology.c | 231 +++++++++++++++++++++++++++++++++++++-- 1 file changed, 219 insertions(+), 12 deletions(-) diff --git a/sound/soc/sof/topology.c b/sound/soc/sof/topology.c index b98078fe49987c..a2c986e4cad094 100644 --- a/sound/soc/sof/topology.c +++ b/sound/soc/sof/topology.c @@ -285,6 +285,15 @@ static int get_token_u32(void *elem, void *object, u32 offset, u32 size) return 0; } +static int get_token_u16(void *elem, void *object, u32 offset, u32 size) +{ + struct snd_soc_tplg_vendor_value_elem *velem = elem; + u16 *val = object + offset; + + *val = velem->value; + return 0; +} + static int get_token_comp_format(void *elem, void *object, u32 offset, u32 size) { struct snd_soc_tplg_vendor_string_elem *velem = elem; @@ -414,6 +423,69 @@ static const struct sof_topology_token ssp_tokens[] = { /* DMIC */ static const struct sof_topology_token dmic_tokens[] = { + {SOF_TKN_INTEL_DMIC_DRIVER_VERSION, + SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32, + offsetof(struct sof_ipc_dai_dmic_params, driver_ipc_version), + 0}, + {SOF_TKN_INTEL_DMIC_CLK_MIN, + SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32, + offsetof(struct sof_ipc_dai_dmic_params, pdmclk_min), 0}, + {SOF_TKN_INTEL_DMIC_CLK_MAX, + SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32, + offsetof(struct sof_ipc_dai_dmic_params, pdmclk_max), 0}, + {SOF_TKN_INTEL_DMIC_SAMPLE_RATE, + SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32, + offsetof(struct sof_ipc_dai_dmic_params, fifo_fs_a), 0}, + {SOF_TKN_INTEL_DMIC_DUTY_MIN, + SND_SOC_TPLG_TUPLE_TYPE_SHORT, get_token_u16, + offsetof(struct sof_ipc_dai_dmic_params, duty_min), 0}, + {SOF_TKN_INTEL_DMIC_DUTY_MAX, + SND_SOC_TPLG_TUPLE_TYPE_SHORT, get_token_u16, + offsetof(struct sof_ipc_dai_dmic_params, duty_max), 0}, + {SOF_TKN_INTEL_DMIC_NUM_PDM_ACTIVE, + SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32, + offsetof(struct sof_ipc_dai_dmic_params, + num_pdm_active), 0}, + {SOF_TKN_INTEL_DMIC_FIFO_WORD_LENGTH, + SND_SOC_TPLG_TUPLE_TYPE_SHORT, get_token_u16, + offsetof(struct sof_ipc_dai_dmic_params, fifo_bits_a), 0}, +}; + +/* + * DMIC PDM Tokens + * SOF_TKN_INTEL_DMIC_PDM_CTRL_ID should be the first token + * as it increments the index while parsing the array of pdm tokens + * and determines the correct offset + */ +static const struct sof_topology_token dmic_pdm_tokens[] = { + {SOF_TKN_INTEL_DMIC_PDM_CTRL_ID, + SND_SOC_TPLG_TUPLE_TYPE_SHORT, get_token_u16, + offsetof(struct sof_ipc_dai_dmic_params, pdm[0]), + 0}, + {SOF_TKN_INTEL_DMIC_PDM_MIC_A_Enable, + SND_SOC_TPLG_TUPLE_TYPE_SHORT, get_token_u16, + offsetof(struct sof_ipc_dai_dmic_params, pdm[0]), + 0}, + {SOF_TKN_INTEL_DMIC_PDM_MIC_B_Enable, + SND_SOC_TPLG_TUPLE_TYPE_SHORT, get_token_u16, + offsetof(struct sof_ipc_dai_dmic_params, pdm[0]), + 0}, + {SOF_TKN_INTEL_DMIC_PDM_POLARITY_A, + SND_SOC_TPLG_TUPLE_TYPE_SHORT, get_token_u16, + offsetof(struct sof_ipc_dai_dmic_params, pdm[0]), + 0}, + {SOF_TKN_INTEL_DMIC_PDM_POLARITY_B, + SND_SOC_TPLG_TUPLE_TYPE_SHORT, get_token_u16, + offsetof(struct sof_ipc_dai_dmic_params, pdm[0]), + 0}, + {SOF_TKN_INTEL_DMIC_PDM_CLK_EDGE, + SND_SOC_TPLG_TUPLE_TYPE_SHORT, get_token_u16, + offsetof(struct sof_ipc_dai_dmic_params, pdm[0]), + 0}, + {SOF_TKN_INTEL_DMIC_PDM_SKEW, + SND_SOC_TPLG_TUPLE_TYPE_SHORT, get_token_u16, + offsetof(struct sof_ipc_dai_dmic_params, pdm[0]), + 0}, }; /* HDA */ @@ -480,6 +552,65 @@ static void sof_parse_string_tokens(struct snd_soc_component *scomp, } } +/* helper function to parse DMIC pdm specific tokens */ +static void sof_parse_pdm_tokens(struct snd_soc_component *scomp, + struct snd_soc_tplg_vendor_value_elem *elem, + void *object, + struct sof_topology_token token) +{ + size_t size = sizeof(struct sof_ipc_dai_dmic_pdm_ctrl); + u32 offset = token.offset; + /* pdm_index determines the index in pdm[] to populate the token */ + static int pdm_index; + /* determines the offset of the item in sof_ipc_dai_dmic_pdm_ctrl */ + u32 pdm_offset; + + switch (token.token) { + case SOF_TKN_INTEL_DMIC_PDM_CTRL_ID: + token.get_token(elem, object, offset + pdm_index * size, + token.size); + /* increment the pdm_index */ + pdm_index++; + return; + case SOF_TKN_INTEL_DMIC_PDM_MIC_A_Enable: + pdm_offset = offsetof(struct sof_ipc_dai_dmic_pdm_ctrl, + enable_mic_a); + break; + case SOF_TKN_INTEL_DMIC_PDM_MIC_B_Enable: + pdm_offset = offsetof(struct sof_ipc_dai_dmic_pdm_ctrl, + enable_mic_b); + break; + case SOF_TKN_INTEL_DMIC_PDM_POLARITY_A: + pdm_offset = offsetof(struct sof_ipc_dai_dmic_pdm_ctrl, + polarity_mic_a); + break; + case SOF_TKN_INTEL_DMIC_PDM_POLARITY_B: + pdm_offset = offsetof(struct sof_ipc_dai_dmic_pdm_ctrl, + polarity_mic_b); + break; + case SOF_TKN_INTEL_DMIC_PDM_CLK_EDGE: + pdm_offset = offsetof(struct sof_ipc_dai_dmic_pdm_ctrl, + clk_edge); + break; + case SOF_TKN_INTEL_DMIC_PDM_SKEW: + pdm_offset = offsetof(struct sof_ipc_dai_dmic_pdm_ctrl, + skew); + break; + default: + break; + } + + /* + * offset: address pointing to pdm[0] in dmic_params + * pdm_index - 1: index of the array item to be populated + * pdm_offset: item offset in sof_ipc_dai_dmic_pdm_ctrl + * indexed by pdm_index + */ + token.get_token(elem, object, + offset + (pdm_index - 1) * size + pdm_offset, + token.size); +} + static void sof_parse_word_tokens(struct snd_soc_component *scomp, void *object, const struct sof_topology_token *tokens, @@ -496,7 +627,8 @@ static void sof_parse_word_tokens(struct snd_soc_component *scomp, /* search for token */ for (j = 0; j < count; j++) { /* match token type */ - if (tokens[j].type != SND_SOC_TPLG_TUPLE_TYPE_WORD) + if (!(tokens[j].type == SND_SOC_TPLG_TUPLE_TYPE_WORD || + tokens[j].type == SND_SOC_TPLG_TUPLE_TYPE_SHORT)) continue; /* match token id */ @@ -504,8 +636,24 @@ static void sof_parse_word_tokens(struct snd_soc_component *scomp, continue; /* matched - now load token */ - tokens[j].get_token(elem, object, tokens[j].offset, - tokens[j].size); + switch (tokens[j].token) { + case SOF_TKN_INTEL_DMIC_PDM_CTRL_ID: + case SOF_TKN_INTEL_DMIC_PDM_MIC_A_Enable: + case SOF_TKN_INTEL_DMIC_PDM_MIC_B_Enable: + case SOF_TKN_INTEL_DMIC_PDM_POLARITY_A: + case SOF_TKN_INTEL_DMIC_PDM_POLARITY_B: + case SOF_TKN_INTEL_DMIC_PDM_CLK_EDGE: + case SOF_TKN_INTEL_DMIC_PDM_SKEW: + /* parse pdm specific tokens */ + sof_parse_pdm_tokens(scomp, elem, object, + tokens[j]); + break; + default: + tokens[j].get_token(elem, object, + tokens[j].offset, + tokens[j].size); + break; + } } } } @@ -1400,16 +1548,15 @@ static int sof_link_dmic_load(struct snd_soc_component *scomp, int index, { struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp); struct snd_soc_tplg_private *private = &cfg->priv; + struct sof_ipc_dai_config *ipc_config; struct sof_ipc_reply reply; - u32 size = sizeof(*config); - int ret; + u32 size; + int ret, j; - /* init IPC */ memset(&config->dmic, 0, sizeof(struct sof_ipc_dai_dmic_params)); - config->hdr.size = size; - /* get any bespoke DAI tokens */ - ret = sof_parse_tokens(scomp, config, dmic_tokens, + /* get DMIC tokens */ + ret = sof_parse_tokens(scomp, &config->dmic, dmic_tokens, ARRAY_SIZE(dmic_tokens), private->array, private->size); if (ret != 0) { @@ -1418,18 +1565,78 @@ static int sof_link_dmic_load(struct snd_soc_component *scomp, int index, return ret; } - dev_dbg(sdev->dev, "tplg: config DMIC%d fmt 0x%x\n", - config->id, config->format); + /* + * allocate memory for common dai params, dmic params + * and dmic pdm controller params + */ + ipc_config = kzalloc(sizeof(*config) + + sizeof(struct sof_ipc_dai_dmic_pdm_ctrl) * + config->dmic.num_pdm_active, + GFP_KERNEL); + if (!ipc_config) { + dev_err(sdev->dev, "error: allocating memory for config\n"); + return -ENOMEM; + } + + /* copy the common dai config and dmic params */ + memcpy(ipc_config, config, sizeof(*config)); + + /* get DMIC PDM tokens */ + ret = sof_parse_tokens(scomp, &ipc_config->dmic, dmic_pdm_tokens, + ARRAY_SIZE(dmic_pdm_tokens), private->array, + private->size); + if (ret != 0) { + dev_err(sdev->dev, "error: parse dmic pdm tokens failed %d\n", + private->size); + kfree(ipc_config); + return ret; + } + + /* set IPC header size */ + size = sizeof(*ipc_config); + ipc_config->hdr.size = size; + + /* debug messages */ + dev_dbg(sdev->dev, "tplg: config DMIC%d driver version %d\n", + ipc_config->id, ipc_config->dmic.driver_ipc_version); + dev_dbg(sdev->dev, "pdmclk_min %d pdm_clkmax %d duty_min %hd\n", + ipc_config->dmic.pdmclk_min, ipc_config->dmic.pdmclk_max, + ipc_config->dmic.duty_min); + dev_dbg(sdev->dev, "duty_max %hd fifo_fs %d num_pdms active %d\n", + ipc_config->dmic.duty_max, ipc_config->dmic.fifo_fs_a, + ipc_config->dmic.num_pdm_active); + dev_dbg(sdev->dev, "fifo word length %hd\n", + ipc_config->dmic.fifo_bits_a); + + for (j = 0; j < ipc_config->dmic.num_pdm_active; j++) { + dev_dbg(sdev->dev, "pdm %hd mic a %hd mic b %hd\n", + ipc_config->dmic.pdm[j].id, + ipc_config->dmic.pdm[j].enable_mic_a, + ipc_config->dmic.pdm[j].enable_mic_b); + dev_dbg(sdev->dev, "pdm %hd polarity a %hd polarity b %hd\n", + ipc_config->dmic.pdm[j].id, + ipc_config->dmic.pdm[j].polarity_mic_a, + ipc_config->dmic.pdm[j].polarity_mic_b); + dev_dbg(sdev->dev, "pdm %hd clk_edge %hd skew %hd\n", + ipc_config->dmic.pdm[j].id, + ipc_config->dmic.pdm[j].clk_edge, + ipc_config->dmic.pdm[j].skew); + } + + /* TODO: check if fifo_b word length is needed */ + ipc_config->dmic.fifo_bits_b = ipc_config->dmic.fifo_bits_a; /* send message to DSP */ ret = sof_ipc_tx_message(sdev->ipc, - config->hdr.cmd, config, size, &reply, + ipc_config->hdr.cmd, ipc_config, size, &reply, sizeof(reply)); if (ret < 0) dev_err(sdev->dev, "error: failed to set DAI config for DMIC%d\n", config->id); + kfree(ipc_config); + return ret; } From 3f3abba33457f77940faf902178122b71f1f20dc Mon Sep 17 00:00:00 2001 From: Ranjani Sridharan Date: Sat, 26 May 2018 23:29:11 -0700 Subject: [PATCH 059/107] ASoC: SOF: support DMIC DAI type during link fixup Handle DMIC configuration errors during link fixup. Signed-off-by: Ranjani Sridharan --- sound/soc/sof/pcm.c | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/sound/soc/sof/pcm.c b/sound/soc/sof/pcm.c index 32d114ee748f7e..50a95fdaae29aa 100644 --- a/sound/soc/sof/pcm.c +++ b/sound/soc/sof/pcm.c @@ -548,7 +548,15 @@ static int sof_pcm_dai_link_fixup(struct snd_soc_pcm_runtime *rtd, break; case SOF_DAI_INTEL_DMIC: - /* fallthrough */ + /* DMIC only supports 16 or 32 bit formats */ + if (dai->comp_dai.config.frame_fmt == SOF_IPC_FRAME_S24_4LE) { + dev_err(sdev->dev, + "error: invalid fmt %d for DAI type %d\n", + dai->comp_dai.config.frame_fmt, + dai->dai_config.type); + } + /* TODO: add any other DMIC specific fixups */ + break; case SOF_DAI_INTEL_HDA: /* fallthrough */ default: From f1ebcc29c3d8e3e1c823c796c6c4598453153fa5 Mon Sep 17 00:00:00 2001 From: Keyon Jie Date: Tue, 15 May 2018 23:27:28 +0800 Subject: [PATCH 060/107] [WORKAROUND] ASoC: SOF: start HDA DMA at hw_params() stage and remove stream_trigger() It require HDA DMA to be available before trigger_start(), Here move host side DMA start to earlier stage(hw_params) and remove stream_trigger() as it is no need anymore. This fix will help to remove HDA DMA workaround fix inside firmware. TODO: revisit this patch for SOF 1.2, this removes the ability to use the RUN bits for multi-stream synchronization and is likely not an acceptable long-term solution Signed-off-by: Keyon Jie Signed-off-by: Pierre-Louis Bossart --- sound/soc/sof/intel/apl.c | 1 - sound/soc/sof/intel/cnl.c | 1 - sound/soc/sof/intel/hda-pcm.c | 23 +++++++++++++++-------- sound/soc/sof/intel/skl.c | 1 - sound/soc/sof/pcm.c | 4 ---- sound/soc/sof/sof-priv.h | 5 ----- 6 files changed, 15 insertions(+), 20 deletions(-) diff --git a/sound/soc/sof/intel/apl.c b/sound/soc/sof/intel/apl.c index d36e30d2a8b388..98274b02943cf4 100644 --- a/sound/soc/sof/intel/apl.c +++ b/sound/soc/sof/intel/apl.c @@ -80,7 +80,6 @@ struct snd_sof_dsp_ops sof_apl_ops = { .host_stream_open = hda_dsp_pcm_open, .host_stream_close = hda_dsp_pcm_close, .host_stream_hw_params = hda_dsp_pcm_hw_params, - .host_stream_trigger = hda_dsp_pcm_trigger, /* firmware loading */ .load_firmware = hda_dsp_cl_load_fw, diff --git a/sound/soc/sof/intel/cnl.c b/sound/soc/sof/intel/cnl.c index 80d6cc772fd74a..8f9ad191f0ce60 100644 --- a/sound/soc/sof/intel/cnl.c +++ b/sound/soc/sof/intel/cnl.c @@ -208,7 +208,6 @@ struct snd_sof_dsp_ops sof_cnl_ops = { .host_stream_open = hda_dsp_pcm_open, .host_stream_close = hda_dsp_pcm_close, .host_stream_hw_params = hda_dsp_pcm_hw_params, - .host_stream_trigger = hda_dsp_pcm_trigger, /* firmware loading */ .load_firmware = hda_dsp_cl_load_fw, diff --git a/sound/soc/sof/intel/hda-pcm.c b/sound/soc/sof/intel/hda-pcm.c index 0abb2e5778cae2..1b50d87bd6bed4 100644 --- a/sound/soc/sof/intel/hda-pcm.c +++ b/sound/soc/sof/intel/hda-pcm.c @@ -123,15 +123,22 @@ int hda_dsp_pcm_hw_params(struct snd_sof_dev *sdev, /* disable SPIB, to enable buffer wrap for stream */ hda_dsp_stream_spib_config(sdev, stream, HDA_DSP_SPIB_DISABLE, 0); - return stream->tag; -} - -int hda_dsp_pcm_trigger(struct snd_sof_dev *sdev, - struct snd_pcm_substream *substream, int cmd) -{ - struct sof_intel_hda_stream *stream = substream->runtime->private_data; + /* + * start HDA DMA here, as DSP require the DMA copy is available + * at its trigger start, which is actually before stream trigger + * start + */ + snd_sof_dsp_update_bits(sdev, HDA_DSP_HDA_BAR, SOF_HDA_INTCTL, + 1 << stream->index, + 1 << stream->index); + + snd_sof_dsp_update_bits(sdev, HDA_DSP_HDA_BAR, stream->sd_offset, + SOF_HDA_SD_CTL_DMA_START | + SOF_HDA_CL_DMA_SD_INT_MASK, + SOF_HDA_SD_CTL_DMA_START | + SOF_HDA_CL_DMA_SD_INT_MASK); - return hda_dsp_stream_trigger(sdev, stream, cmd); + return stream->tag; } int hda_dsp_pcm_open(struct snd_sof_dev *sdev, diff --git a/sound/soc/sof/intel/skl.c b/sound/soc/sof/intel/skl.c index d4b6de626456b1..671f452e9b16d4 100644 --- a/sound/soc/sof/intel/skl.c +++ b/sound/soc/sof/intel/skl.c @@ -85,7 +85,6 @@ struct snd_sof_dsp_ops sof_skl_ops = { .host_stream_open = hda_dsp_pcm_open, .host_stream_close = hda_dsp_pcm_close, .host_stream_hw_params = hda_dsp_pcm_hw_params, - .host_stream_trigger = hda_dsp_pcm_trigger, /* firmware loading */ .load_firmware = hda_dsp_cl_load_fw, diff --git a/sound/soc/sof/pcm.c b/sound/soc/sof/pcm.c index 50a95fdaae29aa..32ca808eb8624b 100644 --- a/sound/soc/sof/pcm.c +++ b/sound/soc/sof/pcm.c @@ -197,7 +197,6 @@ static int sof_pcm_trigger(struct snd_pcm_substream *substream, int cmd) struct snd_sof_pcm *spcm = rtd->sof; struct sof_ipc_stream stream; struct sof_ipc_reply reply; - const struct snd_sof_dsp_ops *ops = sdev->ops; int ret = 0; /* nothing todo for BE */ @@ -236,9 +235,6 @@ static int sof_pcm_trigger(struct snd_pcm_substream *substream, int cmd) ret = sof_ipc_tx_message(sdev->ipc, stream.hdr.cmd, &stream, sizeof(stream), &reply, sizeof(reply)); - if (ops && ops->host_stream_trigger) - ret = ops->host_stream_trigger(sdev, substream, cmd); - return ret; } diff --git a/sound/soc/sof/sof-priv.h b/sound/soc/sof/sof-priv.h index bde985f6f438be..7e86faef5aa287 100644 --- a/sound/soc/sof/sof-priv.h +++ b/sound/soc/sof/sof-priv.h @@ -116,11 +116,6 @@ struct snd_sof_dsp_ops { struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params); - /* host stream trigger */ - int (*host_stream_trigger)(struct snd_sof_dev *sdev, - struct snd_pcm_substream *substream, - int cmd); - /* FW loading */ int (*load_firmware)(struct snd_sof_dev *sof_dev, const struct firmware *fw); From 0ba34ad6a4da54a1baf13def2e81bf8a4eec6a3c Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Thu, 8 Mar 2018 18:36:30 -0600 Subject: [PATCH 061/107] ASoC: Intel: make bytcht_da7213 with SOF Disable hard-coded routes when SOF is enabled Signed-off-by: Pierre-Louis Bossart --- sound/soc/intel/boards/bytcht_da7213.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/sound/soc/intel/boards/bytcht_da7213.c b/sound/soc/intel/boards/bytcht_da7213.c index 2179dedb28ad6d..1e90a9c4b1e892 100644 --- a/sound/soc/intel/boards/bytcht_da7213.c +++ b/sound/soc/intel/boards/bytcht_da7213.c @@ -56,6 +56,7 @@ static const struct snd_soc_dapm_route audio_map[] = { {"MIC1", NULL, "Headset Mic"}, {"MIC2", NULL, "Mic"}, +#if !IS_ENABLED(CONFIG_SND_SOC_SOF_INTEL) /* SOC-codec link */ {"ssp2 Tx", NULL, "codec_out0"}, {"ssp2 Tx", NULL, "codec_out1"}, @@ -64,6 +65,7 @@ static const struct snd_soc_dapm_route audio_map[] = { {"Playback", NULL, "ssp2 Tx"}, {"ssp2 Rx", NULL, "Capture"}, +#endif }; static int codec_fixup(struct snd_soc_pcm_runtime *rtd, From 8b2b17c610264b12da260747ba5ac8b584124e51 Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Thu, 8 Mar 2018 18:39:33 -0600 Subject: [PATCH 062/107] ASoC: Intel: make cht_bsw_max98090 work with SOF Disable hard-coded routes when SOF is enabled Signed-off-by: Pierre-Louis Bossart --- sound/soc/intel/boards/cht_bsw_max98090_ti.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/sound/soc/intel/boards/cht_bsw_max98090_ti.c b/sound/soc/intel/boards/cht_bsw_max98090_ti.c index d3e1c7e12004dd..bbb281f64cbdd3 100644 --- a/sound/soc/intel/boards/cht_bsw_max98090_ti.c +++ b/sound/soc/intel/boards/cht_bsw_max98090_ti.c @@ -88,12 +88,14 @@ static const struct snd_soc_dapm_route cht_audio_map[] = { {"Headphone", NULL, "HPR"}, {"Ext Spk", NULL, "SPKL"}, {"Ext Spk", NULL, "SPKR"}, +#if !IS_ENABLED(CONFIG_SND_SOC_SOF_INTEL) {"HiFi Playback", NULL, "ssp2 Tx"}, {"ssp2 Tx", NULL, "codec_out0"}, {"ssp2 Tx", NULL, "codec_out1"}, {"codec_in0", NULL, "ssp2 Rx" }, {"codec_in1", NULL, "ssp2 Rx" }, {"ssp2 Rx", NULL, "HiFi Capture"}, +#endif {"Headphone", NULL, "Platform Clock"}, {"Headset Mic", NULL, "Platform Clock"}, {"Int Mic", NULL, "Platform Clock"}, From 46a119903fd02e73a4f065f3a8a1d16e548eda19 Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Fri, 10 Nov 2017 14:34:32 -0600 Subject: [PATCH 063/107] ASoC: Intel: cht-bsw-rt5645: work with SOF Disable hard-coded routes when SOF is selected TODO: bytcr is not yet supported Signed-off-by: Pierre-Louis Bossart --- sound/soc/intel/boards/cht_bsw_rt5645.c | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/sound/soc/intel/boards/cht_bsw_rt5645.c b/sound/soc/intel/boards/cht_bsw_rt5645.c index 49ba1a956a06a0..74b30304a2615b 100644 --- a/sound/soc/intel/boards/cht_bsw_rt5645.c +++ b/sound/soc/intel/boards/cht_bsw_rt5645.c @@ -160,35 +160,43 @@ static const struct snd_soc_dapm_route cht_rt5650_audio_map[] = { }; static const struct snd_soc_dapm_route cht_rt5645_ssp2_aif1_map[] = { +#if !IS_ENABLED(CONFIG_SND_SOC_SOF_INTEL) {"AIF1 Playback", NULL, "ssp2 Tx"}, {"ssp2 Tx", NULL, "codec_out0"}, {"ssp2 Tx", NULL, "codec_out1"}, {"codec_in0", NULL, "ssp2 Rx" }, {"codec_in1", NULL, "ssp2 Rx" }, {"ssp2 Rx", NULL, "AIF1 Capture"}, +#endif }; static const struct snd_soc_dapm_route cht_rt5645_ssp2_aif2_map[] = { +#if !IS_ENABLED(CONFIG_SND_SOC_SOF_INTEL) {"AIF2 Playback", NULL, "ssp2 Tx"}, {"ssp2 Tx", NULL, "codec_out0"}, {"ssp2 Tx", NULL, "codec_out1"}, {"codec_in0", NULL, "ssp2 Rx" }, {"codec_in1", NULL, "ssp2 Rx" }, {"ssp2 Rx", NULL, "AIF2 Capture"}, +#endif }; static const struct snd_soc_dapm_route cht_rt5645_ssp0_aif1_map[] = { +#if !IS_ENABLED(CONFIG_SND_SOC_SOF_INTEL) {"AIF1 Playback", NULL, "ssp0 Tx"}, {"ssp0 Tx", NULL, "modem_out"}, {"modem_in", NULL, "ssp0 Rx" }, {"ssp0 Rx", NULL, "AIF1 Capture"}, +#endif }; static const struct snd_soc_dapm_route cht_rt5645_ssp0_aif2_map[] = { +#if !IS_ENABLED(CONFIG_SND_SOC_SOF_INTEL) {"AIF2 Playback", NULL, "ssp0 Tx"}, {"ssp0 Tx", NULL, "modem_out"}, {"modem_in", NULL, "ssp0 Rx" }, {"ssp0 Rx", NULL, "AIF2 Capture"}, +#endif }; static const struct snd_kcontrol_new cht_mc_controls[] = { @@ -585,11 +593,13 @@ static int snd_cht_mc_probe(struct platform_device *pdev) * (will be overridden if DMI quirk is detected) */ if (is_valleyview()) { +#if !IS_ENABLED(CONFIG_SND_SOC_SOF_INTEL) /* FIXME: bytcr not supported yet */ struct sst_platform_info *p_info = mach->pdata; const struct sst_res_info *res_info = p_info->res_info; if (res_info->acpi_ipc_irq_index == 0) is_bytcr = true; +#endif } if (is_bytcr) { From 2759eb0a85898d2b58510d9e0257af585fcdf608 Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Wed, 29 Nov 2017 19:14:19 -0600 Subject: [PATCH 064/107] ASoC: Intel: cht-bsw-rt5672: work with SOF Disable hard-coded routes when SOF is enabled Signed-off-by: Pierre-Louis Bossart --- sound/soc/intel/boards/cht_bsw_rt5672.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/sound/soc/intel/boards/cht_bsw_rt5672.c b/sound/soc/intel/boards/cht_bsw_rt5672.c index e5aa13058dd738..3a2754676f3f1c 100644 --- a/sound/soc/intel/boards/cht_bsw_rt5672.c +++ b/sound/soc/intel/boards/cht_bsw_rt5672.c @@ -127,12 +127,14 @@ static const struct snd_soc_dapm_route cht_audio_map[] = { {"Ext Spk", NULL, "SPOLN"}, {"Ext Spk", NULL, "SPORP"}, {"Ext Spk", NULL, "SPORN"}, +#if !IS_ENABLED(CONFIG_SND_SOC_SOF_INTEL) {"AIF1 Playback", NULL, "ssp2 Tx"}, {"ssp2 Tx", NULL, "codec_out0"}, {"ssp2 Tx", NULL, "codec_out1"}, {"codec_in0", NULL, "ssp2 Rx"}, {"codec_in1", NULL, "ssp2 Rx"}, {"ssp2 Rx", NULL, "AIF1 Capture"}, +#endif {"Headphone", NULL, "Platform Clock"}, {"Headset Mic", NULL, "Platform Clock"}, {"Int Mic", NULL, "Platform Clock"}, From 87d8985bfa9355d2fe9afb1a146583a541d7b6a3 Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Wed, 20 Dec 2017 18:36:49 -0600 Subject: [PATCH 065/107] ASoC: Intel: make bytcr_rt5640 work with SOF Disable hard-coded routes when SOF is enabled TODO: bytcr is not yet supported Signed-off-by: Pierre-Louis Bossart --- sound/soc/intel/boards/bytcr_rt5640.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/sound/soc/intel/boards/bytcr_rt5640.c b/sound/soc/intel/boards/bytcr_rt5640.c index a8d8bff788e70b..8bdf820903541a 100644 --- a/sound/soc/intel/boards/bytcr_rt5640.c +++ b/sound/soc/intel/boards/bytcr_rt5640.c @@ -489,6 +489,7 @@ static int byt_rt5640_init(struct snd_soc_pcm_runtime *runtime) if (ret) return ret; +#if !IS_ENABLED(CONFIG_SND_SOC_SOF_INTEL) if (byt_rt5640_quirk & BYT_RT5640_SSP2_AIF2) { ret = snd_soc_dapm_add_routes(&card->dapm, byt_rt5640_ssp2_aif2_map, @@ -508,6 +509,7 @@ static int byt_rt5640_init(struct snd_soc_pcm_runtime *runtime) } if (ret) return ret; +#endif if (byt_rt5640_quirk & BYT_RT5640_MONO_SPEAKER) { ret = snd_soc_dapm_add_routes(&card->dapm, @@ -775,11 +777,13 @@ static int snd_byt_rt5640_mc_probe(struct platform_device *pdev) * (will be overridden if DMI quirk is detected) */ if (is_valleyview()) { +#if !IS_ENABLED(CONFIG_SND_SOC_SOF_INTEL) /* FIXME: bytcr not supported yet */ struct sst_platform_info *p_info = mach->pdata; const struct sst_res_info *res_info = p_info->res_info; if (res_info->acpi_ipc_irq_index == 0) is_bytcr = true; +#endif } if (is_bytcr) { From 9b9e3920a89e3b4be77407ffdc4db23c389dacc9 Mon Sep 17 00:00:00 2001 From: Liam Girdwood Date: Thu, 7 Dec 2017 21:48:51 +0000 Subject: [PATCH 066/107] ASoC: Intel: Make sure HSW/BDW based machine drivers build for SOF HSW/BDW use hard coded IPC calls to set SSP, not needed in SOF as SSP is configured via topology. Signed-off-by: Liam Girdwood --- sound/soc/intel/boards/bdw-rt5677.c | 4 ++++ sound/soc/intel/boards/broadwell.c | 4 ++++ sound/soc/intel/boards/haswell.c | 4 ++++ 3 files changed, 12 insertions(+) diff --git a/sound/soc/intel/boards/bdw-rt5677.c b/sound/soc/intel/boards/bdw-rt5677.c index 6ea360f33575ae..f287f192929ca5 100644 --- a/sound/soc/intel/boards/bdw-rt5677.c +++ b/sound/soc/intel/boards/bdw-rt5677.c @@ -181,6 +181,7 @@ static const struct snd_soc_ops bdw_rt5677_ops = { .hw_params = bdw_rt5677_hw_params, }; +#if !IS_ENABLED(CONFIG_SND_SOC_SOF_INTEL) static int bdw_rt5677_rtd_init(struct snd_soc_pcm_runtime *rtd) { struct snd_soc_component *component = snd_soc_rtdcom_lookup(rtd, DRV_NAME); @@ -199,6 +200,7 @@ static int bdw_rt5677_rtd_init(struct snd_soc_pcm_runtime *rtd) return 0; } +#endif static int bdw_rt5677_init(struct snd_soc_pcm_runtime *rtd) { @@ -266,7 +268,9 @@ static struct snd_soc_dai_link bdw_rt5677_dais[] = { .dynamic = 1, .codec_name = "snd-soc-dummy", .codec_dai_name = "snd-soc-dummy-dai", +#if !IS_ENABLED(CONFIG_SND_SOC_SOF_INTEL) .init = bdw_rt5677_rtd_init, +#endif .trigger = { SND_SOC_DPCM_TRIGGER_POST, SND_SOC_DPCM_TRIGGER_POST diff --git a/sound/soc/intel/boards/broadwell.c b/sound/soc/intel/boards/broadwell.c index 7b0ee67b4fc8b0..776a1fa7a1e1d6 100644 --- a/sound/soc/intel/boards/broadwell.c +++ b/sound/soc/intel/boards/broadwell.c @@ -130,6 +130,7 @@ static const struct snd_soc_ops broadwell_rt286_ops = { .hw_params = broadwell_rt286_hw_params, }; +#if !IS_ENABLED(CONFIG_SND_SOC_SOF_INTEL) static int broadwell_rtd_init(struct snd_soc_pcm_runtime *rtd) { struct snd_soc_component *component = snd_soc_rtdcom_lookup(rtd, DRV_NAME); @@ -148,6 +149,7 @@ static int broadwell_rtd_init(struct snd_soc_pcm_runtime *rtd) return 0; } +#endif /* broadwell digital audio interface glue - connects codec <--> CPU */ static struct snd_soc_dai_link broadwell_rt286_dais[] = { @@ -160,7 +162,9 @@ static struct snd_soc_dai_link broadwell_rt286_dais[] = { .dynamic = 1, .codec_name = "snd-soc-dummy", .codec_dai_name = "snd-soc-dummy-dai", +#if !IS_ENABLED(CONFIG_SND_SOC_SOF_INTEL) .init = broadwell_rtd_init, +#endif .trigger = {SND_SOC_DPCM_TRIGGER_POST, SND_SOC_DPCM_TRIGGER_POST}, .dpcm_playback = 1, .dpcm_capture = 1, diff --git a/sound/soc/intel/boards/haswell.c b/sound/soc/intel/boards/haswell.c index eab1f439dd3f1a..ce7d7f6422bfb8 100644 --- a/sound/soc/intel/boards/haswell.c +++ b/sound/soc/intel/boards/haswell.c @@ -85,6 +85,7 @@ static const struct snd_soc_ops haswell_rt5640_ops = { .hw_params = haswell_rt5640_hw_params, }; +#if !IS_ENABLED(CONFIG_SND_SOC_SOF_INTEL) static int haswell_rtd_init(struct snd_soc_pcm_runtime *rtd) { struct snd_soc_component *component = snd_soc_rtdcom_lookup(rtd, DRV_NAME); @@ -103,6 +104,7 @@ static int haswell_rtd_init(struct snd_soc_pcm_runtime *rtd) return 0; } +#endif static struct snd_soc_dai_link haswell_rt5640_dais[] = { /* Front End DAI links */ @@ -114,7 +116,9 @@ static struct snd_soc_dai_link haswell_rt5640_dais[] = { .dynamic = 1, .codec_name = "snd-soc-dummy", .codec_dai_name = "snd-soc-dummy-dai", +#if !IS_ENABLED(CONFIG_SND_SOC_SOF_INTEL) .init = haswell_rtd_init, +#endif .trigger = {SND_SOC_DPCM_TRIGGER_POST, SND_SOC_DPCM_TRIGGER_POST}, .dpcm_playback = 1, .dpcm_capture = 1, From e63c8a9c8654829d50fef2bf0953be1d3e60f9b0 Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Tue, 9 Jan 2018 13:40:20 -0600 Subject: [PATCH 067/107] ASoC: Intel: Kconfig: expose common option between SST and SOF drivers Both drivers rely on the same module, expose it for both configurations Signed-off-by: Pierre-Louis Bossart --- sound/soc/intel/Kconfig | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/sound/soc/intel/Kconfig b/sound/soc/intel/Kconfig index 08f00058700023..9ff42b4294e755 100644 --- a/sound/soc/intel/Kconfig +++ b/sound/soc/intel/Kconfig @@ -117,12 +117,14 @@ config SND_SOC_INTEL_SKYLAKE GeminiLake or CannonLake platform with the DSP enabled in the BIOS then enable this option by saying Y or m. +endif ## SND_SOC_INTEL_SST_TOPLEVEL + +if SND_SOC_INTEL_SST_TOPLEVEL || SND_SOC_SOF_INTEL config SND_SOC_ACPI_INTEL_MATCH tristate select SND_SOC_ACPI if ACPI # this option controls the compilation of ACPI matching tables and # helpers and is not meant to be selected by the user. - endif ## SND_SOC_INTEL_SST_TOPLEVEL # ASoC codec drivers From fff657ff5685b06e231302582f8619e0a9379a59 Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Thu, 7 Dec 2017 21:46:33 +0000 Subject: [PATCH 068/107] ASoC: Intel: select relevant machine drivers for SOF SOF can only support specific machine drivers, handle dependencies Signed-off-by: Pierre-Louis Bossart --- sound/soc/intel/boards/Kconfig | 24 ++++++++++++++++++++---- 1 file changed, 20 insertions(+), 4 deletions(-) diff --git a/sound/soc/intel/boards/Kconfig b/sound/soc/intel/boards/Kconfig index 24797482a3d22d..05097128a0822a 100644 --- a/sound/soc/intel/boards/Kconfig +++ b/sound/soc/intel/boards/Kconfig @@ -1,6 +1,6 @@ menuconfig SND_SOC_INTEL_MACH bool "Intel Machine drivers" - depends on SND_SOC_INTEL_SST_TOPLEVEL + depends on SND_SOC_INTEL_SST_TOPLEVEL || SND_SOC_SOF_INTEL help Intel ASoC Machine Drivers. If you have a Intel machine that has an audio controller with a DSP and I2S or DMIC port, then @@ -12,7 +12,7 @@ menuconfig SND_SOC_INTEL_MACH if SND_SOC_INTEL_MACH -if SND_SOC_INTEL_HASWELL +if SND_SOC_INTEL_HASWELL || SND_SOC_SOF_HASWELL config SND_SOC_INTEL_HASWELL_MACH tristate "Haswell Lynxpoint" @@ -24,6 +24,10 @@ config SND_SOC_INTEL_HASWELL_MACH Say Y or m if you have such a device. If unsure select "N". +endif ## SND_SOC_INTEL_HASWELL || SND_SOC_SOF_HASWELL + +if SND_SOC_INTEL_HASWELL || SND_SOC_SOF_BROADWELL + config SND_SOC_INTEL_BDW_RT5677_MACH tristate "Broadwell with RT5677 codec" depends on X86_INTEL_LPSS && I2C && I2C_DESIGNWARE_PLATFORM && GPIOLIB @@ -43,7 +47,7 @@ config SND_SOC_INTEL_BROADWELL_MACH Ultrabook platforms. Say Y or m if you have such a device. This is a recommended option. If unsure select "N". -endif ## SND_SOC_INTEL_HASWELL +endif ## SND_SOC_INTEL_HASWELL || SND_SOC_SOF_BROADWELL if SND_SOC_INTEL_BAYTRAIL @@ -68,7 +72,7 @@ config SND_SOC_INTEL_BYT_RT5640_MACH endif ## SND_SOC_INTEL_BAYTRAIL -if SND_SST_ATOM_HIFI2_PLATFORM +if SND_SST_ATOM_HIFI2_PLATFORM || SND_SOC_SOF_BAYTRAIL config SND_SOC_INTEL_BYTCR_RT5640_MACH tristate "Baytrail and Baytrail-CR with RT5640 codec" @@ -158,6 +162,10 @@ config SND_SOC_INTEL_BYT_CHT_ES8316_MACH Say Y or m if you have such a device. This is a recommended option. If unsure select "N". +endif ## SND_SST_ATOM_HIFI2_PLATFORM || SND_SOC_SOF_BAYTRAIL + +if SND_SST_ATOM_HIFI2_PLATFORM + config SND_SOC_INTEL_BYT_CHT_NOCODEC_MACH tristate "Baytrail & Cherrytrail platform with no codec (MinnowBoard MAX, Up)" depends on X86_INTEL_LPSS && I2C && ACPI @@ -212,6 +220,10 @@ config SND_SOC_INTEL_SKL_NAU88L25_MAX98357A_MACH Say Y or m if you have such a device. This is a recommended option. If unsure select "N". +endif ## SND_SOC_INTEL_SKL + +if SND_SOC_INTEL_SKL || SND_SOC_SOF_APOLLOLAKE + config SND_SOC_INTEL_BXT_DA7219_MAX98357A_MACH tristate "Broxton with DA7219 and MAX98357A in I2S Mode" depends on MFD_INTEL_LPSS && I2C && ACPI @@ -239,6 +251,10 @@ config SND_SOC_INTEL_BXT_RT298_MACH Say Y or m if you have such a device. This is a recommended option. If unsure select "N". +endif ## SND_SOC_INTEL_SKL || SND_SOC_SOF_APOLLOLAKE + +if SND_SOC_INTEL_SKL + config SND_SOC_INTEL_KBL_RT5663_MAX98927_MACH tristate "KBL with RT5663 and MAX98927 in I2S Mode" depends on MFD_INTEL_LPSS && I2C && ACPI From a3039a070980b371ab8a26d7b1f776ec11d4826d Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Mon, 23 Apr 2018 12:06:09 -0500 Subject: [PATCH 069/107] ASoC: Intel: add machine driver for BXT/APL with pcm512x codec This patch adds the machine driver for the Up2 board with the Hifiberry DAC+ codec. Signed-off-by: Ranjani Sridharan Signed-off-by: Pierre-Louis Bossart --- sound/soc/intel/boards/Kconfig | 10 ++ sound/soc/intel/boards/Makefile | 2 + sound/soc/intel/boards/bxt_pcm512x.c | 186 +++++++++++++++++++++++++++ 3 files changed, 198 insertions(+) create mode 100644 sound/soc/intel/boards/bxt_pcm512x.c diff --git a/sound/soc/intel/boards/Kconfig b/sound/soc/intel/boards/Kconfig index 05097128a0822a..6fdc798290982e 100644 --- a/sound/soc/intel/boards/Kconfig +++ b/sound/soc/intel/boards/Kconfig @@ -251,6 +251,16 @@ config SND_SOC_INTEL_BXT_RT298_MACH Say Y or m if you have such a device. This is a recommended option. If unsure select "N". +config SND_SOC_INTEL_BXT_PCM512x_MACH + tristate "Broxton with TI PCM512x codec" + depends on MFD_INTEL_LPSS && I2C && ACPI + select SND_SOC_PCM512x_I2C + help + This adds support for ASoC machine driver for Broxton platforms + with TI PCM512x I2S audio codec. + Say Y or m if you have such a device. This is a recommended option. + If unsure select "N". + endif ## SND_SOC_INTEL_SKL || SND_SOC_SOF_APOLLOLAKE if SND_SOC_INTEL_SKL diff --git a/sound/soc/intel/boards/Makefile b/sound/soc/intel/boards/Makefile index 92b5507291af49..904a5864953ee1 100644 --- a/sound/soc/intel/boards/Makefile +++ b/sound/soc/intel/boards/Makefile @@ -6,6 +6,7 @@ snd-soc-sst-bdw-rt5677-mach-objs := bdw-rt5677.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-bxt-pcm512x-objs := bxt_pcm512x.o snd-soc-sst-bytcr-rt5640-objs := bytcr_rt5640.o snd-soc-sst-bytcr-rt5651-objs := bytcr_rt5651.o snd-soc-sst-cht-bsw-rt5672-objs := cht_bsw_rt5672.o @@ -27,6 +28,7 @@ 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_BXT_PCM512x_MACH) += snd-soc-sst-bxt-pcm512x.o obj-$(CONFIG_SND_SOC_INTEL_BROADWELL_MACH) += snd-soc-sst-broadwell.o obj-$(CONFIG_SND_SOC_INTEL_BDW_RT5677_MACH) += snd-soc-sst-bdw-rt5677-mach.o obj-$(CONFIG_SND_SOC_INTEL_BYTCR_RT5640_MACH) += snd-soc-sst-bytcr-rt5640.o diff --git a/sound/soc/intel/boards/bxt_pcm512x.c b/sound/soc/intel/boards/bxt_pcm512x.c new file mode 100644 index 00000000000000..5bc80853707dd2 --- /dev/null +++ b/sound/soc/intel/boards/bxt_pcm512x.c @@ -0,0 +1,186 @@ +/* + * bxt-pcm512x.c - ASoc Machine driver for Intel Baytrail and + * Cherrytrail-based platforms, with TI PCM512x codec + * + * Copyright (C) 2016 Intel Corporation + * Author: Pierre-Louis Bossart + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * 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. + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + */ +#define DEBUG + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "../../codecs/pcm512x.h" +#include "../atom/sst-atom-controls.h" + +static const struct snd_soc_dapm_widget dapm_widgets[] = { + SND_SOC_DAPM_SPK("Ext Spk", NULL), +}; + +static const struct snd_soc_dapm_route audio_map[] = { + /* Speaker */ + {"Ext Spk", NULL, "OUTR"}, + {"Ext Spk", NULL, "OUTL"}, +}; + +static int codec_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 covert the FE rate to 48k, stereo */ + rate->min = 48000; + rate->max = 48000; + channels->min = 2; + channels->max = 2; + + /* set SSP5 to 24 bit */ + snd_mask_none(fmt); + snd_mask_set(fmt, SNDRV_PCM_FORMAT_S24_LE); + + return 0; +} + +static int aif1_startup(struct snd_pcm_substream *substream) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_codec *codec = rtd->codec; + + snd_soc_update_bits(codec, PCM512x_GPIO_CONTROL_1, 0x08, 0x08); + + return snd_pcm_hw_constraint_single(substream->runtime, + SNDRV_PCM_HW_PARAM_RATE, 48000); +} + +static void aif1_shutdown(struct snd_pcm_substream *substream) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_codec *codec = rtd->codec; + + snd_soc_update_bits(codec, PCM512x_GPIO_CONTROL_1, 0x08, 0x00); +} + +static int init(struct snd_soc_pcm_runtime *rtd) +{ + struct snd_soc_codec *codec = rtd->codec; + + snd_soc_update_bits(codec, PCM512x_GPIO_EN, 0x08, 0x08); + snd_soc_update_bits(codec, PCM512x_GPIO_OUTPUT_4, 0x0f, 0x02); + snd_soc_update_bits(codec, PCM512x_GPIO_CONTROL_1, 0x08, 0x08); + + return 0; +} + +static const struct snd_soc_ops aif1_ops = { + .startup = aif1_startup, + .shutdown = aif1_shutdown, +}; + +static struct snd_soc_dai_link dailink[] = { + /* CODEC<->CODEC link */ + /* back ends */ + { + .name = "SSP5-Codec", + .id = 0, + .cpu_dai_name = "sof-audio", + .platform_name = "sof-audio", + .no_pcm = 1, + .codec_dai_name = "pcm512x-hifi", + .codec_name = "i2c-104C5122:00", + .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF + | SND_SOC_DAIFMT_CBS_CFS, + .init = init, + .be_hw_params_fixup = codec_fixup, + .nonatomic = true, + .dpcm_playback = 1, + }, +}; + +/* SoC card */ +static struct snd_soc_card bxt_pcm512x_card = { + .name = "bxt-pcm512x", + .owner = THIS_MODULE, + .dai_link = dailink, + .num_links = ARRAY_SIZE(dailink), + .dapm_widgets = dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(dapm_widgets), + .dapm_routes = audio_map, + .num_dapm_routes = ARRAY_SIZE(audio_map), +}; + + /* i2c-:00 with HID being 8 chars */ +static char codec_name[SND_ACPI_I2C_ID_LEN]; + +static int bxt_pcm512x_probe(struct platform_device *pdev) +{ + struct snd_soc_card *card; + struct snd_soc_acpi_mach *mach; + const char *i2c_name = NULL; + int dai_index = 0; + int ret_val = 0, i; + + mach = (&pdev->dev)->platform_data; + card = &bxt_pcm512x_card; + card->dev = &pdev->dev; + + /* fix index of codec dai */ + for (i = 0; i < ARRAY_SIZE(dailink); i++) { + if (!strcmp(dailink[i].codec_name, "i2c-104C5122:00")) { + dai_index = i; + break; + } + } + + /* fixup codec name based on HID */ + i2c_name = acpi_dev_get_first_match_name(mach->id, NULL, -1); + if (i2c_name) { + snprintf(codec_name, sizeof(codec_name), + "%s%s", "i2c-", i2c_name); + dailink[dai_index].codec_name = codec_name; + } + + ret_val = devm_snd_soc_register_card(&pdev->dev, card); + if (ret_val) { + dev_err(&pdev->dev, + "snd_soc_register_card failed %d\n", ret_val); + return ret_val; + } + platform_set_drvdata(pdev, card); + return ret_val; +} + +static struct platform_driver bxt_pcm521x_driver = { + .driver = { + .name = "bxt-pcm512x", + }, + .probe = bxt_pcm512x_probe, +}; +module_platform_driver(bxt_pcm521x_driver); + +MODULE_DESCRIPTION("ASoC Intel(R) Broxton + PCM512x Machine driver"); +MODULE_AUTHOR("Pierre-Louis Bossart"); +MODULE_LICENSE("GPL v2"); +MODULE_ALIAS("platform:bxt-pcm512x"); From 18ba099b5a2129ecb23069dea662191e955f9dfa Mon Sep 17 00:00:00 2001 From: Rander Wang Date: Fri, 18 May 2018 09:35:12 +0800 Subject: [PATCH 070/107] ASoC: Intel: add rt274 machine driver for cnl port rt274 machine driver from android kernel source Signed-off-by: Rander Wang Signed-off-by: Pierre-Louis Bossart --- sound/soc/intel/boards/Kconfig | 12 ++ sound/soc/intel/boards/Makefile | 2 + sound/soc/intel/boards/cnl_rt274.c | 247 +++++++++++++++++++++++++++++ 3 files changed, 261 insertions(+) create mode 100644 sound/soc/intel/boards/cnl_rt274.c diff --git a/sound/soc/intel/boards/Kconfig b/sound/soc/intel/boards/Kconfig index 6fdc798290982e..0037fa7bd73d0b 100644 --- a/sound/soc/intel/boards/Kconfig +++ b/sound/soc/intel/boards/Kconfig @@ -309,4 +309,16 @@ config SND_SOC_INTEL_KBL_DA7219_MAX98357A_MACH endif ## SND_SOC_INTEL_SKYLAKE +if SND_SOC_INTEL_SKL || SND_SOC_SOF_CANNONLAKE + +config SND_SOC_INTEL_CNL_RT274_MACH + tristate "ASoC Audio driver for CNL with RT274 in I2S Mode" + select SND_SOC_RT274 + help + This adds support for ASoC machine driver for CNL and codec RT274. This + will create an alsa sound card. Say Y if you have such a device If + unsure select "N". + +endif ## SND_SOC_INTEL_SKL || SND_SOC_SOF_CANNONLAKE + endif ## SND_SOC_INTEL_MACH diff --git a/sound/soc/intel/boards/Makefile b/sound/soc/intel/boards/Makefile index 904a5864953ee1..f58a9bda911ddd 100644 --- a/sound/soc/intel/boards/Makefile +++ b/sound/soc/intel/boards/Makefile @@ -22,6 +22,7 @@ snd-soc-kbl_rt5663_rt5514_max98927-objs := kbl_rt5663_rt5514_max98927.o snd-soc-skl_rt286-objs := skl_rt286.o snd-skl_nau88l25_max98357a-objs := skl_nau88l25_max98357a.o snd-soc-skl_nau88l25_ssm4567-objs := skl_nau88l25_ssm4567.o +snd-soc-cnl-rt274-objs := cnl_rt274.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 @@ -46,3 +47,4 @@ obj-$(CONFIG_SND_SOC_INTEL_KBL_RT5663_RT5514_MAX98927_MACH) += snd-soc-kbl_rt566 obj-$(CONFIG_SND_SOC_INTEL_SKL_RT286_MACH) += snd-soc-skl_rt286.o obj-$(CONFIG_SND_SOC_INTEL_SKL_NAU88L25_MAX98357A_MACH) += snd-skl_nau88l25_max98357a.o obj-$(CONFIG_SND_SOC_INTEL_SKL_NAU88L25_SSM4567_MACH) += snd-soc-skl_nau88l25_ssm4567.o +obj-$(CONFIG_SND_SOC_INTEL_CNL_RT274_MACH) += snd-soc-cnl-rt274.o diff --git a/sound/soc/intel/boards/cnl_rt274.c b/sound/soc/intel/boards/cnl_rt274.c new file mode 100644 index 00000000000000..6f8be5a4d508aa --- /dev/null +++ b/sound/soc/intel/boards/cnl_rt274.c @@ -0,0 +1,247 @@ +/* + * cnl_rt274.c - ASOC Machine driver for CNL + * + * Copyright (C) 2016 Intel Corp + * Author: Guneshwor Singh + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * 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 +#include +#include +#include +#include "../../codecs/rt274.h" + +#define CNL_FREQ_OUT 24000000 +#define CNL_BE_FIXUP_RATE 48000 +#define RT274_CODEC_DAI "rt274-aif1" + +static int cnl_rt274_clock_control(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *k, int event) +{ + struct snd_soc_dapm_context *dapm = w->dapm; + struct snd_soc_card *card = dapm->card; + int ret = 0; + int ratio = 100; + + struct snd_soc_dai *codec_dai = snd_soc_card_get_codec_dai(card, + RT274_CODEC_DAI); + if (!codec_dai) + return -EINVAL; + + ret = snd_soc_dai_set_sysclk(codec_dai, RT274_SCLK_S_PLL1, + CNL_FREQ_OUT, + SND_SOC_CLOCK_IN); + if (ret) { + dev_err(codec_dai->dev, + "failed to enable PLL1: %d\n", ret); + } + + snd_soc_dai_set_bclk_ratio(codec_dai, ratio); + + return ret; +} + +/* Headset jack detection DAPM pins */ +static struct snd_soc_jack_pin cnl_headset_pins[] = { + { + .pin = "Mic Jack", + .mask = SND_JACK_MICROPHONE, + }, + { + .pin = "Headphone Jack", + .mask = SND_JACK_HEADPHONE, + }, +}; + +static const struct snd_kcontrol_new cnl_controls[] = { + SOC_DAPM_PIN_SWITCH("Headphone Jack"), + SOC_DAPM_PIN_SWITCH("Mic Jack"), +}; + +static const struct snd_soc_dapm_widget cnl_rt274_widgets[] = { + SND_SOC_DAPM_HP("Headphone Jack", NULL), + SND_SOC_DAPM_MIC("Mic Jack", NULL), + SND_SOC_DAPM_MIC("SoC DMIC", NULL), + SND_SOC_DAPM_SUPPLY("Platform Clock", SND_SOC_NOPM, 0, 0, + cnl_rt274_clock_control, SND_SOC_DAPM_PRE_PMU | + SND_SOC_DAPM_POST_PMD), +}; + +static const struct snd_soc_pcm_stream dai_params_codec = { + .formats = SNDRV_PCM_FMTBIT_S24_LE, + .rate_min = 48000, + .rate_max = 48000, + .channels_min = 2, + .channels_max = 2, +}; + +static const struct snd_soc_dapm_route cnl_map[] = { + {"Headphone Jack", NULL, "HPO Pin"}, + {"MIC", NULL, "Mic Jack"}, + {"DMic", NULL, "SoC DMIC"}, + {"DMIC01 Rx", NULL, "Capture"}, + {"dmic01_hifi", NULL, "DMIC01 Rx"}, + + {"AIF1 Playback", NULL, "ssp0 Tx"}, + {"ssp0 Tx", NULL, "codec1_out"}, + {"ssp0 Tx", NULL, "codec0_out"}, + + {"ssp0 Rx", NULL, "AIF1 Capture"}, + {"codec0_in", NULL, "ssp0 Rx"}, + + {"Headphone Jack", NULL, "Platform Clock"}, + {"MIC", NULL, "Platform Clock"}, +}; + +static struct snd_soc_jack cnl_headset; + +static int cnl_rt274_init(struct snd_soc_pcm_runtime *runtime) +{ + int ret; + struct snd_soc_codec *codec = runtime->codec; + struct snd_soc_card *card = runtime->card; + struct snd_soc_dai *codec_dai = runtime->codec_dai; + + ret = snd_soc_card_jack_new(runtime->card, "Headset", + SND_JACK_HEADSET, &cnl_headset, + cnl_headset_pins, ARRAY_SIZE(cnl_headset_pins)); + + if (ret) + return ret; + + snd_soc_codec_set_jack(codec, &cnl_headset, NULL); + + /* TDM 4 slots 24 bit, set Rx & Tx bitmask to 4 active slots */ + ret = snd_soc_dai_set_tdm_slot(codec_dai, 0xF, 0xF, 4, 24); + if (ret < 0) { + dev_err(runtime->dev, "can't set codec pcm format %d\n", ret); + return ret; + } + + card->dapm.idle_bias_off = true; + + return 0; +} + +static int cnl_be_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); + + rate->min = rate->max = CNL_BE_FIXUP_RATE; + channels->min = channels->max = 2; + snd_mask_none(hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT)); + snd_mask_set(hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT), + SNDRV_PCM_FORMAT_S24_LE); + + return 0; +} + +static int cnl_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); + channels->min = channels->max = 2; + + return 0; +} + +static const char pname[] = "0000:00:1f.3"; +static const char cname[] = "i2c-INT34C2:00"; + +struct snd_soc_dai_link cnl_rt274_dailink[] = { + { + .name = "SSP0-Codec", + .cpu_dai_name = "SSP0 Pin", + .codec_name = cname, + .codec_dai_name = "rt274-aif1", + .platform_name = pname, + .be_hw_params_fixup = cnl_be_fixup, + .ignore_pmdown_time = 1, + .no_pcm = 1, + .dai_fmt = SND_SOC_DAIFMT_DSP_A | + SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBS_CFS, + .dpcm_playback = 1, + .dpcm_capture = 1, + .init = cnl_rt274_init, + }, + { + .name = "dmic01", + .cpu_dai_name = "DMIC01 Pin", + .codec_name = "dmic-codec", + .codec_dai_name = "dmic-hifi", + .platform_name = pname, + .ignore_suspend = 1, + .no_pcm = 1, + .dpcm_capture = 1, + .be_hw_params_fixup = cnl_dmic_fixup, + }, +}; + +static int +cnl_add_dai_link(struct snd_soc_card *card, struct snd_soc_dai_link *link) +{ + link->platform_name = pname; + link->nonatomic = 1; + + return 0; +} + +/* SoC card */ +static struct snd_soc_card snd_soc_card_cnl = { + .name = "cnl-audio", + .dai_link = cnl_rt274_dailink, + .num_links = ARRAY_SIZE(cnl_rt274_dailink), + .dapm_widgets = cnl_rt274_widgets, + .num_dapm_widgets = ARRAY_SIZE(cnl_rt274_widgets), + .dapm_routes = cnl_map, + .num_dapm_routes = ARRAY_SIZE(cnl_map), + .controls = cnl_controls, + .num_controls = ARRAY_SIZE(cnl_controls), + .add_dai_link = cnl_add_dai_link, + .fully_routed = true, +}; + +static int cnl_rt274_probe(struct platform_device *pdev) +{ + snd_soc_card_cnl.dev = &pdev->dev; + + return devm_snd_soc_register_card(&pdev->dev, &snd_soc_card_cnl); +} + +static const struct platform_device_id cnl_board_ids[] = { + { .name = "cnl_rt274" }, + { } +}; + +static struct platform_driver cnl_rt274_driver = { + .driver = { + .name = "cnl_rt274", + .pm = &snd_soc_pm_ops, + }, + .probe = cnl_rt274_probe, + .id_table = cnl_board_ids, +}; + +module_platform_driver(cnl_rt274_driver); + +MODULE_AUTHOR("Guneshwor Singh "); +MODULE_LICENSE("GPL v2"); +MODULE_ALIAS("platform:cnl_rt274"); From 1ac86e18ef5fa4a1d55d00413d86b12c740ad3b9 Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Thu, 17 May 2018 20:43:51 -0500 Subject: [PATCH 071/107] ASoC: Intel: make cnl_rt274 work with SOF (0)fix alignment issues for upstream (1)refine machine driver to make it work with sof (2)disable DMIC for it is not ready now Signed-off-by: Rander Wang Signed-off-by: Pierre-Louis Bossart --- sound/soc/intel/boards/cnl_rt274.c | 49 ++++++++++++++---------------- 1 file changed, 22 insertions(+), 27 deletions(-) diff --git a/sound/soc/intel/boards/cnl_rt274.c b/sound/soc/intel/boards/cnl_rt274.c index 6f8be5a4d508aa..d31af7dfc1cc59 100644 --- a/sound/soc/intel/boards/cnl_rt274.c +++ b/sound/soc/intel/boards/cnl_rt274.c @@ -29,7 +29,7 @@ #define RT274_CODEC_DAI "rt274-aif1" static int cnl_rt274_clock_control(struct snd_soc_dapm_widget *w, - struct snd_kcontrol *k, int event) + struct snd_kcontrol *k, int event) { struct snd_soc_dapm_context *dapm = w->dapm; struct snd_soc_card *card = dapm->card; @@ -42,8 +42,8 @@ static int cnl_rt274_clock_control(struct snd_soc_dapm_widget *w, return -EINVAL; ret = snd_soc_dai_set_sysclk(codec_dai, RT274_SCLK_S_PLL1, - CNL_FREQ_OUT, - SND_SOC_CLOCK_IN); + CNL_FREQ_OUT, + SND_SOC_CLOCK_IN); if (ret) { dev_err(codec_dai->dev, "failed to enable PLL1: %d\n", ret); @@ -76,8 +76,8 @@ static const struct snd_soc_dapm_widget cnl_rt274_widgets[] = { SND_SOC_DAPM_MIC("Mic Jack", NULL), SND_SOC_DAPM_MIC("SoC DMIC", NULL), SND_SOC_DAPM_SUPPLY("Platform Clock", SND_SOC_NOPM, 0, 0, - cnl_rt274_clock_control, SND_SOC_DAPM_PRE_PMU | - SND_SOC_DAPM_POST_PMD), + cnl_rt274_clock_control, SND_SOC_DAPM_PRE_PMU | + SND_SOC_DAPM_POST_PMD), }; static const struct snd_soc_pcm_stream dai_params_codec = { @@ -116,8 +116,9 @@ static int cnl_rt274_init(struct snd_soc_pcm_runtime *runtime) struct snd_soc_dai *codec_dai = runtime->codec_dai; ret = snd_soc_card_jack_new(runtime->card, "Headset", - SND_JACK_HEADSET, &cnl_headset, - cnl_headset_pins, ARRAY_SIZE(cnl_headset_pins)); + SND_JACK_HEADSET, &cnl_headset, + cnl_headset_pins, + ARRAY_SIZE(cnl_headset_pins)); if (ret) return ret; @@ -137,42 +138,45 @@ static int cnl_rt274_init(struct snd_soc_pcm_runtime *runtime) } static int cnl_be_fixup(struct snd_soc_pcm_runtime *rtd, - struct snd_pcm_hw_params *params) + 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); - rate->min = rate->max = CNL_BE_FIXUP_RATE; - channels->min = channels->max = 2; + rate->min = CNL_BE_FIXUP_RATE; + rate->max = CNL_BE_FIXUP_RATE; + channels->min = 2; + channels->max = 2; snd_mask_none(hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT)); snd_mask_set(hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT), - SNDRV_PCM_FORMAT_S24_LE); + SNDRV_PCM_FORMAT_S24_LE); return 0; } +#if !IS_ENABLED(CONFIG_SND_SOC_SOF_INTEL) static int cnl_dmic_fixup(struct snd_soc_pcm_runtime *rtd, - struct snd_pcm_hw_params *params) + struct snd_pcm_hw_params *params) { struct snd_interval *channels = hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS); - channels->min = channels->max = 2; + channels->min = 2; + channels->max = 2; return 0; } +#endif -static const char pname[] = "0000:00:1f.3"; static const char cname[] = "i2c-INT34C2:00"; -struct snd_soc_dai_link cnl_rt274_dailink[] = { +static struct snd_soc_dai_link cnl_rt274_dailink[] = { { .name = "SSP0-Codec", .cpu_dai_name = "SSP0 Pin", .codec_name = cname, .codec_dai_name = "rt274-aif1", - .platform_name = pname, .be_hw_params_fixup = cnl_be_fixup, .ignore_pmdown_time = 1, .no_pcm = 1, @@ -182,28 +186,20 @@ struct snd_soc_dai_link cnl_rt274_dailink[] = { .dpcm_capture = 1, .init = cnl_rt274_init, }, +#if !IS_ENABLED(CONFIG_SND_SOC_SOF_INTEL) { .name = "dmic01", .cpu_dai_name = "DMIC01 Pin", .codec_name = "dmic-codec", .codec_dai_name = "dmic-hifi", - .platform_name = pname, .ignore_suspend = 1, .no_pcm = 1, .dpcm_capture = 1, .be_hw_params_fixup = cnl_dmic_fixup, }, +#endif }; -static int -cnl_add_dai_link(struct snd_soc_card *card, struct snd_soc_dai_link *link) -{ - link->platform_name = pname; - link->nonatomic = 1; - - return 0; -} - /* SoC card */ static struct snd_soc_card snd_soc_card_cnl = { .name = "cnl-audio", @@ -215,7 +211,6 @@ static struct snd_soc_card snd_soc_card_cnl = { .num_dapm_routes = ARRAY_SIZE(cnl_map), .controls = cnl_controls, .num_controls = ARRAY_SIZE(cnl_controls), - .add_dai_link = cnl_add_dai_link, .fully_routed = true, }; From 270eb2247f2a7accd587add2cbfd07b3a98f106b Mon Sep 17 00:00:00 2001 From: "Wagner, Steffen" Date: Tue, 15 May 2018 17:14:50 +0800 Subject: [PATCH 072/107] ASoC: tdf8532: NXP TDF8532 audio class-D amplifier driver This is a basic driver to register the codec, expose the codec DAI and control the power mode of the amplifier. Change-Id: Ie6ab037cd4d6c87e8e139b6d8af6cd4295445bf2 Signed-off-by: Mohit Sinha Signed-off-by: Steffen Wagner Reviewed-on: https://git-gar-1.devtools.intel.com/gerrit/15296 Reviewed-by: B, Jayachandran Reviewed-by: Shaik, Kareem M Reviewed-by: Koul, Vinod Tested-by: Sm, Bhadur A --- sound/soc/codecs/Kconfig | 5 + sound/soc/codecs/Makefile | 2 + sound/soc/codecs/tdf8532.c | 379 +++++++++++++++++++++++++++++++++++++ sound/soc/codecs/tdf8532.h | 101 ++++++++++ 4 files changed, 487 insertions(+) create mode 100644 sound/soc/codecs/tdf8532.c create mode 100644 sound/soc/codecs/tdf8532.h diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig index 9548f63ca531c8..fbacaa0a724bd4 100644 --- a/sound/soc/codecs/Kconfig +++ b/sound/soc/codecs/Kconfig @@ -157,6 +157,7 @@ config SND_SOC_ALL_CODECS select SND_SOC_TAS5720 if I2C select SND_SOC_TAS6424 if I2C select SND_SOC_TDA7419 if I2C + select SND_SOC_TDF8532 if I2C select SND_SOC_TFA9879 if I2C select SND_SOC_TLV320AIC23_I2C if I2C select SND_SOC_TLV320AIC23_SPI if SPI_MASTER @@ -954,6 +955,10 @@ config SND_SOC_TDA7419 depends on I2C select REGMAP_I2C +config SND_SOC_TDF8532 + tristate + depends on I2C + config SND_SOC_TFA9879 tristate "NXP Semiconductors TFA9879 amplifier" depends on I2C diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile index e849d1495308f9..40480a5243ff2e 100644 --- a/sound/soc/codecs/Makefile +++ b/sound/soc/codecs/Makefile @@ -168,6 +168,7 @@ snd-soc-tas571x-objs := tas571x.o snd-soc-tas5720-objs := tas5720.o snd-soc-tas6424-objs := tas6424.o snd-soc-tda7419-objs := tda7419.o +snd-soc-tdf8532-objs := tdf8532.o snd-soc-tfa9879-objs := tfa9879.o snd-soc-tlv320aic23-objs := tlv320aic23.o snd-soc-tlv320aic23-i2c-objs := tlv320aic23-i2c.o @@ -420,6 +421,7 @@ obj-$(CONFIG_SND_SOC_TAS571X) += snd-soc-tas571x.o obj-$(CONFIG_SND_SOC_TAS5720) += snd-soc-tas5720.o obj-$(CONFIG_SND_SOC_TAS6424) += snd-soc-tas6424.o obj-$(CONFIG_SND_SOC_TDA7419) += snd-soc-tda7419.o +obj-$(CONFIG_SND_SOC_TDF8532) += snd-soc-tdf8532.o obj-$(CONFIG_SND_SOC_TFA9879) += snd-soc-tfa9879.o obj-$(CONFIG_SND_SOC_TLV320AIC23) += snd-soc-tlv320aic23.o obj-$(CONFIG_SND_SOC_TLV320AIC23_I2C) += snd-soc-tlv320aic23-i2c.o diff --git a/sound/soc/codecs/tdf8532.c b/sound/soc/codecs/tdf8532.c new file mode 100644 index 00000000000000..59db83da37587a --- /dev/null +++ b/sound/soc/codecs/tdf8532.c @@ -0,0 +1,379 @@ +/* + * Codec driver for NXP Semiconductors - TDF8532 + * Copyright (c) 2017, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include "tdf8532.h" + +static int __tdf8532_build_pkt(struct tdf8532_priv *dev_data, + va_list valist, u8 *payload) +{ + int param; + u8 len; + u8 *cmd_payload; + const u8 cmd_offset = 3; + + payload[HEADER_TYPE] = MSG_TYPE_STX; + payload[HEADER_PKTID] = dev_data->pkt_id; + + cmd_payload = &(payload[cmd_offset]); + + param = va_arg(valist, int); + len = 0; + + while (param != END) { + cmd_payload[len] = param; + + len++; + + param = va_arg(valist, int); + } + + payload[HEADER_LEN] = len; + + return len + cmd_offset; +} + +static int __tdf8532_single_write(struct tdf8532_priv *dev_data, + int dummy, ...) +{ + va_list valist; + int ret; + u8 len; + u8 payload[255]; + + va_start(valist, dummy); + + len = __tdf8532_build_pkt(dev_data, valist, payload); + + va_end(valist); + + print_hex_dump_debug("tdf8532-codec: Tx:", DUMP_PREFIX_NONE, 32, 1, + payload, len, false); + ret = i2c_master_send(dev_data->i2c, payload, len); + + dev_data->pkt_id++; + + if (ret < 0) { + dev_err(&(dev_data->i2c->dev), + "i2c send packet returned: %d\n", ret); + + return ret; + } + + return 0; +} + + +static uint8_t tdf8532_read_wait_ack(struct tdf8532_priv *dev_data, + unsigned long timeout) +{ + uint8_t ack_repl[HEADER_SIZE] = {0, 0, 0}; + unsigned long timeout_point = jiffies + timeout; + int ret; + + do { + ret = i2c_master_recv(dev_data->i2c, ack_repl, HEADER_SIZE); + if (ret < 0) + goto out; + } while (time_before(jiffies, timeout_point) && + ack_repl[0] != MSG_TYPE_ACK); + + if (ack_repl[0] != MSG_TYPE_ACK) + return -ETIME; + else + return ack_repl[2]; + +out: + return ret; +} + +static uint8_t tdf8532_single_read(struct tdf8532_priv *dev_data, + char **repl_buff) +{ + int ret; + uint8_t len; + + struct device *dev = &(dev_data->i2c->dev); + + ret = tdf8532_read_wait_ack(dev_data, msecs_to_jiffies(ACK_TIMEOUT)); + + if (ret < 0) { + dev_err(dev, + "Error waiting for ACK reply: %d\n", ret); + goto out; + } + + len = ret + HEADER_SIZE; + + *repl_buff = kzalloc(len, GFP_KERNEL); + + ret = i2c_master_recv(dev_data->i2c, *repl_buff, len); + + print_hex_dump_debug("tdf8532-codec: Rx:", DUMP_PREFIX_NONE, 32, 1, + *repl_buff, len, false); + + if (ret < 0 || ret != len) { + dev_err(dev, + "i2c recv packet returned: %d (expected: %d)\n", + ret, len); + goto out_free; + } + + return len; + +out_free: + kfree(*repl_buff); + repl_buff = NULL; +out: + return ret; +} + +static int tdf8532_get_state(struct tdf8532_priv *dev_data, + struct get_dev_status_repl **status_repl) +{ + int ret = 0; + char *repl_buff = NULL; + + ret = tdf8532_amp_write(dev_data, GET_DEV_STATUS); + if (ret < 0) + goto out; + + ret = tdf8532_single_read(dev_data, &repl_buff); + if (ret < 0) + goto out; + + *status_repl = (struct get_dev_status_repl *) repl_buff; + +out: + return ret; +} + +static int tdf8532_wait_state(struct tdf8532_priv *dev_data, u8 req_state, + unsigned long timeout) +{ + unsigned long timeout_point = jiffies + msecs_to_jiffies(timeout); + int ret; + struct get_dev_status_repl *status_repl; + struct device *dev = &(dev_data->i2c->dev); + + do { + ret = tdf8532_get_state(dev_data, &status_repl); + if (ret < 0) + goto out; + + print_hex_dump_debug("tdf8532-codec: wait_state: ", + DUMP_PREFIX_NONE, 32, 1, status_repl, + 6, false); + } while (time_before(jiffies, timeout_point) + && status_repl->state != req_state); + + if (status_repl->state == req_state) + return 0; + + ret = -ETIME; + + dev_err(dev, "tdf8532-codec: state: %u, req_state: %u, ret: %d\n", + status_repl->state, req_state, ret); + +out: + kfree(status_repl); + return ret; +} + +static int tdf8532_start_play(struct tdf8532_priv *tdf8532) +{ + int ret; + + ret = tdf8532_amp_write(tdf8532, SET_CLK_STATE, CLK_CONNECT); + if (ret < 0) + return ret; + + ret = tdf8532_amp_write(tdf8532, SET_CHNL_ENABLE, + CHNL_MASK(tdf8532->channels)); + + if (ret >= 0) + ret = tdf8532_wait_state(tdf8532, STATE_PLAY, ACK_TIMEOUT); + + return ret; +} + + +static int tdf8532_stop_play(struct tdf8532_priv *tdf8532) +{ + int ret; + + ret = tdf8532_amp_write(tdf8532, SET_CHNL_DISABLE, + CHNL_MASK(tdf8532->channels)); + if (ret < 0) + goto out; + + ret = tdf8532_wait_state(tdf8532, STATE_STBY, ACK_TIMEOUT); + if (ret < 0) + goto out; + + ret = tdf8532_amp_write(tdf8532, SET_CLK_STATE, CLK_DISCONNECT); + if (ret < 0) + goto out; + + ret = tdf8532_wait_state(tdf8532, STATE_IDLE, ACK_TIMEOUT); + +out: + return ret; +} + + +static int tdf8532_dai_trigger(struct snd_pcm_substream *substream, int cmd, + struct snd_soc_dai *dai) +{ + int ret = 0; + struct snd_soc_codec *codec = dai->codec; + struct tdf8532_priv *tdf8532 = snd_soc_codec_get_drvdata(codec); + + dev_dbg(codec->dev, "%s: cmd = %d\n", __func__, cmd); + + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: + case SNDRV_PCM_TRIGGER_RESUME: + ret = tdf8532_start_play(tdf8532); + break; + case SNDRV_PCM_TRIGGER_PAUSE_PUSH: + case SNDRV_PCM_TRIGGER_SUSPEND: + case SNDRV_PCM_TRIGGER_STOP: + ret = tdf8532_stop_play(tdf8532); + break; + } + + return ret; +} + +static int tdf8532_mute(struct snd_soc_dai *dai, int mute) +{ + struct snd_soc_codec *codec = dai->codec; + struct tdf8532_priv *tdf8532 = snd_soc_codec_get_drvdata(dai->codec); + + dev_dbg(codec->dev, "%s\n", __func__); + + if (mute) + return tdf8532_amp_write(tdf8532, SET_CHNL_MUTE, + CHNL_MASK(CHNL_MAX)); + else + return tdf8532_amp_write(tdf8532, SET_CHNL_UNMUTE, + CHNL_MASK(CHNL_MAX)); +} + +static const struct snd_soc_dai_ops tdf8532_dai_ops = { + .trigger = tdf8532_dai_trigger, + .digital_mute = tdf8532_mute, +}; + +static struct snd_soc_codec_driver soc_codec_tdf8532; + +static struct snd_soc_dai_driver tdf8532_dai[] = { + { + .name = "tdf8532-hifi", + .playback = { + .stream_name = "Playback", + .channels_min = 4, + .channels_max = 4, + .rates = SNDRV_PCM_RATE_48000, + .formats = SNDRV_PCM_FMTBIT_S16_LE, + }, + .ops = &tdf8532_dai_ops, + } +}; + +static int tdf8532_i2c_probe(struct i2c_client *i2c, + const struct i2c_device_id *id) +{ + int ret; + struct tdf8532_priv *dev_data; + struct device *dev = &(i2c->dev); + + dev_dbg(&i2c->dev, "%s\n", __func__); + + dev_data = devm_kzalloc(dev, sizeof(struct tdf8532_priv), GFP_KERNEL); + + if (!dev_data) { + ret = -ENOMEM; + goto out; + } + + if (ret < 0) + dev_err(&i2c->dev, "Failed to set fast mute option: %d\n", ret); + + dev_data->i2c = i2c; + dev_data->pkt_id = 0; + dev_data->channels = 4; + + i2c_set_clientdata(i2c, dev_data); + + ret = snd_soc_register_codec(&i2c->dev, &soc_codec_tdf8532, + tdf8532_dai, ARRAY_SIZE(tdf8532_dai)); + if (ret != 0) { + dev_err(&i2c->dev, "Failed to register codec: %d\n", ret); + goto out; + } + +out: + return ret; +} + +static int tdf8532_i2c_remove(struct i2c_client *i2c) +{ + snd_soc_unregister_codec(&i2c->dev); + + return 0; +} + +static const struct i2c_device_id tdf8532_i2c_id[] = { + { "tdf8532", 0 }, + { } +}; + +MODULE_DEVICE_TABLE(i2c, tdf8532_i2c_id); + +#if CONFIG_ACPI +static const struct acpi_device_id tdf8532_acpi_match[] = { + {"INT34C3", 0}, + {}, +}; + +MODULE_DEVICE_TABLE(acpi, tdf8532_acpi_match); +#endif + +static struct i2c_driver tdf8532_i2c_driver = { + .driver = { + .name = "tdf8532-codec", + .owner = THIS_MODULE, + .acpi_match_table = ACPI_PTR(tdf8532_acpi_match), + }, + .probe = tdf8532_i2c_probe, + .remove = tdf8532_i2c_remove, + .id_table = tdf8532_i2c_id, +}; + +module_i2c_driver(tdf8532_i2c_driver); + +MODULE_DESCRIPTION("ASoC NXP Semiconductors TDF8532 driver"); +MODULE_AUTHOR("Steffen Wagner "); +MODULE_LICENSE("GPL v2"); diff --git a/sound/soc/codecs/tdf8532.h b/sound/soc/codecs/tdf8532.h new file mode 100644 index 00000000000000..6e3f2c147eace9 --- /dev/null +++ b/sound/soc/codecs/tdf8532.h @@ -0,0 +1,101 @@ +/* + * tdf8532.h - Codec driver for NXP Semiconductors + * Copyright (c) 2017, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope 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. + */ + + +#ifndef __TDF8532_H_ +#define __TDF8532_H_ + +#define ACK_TIMEOUT 300 + +#define CHNL_MAX 5 + +#define AMP_MOD 0x80 +#define END -1 + +#define MSG_TYPE_STX 0x02 +#define MSG_TYPE_NAK 0x15 +#define MSG_TYPE_ACK 0x6 + +#define HEADER_SIZE 3 +#define HEADER_TYPE 0 +#define HEADER_PKTID 1 +#define HEADER_LEN 2 + +/* Set commands */ +#define SET_CLK_STATE 0x1A +#define CLK_DISCONNECT 0x00 +#define CLK_CONNECT 0x01 + +#define SET_CHNL_ENABLE 0x26 +#define SET_CHNL_DISABLE 0x27 + +#define SET_CHNL_MUTE 0x42 +#define SET_CHNL_UNMUTE 0x43 + +struct header_repl { + u8 msg_type; + u8 pkt_id; + u8 len; +} __packed; + +#define GET_IDENT 0xE0 + +struct get_ident_repl { + struct header_repl header; + u8 module_id; + u8 cmd_id; + u8 type_name; + u8 hw_major; + u8 hw_minor; + u8 sw_major; + u8 sw_minor; + u8 sw_sub; +} __packed; + +#define GET_ERROR 0xE2 + +struct get_error_repl { + struct header_repl header; + u8 module_id; + u8 cmd_id; + u8 last_cmd_id; + u8 error; + u8 status; +} __packed; + +#define GET_DEV_STATUS 0x80 + +enum dev_state {STATE_BOOT, STATE_IDLE, STATE_STBY, STATE_LDAG, STATE_PLAY, + STATE_PROT, STATE_SDWN, STATE_CLFA, STATE_NONE }; + +struct get_dev_status_repl { + struct header_repl header; + u8 module_id; + u8 cmd_id; + u8 state; +} __packed; + +/* Helpers */ +#define CHNL_MASK(channels) (u8)((0x00FF << channels) >> 8) + +#define tdf8532_amp_write(dev_data, ...)\ + __tdf8532_single_write(dev_data, 0, AMP_MOD, __VA_ARGS__, END) + +struct tdf8532_priv { + struct i2c_client *i2c; + u8 channels; + u8 pkt_id; +}; + +#endif From f7bae901d8f62c46f1143db913c9a5a0c6f2e55b Mon Sep 17 00:00:00 2001 From: "Gogineni, GiribabuX" Date: Tue, 15 May 2018 17:14:51 +0800 Subject: [PATCH 073/107] ASoC: tdf8532: Fix compilation warnings Initialized the reported variables, listed below warning: 'ret' may be used uninitialized in this function warning: 'status_repl' may be used uninitialized in this function Change-Id: I6ca5a6e017402a582239d75959c122ffaa9f7298 Signed-off-by: Gogineni, GiribabuX Reviewed-on: https://git-gar-1.devtools.intel.com/gerrit/17572 Reviewed-by: Singh, Guneshwor O Reviewed-by: Sinha, Mohit Reviewed-by: Shaik, Kareem M Reviewed-by: Koul, Vinod Tested-by: Sm, Bhadur A --- sound/soc/codecs/tdf8532.c | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/sound/soc/codecs/tdf8532.c b/sound/soc/codecs/tdf8532.c index 59db83da37587a..5f072079fbb76b 100644 --- a/sound/soc/codecs/tdf8532.c +++ b/sound/soc/codecs/tdf8532.c @@ -172,7 +172,7 @@ static int tdf8532_wait_state(struct tdf8532_priv *dev_data, u8 req_state, { unsigned long timeout_point = jiffies + msecs_to_jiffies(timeout); int ret; - struct get_dev_status_repl *status_repl; + struct get_dev_status_repl *status_repl = NULL; struct device *dev = &(dev_data->i2c->dev); do { @@ -318,9 +318,6 @@ static int tdf8532_i2c_probe(struct i2c_client *i2c, goto out; } - if (ret < 0) - dev_err(&i2c->dev, "Failed to set fast mute option: %d\n", ret); - dev_data->i2c = i2c; dev_data->pkt_id = 0; dev_data->channels = 4; From e26299c1dad5e7af4fc4a0bb8f0f8f36b4665ceb Mon Sep 17 00:00:00 2001 From: "Gogineni, GiribabuX" Date: Tue, 15 May 2018 17:14:52 +0800 Subject: [PATCH 074/107] ASoC: tdf8532: Add delay while reading a packet from I2C While doing the continuous play and stop, the codec may not be ready for I2C reading after successive writes. This triggers BE failure, because I2C reading value is incorrect. Fix this by adding 10ms delay to ensure the smooth I2C read and write. Change-Id: If918e263bc799fecc2c807229f5b4b165e011fa6 Signed-off-by: Gogineni, GiribabuX Reviewed-on: https://git-gar-1.devtools.intel.com/gerrit/20404 Reviewed-by: Shaik, Kareem M Reviewed-by: Sinha, Mohit Reviewed-by: Nc, Shreyas Reviewed-by: Periyasamy, SriramX Reviewed-by: Kale, Sanyog R Tested-by: Sm, Bhadur A --- sound/soc/codecs/tdf8532.c | 1 + 1 file changed, 1 insertion(+) diff --git a/sound/soc/codecs/tdf8532.c b/sound/soc/codecs/tdf8532.c index 5f072079fbb76b..1860e8264ebab8 100644 --- a/sound/soc/codecs/tdf8532.c +++ b/sound/soc/codecs/tdf8532.c @@ -90,6 +90,7 @@ static uint8_t tdf8532_read_wait_ack(struct tdf8532_priv *dev_data, unsigned long timeout_point = jiffies + timeout; int ret; + usleep_range(10000,20000); do { ret = i2c_master_recv(dev_data->i2c, ack_repl, HEADER_SIZE); if (ret < 0) From 8dd7f4a1624add0690102ffb09630503a8fa3b14 Mon Sep 17 00:00:00 2001 From: Wu Zhigang Date: Tue, 15 May 2018 17:14:53 +0800 Subject: [PATCH 075/107] ASoC: tdf8532: Fix the codec status error issue on APL-GPMRB Based on the TDF8532 manual: If the wait_state result is ok, we should send CLK_DISCONNECT command to force codec from STANDBY(2) to IDLE(1). If the wait_state result is timeout, the codec state should be at Clockfail(7), we still should send CLK_DISCONNECT command force the codec from Clockfail(7) to Idle(1). Signed-off-by: Wu Zhigang Reviewed-by: Keyon Jie --- sound/soc/codecs/tdf8532.c | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/sound/soc/codecs/tdf8532.c b/sound/soc/codecs/tdf8532.c index 1860e8264ebab8..c7bdb3960eaf49 100644 --- a/sound/soc/codecs/tdf8532.c +++ b/sound/soc/codecs/tdf8532.c @@ -192,7 +192,7 @@ static int tdf8532_wait_state(struct tdf8532_priv *dev_data, u8 req_state, ret = -ETIME; - dev_err(dev, "tdf8532-codec: state: %u, req_state: %u, ret: %d\n", + dev_warn(dev, "tdf8532-codec: state: %u, req_state: %u, ret: %d\n", status_repl->state, req_state, ret); out: @@ -228,8 +228,14 @@ static int tdf8532_stop_play(struct tdf8532_priv *tdf8532) goto out; ret = tdf8532_wait_state(tdf8532, STATE_STBY, ACK_TIMEOUT); - if (ret < 0) - goto out; + + /* Refer to TDF8532 manual: + * If the wait_state result is ok, we should send CLK_DISCONNECT + * command to force codec from STANDBY(2) to IDLE(1). + * If the wait_state result is timeout, the codec state should be + * at Clockfail(7), we still should send CLK_DISCONNECT command + * force the codec from Clockfail(7) to Idle(1). + */ ret = tdf8532_amp_write(tdf8532, SET_CLK_STATE, CLK_DISCONNECT); if (ret < 0) From 052f669324d26b4b161866c50bc37ccde4349633 Mon Sep 17 00:00:00 2001 From: Keyon Jie Date: Tue, 22 May 2018 18:23:43 +0800 Subject: [PATCH 076/107] ASoC: Intel: Boards: Add BXTP MRB machine driver for NXP TDF8532 This is the machine driver for NXP TDF8532, from production kernel, rebased it to sof-v4.14 base. Signed-off-by: Sinha, Mohit Signed-off-by: Markus Schweikhardt Signed-off-by: Pankaj Bharadiya Signed-off-by: Kareem,Shaik Signed-off-by: Keyon Jie --- sound/soc/intel/boards/Kconfig | 9 + sound/soc/intel/boards/Makefile | 2 + sound/soc/intel/boards/bxt_tdf8532.c | 431 +++++++++++++++++++++++++++ 3 files changed, 442 insertions(+) create mode 100644 sound/soc/intel/boards/bxt_tdf8532.c diff --git a/sound/soc/intel/boards/Kconfig b/sound/soc/intel/boards/Kconfig index 0037fa7bd73d0b..44168d6f0fd541 100644 --- a/sound/soc/intel/boards/Kconfig +++ b/sound/soc/intel/boards/Kconfig @@ -259,6 +259,15 @@ config SND_SOC_INTEL_BXT_PCM512x_MACH This adds support for ASoC machine driver for Broxton platforms with TI PCM512x I2S audio codec. Say Y or m if you have such a device. This is a recommended option. + +config SND_SOC_INTEL_BXT_TDF8532_MACH + tristate "ASoC Audio driver for BXT with TDF8532 in I2S mode" + depends on X86 && ACPI && I2C + select SND_SOC_TDF8532 + select SND_SOC_COMPRESS + help + This adds support for ASoC machine driver for Broxton IVI GP MRB platform + Say Y if you have such a device. If unsure select "N". endif ## SND_SOC_INTEL_SKL || SND_SOC_SOF_APOLLOLAKE diff --git a/sound/soc/intel/boards/Makefile b/sound/soc/intel/boards/Makefile index f58a9bda911ddd..8c7d03abeff397 100644 --- a/sound/soc/intel/boards/Makefile +++ b/sound/soc/intel/boards/Makefile @@ -7,6 +7,7 @@ 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-bxt-pcm512x-objs := bxt_pcm512x.o +snd-soc-sst_bxt_tdf8532-objs := bxt_tdf8532.o snd-soc-sst-bytcr-rt5640-objs := bytcr_rt5640.o snd-soc-sst-bytcr-rt5651-objs := bytcr_rt5651.o snd-soc-sst-cht-bsw-rt5672-objs := cht_bsw_rt5672.o @@ -30,6 +31,7 @@ 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_BXT_PCM512x_MACH) += snd-soc-sst-bxt-pcm512x.o +obj-$(CONFIG_SND_SOC_INTEL_BXT_TDF8532_MACH) += snd-soc-sst_bxt_tdf8532.o obj-$(CONFIG_SND_SOC_INTEL_BROADWELL_MACH) += snd-soc-sst-broadwell.o obj-$(CONFIG_SND_SOC_INTEL_BDW_RT5677_MACH) += snd-soc-sst-bdw-rt5677-mach.o obj-$(CONFIG_SND_SOC_INTEL_BYTCR_RT5640_MACH) += snd-soc-sst-bytcr-rt5640.o diff --git a/sound/soc/intel/boards/bxt_tdf8532.c b/sound/soc/intel/boards/bxt_tdf8532.c new file mode 100644 index 00000000000000..38dc1affe9dcd5 --- /dev/null +++ b/sound/soc/intel/boards/bxt_tdf8532.c @@ -0,0 +1,431 @@ +/* + * Intel Broxton-P I2S Machine Driver for IVI reference platform + * Copyright (c) 2017, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope 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 +#include +#include +#include +#include +#include + +static const struct snd_kcontrol_new broxton_tdf8532_controls[] = { + SOC_DAPM_PIN_SWITCH("Speaker"), +}; + +static const struct snd_soc_dapm_widget broxton_tdf8532_widgets[] = { + SND_SOC_DAPM_SPK("Speaker", NULL), + SND_SOC_DAPM_MIC("DiranaCp", NULL), + SND_SOC_DAPM_HP("DiranaPb", NULL), + SND_SOC_DAPM_MIC("HdmiIn", NULL), + SND_SOC_DAPM_MIC("TestPinCp", NULL), + SND_SOC_DAPM_HP("TestPinPb", NULL), + SND_SOC_DAPM_MIC("BtHfpDl", NULL), + SND_SOC_DAPM_HP("BtHfpUl", NULL), + SND_SOC_DAPM_MIC("ModemDl", NULL), + SND_SOC_DAPM_HP("ModemUl", NULL), +}; + +static const struct snd_soc_dapm_route broxton_tdf8532_map[] = { + /* Speaker BE connections */ + { "Speaker", NULL, "ssp4 Tx"}, + { "ssp4 Tx", NULL, "codec0_out"}, + + { "dirana_in", NULL, "ssp2 Rx"}, + { "ssp2 Rx", NULL, "DiranaCp"}, + + { "dirana_aux_in", NULL, "ssp2 Rx"}, + { "ssp2 Rx", NULL, "DiranaCp"}, + + { "dirana_tuner_in", NULL, "ssp2 Rx"}, + { "ssp2 Rx", NULL, "DiranaCp"}, + + { "DiranaPb", NULL, "ssp2 Tx"}, + { "ssp2 Tx", NULL, "dirana_out"}, + + { "hdmi_ssp1_in", NULL, "ssp1 Rx"}, + { "ssp1 Rx", NULL, "HdmiIn"}, + + { "TestPin_ssp5_in", NULL, "ssp5 Rx"}, + { "ssp5 Rx", NULL, "TestPinCp"}, + + { "TestPinPb", NULL, "ssp5 Tx"}, + { "ssp5 Tx", NULL, "TestPin_ssp5_out"}, + + { "BtHfp_ssp0_in", NULL, "ssp0 Rx"}, + { "ssp0 Rx", NULL, "BtHfpDl"}, + + { "BtHfpUl", NULL, "ssp0 Tx"}, + { "ssp0 Tx", NULL, "BtHfp_ssp0_out"}, + + { "Modem_ssp3_in", NULL, "ssp3 Rx"}, + { "ssp3 Rx", NULL, "ModemDl"}, + + { "ModemUl", NULL, "ssp3 Tx"}, + { "ssp3 Tx", NULL, "Modem_ssp3_out"}, +}; + +static int bxt_tdf8532_ssp2_fixup(struct snd_soc_pcm_runtime *rtd, + struct snd_pcm_hw_params *params) +{ + struct snd_mask *fmt = hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT); + + /* set SSP to 32 bit */ + snd_mask_none(fmt); + snd_mask_set(fmt, SNDRV_PCM_FORMAT_S32_LE); + + return 0; +} + +/* broxton digital audio interface glue - connects codec <--> CPU */ +static struct snd_soc_dai_link broxton_tdf8532_dais[] = { + /* Front End DAI links */ + { + .name = "Speaker Port", + .stream_name = "Speaker", + .cpu_dai_name = "Speaker Pin", + .platform_name = "0000:00:0e.0", + .nonatomic = 1, + .dynamic = 1, + .codec_name = "snd-soc-dummy", + .codec_dai_name = "snd-soc-dummy-dai", + .trigger = { + SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST + }, + .dpcm_playback = 1, + }, + { + .name = "Dirana Capture Port", + .stream_name = "Dirana Cp", + .cpu_dai_name = "Dirana Cp 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, + }, + { + .name = "Dirana Playback Port", + .stream_name = "Dirana Pb", + .cpu_dai_name = "Dirana Pb Pin", + .platform_name = "0000:00:0e.0", + .nonatomic = 1, + .dynamic = 1, + .codec_name = "snd-soc-dummy", + .codec_dai_name = "snd-soc-dummy-dai", + .trigger = { + SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST + }, + .dpcm_playback = 1, + }, + { + .name = "TestPin Capture Port", + .stream_name = "TestPin Cp", + .cpu_dai_name = "TestPin Cp 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, + }, + { + .name = "TestPin Playback Port", + .stream_name = "TestPin Pb", + .cpu_dai_name = "TestPin Pb Pin", + .platform_name = "0000:00:0e.0", + .nonatomic = 1, + .dynamic = 1, + .codec_name = "snd-soc-dummy", + .codec_dai_name = "snd-soc-dummy-dai", + .trigger = { + SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST + }, + .dpcm_playback = 1, + }, + { + .name = "BtHfp Capture Port", + .stream_name = "BtHfp Cp", + .cpu_dai_name = "BtHfp Cp 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, + }, + { + .name = "BtHfp Playback Port", + .stream_name = "BtHfp Pb", + .cpu_dai_name = "BtHfp Pb Pin", + .platform_name = "0000:00:0e.0", + .nonatomic = 1, + .dynamic = 1, + .codec_name = "snd-soc-dummy", + .codec_dai_name = "snd-soc-dummy-dai", + .trigger = { + SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST + }, + .dpcm_playback = 1, + }, + { + .name = "Modem Capture Port", + .stream_name = "Modem Cp", + .cpu_dai_name = "Modem Cp 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, + }, + { + .name = "Modem Playback Port", + .stream_name = "Modem Pb", + .cpu_dai_name = "Modem Pb Pin", + .platform_name = "0000:00:0e.0", + .nonatomic = 1, + .dynamic = 1, + .codec_name = "snd-soc-dummy", + .codec_dai_name = "snd-soc-dummy-dai", + .trigger = { + SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST + }, + .dpcm_playback = 1, + }, + { + .name = "HDMI Capture Port", + .stream_name = "HDMI Cp", + .cpu_dai_name = "HDMI Cp 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, + }, + { + .name = "Dirana Aux Capture Port", + .stream_name = "Dirana Aux Cp", + .cpu_dai_name = "Dirana Aux Cp 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, + }, + { + .name = "Dirana Tuner Capture Port", + .stream_name = "Dirana Tuner Cp", + .cpu_dai_name = "Dirana Tuner Cp 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, + }, + /* Probe DAI links*/ + { + .name = "Bxt Compress Probe playback", + .stream_name = "Probe Playback", + .cpu_dai_name = "Compress Probe0 Pin", + .codec_name = "snd-soc-dummy", + .codec_dai_name = "snd-soc-dummy-dai", + .platform_name = "0000:00:0e.0", + .init = NULL, + .nonatomic = 1, + }, + { + .name = "Bxt Compress Probe capture", + .stream_name = "Probe Capture", + .cpu_dai_name = "Compress Probe1 Pin", + .codec_name = "snd-soc-dummy", + .codec_dai_name = "snd-soc-dummy-dai", + .platform_name = "0000:00:0e.0", + .init = NULL, + .nonatomic = 1, + }, + /* Trace Buffer DAI links */ + { + .name = "Bxt Trace Buffer0", + .stream_name = "Core 0 Trace Buffer", + .cpu_dai_name = "TraceBuffer0 Pin", + .codec_name = "snd-soc-dummy", + .codec_dai_name = "snd-soc-dummy-dai", + .platform_name = "0000:00:0e.0", + .capture_only = true, + .ignore_suspend = 1, + }, + { + .name = "Bxt Trace Buffer1", + .stream_name = "Core 1 Trace Buffer", + .cpu_dai_name = "TraceBuffer1 Pin", + .codec_name = "snd-soc-dummy", + .codec_dai_name = "snd-soc-dummy-dai", + .platform_name = "0000:00:0e.0", + .capture_only = true, + .ignore_suspend = 1, + }, + /* Back End DAI links */ + { + /* SSP0 - BT */ + .name = "SSP0-Codec", + .id = 0, + .cpu_dai_name = "SSP0 Pin", + .codec_name = "snd-soc-dummy", + .codec_dai_name = "snd-soc-dummy-dai", + .platform_name = "0000:00:0e.0", + .ignore_suspend = 1, + .dpcm_capture = 1, + .dpcm_playback = 1, + .no_pcm = 1, + }, + { + /* SSP1 - HDMI-In */ + .name = "SSP1-Codec", + .id = 1, + .cpu_dai_name = "SSP1 Pin", + .codec_name = "snd-soc-dummy", + .codec_dai_name = "snd-soc-dummy-dai", + .platform_name = "0000:00:0e.0", + .ignore_suspend = 1, + .dpcm_capture = 1, + .no_pcm = 1, + }, + { + /* SSP2 - Dirana */ + .name = "SSP2-Codec", + .id = 2, + .cpu_dai_name = "SSP2 Pin", + .codec_name = "snd-soc-dummy", + .codec_dai_name = "snd-soc-dummy-dai", + .platform_name = "0000:00:0e.0", + .ignore_suspend = 1, + .dpcm_capture = 1, + .dpcm_playback = 1, + .no_pcm = 1, + .be_hw_params_fixup = bxt_tdf8532_ssp2_fixup, + }, + { + /* SSP3 - Modem */ + .name = "SSP3-Codec", + .id = 3, + .cpu_dai_name = "SSP3 Pin", + .codec_name = "snd-soc-dummy", + .codec_dai_name = "snd-soc-dummy-dai", + .platform_name = "0000:00:0e.0", + .ignore_suspend = 1, + .dpcm_capture = 1, + .dpcm_playback = 1, + .no_pcm = 1, + }, + { + /* SSP4 - Amplifier */ + .name = "SSP4-Codec", + .id = 4, + .cpu_dai_name = "SSP4 Pin", + .codec_name = "i2c-INT34C3:00", + .codec_dai_name = "tdf8532-hifi", + .platform_name = "0000:00:0e.0", + .ignore_suspend = 1, + .dpcm_playback = 1, + .no_pcm = 1, + }, + { + /* SSP5 - TestPin */ + .name = "SSP5-Codec", + .id = 5, + .cpu_dai_name = "SSP5 Pin", + .codec_name = "snd-soc-dummy", + .codec_dai_name = "snd-soc-dummy-dai", + .platform_name = "0000:00:0e.0", + .ignore_suspend = 1, + .dpcm_capture = 1, + .dpcm_playback = 1, + .no_pcm = 1, + }, +}; + +static int bxt_add_dai_link(struct snd_soc_card *card, + struct snd_soc_dai_link *link) +{ + link->platform_name = "0000:00:0e.0"; + link->nonatomic = 1; + return 0; +} + +/* broxton audio machine driver for TDF8532 */ +static struct snd_soc_card broxton_tdf8532 = { + .name = "broxton_tdf8532", + .dai_link = broxton_tdf8532_dais, + .num_links = ARRAY_SIZE(broxton_tdf8532_dais), + .controls = broxton_tdf8532_controls, + .num_controls = ARRAY_SIZE(broxton_tdf8532_controls), + .dapm_widgets = broxton_tdf8532_widgets, + .num_dapm_widgets = ARRAY_SIZE(broxton_tdf8532_widgets), + .dapm_routes = broxton_tdf8532_map, + .num_dapm_routes = ARRAY_SIZE(broxton_tdf8532_map), + .fully_routed = true, + .add_dai_link = bxt_add_dai_link, +}; + +static int broxton_tdf8532_audio_probe(struct platform_device *pdev) +{ + dev_info(&pdev->dev, "%s registering %s\n", __func__, pdev->name); + broxton_tdf8532.dev = &pdev->dev; + return snd_soc_register_card(&broxton_tdf8532); +} + +static int broxton_tdf8532_audio_remove(struct platform_device *pdev) +{ + snd_soc_unregister_card(&broxton_tdf8532); + return 0; +} + +static struct platform_driver broxton_tdf8532_audio = { + .probe = broxton_tdf8532_audio_probe, + .remove = broxton_tdf8532_audio_remove, + .driver = { + .name = "bxt_tdf8532", + .pm = &snd_soc_pm_ops, + }, +}; + +module_platform_driver(broxton_tdf8532_audio) + +/* Module information */ +MODULE_DESCRIPTION("Intel SST Audio for Broxton GP MRB"); +MODULE_LICENSE("GPL v2"); +MODULE_ALIAS("platform:gpmrb_machine"); From 439aaa969fa4ea65fd8bd3f4fcb86aa844dc1ace Mon Sep 17 00:00:00 2001 From: Keyon Jie Date: Tue, 22 May 2018 18:23:44 +0800 Subject: [PATCH 077/107] ASoC: Intel: bxt-tdf8532: reuse machine driver for GP-MRB Signed-off-by: Keyon Jie --- sound/soc/intel/boards/bxt_tdf8532.c | 1 + 1 file changed, 1 insertion(+) diff --git a/sound/soc/intel/boards/bxt_tdf8532.c b/sound/soc/intel/boards/bxt_tdf8532.c index 38dc1affe9dcd5..f5c2a06ae44e6e 100644 --- a/sound/soc/intel/boards/bxt_tdf8532.c +++ b/sound/soc/intel/boards/bxt_tdf8532.c @@ -429,3 +429,4 @@ module_platform_driver(broxton_tdf8532_audio) MODULE_DESCRIPTION("Intel SST Audio for Broxton GP MRB"); MODULE_LICENSE("GPL v2"); MODULE_ALIAS("platform:gpmrb_machine"); +MODULE_ALIAS("platform:bxt_tdf8532"); From b6666c6db6271f3d1177f77629e7b97c7a2055e3 Mon Sep 17 00:00:00 2001 From: Keyon Jie Date: Mon, 28 May 2018 13:43:18 +0800 Subject: [PATCH 078/107] ASoC: Intel: bxt-tdf8532: FIX: don't use add_dai_link() for SOF We set ignore_machine for SOF soc platform driver, which will trigger overriding FEs in soc_check_tplg_fes(), but this overriding may be overidden again by add_dai_link() in bxt_tdf8532, e.g. platform_name will be modified to be "0000:00:0e.0", which is not exist in SOF. Here add #ifdef to bypass add_dai_link() for using the machine driver with SOF to fix the overridden again issue. Signed-off-by: Keyon Jie --- sound/soc/intel/boards/bxt_tdf8532.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/sound/soc/intel/boards/bxt_tdf8532.c b/sound/soc/intel/boards/bxt_tdf8532.c index f5c2a06ae44e6e..ffd9cc5dace7a8 100644 --- a/sound/soc/intel/boards/bxt_tdf8532.c +++ b/sound/soc/intel/boards/bxt_tdf8532.c @@ -378,6 +378,7 @@ static struct snd_soc_dai_link broxton_tdf8532_dais[] = { }, }; +#if !IS_ENABLED(CONFIG_SND_SOC_SOF_INTEL) static int bxt_add_dai_link(struct snd_soc_card *card, struct snd_soc_dai_link *link) { @@ -385,6 +386,7 @@ static int bxt_add_dai_link(struct snd_soc_card *card, link->nonatomic = 1; return 0; } +#endif /* broxton audio machine driver for TDF8532 */ static struct snd_soc_card broxton_tdf8532 = { @@ -398,7 +400,9 @@ static struct snd_soc_card broxton_tdf8532 = { .dapm_routes = broxton_tdf8532_map, .num_dapm_routes = ARRAY_SIZE(broxton_tdf8532_map), .fully_routed = true, +#if !IS_ENABLED(CONFIG_SND_SOC_SOF_INTEL) .add_dai_link = bxt_add_dai_link, +#endif }; static int broxton_tdf8532_audio_probe(struct platform_device *pdev) From 77467b910757a877eaab9df7f0f84a2c53267729 Mon Sep 17 00:00:00 2001 From: Liam Girdwood Date: Fri, 3 Nov 2017 20:56:42 +0000 Subject: [PATCH 079/107] ALSA: HACK: Fix rmmod crash Something appears to corrupt the runtime->status on second playback... Signed-off-by: Liam Girdwood --- sound/core/pcm.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/sound/core/pcm.c b/sound/core/pcm.c index 66ac89aad68178..cee83aac0e565a 100644 --- a/sound/core/pcm.c +++ b/sound/core/pcm.c @@ -1160,6 +1160,7 @@ static int snd_pcm_dev_disconnect(struct snd_device *device) list_del_init(&pcm->list); for (cidx = 0; cidx < 2; cidx++) { for (substream = pcm->streams[cidx].substream; substream; substream = substream->next) { +#if 0 snd_pcm_stream_lock_irq(substream); if (substream->runtime) { if (snd_pcm_running(substream)) @@ -1171,6 +1172,7 @@ static int snd_pcm_dev_disconnect(struct snd_device *device) wake_up(&substream->runtime->tsleep); } snd_pcm_stream_unlock_irq(substream); +#endif } } From dbf9e8c5192184d0b5e90cfaca27d753c6f3ea95 Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Mon, 16 Apr 2018 13:20:32 -0500 Subject: [PATCH 080/107] [NOT FOR UPSTREAM] ASoC: SOF: enable DEBUG by default better to use dynamic debug for products but it's really convenient Signed-off-by: Pierre-Louis Bossart --- sound/soc/sof/Makefile | 2 ++ 1 file changed, 2 insertions(+) diff --git a/sound/soc/sof/Makefile b/sound/soc/sof/Makefile index ece93000a1974d..b759554d497c2c 100644 --- a/sound/soc/sof/Makefile +++ b/sound/soc/sof/Makefile @@ -1,5 +1,7 @@ # SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause) +ccflags-y += -DDEBUG + snd-sof-objs := core.o ops.o loader.o ipc.o pcm.o pm.o debug.o topology.o\ control.o trace.o compressed.o snd-sof-spi-objs := hw-spi.o From bc4b9e39006f67b4d56c203c36469fa6e7bbdd55 Mon Sep 17 00:00:00 2001 From: Pan Xiuli Date: Thu, 7 Jun 2018 14:24:12 +0800 Subject: [PATCH 081/107] ASoC: Intel: replace snd_soc_codec to snd_soc_component in bxt-pcm512x Codec may be can not use now, use the component instead. Signed-off-by: Pan Xiuli --- sound/soc/intel/boards/bxt_pcm512x.c | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/sound/soc/intel/boards/bxt_pcm512x.c b/sound/soc/intel/boards/bxt_pcm512x.c index 5bc80853707dd2..c375d0c201582e 100644 --- a/sound/soc/intel/boards/bxt_pcm512x.c +++ b/sound/soc/intel/boards/bxt_pcm512x.c @@ -67,9 +67,10 @@ static int codec_fixup(struct snd_soc_pcm_runtime *rtd, static int aif1_startup(struct snd_pcm_substream *substream) { struct snd_soc_pcm_runtime *rtd = substream->private_data; - struct snd_soc_codec *codec = rtd->codec; + struct snd_soc_component *codec = rtd->codec_dai->component; - snd_soc_update_bits(codec, PCM512x_GPIO_CONTROL_1, 0x08, 0x08); + snd_soc_component_update_bits(codec, PCM512x_GPIO_CONTROL_1, + 0x08, 0x08); return snd_pcm_hw_constraint_single(substream->runtime, SNDRV_PCM_HW_PARAM_RATE, 48000); @@ -78,18 +79,20 @@ static int aif1_startup(struct snd_pcm_substream *substream) static void aif1_shutdown(struct snd_pcm_substream *substream) { struct snd_soc_pcm_runtime *rtd = substream->private_data; - struct snd_soc_codec *codec = rtd->codec; + struct snd_soc_component *codec = rtd->codec_dai->component; - snd_soc_update_bits(codec, PCM512x_GPIO_CONTROL_1, 0x08, 0x00); + snd_soc_component_update_bits(codec, PCM512x_GPIO_CONTROL_1, + 0x08, 0x00); } static int init(struct snd_soc_pcm_runtime *rtd) { - struct snd_soc_codec *codec = rtd->codec; + struct snd_soc_component *codec = rtd->codec_dai->component; - snd_soc_update_bits(codec, PCM512x_GPIO_EN, 0x08, 0x08); - snd_soc_update_bits(codec, PCM512x_GPIO_OUTPUT_4, 0x0f, 0x02); - snd_soc_update_bits(codec, PCM512x_GPIO_CONTROL_1, 0x08, 0x08); + snd_soc_component_update_bits(codec, PCM512x_GPIO_EN, 0x08, 0x08); + snd_soc_component_update_bits(codec, PCM512x_GPIO_OUTPUT_4, 0x0f, 0x02); + snd_soc_component_update_bits(codec, PCM512x_GPIO_CONTROL_1, + 0x08, 0x08); return 0; } From b7229c1b144a55cc63e8d6157800e396fbf97481 Mon Sep 17 00:00:00 2001 From: Pan Xiuli Date: Wed, 6 Jun 2018 16:11:26 +0800 Subject: [PATCH 082/107] ASoC: Intel: bytcr_rt5651: work with sof only with SSP2 AIF1 bytcr_rt5651 can work with ssp0 and ssp2, disable ssp0 support for SOF only support SSP2 AIF1 now. Also disable hard code routing for SSP2 AIF1. Signed-off-by: Pan Xiuli --- sound/soc/intel/boards/bytcr_rt5651.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/sound/soc/intel/boards/bytcr_rt5651.c b/sound/soc/intel/boards/bytcr_rt5651.c index 1b1997f1d60c84..50672ade6b5b4e 100644 --- a/sound/soc/intel/boards/bytcr_rt5651.c +++ b/sound/soc/intel/boards/bytcr_rt5651.c @@ -294,6 +294,7 @@ static const struct snd_soc_dapm_route byt_rt5651_ssp0_aif2_map[] = { }; static const struct snd_soc_dapm_route byt_rt5651_ssp2_aif1_map[] = { +#if !IS_ENABLED(CONFIG_SND_SOC_SOF_INTEL) {"ssp2 Tx", NULL, "codec_out0"}, {"ssp2 Tx", NULL, "codec_out1"}, {"codec_in0", NULL, "ssp2 Rx"}, @@ -301,6 +302,7 @@ static const struct snd_soc_dapm_route byt_rt5651_ssp2_aif1_map[] = { {"AIF1 Playback", NULL, "ssp2 Tx"}, {"ssp2 Rx", NULL, "AIF1 Capture"}, +#endif }; static const struct snd_soc_dapm_route byt_rt5651_ssp2_aif2_map[] = { @@ -707,6 +709,7 @@ static char byt_rt5651_codec_name[SND_ACPI_I2C_ID_LEN]; static char byt_rt5651_codec_aif_name[12]; /* = "rt5651-aif[1|2]" */ static char byt_rt5651_cpu_dai_name[10]; /* = "ssp[0|2]-port" */ +#if !IS_ENABLED(CONFIG_SND_SOC_SOF_INTEL) static bool is_valleyview(void) { static const struct x86_cpu_id cpu_ids[] = { @@ -718,6 +721,7 @@ static bool is_valleyview(void) return false; return true; } +#endif struct acpi_chan_package { /* ACPICA seems to require 64 bit integers */ u64 aif_value; /* 1: AIF1, 2: AIF2 */ @@ -762,6 +766,7 @@ static int snd_byt_rt5651_mc_probe(struct platform_device *pdev) "%s%s", "i2c-", i2c_name); byt_rt5651_dais[dai_index].codec_name = byt_rt5651_codec_name; +#if !IS_ENABLED(CONFIG_SND_SOC_SOF_INTEL) /* * swap SSP0 if bytcr is detected * (will be overridden if DMI quirk is detected) @@ -820,6 +825,7 @@ static int snd_byt_rt5651_mc_probe(struct platform_device *pdev) byt_rt5651_quirk |= BYT_RT5651_SSP0_AIF2; } } +#endif /* check quirks before creating card */ dmi_check_system(byt_rt5651_quirk_table); From b83debaa69d81c7b19bcf4b512547356cdda06ea Mon Sep 17 00:00:00 2001 From: Ranjani Sridharan Date: Sat, 9 Jun 2018 23:06:25 -0700 Subject: [PATCH 083/107] ASoC: SOF: topology: free volume table while unloading pga widget Free the volume table associated with the pga widget when it is unloaded. Tested on: Minnowboard Turbot w/ RT5651 and Up^2 w/ Hifiberry DAC+ PRO Passed sanity tests on both SOF: Master SOFT: master Kernel: https://github.com/thesofproject/linux branch: topic/sof-dev Signed-off-by: Ranjani Sridharan --- sound/soc/sof/topology.c | 33 ++++++++++++++++++++++++++++----- 1 file changed, 28 insertions(+), 5 deletions(-) diff --git a/sound/soc/sof/topology.c b/sound/soc/sof/topology.c index a2c986e4cad094..cdf21b0df89d69 100644 --- a/sound/soc/sof/topology.c +++ b/sound/soc/sof/topology.c @@ -1380,19 +1380,42 @@ static int sof_widget_ready(struct snd_soc_component *scomp, int index, static int sof_widget_unload(struct snd_soc_component *scomp, struct snd_soc_dobj *dobj) { + const struct snd_kcontrol_new *kc = NULL; + struct snd_soc_dapm_widget *widget; + struct snd_sof_control *scontrol; struct snd_sof_widget *swidget; + struct soc_mixer_control *sm; struct snd_sof_dai *dai; swidget = dobj->private; if (!swidget) return 0; - dai = swidget->private; + widget = swidget->widget; + + switch (swidget->id) { + case snd_soc_dapm_dai_in: + case snd_soc_dapm_dai_out: + dai = swidget->private; + + /* remove and free dai object */ + if (dai) { + list_del(&dai->list); + kfree(dai); + } + break; + case snd_soc_dapm_pga: + + /* get volume kcontrol */ + kc = &widget->kcontrol_news[0]; + sm = (struct soc_mixer_control *)kc->private_value; + scontrol = sm->dobj.private; - /* remove and free dai object */ - if (dai) { - list_del(&dai->list); - kfree(dai); + /* free volume table */ + kfree(scontrol->volume_table); + break; + default: + break; } /* remove and free swidget object */ From ed6cb43abcdf195e63cac698b1d65347792ace92 Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Tue, 12 Jun 2018 18:51:28 -0500 Subject: [PATCH 084/107] acpi: blacklist: remove quirk for Dell XPS13 when SOF is enabled Audio works well in I2S mode with SOF (Sound Open Firmware), no need for this quirk Signed-off-by: Pierre-Louis Bossart --- drivers/acpi/blacklist.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/acpi/blacklist.c b/drivers/acpi/blacklist.c index 995c4d8922b12e..edf821bc582baa 100644 --- a/drivers/acpi/blacklist.c +++ b/drivers/acpi/blacklist.c @@ -91,6 +91,7 @@ static int __init dmi_enable_rev_override(const struct dmi_system_id *d) static const struct dmi_system_id acpi_rev_dmi_table[] __initconst = { #ifdef CONFIG_ACPI_REV_OVERRIDE_POSSIBLE +#if !IS_ENABLED(CONFIG_SND_SOC_SOF_INTEL) /* * DELL XPS 13 (2015) switches sound between HDA and I2S * depending on the ACPI _REV callback. If userspace supports @@ -105,6 +106,7 @@ static const struct dmi_system_id acpi_rev_dmi_table[] __initconst = { DMI_MATCH(DMI_PRODUCT_NAME, "XPS 13 9343"), }, }, +#endif { .callback = dmi_enable_rev_override, .ident = "DELL Precision 5520", From e97bc1f9585303dbdc2fe2086e36a36a824be51f Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Tue, 12 Jun 2018 18:52:58 -0500 Subject: [PATCH 085/107] ASoC: Intel: common: add ACPI match for CannonLake MX98373 is used on some CNL boards Signed-off-by: Pierre-Louis Bossart --- sound/soc/intel/common/soc-acpi-intel-cnl-match.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/sound/soc/intel/common/soc-acpi-intel-cnl-match.c b/sound/soc/intel/common/soc-acpi-intel-cnl-match.c index ec8e28e7b937aa..ae3be064e75e99 100644 --- a/sound/soc/intel/common/soc-acpi-intel-cnl-match.c +++ b/sound/soc/intel/common/soc-acpi-intel-cnl-match.c @@ -24,6 +24,15 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_cnl_machines[] = { .sof_tplg_filename = "intel/sof-cnl-rt274.tplg", .asoc_plat_name = "0000:00:1f.3", }, + { + .id = "MX98373", + .drv_name = "cnl_max98373", + .fw_filename = "intel/dsp_fw_cnl.bin", + .pdata = &cnl_pdata, + .sof_fw_filename = "intel/sof-cnl.ri", + .sof_tplg_filename = "intel/sof-cnl.tplg", + .asoc_plat_name = "0000:00:0e.0", + }, {}, }; EXPORT_SYMBOL_GPL(snd_soc_acpi_intel_cnl_machines); From af410dad2a694bb549704de7e4f3d0b4678d8e5b Mon Sep 17 00:00:00 2001 From: Ranjani Sridharan Date: Mon, 11 Jun 2018 23:27:38 -0700 Subject: [PATCH 086/107] ASoC: SOF: fix warning about assigning __le to u16 type This patchs addresses the warning about assigning a __le value to a u16 type. Signed-off-by: Ranjani Sridharan --- sound/soc/sof/topology.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sound/soc/sof/topology.c b/sound/soc/sof/topology.c index cdf21b0df89d69..dfaae19d466c2d 100644 --- a/sound/soc/sof/topology.c +++ b/sound/soc/sof/topology.c @@ -290,7 +290,7 @@ static int get_token_u16(void *elem, void *object, u32 offset, u32 size) struct snd_soc_tplg_vendor_value_elem *velem = elem; u16 *val = object + offset; - *val = velem->value; + *val = (u16)le32_to_cpu(velem->value); return 0; } From c5450f7971dfc06c033b04c5b658b9ba83c6952e Mon Sep 17 00:00:00 2001 From: Ranjani Sridharan Date: Tue, 12 Jun 2018 12:25:47 -0700 Subject: [PATCH 087/107] ASoC: SOF: topology: fix dmic pdm token offsets This patch fixes the dmic pdm token offsets to reflect the correct offset within the sof_ipc_dai_dmic_pdm_ctrl structure. Signed-off-by: Ranjani Sridharan --- sound/soc/sof/topology.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/sound/soc/sof/topology.c b/sound/soc/sof/topology.c index dfaae19d466c2d..89428f1f6f445e 100644 --- a/sound/soc/sof/topology.c +++ b/sound/soc/sof/topology.c @@ -460,31 +460,31 @@ static const struct sof_topology_token dmic_tokens[] = { static const struct sof_topology_token dmic_pdm_tokens[] = { {SOF_TKN_INTEL_DMIC_PDM_CTRL_ID, SND_SOC_TPLG_TUPLE_TYPE_SHORT, get_token_u16, - offsetof(struct sof_ipc_dai_dmic_params, pdm[0]), + offsetof(struct sof_ipc_dai_dmic_pdm_ctrl, id), 0}, {SOF_TKN_INTEL_DMIC_PDM_MIC_A_Enable, SND_SOC_TPLG_TUPLE_TYPE_SHORT, get_token_u16, - offsetof(struct sof_ipc_dai_dmic_params, pdm[0]), + offsetof(struct sof_ipc_dai_dmic_pdm_ctrl, enable_mic_a), 0}, {SOF_TKN_INTEL_DMIC_PDM_MIC_B_Enable, SND_SOC_TPLG_TUPLE_TYPE_SHORT, get_token_u16, - offsetof(struct sof_ipc_dai_dmic_params, pdm[0]), + offsetof(struct sof_ipc_dai_dmic_pdm_ctrl, enable_mic_b), 0}, {SOF_TKN_INTEL_DMIC_PDM_POLARITY_A, SND_SOC_TPLG_TUPLE_TYPE_SHORT, get_token_u16, - offsetof(struct sof_ipc_dai_dmic_params, pdm[0]), + offsetof(struct sof_ipc_dai_dmic_pdm_ctrl, polarity_mic_a), 0}, {SOF_TKN_INTEL_DMIC_PDM_POLARITY_B, SND_SOC_TPLG_TUPLE_TYPE_SHORT, get_token_u16, - offsetof(struct sof_ipc_dai_dmic_params, pdm[0]), + offsetof(struct sof_ipc_dai_dmic_pdm_ctrl, polarity_mic_b), 0}, {SOF_TKN_INTEL_DMIC_PDM_CLK_EDGE, SND_SOC_TPLG_TUPLE_TYPE_SHORT, get_token_u16, - offsetof(struct sof_ipc_dai_dmic_params, pdm[0]), + offsetof(struct sof_ipc_dai_dmic_pdm_ctrl, clk_edge), 0}, {SOF_TKN_INTEL_DMIC_PDM_SKEW, SND_SOC_TPLG_TUPLE_TYPE_SHORT, get_token_u16, - offsetof(struct sof_ipc_dai_dmic_params, pdm[0]), + offsetof(struct sof_ipc_dai_dmic_pdm_ctrl, skew), 0}, }; From 04ca2617bce30a6a3fafdbd096a647297dfa9f7d Mon Sep 17 00:00:00 2001 From: Ranjani Sridharan Date: Tue, 12 Jun 2018 12:25:48 -0700 Subject: [PATCH 088/107] ASoC: SOF: topology: fix logic for parsing dmic pdm tokens This patch fixes the logic for parsing the dmic pdm tokens by using the private value in sof_dev to track the pdm config array element being parsed and passing the appropriate offset while getting the token. No need for a separate function to parse the pdm tokens anymore. Signed-off-by: Ranjani Sridharan --- sound/soc/sof/topology.c | 104 ++++++++++++++------------------------- 1 file changed, 37 insertions(+), 67 deletions(-) diff --git a/sound/soc/sof/topology.c b/sound/soc/sof/topology.c index 89428f1f6f445e..3c03cac38b5b24 100644 --- a/sound/soc/sof/topology.c +++ b/sound/soc/sof/topology.c @@ -552,73 +552,18 @@ static void sof_parse_string_tokens(struct snd_soc_component *scomp, } } -/* helper function to parse DMIC pdm specific tokens */ -static void sof_parse_pdm_tokens(struct snd_soc_component *scomp, - struct snd_soc_tplg_vendor_value_elem *elem, - void *object, - struct sof_topology_token token) -{ - size_t size = sizeof(struct sof_ipc_dai_dmic_pdm_ctrl); - u32 offset = token.offset; - /* pdm_index determines the index in pdm[] to populate the token */ - static int pdm_index; - /* determines the offset of the item in sof_ipc_dai_dmic_pdm_ctrl */ - u32 pdm_offset; - - switch (token.token) { - case SOF_TKN_INTEL_DMIC_PDM_CTRL_ID: - token.get_token(elem, object, offset + pdm_index * size, - token.size); - /* increment the pdm_index */ - pdm_index++; - return; - case SOF_TKN_INTEL_DMIC_PDM_MIC_A_Enable: - pdm_offset = offsetof(struct sof_ipc_dai_dmic_pdm_ctrl, - enable_mic_a); - break; - case SOF_TKN_INTEL_DMIC_PDM_MIC_B_Enable: - pdm_offset = offsetof(struct sof_ipc_dai_dmic_pdm_ctrl, - enable_mic_b); - break; - case SOF_TKN_INTEL_DMIC_PDM_POLARITY_A: - pdm_offset = offsetof(struct sof_ipc_dai_dmic_pdm_ctrl, - polarity_mic_a); - break; - case SOF_TKN_INTEL_DMIC_PDM_POLARITY_B: - pdm_offset = offsetof(struct sof_ipc_dai_dmic_pdm_ctrl, - polarity_mic_b); - break; - case SOF_TKN_INTEL_DMIC_PDM_CLK_EDGE: - pdm_offset = offsetof(struct sof_ipc_dai_dmic_pdm_ctrl, - clk_edge); - break; - case SOF_TKN_INTEL_DMIC_PDM_SKEW: - pdm_offset = offsetof(struct sof_ipc_dai_dmic_pdm_ctrl, - skew); - break; - default: - break; - } - - /* - * offset: address pointing to pdm[0] in dmic_params - * pdm_index - 1: index of the array item to be populated - * pdm_offset: item offset in sof_ipc_dai_dmic_pdm_ctrl - * indexed by pdm_index - */ - token.get_token(elem, object, - offset + (pdm_index - 1) * size + pdm_offset, - token.size); -} - static void sof_parse_word_tokens(struct snd_soc_component *scomp, void *object, const struct sof_topology_token *tokens, int count, struct snd_soc_tplg_vendor_array *array) { + struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp); struct snd_soc_tplg_vendor_value_elem *elem; + size_t size = sizeof(struct sof_ipc_dai_dmic_pdm_ctrl); int i, j; + u32 offset; + u32 *index = NULL; /* parse element by element */ for (i = 0; i < array->num_elems; i++) { @@ -635,25 +580,43 @@ static void sof_parse_word_tokens(struct snd_soc_component *scomp, if (tokens[j].token != elem->token) continue; - /* matched - now load token */ + /* pdm config array index */ + if (sdev->private) + index = (u32 *)sdev->private; + + /* matched - determine offset */ switch (tokens[j].token) { case SOF_TKN_INTEL_DMIC_PDM_CTRL_ID: + + /* inc number of pdm array index */ + if (index) + ++(*index); case SOF_TKN_INTEL_DMIC_PDM_MIC_A_Enable: case SOF_TKN_INTEL_DMIC_PDM_MIC_B_Enable: case SOF_TKN_INTEL_DMIC_PDM_POLARITY_A: case SOF_TKN_INTEL_DMIC_PDM_POLARITY_B: case SOF_TKN_INTEL_DMIC_PDM_CLK_EDGE: case SOF_TKN_INTEL_DMIC_PDM_SKEW: - /* parse pdm specific tokens */ - sof_parse_pdm_tokens(scomp, elem, object, - tokens[j]); + + /* check if array index is valid */ + if (!index || *index == 0) { + dev_err(sdev->dev, + "error: invalid array offset\n"); + continue; + } else { + /* offset within the pdm config array */ + offset = size * (*index - 1); + } break; default: - tokens[j].get_token(elem, object, - tokens[j].offset, - tokens[j].size); + offset = 0; break; } + + /* load token */ + tokens[j].get_token(elem, object, + offset + tokens[j].offset, + tokens[j].size); } } } @@ -1604,8 +1567,14 @@ static int sof_link_dmic_load(struct snd_soc_component *scomp, int index, /* copy the common dai config and dmic params */ memcpy(ipc_config, config, sizeof(*config)); + /* + * alloc memory for private member + * Used to track the pdm config array index currently being parsed + */ + sdev->private = kzalloc(sizeof(u32), GFP_KERNEL); + /* get DMIC PDM tokens */ - ret = sof_parse_tokens(scomp, &ipc_config->dmic, dmic_pdm_tokens, + ret = sof_parse_tokens(scomp, &ipc_config->dmic.pdm[0], dmic_pdm_tokens, ARRAY_SIZE(dmic_pdm_tokens), private->array, private->size); if (ret != 0) { @@ -1658,6 +1627,7 @@ static int sof_link_dmic_load(struct snd_soc_component *scomp, int index, dev_err(sdev->dev, "error: failed to set DAI config for DMIC%d\n", config->id); + kfree(sdev->private); kfree(ipc_config); return ret; From f42f8cf9ca83f48b4ac564ec2f949ba4df83ad80 Mon Sep 17 00:00:00 2001 From: Keyon Jie Date: Wed, 13 Jun 2018 20:09:27 +0800 Subject: [PATCH 089/107] ASoC: SOF: refinement for HDA DMA start/stop 1. Update to follow the start/stop sequence suggested by firmware team. 2. Preserve trigger stop to fix module unload/reload fail regression issue introduced by commit: [WORKAROUND] ASoC: SOF: start HDA DMA at hw_params() stage and remove stream_trigger(). Signed-off-by: Keyon Jie --- sound/soc/sof/intel/apl.c | 1 + sound/soc/sof/intel/cnl.c | 1 + sound/soc/sof/intel/hda-pcm.c | 23 ++++++++--------------- sound/soc/sof/intel/skl.c | 1 + sound/soc/sof/pcm.c | 5 +++++ sound/soc/sof/sof-priv.h | 5 +++++ 6 files changed, 21 insertions(+), 15 deletions(-) diff --git a/sound/soc/sof/intel/apl.c b/sound/soc/sof/intel/apl.c index 98274b02943cf4..d36e30d2a8b388 100644 --- a/sound/soc/sof/intel/apl.c +++ b/sound/soc/sof/intel/apl.c @@ -80,6 +80,7 @@ struct snd_sof_dsp_ops sof_apl_ops = { .host_stream_open = hda_dsp_pcm_open, .host_stream_close = hda_dsp_pcm_close, .host_stream_hw_params = hda_dsp_pcm_hw_params, + .host_stream_trigger = hda_dsp_pcm_trigger, /* firmware loading */ .load_firmware = hda_dsp_cl_load_fw, diff --git a/sound/soc/sof/intel/cnl.c b/sound/soc/sof/intel/cnl.c index 8f9ad191f0ce60..80d6cc772fd74a 100644 --- a/sound/soc/sof/intel/cnl.c +++ b/sound/soc/sof/intel/cnl.c @@ -208,6 +208,7 @@ struct snd_sof_dsp_ops sof_cnl_ops = { .host_stream_open = hda_dsp_pcm_open, .host_stream_close = hda_dsp_pcm_close, .host_stream_hw_params = hda_dsp_pcm_hw_params, + .host_stream_trigger = hda_dsp_pcm_trigger, /* firmware loading */ .load_firmware = hda_dsp_cl_load_fw, diff --git a/sound/soc/sof/intel/hda-pcm.c b/sound/soc/sof/intel/hda-pcm.c index 1b50d87bd6bed4..0abb2e5778cae2 100644 --- a/sound/soc/sof/intel/hda-pcm.c +++ b/sound/soc/sof/intel/hda-pcm.c @@ -123,24 +123,17 @@ int hda_dsp_pcm_hw_params(struct snd_sof_dev *sdev, /* disable SPIB, to enable buffer wrap for stream */ hda_dsp_stream_spib_config(sdev, stream, HDA_DSP_SPIB_DISABLE, 0); - /* - * start HDA DMA here, as DSP require the DMA copy is available - * at its trigger start, which is actually before stream trigger - * start - */ - snd_sof_dsp_update_bits(sdev, HDA_DSP_HDA_BAR, SOF_HDA_INTCTL, - 1 << stream->index, - 1 << stream->index); - - snd_sof_dsp_update_bits(sdev, HDA_DSP_HDA_BAR, stream->sd_offset, - SOF_HDA_SD_CTL_DMA_START | - SOF_HDA_CL_DMA_SD_INT_MASK, - SOF_HDA_SD_CTL_DMA_START | - SOF_HDA_CL_DMA_SD_INT_MASK); - return stream->tag; } +int hda_dsp_pcm_trigger(struct snd_sof_dev *sdev, + struct snd_pcm_substream *substream, int cmd) +{ + struct sof_intel_hda_stream *stream = substream->runtime->private_data; + + return hda_dsp_stream_trigger(sdev, stream, cmd); +} + int hda_dsp_pcm_open(struct snd_sof_dev *sdev, struct snd_pcm_substream *substream) { diff --git a/sound/soc/sof/intel/skl.c b/sound/soc/sof/intel/skl.c index 671f452e9b16d4..d4b6de626456b1 100644 --- a/sound/soc/sof/intel/skl.c +++ b/sound/soc/sof/intel/skl.c @@ -85,6 +85,7 @@ struct snd_sof_dsp_ops sof_skl_ops = { .host_stream_open = hda_dsp_pcm_open, .host_stream_close = hda_dsp_pcm_close, .host_stream_hw_params = hda_dsp_pcm_hw_params, + .host_stream_trigger = hda_dsp_pcm_trigger, /* firmware loading */ .load_firmware = hda_dsp_cl_load_fw, diff --git a/sound/soc/sof/pcm.c b/sound/soc/sof/pcm.c index 32ca808eb8624b..558d054af826cd 100644 --- a/sound/soc/sof/pcm.c +++ b/sound/soc/sof/pcm.c @@ -197,6 +197,7 @@ static int sof_pcm_trigger(struct snd_pcm_substream *substream, int cmd) struct snd_sof_pcm *spcm = rtd->sof; struct sof_ipc_stream stream; struct sof_ipc_reply reply; + const struct snd_sof_dsp_ops *ops = sdev->ops; int ret = 0; /* nothing todo for BE */ @@ -231,6 +232,10 @@ static int sof_pcm_trigger(struct snd_pcm_substream *substream, int cmd) return -EINVAL; } + /* set RUN firstly per the sequence suggested by firmware team */ + if (ops && ops->host_stream_trigger) + ret = ops->host_stream_trigger(sdev, substream, cmd); + /* send IPC to the DSP */ ret = sof_ipc_tx_message(sdev->ipc, stream.hdr.cmd, &stream, sizeof(stream), &reply, sizeof(reply)); diff --git a/sound/soc/sof/sof-priv.h b/sound/soc/sof/sof-priv.h index 7e86faef5aa287..bde985f6f438be 100644 --- a/sound/soc/sof/sof-priv.h +++ b/sound/soc/sof/sof-priv.h @@ -116,6 +116,11 @@ struct snd_sof_dsp_ops { struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params); + /* host stream trigger */ + int (*host_stream_trigger)(struct snd_sof_dev *sdev, + struct snd_pcm_substream *substream, + int cmd); + /* FW loading */ int (*load_firmware)(struct snd_sof_dev *sof_dev, const struct firmware *fw); From aaeb5cfbfd980d0a66e24769955945da4bf99ed5 Mon Sep 17 00:00:00 2001 From: Ranjani Sridharan Date: Tue, 12 Jun 2018 22:14:43 -0700 Subject: [PATCH 090/107] ASoC: uapi: sof: remove dmac id and dmac channel members from ipc host and dai comp def The firmware no longer uses the dmac id and dmac channel from topology. Signed-off-by: Ranjani Sridharan --- include/uapi/sound/sof-ipc.h | 4 ---- 1 file changed, 4 deletions(-) diff --git a/include/uapi/sound/sof-ipc.h b/include/uapi/sound/sof-ipc.h index d1c972abd03357..e3c3de0a2d19f0 100644 --- a/include/uapi/sound/sof-ipc.h +++ b/include/uapi/sound/sof-ipc.h @@ -591,8 +591,6 @@ struct sof_ipc_comp_host { struct sof_ipc_comp_config config; enum sof_ipc_stream_direction direction; uint32_t no_irq; /* don't send periodic IRQ to host/DSP */ - uint32_t dmac_id; - uint32_t dmac_chan; uint32_t dmac_config; /* DMA engine specific */ } __attribute__((packed)); @@ -603,8 +601,6 @@ struct sof_ipc_comp_dai { enum sof_ipc_stream_direction direction; uint32_t index; enum sof_ipc_dai_type type; - uint32_t dmac_id; - uint32_t dmac_chan; uint32_t dmac_config; /* DMA engine specific */ } __attribute__((packed)); From 9b0fa7235c341643e522cc236e780db1b2afc5e6 Mon Sep 17 00:00:00 2001 From: Ranjani Sridharan Date: Tue, 12 Jun 2018 22:14:44 -0700 Subject: [PATCH 091/107] ASoC: uapi: sof: remove DMAC ID and DMAC channel tokens No need to pass dmac id and dmac chan from topology as the firmware does not use this info anymore. Signed-off-by: Ranjani Sridharan --- include/uapi/sound/sof-topology.h | 4 ---- 1 file changed, 4 deletions(-) diff --git a/include/uapi/sound/sof-topology.h b/include/uapi/sound/sof-topology.h index 5941c4cb8623a3..1a4957b7fd049d 100644 --- a/include/uapi/sound/sof-topology.h +++ b/include/uapi/sound/sof-topology.h @@ -34,8 +34,6 @@ #define SOF_TKN_BUF_CAPS 101 /* DAI */ -#define SOF_TKN_DAI_DMAC 151 -#define SOF_TKN_DAI_DMAC_CHAN 152 #define SOF_TKN_DAI_DMAC_CONFIG 153 #define SOF_TKN_DAI_TYPE 154 #define SOF_TKN_DAI_INDEX 155 @@ -58,8 +56,6 @@ #define SOF_TKN_SRC_RATE_OUT 301 /* PCM */ -#define SOF_TKN_PCM_DMAC 351 -#define SOF_TKN_PCM_DMAC_CHAN 352 #define SOF_TKN_PCM_DMAC_CONFIG 353 /* Generic components */ From 4bd6b227f0b8837afbac46a33b4c926542f562cc Mon Sep 17 00:00:00 2001 From: Ranjani Sridharan Date: Tue, 12 Jun 2018 22:14:45 -0700 Subject: [PATCH 092/107] ASoC: SOF: topology: remove dmac id and dmac channel token parsing dmac id and dmac channel tokens are no longer used by the firmware. So no need to process them. Signed-off-by: Ranjani Sridharan --- sound/soc/sof/topology.c | 16 +++------------- 1 file changed, 3 insertions(+), 13 deletions(-) diff --git a/sound/soc/sof/topology.c b/sound/soc/sof/topology.c index 3c03cac38b5b24..eb9e16f643b5a5 100644 --- a/sound/soc/sof/topology.c +++ b/sound/soc/sof/topology.c @@ -322,10 +322,6 @@ static const struct sof_topology_token buffer_tokens[] = { /* DAI */ static const struct sof_topology_token dai_tokens[] = { - {SOF_TKN_DAI_DMAC, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32, - offsetof(struct sof_ipc_comp_dai, dmac_id), 0}, - {SOF_TKN_DAI_DMAC_CHAN, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32, - offsetof(struct sof_ipc_comp_dai, dmac_chan), 0}, {SOF_TKN_DAI_DMAC_CONFIG, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32, offsetof(struct sof_ipc_comp_dai, dmac_config), 0}, {SOF_TKN_DAI_TYPE, SND_SOC_TPLG_TUPLE_TYPE_STRING, get_token_dai_type, @@ -384,10 +380,6 @@ static const struct sof_topology_token tone_tokens[] = { /* PCM */ static const struct sof_topology_token pcm_tokens[] = { - {SOF_TKN_PCM_DMAC, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32, - offsetof(struct sof_ipc_comp_host, dmac_id), 0}, - {SOF_TKN_PCM_DMAC_CHAN, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32, - offsetof(struct sof_ipc_comp_host, dmac_chan), 0}, {SOF_TKN_PCM_DMAC_CONFIG, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32, offsetof(struct sof_ipc_comp_host, dmac_config), 0}, }; @@ -848,9 +840,8 @@ static int sof_widget_load_dai(struct snd_soc_component *scomp, int index, return ret; } - dev_dbg(sdev->dev, "dai %s: dmac %d chan %d type %d index %d\n", - swidget->widget->name, comp_dai.dmac_id, comp_dai.dmac_chan, - comp_dai.type, comp_dai.index); + dev_dbg(sdev->dev, "dai %s: type %d index %d\n", + swidget->widget->name, comp_dai.type, comp_dai.index); sof_dbg_comp_config(scomp, &comp_dai.config); ret = sof_ipc_tx_message(sdev->ipc, comp_dai.comp.hdr.cmd, @@ -945,8 +936,7 @@ static int sof_widget_load_pcm(struct snd_soc_component *scomp, int index, return ret; } - dev_dbg(sdev->dev, "host %s: dmac %d chan %d\n", - swidget->widget->name, host.dmac_id, host.dmac_chan); + dev_dbg(sdev->dev, "loaded host %s\n", swidget->widget->name); sof_dbg_comp_config(scomp, &host.config); return sof_ipc_tx_message(sdev->ipc, From 59efe711a927ee8c6b694b26f2a27a8de1c5dd1b Mon Sep 17 00:00:00 2001 From: Liam Girdwood Date: Thu, 14 Jun 2018 21:38:15 +0100 Subject: [PATCH 093/107] ASoC: SOF: Move xtensa oops/stack dump out of SOF core Move it to sof/xtensa/core.c Signed-off-by: Liam Girdwood --- sound/soc/sof/Kconfig | 1 + sound/soc/sof/Makefile | 1 + sound/soc/sof/core.c | 134 +--------------------------- sound/soc/sof/intel/Kconfig | 1 + sound/soc/sof/intel/bdw.c | 3 + sound/soc/sof/intel/byt.c | 3 + sound/soc/sof/intel/hda.c | 3 + sound/soc/sof/intel/hsw.c | 3 + sound/soc/sof/sof-priv.h | 24 +++++ sound/soc/sof/xtensa/Kconfig | 3 + sound/soc/sof/xtensa/Makefile | 7 ++ sound/soc/sof/xtensa/core.c | 159 ++++++++++++++++++++++++++++++++++ 12 files changed, 210 insertions(+), 132 deletions(-) create mode 100644 sound/soc/sof/xtensa/Kconfig create mode 100644 sound/soc/sof/xtensa/Makefile create mode 100644 sound/soc/sof/xtensa/core.c diff --git a/sound/soc/sof/Kconfig b/sound/soc/sof/Kconfig index 0b2e7a5fd31e81..bdae3f5969915b 100644 --- a/sound/soc/sof/Kconfig +++ b/sound/soc/sof/Kconfig @@ -42,3 +42,4 @@ config SND_SOC_SOF_FORCE_NOCODEC_MODE If unsure select "N". source "sound/soc/sof/intel/Kconfig" +source "sound/soc/sof/xtensa/Kconfig" diff --git a/sound/soc/sof/Makefile b/sound/soc/sof/Makefile index b759554d497c2c..e081723bf82e25 100644 --- a/sound/soc/sof/Makefile +++ b/sound/soc/sof/Makefile @@ -22,3 +22,4 @@ obj-$(SND_SOC_SOF_PLATFORM) += sof-platform-dev.o obj-$(CONFIG_SND_SOC_SOF_SPIDSP) += snd-sof-spi.o obj-$(CONFIG_SND_SOC_SOF_INTEL) += intel/ +obj-$(CONFIG_SND_SOC_SOF_XTENSA) += xtensa/ diff --git a/sound/soc/sof/core.c b/sound/soc/sof/core.c index 9e0e34075f4f07..b523496612d5ae 100644 --- a/sound/soc/sof/core.c +++ b/sound/soc/sof/core.c @@ -140,136 +140,6 @@ static const struct sof_panic_msg panic_msg[] = { {SOF_IPC_PANIC_IDLE, "can't enter idle"}, }; -struct sof_exception_cause { - u32 id; - const char *msg; - const char *description; -}; - -/* From 4.4.1.5 table 4-64 Exception Causes of - * Xtensa Instruction Set Architecture (ISA) Reference Manual - */ -static const struct sof_exception_cause xtensa_exception_causes[] = { - {0, "IllegalInstructionCause", "Illegal instruction"}, - {1, "SyscallCause", "SYSCALL instruction"}, - {2, "InstructionFetchErrorCause", - "Processor internal physical address or data error during instruction fetch"}, - {3, "LoadStoreErrorCause", - "Processor internal physical address or data error during load or store"}, - {4, "Level1InterruptCause", - "Level-1 interrupt as indicated by set level-1 bits in the INTERRUPT register"}, - {5, "AllocaCause", - "MOVSP instruction, if caller’s registers are not in the register file"}, - {6, "IntegerDivideByZeroCause", - "QUOS, QUOU, REMS, or REMU divisor operand is zero"}, - {8, "PrivilegedCause", - "Attempt to execute a privileged operation when CRING ? 0"}, - {9, "LoadStoreAlignmentCause", "Load or store to an unaligned address"}, - {12, "InstrPIFDataErrorCause", - "PIF data error during instruction fetch"}, - {13, "LoadStorePIFDataErrorCause", - "Synchronous PIF data error during LoadStore access"}, - {14, "InstrPIFAddrErrorCause", - "PIF address error during instruction fetch"}, - {15, "LoadStorePIFAddrErrorCause", - "Synchronous PIF address error during LoadStore access"}, - {16, "InstTLBMissCause", "Error during Instruction TLB refill"}, - {17, "InstTLBMultiHitCause", - "Multiple instruction TLB entries matched"}, - {18, "InstFetchPrivilegeCause", - "An instruction fetch referenced a virtual address at a ring level less than CRING"}, - {20, "InstFetchProhibitedCause", - "An instruction fetch referenced a page mapped with an attribute that does not permit instruction fetch"}, - {24, "LoadStoreTLBMissCause", - "Error during TLB refill for a load or store"}, - {25, "LoadStoreTLBMultiHitCause", - "Multiple TLB entries matched for a load or store"}, - {26, "LoadStorePrivilegeCause", - "A load or store referenced a virtual address at a ring level less than CRING"}, - {28, "LoadProhibitedCause", - "A load referenced a page mapped with an attribute that does not permit loads"}, - {32, "Coprocessor0Disabled", - "Coprocessor 0 instruction when cp0 disabled"}, - {33, "Coprocessor1Disabled", - "Coprocessor 1 instruction when cp1 disabled"}, - {34, "Coprocessor2Disabled", - "Coprocessor 2 instruction when cp2 disabled"}, - {35, "Coprocessor3Disabled", - "Coprocessor 3 instruction when cp3 disabled"}, - {36, "Coprocessor4Disabled", - "Coprocessor 4 instruction when cp4 disabled"}, - {37, "Coprocessor5Disabled", - "Coprocessor 5 instruction when cp5 disabled"}, - {38, "Coprocessor6Disabled", - "Coprocessor 6 instruction when cp6 disabled"}, - {39, "Coprocessor7Disabled", - "Coprocessor 7 instruction when cp7 disabled"}, -}; - -/* only need xtensa atm */ -static void sof_arch_dsp_oops(struct snd_sof_dev *sdev, void *oops) -{ - struct sof_ipc_dsp_oops_xtensa *xoops = oops; - int i; - - dev_err(sdev->dev, "error: DSP Firmware Oops\n"); - for (i = 0; i < ARRAY_SIZE(xtensa_exception_causes); i++) { - if (xtensa_exception_causes[i].id == xoops->exccause) { - dev_err(sdev->dev, "error: Exception Cause: %s, %s\n", - xtensa_exception_causes[i].msg, - xtensa_exception_causes[i].description); - } - } - dev_err(sdev->dev, "EXCCAUSE 0x%8.8x EXCVADDR 0x%8.8x PS 0x%8.8x SAR 0x%8.8x\n", - xoops->exccause, xoops->excvaddr, xoops->ps, xoops->sar); - dev_err(sdev->dev, "EPC1 0x%8.8x EPC2 0x%8.8x EPC3 0x%8.8x EPC4 0x%8.8x", - xoops->epc1, xoops->epc2, xoops->epc3, xoops->epc4); - dev_err(sdev->dev, "EPC5 0x%8.8x EPC6 0x%8.8x EPC7 0x%8.8x DEPC 0x%8.8x", - xoops->epc5, xoops->epc6, xoops->epc7, xoops->depc); - dev_err(sdev->dev, "EPS2 0x%8.8x EPS3 0x%8.8x EPS4 0x%8.8x EPS5 0x%8.8x", - xoops->eps2, xoops->eps3, xoops->eps4, xoops->eps5); - dev_err(sdev->dev, "EPS6 0x%8.8x EPS7 0x%8.8x INTENABL 0x%8.8x INTERRU 0x%8.8x", - xoops->eps6, xoops->eps7, xoops->intenable, xoops->interrupt); -} - -static void sof_dsp_dump_stack(struct snd_sof_dev *sdev, void *oops, - u32 *stack, u32 stack_words) -{ - struct sof_ipc_dsp_oops_xtensa *xoops = oops; - u32 stack_ptr = xoops->stack; - int i; - - dev_err(sdev->dev, "stack dump from 0x%8.8x\n", stack_ptr); - - for (i = 0; i <= stack_words - 4; i += 4) { - dev_err(sdev->dev, "0x%8.8x: 0x%8.8x 0x%8.8x 0x%8.8x 0x%8.8x\n", - stack_ptr + i, stack[i], stack[i + 1], stack[i + 2], - stack[i + 3]); - } - - /* deal with any remaining words */ - switch (stack_words - i) { - case 0: - break; - case 1: - dev_err(sdev->dev, "0x%8.8x: 0x%8.8x\n", - stack_ptr + stack_words - 1, stack[stack_words - 1]); - break; - case 2: - dev_err(sdev->dev, "0x%8.8x: 0x%8.8x 0x%8.8x\n", - stack_ptr + stack_words - 2, stack[stack_words - 2], - stack[stack_words - 1]); - break; - case 3: - dev_err(sdev->dev, "0x%8.8x: 0x%8.8x 0x%8.8x 0x%8.8x\n", - stack_ptr + stack_words - 3, stack[stack_words - 3], - stack[stack_words - 2], stack[stack_words - 1]); - break; - default: - break; - } -} - int snd_sof_get_status(struct snd_sof_dev *sdev, u32 panic_code, u32 tracep_code, void *oops, void *stack, size_t stack_words) @@ -301,8 +171,8 @@ int snd_sof_get_status(struct snd_sof_dev *sdev, u32 panic_code, dev_err(sdev->dev, "error: trace point %8.8x\n", tracep_code); out: - sof_arch_dsp_oops(sdev, oops); - sof_dsp_dump_stack(sdev, oops, stack, stack_words); + sof_oops(sdev, oops); + sof_stack(sdev, oops, stack, stack_words); return -EFAULT; } EXPORT_SYMBOL(snd_sof_get_status); diff --git a/sound/soc/sof/intel/Kconfig b/sound/soc/sof/intel/Kconfig index 2780750d6289d7..d9b4b6a23f6021 100644 --- a/sound/soc/sof/intel/Kconfig +++ b/sound/soc/sof/intel/Kconfig @@ -3,6 +3,7 @@ config SND_SOC_SOF_INTEL depends on SND_SOC_SOF depends on SND_DMA_SGBUF select SND_SOC_INTEL_MACH + select SND_SOC_SOF_XTENSA help This adds support for Sound Open Firmware for Intel(R) platforms. Say Y if you have such a device. diff --git a/sound/soc/sof/intel/bdw.c b/sound/soc/sof/intel/bdw.c index d1dbef55e27059..eb8ee7dd72a21b 100644 --- a/sound/soc/sof/intel/bdw.c +++ b/sound/soc/sof/intel/bdw.c @@ -600,6 +600,9 @@ static int bdw_probe(struct snd_sof_dev *sdev) u32 base, size; int ret = 0; + /* set DSP arch ops */ + sdev->arch_ops = &sof_xtensa_arch_ops; + /* LPE base */ mmio = platform_get_resource(pdev, IORESOURCE_MEM, desc->resindex_lpe_base); diff --git a/sound/soc/sof/intel/byt.c b/sound/soc/sof/intel/byt.c index 3103987207e4ae..fa76478d5beed9 100644 --- a/sound/soc/sof/intel/byt.c +++ b/sound/soc/sof/intel/byt.c @@ -531,6 +531,9 @@ static int byt_acpi_probe(struct snd_sof_dev *sdev) u32 base, size; int ret = 0; + /* set DSP arch ops */ + sdev->arch_ops = &sof_xtensa_arch_ops; + /* DSP DMA can only access low 31 bits of host memory */ ret = dma_coerce_mask_and_coherent(sdev->dev, DMA_BIT_MASK(31)); if (ret < 0) { diff --git a/sound/soc/sof/intel/hda.c b/sound/soc/sof/intel/hda.c index de138cfaa08d85..7949d6d5508025 100644 --- a/sound/soc/sof/intel/hda.c +++ b/sound/soc/sof/intel/hda.c @@ -311,6 +311,9 @@ int hda_dsp_probe(struct snd_sof_dev *sdev) int i; int ret = 0; + /* set DSP arch ops */ + sdev->arch_ops = &sof_xtensa_arch_ops; + chip = get_chip_info(pci->device); if (!chip) { dev_err(sdev->dev, "no such device supported, chip id:%x\n", diff --git a/sound/soc/sof/intel/hsw.c b/sound/soc/sof/intel/hsw.c index b9a51021550790..e4016738123a3c 100644 --- a/sound/soc/sof/intel/hsw.c +++ b/sound/soc/sof/intel/hsw.c @@ -601,6 +601,9 @@ static int hsw_probe(struct snd_sof_dev *sdev) u32 base, size; int ret = 0; + /* set DSP arch ops */ + sdev->arch_ops = &sof_xtensa_arch_ops; + /* LPE base */ mmio = platform_get_resource(pdev, IORESOURCE_MEM, desc->resindex_lpe_base); diff --git a/sound/soc/sof/sof-priv.h b/sound/soc/sof/sof-priv.h index bde985f6f438be..6cc851347d59d4 100644 --- a/sound/soc/sof/sof-priv.h +++ b/sound/soc/sof/sof-priv.h @@ -134,6 +134,12 @@ struct snd_sof_dsp_ops { int (*trace_trigger)(struct snd_sof_dev *sdev, int cmd); }; +struct sof_arch_ops { + void (*dsp_oops)(struct snd_sof_dev *sdev, void *oops); + void (*dsp_stack)(struct snd_sof_dev *sdev, void *oops, + u32 *stack, u32 stack_words); +}; + struct snd_sof_pdata; struct sof_ops_table { @@ -254,6 +260,7 @@ struct snd_sof_dev { struct snd_sof_pdata *pdata; const struct snd_sof_dsp_ops *ops; struct sof_intel_hda_dev *hda; /* for HDA based DSP HW */ + const struct sof_arch_ops *arch_ops; /* IPC */ struct snd_sof_ipc *ipc; @@ -434,4 +441,21 @@ int snd_sof_bytes_get(struct snd_kcontrol *kcontrol, int snd_sof_bytes_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol); +/* + * DSP Architectures. + */ +static inline void sof_stack(struct snd_sof_dev *sdev, void *oops, u32 *stack, + u32 stack_words) +{ + if (sdev->arch_ops->dsp_stack) + sdev->arch_ops->dsp_stack(sdev, oops, stack, stack_words); +} + +static inline void sof_oops(struct snd_sof_dev *sdev, void *oops) +{ + if (sdev->arch_ops->dsp_oops) + sdev->arch_ops->dsp_oops(sdev, oops); +} + +extern const struct sof_arch_ops sof_xtensa_arch_ops; #endif diff --git a/sound/soc/sof/xtensa/Kconfig b/sound/soc/sof/xtensa/Kconfig new file mode 100644 index 00000000000000..f66f17bb03357f --- /dev/null +++ b/sound/soc/sof/xtensa/Kconfig @@ -0,0 +1,3 @@ +config SND_SOC_SOF_XTENSA + tristate + diff --git a/sound/soc/sof/xtensa/Makefile b/sound/soc/sof/xtensa/Makefile new file mode 100644 index 00000000000000..34b27be0f7be07 --- /dev/null +++ b/sound/soc/sof/xtensa/Makefile @@ -0,0 +1,7 @@ +# SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause) + +ccflags-y += -DDEBUG + +snd-sof-xtensa-dsp-objs := core.o + +obj-$(CONFIG_SND_SOC_SOF_XTENSA) += snd-sof-xtensa-dsp.o diff --git a/sound/soc/sof/xtensa/core.c b/sound/soc/sof/xtensa/core.c new file mode 100644 index 00000000000000..5ca9185f5afdf5 --- /dev/null +++ b/sound/soc/sof/xtensa/core.c @@ -0,0 +1,159 @@ +// SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause) +/* + * This file is provided under a dual BSD/GPLv2 license. When using or + * redistributing this file, you may do so under either license. + * + * Copyright(c) 2018 Intel Corporation. All rights reserved. + * + * Author: Pan Xiuli + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include "../sof-priv.h" +#include "../ops.h" + +struct xtensa_exception_cause { + u32 id; + const char *msg; + const char *description; +}; + +/* From 4.4.1.5 table 4-64 Exception Causes of + * Xtensa Instruction Set Architecture (ISA) Reference Manual + */ +static const struct xtensa_exception_cause xtensa_exception_causes[] = { + {0, "IllegalInstructionCause", "Illegal instruction"}, + {1, "SyscallCause", "SYSCALL instruction"}, + {2, "InstructionFetchErrorCause", + "Processor internal physical address or data error during instruction fetch"}, + {3, "LoadStoreErrorCause", + "Processor internal physical address or data error during load or store"}, + {4, "Level1InterruptCause", + "Level-1 interrupt as indicated by set level-1 bits in the INTERRUPT register"}, + {5, "AllocaCause", + "MOVSP instruction, if caller’s registers are not in the register file"}, + {6, "IntegerDivideByZeroCause", + "QUOS, QUOU, REMS, or REMU divisor operand is zero"}, + {8, "PrivilegedCause", + "Attempt to execute a privileged operation when CRING ? 0"}, + {9, "LoadStoreAlignmentCause", "Load or store to an unaligned address"}, + {12, "InstrPIFDataErrorCause", + "PIF data error during instruction fetch"}, + {13, "LoadStorePIFDataErrorCause", + "Synchronous PIF data error during LoadStore access"}, + {14, "InstrPIFAddrErrorCause", + "PIF address error during instruction fetch"}, + {15, "LoadStorePIFAddrErrorCause", + "Synchronous PIF address error during LoadStore access"}, + {16, "InstTLBMissCause", "Error during Instruction TLB refill"}, + {17, "InstTLBMultiHitCause", + "Multiple instruction TLB entries matched"}, + {18, "InstFetchPrivilegeCause", + "An instruction fetch referenced a virtual address at a ring level less than CRING"}, + {20, "InstFetchProhibitedCause", + "An instruction fetch referenced a page mapped with an attribute that does not permit instruction fetch"}, + {24, "LoadStoreTLBMissCause", + "Error during TLB refill for a load or store"}, + {25, "LoadStoreTLBMultiHitCause", + "Multiple TLB entries matched for a load or store"}, + {26, "LoadStorePrivilegeCause", + "A load or store referenced a virtual address at a ring level less than CRING"}, + {28, "LoadProhibitedCause", + "A load referenced a page mapped with an attribute that does not permit loads"}, + {32, "Coprocessor0Disabled", + "Coprocessor 0 instruction when cp0 disabled"}, + {33, "Coprocessor1Disabled", + "Coprocessor 1 instruction when cp1 disabled"}, + {34, "Coprocessor2Disabled", + "Coprocessor 2 instruction when cp2 disabled"}, + {35, "Coprocessor3Disabled", + "Coprocessor 3 instruction when cp3 disabled"}, + {36, "Coprocessor4Disabled", + "Coprocessor 4 instruction when cp4 disabled"}, + {37, "Coprocessor5Disabled", + "Coprocessor 5 instruction when cp5 disabled"}, + {38, "Coprocessor6Disabled", + "Coprocessor 6 instruction when cp6 disabled"}, + {39, "Coprocessor7Disabled", + "Coprocessor 7 instruction when cp7 disabled"}, +}; + +/* only need xtensa atm */ +static void xtensa_dsp_oops(struct snd_sof_dev *sdev, void *oops) +{ + struct sof_ipc_dsp_oops_xtensa *xoops = oops; + int i; + + dev_err(sdev->dev, "error: DSP Firmware Oops\n"); + for (i = 0; i < ARRAY_SIZE(xtensa_exception_causes); i++) { + if (xtensa_exception_causes[i].id == xoops->exccause) { + dev_err(sdev->dev, "error: Exception Cause: %s, %s\n", + xtensa_exception_causes[i].msg, + xtensa_exception_causes[i].description); + } + } + dev_err(sdev->dev, "EXCCAUSE 0x%8.8x EXCVADDR 0x%8.8x PS 0x%8.8x SAR 0x%8.8x\n", + xoops->exccause, xoops->excvaddr, xoops->ps, xoops->sar); + dev_err(sdev->dev, "EPC1 0x%8.8x EPC2 0x%8.8x EPC3 0x%8.8x EPC4 0x%8.8x", + xoops->epc1, xoops->epc2, xoops->epc3, xoops->epc4); + dev_err(sdev->dev, "EPC5 0x%8.8x EPC6 0x%8.8x EPC7 0x%8.8x DEPC 0x%8.8x", + xoops->epc5, xoops->epc6, xoops->epc7, xoops->depc); + dev_err(sdev->dev, "EPS2 0x%8.8x EPS3 0x%8.8x EPS4 0x%8.8x EPS5 0x%8.8x", + xoops->eps2, xoops->eps3, xoops->eps4, xoops->eps5); + dev_err(sdev->dev, "EPS6 0x%8.8x EPS7 0x%8.8x INTENABL 0x%8.8x INTERRU 0x%8.8x", + xoops->eps6, xoops->eps7, xoops->intenable, xoops->interrupt); +} + +static void xtensa_stack(struct snd_sof_dev *sdev, void *oops, u32 *stack, + u32 stack_words) +{ + struct sof_ipc_dsp_oops_xtensa *xoops = oops; + u32 stack_ptr = xoops->stack; + int i; + + dev_err(sdev->dev, "stack dump from 0x%8.8x\n", stack_ptr); + + for (i = 0; i <= stack_words - 4; i += 4) { + dev_err(sdev->dev, "0x%8.8x: 0x%8.8x 0x%8.8x 0x%8.8x 0x%8.8x\n", + stack_ptr + i, stack[i], stack[i + 1], stack[i + 2], + stack[i + 3]); + } + + /* deal with any remaining words */ + switch (stack_words - i) { + case 0: + break; + case 1: + dev_err(sdev->dev, "0x%8.8x: 0x%8.8x\n", + stack_ptr + stack_words - 1, stack[stack_words - 1]); + break; + case 2: + dev_err(sdev->dev, "0x%8.8x: 0x%8.8x 0x%8.8x\n", + stack_ptr + stack_words - 2, stack[stack_words - 2], + stack[stack_words - 1]); + break; + case 3: + dev_err(sdev->dev, "0x%8.8x: 0x%8.8x 0x%8.8x 0x%8.8x\n", + stack_ptr + stack_words - 3, stack[stack_words - 3], + stack[stack_words - 2], stack[stack_words - 1]); + break; + default: + break; + } +} + +const struct sof_arch_ops sof_xtensa_arch_ops = { + .dsp_oops = xtensa_dsp_oops, + .dsp_stack = xtensa_stack, +}; +EXPORT_SYMBOL(sof_xtensa_arch_ops); + +MODULE_DESCRIPTION("SOF Xtensa DSP support"); +MODULE_LICENSE("Dual BSD/GPL"); From df15329163229fe3436ad914561430c20f778cf1 Mon Sep 17 00:00:00 2001 From: Liam Girdwood Date: Fri, 15 Jun 2018 20:36:21 +0100 Subject: [PATCH 094/107] ASoC: SOF: Cleanup priv.h and add comments for upstreaming. Signed-off-by: Liam Girdwood --- sound/soc/sof/sof-priv.h | 52 +++++++++++++++++++++++++--------------- 1 file changed, 33 insertions(+), 19 deletions(-) diff --git a/sound/soc/sof/sof-priv.h b/sound/soc/sof/sof-priv.h index 6cc851347d59d4..fabfcb25180940 100644 --- a/sound/soc/sof/sof-priv.h +++ b/sound/soc/sof/sof-priv.h @@ -49,7 +49,14 @@ struct snd_sof_ipc; struct snd_sof_debugfs_map; struct snd_soc_tplg_ops; struct snd_soc_component; +struct sof_intel_hda_dev; +struct snd_sof_pdata; +/* + * SOF DSP HW abstraction operations. + * Used to abstract DSP HW architecture and any IO busses between host CPU + * and DSP device(s). + */ struct snd_sof_dsp_ops { /* probe and remove */ int (*remove)(struct snd_sof_dev *sof_dev); @@ -134,20 +141,21 @@ struct snd_sof_dsp_ops { int (*trace_trigger)(struct snd_sof_dev *sdev, int cmd); }; +/* DSP architecture specific callbacks for oops and stack dumps */ struct sof_arch_ops { void (*dsp_oops)(struct snd_sof_dev *sdev, void *oops); void (*dsp_stack)(struct snd_sof_dev *sdev, void *oops, u32 *stack, u32 stack_words); }; -struct snd_sof_pdata; - +/* DSP device HW descriptor mapping between bus ID and ops */ struct sof_ops_table { const struct sof_dev_desc *desc; struct snd_sof_dsp_ops *ops; struct platform_device *(*new_data)(struct snd_sof_pdata *pdata); }; +/* FS entry for debug files that can expose DSP memories, registers */ struct snd_sof_dfsentry { struct dentry *dfsentry; size_t size; @@ -155,6 +163,7 @@ struct snd_sof_dfsentry { struct snd_sof_dev *sdev; }; +/* Debug mapping for any DSP memory or registers that can used for debug */ struct snd_sof_debugfs_map { const char *name; u32 bar; @@ -162,11 +171,28 @@ struct snd_sof_debugfs_map { u32 size; }; +/* mailbox descriptor, used for host <-> DSP IPC */ struct snd_sof_mailbox { u32 offset; size_t size; }; +/* IPC message descriptor for host <-> DSP IO */ +struct snd_sof_ipc_msg { + struct list_head list; + + /* message data */ + u32 header; + void *msg_data; + void *reply_data; + size_t msg_size; + size_t reply_size; + + wait_queue_head_t waitq; + bool complete; +}; + +/* PCM stream, mapped to FW component */ struct snd_sof_pcm_stream { u32 comp_id; struct snd_dma_buffer page_table; @@ -174,6 +200,7 @@ struct snd_sof_pcm_stream { struct snd_pcm_substream *substream; }; +/* ASLA SOF PCM device */ struct snd_sof_pcm { struct snd_sof_dev *sdev; struct snd_soc_tplg_pcm pcm; @@ -183,6 +210,7 @@ struct snd_sof_pcm { struct list_head list; /* list in sdev pcm list */ }; +/* ALSA SOF Kcontrol device */ struct snd_sof_control { struct snd_sof_dev *sdev; int comp_id; @@ -197,6 +225,7 @@ struct snd_sof_control { struct list_head list; /* list in sdev control list */ }; +/* ASoC SOF DAPM widget */ struct snd_sof_widget { struct snd_sof_dev *sdev; int comp_id; @@ -208,9 +237,10 @@ struct snd_sof_widget { struct mutex mutex; struct list_head list; /* list in sdev widget list */ - void *private; /* core does not touch this */ + void *private; /* core does not touch this */ }; +/* ASoC DAI device */ struct snd_sof_dai { struct snd_sof_dev *sdev; const char *name; @@ -220,22 +250,6 @@ struct snd_sof_dai { struct list_head list; /* list in sdev dai list */ }; -struct snd_sof_ipc_msg { - struct list_head list; - - /* message data */ - u32 header; - void *msg_data; - void *reply_data; - size_t msg_size; - size_t reply_size; - - wait_queue_head_t waitq; - bool complete; -}; - -struct sof_intel_hda_dev; - /* * SOF Device Level. */ From 51f6db7f214345ab933ce0ce965d40fd45f77fbc Mon Sep 17 00:00:00 2001 From: Pan Xiuli Date: Wed, 20 Jun 2018 17:16:50 +0800 Subject: [PATCH 095/107] ASoC: Intel: replace snd_soc_codec to snd_soc_component in cnl_rt274 Codec may be can not use now, use the component instead Signed-off-by: Pan Xiuli --- sound/soc/intel/boards/cnl_rt274.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sound/soc/intel/boards/cnl_rt274.c b/sound/soc/intel/boards/cnl_rt274.c index d31af7dfc1cc59..16daa31b8d2ca0 100644 --- a/sound/soc/intel/boards/cnl_rt274.c +++ b/sound/soc/intel/boards/cnl_rt274.c @@ -111,9 +111,9 @@ static struct snd_soc_jack cnl_headset; static int cnl_rt274_init(struct snd_soc_pcm_runtime *runtime) { int ret; - struct snd_soc_codec *codec = runtime->codec; struct snd_soc_card *card = runtime->card; struct snd_soc_dai *codec_dai = runtime->codec_dai; + struct snd_soc_component *component = codec_dai->component; ret = snd_soc_card_jack_new(runtime->card, "Headset", SND_JACK_HEADSET, &cnl_headset, @@ -123,7 +123,7 @@ static int cnl_rt274_init(struct snd_soc_pcm_runtime *runtime) if (ret) return ret; - snd_soc_codec_set_jack(codec, &cnl_headset, NULL); + snd_soc_component_set_jack(component, &cnl_headset, NULL); /* TDM 4 slots 24 bit, set Rx & Tx bitmask to 4 active slots */ ret = snd_soc_dai_set_tdm_slot(codec_dai, 0xF, 0xF, 4, 24); From 9b843df48bbaea2a067df9d098cd700d3abd6155 Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Fri, 22 Jun 2018 15:47:24 -0500 Subject: [PATCH 096/107] ASoC: Intel: common: add ACPI matching tables for ICL Signed-off-by: Pierre-Louis Bossart --- include/sound/soc-acpi-intel-match.h | 1 + sound/soc/intel/common/Makefile | 2 +- .../intel/common/soc-acpi-intel-icl-match.c | 32 +++++++++++++++++++ 3 files changed, 34 insertions(+), 1 deletion(-) create mode 100644 sound/soc/intel/common/soc-acpi-intel-icl-match.c diff --git a/include/sound/soc-acpi-intel-match.h b/include/sound/soc-acpi-intel-match.h index 917ddd0f2762ea..1de9ed784c1b23 100644 --- a/include/sound/soc-acpi-intel-match.h +++ b/include/sound/soc-acpi-intel-match.h @@ -34,5 +34,6 @@ extern struct snd_soc_acpi_mach snd_soc_acpi_intel_kbl_machines[]; extern struct snd_soc_acpi_mach snd_soc_acpi_intel_bxt_machines[]; extern struct snd_soc_acpi_mach snd_soc_acpi_intel_glk_machines[]; extern struct snd_soc_acpi_mach snd_soc_acpi_intel_cnl_machines[]; +extern struct snd_soc_acpi_mach snd_soc_acpi_intel_icl_machines[]; #endif diff --git a/sound/soc/intel/common/Makefile b/sound/soc/intel/common/Makefile index 915a34cdc8acbb..fd52419f1c814a 100644 --- a/sound/soc/intel/common/Makefile +++ b/sound/soc/intel/common/Makefile @@ -7,7 +7,7 @@ snd-soc-acpi-intel-match-objs := soc-acpi-intel-byt-match.o soc-acpi-intel-cht-m soc-acpi-intel-hsw-bdw-match.o \ soc-acpi-intel-skl-match.o soc-acpi-intel-kbl-match.o \ soc-acpi-intel-bxt-match.o soc-acpi-intel-glk-match.o \ - soc-acpi-intel-cnl-match.o + soc-acpi-intel-cnl-match.o soc-acpi-intel-icl-match.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 diff --git a/sound/soc/intel/common/soc-acpi-intel-icl-match.c b/sound/soc/intel/common/soc-acpi-intel-icl-match.c new file mode 100644 index 00000000000000..33b441dca4d308 --- /dev/null +++ b/sound/soc/intel/common/soc-acpi-intel-icl-match.c @@ -0,0 +1,32 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * soc-apci-intel-icl-match.c - tables and support for ICL ACPI enumeration. + * + * Copyright (c) 2018, Intel Corporation. + * + */ + +#include +#include +#include "../skylake/skl.h" + +static struct skl_machine_pdata icl_pdata = { + .use_tplg_pcm = true, +}; + +struct snd_soc_acpi_mach snd_soc_acpi_intel_icl_machines[] = { + { + .id = "INT34C2", + .drv_name = "icl_rt274", + .fw_filename = "intel/dsp_fw_icl.bin", + .pdata = &icl_pdata, + .sof_fw_filename = "intel/sof-icl.ri", + .sof_tplg_filename = "intel/sof-icl-rt274.tplg", + .asoc_plat_name = "0000:00:1f.3", + }, + {}, +}; +EXPORT_SYMBOL_GPL(snd_soc_acpi_intel_icl_machines); + +MODULE_LICENSE("GPL v2"); +MODULE_DESCRIPTION("Intel Common ACPI Match module"); From 2b2dae0297a54af8d1ec9fa371ddae1e08962418 Mon Sep 17 00:00:00 2001 From: Ranjani Sridharan Date: Wed, 20 Jun 2018 15:20:49 -0700 Subject: [PATCH 097/107] ASoC: uapi: sof: rename clk_id to mclk_id to make it explicit and fix the comment Some platforms have more than one MCLK's exposed. So fix the comment and rename the member to mclk_id to make it explicit to be used for passing the corrent mclk id to the firmware. Signed-off-by: Ranjani Sridharan --- include/uapi/sound/sof-ipc.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/uapi/sound/sof-ipc.h b/include/uapi/sound/sof-ipc.h index e3c3de0a2d19f0..f78b5cb56508de 100644 --- a/include/uapi/sound/sof-ipc.h +++ b/include/uapi/sound/sof-ipc.h @@ -209,7 +209,7 @@ enum sof_ipc_dai_type { /* SSP Configuration Request - SOF_IPC_DAI_SSP_CONFIG */ struct sof_ipc_dai_ssp_params { uint16_t mode; // FIXME: do we need this? - uint16_t clk_id; // FIXME: do we need this? + uint16_t mclk_id; uint32_t mclk_rate; /* mclk frequency in Hz */ uint32_t fsync_rate; /* fsync frequency in Hz */ From 7f45e9bbd30f35647a5a0c37c8aacaa2040c9987 Mon Sep 17 00:00:00 2001 From: Ranjani Sridharan Date: Wed, 20 Jun 2018 15:22:43 -0700 Subject: [PATCH 098/107] ASoC: uapi: sof: add token for SSP MCLK ID Add a token for SSP_MCLK_ID which will be used to pass the MCLK ID for configuring the SSP. Signed-off-by: Ranjani Sridharan --- include/uapi/sound/sof-topology.h | 1 + 1 file changed, 1 insertion(+) diff --git a/include/uapi/sound/sof-topology.h b/include/uapi/sound/sof-topology.h index 1a4957b7fd049d..721a2b52a00b07 100644 --- a/include/uapi/sound/sof-topology.h +++ b/include/uapi/sound/sof-topology.h @@ -68,6 +68,7 @@ #define SOF_TKN_INTEL_SSP_MCLK_KEEP_ACTIVE 500 #define SOF_TKN_INTEL_SSP_BCLK_KEEP_ACTIVE 501 #define SOF_TKN_INTEL_SSP_FS_KEEP_ACTIVE 502 +#define SOF_TKN_INTEL_SSP_MCLK_ID 503 /* DMIC */ #define SOF_TKN_INTEL_DMIC_DRIVER_VERSION 600 From e409473e275f4596dc5c02fd9d03d11acfd5dcc0 Mon Sep 17 00:00:00 2001 From: Ranjani Sridharan Date: Wed, 20 Jun 2018 15:25:09 -0700 Subject: [PATCH 099/107] ASoC: SOF: topology: add SSP_MCLK_ID to ssp_tokens to enable parsing the mclk id from topology Signed-off-by: Ranjani Sridharan --- sound/soc/sof/topology.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/sound/soc/sof/topology.c b/sound/soc/sof/topology.c index eb9e16f643b5a5..1d4685c89f932c 100644 --- a/sound/soc/sof/topology.c +++ b/sound/soc/sof/topology.c @@ -411,6 +411,9 @@ static const struct sof_topology_token ssp_tokens[] = { {SOF_TKN_INTEL_SSP_FS_KEEP_ACTIVE, SND_SOC_TPLG_TUPLE_TYPE_BOOL, get_token_u32, offsetof(struct sof_ipc_dai_ssp_params, fs_keep_active), 0}, + {SOF_TKN_INTEL_SSP_MCLK_ID, + SND_SOC_TPLG_TUPLE_TYPE_SHORT, get_token_u16, + offsetof(struct sof_ipc_dai_ssp_params, mclk_id), 0}, }; /* DMIC */ @@ -1480,7 +1483,7 @@ static int sof_link_ssp_load(struct snd_soc_component *scomp, int index, return ret; } - ret = sof_parse_tokens(scomp, config, ssp_tokens, + ret = sof_parse_tokens(scomp, &config->ssp, ssp_tokens, ARRAY_SIZE(ssp_tokens), private->array, private->size); if (ret != 0) { @@ -1498,11 +1501,11 @@ static int sof_link_ssp_load(struct snd_soc_component *scomp, int index, config->ssp.rx_slots = hw_config->rx_slots; config->ssp.tx_slots = hw_config->tx_slots; - dev_dbg(sdev->dev, "tplg: config SSP%d fmt 0x%x mclk %d bclk %d fclk %d width (%d)%d slots %d\n", + dev_dbg(sdev->dev, "tplg: config SSP%d fmt 0x%x mclk %d bclk %d fclk %d width (%d)%d slots %d mclk id %d\n", config->id, config->format, config->ssp.mclk_rate, config->ssp.bclk_rate, config->ssp.fsync_rate, config->ssp.sample_valid_bits, - config->ssp.tdm_slot_width, config->ssp.tdm_slots); + config->ssp.tdm_slot_width, config->ssp.tdm_slots, config->ssp.mclk_id); /* send message to DSP */ ret = sof_ipc_tx_message(sdev->ipc, From cb05347d9f85e18b414098d512c8287c297933d2 Mon Sep 17 00:00:00 2001 From: Ranjani Sridharan Date: Mon, 25 Jun 2018 00:14:40 -0700 Subject: [PATCH 100/107] ASoC: SOF: merge sample_bits tokens with the rest of the ssp_tokens Sample bits is SSP specific. So move it to be part of SSP tokens. dai_ssp_link_tokens are not need anymore. So remove it and the corresponding call to parse the tokens. Signed-off-by: Ranjani Sridharan --- include/uapi/sound/sof-topology.h | 2 +- sound/soc/sof/topology.c | 18 +++--------------- 2 files changed, 4 insertions(+), 16 deletions(-) diff --git a/include/uapi/sound/sof-topology.h b/include/uapi/sound/sof-topology.h index 721a2b52a00b07..096aaed4333059 100644 --- a/include/uapi/sound/sof-topology.h +++ b/include/uapi/sound/sof-topology.h @@ -37,7 +37,6 @@ #define SOF_TKN_DAI_DMAC_CONFIG 153 #define SOF_TKN_DAI_TYPE 154 #define SOF_TKN_DAI_INDEX 155 -#define SOF_TKN_DAI_SAMPLE_BITS 156 /* scheduling */ #define SOF_TKN_SCHED_DEADLINE 200 @@ -69,6 +68,7 @@ #define SOF_TKN_INTEL_SSP_BCLK_KEEP_ACTIVE 501 #define SOF_TKN_INTEL_SSP_FS_KEEP_ACTIVE 502 #define SOF_TKN_INTEL_SSP_MCLK_ID 503 +#define SOF_TKN_INTEL_SSP_SAMPLE_BITS 504 /* DMIC */ #define SOF_TKN_INTEL_DMIC_DRIVER_VERSION 600 diff --git a/sound/soc/sof/topology.c b/sound/soc/sof/topology.c index 1d4685c89f932c..f9347384ecf3ad 100644 --- a/sound/soc/sof/topology.c +++ b/sound/soc/sof/topology.c @@ -336,11 +336,6 @@ static const struct sof_topology_token dai_link_tokens[] = { offsetof(struct sof_ipc_dai_config, type), 0}, }; -static const struct sof_topology_token dai_ssp_link_tokens[] = { - {SOF_TKN_DAI_SAMPLE_BITS, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32, - offsetof(struct sof_ipc_dai_ssp_params, sample_valid_bits), 0}, -}; - /* scheduling */ static const struct sof_topology_token sched_tokens[] = { {SOF_TKN_SCHED_DEADLINE, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32, @@ -414,6 +409,9 @@ static const struct sof_topology_token ssp_tokens[] = { {SOF_TKN_INTEL_SSP_MCLK_ID, SND_SOC_TPLG_TUPLE_TYPE_SHORT, get_token_u16, offsetof(struct sof_ipc_dai_ssp_params, mclk_id), 0}, + {SOF_TKN_INTEL_SSP_SAMPLE_BITS, SND_SOC_TPLG_TUPLE_TYPE_WORD, + get_token_u32, + offsetof(struct sof_ipc_dai_ssp_params, sample_valid_bits), 0}, }; /* DMIC */ @@ -1473,16 +1471,6 @@ static int sof_link_ssp_load(struct snd_soc_component *scomp, int index, memset(&config->ssp, 0, sizeof(struct sof_ipc_dai_ssp_params)); config->hdr.size = size; - /* get any bespoke DAI tokens */ - ret = sof_parse_tokens(scomp, &config->ssp, dai_ssp_link_tokens, - ARRAY_SIZE(dai_ssp_link_tokens), - private->array, private->size); - if (ret != 0) { - dev_err(sdev->dev, "error: parse ssp link tokens failed %d\n", - private->size); - return ret; - } - ret = sof_parse_tokens(scomp, &config->ssp, ssp_tokens, ARRAY_SIZE(ssp_tokens), private->array, private->size); From 049371d9e2988bc405d1ee2aefc1d89081743744 Mon Sep 17 00:00:00 2001 From: Pan Xiuli Date: Wed, 13 Jun 2018 16:25:41 +0800 Subject: [PATCH 101/107] ASoC: SOF: Add debug_mode flag in sof dev This flag will control debug usage function. Signed-off-by: Pan Xiuli --- sound/soc/sof/sof-priv.h | 1 + 1 file changed, 1 insertion(+) diff --git a/sound/soc/sof/sof-priv.h b/sound/soc/sof/sof-priv.h index fabfcb25180940..40615a4ead377a 100644 --- a/sound/soc/sof/sof-priv.h +++ b/sound/soc/sof/sof-priv.h @@ -293,6 +293,7 @@ struct snd_sof_dev { /* debug */ struct dentry *debugfs_root; + u32 debug_mode; /* firmware loader */ int cl_bar; From 0e671821b5dcd0d843a77140be3ff1fe64cbe83a Mon Sep 17 00:00:00 2001 From: Pan Xiuli Date: Wed, 13 Jun 2018 16:28:20 +0800 Subject: [PATCH 102/107] ASoC: SOF: debug: add debugmode debugfs for sof_dev debug_mode Use debugfs to control debug_mode flag for sof dev. Default enable the debug mode in DEBUG build. Signed-off-by: Pan Xiuli --- sound/soc/sof/debug.c | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/sound/soc/sof/debug.c b/sound/soc/sof/debug.c index c1580a17d2f2d2..85d4edb7490bed 100644 --- a/sound/soc/sof/debug.c +++ b/sound/soc/sof/debug.c @@ -107,6 +107,7 @@ int snd_sof_dbg_init(struct snd_sof_dev *sdev) { const struct snd_sof_dsp_ops *ops = sdev->ops; const struct snd_sof_debugfs_map *map; + struct dentry *dfsentry; int err = 0, i; sdev->debugfs_root = debugfs_create_dir("sof", NULL); @@ -126,6 +127,19 @@ int snd_sof_dbg_init(struct snd_sof_dev *sdev) map->name); } +#ifdef DEBUG + /* enable debug mode for DEBUG build */ + sdev->debug_mode = 1; +#else + sdev->debug_mode = 0; +#endif + dfsentry = debugfs_create_u32("debugmode", 0644, sdev->debugfs_root, + &sdev->debug_mode); + if (!dfsentry) { + dev_err(sdev->dev, "cannot create debugmode debugfs entry.\n"); + return -ENODEV; + } + return err; } EXPORT_SYMBOL(snd_sof_dbg_init); From 36cb3001e6edeeac4c7f3bb75c7ad95bcf4c6a0e Mon Sep 17 00:00:00 2001 From: Pan Xiuli Date: Mon, 4 Jun 2018 13:51:59 +0800 Subject: [PATCH 103/107] ASoC: SOF: uapi: topology: Add SOF_TKN_DAI_EXT_CONFIG dai extra config Signed-off-by: Pan Xiuli --- include/uapi/sound/sof-topology.h | 1 + 1 file changed, 1 insertion(+) diff --git a/include/uapi/sound/sof-topology.h b/include/uapi/sound/sof-topology.h index 096aaed4333059..1ea5483b776a73 100644 --- a/include/uapi/sound/sof-topology.h +++ b/include/uapi/sound/sof-topology.h @@ -37,6 +37,7 @@ #define SOF_TKN_DAI_DMAC_CONFIG 153 #define SOF_TKN_DAI_TYPE 154 #define SOF_TKN_DAI_INDEX 155 +#define SOF_TKN_DAI_EXT_CONFIG 157 /* scheduling */ #define SOF_TKN_SCHED_DEADLINE 200 From f274a944d8594f3b82a62386798ebcb8b06d48a9 Mon Sep 17 00:00:00 2001 From: Pan Xiuli Date: Thu, 28 Jun 2018 16:28:48 +0800 Subject: [PATCH 104/107] ASoC: SOF: add SOF_DAI_EXT_SSP_LBM bit for the DAI CONFIG This bit macro is used for the SOF_TKN_DAI_EXT_CONFIG. Signed-off-by: Pan Xiuli --- sound/soc/sof/sof-priv.h | 3 +++ 1 file changed, 3 insertions(+) diff --git a/sound/soc/sof/sof-priv.h b/sound/soc/sof/sof-priv.h index 40615a4ead377a..a43df17df4a1ad 100644 --- a/sound/soc/sof/sof-priv.h +++ b/sound/soc/sof/sof-priv.h @@ -43,6 +43,9 @@ /* max number of FE PCMs before BEs */ #define SOF_BE_PCM_BASE 16 +/* dai extra config bits */ +#define SOF_DAI_EXT_SSP_LBM BIT(0) + struct snd_sof_dev; struct snd_sof_ipc_msg; struct snd_sof_ipc; From a1c419f1ffc54114356a4f5a035f1c3ba7149972 Mon Sep 17 00:00:00 2001 From: Pan Xiuli Date: Wed, 20 Jun 2018 16:08:50 +0800 Subject: [PATCH 105/107] ASoC: SOF: add headers for switch control callback functions Add switch kcontrol IO callback functions. debug switch callback function will only take effects when we are in debug mode. It will used for some debug only switch kcontrol. Signed-off-by: Pan Xiuli --- sound/soc/sof/sof-priv.h | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/sound/soc/sof/sof-priv.h b/sound/soc/sof/sof-priv.h index a43df17df4a1ad..67b70a14b52f58 100644 --- a/sound/soc/sof/sof-priv.h +++ b/sound/soc/sof/sof-priv.h @@ -458,6 +458,14 @@ int snd_sof_bytes_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol); int snd_sof_bytes_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol); +int snd_sof_switch_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol); +int snd_sof_switch_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol); +int snd_sof_switch_debug_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol); +int snd_sof_switch_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo); /* * DSP Architectures. From e95f693b6af31b59a2096ab82964b201e9f1fbcc Mon Sep 17 00:00:00 2001 From: Pan Xiuli Date: Wed, 20 Jun 2018 16:16:54 +0800 Subject: [PATCH 106/107] ASoC: SOF: add switch kcontrol callback functions Add switch_get/put/info callback functions for switch kcontrol. Add switch_debug_put callback fucntions for debug use switch kcontrol. Signed-off-by: Pan Xiuli --- sound/soc/sof/control.c | 96 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 96 insertions(+) diff --git a/sound/soc/sof/control.c b/sound/soc/sof/control.c index db09ba63154b69..8374656683bca0 100644 --- a/sound/soc/sof/control.c +++ b/sound/soc/sof/control.c @@ -195,3 +195,99 @@ int snd_sof_bytes_put(struct snd_kcontrol *kcontrol, pm_runtime_put_autosuspend(sdev->dev); return 0; } + +int snd_sof_switch_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_sof_control *scontrol = + (struct snd_sof_control *)kcontrol->private_value; + struct snd_sof_dev *sdev = scontrol->sdev; + struct sof_ipc_ctrl_data *cdata = scontrol->control_data; + int err; + + pm_runtime_get_sync(sdev->dev); + + /* get all the mixer data from DSP */ + err = snd_sof_ipc_get_comp_data(sdev->ipc, scontrol, + SOF_IPC_COMP_GET_VALUE, + SOF_CTRL_TYPE_VALUE_COMP_GET, + scontrol->cmd); + if (err < 0) { + dev_err(sdev->dev, "error: failed to get comp %d switch\n", + cdata->comp_id); + return err; + } + + dev_dbg(sdev->dev, "comp %d switch get %d\n", scontrol->comp_id, + cdata->compv[0].uvalue); + /* get value from ipc control data */ + ucontrol->value.integer.value[0] = cdata->compv[0].uvalue; + + pm_runtime_mark_last_busy(sdev->dev); + pm_runtime_put_autosuspend(sdev->dev); + return 0; +} + +int snd_sof_switch_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_sof_control *scontrol = + (struct snd_sof_control *)kcontrol->private_value; + struct snd_sof_dev *sdev = scontrol->sdev; + struct sof_ipc_ctrl_data *cdata = scontrol->control_data; + int changed = 0; + int err; + + pm_runtime_get_sync(sdev->dev); + + if (cdata->compv[0].uvalue != ucontrol->value.integer.value[0]) { + /* set value into ipc control data */ + cdata->compv[0].uvalue = ucontrol->value.integer.value[0]; + err = snd_sof_ipc_set_comp_data(sdev->ipc, scontrol, + SOF_IPC_COMP_SET_VALUE, + SOF_CTRL_TYPE_VALUE_COMP_SET, + scontrol->cmd); + + if (err < 0) { + dev_err(sdev->dev, "error: failed to set comp %d switch\n", + cdata->comp_id); + return err; + } + + dev_dbg(sdev->dev, "comp %d switch put %d\n", scontrol->comp_id, + cdata->compv[0].uvalue); + + changed = 1; + } + + pm_runtime_mark_last_busy(sdev->dev); + pm_runtime_put_autosuspend(sdev->dev); + return changed; +} + +int snd_sof_switch_debug_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_sof_control *scontrol = + (struct snd_sof_control *)kcontrol->private_value; + struct snd_sof_dev *sdev = scontrol->sdev; + + /* can only switch when debug_mode enable */ + if (!sdev->debug_mode) { + dev_err(sdev->dev, + "debug switch only enabled with debugfs debug_mode nonzero!\n"); + return 0; + } + + return snd_sof_switch_put(kcontrol, ucontrol); +} + +int snd_sof_switch_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; + uinfo->count = 1; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = 1; + return 0; +} From b45116c3e9d41114c2db2194d79926df33e6548e Mon Sep 17 00:00:00 2001 From: Pan Xiuli Date: Wed, 20 Jun 2018 16:23:55 +0800 Subject: [PATCH 107/107] ASoC: SOF: topology: Add topology handler for dai lbm kcontrol Add handler to get loopback mode kcontrol bit from topology DAI extra config token. Then create the kcontrol for the loopback mode for SSP. Signed-off-by: Pan Xiuli --- sound/soc/sof/topology.c | 94 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 94 insertions(+) diff --git a/sound/soc/sof/topology.c b/sound/soc/sof/topology.c index f9347384ecf3ad..87be51c03e0450 100644 --- a/sound/soc/sof/topology.c +++ b/sound/soc/sof/topology.c @@ -312,6 +312,11 @@ static int get_token_dai_type(void *elem, void *object, u32 offset, u32 size) return 0; } +struct sof_dai_extra_config { + u32 configs; + u32 sspindex; +}; + /* Buffers */ static const struct sof_topology_token buffer_tokens[] = { {SOF_TKN_BUF_SIZE, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32, @@ -330,6 +335,14 @@ static const struct sof_topology_token dai_tokens[] = { offsetof(struct sof_ipc_comp_dai, index), 0}, }; +/* DAI EXTRA CONFIG */ +static const struct sof_topology_token sof_dai_extra_config_tokens[] = { + {SOF_TKN_DAI_EXT_CONFIG, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32, + offsetof(struct sof_dai_extra_config, configs), 0}, + {SOF_TKN_DAI_INDEX, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32, + offsetof(struct sof_dai_extra_config, sspindex), 0}, +}; + /* BE DAI link */ static const struct sof_topology_token dai_link_tokens[] = { {SOF_TKN_DAI_TYPE, SND_SOC_TPLG_TUPLE_TYPE_STRING, get_token_dai_type, @@ -804,6 +817,70 @@ static int sof_connect_dai_widget(struct snd_soc_component *scomp, return 0; } +/* LBM kcontrol, used to control loopback mode on SSP */ +static int sof_dai_add_lbm_kcontrol(struct snd_soc_component *scomp, + u32 comp_id, u32 index) +{ + struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp); + struct snd_sof_control *scontrol; + struct sof_ipc_ctrl_data *cdata; + struct snd_kcontrol *kcontrol; + struct snd_kcontrol_new *kc; + char kcname[50]; + int err; + + sprintf(kcname, "sof SSP%d LBM Switch", index); + dev_dbg(sdev->dev, "Adding %s\n", kcname); + + kc = kzalloc(sizeof(*kc), GFP_KERNEL); + if (!kc) + return -ENOMEM; + + scontrol = kzalloc(sizeof(*scontrol), GFP_KERNEL); + if (!scontrol) + return -ENOMEM; + + scontrol->size = sizeof(struct sof_ipc_ctrl_data) + + sizeof(struct sof_ipc_ctrl_value_comp); + cdata = kzalloc(scontrol->size, GFP_KERNEL); + if (!cdata) + return -ENOMEM; + + /* set sof control */ + scontrol->control_data = cdata; + scontrol->comp_id = comp_id; + scontrol->sdev = sdev; + scontrol->cmd = SOF_CTRL_CMD_SWITCH; + scontrol->num_channels = 1; + + /* set kcontrol new structure for kcontrol create */ + kc->name = kcname; + kc->private_value = (long)scontrol; + kc->iface = SNDRV_CTL_ELEM_IFACE_MIXER; + kc->access = SNDRV_CTL_ELEM_ACCESS_READWRITE; + kc->info = snd_sof_switch_info; + kc->put = snd_sof_switch_debug_put; + kc->get = snd_sof_switch_get; + + /* create kcontrol */ + kcontrol = snd_soc_cnew(kc, scomp, kc->name, NULL); + if (!kcontrol) { + dev_err(sdev->dev, "ASoC: Failed to create new kcontrol %s\n", + kc->name); + return -ENOMEM; + } + + /* add kcontrol to card */ + err = snd_ctl_add(scomp->card->snd_card, kcontrol); + if (err < 0) { + dev_err(sdev->dev, "ASoC: Failed to add %s: %d\n", + kc->name, err); + return err; + } + + return 0; +} + static int sof_widget_load_dai(struct snd_soc_component *scomp, int index, struct snd_sof_widget *swidget, struct snd_soc_tplg_dapm_widget *tw, @@ -812,6 +889,7 @@ static int sof_widget_load_dai(struct snd_soc_component *scomp, int index, { struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp); struct snd_soc_tplg_private *private = &tw->priv; + struct sof_dai_extra_config ext_config; struct sof_ipc_comp_dai comp_dai; int ret; @@ -841,6 +919,22 @@ static int sof_widget_load_dai(struct snd_soc_component *scomp, int index, return ret; } + ext_config.configs = 0; + ret = sof_parse_tokens(scomp, &ext_config, sof_dai_extra_config_tokens, + ARRAY_SIZE(sof_dai_extra_config_tokens), + private->array, private->size); + if (ret != 0) { + dev_err(sdev->dev, + "error: parse dai ext config tokens failed %d\n", + private->size); + return ret; + } + + /* add kcontrol for loop back mode */ + if (ext_config.configs && SOF_DAI_EXT_SSP_LBM) + sof_dai_add_lbm_kcontrol(scomp, swidget->comp_id, + ext_config.sspindex); + dev_dbg(sdev->dev, "dai %s: type %d index %d\n", swidget->widget->name, comp_dai.type, comp_dai.index); sof_dbg_comp_config(scomp, &comp_dai.config);