Merge remote-tracking branches 'asoc/topic/imx' and 'asoc/topic/intel' into asoc...
authorMark Brown <broonie@kernel.org>
Mon, 26 Oct 2015 02:16:06 +0000 (11:16 +0900)
committerMark Brown <broonie@kernel.org>
Mon, 26 Oct 2015 02:16:06 +0000 (11:16 +0900)
22 files changed:
sound/soc/fsl/imx-spdif.c
sound/soc/intel/Kconfig
sound/soc/intel/boards/Makefile
sound/soc/intel/boards/skl_rt286.c [new file with mode: 0644]
sound/soc/intel/common/Makefile
sound/soc/intel/common/sst-dsp-priv.h
sound/soc/intel/common/sst-dsp.c
sound/soc/intel/common/sst-dsp.h
sound/soc/intel/common/sst-firmware.c
sound/soc/intel/skylake/Makefile
sound/soc/intel/skylake/skl-messages.c
sound/soc/intel/skylake/skl-nhlt.c
sound/soc/intel/skylake/skl-pcm.c
sound/soc/intel/skylake/skl-sst-dsp.c
sound/soc/intel/skylake/skl-sst-ipc.c
sound/soc/intel/skylake/skl-sst-ipc.h
sound/soc/intel/skylake/skl-sst.c
sound/soc/intel/skylake/skl-topology.c [new file with mode: 0644]
sound/soc/intel/skylake/skl-topology.h
sound/soc/intel/skylake/skl-tplg-interface.h
sound/soc/intel/skylake/skl.c
sound/soc/intel/skylake/skl.h

index 33da26a..a407e83 100644 (file)
@@ -89,6 +89,7 @@ MODULE_DEVICE_TABLE(of, imx_spdif_dt_ids);
 static struct platform_driver imx_spdif_driver = {
        .driver = {
                .name = "imx-spdif",
+               .pm = &snd_soc_pm_ops,
                .of_match_table = imx_spdif_dt_ids,
        },
        .probe = imx_spdif_audio_probe,
index 221e3bd..7b778ab 100644 (file)
@@ -139,4 +139,18 @@ config SND_SOC_INTEL_CHT_BSW_MAX98090_TI_MACH
 config SND_SOC_INTEL_SKYLAKE
        tristate
        select SND_HDA_EXT_CORE
+       select SND_SOC_TOPOLOGY
        select SND_SOC_INTEL_SST
+
+config SND_SOC_INTEL_SKL_RT286_MACH
+       tristate "ASoC Audio driver for SKL with RT286 I2S mode"
+       depends on X86 && ACPI
+       select SND_SOC_INTEL_SST
+       select SND_SOC_INTEL_SKYLAKE
+       select SND_SOC_RT286
+       select SND_SOC_DMIC
+       help
+          This adds support for ASoC machine driver for Skylake platforms
+          with RT286 I2S audio codec.
+          Say Y if you have such a device
+          If unsure select "N".
index cb94895..371c456 100644 (file)
@@ -6,6 +6,7 @@ snd-soc-sst-bytcr-rt5640-objs := bytcr_rt5640.o
 snd-soc-sst-cht-bsw-rt5672-objs := cht_bsw_rt5672.o
 snd-soc-sst-cht-bsw-rt5645-objs := cht_bsw_rt5645.o
 snd-soc-sst-cht-bsw-max98090_ti-objs := cht_bsw_max98090_ti.o
+snd-soc-skl_rt286-objs := skl_rt286.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
@@ -15,3 +16,4 @@ obj-$(CONFIG_SND_SOC_INTEL_BYTCR_RT5640_MACH) += snd-soc-sst-bytcr-rt5640.o
 obj-$(CONFIG_SND_SOC_INTEL_CHT_BSW_RT5672_MACH) += snd-soc-sst-cht-bsw-rt5672.o
 obj-$(CONFIG_SND_SOC_INTEL_CHT_BSW_RT5645_MACH) += snd-soc-sst-cht-bsw-rt5645.o
 obj-$(CONFIG_SND_SOC_INTEL_CHT_BSW_MAX98090_TI_MACH) += snd-soc-sst-cht-bsw-max98090_ti.o
+obj-$(CONFIG_SND_SOC_INTEL_SKL_RT286_MACH) += snd-soc-skl_rt286.o
diff --git a/sound/soc/intel/boards/skl_rt286.c b/sound/soc/intel/boards/skl_rt286.c
new file mode 100644 (file)
index 0000000..a73a431
--- /dev/null
@@ -0,0 +1,259 @@
+/*
+ * Intel Skylake I2S Machine Driver
+ *
+ * Copyright (C) 2014-2015, Intel Corporation. All rights reserved.
+ *
+ * Modified from:
+ *   Intel Broadwell Wildcatpoint SST Audio
+ *
+ *   Copyright (C) 2013, Intel Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License version
+ * 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/soc.h>
+#include <sound/jack.h>
+#include <sound/pcm_params.h>
+#include "../../codecs/rt286.h"
+
+static struct snd_soc_jack skylake_headset;
+/* Headset jack detection DAPM pins */
+static struct snd_soc_jack_pin skylake_headset_pins[] = {
+       {
+               .pin = "Mic Jack",
+               .mask = SND_JACK_MICROPHONE,
+       },
+       {
+               .pin = "Headphone Jack",
+               .mask = SND_JACK_HEADPHONE,
+       },
+};
+
+static const struct snd_kcontrol_new skylake_controls[] = {
+       SOC_DAPM_PIN_SWITCH("Speaker"),
+       SOC_DAPM_PIN_SWITCH("Headphone Jack"),
+       SOC_DAPM_PIN_SWITCH("Mic Jack"),
+};
+
+static const struct snd_soc_dapm_widget skylake_widgets[] = {
+       SND_SOC_DAPM_HP("Headphone Jack", NULL),
+       SND_SOC_DAPM_SPK("Speaker", NULL),
+       SND_SOC_DAPM_MIC("Mic Jack", NULL),
+       SND_SOC_DAPM_MIC("DMIC2", NULL),
+       SND_SOC_DAPM_MIC("SoC DMIC", NULL),
+};
+
+static const struct snd_soc_dapm_route skylake_rt286_map[] = {
+       /* speaker */
+       {"Speaker", NULL, "SPOR"},
+       {"Speaker", NULL, "SPOL"},
+
+       /* HP jack connectors - unknown if we have jack deteck */
+       {"Headphone Jack", NULL, "HPO Pin"},
+
+       /* other jacks */
+       {"MIC1", NULL, "Mic Jack"},
+
+       /* digital mics */
+       {"DMIC1 Pin", NULL, "DMIC2"},
+       {"DMIC AIF", NULL, "SoC DMIC"},
+
+       /* CODEC BE connections */
+       { "AIF1 Playback", NULL, "ssp0 Tx"},
+       { "ssp0 Tx", NULL, "codec0_out"},
+       { "ssp0 Tx", NULL, "codec1_out"},
+
+       { "codec0_in", NULL, "ssp0 Rx" },
+       { "codec1_in", NULL, "ssp0 Rx" },
+       { "ssp0 Rx", NULL, "AIF1 Capture" },
+
+       { "dmic01_hifi", NULL, "DMIC01 Rx" },
+       { "DMIC01 Rx", NULL, "Capture" },
+
+       { "hif1", NULL, "iDisp Tx"},
+       { "iDisp Tx", NULL, "iDisp_out"},
+
+};
+
+static int skylake_rt286_codec_init(struct snd_soc_pcm_runtime *rtd)
+{
+       struct snd_soc_codec *codec = rtd->codec;
+       int ret;
+
+       ret = snd_soc_card_jack_new(rtd->card, "Headset",
+               SND_JACK_HEADSET | SND_JACK_BTN_0,
+               &skylake_headset,
+               skylake_headset_pins, ARRAY_SIZE(skylake_headset_pins));
+
+       if (ret)
+               return ret;
+
+       rt286_mic_detect(codec, &skylake_headset);
+
+       return 0;
+}
+
+
+static int skylake_ssp0_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);
+
+       /* The output is 48KHz, stereo, 16bits */
+       rate->min = rate->max = 48000;
+       channels->min = channels->max = 2;
+       params_set_format(params, SNDRV_PCM_FORMAT_S16_LE);
+
+       return 0;
+}
+
+static int skylake_rt286_hw_params(struct snd_pcm_substream *substream,
+       struct snd_pcm_hw_params *params)
+{
+       struct snd_soc_pcm_runtime *rtd = substream->private_data;
+       struct snd_soc_dai *codec_dai = rtd->codec_dai;
+       int ret;
+
+       ret = snd_soc_dai_set_sysclk(codec_dai, RT286_SCLK_S_PLL, 24000000,
+               SND_SOC_CLOCK_IN);
+       if (ret < 0)
+               dev_err(rtd->dev, "set codec sysclk failed: %d\n", ret);
+
+       return ret;
+}
+
+static struct snd_soc_ops skylake_rt286_ops = {
+       .hw_params = skylake_rt286_hw_params,
+};
+
+/* skylake digital audio interface glue - connects codec <--> CPU */
+static struct snd_soc_dai_link skylake_rt286_dais[] = {
+       /* Front End DAI links */
+       {
+               .name = "Skl Audio Port",
+               .stream_name = "Audio",
+               .cpu_dai_name = "System Pin",
+               .platform_name = "0000:00:1f.3",
+               .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 = "Skl Audio Capture Port",
+               .stream_name = "Audio Record",
+               .cpu_dai_name = "System Pin",
+               .platform_name = "0000:00:1f.3",
+               .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_capture = 1,
+       },
+       {
+               .name = "Skl Audio Reference cap",
+               .stream_name = "refcap",
+               .cpu_dai_name = "Reference Pin",
+               .codec_name = "snd-soc-dummy",
+               .codec_dai_name = "snd-soc-dummy-dai",
+               .platform_name = "0000:00:1f.3",
+               .init = NULL,
+               .dpcm_capture = 1,
+               .ignore_suspend = 1,
+               .nonatomic = 1,
+               .dynamic = 1,
+       },
+
+       /* Back End DAI links */
+       {
+               /* SSP0 - Codec */
+               .name = "SSP0-Codec",
+               .be_id = 0,
+               .cpu_dai_name = "SSP0 Pin",
+               .platform_name = "0000:00:1f.3",
+               .no_pcm = 1,
+               .codec_name = "i2c-INT343A:00",
+               .codec_dai_name = "rt286-aif1",
+               .init = skylake_rt286_codec_init,
+               .dai_fmt = SND_SOC_DAIFMT_I2S |
+                       SND_SOC_DAIFMT_NB_NF |
+                       SND_SOC_DAIFMT_CBS_CFS,
+               .ignore_suspend = 1,
+               .ignore_pmdown_time = 1,
+               .be_hw_params_fixup = skylake_ssp0_fixup,
+               .ops = &skylake_rt286_ops,
+               .dpcm_playback = 1,
+               .dpcm_capture = 1,
+       },
+       {
+               .name = "dmic01",
+               .be_id = 1,
+               .cpu_dai_name = "DMIC01 Pin",
+               .codec_name = "dmic-codec",
+               .codec_dai_name = "dmic-hifi",
+               .platform_name = "0000:00:1f.3",
+               .ignore_suspend = 1,
+               .dpcm_capture = 1,
+               .no_pcm = 1,
+       },
+};
+
+/* skylake audio machine driver for SPT + RT286S */
+static struct snd_soc_card skylake_rt286 = {
+       .name = "skylake-rt286",
+       .owner = THIS_MODULE,
+       .dai_link = skylake_rt286_dais,
+       .num_links = ARRAY_SIZE(skylake_rt286_dais),
+       .controls = skylake_controls,
+       .num_controls = ARRAY_SIZE(skylake_controls),
+       .dapm_widgets = skylake_widgets,
+       .num_dapm_widgets = ARRAY_SIZE(skylake_widgets),
+       .dapm_routes = skylake_rt286_map,
+       .num_dapm_routes = ARRAY_SIZE(skylake_rt286_map),
+       .fully_routed = true,
+};
+
+static int skylake_audio_probe(struct platform_device *pdev)
+{
+       skylake_rt286.dev = &pdev->dev;
+
+       return devm_snd_soc_register_card(&pdev->dev, &skylake_rt286);
+}
+
+static struct platform_driver skylake_audio = {
+       .probe = skylake_audio_probe,
+       .driver = {
+               .name = "skl_alc286s_i2s",
+       },
+};
+
+module_platform_driver(skylake_audio)
+
+/* Module information */
+MODULE_AUTHOR("Omair Mohammed Abdullah <omair.m.abdullah@intel.com>");
+MODULE_DESCRIPTION("Intel SST Audio for Skylake");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:skl_alc286s_i2s");
index f24154c..d910558 100644 (file)
@@ -1,7 +1,11 @@
-snd-soc-sst-dsp-objs := sst-dsp.o sst-firmware.o
+snd-soc-sst-dsp-objs := sst-dsp.o
 snd-soc-sst-acpi-objs := sst-acpi.o
 snd-soc-sst-ipc-objs := sst-ipc.o
 
+ifneq ($(CONFIG_DW_DMAC_CORE),)
+snd-soc-sst-dsp-objs += sst-firmware.o
+endif
+
 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
 
index cbd568e..2151652 100644 (file)
@@ -314,6 +314,7 @@ struct sst_dsp {
        int sst_state;
        struct skl_cl_dev cl_dev;
        u32 intr_status;
+       const struct firmware *fw;
 };
 
 /* Size optimised DRAM/IRAM memcpy */
index a627236..c9452e0 100644 (file)
@@ -420,6 +420,7 @@ void sst_dsp_inbox_read(struct sst_dsp *sst, void *message, size_t bytes)
 }
 EXPORT_SYMBOL_GPL(sst_dsp_inbox_read);
 
+#if IS_ENABLED(CONFIG_DW_DMAC_CORE)
 struct sst_dsp *sst_dsp_new(struct device *dev,
        struct sst_dsp_device *sst_dev, struct sst_pdata *pdata)
 {
@@ -484,6 +485,7 @@ void sst_dsp_free(struct sst_dsp *sst)
        sst_dma_free(sst->dma);
 }
 EXPORT_SYMBOL_GPL(sst_dsp_free);
+#endif
 
 /* Module information */
 MODULE_AUTHOR("Liam Girdwood");
index 1f45f18..859f0de 100644 (file)
@@ -216,10 +216,12 @@ struct sst_pdata {
        void *dsp;
 };
 
+#if IS_ENABLED(CONFIG_DW_DMAC_CORE)
 /* Initialization */
 struct sst_dsp *sst_dsp_new(struct device *dev,
        struct sst_dsp_device *sst_dev, struct sst_pdata *pdata);
 void sst_dsp_free(struct sst_dsp *sst);
+#endif
 
 /* SHIM Read / Write */
 void sst_dsp_shim_write(struct sst_dsp *sst, u32 offset, u32 value);
index ebcca6d..1636a1e 100644 (file)
@@ -26,7 +26,6 @@
 #include <linux/acpi.h>
 
 /* supported DMA engine drivers */
-#include <linux/platform_data/dma-dw.h>
 #include <linux/dma/dw.h>
 
 #include <asm/page.h>
@@ -169,12 +168,6 @@ err:
        return ret;
 }
 
-static struct dw_dma_platform_data dw_pdata = {
-       .is_private = 1,
-       .chan_allocation_order = CHAN_ALLOCATION_ASCENDING,
-       .chan_priority = CHAN_PRIORITY_ASCENDING,
-};
-
 static struct dw_dma_chip *dw_probe(struct device *dev, struct resource *mem,
        int irq)
 {
@@ -195,7 +188,8 @@ static struct dw_dma_chip *dw_probe(struct device *dev, struct resource *mem,
                return ERR_PTR(err);
 
        chip->dev = dev;
-       err = dw_dma_probe(chip, &dw_pdata);
+
+       err = dw_dma_probe(chip, NULL);
        if (err)
                return ERR_PTR(err);
 
index 27db221..914b6da 100644 (file)
@@ -1,4 +1,5 @@
-snd-soc-skl-objs := skl.o skl-pcm.o skl-nhlt.o skl-messages.o
+snd-soc-skl-objs := skl.o skl-pcm.o skl-nhlt.o skl-messages.o \
+skl-topology.o
 
 obj-$(CONFIG_SND_SOC_INTEL_SKYLAKE) += snd-soc-skl.o
 
index 826d4fd..50a1095 100644 (file)
@@ -54,6 +54,24 @@ static int skl_free_dma_buf(struct device *dev, struct snd_dma_buffer *dmab)
        return 0;
 }
 
+#define NOTIFICATION_PARAM_ID 3
+#define NOTIFICATION_MASK 0xf
+
+/* disable notfication for underruns/overruns from firmware module */
+static void skl_dsp_enable_notification(struct skl_sst *ctx, bool enable)
+{
+       struct notification_mask mask;
+       struct skl_ipc_large_config_msg msg = {0};
+
+       mask.notify = NOTIFICATION_MASK;
+       mask.enable = enable;
+
+       msg.large_param_id = NOTIFICATION_PARAM_ID;
+       msg.param_data_size = sizeof(mask);
+
+       skl_ipc_set_large_config(&ctx->ipc, &msg, (u32 *)&mask);
+}
+
 int skl_init_dsp(struct skl *skl)
 {
        void __iomem *mmio_base;
@@ -79,7 +97,10 @@ int skl_init_dsp(struct skl *skl)
 
        ret = skl_sst_dsp_init(bus->dev, mmio_base, irq,
                        loader_ops, &skl->skl_sst);
+       if (ret < 0)
+               return ret;
 
+       skl_dsp_enable_notification(skl->skl_sst, false);
        dev_dbg(bus->dev, "dsp registration status=%d\n", ret);
 
        return ret;
@@ -122,6 +143,7 @@ int skl_suspend_dsp(struct skl *skl)
 int skl_resume_dsp(struct skl *skl)
 {
        struct skl_sst *ctx = skl->skl_sst;
+       int ret;
 
        /* if ppcap is not supported return 0 */
        if (!skl->ebus.ppcap)
@@ -131,7 +153,12 @@ int skl_resume_dsp(struct skl *skl)
        snd_hdac_ext_bus_ppcap_enable(&skl->ebus, true);
        snd_hdac_ext_bus_ppcap_int_enable(&skl->ebus, true);
 
-       return skl_dsp_wake(ctx->dsp);
+       ret = skl_dsp_wake(ctx->dsp);
+       if (ret < 0)
+               return ret;
+
+       skl_dsp_enable_notification(skl->skl_sst, false);
+       return ret;
 }
 
 enum skl_bitdepth skl_get_bit_depth(int params)
@@ -294,6 +321,7 @@ static void skl_copy_copier_caps(struct skl_module_cfg *mconfig,
                        (mconfig->formats_config.caps_size) / 4;
 }
 
+#define SKL_NON_GATEWAY_CPR_NODE_ID 0xFFFFFFFF
 /*
  * Calculate the gatewat settings required for copier module, type of
  * gateway and index of gateway to use
@@ -303,6 +331,7 @@ static void skl_setup_cpr_gateway_cfg(struct skl_sst *ctx,
                        struct skl_cpr_cfg *cpr_mconfig)
 {
        union skl_connector_node_id node_id = {0};
+       union skl_ssp_dma_node ssp_node  = {0};
        struct skl_pipe_params *params = mconfig->pipe->p_params;
 
        switch (mconfig->dev_type) {
@@ -320,9 +349,9 @@ static void skl_setup_cpr_gateway_cfg(struct skl_sst *ctx,
                        (SKL_CONN_SOURCE == mconfig->hw_conn_type) ?
                        SKL_DMA_I2S_LINK_OUTPUT_CLASS :
                        SKL_DMA_I2S_LINK_INPUT_CLASS;
-               node_id.node.vindex = params->host_dma_id +
-                                        (mconfig->time_slot << 1) +
-                                        (mconfig->vbus_id << 3);
+               ssp_node.dma_node.time_slot_index = mconfig->time_slot;
+               ssp_node.dma_node.i2s_instance = mconfig->vbus_id;
+               node_id.node.vindex = ssp_node.val;
                break;
 
        case SKL_DEVICE_DMIC:
@@ -339,13 +368,18 @@ static void skl_setup_cpr_gateway_cfg(struct skl_sst *ctx,
                node_id.node.vindex = params->link_dma_id;
                break;
 
-       default:
+       case SKL_DEVICE_HDAHOST:
                node_id.node.dma_type =
                        (SKL_CONN_SOURCE == mconfig->hw_conn_type) ?
                        SKL_DMA_HDA_HOST_OUTPUT_CLASS :
                        SKL_DMA_HDA_HOST_INPUT_CLASS;
                node_id.node.vindex = params->host_dma_id;
                break;
+
+       default:
+               cpr_mconfig->gtw_cfg.node_id = SKL_NON_GATEWAY_CPR_NODE_ID;
+               cpr_mconfig->cpr_feature_mask = 0;
+               return;
        }
 
        cpr_mconfig->gtw_cfg.node_id = node_id.val;
index 13036b1..b0c7bd1 100644 (file)
@@ -25,7 +25,7 @@ static u8 OSC_UUID[16] = {0x6E, 0x88, 0x9F, 0xA6, 0xEB, 0x6C, 0x94, 0x45,
 
 #define DSDT_NHLT_PATH "\\_SB.PCI0.HDAS"
 
-void __iomem *skl_nhlt_init(struct device *dev)
+void *skl_nhlt_init(struct device *dev)
 {
        acpi_handle handle;
        union acpi_object *obj;
@@ -40,17 +40,17 @@ void __iomem *skl_nhlt_init(struct device *dev)
        if (obj && obj->type == ACPI_TYPE_BUFFER) {
                nhlt_ptr = (struct nhlt_resource_desc  *)obj->buffer.pointer;
 
-               return ioremap_cache(nhlt_ptr->min_addr, nhlt_ptr->length);
+               return memremap(nhlt_ptr->min_addr, nhlt_ptr->length,
+                               MEMREMAP_WB);
        }
 
        dev_err(dev, "device specific method to extract NHLT blob failed\n");
        return NULL;
 }
 
-void skl_nhlt_free(void __iomem *addr)
+void skl_nhlt_free(void *addr)
 {
-       iounmap(addr);
-       addr = NULL;
+       memunmap(addr);
 }
 
 static struct nhlt_specific_cfg *skl_get_specific_cfg(
index bea2673..a2f94ce 100644 (file)
@@ -24,6 +24,7 @@
 #include <sound/pcm_params.h>
 #include <sound/soc.h>
 #include "skl.h"
+#include "skl-topology.h"
 
 #define HDA_MONO 1
 #define HDA_STEREO 2
@@ -115,7 +116,7 @@ static int skl_pcm_open(struct snd_pcm_substream *substream,
 
        dev_dbg(dai->dev, "%s: %s\n", __func__, dai->name);
        ret = pm_runtime_get_sync(dai->dev);
-       if (ret)
+       if (ret < 0)
                return ret;
 
        stream = snd_hdac_ext_stream_assign(ebus, substream,
@@ -214,6 +215,8 @@ static int skl_pcm_hw_params(struct snd_pcm_substream *substream,
        struct hdac_ext_bus *ebus = dev_get_drvdata(dai->dev);
        struct hdac_ext_stream *stream = get_hdac_ext_stream(substream);
        struct snd_pcm_runtime *runtime = substream->runtime;
+       struct skl_pipe_params p_params = {0};
+       struct skl_module_cfg *m_cfg;
        int ret, dma_id;
 
        dev_dbg(dai->dev, "%s: %s\n", __func__, dai->name);
@@ -228,6 +231,16 @@ static int skl_pcm_hw_params(struct snd_pcm_substream *substream,
        dma_id = hdac_stream(stream)->stream_tag - 1;
        dev_dbg(dai->dev, "dma_id=%d\n", dma_id);
 
+       p_params.s_fmt = snd_pcm_format_width(params_format(params));
+       p_params.ch = params_channels(params);
+       p_params.s_freq = params_rate(params);
+       p_params.host_dma_id = dma_id;
+       p_params.stream = substream->stream;
+
+       m_cfg = skl_tplg_fe_get_cpr_module(dai, p_params.stream);
+       if (m_cfg)
+               skl_tplg_update_pipe_params(dai->dev, m_cfg, &p_params);
+
        return 0;
 }
 
@@ -268,6 +281,46 @@ static int skl_pcm_hw_free(struct snd_pcm_substream *substream,
        return skl_substream_free_pages(ebus_to_hbus(ebus), substream);
 }
 
+static int skl_be_hw_params(struct snd_pcm_substream *substream,
+                               struct snd_pcm_hw_params *params,
+                               struct snd_soc_dai *dai)
+{
+       struct skl_pipe_params p_params = {0};
+
+       p_params.s_fmt = snd_pcm_format_width(params_format(params));
+       p_params.ch = params_channels(params);
+       p_params.s_freq = params_rate(params);
+       p_params.stream = substream->stream;
+       skl_tplg_be_update_params(dai, &p_params);
+
+       return 0;
+}
+
+static int skl_pcm_trigger(struct snd_pcm_substream *substream, int cmd,
+               struct snd_soc_dai *dai)
+{
+       struct skl *skl = get_skl_ctx(dai->dev);
+       struct skl_sst *ctx = skl->skl_sst;
+       struct skl_module_cfg *mconfig;
+
+       mconfig = skl_tplg_fe_get_cpr_module(dai, substream->stream);
+       if (!mconfig)
+               return -EIO;
+
+       switch (cmd) {
+       case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+       case SNDRV_PCM_TRIGGER_RESUME:
+               return skl_run_pipe(ctx, mconfig->pipe);
+
+       case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+       case SNDRV_PCM_TRIGGER_SUSPEND:
+               return skl_stop_pipe(ctx, mconfig->pipe);
+
+       default:
+               return 0;
+       }
+}
+
 static int skl_link_hw_params(struct snd_pcm_substream *substream,
                                struct snd_pcm_hw_params *params,
                                struct snd_soc_dai *dai)
@@ -277,9 +330,8 @@ static int skl_link_hw_params(struct snd_pcm_substream *substream,
        struct snd_soc_pcm_runtime *rtd = snd_pcm_substream_chip(substream);
        struct skl_dma_params *dma_params;
        struct snd_soc_dai *codec_dai = rtd->codec_dai;
-       int dma_id;
+       struct skl_pipe_params p_params = {0};
 
-       pr_debug("%s\n", __func__);
        link_dev = snd_hdac_ext_stream_assign(ebus, substream,
                                        HDAC_EXT_STREAM_TYPE_LINK);
        if (!link_dev)
@@ -293,7 +345,14 @@ static int skl_link_hw_params(struct snd_pcm_substream *substream,
        if (dma_params)
                dma_params->stream_tag =  hdac_stream(link_dev)->stream_tag;
        snd_soc_dai_set_dma_data(codec_dai, substream, (void *)dma_params);
-       dma_id = hdac_stream(link_dev)->stream_tag - 1;
+
+       p_params.s_fmt = snd_pcm_format_width(params_format(params));
+       p_params.ch = params_channels(params);
+       p_params.s_freq = params_rate(params);
+       p_params.stream = substream->stream;
+       p_params.link_dma_id = hdac_stream(link_dev)->stream_tag - 1;
+
+       skl_tplg_be_update_params(dai, &p_params);
 
        return 0;
 }
@@ -308,27 +367,12 @@ static int skl_link_pcm_prepare(struct snd_pcm_substream *substream,
        unsigned int format_val = 0;
        struct skl_dma_params *dma_params;
        struct snd_soc_dai *codec_dai = rtd->codec_dai;
-       struct snd_pcm_hw_params *params;
-       struct snd_interval *channels, *rate;
        struct hdac_ext_link *link;
 
-       dev_dbg(dai->dev, "%s: %s\n", __func__, dai->name);
        if (link_dev->link_prepared) {
                dev_dbg(dai->dev, "already stream is prepared - returning\n");
                return 0;
        }
-       params  = devm_kzalloc(dai->dev, sizeof(*params), GFP_KERNEL);
-       if (params == NULL)
-               return -ENOMEM;
-
-       channels = hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS);
-       channels->min = channels->max = substream->runtime->channels;
-       rate = hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE);
-       rate->min = rate->max = substream->runtime->rate;
-       snd_mask_set(&params->masks[SNDRV_PCM_HW_PARAM_FORMAT -
-                                       SNDRV_PCM_HW_PARAM_FIRST_MASK],
-                                       substream->runtime->format);
-
 
        dma_params  = (struct skl_dma_params *)
                        snd_soc_dai_get_dma_data(codec_dai, substream);
@@ -399,13 +443,13 @@ static int skl_link_hw_free(struct snd_pcm_substream *substream,
        return 0;
 }
 
-static int skl_hda_be_startup(struct snd_pcm_substream *substream,
+static int skl_be_startup(struct snd_pcm_substream *substream,
                struct snd_soc_dai *dai)
 {
        return pm_runtime_get_sync(dai->dev);
 }
 
-static void skl_hda_be_shutdown(struct snd_pcm_substream *substream,
+static void skl_be_shutdown(struct snd_pcm_substream *substream,
                struct snd_soc_dai *dai)
 {
        pm_runtime_mark_last_busy(dai->dev);
@@ -418,20 +462,28 @@ static struct snd_soc_dai_ops skl_pcm_dai_ops = {
        .prepare = skl_pcm_prepare,
        .hw_params = skl_pcm_hw_params,
        .hw_free = skl_pcm_hw_free,
+       .trigger = skl_pcm_trigger,
 };
 
 static struct snd_soc_dai_ops skl_dmic_dai_ops = {
-       .startup = skl_hda_be_startup,
-       .shutdown = skl_hda_be_shutdown,
+       .startup = skl_be_startup,
+       .hw_params = skl_be_hw_params,
+       .shutdown = skl_be_shutdown,
+};
+
+static struct snd_soc_dai_ops skl_be_ssp_dai_ops = {
+       .startup = skl_be_startup,
+       .hw_params = skl_be_hw_params,
+       .shutdown = skl_be_shutdown,
 };
 
 static struct snd_soc_dai_ops skl_link_dai_ops = {
-       .startup = skl_hda_be_startup,
+       .startup = skl_be_startup,
        .prepare = skl_link_pcm_prepare,
        .hw_params = skl_link_hw_params,
        .hw_free = skl_link_hw_free,
        .trigger = skl_link_pcm_trigger,
-       .shutdown = skl_hda_be_shutdown,
+       .shutdown = skl_be_shutdown,
 };
 
 static struct snd_soc_dai_driver skl_platform_dai[] = {
@@ -487,6 +539,24 @@ static struct snd_soc_dai_driver skl_platform_dai[] = {
        },
 },
 /* BE CPU  Dais */
+{
+       .name = "SSP0 Pin",
+       .ops = &skl_be_ssp_dai_ops,
+       .playback = {
+               .stream_name = "ssp0 Tx",
+               .channels_min = HDA_STEREO,
+               .channels_max = HDA_STEREO,
+               .rates = SNDRV_PCM_RATE_48000,
+               .formats = SNDRV_PCM_FMTBIT_S16_LE,
+       },
+       .capture = {
+               .stream_name = "ssp0 Rx",
+               .channels_min = HDA_STEREO,
+               .channels_max = HDA_STEREO,
+               .rates = SNDRV_PCM_RATE_48000,
+               .formats = SNDRV_PCM_FMTBIT_S16_LE,
+       },
+},
 {
        .name = "iDisp Pin",
        .ops = &skl_link_dai_ops,
@@ -544,7 +614,7 @@ static int skl_platform_open(struct snd_pcm_substream *substream)
        return 0;
 }
 
-static int skl_pcm_trigger(struct snd_pcm_substream *substream,
+static int skl_coupled_trigger(struct snd_pcm_substream *substream,
                                        int cmd)
 {
        struct hdac_ext_bus *ebus = get_bus_ctx(substream);
@@ -618,7 +688,7 @@ static int skl_pcm_trigger(struct snd_pcm_substream *substream,
        return 0;
 }
 
-static int skl_dsp_trigger(struct snd_pcm_substream *substream,
+static int skl_decoupled_trigger(struct snd_pcm_substream *substream,
                int cmd)
 {
        struct hdac_ext_bus *ebus = get_bus_ctx(substream);
@@ -675,9 +745,9 @@ static int skl_platform_pcm_trigger(struct snd_pcm_substream *substream,
        struct hdac_ext_bus *ebus = get_bus_ctx(substream);
 
        if (ebus->ppcap)
-               return skl_dsp_trigger(substream, cmd);
+               return skl_decoupled_trigger(substream, cmd);
        else
-               return skl_pcm_trigger(substream, cmd);
+               return skl_coupled_trigger(substream, cmd);
 }
 
 /* calculate runtime delay from LPIB */
@@ -844,7 +914,17 @@ static int skl_pcm_new(struct snd_soc_pcm_runtime *rtd)
        return retval;
 }
 
+static int skl_platform_soc_probe(struct snd_soc_platform *platform)
+{
+       struct hdac_ext_bus *ebus = dev_get_drvdata(platform->dev);
+
+       if (ebus->ppcap)
+               return skl_tplg_init(platform, ebus);
+
+       return 0;
+}
 static struct snd_soc_platform_driver skl_platform_drv  = {
+       .probe          = skl_platform_soc_probe,
        .ops            = &skl_platform_ops,
        .pcm_new        = skl_pcm_new,
        .pcm_free       = skl_pcm_free,
@@ -857,6 +937,11 @@ static const struct snd_soc_component_driver skl_component = {
 int skl_platform_register(struct device *dev)
 {
        int ret;
+       struct hdac_ext_bus *ebus = dev_get_drvdata(dev);
+       struct skl *skl = ebus_to_skl(ebus);
+
+       INIT_LIST_HEAD(&skl->ppl_list);
+       INIT_LIST_HEAD(&skl->dapm_path_list);
 
        ret = snd_soc_register_platform(dev, &skl_platform_drv);
        if (ret) {
index 94875b0..1bfb7f6 100644 (file)
@@ -175,7 +175,7 @@ static int skl_dsp_core_power_down(struct sst_dsp  *ctx)
        /* poll with timeout to check if operation successful */
        return sst_dsp_register_poll(ctx,
                        SKL_ADSP_REG_ADSPCS,
-                       SKL_ADSPCS_SPA_MASK,
+                       SKL_ADSPCS_CPA_MASK,
                        0,
                        SKL_DSP_PD_TO,
                        "Power down");
@@ -262,6 +262,11 @@ irqreturn_t skl_dsp_sst_interrupt(int irq, void *dev_id)
        val = sst_dsp_shim_read_unlocked(ctx, SKL_ADSP_REG_ADSPIS);
        ctx->intr_status = val;
 
+       if (val == 0xffffffff) {
+               spin_unlock(&ctx->spinlock);
+               return IRQ_NONE;
+       }
+
        if (val & SKL_ADSPIS_IPC) {
                skl_ipc_int_disable(ctx);
                result = IRQ_WAKE_THREAD;
index 937a0a3..3345ea0 100644 (file)
@@ -464,6 +464,18 @@ void skl_ipc_op_int_enable(struct sst_dsp *ctx)
                SKL_ADSP_REG_HIPCCTL_BUSY, SKL_ADSP_REG_HIPCCTL_BUSY);
 }
 
+void skl_ipc_op_int_disable(struct sst_dsp *ctx)
+{
+       /* disable IPC DONE interrupt */
+       sst_dsp_shim_update_bits_unlocked(ctx, SKL_ADSP_REG_HIPCCTL,
+                                       SKL_ADSP_REG_HIPCCTL_DONE, 0);
+
+       /* Disable IPC BUSY interrupt */
+       sst_dsp_shim_update_bits_unlocked(ctx, SKL_ADSP_REG_HIPCCTL,
+                                       SKL_ADSP_REG_HIPCCTL_BUSY, 0);
+
+}
+
 bool skl_ipc_int_status(struct sst_dsp *ctx)
 {
        return sst_dsp_shim_read_unlocked(ctx,
index 9f5f672..f1a154e 100644 (file)
@@ -116,6 +116,7 @@ int skl_ipc_set_large_config(struct sst_generic_ipc *ipc,
 
 void skl_ipc_int_enable(struct sst_dsp *dsp);
 void skl_ipc_op_int_enable(struct sst_dsp *ctx);
+void skl_ipc_op_int_disable(struct sst_dsp *ctx);
 void skl_ipc_int_disable(struct sst_dsp *dsp);
 
 bool skl_ipc_int_status(struct sst_dsp *dsp);
index c18ea51..3b83dc9 100644 (file)
@@ -70,15 +70,31 @@ static int skl_transfer_firmware(struct sst_dsp *ctx,
 static int skl_load_base_firmware(struct sst_dsp *ctx)
 {
        int ret = 0, i;
-       const struct firmware *fw = NULL;
        struct skl_sst *skl = ctx->thread_context;
        u32 reg;
 
-       ret = request_firmware(&fw, "dsp_fw_release.bin", ctx->dev);
+       skl->boot_complete = false;
+       init_waitqueue_head(&skl->boot_wait);
+
+       if (ctx->fw == NULL) {
+               ret = request_firmware(&ctx->fw, "dsp_fw_release.bin", ctx->dev);
+               if (ret < 0) {
+                       dev_err(ctx->dev, "Request firmware failed %d\n", ret);
+                       skl_dsp_disable_core(ctx);
+                       return -EIO;
+               }
+       }
+
+       ret = skl_dsp_boot(ctx);
        if (ret < 0) {
-               dev_err(ctx->dev, "Request firmware failed %d\n", ret);
-               skl_dsp_disable_core(ctx);
-               return -EIO;
+               dev_err(ctx->dev, "Boot dsp core failed ret: %d", ret);
+               goto skl_load_base_firmware_failed;
+       }
+
+       ret = skl_cldma_prepare(ctx);
+       if (ret < 0) {
+               dev_err(ctx->dev, "CL dma prepare failed : %d", ret);
+               goto skl_load_base_firmware_failed;
        }
 
        /* enable Interrupt */
@@ -102,7 +118,7 @@ static int skl_load_base_firmware(struct sst_dsp *ctx)
                goto skl_load_base_firmware_failed;
        }
 
-       ret = skl_transfer_firmware(ctx, fw->data, fw->size);
+       ret = skl_transfer_firmware(ctx, ctx->fw->data, ctx->fw->size);
        if (ret < 0) {
                dev_err(ctx->dev, "Transfer firmware failed%d\n", ret);
                goto skl_load_base_firmware_failed;
@@ -118,13 +134,12 @@ static int skl_load_base_firmware(struct sst_dsp *ctx)
                dev_dbg(ctx->dev, "Download firmware successful%d\n", ret);
                skl_dsp_set_state_locked(ctx, SKL_DSP_RUNNING);
        }
-       release_firmware(fw);
-
        return 0;
 
 skl_load_base_firmware_failed:
        skl_dsp_disable_core(ctx);
-       release_firmware(fw);
+       release_firmware(ctx->fw);
+       ctx->fw = NULL;
        return ret;
 }
 
@@ -172,6 +187,12 @@ static int skl_set_dsp_D3(struct sst_dsp *ctx)
        }
        skl_dsp_set_state_locked(ctx, SKL_DSP_RESET);
 
+       /* disable Interrupt */
+       ctx->cl_dev.ops.cl_cleanup_controller(ctx);
+       skl_cldma_int_disable(ctx);
+       skl_ipc_op_int_disable(ctx);
+       skl_ipc_int_disable(ctx);
+
        return ret;
 }
 
@@ -235,22 +256,6 @@ int skl_sst_dsp_init(struct device *dev, void __iomem *mmio_base, int irq,
        if (ret)
                return ret;
 
-       skl->boot_complete = false;
-       init_waitqueue_head(&skl->boot_wait);
-
-       ret = skl_dsp_boot(sst);
-       if (ret < 0) {
-               dev_err(skl->dev, "Boot dsp core failed ret: %d", ret);
-               goto free_ipc;
-       }
-
-       ret = skl_cldma_prepare(sst);
-       if (ret < 0) {
-               dev_err(dev, "CL dma prepare failed : %d", ret);
-               goto free_ipc;
-       }
-
-
        ret = sst->fw_ops.load_fw(sst);
        if (ret < 0) {
                dev_err(dev, "Load base fw failed : %d", ret);
@@ -262,7 +267,6 @@ int skl_sst_dsp_init(struct device *dev, void __iomem *mmio_base, int irq,
 
        return 0;
 
-free_ipc:
        skl_ipc_free(&skl->ipc);
        return ret;
 }
diff --git a/sound/soc/intel/skylake/skl-topology.c b/sound/soc/intel/skylake/skl-topology.c
new file mode 100644 (file)
index 0000000..a7854c8
--- /dev/null
@@ -0,0 +1,1252 @@
+/*
+ *  skl-topology.c - Implements Platform component ALSA controls/widget
+ *  handlers.
+ *
+ *  Copyright (C) 2014-2015 Intel Corp
+ *  Author: Jeeja KP <jeeja.kp@intel.com>
+ *  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as version 2, as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ */
+
+#include <linux/slab.h>
+#include <linux/types.h>
+#include <linux/firmware.h>
+#include <sound/soc.h>
+#include <sound/soc-topology.h>
+#include "skl-sst-dsp.h"
+#include "skl-sst-ipc.h"
+#include "skl-topology.h"
+#include "skl.h"
+#include "skl-tplg-interface.h"
+
+#define SKL_CH_FIXUP_MASK              (1 << 0)
+#define SKL_RATE_FIXUP_MASK            (1 << 1)
+#define SKL_FMT_FIXUP_MASK             (1 << 2)
+
+/*
+ * SKL DSP driver modelling uses only few DAPM widgets so for rest we will
+ * ignore. This helpers checks if the SKL driver handles this widget type
+ */
+static int is_skl_dsp_widget_type(struct snd_soc_dapm_widget *w)
+{
+       switch (w->id) {
+       case snd_soc_dapm_dai_link:
+       case snd_soc_dapm_dai_in:
+       case snd_soc_dapm_aif_in:
+       case snd_soc_dapm_aif_out:
+       case snd_soc_dapm_dai_out:
+       case snd_soc_dapm_switch:
+               return false;
+       default:
+               return true;
+       }
+}
+
+/*
+ * Each pipelines needs memory to be allocated. Check if we have free memory
+ * from available pool. Then only add this to pool
+ * This is freed when pipe is deleted
+ * Note: DSP does actual memory management we only keep track for complete
+ * pool
+ */
+static bool skl_tplg_alloc_pipe_mem(struct skl *skl,
+                               struct skl_module_cfg *mconfig)
+{
+       struct skl_sst *ctx = skl->skl_sst;
+
+       if (skl->resource.mem + mconfig->pipe->memory_pages >
+                               skl->resource.max_mem) {
+               dev_err(ctx->dev,
+                               "%s: module_id %d instance %d\n", __func__,
+                               mconfig->id.module_id,
+                               mconfig->id.instance_id);
+               dev_err(ctx->dev,
+                               "exceeds ppl memory available %d mem %d\n",
+                               skl->resource.max_mem, skl->resource.mem);
+               return false;
+       }
+
+       skl->resource.mem += mconfig->pipe->memory_pages;
+       return true;
+}
+
+/*
+ * Pipeline needs needs DSP CPU resources for computation, this is
+ * quantified in MCPS (Million Clocks Per Second) required for module/pipe
+ *
+ * Each pipelines needs mcps to be allocated. Check if we have mcps for this
+ * pipe. This adds the mcps to driver counter
+ * This is removed on pipeline delete
+ */
+static bool skl_tplg_alloc_pipe_mcps(struct skl *skl,
+                               struct skl_module_cfg *mconfig)
+{
+       struct skl_sst *ctx = skl->skl_sst;
+
+       if (skl->resource.mcps + mconfig->mcps > skl->resource.max_mcps) {
+               dev_err(ctx->dev,
+                       "%s: module_id %d instance %d\n", __func__,
+                       mconfig->id.module_id, mconfig->id.instance_id);
+               dev_err(ctx->dev,
+                       "exceeds ppl memory available %d > mem %d\n",
+                       skl->resource.max_mcps, skl->resource.mcps);
+               return false;
+       }
+
+       skl->resource.mcps += mconfig->mcps;
+       return true;
+}
+
+/*
+ * Free the mcps when tearing down
+ */
+static void
+skl_tplg_free_pipe_mcps(struct skl *skl, struct skl_module_cfg *mconfig)
+{
+       skl->resource.mcps -= mconfig->mcps;
+}
+
+/*
+ * Free the memory when tearing down
+ */
+static void
+skl_tplg_free_pipe_mem(struct skl *skl, struct skl_module_cfg *mconfig)
+{
+       skl->resource.mem -= mconfig->pipe->memory_pages;
+}
+
+
+static void skl_dump_mconfig(struct skl_sst *ctx,
+                                       struct skl_module_cfg *mcfg)
+{
+       dev_dbg(ctx->dev, "Dumping config\n");
+       dev_dbg(ctx->dev, "Input Format:\n");
+       dev_dbg(ctx->dev, "channels = %d\n", mcfg->in_fmt.channels);
+       dev_dbg(ctx->dev, "s_freq = %d\n", mcfg->in_fmt.s_freq);
+       dev_dbg(ctx->dev, "ch_cfg = %d\n", mcfg->in_fmt.ch_cfg);
+       dev_dbg(ctx->dev, "valid bit depth = %d\n",
+                       mcfg->in_fmt.valid_bit_depth);
+       dev_dbg(ctx->dev, "Output Format:\n");
+       dev_dbg(ctx->dev, "channels = %d\n", mcfg->out_fmt.channels);
+       dev_dbg(ctx->dev, "s_freq = %d\n", mcfg->out_fmt.s_freq);
+       dev_dbg(ctx->dev, "valid bit depth = %d\n",
+                       mcfg->out_fmt.valid_bit_depth);
+       dev_dbg(ctx->dev, "ch_cfg = %d\n", mcfg->out_fmt.ch_cfg);
+}
+
+static void skl_tplg_update_params(struct skl_module_fmt *fmt,
+                       struct skl_pipe_params *params, int fixup)
+{
+       if (fixup & SKL_RATE_FIXUP_MASK)
+               fmt->s_freq = params->s_freq;
+       if (fixup & SKL_CH_FIXUP_MASK)
+               fmt->channels = params->ch;
+       if (fixup & SKL_FMT_FIXUP_MASK)
+               fmt->valid_bit_depth = params->s_fmt;
+}
+
+/*
+ * A pipeline may have modules which impact the pcm parameters, like SRC,
+ * channel converter, format converter.
+ * We need to calculate the output params by applying the 'fixup'
+ * Topology will tell driver which type of fixup is to be applied by
+ * supplying the fixup mask, so based on that we calculate the output
+ *
+ * Now In FE the pcm hw_params is source/target format. Same is applicable
+ * for BE with its hw_params invoked.
+ * here based on FE, BE pipeline and direction we calculate the input and
+ * outfix and then apply that for a module
+ */
+static void skl_tplg_update_params_fixup(struct skl_module_cfg *m_cfg,
+               struct skl_pipe_params *params, bool is_fe)
+{
+       int in_fixup, out_fixup;
+       struct skl_module_fmt *in_fmt, *out_fmt;
+
+       in_fmt = &m_cfg->in_fmt;
+       out_fmt = &m_cfg->out_fmt;
+
+       if (params->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+               if (is_fe) {
+                       in_fixup = m_cfg->params_fixup;
+                       out_fixup = (~m_cfg->converter) &
+                                       m_cfg->params_fixup;
+               } else {
+                       out_fixup = m_cfg->params_fixup;
+                       in_fixup = (~m_cfg->converter) &
+                                       m_cfg->params_fixup;
+               }
+       } else {
+               if (is_fe) {
+                       out_fixup = m_cfg->params_fixup;
+                       in_fixup = (~m_cfg->converter) &
+                                       m_cfg->params_fixup;
+               } else {
+                       in_fixup = m_cfg->params_fixup;
+                       out_fixup = (~m_cfg->converter) &
+                                       m_cfg->params_fixup;
+               }
+       }
+
+       skl_tplg_update_params(in_fmt, params, in_fixup);
+       skl_tplg_update_params(out_fmt, params, out_fixup);
+}
+
+/*
+ * A module needs input and output buffers, which are dependent upon pcm
+ * params, so once we have calculate params, we need buffer calculation as
+ * well.
+ */
+static void skl_tplg_update_buffer_size(struct skl_sst *ctx,
+                               struct skl_module_cfg *mcfg)
+{
+       int multiplier = 1;
+
+       if (mcfg->m_type == SKL_MODULE_TYPE_SRCINT)
+               multiplier = 5;
+
+       mcfg->ibs = (mcfg->in_fmt.s_freq / 1000) *
+                               (mcfg->in_fmt.channels) *
+                               (mcfg->in_fmt.bit_depth >> 3) *
+                               multiplier;
+
+       mcfg->obs = (mcfg->out_fmt.s_freq / 1000) *
+                               (mcfg->out_fmt.channels) *
+                               (mcfg->out_fmt.bit_depth >> 3) *
+                               multiplier;
+}
+
+static void skl_tplg_update_module_params(struct snd_soc_dapm_widget *w,
+                                                       struct skl_sst *ctx)
+{
+       struct skl_module_cfg *m_cfg = w->priv;
+       struct skl_pipe_params *params = m_cfg->pipe->p_params;
+       int p_conn_type = m_cfg->pipe->conn_type;
+       bool is_fe;
+
+       if (!m_cfg->params_fixup)
+               return;
+
+       dev_dbg(ctx->dev, "Mconfig for widget=%s BEFORE updation\n",
+                               w->name);
+
+       skl_dump_mconfig(ctx, m_cfg);
+
+       if (p_conn_type == SKL_PIPE_CONN_TYPE_FE)
+               is_fe = true;
+       else
+               is_fe = false;
+
+       skl_tplg_update_params_fixup(m_cfg, params, is_fe);
+       skl_tplg_update_buffer_size(ctx, m_cfg);
+
+       dev_dbg(ctx->dev, "Mconfig for widget=%s AFTER updation\n",
+                               w->name);
+
+       skl_dump_mconfig(ctx, m_cfg);
+}
+
+/*
+ * A pipe can have multiple modules, each of them will be a DAPM widget as
+ * well. While managing a pipeline we need to get the list of all the
+ * widgets in a pipelines, so this helper - skl_tplg_get_pipe_widget() helps
+ * to get the SKL type widgets in that pipeline
+ */
+static int skl_tplg_alloc_pipe_widget(struct device *dev,
+       struct snd_soc_dapm_widget *w, struct skl_pipe *pipe)
+{
+       struct skl_module_cfg *src_module = NULL;
+       struct snd_soc_dapm_path *p = NULL;
+       struct skl_pipe_module *p_module = NULL;
+
+       p_module = devm_kzalloc(dev, sizeof(*p_module), GFP_KERNEL);
+       if (!p_module)
+               return -ENOMEM;
+
+       p_module->w = w;
+       list_add_tail(&p_module->node, &pipe->w_list);
+
+       snd_soc_dapm_widget_for_each_sink_path(w, p) {
+               if ((p->sink->priv == NULL)
+                               && (!is_skl_dsp_widget_type(w)))
+                       continue;
+
+               if ((p->sink->priv != NULL) && p->connect
+                               && is_skl_dsp_widget_type(p->sink)) {
+
+                       src_module = p->sink->priv;
+                       if (pipe->ppl_id == src_module->pipe->ppl_id)
+                               skl_tplg_alloc_pipe_widget(dev,
+                                                       p->sink, pipe);
+               }
+       }
+       return 0;
+}
+
+/*
+ * Inside a pipe instance, we can have various modules. These modules need
+ * to instantiated in DSP by invoking INIT_MODULE IPC, which is achieved by
+ * skl_init_module() routine, so invoke that for all modules in a pipeline
+ */
+static int
+skl_tplg_init_pipe_modules(struct skl *skl, struct skl_pipe *pipe)
+{
+       struct skl_pipe_module *w_module;
+       struct snd_soc_dapm_widget *w;
+       struct skl_module_cfg *mconfig;
+       struct skl_sst *ctx = skl->skl_sst;
+       int ret = 0;
+
+       list_for_each_entry(w_module, &pipe->w_list, node) {
+               w = w_module->w;
+               mconfig = w->priv;
+
+               /* check resource available */
+               if (!skl_tplg_alloc_pipe_mcps(skl, mconfig))
+                       return -ENOMEM;
+
+               /*
+                * apply fix/conversion to module params based on
+                * FE/BE params
+                */
+               skl_tplg_update_module_params(w, ctx);
+               ret = skl_init_module(ctx, mconfig, NULL);
+               if (ret < 0)
+                       return ret;
+       }
+
+       return 0;
+}
+
+/*
+ * Mixer module represents a pipeline. So in the Pre-PMU event of mixer we
+ * need create the pipeline. So we do following:
+ *   - check the resources
+ *   - Create the pipeline
+ *   - Initialize the modules in pipeline
+ *   - finally bind all modules together
+ */
+static int skl_tplg_mixer_dapm_pre_pmu_event(struct snd_soc_dapm_widget *w,
+                                                       struct skl *skl)
+{
+       int ret;
+       struct skl_module_cfg *mconfig = w->priv;
+       struct skl_pipe_module *w_module;
+       struct skl_pipe *s_pipe = mconfig->pipe;
+       struct skl_module_cfg *src_module = NULL, *dst_module;
+       struct skl_sst *ctx = skl->skl_sst;
+
+       /* check resource available */
+       if (!skl_tplg_alloc_pipe_mcps(skl, mconfig))
+               return -EBUSY;
+
+       if (!skl_tplg_alloc_pipe_mem(skl, mconfig))
+               return -ENOMEM;
+
+       /*
+        * Create a list of modules for pipe.
+        * This list contains modules from source to sink
+        */
+       ret = skl_create_pipeline(ctx, mconfig->pipe);
+       if (ret < 0)
+               return ret;
+
+       /*
+        * we create a w_list of all widgets in that pipe. This list is not
+        * freed on PMD event as widgets within a pipe are static. This
+        * saves us cycles to get widgets in pipe every time.
+        *
+        * So if we have already initialized all the widgets of a pipeline
+        * we skip, so check for list_empty and create the list if empty
+        */
+       if (list_empty(&s_pipe->w_list)) {
+               ret = skl_tplg_alloc_pipe_widget(ctx->dev, w, s_pipe);
+               if (ret < 0)
+                       return ret;
+       }
+
+       /* Init all pipe modules from source to sink */
+       ret = skl_tplg_init_pipe_modules(skl, s_pipe);
+       if (ret < 0)
+               return ret;
+
+       /* Bind modules from source to sink */
+       list_for_each_entry(w_module, &s_pipe->w_list, node) {
+               dst_module = w_module->w->priv;
+
+               if (src_module == NULL) {
+                       src_module = dst_module;
+                       continue;
+               }
+
+               ret = skl_bind_modules(ctx, src_module, dst_module);
+               if (ret < 0)
+                       return ret;
+
+               src_module = dst_module;
+       }
+
+       return 0;
+}
+
+/*
+ * A PGA represents a module in a pipeline. So in the Pre-PMU event of PGA
+ * we need to do following:
+ *   - Bind to sink pipeline
+ *      Since the sink pipes can be running and we don't get mixer event on
+ *      connect for already running mixer, we need to find the sink pipes
+ *      here and bind to them. This way dynamic connect works.
+ *   - Start sink pipeline, if not running
+ *   - Then run current pipe
+ */
+static int skl_tplg_pga_dapm_pre_pmu_event(struct snd_soc_dapm_widget *w,
+                                                       struct skl *skl)
+{
+       struct snd_soc_dapm_path *p;
+       struct skl_dapm_path_list *path_list;
+       struct snd_soc_dapm_widget *source, *sink;
+       struct skl_module_cfg *src_mconfig, *sink_mconfig;
+       struct skl_sst *ctx = skl->skl_sst;
+       int ret = 0;
+
+       source = w;
+       src_mconfig = source->priv;
+
+       /*
+        * find which sink it is connected to, bind with the sink,
+        * if sink is not started, start sink pipe first, then start
+        * this pipe
+        */
+       snd_soc_dapm_widget_for_each_source_path(w, p) {
+               if (!p->connect)
+                       continue;
+
+               dev_dbg(ctx->dev, "%s: src widget=%s\n", __func__, w->name);
+               dev_dbg(ctx->dev, "%s: sink widget=%s\n", __func__, p->sink->name);
+
+               /*
+                * here we will check widgets in sink pipelines, so that
+                * can be any widgets type and we are only interested if
+                * they are ones used for SKL so check that first
+                */
+               if ((p->sink->priv != NULL) &&
+                                       is_skl_dsp_widget_type(p->sink)) {
+
+                       sink = p->sink;
+                       src_mconfig = source->priv;
+                       sink_mconfig = sink->priv;
+
+                       /* Bind source to sink, mixin is always source */
+                       ret = skl_bind_modules(ctx, src_mconfig, sink_mconfig);
+                       if (ret)
+                               return ret;
+
+                       /* Start sinks pipe first */
+                       if (sink_mconfig->pipe->state != SKL_PIPE_STARTED) {
+                               ret = skl_run_pipe(ctx, sink_mconfig->pipe);
+                               if (ret)
+                                       return ret;
+                       }
+
+                       path_list = kzalloc(
+                                       sizeof(struct skl_dapm_path_list),
+                                       GFP_KERNEL);
+                       if (path_list == NULL)
+                               return -ENOMEM;
+
+                       /* Add connected path to one global list */
+                       path_list->dapm_path = p;
+                       list_add_tail(&path_list->node, &skl->dapm_path_list);
+                       break;
+               }
+       }
+
+       /* Start source pipe last after starting all sinks */
+       ret = skl_run_pipe(ctx, src_mconfig->pipe);
+       if (ret)
+               return ret;
+
+       return 0;
+}
+
+/*
+ * in the Post-PMU event of mixer we need to do following:
+ *   - Check if this pipe is running
+ *   - if not, then
+ *     - bind this pipeline to its source pipeline
+ *       if source pipe is already running, this means it is a dynamic
+ *       connection and we need to bind only to that pipe
+ *     - start this pipeline
+ */
+static int skl_tplg_mixer_dapm_post_pmu_event(struct snd_soc_dapm_widget *w,
+                                                       struct skl *skl)
+{
+       int ret = 0;
+       struct snd_soc_dapm_path *p;
+       struct snd_soc_dapm_widget *source, *sink;
+       struct skl_module_cfg *src_mconfig, *sink_mconfig;
+       struct skl_sst *ctx = skl->skl_sst;
+       int src_pipe_started = 0;
+
+       sink = w;
+       sink_mconfig = sink->priv;
+
+       /*
+        * If source pipe is already started, that means source is driving
+        * one more sink before this sink got connected, Since source is
+        * started, bind this sink to source and start this pipe.
+        */
+       snd_soc_dapm_widget_for_each_sink_path(w, p) {
+               if (!p->connect)
+                       continue;
+
+               dev_dbg(ctx->dev, "sink widget=%s\n", w->name);
+               dev_dbg(ctx->dev, "src widget=%s\n", p->source->name);
+
+               /*
+                * here we will check widgets in sink pipelines, so that
+                * can be any widgets type and we are only interested if
+                * they are ones used for SKL so check that first
+                */
+               if ((p->source->priv != NULL) &&
+                                       is_skl_dsp_widget_type(p->source)) {
+                       source = p->source;
+                       src_mconfig = source->priv;
+                       sink_mconfig = sink->priv;
+                       src_pipe_started = 1;
+
+                       /*
+                        * check pipe state, then no need to bind or start
+                        * the pipe
+                        */
+                       if (src_mconfig->pipe->state != SKL_PIPE_STARTED)
+                               src_pipe_started = 0;
+               }
+       }
+
+       if (src_pipe_started) {
+               ret = skl_bind_modules(ctx, src_mconfig, sink_mconfig);
+               if (ret)
+                       return ret;
+
+               ret = skl_run_pipe(ctx, sink_mconfig->pipe);
+       }
+
+       return ret;
+}
+
+/*
+ * in the Pre-PMD event of mixer we need to do following:
+ *   - Stop the pipe
+ *   - find the source connections and remove that from dapm_path_list
+ *   - unbind with source pipelines if still connected
+ */
+static int skl_tplg_mixer_dapm_pre_pmd_event(struct snd_soc_dapm_widget *w,
+                                                       struct skl *skl)
+{
+       struct snd_soc_dapm_widget *source, *sink;
+       struct skl_module_cfg *src_mconfig, *sink_mconfig;
+       int ret = 0, path_found = 0;
+       struct skl_dapm_path_list *path_list, *tmp_list;
+       struct skl_sst *ctx = skl->skl_sst;
+
+       sink = w;
+       sink_mconfig = sink->priv;
+
+       /* Stop the pipe */
+       ret = skl_stop_pipe(ctx, sink_mconfig->pipe);
+       if (ret)
+               return ret;
+
+       /*
+        * This list, dapm_path_list handling here does not need any locks
+        * as we are under dapm lock while handling widget events.
+        * List can be manipulated safely only under dapm widgets handler
+        * routines
+        */
+       list_for_each_entry_safe(path_list, tmp_list,
+                               &skl->dapm_path_list, node) {
+               if (path_list->dapm_path->sink == sink) {
+                       dev_dbg(ctx->dev, "Path found = %s\n",
+                                       path_list->dapm_path->name);
+                       source = path_list->dapm_path->source;
+                       src_mconfig = source->priv;
+                       path_found = 1;
+
+                       list_del(&path_list->node);
+                       kfree(path_list);
+                       break;
+               }
+       }
+
+       /*
+        * If path_found == 1, that means pmd for source pipe has
+        * not occurred, source is connected to some other sink.
+        * so its responsibility of sink to unbind itself from source.
+        */
+       if (path_found) {
+               ret = skl_stop_pipe(ctx, src_mconfig->pipe);
+               if (ret < 0)
+                       return ret;
+
+               ret = skl_unbind_modules(ctx, src_mconfig, sink_mconfig);
+       }
+
+       return ret;
+}
+
+/*
+ * in the Post-PMD event of mixer we need to do following:
+ *   - Free the mcps used
+ *   - Free the mem used
+ *   - Unbind the modules within the pipeline
+ *   - Delete the pipeline (modules are not required to be explicitly
+ *     deleted, pipeline delete is enough here
+ */
+static int skl_tplg_mixer_dapm_post_pmd_event(struct snd_soc_dapm_widget *w,
+                                                       struct skl *skl)
+{
+       struct skl_module_cfg *mconfig = w->priv;
+       struct skl_pipe_module *w_module;
+       struct skl_module_cfg *src_module = NULL, *dst_module;
+       struct skl_sst *ctx = skl->skl_sst;
+       struct skl_pipe *s_pipe = mconfig->pipe;
+       int ret = 0;
+
+       skl_tplg_free_pipe_mcps(skl, mconfig);
+
+       list_for_each_entry(w_module, &s_pipe->w_list, node) {
+               dst_module = w_module->w->priv;
+
+               if (src_module == NULL) {
+                       src_module = dst_module;
+                       continue;
+               }
+
+               ret = skl_unbind_modules(ctx, src_module, dst_module);
+               if (ret < 0)
+                       return ret;
+
+               src_module = dst_module;
+       }
+
+       ret = skl_delete_pipe(ctx, mconfig->pipe);
+       skl_tplg_free_pipe_mem(skl, mconfig);
+
+       return ret;
+}
+
+/*
+ * in the Post-PMD event of PGA we need to do following:
+ *   - Free the mcps used
+ *   - Stop the pipeline
+ *   - In source pipe is connected, unbind with source pipelines
+ */
+static int skl_tplg_pga_dapm_post_pmd_event(struct snd_soc_dapm_widget *w,
+                                                               struct skl *skl)
+{
+       struct snd_soc_dapm_widget *source, *sink;
+       struct skl_module_cfg *src_mconfig, *sink_mconfig;
+       int ret = 0, path_found = 0;
+       struct skl_dapm_path_list *path_list, *tmp_path_list;
+       struct skl_sst *ctx = skl->skl_sst;
+
+       source = w;
+       src_mconfig = source->priv;
+
+       skl_tplg_free_pipe_mcps(skl, src_mconfig);
+       /* Stop the pipe since this is a mixin module */
+       ret = skl_stop_pipe(ctx, src_mconfig->pipe);
+       if (ret)
+               return ret;
+
+       list_for_each_entry_safe(path_list, tmp_path_list, &skl->dapm_path_list, node) {
+               if (path_list->dapm_path->source == source) {
+                       dev_dbg(ctx->dev, "Path found = %s\n",
+                                       path_list->dapm_path->name);
+                       sink = path_list->dapm_path->sink;
+                       sink_mconfig = sink->priv;
+                       path_found = 1;
+
+                       list_del(&path_list->node);
+                       kfree(path_list);
+                       break;
+               }
+       }
+
+       /*
+        * This is a connector and if path is found that means
+        * unbind between source and sink has not happened yet
+        */
+       if (path_found) {
+               ret = skl_stop_pipe(ctx, src_mconfig->pipe);
+               if (ret < 0)
+                       return ret;
+
+               ret = skl_unbind_modules(ctx, src_mconfig, sink_mconfig);
+       }
+
+       return ret;
+}
+
+/*
+ * In modelling, we assume there will be ONLY one mixer in a pipeline.  If
+ * mixer is not required then it is treated as static mixer aka vmixer with
+ * a hard path to source module
+ * So we don't need to check if source is started or not as hard path puts
+ * dependency on each other
+ */
+static int skl_tplg_vmixer_event(struct snd_soc_dapm_widget *w,
+                               struct snd_kcontrol *k, int event)
+{
+       struct snd_soc_dapm_context *dapm = w->dapm;
+       struct skl *skl = get_skl_ctx(dapm->dev);
+
+       switch (event) {
+       case SND_SOC_DAPM_PRE_PMU:
+               return skl_tplg_mixer_dapm_pre_pmu_event(w, skl);
+
+       case SND_SOC_DAPM_POST_PMD:
+               return skl_tplg_mixer_dapm_post_pmd_event(w, skl);
+       }
+
+       return 0;
+}
+
+/*
+ * In modelling, we assume there will be ONLY one mixer in a pipeline. If a
+ * second one is required that is created as another pipe entity.
+ * The mixer is responsible for pipe management and represent a pipeline
+ * instance
+ */
+static int skl_tplg_mixer_event(struct snd_soc_dapm_widget *w,
+                               struct snd_kcontrol *k, int event)
+{
+       struct snd_soc_dapm_context *dapm = w->dapm;
+       struct skl *skl = get_skl_ctx(dapm->dev);
+
+       switch (event) {
+       case SND_SOC_DAPM_PRE_PMU:
+               return skl_tplg_mixer_dapm_pre_pmu_event(w, skl);
+
+       case SND_SOC_DAPM_POST_PMU:
+               return skl_tplg_mixer_dapm_post_pmu_event(w, skl);
+
+       case SND_SOC_DAPM_PRE_PMD:
+               return skl_tplg_mixer_dapm_pre_pmd_event(w, skl);
+
+       case SND_SOC_DAPM_POST_PMD:
+               return skl_tplg_mixer_dapm_post_pmd_event(w, skl);
+       }
+
+       return 0;
+}
+
+/*
+ * In modelling, we assumed rest of the modules in pipeline are PGA. But we
+ * are interested in last PGA (leaf PGA) in a pipeline to disconnect with
+ * the sink when it is running (two FE to one BE or one FE to two BE)
+ * scenarios
+ */
+static int skl_tplg_pga_event(struct snd_soc_dapm_widget *w,
+                       struct snd_kcontrol *k, int event)
+
+{
+       struct snd_soc_dapm_context *dapm = w->dapm;
+       struct skl *skl = get_skl_ctx(dapm->dev);
+
+       switch (event) {
+       case SND_SOC_DAPM_PRE_PMU:
+               return skl_tplg_pga_dapm_pre_pmu_event(w, skl);
+
+       case SND_SOC_DAPM_POST_PMD:
+               return skl_tplg_pga_dapm_post_pmd_event(w, skl);
+       }
+
+       return 0;
+}
+
+/*
+ * The FE params are passed by hw_params of the DAI.
+ * On hw_params, the params are stored in Gateway module of the FE and we
+ * need to calculate the format in DSP module configuration, that
+ * conversion is done here
+ */
+int skl_tplg_update_pipe_params(struct device *dev,
+                       struct skl_module_cfg *mconfig,
+                       struct skl_pipe_params *params)
+{
+       struct skl_pipe *pipe = mconfig->pipe;
+       struct skl_module_fmt *format = NULL;
+
+       memcpy(pipe->p_params, params, sizeof(*params));
+
+       if (params->stream == SNDRV_PCM_STREAM_PLAYBACK)
+               format = &mconfig->in_fmt;
+       else
+               format = &mconfig->out_fmt;
+
+       /* set the hw_params */
+       format->s_freq = params->s_freq;
+       format->channels = params->ch;
+       format->valid_bit_depth = skl_get_bit_depth(params->s_fmt);
+
+       /*
+        * 16 bit is 16 bit container whereas 24 bit is in 32 bit
+        * container so update bit depth accordingly
+        */
+       switch (format->valid_bit_depth) {
+       case SKL_DEPTH_16BIT:
+               format->bit_depth = format->valid_bit_depth;
+               break;
+
+       case SKL_DEPTH_24BIT:
+               format->bit_depth = SKL_DEPTH_32BIT;
+               break;
+
+       default:
+               dev_err(dev, "Invalid bit depth %x for pipe\n",
+                               format->valid_bit_depth);
+               return -EINVAL;
+       }
+
+       if (params->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+               mconfig->ibs = (format->s_freq / 1000) *
+                               (format->channels) *
+                               (format->bit_depth >> 3);
+       } else {
+               mconfig->obs = (format->s_freq / 1000) *
+                               (format->channels) *
+                               (format->bit_depth >> 3);
+       }
+
+       return 0;
+}
+
+/*
+ * Query the module config for the FE DAI
+ * This is used to find the hw_params set for that DAI and apply to FE
+ * pipeline
+ */
+struct skl_module_cfg *
+skl_tplg_fe_get_cpr_module(struct snd_soc_dai *dai, int stream)
+{
+       struct snd_soc_dapm_widget *w;
+       struct snd_soc_dapm_path *p = NULL;
+
+       if (stream == SNDRV_PCM_STREAM_PLAYBACK) {
+               w = dai->playback_widget;
+               snd_soc_dapm_widget_for_each_sink_path(w, p) {
+                       if (p->connect && p->sink->power &&
+                                       is_skl_dsp_widget_type(p->sink))
+                               continue;
+
+                       if (p->sink->priv) {
+                               dev_dbg(dai->dev, "set params for %s\n",
+                                               p->sink->name);
+                               return p->sink->priv;
+                       }
+               }
+       } else {
+               w = dai->capture_widget;
+               snd_soc_dapm_widget_for_each_source_path(w, p) {
+                       if (p->connect && p->source->power &&
+                                       is_skl_dsp_widget_type(p->source))
+                               continue;
+
+                       if (p->source->priv) {
+                               dev_dbg(dai->dev, "set params for %s\n",
+                                               p->source->name);
+                               return p->source->priv;
+                       }
+               }
+       }
+
+       return NULL;
+}
+
+static u8 skl_tplg_be_link_type(int dev_type)
+{
+       int ret;
+
+       switch (dev_type) {
+       case SKL_DEVICE_BT:
+               ret = NHLT_LINK_SSP;
+               break;
+
+       case SKL_DEVICE_DMIC:
+               ret = NHLT_LINK_DMIC;
+               break;
+
+       case SKL_DEVICE_I2S:
+               ret = NHLT_LINK_SSP;
+               break;
+
+       case SKL_DEVICE_HDALINK:
+               ret = NHLT_LINK_HDA;
+               break;
+
+       default:
+               ret = NHLT_LINK_INVALID;
+               break;
+       }
+
+       return ret;
+}
+
+/*
+ * Fill the BE gateway parameters
+ * The BE gateway expects a blob of parameters which are kept in the ACPI
+ * NHLT blob, so query the blob for interface type (i2s/pdm) and instance.
+ * The port can have multiple settings so pick based on the PCM
+ * parameters
+ */
+static int skl_tplg_be_fill_pipe_params(struct snd_soc_dai *dai,
+                               struct skl_module_cfg *mconfig,
+                               struct skl_pipe_params *params)
+{
+       struct skl_pipe *pipe = mconfig->pipe;
+       struct nhlt_specific_cfg *cfg;
+       struct skl *skl = get_skl_ctx(dai->dev);
+       int link_type = skl_tplg_be_link_type(mconfig->dev_type);
+
+       memcpy(pipe->p_params, params, sizeof(*params));
+
+       /* update the blob based on virtual bus_id*/
+       cfg = skl_get_ep_blob(skl, mconfig->vbus_id, link_type,
+                                       params->s_fmt, params->ch,
+                                       params->s_freq, params->stream);
+       if (cfg) {
+               mconfig->formats_config.caps_size = cfg->size;
+               mconfig->formats_config.caps = (u32 *) &cfg->caps;
+       } else {
+               dev_err(dai->dev, "Blob NULL for id %x type %d dirn %d\n",
+                                       mconfig->vbus_id, link_type,
+                                       params->stream);
+               dev_err(dai->dev, "PCM: ch %d, freq %d, fmt %d\n",
+                                params->ch, params->s_freq, params->s_fmt);
+               return -EINVAL;
+       }
+
+       return 0;
+}
+
+static int skl_tplg_be_set_src_pipe_params(struct snd_soc_dai *dai,
+                               struct snd_soc_dapm_widget *w,
+                               struct skl_pipe_params *params)
+{
+       struct snd_soc_dapm_path *p;
+       int ret = -EIO;
+
+       snd_soc_dapm_widget_for_each_source_path(w, p) {
+               if (p->connect && is_skl_dsp_widget_type(p->source) &&
+                                               p->source->priv) {
+
+                       if (!p->source->power) {
+                               ret = skl_tplg_be_fill_pipe_params(
+                                               dai, p->source->priv,
+                                               params);
+                               if (ret < 0)
+                                       return ret;
+                       } else {
+                               return -EBUSY;
+                       }
+               } else {
+                       ret = skl_tplg_be_set_src_pipe_params(
+                                               dai, p->source, params);
+                       if (ret < 0)
+                               return ret;
+               }
+       }
+
+       return ret;
+}
+
+static int skl_tplg_be_set_sink_pipe_params(struct snd_soc_dai *dai,
+       struct snd_soc_dapm_widget *w, struct skl_pipe_params *params)
+{
+       struct snd_soc_dapm_path *p = NULL;
+       int ret = -EIO;
+
+       snd_soc_dapm_widget_for_each_sink_path(w, p) {
+               if (p->connect && is_skl_dsp_widget_type(p->sink) &&
+                                               p->sink->priv) {
+
+                       if (!p->sink->power) {
+                               ret = skl_tplg_be_fill_pipe_params(
+                                               dai, p->sink->priv, params);
+                               if (ret < 0)
+                                       return ret;
+                       } else {
+                               return -EBUSY;
+                       }
+
+               } else {
+                       ret = skl_tplg_be_set_sink_pipe_params(
+                                               dai, p->sink, params);
+                       if (ret < 0)
+                               return ret;
+               }
+       }
+
+       return ret;
+}
+
+/*
+ * BE hw_params can be a source parameters (capture) or sink parameters
+ * (playback). Based on sink and source we need to either find the source
+ * list or the sink list and set the pipeline parameters
+ */
+int skl_tplg_be_update_params(struct snd_soc_dai *dai,
+                               struct skl_pipe_params *params)
+{
+       struct snd_soc_dapm_widget *w;
+
+       if (params->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+               w = dai->playback_widget;
+
+               return skl_tplg_be_set_src_pipe_params(dai, w, params);
+
+       } else {
+               w = dai->capture_widget;
+
+               return skl_tplg_be_set_sink_pipe_params(dai, w, params);
+       }
+
+       return 0;
+}
+
+static const struct snd_soc_tplg_widget_events skl_tplg_widget_ops[] = {
+       {SKL_MIXER_EVENT, skl_tplg_mixer_event},
+       {SKL_VMIXER_EVENT, skl_tplg_vmixer_event},
+       {SKL_PGA_EVENT, skl_tplg_pga_event},
+};
+
+/*
+ * The topology binary passes the pin info for a module so initialize the pin
+ * info passed into module instance
+ */
+static void skl_fill_module_pin_info(struct skl_dfw_module_pin *dfw_pin,
+                                               struct skl_module_pin *m_pin,
+                                               bool is_dynamic, int max_pin)
+{
+       int i;
+
+       for (i = 0; i < max_pin; i++) {
+               m_pin[i].id.module_id = dfw_pin[i].module_id;
+               m_pin[i].id.instance_id = dfw_pin[i].instance_id;
+               m_pin[i].in_use = false;
+               m_pin[i].is_dynamic = is_dynamic;
+       }
+}
+
+/*
+ * Add pipeline from topology binary into driver pipeline list
+ *
+ * If already added we return that instance
+ * Otherwise we create a new instance and add into driver list
+ */
+static struct skl_pipe *skl_tplg_add_pipe(struct device *dev,
+                       struct skl *skl, struct skl_dfw_pipe *dfw_pipe)
+{
+       struct skl_pipeline *ppl;
+       struct skl_pipe *pipe;
+       struct skl_pipe_params *params;
+
+       list_for_each_entry(ppl, &skl->ppl_list, node) {
+               if (ppl->pipe->ppl_id == dfw_pipe->pipe_id)
+                       return ppl->pipe;
+       }
+
+       ppl = devm_kzalloc(dev, sizeof(*ppl), GFP_KERNEL);
+       if (!ppl)
+               return NULL;
+
+       pipe = devm_kzalloc(dev, sizeof(*pipe), GFP_KERNEL);
+       if (!pipe)
+               return NULL;
+
+       params = devm_kzalloc(dev, sizeof(*params), GFP_KERNEL);
+       if (!params)
+               return NULL;
+
+       pipe->ppl_id = dfw_pipe->pipe_id;
+       pipe->memory_pages = dfw_pipe->memory_pages;
+       pipe->pipe_priority = dfw_pipe->pipe_priority;
+       pipe->conn_type = dfw_pipe->conn_type;
+       pipe->state = SKL_PIPE_INVALID;
+       pipe->p_params = params;
+       INIT_LIST_HEAD(&pipe->w_list);
+
+       ppl->pipe = pipe;
+       list_add(&ppl->node, &skl->ppl_list);
+
+       return ppl->pipe;
+}
+
+/*
+ * Topology core widget load callback
+ *
+ * This is used to save the private data for each widget which gives
+ * 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,
+                               struct snd_soc_dapm_widget *w,
+                               struct snd_soc_tplg_dapm_widget *tplg_w)
+{
+       int ret;
+       struct hdac_ext_bus *ebus = snd_soc_component_get_drvdata(cmpnt);
+       struct skl *skl = ebus_to_skl(ebus);
+       struct hdac_bus *bus = ebus_to_hbus(ebus);
+       struct skl_module_cfg *mconfig;
+       struct skl_pipe *pipe;
+       struct skl_dfw_module *dfw_config =
+                               (struct skl_dfw_module *)tplg_w->priv.data;
+
+       if (!tplg_w->priv.size)
+               goto bind_event;
+
+       mconfig = devm_kzalloc(bus->dev, sizeof(*mconfig), GFP_KERNEL);
+
+       if (!mconfig)
+               return -ENOMEM;
+
+       w->priv = mconfig;
+       mconfig->id.module_id = dfw_config->module_id;
+       mconfig->id.instance_id = dfw_config->instance_id;
+       mconfig->mcps = dfw_config->max_mcps;
+       mconfig->ibs = dfw_config->ibs;
+       mconfig->obs = dfw_config->obs;
+       mconfig->core_id = dfw_config->core_id;
+       mconfig->max_in_queue = dfw_config->max_in_queue;
+       mconfig->max_out_queue = dfw_config->max_out_queue;
+       mconfig->is_loadable = dfw_config->is_loadable;
+       mconfig->in_fmt.channels = dfw_config->in_fmt.channels;
+       mconfig->in_fmt.s_freq = dfw_config->in_fmt.freq;
+       mconfig->in_fmt.bit_depth = dfw_config->in_fmt.bit_depth;
+       mconfig->in_fmt.valid_bit_depth =
+                               dfw_config->in_fmt.valid_bit_depth;
+       mconfig->in_fmt.ch_cfg = dfw_config->in_fmt.ch_cfg;
+       mconfig->out_fmt.channels = dfw_config->out_fmt.channels;
+       mconfig->out_fmt.s_freq = dfw_config->out_fmt.freq;
+       mconfig->out_fmt.bit_depth = dfw_config->out_fmt.bit_depth;
+       mconfig->out_fmt.valid_bit_depth =
+                               dfw_config->out_fmt.valid_bit_depth;
+       mconfig->out_fmt.ch_cfg = dfw_config->out_fmt.ch_cfg;
+       mconfig->params_fixup = dfw_config->params_fixup;
+       mconfig->converter = dfw_config->converter;
+       mconfig->m_type = dfw_config->module_type;
+       mconfig->vbus_id = dfw_config->vbus_id;
+
+       pipe = skl_tplg_add_pipe(bus->dev, skl, &dfw_config->pipe);
+       if (pipe)
+               mconfig->pipe = pipe;
+
+       mconfig->dev_type = dfw_config->dev_type;
+       mconfig->hw_conn_type = dfw_config->hw_conn_type;
+       mconfig->time_slot = dfw_config->time_slot;
+       mconfig->formats_config.caps_size = dfw_config->caps.caps_size;
+
+       mconfig->m_in_pin = devm_kzalloc(bus->dev,
+                               (mconfig->max_in_queue) *
+                                       sizeof(*mconfig->m_in_pin),
+                               GFP_KERNEL);
+       if (!mconfig->m_in_pin)
+               return -ENOMEM;
+
+       mconfig->m_out_pin = devm_kzalloc(bus->dev, (mconfig->max_out_queue) *
+                                               sizeof(*mconfig->m_out_pin),
+                                               GFP_KERNEL);
+       if (!mconfig->m_out_pin)
+               return -ENOMEM;
+
+       skl_fill_module_pin_info(dfw_config->in_pin, mconfig->m_in_pin,
+                                               dfw_config->is_dynamic_in_pin,
+                                               mconfig->max_in_queue);
+
+       skl_fill_module_pin_info(dfw_config->out_pin, mconfig->m_out_pin,
+                                                dfw_config->is_dynamic_out_pin,
+                                                       mconfig->max_out_queue);
+
+
+       if (mconfig->formats_config.caps_size == 0)
+               goto bind_event;
+
+       mconfig->formats_config.caps = (u32 *)devm_kzalloc(bus->dev,
+                       mconfig->formats_config.caps_size, GFP_KERNEL);
+
+       if (mconfig->formats_config.caps == NULL)
+               return -ENOMEM;
+
+       memcpy(mconfig->formats_config.caps, dfw_config->caps.caps,
+                                        dfw_config->caps.caps_size);
+
+bind_event:
+       if (tplg_w->event_type == 0) {
+               dev_dbg(bus->dev, "ASoC: No event handler required\n");
+               return 0;
+       }
+
+       ret = snd_soc_tplg_widget_bind_event(w, skl_tplg_widget_ops,
+                                       ARRAY_SIZE(skl_tplg_widget_ops),
+                                       tplg_w->event_type);
+
+       if (ret) {
+               dev_err(bus->dev, "%s: No matching event handlers found for %d\n",
+                                       __func__, tplg_w->event_type);
+               return -EINVAL;
+       }
+
+       return 0;
+}
+
+static struct snd_soc_tplg_ops skl_tplg_ops  = {
+       .widget_load = skl_tplg_widget_load,
+};
+
+/* This will be read from topology manifest, currently defined here */
+#define SKL_MAX_MCPS 30000000
+#define SKL_FW_MAX_MEM 1000000
+
+/*
+ * SKL topology init routine
+ */
+int skl_tplg_init(struct snd_soc_platform *platform, struct hdac_ext_bus *ebus)
+{
+       int ret;
+       const struct firmware *fw;
+       struct hdac_bus *bus = ebus_to_hbus(ebus);
+       struct skl *skl = ebus_to_skl(ebus);
+
+       ret = request_firmware(&fw, "dfw_sst.bin", bus->dev);
+       if (ret < 0) {
+               dev_err(bus->dev, "tplg fw %s load failed with %d\n",
+                               "dfw_sst.bin", ret);
+               return ret;
+       }
+
+       /*
+        * The complete tplg for SKL is loaded as index 0, we don't use
+        * any other index
+        */
+       ret = snd_soc_tplg_component_load(&platform->component,
+                                       &skl_tplg_ops, fw, 0);
+       if (ret < 0) {
+               dev_err(bus->dev, "tplg component load failed%d\n", ret);
+               return -EINVAL;
+       }
+
+       skl->resource.max_mcps = SKL_MAX_MCPS;
+       skl->resource.max_mem = SKL_FW_MAX_MEM;
+
+       return 0;
+}
index 8c7767b..76053a8 100644 (file)
@@ -129,6 +129,11 @@ struct skl_src_module_cfg {
        enum skl_s_freq src_cfg;
 } __packed;
 
+struct notification_mask {
+       u32 notify;
+       u32 enable;
+} __packed;
+
 struct skl_up_down_mixer_cfg {
        struct skl_base_cfg base_cfg;
        enum skl_ch_cfg out_ch_cfg;
@@ -153,8 +158,7 @@ enum skl_dma_type {
 union skl_ssp_dma_node {
        u8 val;
        struct {
-               u8 dual_mono:1;
-               u8 time_slot:3;
+               u8 time_slot_index:4;
                u8 i2s_instance:4;
        } dma_node;
 };
@@ -263,6 +267,34 @@ struct skl_module_cfg {
        struct skl_specific_cfg formats_config;
 };
 
+struct skl_pipeline {
+       struct skl_pipe *pipe;
+       struct list_head node;
+};
+
+struct skl_dapm_path_list {
+       struct snd_soc_dapm_path *dapm_path;
+       struct list_head node;
+};
+
+static inline struct skl *get_skl_ctx(struct device *dev)
+{
+       struct hdac_ext_bus *ebus = dev_get_drvdata(dev);
+
+       return ebus_to_skl(ebus);
+}
+
+int skl_tplg_be_update_params(struct snd_soc_dai *dai,
+       struct skl_pipe_params *params);
+void skl_tplg_set_be_dmic_config(struct snd_soc_dai *dai,
+       struct skl_pipe_params *params, int stream);
+int skl_tplg_init(struct snd_soc_platform *platform,
+                               struct hdac_ext_bus *ebus);
+struct skl_module_cfg *skl_tplg_fe_get_cpr_module(
+               struct snd_soc_dai *dai, int stream);
+int skl_tplg_update_pipe_params(struct device *dev,
+               struct skl_module_cfg *mconfig, struct skl_pipe_params *params);
+
 int skl_create_pipeline(struct skl_sst *ctx, struct skl_pipe *pipe);
 
 int skl_run_pipe(struct skl_sst *ctx, struct skl_pipe *pipe);
index a506898..2bc396d 100644 (file)
 #ifndef __HDA_TPLG_INTERFACE_H__
 #define __HDA_TPLG_INTERFACE_H__
 
+/*
+ * Default types range from 0~12. type can range from 0 to 0xff
+ * SST types start at higher to avoid any overlapping in future
+ */
+#define SOC_CONTROL_TYPE_HDA_SST_ALGO_PARAMS   0x100
+#define SOC_CONTROL_TYPE_HDA_SST_MUX           0x101
+#define SOC_CONTROL_TYPE_HDA_SST_MIX           0x101
+#define SOC_CONTROL_TYPE_HDA_SST_BYTE          0x103
+
+#define HDA_SST_CFG_MAX        900 /* size of copier cfg*/
+#define MAX_IN_QUEUE 8
+#define MAX_OUT_QUEUE 8
+
+/* Event types goes here */
+/* Reserve event type 0 for no event handlers */
+enum skl_event_types {
+       SKL_EVENT_NONE = 0,
+       SKL_MIXER_EVENT,
+       SKL_MUX_EVENT,
+       SKL_VMIXER_EVENT,
+       SKL_PGA_EVENT
+};
+
 /**
  * enum skl_ch_cfg - channel configuration
  *
@@ -83,6 +106,66 @@ enum skl_dev_type {
        SKL_DEVICE_I2S = 0x2,
        SKL_DEVICE_SLIMBUS = 0x3,
        SKL_DEVICE_HDALINK = 0x4,
+       SKL_DEVICE_HDAHOST = 0x5,
        SKL_DEVICE_NONE
 };
+
+struct skl_dfw_module_pin {
+       u16 module_id;
+       u16 instance_id;
+} __packed;
+
+struct skl_dfw_module_fmt {
+       u32 channels;
+       u32 freq;
+       u32 bit_depth;
+       u32 valid_bit_depth;
+       u32 ch_cfg;
+} __packed;
+
+struct skl_dfw_module_caps {
+       u32 caps_size;
+       u32 caps[HDA_SST_CFG_MAX];
+};
+
+struct skl_dfw_pipe {
+       u8 pipe_id;
+       u8 pipe_priority;
+       u16 conn_type;
+       u32 memory_pages;
+} __packed;
+
+struct skl_dfw_module {
+       u16 module_id;
+       u16 instance_id;
+       u32 max_mcps;
+       u8 core_id;
+       u8 max_in_queue;
+       u8 max_out_queue;
+       u8 is_loadable;
+       u8 conn_type;
+       u8 dev_type;
+       u8 hw_conn_type;
+       u8 time_slot;
+       u32 obs;
+       u32 ibs;
+       u32 params_fixup;
+       u32 converter;
+       u32 module_type;
+       u32 vbus_id;
+       u8 is_dynamic_in_pin;
+       u8 is_dynamic_out_pin;
+       struct skl_dfw_pipe pipe;
+       struct skl_dfw_module_fmt in_fmt;
+       struct skl_dfw_module_fmt out_fmt;
+       struct skl_dfw_module_pin in_pin[MAX_IN_QUEUE];
+       struct skl_dfw_module_pin out_pin[MAX_OUT_QUEUE];
+       struct skl_dfw_module_caps caps;
+} __packed;
+
+struct skl_dfw_algo_data {
+       u32 max;
+       char *params;
+} __packed;
+
 #endif
index 348d094..5319529 100644 (file)
@@ -166,12 +166,20 @@ static int skl_runtime_suspend(struct device *dev)
        struct pci_dev *pci = to_pci_dev(dev);
        struct hdac_ext_bus *ebus = pci_get_drvdata(pci);
        struct hdac_bus *bus = ebus_to_hbus(ebus);
+       struct skl *skl = ebus_to_skl(ebus);
+       int ret;
 
        dev_dbg(bus->dev, "in %s\n", __func__);
 
        /* enable controller wake up event */
        snd_hdac_chip_updatew(bus, WAKEEN, 0, STATESTS_INT_MASK);
 
+       snd_hdac_ext_bus_link_power_down_all(ebus);
+
+       ret = skl_suspend_dsp(skl);
+       if (ret < 0)
+               return ret;
+
        snd_hdac_bus_stop_chip(bus);
        snd_hdac_bus_enter_link_reset(bus);
 
@@ -183,7 +191,7 @@ static int skl_runtime_resume(struct device *dev)
        struct pci_dev *pci = to_pci_dev(dev);
        struct hdac_ext_bus *ebus = pci_get_drvdata(pci);
        struct hdac_bus *bus = ebus_to_hbus(ebus);
-       struct skl *hda = ebus_to_skl(ebus);
+       struct skl *skl = ebus_to_skl(ebus);
        int status;
 
        dev_dbg(bus->dev, "in %s\n", __func__);
@@ -191,12 +199,12 @@ static int skl_runtime_resume(struct device *dev)
        /* Read STATESTS before controller reset */
        status = snd_hdac_chip_readw(bus, STATESTS);
 
-       skl_init_pci(hda);
+       skl_init_pci(skl);
        snd_hdac_bus_init_chip(bus, true);
        /* disable controller Wake Up event */
        snd_hdac_chip_updatew(bus, WAKEEN, STATESTS_INT_MASK, 0);
 
-       return 0;
+       return skl_resume_dsp(skl);
 }
 #endif /* CONFIG_PM */
 
@@ -453,21 +461,28 @@ static int skl_probe(struct pci_dev *pci,
        if (err < 0)
                goto out_free;
 
+       skl->nhlt = skl_nhlt_init(bus->dev);
+
+       if (skl->nhlt == NULL)
+               goto out_free;
+
        pci_set_drvdata(skl->pci, ebus);
 
        /* check if dsp is there */
        if (ebus->ppcap) {
-               /* TODO register with dsp IPC */
-               dev_dbg(bus->dev, "Register dsp\n");
+               err = skl_init_dsp(skl);
+               if (err < 0) {
+                       dev_dbg(bus->dev, "error failed to register dsp\n");
+                       goto out_free;
+               }
        }
-
        if (ebus->mlcap)
                snd_hdac_ext_bus_get_ml_capabilities(ebus);
 
        /* create device for soc dmic */
        err = skl_dmic_device_register(skl);
        if (err < 0)
-               goto out_free;
+               goto out_dsp_free;
 
        /* register platform dai and controls */
        err = skl_platform_register(bus->dev);
@@ -491,6 +506,8 @@ out_unregister:
        skl_platform_unregister(bus->dev);
 out_dmic_free:
        skl_dmic_device_unregister(skl);
+out_dsp_free:
+       skl_free_dsp(skl);
 out_free:
        skl->init_failed = 1;
        skl_free(ebus);
@@ -507,6 +524,7 @@ static void skl_remove(struct pci_dev *pci)
                pm_runtime_get_noresume(&pci->dev);
        pci_dev_put(pci);
        skl_platform_unregister(&pci->dev);
+       skl_free_dsp(skl);
        skl_dmic_device_unregister(skl);
        skl_free(ebus);
        dev_set_drvdata(&pci->dev, NULL);
index f7fdbb0..dd2e79a 100644 (file)
 #define AZX_REG_VS_SDXEFIFOS_XBASE     0x1094
 #define AZX_REG_VS_SDXEFIFOS_XINTERVAL 0x20
 
+struct skl_dsp_resource {
+       u32 max_mcps;
+       u32 max_mem;
+       u32 mcps;
+       u32 mem;
+};
+
 struct skl {
        struct hdac_ext_bus ebus;
        struct pci_dev *pci;
@@ -55,8 +62,12 @@ struct skl {
        unsigned int init_failed:1; /* delayed init failed */
        struct platform_device *dmic_dev;
 
-       void __iomem *nhlt; /* nhlt ptr */
+       void *nhlt; /* nhlt ptr */
        struct skl_sst *skl_sst; /* sst skl ctx */
+
+       struct skl_dsp_resource resource;
+       struct list_head ppl_list;
+       struct list_head dapm_path_list;
 };
 
 #define skl_to_ebus(s) (&(s)->ebus)
@@ -72,8 +83,8 @@ struct skl_dma_params {
 int skl_platform_unregister(struct device *dev);
 int skl_platform_register(struct device *dev);
 
-void __iomem *skl_nhlt_init(struct device *dev);
-void skl_nhlt_free(void __iomem *addr);
+void *skl_nhlt_init(struct device *dev);
+void skl_nhlt_free(void *addr);
 struct nhlt_specific_cfg *skl_get_ep_blob(struct skl *skl, u32 instance,
                        u8 link_type, u8 s_fmt, u8 no_ch, u32 s_rate, u8 dirn);