Merge remote-tracking branches 'asoc/topic/hdmi', 'asoc/topic/intel', 'asoc/topic...
authorMark Brown <broonie@kernel.org>
Mon, 8 Dec 2014 13:12:00 +0000 (13:12 +0000)
committerMark Brown <broonie@kernel.org>
Mon, 8 Dec 2014 13:12:00 +0000 (13:12 +0000)
35 files changed:
arch/x86/include/asm/platform_sst_audio.h
sound/soc/codecs/hdmi.c
sound/soc/codecs/lm49453.c
sound/soc/intel/Kconfig
sound/soc/intel/Makefile
sound/soc/intel/broadwell.c
sound/soc/intel/bytcr_dpcm_rt5640.c [new file with mode: 0644]
sound/soc/intel/cht_bsw_rt5672.c [new file with mode: 0644]
sound/soc/intel/haswell.c
sound/soc/intel/sst-atom-controls.c
sound/soc/intel/sst-atom-controls.h
sound/soc/intel/sst-baytrail-dsp.c
sound/soc/intel/sst-dsp-priv.h
sound/soc/intel/sst-dsp.c
sound/soc/intel/sst-dsp.h
sound/soc/intel/sst-firmware.c
sound/soc/intel/sst-haswell-dsp.c
sound/soc/intel/sst-haswell-ipc.c
sound/soc/intel/sst-haswell-ipc.h
sound/soc/intel/sst-haswell-pcm.c
sound/soc/intel/sst-mfld-platform-compress.c
sound/soc/intel/sst-mfld-platform-pcm.c
sound/soc/intel/sst-mfld-platform.h
sound/soc/intel/sst/Makefile [new file with mode: 0644]
sound/soc/intel/sst/sst.c [new file with mode: 0644]
sound/soc/intel/sst/sst.h [new file with mode: 0644]
sound/soc/intel/sst/sst_acpi.c [new file with mode: 0644]
sound/soc/intel/sst/sst_drv_interface.c [new file with mode: 0644]
sound/soc/intel/sst/sst_ipc.c [new file with mode: 0644]
sound/soc/intel/sst/sst_loader.c [new file with mode: 0644]
sound/soc/intel/sst/sst_pci.c [new file with mode: 0644]
sound/soc/intel/sst/sst_pvt.c [new file with mode: 0644]
sound/soc/intel/sst/sst_stream.c [new file with mode: 0644]
sound/soc/jz4740/qi_lb60.c
sound/soc/soc-jack.c

index 0a4e140..7249e6d 100644 (file)
@@ -16,6 +16,9 @@
 
 #include <linux/sfi.h>
 
+#define MAX_NUM_STREAMS_MRFLD  25
+#define MAX_NUM_STREAMS        MAX_NUM_STREAMS_MRFLD
+
 enum sst_audio_task_id_mrfld {
        SST_TASK_ID_NONE = 0,
        SST_TASK_ID_SBA = 1,
@@ -73,6 +76,65 @@ struct sst_platform_data {
        unsigned int strm_map_size;
 };
 
+struct sst_info {
+       u32 iram_start;
+       u32 iram_end;
+       bool iram_use;
+       u32 dram_start;
+       u32 dram_end;
+       bool dram_use;
+       u32 imr_start;
+       u32 imr_end;
+       bool imr_use;
+       u32 mailbox_start;
+       bool use_elf;
+       bool lpe_viewpt_rqd;
+       unsigned int max_streams;
+       u32 dma_max_len;
+       u8 num_probes;
+};
+
+struct sst_lib_dnld_info {
+       unsigned int mod_base;
+       unsigned int mod_end;
+       unsigned int mod_table_offset;
+       unsigned int mod_table_size;
+       bool mod_ddr_dnld;
+};
+
+struct sst_res_info {
+       unsigned int shim_offset;
+       unsigned int shim_size;
+       unsigned int shim_phy_addr;
+       unsigned int ssp0_offset;
+       unsigned int ssp0_size;
+       unsigned int dma0_offset;
+       unsigned int dma0_size;
+       unsigned int dma1_offset;
+       unsigned int dma1_size;
+       unsigned int iram_offset;
+       unsigned int iram_size;
+       unsigned int dram_offset;
+       unsigned int dram_size;
+       unsigned int mbox_offset;
+       unsigned int mbox_size;
+       unsigned int acpi_lpe_res_index;
+       unsigned int acpi_ddr_index;
+       unsigned int acpi_ipc_irq_index;
+};
+
+struct sst_ipc_info {
+       int ipc_offset;
+       unsigned int mbox_recv_off;
+};
+
+struct sst_platform_info {
+       const struct sst_info *probe_data;
+       const struct sst_ipc_info *ipc_info;
+       const struct sst_res_info *res_info;
+       const struct sst_lib_dnld_info *lib_info;
+       const char *platform;
+};
 int add_sst_platform_device(void);
 #endif
 
index 1087fd5..1391ad5 100644 (file)
@@ -47,6 +47,7 @@ static struct snd_soc_dai_driver hdmi_codec_dai = {
                        SNDRV_PCM_RATE_176400 | SNDRV_PCM_RATE_192000,
                .formats = SNDRV_PCM_FMTBIT_S16_LE |
                        SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE,
+               .sig_bits = 24,
        },
        .capture = {
                .stream_name = "Capture",
@@ -75,6 +76,7 @@ static struct snd_soc_codec_driver hdmi_codec = {
        .num_dapm_widgets = ARRAY_SIZE(hdmi_widgets),
        .dapm_routes = hdmi_routes,
        .num_dapm_routes = ARRAY_SIZE(hdmi_routes),
+       .ignore_pmdown_time = true,
 };
 
 static int hdmi_codec_probe(struct platform_device *pdev)
index c1ae576..c4dfde9 100644 (file)
@@ -1395,15 +1395,7 @@ static struct snd_soc_dai_driver lm49453_dai[] = {
        },
 };
 
-/* power down chip */
-static int lm49453_remove(struct snd_soc_codec *codec)
-{
-       lm49453_set_bias_level(codec, SND_SOC_BIAS_OFF);
-       return 0;
-}
-
 static struct snd_soc_codec_driver soc_codec_dev_lm49453 = {
-       .remove = lm49453_remove,
        .set_bias_level = lm49453_set_bias_level,
        .controls = lm49453_snd_controls,
        .num_controls = ARRAY_SIZE(lm49453_snd_controls),
index f5b4a9c..e989ecf 100644 (file)
@@ -3,6 +3,7 @@ config SND_MFLD_MACHINE
        depends on INTEL_SCU_IPC
        select SND_SOC_SN95031
        select SND_SST_MFLD_PLATFORM
+       select SND_SST_IPC_PCI
        help
           This adds support for ASoC machine driver for Intel(R) MID Medfield platform
           used as alsa device in audio substem in Intel(R) MID devices
@@ -12,10 +13,23 @@ config SND_MFLD_MACHINE
 config SND_SST_MFLD_PLATFORM
        tristate
 
+config SND_SST_IPC
+       tristate
+
+config SND_SST_IPC_PCI
+       tristate
+       select SND_SST_IPC
+
+config SND_SST_IPC_ACPI
+       tristate
+       select SND_SST_IPC
+       depends on ACPI
+
 config SND_SOC_INTEL_SST
        tristate "ASoC support for Intel(R) Smart Sound Technology"
        select SND_SOC_INTEL_SST_ACPI if ACPI
        depends on (X86 || COMPILE_TEST)
+       depends on DW_DMAC_CORE
        help
           This adds support for Intel(R) Smart Sound Technology (SST).
           Say Y if you have such a device
@@ -32,7 +46,8 @@ config SND_SOC_INTEL_BAYTRAIL
 
 config SND_SOC_INTEL_HASWELL_MACH
        tristate "ASoC Audio DSP support for Intel Haswell Lynxpoint"
-       depends on SND_SOC_INTEL_SST && X86_INTEL_LPSS && I2C
+       depends on SND_SOC_INTEL_SST && X86_INTEL_LPSS && I2C && \\
+                  I2C_DESIGNWARE_PLATFORM
        select SND_SOC_INTEL_HASWELL
        select SND_SOC_RT5640
        help
@@ -61,7 +76,8 @@ config SND_SOC_INTEL_BYT_MAX98090_MACH
 
 config SND_SOC_INTEL_BROADWELL_MACH
        tristate "ASoC Audio DSP support for Intel Broadwell Wildcatpoint"
-       depends on SND_SOC_INTEL_SST && X86_INTEL_LPSS && DW_DMAC
+       depends on SND_SOC_INTEL_SST && X86_INTEL_LPSS && DW_DMAC && \\
+                  I2C_DESIGNWARE_PLATFORM
        select SND_SOC_INTEL_HASWELL
        select SND_COMPRESS_OFFLOAD
        select SND_SOC_RT286
@@ -70,3 +86,27 @@ config SND_SOC_INTEL_BROADWELL_MACH
          Ultrabook platforms.
          Say Y if you have such a device
          If unsure select "N".
+
+config SND_SOC_INTEL_BYTCR_RT5640_MACH
+       tristate "ASoC Audio DSP Support for MID BYT Platform"
+       depends on X86
+       select SND_SOC_RT5640
+       select SND_SST_MFLD_PLATFORM
+       select SND_SST_IPC_ACPI
+       help
+         This adds support for ASoC machine driver for Intel(R) MID Baytrail platform
+          used as alsa device in audio substem in Intel(R) MID devices
+          Say Y if you have such a device
+          If unsure select "N".
+
+config SND_SOC_INTEL_CHT_BSW_RT5672_MACH
+        tristate "ASoC Audio driver for Intel Cherrytrail & Braswell with RT5672 codec"
+        depends on X86_INTEL_LPSS
+        select SND_SOC_RT5670
+        select SND_SST_MFLD_PLATFORM
+        select SND_SST_IPC_ACPI
+        help
+          This adds support for ASoC machine driver for Intel(R) Cherrytrail & Braswell
+          platforms with RT5672 audio codec.
+          Say Y if you have such a device
+          If unsure select "N".
index f841786..e928ec3 100644 (file)
@@ -26,8 +26,15 @@ snd-soc-sst-haswell-objs := haswell.o
 snd-soc-sst-byt-rt5640-mach-objs := byt-rt5640.o
 snd-soc-sst-byt-max98090-mach-objs := byt-max98090.o
 snd-soc-sst-broadwell-objs := broadwell.o
+snd-soc-sst-bytcr-dpcm-rt5640-objs := bytcr_dpcm_rt5640.o
+snd-soc-sst-cht-bsw-rt5672-objs := cht_bsw_rt5672.o
 
 obj-$(CONFIG_SND_SOC_INTEL_HASWELL_MACH) += snd-soc-sst-haswell.o
 obj-$(CONFIG_SND_SOC_INTEL_BYT_RT5640_MACH) += snd-soc-sst-byt-rt5640-mach.o
 obj-$(CONFIG_SND_SOC_INTEL_BYT_MAX98090_MACH) += snd-soc-sst-byt-max98090-mach.o
 obj-$(CONFIG_SND_SOC_INTEL_BROADWELL_MACH) += snd-soc-sst-broadwell.o
+obj-$(CONFIG_SND_SOC_INTEL_BYTCR_RT5640_MACH) += snd-soc-sst-bytcr-dpcm-rt5640.o
+obj-$(CONFIG_SND_SOC_INTEL_CHT_BSW_RT5672_MACH) += snd-soc-sst-cht-bsw-rt5672.o
+
+# DSP driver
+obj-$(CONFIG_SND_SST_IPC) += sst/
index 0e550f1..c256764 100644 (file)
@@ -19,6 +19,7 @@
 #include <sound/core.h>
 #include <sound/pcm.h>
 #include <sound/soc.h>
+#include <sound/jack.h>
 #include <sound/pcm_params.h>
 
 #include "sst-dsp.h"
 
 #include "../codecs/rt286.h"
 
+static struct snd_soc_jack broadwell_headset;
+/* Headset jack detection DAPM pins */
+static struct snd_soc_jack_pin broadwell_headset_pins[] = {
+       {
+               .pin = "Mic Jack",
+               .mask = SND_JACK_MICROPHONE,
+       },
+       {
+               .pin = "Headphone Jack",
+               .mask = SND_JACK_HEADPHONE,
+       },
+};
+
+static const struct snd_kcontrol_new broadwell_controls[] = {
+       SOC_DAPM_PIN_SWITCH("Speaker"),
+       SOC_DAPM_PIN_SWITCH("Headphone Jack"),
+};
+
 static const struct snd_soc_dapm_widget broadwell_widgets[] = {
-       SND_SOC_DAPM_HP("Headphones", NULL),
+       SND_SOC_DAPM_HP("Headphone Jack", NULL),
        SND_SOC_DAPM_SPK("Speaker", NULL),
        SND_SOC_DAPM_MIC("Mic Jack", NULL),
        SND_SOC_DAPM_MIC("DMIC1", NULL),
@@ -42,7 +61,7 @@ static const struct snd_soc_dapm_route broadwell_rt286_map[] = {
        {"Speaker", NULL, "SPOL"},
 
        /* HP jack connectors - unknown if we have jack deteck */
-       {"Headphones", NULL, "HPO Pin"},
+       {"Headphone Jack", NULL, "HPO Pin"},
 
        /* other jacks */
        {"MIC1", NULL, "Mic Jack"},
@@ -57,6 +76,27 @@ static const struct snd_soc_dapm_route broadwell_rt286_map[] = {
        {"AIF1 Playback", NULL, "SSP0 CODEC OUT"},
 };
 
+static int broadwell_rt286_codec_init(struct snd_soc_pcm_runtime *rtd)
+{
+       struct snd_soc_codec *codec = rtd->codec;
+       int ret = 0;
+       ret = snd_soc_jack_new(codec, "Headset",
+               SND_JACK_HEADSET | SND_JACK_BTN_0, &broadwell_headset);
+
+       if (ret)
+               return ret;
+
+       ret = snd_soc_jack_add_pins(&broadwell_headset,
+               ARRAY_SIZE(broadwell_headset_pins),
+               broadwell_headset_pins);
+       if (ret)
+               return ret;
+
+       rt286_mic_detect(codec, &broadwell_headset);
+       return 0;
+}
+
+
 static int broadwell_ssp0_fixup(struct snd_soc_pcm_runtime *rtd,
                        struct snd_pcm_hw_params *params)
 {
@@ -116,7 +156,7 @@ static int broadwell_rtd_init(struct snd_soc_pcm_runtime *rtd)
        }
 
        /* always connected - check HP for jack detect */
-       snd_soc_dapm_enable_pin(dapm, "Headphones");
+       snd_soc_dapm_enable_pin(dapm, "Headphone Jack");
        snd_soc_dapm_enable_pin(dapm, "Speaker");
        snd_soc_dapm_enable_pin(dapm, "Mic Jack");
        snd_soc_dapm_enable_pin(dapm, "Line Jack");
@@ -131,7 +171,7 @@ static struct snd_soc_dai_link broadwell_rt286_dais[] = {
        /* Front End DAI links */
        {
                .name = "System PCM",
-               .stream_name = "System Playback",
+               .stream_name = "System Playback/Capture",
                .cpu_dai_name = "System Pin",
                .platform_name = "haswell-pcm-audio",
                .dynamic = 1,
@@ -140,6 +180,7 @@ static struct snd_soc_dai_link broadwell_rt286_dais[] = {
                .init = broadwell_rtd_init,
                .trigger = {SND_SOC_DPCM_TRIGGER_POST, SND_SOC_DPCM_TRIGGER_POST},
                .dpcm_playback = 1,
+               .dpcm_capture = 1,
        },
        {
                .name = "Offload0",
@@ -174,18 +215,6 @@ static struct snd_soc_dai_link broadwell_rt286_dais[] = {
                .trigger = {SND_SOC_DPCM_TRIGGER_POST, SND_SOC_DPCM_TRIGGER_POST},
                .dpcm_capture = 1,
        },
-       {
-               .name = "Capture PCM",
-               .stream_name = "Capture",
-               .cpu_dai_name = "Capture Pin",
-               .platform_name = "haswell-pcm-audio",
-               .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,
-       },
-
        /* Back End DAI links */
        {
                /* SSP0 - Codec */
@@ -196,6 +225,7 @@ static struct snd_soc_dai_link broadwell_rt286_dais[] = {
                .no_pcm = 1,
                .codec_name = "i2c-INT343A:00",
                .codec_dai_name = "rt286-aif1",
+               .init = broadwell_rt286_codec_init,
                .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF |
                        SND_SOC_DAIFMT_CBS_CFS,
                .ignore_suspend = 1,
@@ -213,6 +243,8 @@ static struct snd_soc_card broadwell_rt286 = {
        .owner = THIS_MODULE,
        .dai_link = broadwell_rt286_dais,
        .num_links = ARRAY_SIZE(broadwell_rt286_dais),
+       .controls = broadwell_controls,
+       .num_controls = ARRAY_SIZE(broadwell_controls),
        .dapm_widgets = broadwell_widgets,
        .num_dapm_widgets = ARRAY_SIZE(broadwell_widgets),
        .dapm_routes = broadwell_rt286_map,
diff --git a/sound/soc/intel/bytcr_dpcm_rt5640.c b/sound/soc/intel/bytcr_dpcm_rt5640.c
new file mode 100644 (file)
index 0000000..f5d0fc1
--- /dev/null
@@ -0,0 +1,230 @@
+/*
+ *  byt_cr_dpcm_rt5640.c - ASoc Machine driver for Intel Byt CR platform
+ *
+ *  Copyright (C) 2014 Intel Corp
+ *  Author: Subhransu S. Prusty <subhransu.s.prusty@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 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 <linux/init.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/device.h>
+#include <linux/slab.h>
+#include <linux/input.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include "../codecs/rt5640.h"
+#include "sst-atom-controls.h"
+
+static const struct snd_soc_dapm_widget byt_dapm_widgets[] = {
+       SND_SOC_DAPM_HP("Headphone", NULL),
+       SND_SOC_DAPM_MIC("Headset Mic", NULL),
+       SND_SOC_DAPM_MIC("Int Mic", NULL),
+       SND_SOC_DAPM_SPK("Ext Spk", NULL),
+};
+
+static const struct snd_soc_dapm_route byt_audio_map[] = {
+       {"IN2P", NULL, "Headset Mic"},
+       {"IN2N", NULL, "Headset Mic"},
+       {"Headset Mic", NULL, "MICBIAS1"},
+       {"IN1P", NULL, "MICBIAS1"},
+       {"LDO2", NULL, "Int Mic"},
+       {"Headphone", NULL, "HPOL"},
+       {"Headphone", NULL, "HPOR"},
+       {"Ext Spk", NULL, "SPOLP"},
+       {"Ext Spk", NULL, "SPOLN"},
+       {"Ext Spk", NULL, "SPORP"},
+       {"Ext Spk", NULL, "SPORN"},
+
+       {"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"},
+};
+
+static const struct snd_kcontrol_new byt_mc_controls[] = {
+       SOC_DAPM_PIN_SWITCH("Headphone"),
+       SOC_DAPM_PIN_SWITCH("Headset Mic"),
+       SOC_DAPM_PIN_SWITCH("Int Mic"),
+       SOC_DAPM_PIN_SWITCH("Ext Spk"),
+};
+
+static int byt_aif1_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;
+
+       snd_soc_dai_set_bclk_ratio(codec_dai, 50);
+
+       ret = snd_soc_dai_set_sysclk(codec_dai, RT5640_SCLK_S_PLL1,
+                                    params_rate(params) * 512,
+                                    SND_SOC_CLOCK_IN);
+       if (ret < 0) {
+               dev_err(rtd->dev, "can't set codec clock %d\n", ret);
+               return ret;
+       }
+
+       ret = snd_soc_dai_set_pll(codec_dai, 0, RT5640_PLL1_S_BCLK1,
+                                 params_rate(params) * 50,
+                                 params_rate(params) * 512);
+       if (ret < 0) {
+               dev_err(rtd->dev, "can't set codec pll: %d\n", ret);
+               return ret;
+       }
+
+       return 0;
+}
+
+static const struct snd_soc_pcm_stream byt_dai_params = {
+       .formats = SNDRV_PCM_FMTBIT_S24_LE,
+       .rate_min = 48000,
+       .rate_max = 48000,
+       .channels_min = 2,
+       .channels_max = 2,
+};
+
+static int byt_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);
+
+       /* The DSP will covert the FE rate to 48k, stereo, 24bits */
+       rate->min = rate->max = 48000;
+       channels->min = channels->max = 2;
+
+       /* set SSP2 to 24-bit */
+       snd_mask_set(&params->masks[SNDRV_PCM_HW_PARAM_FORMAT -
+                                   SNDRV_PCM_HW_PARAM_FIRST_MASK],
+                                   SNDRV_PCM_FORMAT_S24_LE);
+       return 0;
+}
+
+static unsigned int rates_48000[] = {
+       48000,
+};
+
+static struct snd_pcm_hw_constraint_list constraints_48000 = {
+       .count = ARRAY_SIZE(rates_48000),
+       .list  = rates_48000,
+};
+
+static int byt_aif1_startup(struct snd_pcm_substream *substream)
+{
+       return snd_pcm_hw_constraint_list(substream->runtime, 0,
+                       SNDRV_PCM_HW_PARAM_RATE,
+                       &constraints_48000);
+}
+
+static struct snd_soc_ops byt_aif1_ops = {
+       .startup = byt_aif1_startup,
+};
+
+static struct snd_soc_ops byt_be_ssp2_ops = {
+       .hw_params = byt_aif1_hw_params,
+};
+
+static struct snd_soc_dai_link byt_dailink[] = {
+       [MERR_DPCM_AUDIO] = {
+               .name = "Baytrail Audio Port",
+               .stream_name = "Baytrail Audio",
+               .cpu_dai_name = "media-cpu-dai",
+               .codec_dai_name = "snd-soc-dummy-dai",
+               .codec_name = "snd-soc-dummy",
+               .platform_name = "sst-mfld-platform",
+               .ignore_suspend = 1,
+               .dynamic = 1,
+               .dpcm_playback = 1,
+               .dpcm_capture = 1,
+               .ops = &byt_aif1_ops,
+       },
+       [MERR_DPCM_COMPR] = {
+               .name = "Baytrail Compressed Port",
+               .stream_name = "Baytrail Compress",
+               .cpu_dai_name = "compress-cpu-dai",
+               .codec_dai_name = "snd-soc-dummy-dai",
+               .codec_name = "snd-soc-dummy",
+               .platform_name = "sst-mfld-platform",
+       },
+               /* back ends */
+       {
+               .name = "SSP2-Codec",
+               .be_id = 1,
+               .cpu_dai_name = "ssp2-port",
+               .platform_name = "sst-mfld-platform",
+               .no_pcm = 1,
+               .codec_dai_name = "rt5640-aif1",
+               .codec_name = "i2c-10EC5640:00",
+               .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF
+                                               | SND_SOC_DAIFMT_CBS_CFS,
+               .be_hw_params_fixup = byt_codec_fixup,
+               .ignore_suspend = 1,
+               .dpcm_playback = 1,
+               .dpcm_capture = 1,
+               .ops = &byt_be_ssp2_ops,
+       },
+};
+
+/* SoC card */
+static struct snd_soc_card snd_soc_card_byt = {
+       .name = "baytrailcraudio",
+       .dai_link = byt_dailink,
+       .num_links = ARRAY_SIZE(byt_dailink),
+       .dapm_widgets = byt_dapm_widgets,
+       .num_dapm_widgets = ARRAY_SIZE(byt_dapm_widgets),
+       .dapm_routes = byt_audio_map,
+       .num_dapm_routes = ARRAY_SIZE(byt_audio_map),
+       .controls = byt_mc_controls,
+       .num_controls = ARRAY_SIZE(byt_mc_controls),
+};
+
+static int snd_byt_mc_probe(struct platform_device *pdev)
+{
+       int ret_val = 0;
+
+       /* register the soc card */
+       snd_soc_card_byt.dev = &pdev->dev;
+
+       ret_val = devm_snd_soc_register_card(&pdev->dev, &snd_soc_card_byt);
+       if (ret_val) {
+               dev_err(&pdev->dev, "devm_snd_soc_register_card failed %d\n", ret_val);
+               return ret_val;
+       }
+       platform_set_drvdata(pdev, &snd_soc_card_byt);
+       return ret_val;
+}
+
+static struct platform_driver snd_byt_mc_driver = {
+       .driver = {
+               .owner = THIS_MODULE,
+               .name = "bytt100_rt5640",
+               .pm = &snd_soc_pm_ops,
+       },
+       .probe = snd_byt_mc_probe,
+};
+
+module_platform_driver(snd_byt_mc_driver);
+
+MODULE_DESCRIPTION("ASoC Intel(R) Baytrail CR Machine driver");
+MODULE_AUTHOR("Subhransu S. Prusty <subhransu.s.prusty@intel.com>");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:bytrt5640-audio");
diff --git a/sound/soc/intel/cht_bsw_rt5672.c b/sound/soc/intel/cht_bsw_rt5672.c
new file mode 100644 (file)
index 0000000..9b8b561
--- /dev/null
@@ -0,0 +1,285 @@
+/*
+ *  cht_bsw_rt5672.c - ASoc Machine driver for Intel Cherryview-based platforms
+ *                     Cherrytrail and Braswell, with RT5672 codec.
+ *
+ *  Copyright (C) 2014 Intel Corp
+ *  Author: Subhransu S. Prusty <subhransu.s.prusty@intel.com>
+ *          Mengdong Lin <mengdong.lin@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 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 <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include "../codecs/rt5670.h"
+#include "sst-atom-controls.h"
+
+/* The platform clock #3 outputs 19.2Mhz clock to codec as I2S MCLK */
+#define CHT_PLAT_CLK_3_HZ      19200000
+#define CHT_CODEC_DAI  "rt5670-aif1"
+
+static inline struct snd_soc_dai *cht_get_codec_dai(struct snd_soc_card *card)
+{
+       int i;
+
+       for (i = 0; i < card->num_rtd; i++) {
+               struct snd_soc_pcm_runtime *rtd;
+
+               rtd = card->rtd + i;
+               if (!strncmp(rtd->codec_dai->name, CHT_CODEC_DAI,
+                            strlen(CHT_CODEC_DAI)))
+                       return rtd->codec_dai;
+       }
+       return NULL;
+}
+
+static int platform_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;
+       struct snd_soc_dai *codec_dai;
+
+       codec_dai = cht_get_codec_dai(card);
+       if (!codec_dai) {
+               dev_err(card->dev, "Codec dai not found; Unable to set platform clock\n");
+               return -EIO;
+       }
+
+       if (!SND_SOC_DAPM_EVENT_OFF(event))
+               return 0;
+
+       /* Set codec sysclk source to its internal clock because codec PLL will
+        * be off when idle and MCLK will also be off by ACPI when codec is
+        * runtime suspended. Codec needs clock for jack detection and button
+        * press.
+        */
+       snd_soc_dai_set_sysclk(codec_dai, RT5670_SCLK_S_RCCLK,
+                              0, SND_SOC_CLOCK_IN);
+
+       return 0;
+}
+
+static const struct snd_soc_dapm_widget cht_dapm_widgets[] = {
+       SND_SOC_DAPM_HP("Headphone", NULL),
+       SND_SOC_DAPM_MIC("Headset Mic", NULL),
+       SND_SOC_DAPM_MIC("Int Mic", NULL),
+       SND_SOC_DAPM_SPK("Ext Spk", NULL),
+       SND_SOC_DAPM_SUPPLY("Platform Clock", SND_SOC_NOPM, 0, 0,
+                       platform_clock_control, SND_SOC_DAPM_POST_PMD),
+};
+
+static const struct snd_soc_dapm_route cht_audio_map[] = {
+       {"IN1P", NULL, "Headset Mic"},
+       {"IN1N", NULL, "Headset Mic"},
+       {"DMIC L1", NULL, "Int Mic"},
+       {"DMIC R1", NULL, "Int Mic"},
+       {"Headphone", NULL, "HPOL"},
+       {"Headphone", NULL, "HPOR"},
+       {"Ext Spk", NULL, "SPOLP"},
+       {"Ext Spk", NULL, "SPOLN"},
+       {"Ext Spk", NULL, "SPORP"},
+       {"Ext Spk", NULL, "SPORN"},
+       {"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"},
+       {"Headphone", NULL, "Platform Clock"},
+       {"Headset Mic", NULL, "Platform Clock"},
+       {"Int Mic", NULL, "Platform Clock"},
+       {"Ext Spk", NULL, "Platform Clock"},
+};
+
+static const struct snd_kcontrol_new cht_mc_controls[] = {
+       SOC_DAPM_PIN_SWITCH("Headphone"),
+       SOC_DAPM_PIN_SWITCH("Headset Mic"),
+       SOC_DAPM_PIN_SWITCH("Int Mic"),
+       SOC_DAPM_PIN_SWITCH("Ext Spk"),
+};
+
+static int cht_aif1_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;
+
+       /* set codec PLL source to the 19.2MHz platform clock (MCLK) */
+       ret = snd_soc_dai_set_pll(codec_dai, 0, RT5670_PLL1_S_MCLK,
+                                 CHT_PLAT_CLK_3_HZ, params_rate(params) * 512);
+       if (ret < 0) {
+               dev_err(rtd->dev, "can't set codec pll: %d\n", ret);
+               return ret;
+       }
+
+       /* set codec sysclk source to PLL */
+       ret = snd_soc_dai_set_sysclk(codec_dai, RT5670_SCLK_S_PLL1,
+                                    params_rate(params) * 512,
+                                    SND_SOC_CLOCK_IN);
+       if (ret < 0) {
+               dev_err(rtd->dev, "can't set codec sysclk: %d\n", ret);
+               return ret;
+       }
+       return 0;
+}
+
+static int cht_codec_init(struct snd_soc_pcm_runtime *runtime)
+{
+       int ret;
+       struct snd_soc_dai *codec_dai = runtime->codec_dai;
+
+       /* 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;
+       }
+
+       return 0;
+}
+
+static int cht_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);
+
+       /* The DSP will covert the FE rate to 48k, stereo, 24bits */
+       rate->min = rate->max = 48000;
+       channels->min = channels->max = 2;
+
+       /* set SSP2 to 24-bit */
+       snd_mask_set(&params->masks[SNDRV_PCM_HW_PARAM_FORMAT -
+                                   SNDRV_PCM_HW_PARAM_FIRST_MASK],
+                                   SNDRV_PCM_FORMAT_S24_LE);
+       return 0;
+}
+
+static unsigned int rates_48000[] = {
+       48000,
+};
+
+static struct snd_pcm_hw_constraint_list constraints_48000 = {
+       .count = ARRAY_SIZE(rates_48000),
+       .list  = rates_48000,
+};
+
+static int cht_aif1_startup(struct snd_pcm_substream *substream)
+{
+       return snd_pcm_hw_constraint_list(substream->runtime, 0,
+                       SNDRV_PCM_HW_PARAM_RATE,
+                       &constraints_48000);
+}
+
+static struct snd_soc_ops cht_aif1_ops = {
+       .startup = cht_aif1_startup,
+};
+
+static struct snd_soc_ops cht_be_ssp2_ops = {
+       .hw_params = cht_aif1_hw_params,
+};
+
+static struct snd_soc_dai_link cht_dailink[] = {
+       /* Front End DAI links */
+       [MERR_DPCM_AUDIO] = {
+               .name = "Audio Port",
+               .stream_name = "Audio",
+               .cpu_dai_name = "media-cpu-dai",
+               .codec_dai_name = "snd-soc-dummy-dai",
+               .codec_name = "snd-soc-dummy",
+               .platform_name = "sst-mfld-platform",
+               .ignore_suspend = 1,
+               .dynamic = 1,
+               .dpcm_playback = 1,
+               .dpcm_capture = 1,
+               .ops = &cht_aif1_ops,
+       },
+       [MERR_DPCM_COMPR] = {
+               .name = "Compressed Port",
+               .stream_name = "Compress",
+               .cpu_dai_name = "compress-cpu-dai",
+               .codec_dai_name = "snd-soc-dummy-dai",
+               .codec_name = "snd-soc-dummy",
+               .platform_name = "sst-mfld-platform",
+       },
+
+       /* Back End DAI links */
+       {
+               /* SSP2 - Codec */
+               .name = "SSP2-Codec",
+               .be_id = 1,
+               .cpu_dai_name = "ssp2-port",
+               .platform_name = "sst-mfld-platform",
+               .no_pcm = 1,
+               .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,
+               .ignore_suspend = 1,
+               .dpcm_playback = 1,
+               .dpcm_capture = 1,
+               .ops = &cht_be_ssp2_ops,
+       },
+};
+
+/* SoC card */
+static struct snd_soc_card snd_soc_card_cht = {
+       .name = "cherrytrailcraudio",
+       .dai_link = cht_dailink,
+       .num_links = ARRAY_SIZE(cht_dailink),
+       .dapm_widgets = cht_dapm_widgets,
+       .num_dapm_widgets = ARRAY_SIZE(cht_dapm_widgets),
+       .dapm_routes = cht_audio_map,
+       .num_dapm_routes = ARRAY_SIZE(cht_audio_map),
+       .controls = cht_mc_controls,
+       .num_controls = ARRAY_SIZE(cht_mc_controls),
+};
+
+static int snd_cht_mc_probe(struct platform_device *pdev)
+{
+       int ret_val = 0;
+
+       /* register the soc card */
+       snd_soc_card_cht.dev = &pdev->dev;
+       ret_val = devm_snd_soc_register_card(&pdev->dev, &snd_soc_card_cht);
+       if (ret_val) {
+               dev_err(&pdev->dev,
+                       "snd_soc_register_card failed %d\n", ret_val);
+               return ret_val;
+       }
+       platform_set_drvdata(pdev, &snd_soc_card_cht);
+       return ret_val;
+}
+
+static struct platform_driver snd_cht_mc_driver = {
+       .driver = {
+               .owner = THIS_MODULE,
+               .name = "cht-bsw-rt5672",
+               .pm = &snd_soc_pm_ops,
+       },
+       .probe = snd_cht_mc_probe,
+};
+
+module_platform_driver(snd_cht_mc_driver);
+
+MODULE_DESCRIPTION("ASoC Intel(R) Baytrail CR Machine driver");
+MODULE_AUTHOR("Subhransu S. Prusty, Mengdong Lin");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:cht-bsw-rt5672");
index 3981982..cb8a482 100644 (file)
@@ -109,7 +109,7 @@ static struct snd_soc_dai_link haswell_rt5640_dais[] = {
        /* Front End DAI links */
        {
                .name = "System",
-               .stream_name = "System Playback",
+               .stream_name = "System Playback/Capture",
                .cpu_dai_name = "System Pin",
                .platform_name = "haswell-pcm-audio",
                .dynamic = 1,
@@ -118,6 +118,7 @@ static struct snd_soc_dai_link haswell_rt5640_dais[] = {
                .init = haswell_rtd_init,
                .trigger = {SND_SOC_DPCM_TRIGGER_POST, SND_SOC_DPCM_TRIGGER_POST},
                .dpcm_playback = 1,
+               .dpcm_capture = 1,
        },
        {
                .name = "Offload0",
@@ -152,17 +153,6 @@ static struct snd_soc_dai_link haswell_rt5640_dais[] = {
                .trigger = {SND_SOC_DPCM_TRIGGER_POST, SND_SOC_DPCM_TRIGGER_POST},
                .dpcm_capture = 1,
        },
-       {
-               .name = "Capture",
-               .stream_name = "Capture",
-               .cpu_dai_name = "Capture Pin",
-               .platform_name = "haswell-pcm-audio",
-               .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,
-       },
 
        /* Back End DAI links */
        {
index 7104a34..90aa5c0 100644 (file)
@@ -15,6 +15,9 @@
  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  *  General Public License for more details.
  *
+ *  In the dpcm driver modelling when a particular FE/BE/Mixer/Pipe is active
+ *  we forward the settings and parameters, rest we keep the values  in
+ *  driver and forward when DAPM enables them
  * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  */
 #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
@@ -81,6 +84,183 @@ static int sst_fill_and_send_cmd(struct sst_data *drv,
        return ret;
 }
 
+/**
+ * tx map value is a bitfield where each bit represents a FW channel
+ *
+ *                     3 2 1 0         # 0 = codec0, 1 = codec1
+ *                     RLRLRLRL        # 3, 4 = reserved
+ *
+ * e.g. slot 0 rx map =        00001100b -> data from slot 0 goes into codec_in1 L,R
+ */
+static u8 sst_ssp_tx_map[SST_MAX_TDM_SLOTS] = {
+       0x1, 0x2, 0x4, 0x8, 0x10, 0x20, 0x40, 0x80, /* default rx map */
+};
+
+/**
+ * rx map value is a bitfield where each bit represents a slot
+ *
+ *                       76543210      # 0 = slot 0, 1 = slot 1
+ *
+ * e.g. codec1_0 tx map = 00000101b -> data from codec_out1_0 goes into slot 0, 2
+ */
+static u8 sst_ssp_rx_map[SST_MAX_TDM_SLOTS] = {
+       0x1, 0x2, 0x4, 0x8, 0x10, 0x20, 0x40, 0x80, /* default tx map */
+};
+
+/**
+ * NOTE: this is invoked with lock held
+ */
+static int sst_send_slot_map(struct sst_data *drv)
+{
+       struct sst_param_sba_ssp_slot_map cmd;
+
+       SST_FILL_DEFAULT_DESTINATION(cmd.header.dst);
+       cmd.header.command_id = SBA_SET_SSP_SLOT_MAP;
+       cmd.header.length = sizeof(struct sst_param_sba_ssp_slot_map)
+                               - sizeof(struct sst_dsp_header);
+
+       cmd.param_id = SBA_SET_SSP_SLOT_MAP;
+       cmd.param_len = sizeof(cmd.rx_slot_map) + sizeof(cmd.tx_slot_map)
+                                       + sizeof(cmd.ssp_index);
+       cmd.ssp_index = SSP_CODEC;
+
+       memcpy(cmd.rx_slot_map, &sst_ssp_tx_map[0], sizeof(cmd.rx_slot_map));
+       memcpy(cmd.tx_slot_map, &sst_ssp_rx_map[0], sizeof(cmd.tx_slot_map));
+
+       return sst_fill_and_send_cmd_unlocked(drv, SST_IPC_IA_SET_PARAMS,
+                       SST_FLAG_BLOCKED, SST_TASK_SBA, 0, &cmd,
+                             sizeof(cmd.header) + cmd.header.length);
+}
+
+int sst_slot_enum_info(struct snd_kcontrol *kcontrol,
+                      struct snd_ctl_elem_info *uinfo)
+{
+       struct sst_enum *e = (struct sst_enum *)kcontrol->private_value;
+
+       uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
+       uinfo->count = 1;
+       uinfo->value.enumerated.items = e->max;
+
+       if (uinfo->value.enumerated.item > e->max - 1)
+               uinfo->value.enumerated.item = e->max - 1;
+       strcpy(uinfo->value.enumerated.name,
+               e->texts[uinfo->value.enumerated.item]);
+
+       return 0;
+}
+
+/**
+ * sst_slot_get - get the status of the interleaver/deinterleaver control
+ *
+ * Searches the map where the control status is stored, and gets the
+ * channel/slot which is currently set for this enumerated control. Since it is
+ * an enumerated control, there is only one possible value.
+ */
+static int sst_slot_get(struct snd_kcontrol *kcontrol,
+                       struct snd_ctl_elem_value *ucontrol)
+{
+       struct sst_enum *e = (void *)kcontrol->private_value;
+       struct snd_soc_component *c = snd_kcontrol_chip(kcontrol);
+       struct sst_data *drv = snd_soc_component_get_drvdata(c);
+       unsigned int ctl_no = e->reg;
+       unsigned int is_tx = e->tx;
+       unsigned int val, mux;
+       u8 *map = is_tx ? sst_ssp_rx_map : sst_ssp_tx_map;
+
+       mutex_lock(&drv->lock);
+       val = 1 << ctl_no;
+       /* search which slot/channel has this bit set - there should be only one */
+       for (mux = e->max; mux > 0;  mux--)
+               if (map[mux - 1] & val)
+                       break;
+
+       ucontrol->value.enumerated.item[0] = mux;
+       mutex_unlock(&drv->lock);
+
+       dev_dbg(c->dev, "%s - %s map = %#x\n",
+                       is_tx ? "tx channel" : "rx slot",
+                        e->texts[mux], mux ? map[mux - 1] : -1);
+       return 0;
+}
+
+/* sst_check_and_send_slot_map - helper for checking power state and sending
+ * slot map cmd
+ *
+ * called with lock held
+ */
+static int sst_check_and_send_slot_map(struct sst_data *drv, struct snd_kcontrol *kcontrol)
+{
+       struct sst_enum *e = (void *)kcontrol->private_value;
+       int ret = 0;
+
+       if (e->w && e->w->power)
+               ret = sst_send_slot_map(drv);
+       else
+               dev_err(&drv->pdev->dev, "Slot control: %s doesn't have DAPM widget!!!\n",
+                               kcontrol->id.name);
+       return ret;
+}
+
+/**
+ * sst_slot_put - set the status of interleaver/deinterleaver control
+ *
+ * (de)interleaver controls are defined in opposite sense to be user-friendly
+ *
+ * Instead of the enum value being the value written to the register, it is the
+ * register address; and the kcontrol number (register num) is the value written
+ * to the register. This is so that there can be only one value for each
+ * slot/channel since there is only one control for each slot/channel.
+ *
+ * This means that whenever an enum is set, we need to clear the bit
+ * for that kcontrol_no for all the interleaver OR deinterleaver registers
+ */
+static int sst_slot_put(struct snd_kcontrol *kcontrol,
+                       struct snd_ctl_elem_value *ucontrol)
+{
+       struct snd_soc_component *c = snd_soc_kcontrol_component(kcontrol);
+       struct sst_data *drv = snd_soc_component_get_drvdata(c);
+       struct sst_enum *e = (void *)kcontrol->private_value;
+       int i, ret = 0;
+       unsigned int ctl_no = e->reg;
+       unsigned int is_tx = e->tx;
+       unsigned int slot_channel_no;
+       unsigned int val, mux;
+       u8 *map;
+
+       map = is_tx ? sst_ssp_rx_map : sst_ssp_tx_map;
+
+       val = 1 << ctl_no;
+       mux = ucontrol->value.enumerated.item[0];
+       if (mux > e->max - 1)
+               return -EINVAL;
+
+       mutex_lock(&drv->lock);
+       /* first clear all registers of this bit */
+       for (i = 0; i < e->max; i++)
+               map[i] &= ~val;
+
+       if (mux == 0) {
+               /* kctl set to 'none' and we reset the bits so send IPC */
+               ret = sst_check_and_send_slot_map(drv, kcontrol);
+
+               mutex_unlock(&drv->lock);
+               return ret;
+       }
+
+       /* offset by one to take "None" into account */
+       slot_channel_no = mux - 1;
+       map[slot_channel_no] |= val;
+
+       dev_dbg(c->dev, "%s %s map = %#x\n",
+                       is_tx ? "tx channel" : "rx slot",
+                       e->texts[mux], map[slot_channel_no]);
+
+       ret = sst_check_and_send_slot_map(drv, kcontrol);
+
+       mutex_unlock(&drv->lock);
+       return ret;
+}
+
 static int sst_send_algo_cmd(struct sst_data *drv,
                              struct sst_algo_control *bc)
 {
@@ -104,6 +284,34 @@ static int sst_send_algo_cmd(struct sst_data *drv,
        return ret;
 }
 
+/**
+ * sst_find_and_send_pipe_algo - send all the algo parameters for a pipe
+ *
+ * The algos which are in each pipeline are sent to the firmware one by one
+ *
+ * Called with lock held
+ */
+static int sst_find_and_send_pipe_algo(struct sst_data *drv,
+                                       const char *pipe, struct sst_ids *ids)
+{
+       int ret = 0;
+       struct sst_algo_control *bc;
+       struct sst_module *algo = NULL;
+
+       dev_dbg(&drv->pdev->dev, "Enter: widget=%s\n", pipe);
+
+       list_for_each_entry(algo, &ids->algo_list, node) {
+               bc = (void *)algo->kctl->private_value;
+
+               dev_dbg(&drv->pdev->dev, "Found algo control name=%s pipe=%s\n",
+                               algo->kctl->id.name, pipe);
+               ret = sst_send_algo_cmd(drv, bc);
+               if (ret)
+                       return ret;
+       }
+       return ret;
+}
+
 static int sst_algo_bytes_ctl_info(struct snd_kcontrol *kcontrol,
                            struct snd_ctl_elem_info *uinfo)
 {
@@ -162,6 +370,743 @@ static int sst_algo_control_set(struct snd_kcontrol *kcontrol,
        return ret;
 }
 
+static int sst_gain_ctl_info(struct snd_kcontrol *kcontrol,
+       struct snd_ctl_elem_info *uinfo)
+{
+       struct sst_gain_mixer_control *mc = (void *)kcontrol->private_value;
+
+       uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+       uinfo->count = mc->stereo ? 2 : 1;
+       uinfo->value.integer.min = mc->min;
+       uinfo->value.integer.max = mc->max;
+
+       return 0;
+}
+
+/**
+ * sst_send_gain_cmd - send the gain algorithm IPC to the FW
+ * @gv:                the stored value of gain (also contains rampduration)
+ * @mute:      flag that indicates whether this was called from the
+ *             digital_mute callback or directly. If called from the
+ *             digital_mute callback, module will be muted/unmuted based on this
+ *             flag. The flag is always 0 if called directly.
+ *
+ * Called with sst_data.lock held
+ *
+ * The user-set gain value is sent only if the user-controllable 'mute' control
+ * is OFF (indicated by gv->mute). Otherwise, the mute value (MIN value) is
+ * sent.
+ */
+static int sst_send_gain_cmd(struct sst_data *drv, struct sst_gain_value *gv,
+                             u16 task_id, u16 loc_id, u16 module_id, int mute)
+{
+       struct sst_cmd_set_gain_dual cmd;
+
+       dev_dbg(&drv->pdev->dev, "Enter\n");
+
+       cmd.header.command_id = MMX_SET_GAIN;
+       SST_FILL_DEFAULT_DESTINATION(cmd.header.dst);
+       cmd.gain_cell_num = 1;
+
+       if (mute || gv->mute) {
+               cmd.cell_gains[0].cell_gain_left = SST_GAIN_MIN_VALUE;
+               cmd.cell_gains[0].cell_gain_right = SST_GAIN_MIN_VALUE;
+       } else {
+               cmd.cell_gains[0].cell_gain_left = gv->l_gain;
+               cmd.cell_gains[0].cell_gain_right = gv->r_gain;
+       }
+
+       SST_FILL_DESTINATION(2, cmd.cell_gains[0].dest,
+                            loc_id, module_id);
+       cmd.cell_gains[0].gain_time_constant = gv->ramp_duration;
+
+       cmd.header.length = sizeof(struct sst_cmd_set_gain_dual)
+                               - sizeof(struct sst_dsp_header);
+
+       /* we are with lock held, so call the unlocked api  to send */
+       return sst_fill_and_send_cmd_unlocked(drv, SST_IPC_IA_SET_PARAMS,
+                               SST_FLAG_BLOCKED, task_id, 0, &cmd,
+                             sizeof(cmd.header) + cmd.header.length);
+}
+
+static int sst_gain_get(struct snd_kcontrol *kcontrol,
+                       struct snd_ctl_elem_value *ucontrol)
+{
+       struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+       struct sst_gain_mixer_control *mc = (void *)kcontrol->private_value;
+       struct sst_gain_value *gv = mc->gain_val;
+
+       switch (mc->type) {
+       case SST_GAIN_TLV:
+               ucontrol->value.integer.value[0] = gv->l_gain;
+               ucontrol->value.integer.value[1] = gv->r_gain;
+               break;
+
+       case SST_GAIN_MUTE:
+               ucontrol->value.integer.value[0] = gv->mute ? 1 : 0;
+               break;
+
+       case SST_GAIN_RAMP_DURATION:
+               ucontrol->value.integer.value[0] = gv->ramp_duration;
+               break;
+
+       default:
+               dev_err(component->dev, "Invalid Input- gain type:%d\n",
+                               mc->type);
+               return -EINVAL;
+       }
+
+       return 0;
+}
+
+static int sst_gain_put(struct snd_kcontrol *kcontrol,
+                       struct snd_ctl_elem_value *ucontrol)
+{
+       int ret = 0;
+       struct snd_soc_component *cmpnt = snd_soc_kcontrol_component(kcontrol);
+       struct sst_data *drv = snd_soc_component_get_drvdata(cmpnt);
+       struct sst_gain_mixer_control *mc = (void *)kcontrol->private_value;
+       struct sst_gain_value *gv = mc->gain_val;
+
+       mutex_lock(&drv->lock);
+
+       switch (mc->type) {
+       case SST_GAIN_TLV:
+               gv->l_gain = ucontrol->value.integer.value[0];
+               gv->r_gain = ucontrol->value.integer.value[1];
+               dev_dbg(cmpnt->dev, "%s: Volume %d, %d\n",
+                               mc->pname, gv->l_gain, gv->r_gain);
+               break;
+
+       case SST_GAIN_MUTE:
+               gv->mute = !!ucontrol->value.integer.value[0];
+               dev_dbg(cmpnt->dev, "%s: Mute %d\n", mc->pname, gv->mute);
+               break;
+
+       case SST_GAIN_RAMP_DURATION:
+               gv->ramp_duration = ucontrol->value.integer.value[0];
+               dev_dbg(cmpnt->dev, "%s: Ramp Delay%d\n",
+                                       mc->pname, gv->ramp_duration);
+               break;
+
+       default:
+               mutex_unlock(&drv->lock);
+               dev_err(cmpnt->dev, "Invalid Input- gain type:%d\n",
+                               mc->type);
+               return -EINVAL;
+       }
+
+       if (mc->w && mc->w->power)
+               ret = sst_send_gain_cmd(drv, gv, mc->task_id,
+                       mc->pipe_id | mc->instance_id, mc->module_id, 0);
+       mutex_unlock(&drv->lock);
+
+       return ret;
+}
+
+static int sst_set_pipe_gain(struct sst_ids *ids,
+                               struct sst_data *drv, int mute);
+
+static int sst_send_pipe_module_params(struct snd_soc_dapm_widget *w,
+               struct snd_kcontrol *kcontrol)
+{
+       struct snd_soc_component *c = snd_soc_dapm_to_component(w->dapm);
+       struct sst_data *drv = snd_soc_component_get_drvdata(c);
+       struct sst_ids *ids = w->priv;
+
+       mutex_lock(&drv->lock);
+       sst_find_and_send_pipe_algo(drv, w->name, ids);
+       sst_set_pipe_gain(ids, drv, 0);
+       mutex_unlock(&drv->lock);
+
+       return 0;
+}
+
+static int sst_generic_modules_event(struct snd_soc_dapm_widget *w,
+                                    struct snd_kcontrol *k, int event)
+{
+       if (SND_SOC_DAPM_EVENT_ON(event))
+               return sst_send_pipe_module_params(w, k);
+       return 0;
+}
+
+static const DECLARE_TLV_DB_SCALE(sst_gain_tlv_common, SST_GAIN_MIN_VALUE * 10, 10, 0);
+
+/* Look up table to convert MIXER SW bit regs to SWM inputs */
+static const uint swm_mixer_input_ids[SST_SWM_INPUT_COUNT] = {
+       [SST_IP_CODEC0]         = SST_SWM_IN_CODEC0,
+       [SST_IP_CODEC1]         = SST_SWM_IN_CODEC1,
+       [SST_IP_LOOP0]          = SST_SWM_IN_SPROT_LOOP,
+       [SST_IP_LOOP1]          = SST_SWM_IN_MEDIA_LOOP1,
+       [SST_IP_LOOP2]          = SST_SWM_IN_MEDIA_LOOP2,
+       [SST_IP_PCM0]           = SST_SWM_IN_PCM0,
+       [SST_IP_PCM1]           = SST_SWM_IN_PCM1,
+       [SST_IP_MEDIA0]         = SST_SWM_IN_MEDIA0,
+       [SST_IP_MEDIA1]         = SST_SWM_IN_MEDIA1,
+       [SST_IP_MEDIA2]         = SST_SWM_IN_MEDIA2,
+       [SST_IP_MEDIA3]         = SST_SWM_IN_MEDIA3,
+};
+
+/**
+ * fill_swm_input - fill in the SWM input ids given the register
+ *
+ * The register value is a bit-field inicated which mixer inputs are ON. Use the
+ * lookup table to get the input-id and fill it in the structure.
+ */
+static int fill_swm_input(struct snd_soc_component *cmpnt,
+               struct swm_input_ids *swm_input, unsigned int reg)
+{
+       uint i, is_set, nb_inputs = 0;
+       u16 input_loc_id;
+
+       dev_dbg(cmpnt->dev, "reg: %#x\n", reg);
+       for (i = 0; i < SST_SWM_INPUT_COUNT; i++) {
+               is_set = reg & BIT(i);
+               if (!is_set)
+                       continue;
+
+               input_loc_id = swm_mixer_input_ids[i];
+               SST_FILL_DESTINATION(2, swm_input->input_id,
+                                    input_loc_id, SST_DEFAULT_MODULE_ID);
+               nb_inputs++;
+               swm_input++;
+               dev_dbg(cmpnt->dev, "input id: %#x, nb_inputs: %d\n",
+                               input_loc_id, nb_inputs);
+
+               if (nb_inputs == SST_CMD_SWM_MAX_INPUTS) {
+                       dev_warn(cmpnt->dev, "SET_SWM cmd max inputs reached");
+                       break;
+               }
+       }
+       return nb_inputs;
+}
+
+
+/**
+ * called with lock held
+ */
+static int sst_set_pipe_gain(struct sst_ids *ids,
+                       struct sst_data *drv, int mute)
+{
+       int ret = 0;
+       struct sst_gain_mixer_control *mc;
+       struct sst_gain_value *gv;
+       struct sst_module *gain = NULL;
+
+       list_for_each_entry(gain, &ids->gain_list, node) {
+               struct snd_kcontrol *kctl = gain->kctl;
+
+               dev_dbg(&drv->pdev->dev, "control name=%s\n", kctl->id.name);
+               mc = (void *)kctl->private_value;
+               gv = mc->gain_val;
+
+               ret = sst_send_gain_cmd(drv, gv, mc->task_id,
+                       mc->pipe_id | mc->instance_id, mc->module_id, mute);
+               if (ret)
+                       return ret;
+       }
+       return ret;
+}
+
+static int sst_swm_mixer_event(struct snd_soc_dapm_widget *w,
+                       struct snd_kcontrol *k, int event)
+{
+       struct sst_cmd_set_swm cmd;
+       struct snd_soc_component *cmpnt = snd_soc_dapm_to_component(w->dapm);
+       struct sst_data *drv = snd_soc_component_get_drvdata(cmpnt);
+       struct sst_ids *ids = w->priv;
+       bool set_mixer = false;
+       struct soc_mixer_control *mc;
+       int val = 0;
+       int i = 0;
+
+       dev_dbg(cmpnt->dev, "widget = %s\n", w->name);
+       /*
+        * Identify which mixer input is on and send the bitmap of the
+        * inputs as an IPC to the DSP.
+        */
+       for (i = 0; i < w->num_kcontrols; i++) {
+               if (dapm_kcontrol_get_value(w->kcontrols[i])) {
+                       mc = (struct soc_mixer_control *)(w->kcontrols[i])->private_value;
+                       val |= 1 << mc->shift;
+               }
+       }
+       dev_dbg(cmpnt->dev, "val = %#x\n", val);
+
+       switch (event) {
+       case SND_SOC_DAPM_PRE_PMU:
+       case SND_SOC_DAPM_POST_PMD:
+               set_mixer = true;
+               break;
+       case SND_SOC_DAPM_POST_REG:
+               if (w->power)
+                       set_mixer = true;
+               break;
+       default:
+               set_mixer = false;
+       }
+
+       if (set_mixer == false)
+               return 0;
+
+       if (SND_SOC_DAPM_EVENT_ON(event) ||
+           event == SND_SOC_DAPM_POST_REG)
+               cmd.switch_state = SST_SWM_ON;
+       else
+               cmd.switch_state = SST_SWM_OFF;
+
+       SST_FILL_DEFAULT_DESTINATION(cmd.header.dst);
+       /* MMX_SET_SWM == SBA_SET_SWM */
+       cmd.header.command_id = SBA_SET_SWM;
+
+       SST_FILL_DESTINATION(2, cmd.output_id,
+                            ids->location_id, SST_DEFAULT_MODULE_ID);
+       cmd.nb_inputs = fill_swm_input(cmpnt, &cmd.input[0], val);
+       cmd.header.length = offsetof(struct sst_cmd_set_swm, input)
+                               - sizeof(struct sst_dsp_header)
+                               + (cmd.nb_inputs * sizeof(cmd.input[0]));
+
+       return sst_fill_and_send_cmd(drv, SST_IPC_IA_CMD, SST_FLAG_BLOCKED,
+                             ids->task_id, 0, &cmd,
+                             sizeof(cmd.header) + cmd.header.length);
+}
+
+/* SBA mixers - 16 inputs */
+#define SST_SBA_DECLARE_MIX_CONTROLS(kctl_name)                                                        \
+       static const struct snd_kcontrol_new kctl_name[] = {                                    \
+               SOC_DAPM_SINGLE("codec_in0 Switch", SND_SOC_NOPM, SST_IP_CODEC0, 1, 0),         \
+               SOC_DAPM_SINGLE("codec_in1 Switch", SND_SOC_NOPM, SST_IP_CODEC1, 1, 0),         \
+               SOC_DAPM_SINGLE("sprot_loop_in Switch", SND_SOC_NOPM, SST_IP_LOOP0, 1, 0),      \
+               SOC_DAPM_SINGLE("media_loop1_in Switch", SND_SOC_NOPM, SST_IP_LOOP1, 1, 0),     \
+               SOC_DAPM_SINGLE("media_loop2_in Switch", SND_SOC_NOPM, SST_IP_LOOP2, 1, 0),     \
+               SOC_DAPM_SINGLE("pcm0_in Switch", SND_SOC_NOPM, SST_IP_PCM0, 1, 0),             \
+               SOC_DAPM_SINGLE("pcm1_in Switch", SND_SOC_NOPM, SST_IP_PCM1, 1, 0),             \
+       }
+
+#define SST_SBA_MIXER_GRAPH_MAP(mix_name)                      \
+       { mix_name, "codec_in0 Switch", "codec_in0" },          \
+       { mix_name, "codec_in1 Switch", "codec_in1" },          \
+       { mix_name, "sprot_loop_in Switch",     "sprot_loop_in" },      \
+       { mix_name, "media_loop1_in Switch",    "media_loop1_in" },     \
+       { mix_name, "media_loop2_in Switch",    "media_loop2_in" },     \
+       { mix_name, "pcm0_in Switch",           "pcm0_in" },            \
+       { mix_name, "pcm1_in Switch",           "pcm1_in" }
+
+#define SST_MMX_DECLARE_MIX_CONTROLS(kctl_name)                                                \
+       static const struct snd_kcontrol_new kctl_name[] = {                            \
+               SOC_DAPM_SINGLE("media0_in Switch", SND_SOC_NOPM, SST_IP_MEDIA0, 1, 0), \
+               SOC_DAPM_SINGLE("media1_in Switch", SND_SOC_NOPM, SST_IP_MEDIA1, 1, 0), \
+               SOC_DAPM_SINGLE("media2_in Switch", SND_SOC_NOPM, SST_IP_MEDIA2, 1, 0), \
+               SOC_DAPM_SINGLE("media3_in Switch", SND_SOC_NOPM, SST_IP_MEDIA3, 1, 0), \
+       }
+
+SST_MMX_DECLARE_MIX_CONTROLS(sst_mix_media0_controls);
+SST_MMX_DECLARE_MIX_CONTROLS(sst_mix_media1_controls);
+
+/* 18 SBA mixers */
+SST_SBA_DECLARE_MIX_CONTROLS(sst_mix_pcm0_controls);
+SST_SBA_DECLARE_MIX_CONTROLS(sst_mix_pcm1_controls);
+SST_SBA_DECLARE_MIX_CONTROLS(sst_mix_pcm2_controls);
+SST_SBA_DECLARE_MIX_CONTROLS(sst_mix_sprot_l0_controls);
+SST_SBA_DECLARE_MIX_CONTROLS(sst_mix_media_l1_controls);
+SST_SBA_DECLARE_MIX_CONTROLS(sst_mix_media_l2_controls);
+SST_SBA_DECLARE_MIX_CONTROLS(sst_mix_voip_controls);
+SST_SBA_DECLARE_MIX_CONTROLS(sst_mix_codec0_controls);
+SST_SBA_DECLARE_MIX_CONTROLS(sst_mix_codec1_controls);
+
+/*
+ * sst_handle_vb_timer - Start/Stop the DSP scheduler
+ *
+ * The DSP expects first cmd to be SBA_VB_START, so at first startup send
+ * that.
+ * DSP expects last cmd to be SBA_VB_IDLE, so at last shutdown send that.
+ *
+ * Do refcount internally so that we send command only at first start
+ * and last end. Since SST driver does its own ref count, invoke sst's
+ * power ops always!
+ */
+int sst_handle_vb_timer(struct snd_soc_dai *dai, bool enable)
+{
+       int ret = 0;
+       struct sst_cmd_generic cmd;
+       struct sst_data *drv = snd_soc_dai_get_drvdata(dai);
+       static int timer_usage;
+
+       if (enable)
+               cmd.header.command_id = SBA_VB_START;
+       else
+               cmd.header.command_id = SBA_IDLE;
+       dev_dbg(dai->dev, "enable=%u, usage=%d\n", enable, timer_usage);
+
+       SST_FILL_DEFAULT_DESTINATION(cmd.header.dst);
+       cmd.header.length = 0;
+
+       if (enable) {
+               ret = sst->ops->power(sst->dev, true);
+               if (ret < 0)
+                       return ret;
+       }
+
+       mutex_lock(&drv->lock);
+       if (enable)
+               timer_usage++;
+       else
+               timer_usage--;
+
+       /*
+        * Send the command only if this call is the first enable or last
+        * disable
+        */
+       if ((enable && (timer_usage == 1)) ||
+           (!enable && (timer_usage == 0))) {
+               ret = sst_fill_and_send_cmd_unlocked(drv, SST_IPC_IA_CMD,
+                               SST_FLAG_BLOCKED, SST_TASK_SBA, 0, &cmd,
+                               sizeof(cmd.header) + cmd.header.length);
+               if (ret && enable) {
+                       timer_usage--;
+                       enable  = false;
+               }
+       }
+       mutex_unlock(&drv->lock);
+
+       if (!enable)
+               sst->ops->power(sst->dev, false);
+       return ret;
+}
+
+/**
+ * sst_ssp_config - contains SSP configuration for media UC
+ */
+static const struct sst_ssp_config sst_ssp_configs = {
+       .ssp_id = SSP_CODEC,
+       .bits_per_slot = 24,
+       .slots = 4,
+       .ssp_mode = SSP_MODE_MASTER,
+       .pcm_mode = SSP_PCM_MODE_NETWORK,
+       .duplex = SSP_DUPLEX,
+       .ssp_protocol = SSP_MODE_PCM,
+       .fs_width = 1,
+       .fs_frequency = SSP_FS_48_KHZ,
+       .active_slot_map = 0xF,
+       .start_delay = 0,
+};
+
+int send_ssp_cmd(struct snd_soc_dai *dai, const char *id, bool enable)
+{
+       struct sst_cmd_sba_hw_set_ssp cmd;
+       struct sst_data *drv = snd_soc_dai_get_drvdata(dai);
+       const struct sst_ssp_config *config;
+
+       dev_info(dai->dev, "Enter: enable=%d port_name=%s\n", enable, id);
+
+       SST_FILL_DEFAULT_DESTINATION(cmd.header.dst);
+       cmd.header.command_id = SBA_HW_SET_SSP;
+       cmd.header.length = sizeof(struct sst_cmd_sba_hw_set_ssp)
+                               - sizeof(struct sst_dsp_header);
+
+       config = &sst_ssp_configs;
+       dev_dbg(dai->dev, "ssp_id: %u\n", config->ssp_id);
+
+       if (enable)
+               cmd.switch_state = SST_SWITCH_ON;
+       else
+               cmd.switch_state = SST_SWITCH_OFF;
+
+       cmd.selection = config->ssp_id;
+       cmd.nb_bits_per_slots = config->bits_per_slot;
+       cmd.nb_slots = config->slots;
+       cmd.mode = config->ssp_mode | (config->pcm_mode << 1);
+       cmd.duplex = config->duplex;
+       cmd.active_tx_slot_map = config->active_slot_map;
+       cmd.active_rx_slot_map = config->active_slot_map;
+       cmd.frame_sync_frequency = config->fs_frequency;
+       cmd.frame_sync_polarity = SSP_FS_ACTIVE_HIGH;
+       cmd.data_polarity = 1;
+       cmd.frame_sync_width = config->fs_width;
+       cmd.ssp_protocol = config->ssp_protocol;
+       cmd.start_delay = config->start_delay;
+       cmd.reserved1 = cmd.reserved2 = 0xFF;
+
+       return sst_fill_and_send_cmd(drv, SST_IPC_IA_CMD, SST_FLAG_BLOCKED,
+                               SST_TASK_SBA, 0, &cmd,
+                               sizeof(cmd.header) + cmd.header.length);
+}
+
+static int sst_set_be_modules(struct snd_soc_dapm_widget *w,
+                        struct snd_kcontrol *k, int event)
+{
+       int ret = 0;
+       struct snd_soc_component *c = snd_soc_dapm_to_component(w->dapm);
+       struct sst_data *drv = snd_soc_component_get_drvdata(c);
+
+       dev_dbg(c->dev, "Enter: widget=%s\n", w->name);
+
+       if (SND_SOC_DAPM_EVENT_ON(event)) {
+               ret = sst_send_slot_map(drv);
+               if (ret)
+                       return ret;
+               ret = sst_send_pipe_module_params(w, k);
+       }
+       return ret;
+}
+
+static int sst_set_media_path(struct snd_soc_dapm_widget *w,
+                             struct snd_kcontrol *k, int event)
+{
+       int ret = 0;
+       struct sst_cmd_set_media_path cmd;
+       struct snd_soc_component *c = snd_soc_dapm_to_component(w->dapm);
+       struct sst_data *drv = snd_soc_component_get_drvdata(c);
+       struct sst_ids *ids = w->priv;
+
+       dev_dbg(c->dev, "widget=%s\n", w->name);
+       dev_dbg(c->dev, "task=%u, location=%#x\n",
+                               ids->task_id, ids->location_id);
+
+       if (SND_SOC_DAPM_EVENT_ON(event))
+               cmd.switch_state = SST_PATH_ON;
+       else
+               cmd.switch_state = SST_PATH_OFF;
+
+       SST_FILL_DESTINATION(2, cmd.header.dst,
+                            ids->location_id, SST_DEFAULT_MODULE_ID);
+
+       /* MMX_SET_MEDIA_PATH == SBA_SET_MEDIA_PATH */
+       cmd.header.command_id = MMX_SET_MEDIA_PATH;
+       cmd.header.length = sizeof(struct sst_cmd_set_media_path)
+                               - sizeof(struct sst_dsp_header);
+
+       ret = sst_fill_and_send_cmd(drv, SST_IPC_IA_CMD, SST_FLAG_BLOCKED,
+                             ids->task_id, 0, &cmd,
+                             sizeof(cmd.header) + cmd.header.length);
+       if (ret)
+               return ret;
+
+       if (SND_SOC_DAPM_EVENT_ON(event))
+               ret = sst_send_pipe_module_params(w, k);
+       return ret;
+}
+
+static int sst_set_media_loop(struct snd_soc_dapm_widget *w,
+                       struct snd_kcontrol *k, int event)
+{
+       int ret = 0;
+       struct sst_cmd_sba_set_media_loop_map cmd;
+       struct snd_soc_component *c = snd_soc_dapm_to_component(w->dapm);
+       struct sst_data *drv = snd_soc_component_get_drvdata(c);
+       struct sst_ids *ids = w->priv;
+
+       dev_dbg(c->dev, "Enter:widget=%s\n", w->name);
+       if (SND_SOC_DAPM_EVENT_ON(event))
+               cmd.switch_state = SST_SWITCH_ON;
+       else
+               cmd.switch_state = SST_SWITCH_OFF;
+
+       SST_FILL_DESTINATION(2, cmd.header.dst,
+                            ids->location_id, SST_DEFAULT_MODULE_ID);
+
+       cmd.header.command_id = SBA_SET_MEDIA_LOOP_MAP;
+       cmd.header.length = sizeof(struct sst_cmd_sba_set_media_loop_map)
+                                - sizeof(struct sst_dsp_header);
+       cmd.param.part.cfg.rate = 2; /* 48khz */
+
+       cmd.param.part.cfg.format = ids->format; /* stereo/Mono */
+       cmd.param.part.cfg.s_length = 1; /* 24bit left justified */
+       cmd.map = 0; /* Algo sequence: Gain - DRP - FIR - IIR */
+
+       ret = sst_fill_and_send_cmd(drv, SST_IPC_IA_CMD, SST_FLAG_BLOCKED,
+                             SST_TASK_SBA, 0, &cmd,
+                             sizeof(cmd.header) + cmd.header.length);
+       if (ret)
+               return ret;
+
+       if (SND_SOC_DAPM_EVENT_ON(event))
+               ret = sst_send_pipe_module_params(w, k);
+       return ret;
+}
+
+static const struct snd_soc_dapm_widget sst_dapm_widgets[] = {
+       SST_AIF_IN("codec_in0", sst_set_be_modules),
+       SST_AIF_IN("codec_in1", sst_set_be_modules),
+       SST_AIF_OUT("codec_out0", sst_set_be_modules),
+       SST_AIF_OUT("codec_out1", sst_set_be_modules),
+
+       /* Media Paths */
+       /* MediaX IN paths are set via ALLOC, so no SET_MEDIA_PATH command */
+       SST_PATH_INPUT("media0_in", SST_TASK_MMX, SST_SWM_IN_MEDIA0, sst_generic_modules_event),
+       SST_PATH_INPUT("media1_in", SST_TASK_MMX, SST_SWM_IN_MEDIA1, NULL),
+       SST_PATH_INPUT("media2_in", SST_TASK_MMX, SST_SWM_IN_MEDIA2, sst_set_media_path),
+       SST_PATH_INPUT("media3_in", SST_TASK_MMX, SST_SWM_IN_MEDIA3, NULL),
+       SST_PATH_OUTPUT("media0_out", SST_TASK_MMX, SST_SWM_OUT_MEDIA0, sst_set_media_path),
+       SST_PATH_OUTPUT("media1_out", SST_TASK_MMX, SST_SWM_OUT_MEDIA1, sst_set_media_path),
+
+       /* SBA PCM Paths */
+       SST_PATH_INPUT("pcm0_in", SST_TASK_SBA, SST_SWM_IN_PCM0, sst_set_media_path),
+       SST_PATH_INPUT("pcm1_in", SST_TASK_SBA, SST_SWM_IN_PCM1, sst_set_media_path),
+       SST_PATH_OUTPUT("pcm0_out", SST_TASK_SBA, SST_SWM_OUT_PCM0, sst_set_media_path),
+       SST_PATH_OUTPUT("pcm1_out", SST_TASK_SBA, SST_SWM_OUT_PCM1, sst_set_media_path),
+       SST_PATH_OUTPUT("pcm2_out", SST_TASK_SBA, SST_SWM_OUT_PCM2, sst_set_media_path),
+
+       /* SBA Loops */
+       SST_PATH_INPUT("sprot_loop_in", SST_TASK_SBA, SST_SWM_IN_SPROT_LOOP, NULL),
+       SST_PATH_INPUT("media_loop1_in", SST_TASK_SBA, SST_SWM_IN_MEDIA_LOOP1, NULL),
+       SST_PATH_INPUT("media_loop2_in", SST_TASK_SBA, SST_SWM_IN_MEDIA_LOOP2, NULL),
+       SST_PATH_MEDIA_LOOP_OUTPUT("sprot_loop_out", SST_TASK_SBA, SST_SWM_OUT_SPROT_LOOP, SST_FMT_MONO, sst_set_media_loop),
+       SST_PATH_MEDIA_LOOP_OUTPUT("media_loop1_out", SST_TASK_SBA, SST_SWM_OUT_MEDIA_LOOP1, SST_FMT_MONO, sst_set_media_loop),
+       SST_PATH_MEDIA_LOOP_OUTPUT("media_loop2_out", SST_TASK_SBA, SST_SWM_OUT_MEDIA_LOOP2, SST_FMT_STEREO, sst_set_media_loop),
+
+       /* Media Mixers */
+       SST_SWM_MIXER("media0_out mix 0", SND_SOC_NOPM, SST_TASK_MMX, SST_SWM_OUT_MEDIA0,
+                     sst_mix_media0_controls, sst_swm_mixer_event),
+       SST_SWM_MIXER("media1_out mix 0", SND_SOC_NOPM, SST_TASK_MMX, SST_SWM_OUT_MEDIA1,
+                     sst_mix_media1_controls, sst_swm_mixer_event),
+
+       /* SBA PCM mixers */
+       SST_SWM_MIXER("pcm0_out mix 0", SND_SOC_NOPM, SST_TASK_SBA, SST_SWM_OUT_PCM0,
+                     sst_mix_pcm0_controls, sst_swm_mixer_event),
+       SST_SWM_MIXER("pcm1_out mix 0", SND_SOC_NOPM, SST_TASK_SBA, SST_SWM_OUT_PCM1,
+                     sst_mix_pcm1_controls, sst_swm_mixer_event),
+       SST_SWM_MIXER("pcm2_out mix 0", SND_SOC_NOPM, SST_TASK_SBA, SST_SWM_OUT_PCM2,
+                     sst_mix_pcm2_controls, sst_swm_mixer_event),
+
+       /* SBA Loop mixers */
+       SST_SWM_MIXER("sprot_loop_out mix 0", SND_SOC_NOPM, SST_TASK_SBA, SST_SWM_OUT_SPROT_LOOP,
+                     sst_mix_sprot_l0_controls, sst_swm_mixer_event),
+       SST_SWM_MIXER("media_loop1_out mix 0", SND_SOC_NOPM, SST_TASK_SBA, SST_SWM_OUT_MEDIA_LOOP1,
+                     sst_mix_media_l1_controls, sst_swm_mixer_event),
+       SST_SWM_MIXER("media_loop2_out mix 0", SND_SOC_NOPM, SST_TASK_SBA, SST_SWM_OUT_MEDIA_LOOP2,
+                     sst_mix_media_l2_controls, sst_swm_mixer_event),
+
+       /* SBA Backend mixers */
+       SST_SWM_MIXER("codec_out0 mix 0", SND_SOC_NOPM, SST_TASK_SBA, SST_SWM_OUT_CODEC0,
+                     sst_mix_codec0_controls, sst_swm_mixer_event),
+       SST_SWM_MIXER("codec_out1 mix 0", SND_SOC_NOPM, SST_TASK_SBA, SST_SWM_OUT_CODEC1,
+                     sst_mix_codec1_controls, sst_swm_mixer_event),
+};
+
+static const struct snd_soc_dapm_route intercon[] = {
+       {"media0_in", NULL, "Compress Playback"},
+       {"media1_in", NULL, "Headset Playback"},
+       {"media2_in", NULL, "pcm0_out"},
+
+       {"media0_out mix 0", "media0_in Switch", "media0_in"},
+       {"media0_out mix 0", "media1_in Switch", "media1_in"},
+       {"media0_out mix 0", "media2_in Switch", "media2_in"},
+       {"media0_out mix 0", "media3_in Switch", "media3_in"},
+       {"media1_out mix 0", "media0_in Switch", "media0_in"},
+       {"media1_out mix 0", "media1_in Switch", "media1_in"},
+       {"media1_out mix 0", "media2_in Switch", "media2_in"},
+       {"media1_out mix 0", "media3_in Switch", "media3_in"},
+
+       {"media0_out", NULL, "media0_out mix 0"},
+       {"media1_out", NULL, "media1_out mix 0"},
+       {"pcm0_in", NULL, "media0_out"},
+       {"pcm1_in", NULL, "media1_out"},
+
+       {"Headset Capture", NULL, "pcm1_out"},
+       {"Headset Capture", NULL, "pcm2_out"},
+       {"pcm0_out", NULL, "pcm0_out mix 0"},
+       SST_SBA_MIXER_GRAPH_MAP("pcm0_out mix 0"),
+       {"pcm1_out", NULL, "pcm1_out mix 0"},
+       SST_SBA_MIXER_GRAPH_MAP("pcm1_out mix 0"),
+       {"pcm2_out", NULL, "pcm2_out mix 0"},
+       SST_SBA_MIXER_GRAPH_MAP("pcm2_out mix 0"),
+
+       {"media_loop1_in", NULL, "media_loop1_out"},
+       {"media_loop1_out", NULL, "media_loop1_out mix 0"},
+       SST_SBA_MIXER_GRAPH_MAP("media_loop1_out mix 0"),
+       {"media_loop2_in", NULL, "media_loop2_out"},
+       {"media_loop2_out", NULL, "media_loop2_out mix 0"},
+       SST_SBA_MIXER_GRAPH_MAP("media_loop2_out mix 0"),
+       {"sprot_loop_in", NULL, "sprot_loop_out"},
+       {"sprot_loop_out", NULL, "sprot_loop_out mix 0"},
+       SST_SBA_MIXER_GRAPH_MAP("sprot_loop_out mix 0"),
+
+       {"codec_out0", NULL, "codec_out0 mix 0"},
+       SST_SBA_MIXER_GRAPH_MAP("codec_out0 mix 0"),
+       {"codec_out1", NULL, "codec_out1 mix 0"},
+       SST_SBA_MIXER_GRAPH_MAP("codec_out1 mix 0"),
+
+};
+static const char * const slot_names[] = {
+       "none",
+       "slot 0", "slot 1", "slot 2", "slot 3",
+       "slot 4", "slot 5", "slot 6", "slot 7", /* not supported by FW */
+};
+
+static const char * const channel_names[] = {
+       "none",
+       "codec_out0_0", "codec_out0_1", "codec_out1_0", "codec_out1_1",
+       "codec_out2_0", "codec_out2_1", "codec_out3_0", "codec_out3_1", /* not supported by FW */
+};
+
+#define SST_INTERLEAVER(xpname, slot_name, slotno) \
+       SST_SSP_SLOT_CTL(xpname, "tx interleaver", slot_name, slotno, true, \
+                        channel_names, sst_slot_get, sst_slot_put)
+
+#define SST_DEINTERLEAVER(xpname, channel_name, channel_no) \
+       SST_SSP_SLOT_CTL(xpname, "rx deinterleaver", channel_name, channel_no, false, \
+                        slot_names, sst_slot_get, sst_slot_put)
+
+static const struct snd_kcontrol_new sst_slot_controls[] = {
+       SST_INTERLEAVER("codec_out", "slot 0", 0),
+       SST_INTERLEAVER("codec_out", "slot 1", 1),
+       SST_INTERLEAVER("codec_out", "slot 2", 2),
+       SST_INTERLEAVER("codec_out", "slot 3", 3),
+       SST_DEINTERLEAVER("codec_in", "codec_in0_0", 0),
+       SST_DEINTERLEAVER("codec_in", "codec_in0_1", 1),
+       SST_DEINTERLEAVER("codec_in", "codec_in1_0", 2),
+       SST_DEINTERLEAVER("codec_in", "codec_in1_1", 3),
+};
+
+/* Gain helper with min/max set */
+#define SST_GAIN(name, path_id, task_id, instance, gain_var)                           \
+       SST_GAIN_KCONTROLS(name, "Gain", SST_GAIN_MIN_VALUE, SST_GAIN_MAX_VALUE,        \
+               SST_GAIN_TC_MIN, SST_GAIN_TC_MAX,                                       \
+               sst_gain_get, sst_gain_put,                                             \
+               SST_MODULE_ID_GAIN_CELL, path_id, instance, task_id,                    \
+               sst_gain_tlv_common, gain_var)
+
+#define SST_VOLUME(name, path_id, task_id, instance, gain_var)                         \
+       SST_GAIN_KCONTROLS(name, "Volume", SST_GAIN_MIN_VALUE, SST_GAIN_MAX_VALUE,      \
+               SST_GAIN_TC_MIN, SST_GAIN_TC_MAX,                                       \
+               sst_gain_get, sst_gain_put,                                             \
+               SST_MODULE_ID_VOLUME, path_id, instance, task_id,                       \
+               sst_gain_tlv_common, gain_var)
+
+static struct sst_gain_value sst_gains[];
+
+static const struct snd_kcontrol_new sst_gain_controls[] = {
+       SST_GAIN("media0_in", SST_PATH_INDEX_MEDIA0_IN, SST_TASK_MMX, 0, &sst_gains[0]),
+       SST_GAIN("media1_in", SST_PATH_INDEX_MEDIA1_IN, SST_TASK_MMX, 0, &sst_gains[1]),
+       SST_GAIN("media2_in", SST_PATH_INDEX_MEDIA2_IN, SST_TASK_MMX, 0, &sst_gains[2]),
+       SST_GAIN("media3_in", SST_PATH_INDEX_MEDIA3_IN, SST_TASK_MMX, 0, &sst_gains[3]),
+
+       SST_GAIN("pcm0_in", SST_PATH_INDEX_PCM0_IN, SST_TASK_SBA, 0, &sst_gains[4]),
+       SST_GAIN("pcm1_in", SST_PATH_INDEX_PCM1_IN, SST_TASK_SBA, 0, &sst_gains[5]),
+       SST_GAIN("pcm1_out", SST_PATH_INDEX_PCM1_OUT, SST_TASK_SBA, 0, &sst_gains[6]),
+       SST_GAIN("pcm2_out", SST_PATH_INDEX_PCM2_OUT, SST_TASK_SBA, 0, &sst_gains[7]),
+
+       SST_GAIN("codec_in0", SST_PATH_INDEX_CODEC_IN0, SST_TASK_SBA, 0, &sst_gains[8]),
+       SST_GAIN("codec_in1", SST_PATH_INDEX_CODEC_IN1, SST_TASK_SBA, 0, &sst_gains[9]),
+       SST_GAIN("codec_out0", SST_PATH_INDEX_CODEC_OUT0, SST_TASK_SBA, 0, &sst_gains[10]),
+       SST_GAIN("codec_out1", SST_PATH_INDEX_CODEC_OUT1, SST_TASK_SBA, 0, &sst_gains[11]),
+       SST_GAIN("media_loop1_out", SST_PATH_INDEX_MEDIA_LOOP1_OUT, SST_TASK_SBA, 0, &sst_gains[12]),
+       SST_GAIN("media_loop2_out", SST_PATH_INDEX_MEDIA_LOOP2_OUT, SST_TASK_SBA, 0, &sst_gains[13]),
+       SST_GAIN("sprot_loop_out", SST_PATH_INDEX_SPROT_LOOP_OUT, SST_TASK_SBA, 0, &sst_gains[14]),
+       SST_VOLUME("media0_in", SST_PATH_INDEX_MEDIA0_IN, SST_TASK_MMX, 0, &sst_gains[15]),
+};
+
+#define SST_GAIN_NUM_CONTROLS 3
+/* the SST_GAIN macro above will create three alsa controls for each
+ * instance invoked, gain, mute and ramp duration, which use the same gain
+ * cell sst_gain to keep track of data
+ * To calculate number of gain cell instances we need to device by 3 in
+ * below caulcation for gain cell memory.
+ * This gets rid of static number and issues while adding new controls
+ */
+static struct sst_gain_value sst_gains[ARRAY_SIZE(sst_gain_controls)/SST_GAIN_NUM_CONTROLS];
+
 static const struct snd_kcontrol_new sst_algo_controls[] = {
        SST_ALGO_KCONTROL_BYTES("media_loop1_out", "fir", 272, SST_MODULE_ID_FIR_24,
                 SST_PATH_INDEX_MEDIA_LOOP1_OUT, 0, SST_TASK_SBA, SBA_VB_SET_FIR),
@@ -198,21 +1143,280 @@ static int sst_algo_control_init(struct device *dev)
        return 0;
 }
 
-int sst_dsp_init_v2_dpcm(struct snd_soc_platform *platform)
+static bool is_sst_dapm_widget(struct snd_soc_dapm_widget *w)
+{
+       switch (w->id) {
+       case snd_soc_dapm_pga:
+       case snd_soc_dapm_aif_in:
+       case snd_soc_dapm_aif_out:
+       case snd_soc_dapm_input:
+       case snd_soc_dapm_output:
+       case snd_soc_dapm_mixer:
+               return true;
+       default:
+               return false;
+       }
+}
+
+/**
+ * sst_send_pipe_gains - send gains for the front-end DAIs
+ *
+ * The gains in the pipes connected to the front-ends are muted/unmuted
+ * automatically via the digital_mute() DAPM callback. This function sends the
+ * gains for the front-end pipes.
+ */
+int sst_send_pipe_gains(struct snd_soc_dai *dai, int stream, int mute)
+{
+       struct sst_data *drv = snd_soc_dai_get_drvdata(dai);
+       struct snd_soc_dapm_widget *w;
+       struct snd_soc_dapm_path *p = NULL;
+
+       dev_dbg(dai->dev, "enter, dai-name=%s dir=%d\n", dai->name, stream);
+
+       if (stream == SNDRV_PCM_STREAM_PLAYBACK) {
+               dev_dbg(dai->dev, "Stream name=%s\n",
+                               dai->playback_widget->name);
+               w = dai->playback_widget;
+               list_for_each_entry(p, &w->sinks, list_source) {
+                       if (p->connected && !p->connected(w, p->sink))
+                               continue;
+
+                       if (p->connect && p->sink->power &&
+                                       is_sst_dapm_widget(p->sink)) {
+                               struct sst_ids *ids = p->sink->priv;
+
+                               dev_dbg(dai->dev, "send gains for widget=%s\n",
+                                               p->sink->name);
+                               mutex_lock(&drv->lock);
+                               sst_set_pipe_gain(ids, drv, mute);
+                               mutex_unlock(&drv->lock);
+                       }
+               }
+       } else {
+               dev_dbg(dai->dev, "Stream name=%s\n",
+                               dai->capture_widget->name);
+               w = dai->capture_widget;
+               list_for_each_entry(p, &w->sources, list_sink) {
+                       if (p->connected && !p->connected(w, p->sink))
+                               continue;
+
+                       if (p->connect &&  p->source->power &&
+                                       is_sst_dapm_widget(p->source)) {
+                               struct sst_ids *ids = p->source->priv;
+
+                               dev_dbg(dai->dev, "send gain for widget=%s\n",
+                                               p->source->name);
+                               mutex_lock(&drv->lock);
+                               sst_set_pipe_gain(ids, drv, mute);
+                               mutex_unlock(&drv->lock);
+                       }
+               }
+       }
+       return 0;
+}
+
+/**
+ * sst_fill_module_list - populate the list of modules/gains for a pipe
+ *
+ *
+ * Fills the widget pointer in the kcontrol private data, and also fills the
+ * kcontrol pointer in the widget private data.
+ *
+ * Widget pointer is used to send the algo/gain in the .put() handler if the
+ * widget is powerd on.
+ *
+ * Kcontrol pointer is used to send the algo/gain in the widget power ON/OFF
+ * event handler. Each widget (pipe) has multiple algos stored in the algo_list.
+ */
+static int sst_fill_module_list(struct snd_kcontrol *kctl,
+        struct snd_soc_dapm_widget *w, int type)
 {
+       struct sst_module *module = NULL;
+       struct snd_soc_component *c = snd_soc_dapm_to_component(w->dapm);
+       struct sst_ids *ids = w->priv;
        int ret = 0;
+
+       module = devm_kzalloc(c->dev, sizeof(*module), GFP_KERNEL);
+       if (!module)
+               return -ENOMEM;
+
+       if (type == SST_MODULE_GAIN) {
+               struct sst_gain_mixer_control *mc = (void *)kctl->private_value;
+
+               mc->w = w;
+               module->kctl = kctl;
+               list_add_tail(&module->node, &ids->gain_list);
+       } else if (type == SST_MODULE_ALGO) {
+               struct sst_algo_control *bc = (void *)kctl->private_value;
+
+               bc->w = w;
+               module->kctl = kctl;
+               list_add_tail(&module->node, &ids->algo_list);
+       } else {
+               dev_err(c->dev, "invoked for unknown type %d module %s",
+                               type, kctl->id.name);
+               ret = -EINVAL;
+       }
+
+       return ret;
+}
+
+/**
+ * sst_fill_widget_module_info - fill list of gains/algos for the pipe
+ * @widget:    pipe modelled as a DAPM widget
+ *
+ * Fill the list of gains/algos for the widget by looking at all the card
+ * controls and comparing the name of the widget with the first part of control
+ * name. First part of control name contains the pipe name (widget name).
+ */
+static int sst_fill_widget_module_info(struct snd_soc_dapm_widget *w,
+       struct snd_soc_platform *platform)
+{
+       struct snd_kcontrol *kctl;
+       int index, ret = 0;
+       struct snd_card *card = platform->component.card->snd_card;
+       char *idx;
+
+       down_read(&card->controls_rwsem);
+
+       list_for_each_entry(kctl, &card->controls, list) {
+               idx = strstr(kctl->id.name, " ");
+               if (idx == NULL)
+                       continue;
+               index  = strlen(kctl->id.name) - strlen(idx);
+
+               if (strstr(kctl->id.name, "Volume") &&
+                   !strncmp(kctl->id.name, w->name, index))
+                       ret = sst_fill_module_list(kctl, w, SST_MODULE_GAIN);
+
+               else if (strstr(kctl->id.name, "params") &&
+                        !strncmp(kctl->id.name, w->name, index))
+                       ret = sst_fill_module_list(kctl, w, SST_MODULE_ALGO);
+
+               else if (strstr(kctl->id.name, "Switch") &&
+                        !strncmp(kctl->id.name, w->name, index) &&
+                        strstr(kctl->id.name, "Gain")) {
+                       struct sst_gain_mixer_control *mc =
+                                               (void *)kctl->private_value;
+
+                       mc->w = w;
+
+               } else if (strstr(kctl->id.name, "interleaver") &&
+                        !strncmp(kctl->id.name, w->name, index)) {
+                       struct sst_enum *e = (void *)kctl->private_value;
+
+                       e->w = w;
+
+               } else if (strstr(kctl->id.name, "deinterleaver") &&
+                        !strncmp(kctl->id.name, w->name, index)) {
+
+                       struct sst_enum *e = (void *)kctl->private_value;
+
+                       e->w = w;
+               }
+
+               if (ret < 0) {
+                       up_read(&card->controls_rwsem);
+                       return ret;
+               }
+       }
+
+       up_read(&card->controls_rwsem);
+       return 0;
+}
+
+/**
+ * sst_fill_linked_widgets - fill the parent pointer for the linked widget
+ */
+static void sst_fill_linked_widgets(struct snd_soc_platform *platform,
+                                               struct sst_ids *ids)
+{
+       struct snd_soc_dapm_widget *w;
+       unsigned int len = strlen(ids->parent_wname);
+
+       list_for_each_entry(w, &platform->component.card->widgets, list) {
+               if (!strncmp(ids->parent_wname, w->name, len)) {
+                       ids->parent_w = w;
+                       break;
+               }
+       }
+}
+
+/**
+ * sst_map_modules_to_pipe - fill algo/gains list for all pipes
+ */
+static int sst_map_modules_to_pipe(struct snd_soc_platform *platform)
+{
+       struct snd_soc_dapm_widget *w;
+       int ret = 0;
+
+       list_for_each_entry(w, &platform->component.card->widgets, list) {
+               if (is_sst_dapm_widget(w) && (w->priv)) {
+                       struct sst_ids *ids = w->priv;
+
+                       dev_dbg(platform->dev, "widget type=%d name=%s\n",
+                                       w->id, w->name);
+                       INIT_LIST_HEAD(&ids->algo_list);
+                       INIT_LIST_HEAD(&ids->gain_list);
+                       ret = sst_fill_widget_module_info(w, platform);
+
+                       if (ret < 0)
+                               return ret;
+
+                       /* fill linked widgets */
+                       if (ids->parent_wname !=  NULL)
+                               sst_fill_linked_widgets(platform, ids);
+               }
+       }
+       return 0;
+}
+
+int sst_dsp_init_v2_dpcm(struct snd_soc_platform *platform)
+{
+       int i, ret = 0;
+       struct snd_soc_dapm_context *dapm =
+                       snd_soc_component_get_dapm(&platform->component);
        struct sst_data *drv = snd_soc_platform_get_drvdata(platform);
+       unsigned int gains = ARRAY_SIZE(sst_gain_controls)/3;
 
        drv->byte_stream = devm_kzalloc(platform->dev,
                                        SST_MAX_BIN_BYTES, GFP_KERNEL);
        if (!drv->byte_stream)
                return -ENOMEM;
 
-       /*Initialize algo control params*/
+       snd_soc_dapm_new_controls(dapm, sst_dapm_widgets,
+                       ARRAY_SIZE(sst_dapm_widgets));
+       snd_soc_dapm_add_routes(dapm, intercon,
+                       ARRAY_SIZE(intercon));
+       snd_soc_dapm_new_widgets(dapm->card);
+
+       for (i = 0; i < gains; i++) {
+               sst_gains[i].mute = SST_GAIN_MUTE_DEFAULT;
+               sst_gains[i].l_gain = SST_GAIN_VOLUME_DEFAULT;
+               sst_gains[i].r_gain = SST_GAIN_VOLUME_DEFAULT;
+               sst_gains[i].ramp_duration = SST_GAIN_RAMP_DURATION_DEFAULT;
+       }
+
+       ret = snd_soc_add_platform_controls(platform, sst_gain_controls,
+                       ARRAY_SIZE(sst_gain_controls));
+       if (ret)
+               return ret;
+
+       /* Initialize algo control params */
        ret = sst_algo_control_init(platform->dev);
        if (ret)
                return ret;
        ret = snd_soc_add_platform_controls(platform, sst_algo_controls,
                        ARRAY_SIZE(sst_algo_controls));
+       if (ret)
+               return ret;
+
+       ret = snd_soc_add_platform_controls(platform, sst_slot_controls,
+                       ARRAY_SIZE(sst_slot_controls));
+       if (ret)
+               return ret;
+
+       ret = sst_map_modules_to_pipe(platform);
+
        return ret;
 }
index a73e894..dfebfdd 100644 (file)
@@ -23,6 +23,9 @@
 #ifndef __SST_ATOM_CONTROLS_H__
 #define __SST_ATOM_CONTROLS_H__
 
+#include <sound/soc.h>
+#include <sound/tlv.h>
+
 enum {
        MERR_DPCM_AUDIO = 0,
        MERR_DPCM_COMPR,
@@ -360,16 +363,416 @@ struct sst_dsp_header {
 struct sst_cmd_generic {
        struct sst_dsp_header header;
 } __packed;
+
+struct swm_input_ids {
+       struct sst_destination_id input_id;
+} __packed;
+
+struct sst_cmd_set_swm {
+       struct sst_dsp_header header;
+       struct sst_destination_id output_id;
+       u16    switch_state;
+       u16    nb_inputs;
+       struct swm_input_ids input[SST_CMD_SWM_MAX_INPUTS];
+} __packed;
+
+struct sst_cmd_set_media_path {
+       struct sst_dsp_header header;
+       u16    switch_state;
+} __packed;
+
+struct pcm_cfg {
+               u8 s_length:2;
+               u8 rate:3;
+               u8 format:3;
+} __packed;
+
+struct sst_cmd_set_speech_path {
+       struct sst_dsp_header header;
+       u16    switch_state;
+       struct {
+               u16 rsvd:8;
+               struct pcm_cfg cfg;
+       } config;
+} __packed;
+
+struct gain_cell {
+       struct sst_destination_id dest;
+       s16 cell_gain_left;
+       s16 cell_gain_right;
+       u16 gain_time_constant;
+} __packed;
+
+#define NUM_GAIN_CELLS 1
+struct sst_cmd_set_gain_dual {
+       struct sst_dsp_header header;
+       u16    gain_cell_num;
+       struct gain_cell cell_gains[NUM_GAIN_CELLS];
+} __packed;
 struct sst_cmd_set_params {
        struct sst_destination_id dst;
        u16 command_id;
        char params[0];
 } __packed;
+
+
+struct sst_cmd_sba_vb_start {
+       struct sst_dsp_header header;
+} __packed;
+
+union sba_media_loop_params {
+       struct {
+               u16 rsvd:8;
+               struct pcm_cfg cfg;
+       } part;
+       u16 full;
+} __packed;
+
+struct sst_cmd_sba_set_media_loop_map {
+       struct  sst_dsp_header header;
+       u16     switch_state;
+       union   sba_media_loop_params param;
+       u16     map;
+} __packed;
+
+struct sst_cmd_tone_stop {
+       struct  sst_dsp_header header;
+       u16     switch_state;
+} __packed;
+
+enum sst_ssp_mode {
+       SSP_MODE_MASTER = 0,
+       SSP_MODE_SLAVE = 1,
+};
+
+enum sst_ssp_pcm_mode {
+       SSP_PCM_MODE_NORMAL = 0,
+       SSP_PCM_MODE_NETWORK = 1,
+};
+
+enum sst_ssp_duplex {
+       SSP_DUPLEX = 0,
+       SSP_RX = 1,
+       SSP_TX = 2,
+};
+
+enum sst_ssp_fs_frequency {
+       SSP_FS_8_KHZ = 0,
+       SSP_FS_16_KHZ = 1,
+       SSP_FS_44_1_KHZ = 2,
+       SSP_FS_48_KHZ = 3,
+};
+
+enum sst_ssp_fs_polarity {
+       SSP_FS_ACTIVE_LOW = 0,
+       SSP_FS_ACTIVE_HIGH = 1,
+};
+
+enum sst_ssp_protocol {
+       SSP_MODE_PCM = 0,
+       SSP_MODE_I2S = 1,
+};
+
+enum sst_ssp_port_id {
+       SSP_MODEM = 0,
+       SSP_BT = 1,
+       SSP_FM = 2,
+       SSP_CODEC = 3,
+};
+
+struct sst_cmd_sba_hw_set_ssp {
+       struct sst_dsp_header header;
+       u16 selection;                  /* 0:SSP0(def), 1:SSP1, 2:SSP2 */
+
+       u16 switch_state;
+
+       u16 nb_bits_per_slots:6;        /* 0-32 bits, 24 (def) */
+       u16 nb_slots:4;                 /* 0-8: slots per frame  */
+       u16 mode:3;                     /* 0:Master, 1: Slave  */
+       u16 duplex:3;
+
+       u16 active_tx_slot_map:8;       /* Bit map, 0:off, 1:on */
+       u16 reserved1:8;
+
+       u16 active_rx_slot_map:8;       /* Bit map 0: Off, 1:On */
+       u16 reserved2:8;
+
+       u16 frame_sync_frequency;
+
+       u16 frame_sync_polarity:8;
+       u16 data_polarity:8;
+
+       u16 frame_sync_width;           /* 1 to N clocks */
+       u16 ssp_protocol:8;
+       u16 start_delay:8;              /* Start delay in terms of clock ticks */
+} __packed;
+
+#define SST_MAX_TDM_SLOTS 8
+
+struct sst_param_sba_ssp_slot_map {
+       struct sst_dsp_header header;
+
+       u16 param_id;
+       u16 param_len;
+       u16 ssp_index;
+
+       u8 rx_slot_map[SST_MAX_TDM_SLOTS];
+       u8 tx_slot_map[SST_MAX_TDM_SLOTS];
+} __packed;
+
+enum {
+       SST_PROBE_EXTRACTOR = 0,
+       SST_PROBE_INJECTOR = 1,
+};
+
+/**** widget defines *****/
+
+#define SST_MODULE_GAIN 1
+#define SST_MODULE_ALGO 2
+
+#define SST_FMT_MONO 0
+#define SST_FMT_STEREO 3
+
+/* physical SSP numbers */
+enum {
+       SST_SSP0 = 0,
+       SST_SSP1,
+       SST_SSP2,
+       SST_SSP_LAST = SST_SSP2,
+};
+
+#define SST_NUM_SSPS           (SST_SSP_LAST + 1)      /* physical SSPs */
+#define SST_MAX_SSP_MUX                2                       /* single SSP muxed between pipes */
+#define SST_MAX_SSP_DOMAINS    2                       /* domains present in each pipe */
+
+struct sst_module {
+       struct snd_kcontrol *kctl;
+       struct list_head node;
+};
+
+struct sst_ssp_config {
+       u8 ssp_id;
+       u8 bits_per_slot;
+       u8 slots;
+       u8 ssp_mode;
+       u8 pcm_mode;
+       u8 duplex;
+       u8 ssp_protocol;
+       u8 fs_frequency;
+       u8 active_slot_map;
+       u8 start_delay;
+       u16 fs_width;
+};
+
+struct sst_ssp_cfg {
+       const u8 ssp_number;
+       const int *mux_shift;
+       const int (*domain_shift)[SST_MAX_SSP_MUX];
+       const struct sst_ssp_config (*ssp_config)[SST_MAX_SSP_MUX][SST_MAX_SSP_DOMAINS];
+};
+
+struct sst_ids {
+       u16 location_id;
+       u16 module_id;
+       u8  task_id;
+       u8  format;
+       u8  reg;
+       const char *parent_wname;
+       struct snd_soc_dapm_widget *parent_w;
+       struct list_head algo_list;
+       struct list_head gain_list;
+       const struct sst_pcm_format *pcm_fmt;
+};
+
+
+#define SST_AIF_IN(wname, wevent)                                                      \
+{      .id = snd_soc_dapm_aif_in, .name = wname, .sname = NULL,                        \
+       .reg = SND_SOC_NOPM, .shift = 0,                                        \
+       .on_val = 1, .off_val = 0,                                                      \
+       .event = wevent, .event_flags = SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD,   \
+       .priv = (void *)&(struct sst_ids) { .task_id = 0, .location_id = 0 }            \
+}
+
+#define SST_AIF_OUT(wname, wevent)                                                     \
+{      .id = snd_soc_dapm_aif_out, .name = wname, .sname = NULL,                       \
+       .reg = SND_SOC_NOPM, .shift = 0,                                                \
+       .on_val = 1, .off_val = 0,                                                      \
+       .event = wevent, .event_flags = SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD,   \
+       .priv = (void *)&(struct sst_ids) { .task_id = 0, .location_id = 0 }            \
+}
+
+#define SST_INPUT(wname, wevent)                                                       \
+{      .id = snd_soc_dapm_input, .name = wname, .sname = NULL,                         \
+       .reg = SND_SOC_NOPM, .shift = 0,                                                \
+       .on_val = 1, .off_val = 0,                                                      \
+       .event = wevent, .event_flags = SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD,   \
+       .priv = (void *)&(struct sst_ids) { .task_id = 0, .location_id = 0 }            \
+}
+
+#define SST_OUTPUT(wname, wevent)                                                      \
+{      .id = snd_soc_dapm_output, .name = wname, .sname = NULL,                        \
+       .reg = SND_SOC_NOPM, .shift = 0,                                                \
+       .on_val = 1, .off_val = 0,                                                      \
+       .event = wevent, .event_flags = SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD,   \
+       .priv = (void *)&(struct sst_ids) { .task_id = 0, .location_id = 0 }            \
+}
+
+#define SST_DAPM_OUTPUT(wname, wloc_id, wtask_id, wformat, wevent)                      \
+{      .id = snd_soc_dapm_output, .name = wname, .sname = NULL,                        \
+       .reg = SND_SOC_NOPM, .shift = 0,                                                \
+       .on_val = 1, .off_val = 0,                                                      \
+       .event = wevent, .event_flags = SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD,   \
+       .priv = (void *)&(struct sst_ids) { .location_id = wloc_id, .task_id = wtask_id,\
+                                               .pcm_fmt = wformat, }                   \
+}
+
+#define SST_PATH(wname, wtask, wloc_id, wevent, wflags)                                        \
+{      .id = snd_soc_dapm_pga, .name = wname, .reg = SND_SOC_NOPM, .shift = 0,         \
+       .kcontrol_news = NULL, .num_kcontrols = 0,                              \
+       .on_val = 1, .off_val = 0,                                                      \
+       .event = wevent, .event_flags = wflags,                                         \
+       .priv = (void *)&(struct sst_ids) { .task_id = wtask, .location_id = wloc_id, } \
+}
+
+#define SST_LINKED_PATH(wname, wtask, wloc_id, linked_wname, wevent, wflags)           \
+{      .id = snd_soc_dapm_pga, .name = wname, .reg = SND_SOC_NOPM, .shift = 0,         \
+       .kcontrol_news = NULL, .num_kcontrols = 0,                              \
+       .on_val = 1, .off_val = 0,                                                      \
+       .event = wevent, .event_flags = wflags,                                         \
+       .priv = (void *)&(struct sst_ids) { .task_id = wtask, .location_id = wloc_id,   \
+                                       .parent_wname = linked_wname}                   \
+}
+
+#define SST_PATH_MEDIA_LOOP(wname, wtask, wloc_id, wformat, wevent, wflags)             \
+{      .id = snd_soc_dapm_pga, .name = wname, .reg = SND_SOC_NOPM, .shift = 0,         \
+       .kcontrol_news = NULL, .num_kcontrols = 0,                         \
+       .event = wevent, .event_flags = wflags,                                         \
+       .priv = (void *)&(struct sst_ids) { .task_id = wtask, .location_id = wloc_id,   \
+                                           .format = wformat,}                         \
+}
+
+/* output is triggered before input */
+#define SST_PATH_INPUT(name, task_id, loc_id, event)                                   \
+       SST_PATH(name, task_id, loc_id, event, SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD)
+
+#define SST_PATH_LINKED_INPUT(name, task_id, loc_id, linked_wname, event)              \
+       SST_LINKED_PATH(name, task_id, loc_id, linked_wname, event,                     \
+                                       SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD)
+
+#define SST_PATH_OUTPUT(name, task_id, loc_id, event)                                  \
+       SST_PATH(name, task_id, loc_id, event, SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD)
+
+#define SST_PATH_LINKED_OUTPUT(name, task_id, loc_id, linked_wname, event)             \
+       SST_LINKED_PATH(name, task_id, loc_id, linked_wname, event,                     \
+                                       SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD)
+
+#define SST_PATH_MEDIA_LOOP_OUTPUT(name, task_id, loc_id, format, event)               \
+       SST_PATH_MEDIA_LOOP(name, task_id, loc_id, format, event, SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD)
+
+
+#define SST_SWM_MIXER(wname, wreg, wtask, wloc_id, wcontrols, wevent)                  \
+{      .id = snd_soc_dapm_mixer, .name = wname, .reg = SND_SOC_NOPM, .shift = 0,       \
+       .kcontrol_news = wcontrols, .num_kcontrols = ARRAY_SIZE(wcontrols),\
+       .event = wevent, .event_flags = SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD |  \
+                                       SND_SOC_DAPM_POST_REG,                          \
+       .priv = (void *)&(struct sst_ids) { .task_id = wtask, .location_id = wloc_id,   \
+                                           .reg = wreg }                               \
+}
+
+enum sst_gain_kcontrol_type {
+       SST_GAIN_TLV,
+       SST_GAIN_MUTE,
+       SST_GAIN_RAMP_DURATION,
+};
+
+struct sst_gain_mixer_control {
+       bool stereo;
+       enum sst_gain_kcontrol_type type;
+       struct sst_gain_value *gain_val;
+       int max;
+       int min;
+       u16 instance_id;
+       u16 module_id;
+       u16 pipe_id;
+       u16 task_id;
+       char pname[44];
+       struct snd_soc_dapm_widget *w;
+};
+
+struct sst_gain_value {
+       u16 ramp_duration;
+       s16 l_gain;
+       s16 r_gain;
+       bool mute;
+};
+#define SST_GAIN_VOLUME_DEFAULT                (-1440)
+#define SST_GAIN_RAMP_DURATION_DEFAULT 5 /* timeconstant */
+#define SST_GAIN_MUTE_DEFAULT          true
+
+#define SST_GAIN_KCONTROL_TLV(xname, xhandler_get, xhandler_put, \
+                             xmod, xpipe, xinstance, xtask, tlv_array, xgain_val, \
+                             xmin, xmax, xpname) \
+       .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \
+       .access = SNDRV_CTL_ELEM_ACCESS_TLV_READ | \
+                 SNDRV_CTL_ELEM_ACCESS_READWRITE, \
+       .tlv.p = (tlv_array), \
+       .info = sst_gain_ctl_info,\
+       .get = xhandler_get, .put = xhandler_put, \
+       .private_value = (unsigned long)&(struct sst_gain_mixer_control) \
+       { .stereo = true, .max = xmax, .min = xmin, .type = SST_GAIN_TLV, \
+         .module_id = xmod, .pipe_id = xpipe, .task_id = xtask,\
+         .instance_id = xinstance, .gain_val = xgain_val, .pname = xpname}
+
+#define SST_GAIN_KCONTROL_INT(xname, xhandler_get, xhandler_put, \
+                             xmod, xpipe, xinstance, xtask, xtype, xgain_val, \
+                             xmin, xmax, xpname) \
+       .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \
+       .info = sst_gain_ctl_info, \
+       .get = xhandler_get, .put = xhandler_put, \
+       .private_value = (unsigned long)&(struct sst_gain_mixer_control) \
+       { .stereo = false, .max = xmax, .min = xmin, .type = xtype, \
+         .module_id = xmod, .pipe_id = xpipe, .task_id = xtask,\
+         .instance_id = xinstance, .gain_val = xgain_val, .pname =  xpname}
+
+#define SST_GAIN_KCONTROL_BOOL(xname, xhandler_get, xhandler_put,\
+                              xmod, xpipe, xinstance, xtask, xgain_val, xpname) \
+       .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \
+       .info = snd_soc_info_bool_ext, \
+       .get = xhandler_get, .put = xhandler_put, \
+       .private_value = (unsigned long)&(struct sst_gain_mixer_control) \
+       { .stereo = false, .type = SST_GAIN_MUTE, \
+         .module_id = xmod, .pipe_id = xpipe, .task_id = xtask,\
+         .instance_id = xinstance, .gain_val = xgain_val, .pname = xpname}
 #define SST_CONTROL_NAME(xpname, xmname, xinstance, xtype) \
        xpname " " xmname " " #xinstance " " xtype
 
 #define SST_COMBO_CONTROL_NAME(xpname, xmname, xinstance, xtype, xsubmodule) \
        xpname " " xmname " " #xinstance " " xtype " " xsubmodule
+
+/*
+ * 3 Controls for each Gain module
+ * e.g.        - pcm0_in Gain 0 Volume
+ *     - pcm0_in Gain 0 Ramp Delay
+ *     - pcm0_in Gain 0 Switch
+ */
+#define SST_GAIN_KCONTROLS(xpname, xmname, xmin_gain, xmax_gain, xmin_tc, xmax_tc, \
+                          xhandler_get, xhandler_put, \
+                          xmod, xpipe, xinstance, xtask, tlv_array, xgain_val) \
+       { SST_GAIN_KCONTROL_INT(SST_CONTROL_NAME(xpname, xmname, xinstance, "Ramp Delay"), \
+               xhandler_get, xhandler_put, xmod, xpipe, xinstance, xtask, SST_GAIN_RAMP_DURATION, \
+               xgain_val, xmin_tc, xmax_tc, xpname) }, \
+       { SST_GAIN_KCONTROL_BOOL(SST_CONTROL_NAME(xpname, xmname, xinstance, "Switch"), \
+               xhandler_get, xhandler_put, xmod, xpipe, xinstance, xtask, \
+               xgain_val, xpname) } ,\
+       { SST_GAIN_KCONTROL_TLV(SST_CONTROL_NAME(xpname, xmname, xinstance, "Volume"), \
+               xhandler_get, xhandler_put, xmod, xpipe, xinstance, xtask, tlv_array, \
+               xgain_val, xmin_gain, xmax_gain, xpname) }
+
+#define SST_GAIN_TC_MIN                5
+#define SST_GAIN_TC_MAX                5000
+#define SST_GAIN_MIN_VALUE     -1440 /* in 0.1 DB units */
+#define SST_GAIN_MAX_VALUE     360
+
 enum sst_algo_kcontrol_type {
        SST_ALGO_PARAMS,
        SST_ALGO_BYPASS,
@@ -439,4 +842,29 @@ struct sst_enum {
        struct snd_soc_dapm_widget *w;
 };
 
+/* only 4 slots/channels supported atm */
+#define SST_SSP_SLOT_ENUM(s_ch_no, is_tx, xtexts) \
+       (struct sst_enum){ .reg = s_ch_no, .tx = is_tx, .max = 4+1, .texts = xtexts, }
+
+#define SST_SLOT_CTL_NAME(xpname, xmname, s_ch_name) \
+       xpname " " xmname " " s_ch_name
+
+#define SST_SSP_SLOT_CTL(xpname, xmname, s_ch_name, s_ch_no, is_tx, xtexts, xget, xput) \
+{      .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
+       .name = SST_SLOT_CTL_NAME(xpname, xmname, s_ch_name), \
+       .info = sst_slot_enum_info, \
+       .get = xget, .put = xput, \
+       .private_value = (unsigned long)&SST_SSP_SLOT_ENUM(s_ch_no, is_tx, xtexts), \
+}
+
+#define SST_MUX_CTL_NAME(xpname, xinstance) \
+       xpname " " #xinstance
+
+#define SST_SSP_MUX_ENUM(xreg, xshift, xtexts) \
+       (struct soc_enum) SOC_ENUM_DOUBLE(xreg, xshift, xshift, ARRAY_SIZE(xtexts), xtexts)
+
+#define SST_SSP_MUX_CTL(xpname, xinstance, xreg, xshift, xtexts) \
+       SOC_DAPM_ENUM(SST_MUX_CTL_NAME(xpname, xinstance), \
+                         SST_SSP_MUX_ENUM(xreg, xshift, xtexts))
+
 #endif
index fc58876..5a9e567 100644 (file)
@@ -67,17 +67,12 @@ static int sst_byt_parse_module(struct sst_dsp *dsp, struct sst_fw *fw,
 {
        struct dma_block_info *block;
        struct sst_module *mod;
-       struct sst_module_data block_data;
        struct sst_module_template template;
        int count;
 
        memset(&template, 0, sizeof(template));
        template.id = module->type;
        template.entry = module->entry_point;
-       template.p.type = SST_MEM_DRAM;
-       template.p.data_type = SST_DATA_P;
-       template.s.type = SST_MEM_DRAM;
-       template.s.data_type = SST_DATA_S;
 
        mod = sst_module_new(fw, &template, NULL);
        if (mod == NULL)
@@ -94,19 +89,19 @@ static int sst_byt_parse_module(struct sst_dsp *dsp, struct sst_fw *fw,
 
                switch (block->type) {
                case SST_BYT_IRAM:
-                       block_data.offset = block->ram_offset +
+                       mod->offset = block->ram_offset +
                                            dsp->addr.iram_offset;
-                       block_data.type = SST_MEM_IRAM;
+                       mod->type = SST_MEM_IRAM;
                        break;
                case SST_BYT_DRAM:
-                       block_data.offset = block->ram_offset +
+                       mod->offset = block->ram_offset +
                                            dsp->addr.dram_offset;
-                       block_data.type = SST_MEM_DRAM;
+                       mod->type = SST_MEM_DRAM;
                        break;
                case SST_BYT_CACHE:
-                       block_data.offset = block->ram_offset +
+                       mod->offset = block->ram_offset +
                                            (dsp->addr.fw_ext - dsp->addr.lpe);
-                       block_data.type = SST_MEM_CACHE;
+                       mod->type = SST_MEM_CACHE;
                        break;
                default:
                        dev_err(dsp->dev, "wrong ram type 0x%x in block0x%x\n",
@@ -114,11 +109,10 @@ static int sst_byt_parse_module(struct sst_dsp *dsp, struct sst_fw *fw,
                        return -EINVAL;
                }
 
-               block_data.size = block->size;
-               block_data.data_type = SST_DATA_M;
-               block_data.data = (void *)block + sizeof(*block);
+               mod->size = block->size;
+               mod->data = (void *)block + sizeof(*block);
 
-               sst_module_insert_fixed_block(mod, &block_data);
+               sst_module_alloc_blocks(mod);
 
                block = (void *)block + sizeof(*block) + block->size;
        }
index ffb308b..b9da030 100644 (file)
@@ -26,6 +26,9 @@ struct sst_mem_block;
 struct sst_module;
 struct sst_fw;
 
+/* do we need to remove or keep */
+#define DSP_DRAM_ADDR_OFFSET           0x400000
+
 /*
  * DSP Operations exported by platform Audio DSP driver.
  */
@@ -33,6 +36,9 @@ struct sst_ops {
        /* DSP core boot / reset */
        void (*boot)(struct sst_dsp *);
        void (*reset)(struct sst_dsp *);
+       int (*wake)(struct sst_dsp *);
+       void (*sleep)(struct sst_dsp *);
+       void (*stall)(struct sst_dsp *);
 
        /* Shim IO */
        void (*write)(void __iomem *addr, u32 offset, u32 value);
@@ -67,6 +73,8 @@ struct sst_addr {
        u32 shim_offset;
        u32 iram_offset;
        u32 dram_offset;
+       u32 dsp_iram_offset;
+       u32 dsp_dram_offset;
        void __iomem *lpe;
        void __iomem *shim;
        void __iomem *pci_cfg;
@@ -83,15 +91,6 @@ struct sst_mailbox {
        size_t out_size;
 };
 
-/*
- * Audio DSP Firmware data types.
- */
-enum sst_data_type {
-       SST_DATA_M      = 0, /* module block data */
-       SST_DATA_P      = 1, /* peristant data (text, data) */
-       SST_DATA_S      = 2, /* scratch data (usually buffers) */
-};
-
 /*
  * Audio DSP memory block types.
  */
@@ -124,23 +123,6 @@ struct sst_fw {
        void *private;                  /* core doesn't touch this */
 };
 
-/*
- * Audio DSP Generic Module data.
- *
- * This is used to dsecribe any sections of persistent (text and data) and
- * scratch (buffers) of module data in ADSP memory space.
- */
-struct sst_module_data {
-
-       enum sst_mem_type type;         /* destination memory type */
-       enum sst_data_type data_type;   /* type of module data */
-
-       u32 size;               /* size in bytes */
-       int32_t offset;         /* offset in FW file */
-       u32 data_offset;        /* offset in ADSP memory space */
-       void *data;             /* module data */
-};
-
 /*
  * Audio DSP Generic Module Template.
  *
@@ -150,15 +132,52 @@ struct sst_module_data {
 struct sst_module_template {
        u32 id;
        u32 entry;                      /* entry point */
-       struct sst_module_data s;       /* scratch data */
-       struct sst_module_data p;       /* peristant data */
+       u32 scratch_size;
+       u32 persistent_size;
+};
+
+/*
+ * Block Allocator - Used to allocate blocks of DSP memory.
+ */
+struct sst_block_allocator {
+       u32 id;
+       u32 offset;
+       int size;
+       enum sst_mem_type type;
+};
+
+/*
+ * Runtime Module Instance - A module object can be instanciated multiple
+ * times within the DSP FW.
+ */
+struct sst_module_runtime {
+       struct sst_dsp *dsp;
+       int id;
+       struct sst_module *module;      /* parent module we belong too */
+
+       u32 persistent_offset;          /* private memory offset */
+       void *private;
+
+       struct list_head list;
+       struct list_head block_list;    /* list of blocks used */
+};
+
+/*
+ * Runtime Module Context - The runtime context must be manually stored by the
+ * driver prior to enter S3 and restored after leaving S3. This should really be
+ * part of the memory context saved by the enter D3 message IPC ???
+ */
+struct sst_module_runtime_context {
+       dma_addr_t dma_buffer;
+       u32 *buffer;
 };
 
 /*
  * Audio DSP Generic Module.
  *
  * Each Firmware file can consist of 1..N modules. A module can span multiple
- * ADSP memory blocks. The simplest FW will be a file with 1 module.
+ * ADSP memory blocks. The simplest FW will be a file with 1 module. A module
+ * can be instanciated multiple times in the DSP.
  */
 struct sst_module {
        struct sst_dsp *dsp;
@@ -167,10 +186,13 @@ struct sst_module {
        /* module configuration */
        u32 id;
        u32 entry;                      /* module entry point */
-       u32 offset;                     /* module offset in firmware file */
+       s32 offset;                     /* module offset in firmware file */
        u32 size;                       /* module size */
-       struct sst_module_data s;       /* scratch data */
-       struct sst_module_data p;       /* peristant data */
+       u32 scratch_size;               /* global scratch memory required */
+       u32 persistent_size;            /* private memory required */
+       enum sst_mem_type type;         /* destination memory type */
+       u32 data_offset;                /* offset in ADSP memory space */
+       void *data;                     /* module data */
 
        /* runtime */
        u32 usage_count;                /* can be unloaded if count == 0 */
@@ -180,6 +202,7 @@ struct sst_module {
        struct list_head block_list;    /* Module list of blocks in use */
        struct list_head list;          /* DSP list of modules */
        struct list_head list_fw;       /* FW list of modules */
+       struct list_head runtime_list;  /* list of runtime module objects*/
 };
 
 /*
@@ -208,7 +231,6 @@ struct sst_mem_block {
        struct sst_block_ops *ops;      /* block operations, if any */
 
        /* block status */
-       enum sst_data_type data_type;   /* data type held in this block */
        u32 bytes_used;                 /* bytes in use by modules */
        void *private;                  /* generic core does not touch this */
        int users;                      /* number of modules using this block */
@@ -253,6 +275,11 @@ struct sst_dsp {
        struct list_head module_list;
        struct list_head fw_list;
 
+       /* scratch buffer */
+       struct list_head scratch_block_list;
+       u32 scratch_offset;
+       u32 scratch_size;
+
        /* platform data */
        struct sst_pdata *pdata;
 
@@ -290,18 +317,33 @@ void sst_fw_unload(struct sst_fw *sst_fw);
 /* Create/Free firmware modules */
 struct sst_module *sst_module_new(struct sst_fw *sst_fw,
        struct sst_module_template *template, void *private);
-void sst_module_free(struct sst_module *sst_module);
-int sst_module_insert(struct sst_module *sst_module);
-int sst_module_remove(struct sst_module *sst_module);
-int sst_module_insert_fixed_block(struct sst_module *module,
-       struct sst_module_data *data);
+void sst_module_free(struct sst_module *module);
 struct sst_module *sst_module_get_from_id(struct sst_dsp *dsp, u32 id);
-
-/* allocate/free pesistent/scratch memory regions managed by drv */
-struct sst_module *sst_mem_block_alloc_scratch(struct sst_dsp *dsp);
-void sst_mem_block_free_scratch(struct sst_dsp *dsp,
-       struct sst_module *scratch);
-int sst_block_module_remove(struct sst_module *module);
+int sst_module_alloc_blocks(struct sst_module *module);
+int sst_module_free_blocks(struct sst_module *module);
+
+/* Create/Free firmware module runtime instances */
+struct sst_module_runtime *sst_module_runtime_new(struct sst_module *module,
+       int id, void *private);
+void sst_module_runtime_free(struct sst_module_runtime *runtime);
+struct sst_module_runtime *sst_module_runtime_get_from_id(
+       struct sst_module *module, u32 id);
+int sst_module_runtime_alloc_blocks(struct sst_module_runtime *runtime,
+       int offset);
+int sst_module_runtime_free_blocks(struct sst_module_runtime *runtime);
+int sst_module_runtime_save(struct sst_module_runtime *runtime,
+       struct sst_module_runtime_context *context);
+int sst_module_runtime_restore(struct sst_module_runtime *runtime,
+       struct sst_module_runtime_context *context);
+
+/* generic block allocation */
+int sst_alloc_blocks(struct sst_dsp *dsp, struct sst_block_allocator *ba,
+       struct list_head *block_list);
+int sst_free_blocks(struct sst_dsp *dsp, struct list_head *block_list);
+
+/* scratch allocation */
+int sst_block_alloc_scratch(struct sst_dsp *dsp);
+void sst_block_free_scratch(struct sst_dsp *dsp);
 
 /* Register the DSPs memory blocks - would be nice to read from ACPI */
 struct sst_mem_block *sst_mem_block_register(struct sst_dsp *dsp, u32 offset,
@@ -309,4 +351,10 @@ struct sst_mem_block *sst_mem_block_register(struct sst_dsp *dsp, u32 offset,
        void *private);
 void sst_mem_block_unregister_all(struct sst_dsp *dsp);
 
+/* Create/Free DMA resources */
+int sst_dma_new(struct sst_dsp *sst);
+void sst_dma_free(struct sst_dma *dma);
+
+u32 sst_dsp_get_offset(struct sst_dsp *dsp, u32 offset,
+       enum sst_mem_type type);
 #endif
index cd23060..86e4108 100644 (file)
@@ -245,6 +245,29 @@ int sst_dsp_boot(struct sst_dsp *sst)
 }
 EXPORT_SYMBOL_GPL(sst_dsp_boot);
 
+int sst_dsp_wake(struct sst_dsp *sst)
+{
+       if (sst->ops->wake)
+               return sst->ops->wake(sst);
+
+       return 0;
+}
+EXPORT_SYMBOL_GPL(sst_dsp_wake);
+
+void sst_dsp_sleep(struct sst_dsp *sst)
+{
+       if (sst->ops->sleep)
+               sst->ops->sleep(sst);
+}
+EXPORT_SYMBOL_GPL(sst_dsp_sleep);
+
+void sst_dsp_stall(struct sst_dsp *sst)
+{
+       if (sst->ops->stall)
+               sst->ops->stall(sst);
+}
+EXPORT_SYMBOL_GPL(sst_dsp_stall);
+
 void sst_dsp_ipc_msg_tx(struct sst_dsp *dsp, u32 msg)
 {
        sst_dsp_shim_write_unlocked(dsp, SST_IPCX, msg | SST_IPCX_BUSY);
@@ -352,6 +375,7 @@ struct sst_dsp *sst_dsp_new(struct device *dev,
        INIT_LIST_HEAD(&sst->free_block_list);
        INIT_LIST_HEAD(&sst->module_list);
        INIT_LIST_HEAD(&sst->fw_list);
+       INIT_LIST_HEAD(&sst->scratch_block_list);
 
        /* Initialise SST Audio DSP */
        if (sst->ops->init) {
@@ -366,6 +390,10 @@ struct sst_dsp *sst_dsp_new(struct device *dev,
        if (err)
                goto irq_err;
 
+       err = sst_dma_new(sst);
+       if (err)
+               dev_warn(dev, "sst_dma_new failed %d\n", err);
+
        return sst;
 
 irq_err:
@@ -381,6 +409,9 @@ void sst_dsp_free(struct sst_dsp *sst)
        free_irq(sst->irq, sst);
        if (sst->ops->free)
                sst->ops->free(sst);
+
+       if (sst->dma)
+               sst_dma_free(sst->dma);
 }
 EXPORT_SYMBOL_GPL(sst_dsp_free);
 
index 3165dfa..f291e32 100644 (file)
@@ -30,6 +30,9 @@
 #define SST_DMA_TYPE_DW                1
 #define SST_DMA_TYPE_MID       2
 
+/* autosuspend delay 5s*/
+#define SST_RUNTIME_SUSPEND_DELAY      (5 * 1000)
+
 /* SST Shim register map
  * The register naming can differ between products. Some products also
  * contain extra functionality.
 #define SST_VDRTCTL3           0xaC
 
 /* VDRTCTL0 */
-#define SST_VDRTCL0_APLLSE_MASK                1
-#define SST_VDRTCL0_DSRAMPGE_SHIFT     16
-#define SST_VDRTCL0_DSRAMPGE_MASK      (0xffff << SST_VDRTCL0_DSRAMPGE_SHIFT)
-#define SST_VDRTCL0_ISRAMPGE_SHIFT     6
+#define SST_VDRTCL0_D3PGD              (1 << 0)
+#define SST_VDRTCL0_D3SRAMPGD          (1 << 1)
+#define SST_VDRTCL0_DSRAMPGE_SHIFT     12
+#define SST_VDRTCL0_DSRAMPGE_MASK      (0xfffff << SST_VDRTCL0_DSRAMPGE_SHIFT)
+#define SST_VDRTCL0_ISRAMPGE_SHIFT     2
 #define SST_VDRTCL0_ISRAMPGE_MASK      (0x3ff << SST_VDRTCL0_ISRAMPGE_SHIFT)
 
+/* VDRTCTL2 */
+#define SST_VDRTCL2_DCLCGE             (1 << 1)
+#define SST_VDRTCL2_DTCGE              (1 << 10)
+#define SST_VDRTCL2_APLLSE_MASK                (1 << 31)
+
 /* PMCS */
 #define SST_PMCS               0x84
 #define SST_PMCS_PS_MASK       0x3
@@ -245,6 +254,17 @@ void sst_memcpy_fromio_32(struct sst_dsp *sst,
 /* DSP reset & boot */
 void sst_dsp_reset(struct sst_dsp *sst);
 int sst_dsp_boot(struct sst_dsp *sst);
+int sst_dsp_wake(struct sst_dsp *sst);
+void sst_dsp_sleep(struct sst_dsp *sst);
+void sst_dsp_stall(struct sst_dsp *sst);
+
+/* DMA */
+int sst_dsp_dma_get_channel(struct sst_dsp *dsp, int chan_id);
+void sst_dsp_dma_put_channel(struct sst_dsp *dsp);
+int sst_dsp_dma_copyfrom(struct sst_dsp *sst, dma_addr_t dest_addr,
+       dma_addr_t src_addr, size_t size);
+int sst_dsp_dma_copyto(struct sst_dsp *sst, dma_addr_t dest_addr,
+       dma_addr_t src_addr, size_t size);
 
 /* Msg IO */
 void sst_dsp_ipc_msg_tx(struct sst_dsp *dsp, u32 msg);
index 3bb43da..4a5bde9 100644 (file)
 #include <linux/dma-mapping.h>
 #include <linux/dmaengine.h>
 #include <linux/pci.h>
+#include <linux/acpi.h>
+
+/* supported DMA engine drivers */
+#include <linux/platform_data/dma-dw.h>
+#include <linux/dma/dw.h>
 
 #include <asm/page.h>
 #include <asm/pgtable.h>
 #include "sst-dsp.h"
 #include "sst-dsp-priv.h"
 
-static void block_module_remove(struct sst_module *module);
+#define SST_DMA_RESOURCES      2
+#define SST_DSP_DMA_MAX_BURST  0x3
+#define SST_HSW_BLOCK_ANY      0xffffffff
+
+#define SST_HSW_MASK_DMA_ADDR_DSP 0xfff00000
+
+struct sst_dma {
+       struct sst_dsp *sst;
+
+       struct dw_dma_chip *chip;
+
+       struct dma_async_tx_descriptor *desc;
+       struct dma_chan *ch;
+};
+
+static inline void sst_memcpy32(volatile void __iomem *dest, void *src, u32 bytes)
+{
+       /* __iowrite32_copy use 32bit size values so divide by 4 */
+       __iowrite32_copy((void *)dest, src, bytes/4);
+}
+
+static void sst_dma_transfer_complete(void *arg)
+{
+       struct sst_dsp *sst = (struct sst_dsp *)arg;
+
+       dev_dbg(sst->dev, "DMA: callback\n");
+}
+
+static int sst_dsp_dma_copy(struct sst_dsp *sst, dma_addr_t dest_addr,
+       dma_addr_t src_addr, size_t size)
+{
+       struct dma_async_tx_descriptor *desc;
+       struct sst_dma *dma = sst->dma;
+
+       if (dma->ch == NULL) {
+               dev_err(sst->dev, "error: no DMA channel\n");
+               return -ENODEV;
+       }
+
+       dev_dbg(sst->dev, "DMA: src: 0x%lx dest 0x%lx size %zu\n",
+               (unsigned long)src_addr, (unsigned long)dest_addr, size);
+
+       desc = dma->ch->device->device_prep_dma_memcpy(dma->ch, dest_addr,
+               src_addr, size, DMA_CTRL_ACK);
+       if (!desc){
+               dev_err(sst->dev, "error: dma prep memcpy failed\n");
+               return -EINVAL;
+       }
+
+       desc->callback = sst_dma_transfer_complete;
+       desc->callback_param = sst;
+
+       desc->tx_submit(desc);
+       dma_wait_for_async_tx(desc);
+
+       return 0;
+}
+
+/* copy to DSP */
+int sst_dsp_dma_copyto(struct sst_dsp *sst, dma_addr_t dest_addr,
+       dma_addr_t src_addr, size_t size)
+{
+       return sst_dsp_dma_copy(sst, dest_addr | SST_HSW_MASK_DMA_ADDR_DSP,
+                       src_addr, size);
+}
+EXPORT_SYMBOL_GPL(sst_dsp_dma_copyto);
+
+/* copy from DSP */
+int sst_dsp_dma_copyfrom(struct sst_dsp *sst, dma_addr_t dest_addr,
+       dma_addr_t src_addr, size_t size)
+{
+       return sst_dsp_dma_copy(sst, dest_addr,
+               src_addr | SST_HSW_MASK_DMA_ADDR_DSP, size);
+}
+EXPORT_SYMBOL_GPL(sst_dsp_dma_copyfrom);
+
+/* remove module from memory - callers hold locks */
+static void block_list_remove(struct sst_dsp *dsp,
+       struct list_head *block_list)
+{
+       struct sst_mem_block *block, *tmp;
+       int err;
+
+       /* disable each block  */
+       list_for_each_entry(block, block_list, module_list) {
+
+               if (block->ops && block->ops->disable) {
+                       err = block->ops->disable(block);
+                       if (err < 0)
+                               dev_err(dsp->dev,
+                                       "error: cant disable block %d:%d\n",
+                                       block->type, block->index);
+               }
+       }
+
+       /* mark each block as free */
+       list_for_each_entry_safe(block, tmp, block_list, module_list) {
+               list_del(&block->module_list);
+               list_move(&block->list, &dsp->free_block_list);
+               dev_dbg(dsp->dev, "block freed %d:%d at offset 0x%x\n",
+                       block->type, block->index, block->offset);
+       }
+}
+
+/* prepare the memory block to receive data from host - callers hold locks */
+static int block_list_prepare(struct sst_dsp *dsp,
+       struct list_head *block_list)
+{
+       struct sst_mem_block *block;
+       int ret = 0;
+
+       /* enable each block so that's it'e ready for data */
+       list_for_each_entry(block, block_list, module_list) {
+
+               if (block->ops && block->ops->enable && !block->users) {
+                       ret = block->ops->enable(block);
+                       if (ret < 0) {
+                               dev_err(dsp->dev,
+                                       "error: cant disable block %d:%d\n",
+                                       block->type, block->index);
+                               goto err;
+                       }
+               }
+       }
+       return ret;
+
+err:
+       list_for_each_entry(block, block_list, module_list) {
+               if (block->ops && block->ops->disable)
+                       block->ops->disable(block);
+       }
+       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)
+{
+       struct dw_dma_chip *chip;
+       int err;
+
+       chip = devm_kzalloc(dev, sizeof(*chip), GFP_KERNEL);
+       if (!chip)
+               return ERR_PTR(-ENOMEM);
+
+       chip->irq = irq;
+       chip->regs = devm_ioremap_resource(dev, mem);
+       if (IS_ERR(chip->regs))
+               return ERR_CAST(chip->regs);
+
+       err = dma_coerce_mask_and_coherent(dev, DMA_BIT_MASK(31));
+       if (err)
+               return ERR_PTR(err);
+
+       chip->dev = dev;
+       err = dw_dma_probe(chip, &dw_pdata);
+       if (err)
+               return ERR_PTR(err);
+
+       return chip;
+}
+
+static void dw_remove(struct dw_dma_chip *chip)
+{
+       dw_dma_remove(chip);
+}
+
+static bool dma_chan_filter(struct dma_chan *chan, void *param)
+{
+       struct sst_dsp *dsp = (struct sst_dsp *)param;
+
+       return chan->device->dev == dsp->dma_dev;
+}
 
-static void sst_memcpy32(volatile void __iomem *dest, void *src, u32 bytes)
+int sst_dsp_dma_get_channel(struct sst_dsp *dsp, int chan_id)
 {
-       u32 i;
+       struct sst_dma *dma = dsp->dma;
+       struct dma_slave_config slave;
+       dma_cap_mask_t mask;
+       int ret;
+
+       /* The Intel MID DMA engine driver needs the slave config set but
+        * Synopsis DMA engine driver safely ignores the slave config */
+       dma_cap_zero(mask);
+       dma_cap_set(DMA_SLAVE, mask);
+       dma_cap_set(DMA_MEMCPY, mask);
+
+       dma->ch = dma_request_channel(mask, dma_chan_filter, dsp);
+       if (dma->ch == NULL) {
+               dev_err(dsp->dev, "error: DMA request channel failed\n");
+               return -EIO;
+       }
+
+       memset(&slave, 0, sizeof(slave));
+       slave.direction = DMA_MEM_TO_DEV;
+       slave.src_addr_width =
+               slave.dst_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
+       slave.src_maxburst = slave.dst_maxburst = SST_DSP_DMA_MAX_BURST;
+
+       ret = dmaengine_slave_config(dma->ch, &slave);
+       if (ret) {
+               dev_err(dsp->dev, "error: unable to set DMA slave config %d\n",
+                       ret);
+               dma_release_channel(dma->ch);
+               dma->ch = NULL;
+       }
 
-       /* copy one 32 bit word at a time as 64 bit access is not supported */
-       for (i = 0; i < bytes; i += 4)
-               memcpy_toio(dest + i, src + i, 4);
+       return ret;
 }
+EXPORT_SYMBOL_GPL(sst_dsp_dma_get_channel);
+
+void sst_dsp_dma_put_channel(struct sst_dsp *dsp)
+{
+       struct sst_dma *dma = dsp->dma;
+
+       if (!dma->ch)
+               return;
+
+       dma_release_channel(dma->ch);
+       dma->ch = NULL;
+}
+EXPORT_SYMBOL_GPL(sst_dsp_dma_put_channel);
+
+int sst_dma_new(struct sst_dsp *sst)
+{
+       struct sst_pdata *sst_pdata = sst->pdata;
+       struct sst_dma *dma;
+       struct resource mem;
+       const char *dma_dev_name;
+       int ret = 0;
+
+       /* configure the correct platform data for whatever DMA engine
+       * is attached to the ADSP IP. */
+       switch (sst->pdata->dma_engine) {
+       case SST_DMA_TYPE_DW:
+               dma_dev_name = "dw_dmac";
+               break;
+       case SST_DMA_TYPE_MID:
+               dma_dev_name = "Intel MID DMA";
+               break;
+       default:
+               dev_err(sst->dev, "error: invalid DMA engine %d\n",
+                       sst->pdata->dma_engine);
+               return -EINVAL;
+       }
+
+       dma = devm_kzalloc(sst->dev, sizeof(struct sst_dma), GFP_KERNEL);
+       if (!dma)
+               return -ENOMEM;
+
+       dma->sst = sst;
+
+       memset(&mem, 0, sizeof(mem));
+
+       mem.start = sst->addr.lpe_base + sst_pdata->dma_base;
+       mem.end   = sst->addr.lpe_base + sst_pdata->dma_base + sst_pdata->dma_size - 1;
+       mem.flags = IORESOURCE_MEM;
+
+       /* now register DMA engine device */
+       dma->chip = dw_probe(sst->dma_dev, &mem, sst_pdata->irq);
+       if (IS_ERR(dma->chip)) {
+               dev_err(sst->dev, "error: DMA device register failed\n");
+               ret = PTR_ERR(dma->chip);
+               goto err_dma_dev;
+       }
+
+       sst->dma = dma;
+       sst->fw_use_dma = true;
+       return 0;
+
+err_dma_dev:
+       devm_kfree(sst->dev, dma);
+       return ret;
+}
+EXPORT_SYMBOL(sst_dma_new);
+
+void sst_dma_free(struct sst_dma *dma)
+{
+
+       if (dma == NULL)
+               return;
+
+       if (dma->ch)
+               dma_release_channel(dma->ch);
+
+       if (dma->chip)
+               dw_remove(dma->chip);
+
+}
+EXPORT_SYMBOL(sst_dma_free);
 
 /* create new generic firmware object */
 struct sst_fw *sst_fw_new(struct sst_dsp *dsp, 
@@ -71,6 +361,12 @@ struct sst_fw *sst_fw_new(struct sst_dsp *dsp,
        /* copy FW data to DMA-able memory */
        memcpy((void *)sst_fw->dma_buf, (void *)fw->data, fw->size);
 
+       if (dsp->fw_use_dma) {
+               err = sst_dsp_dma_get_channel(dsp, 0);
+               if (err < 0)
+                       goto chan_err;
+       }
+
        /* call core specific FW paser to load FW data into DSP */
        err = dsp->ops->parse_fw(sst_fw);
        if (err < 0) {
@@ -78,6 +374,9 @@ struct sst_fw *sst_fw_new(struct sst_dsp *dsp,
                goto parse_err;
        }
 
+       if (dsp->fw_use_dma)
+               sst_dsp_dma_put_channel(dsp);
+
        mutex_lock(&dsp->mutex);
        list_add(&sst_fw->list, &dsp->fw_list);
        mutex_unlock(&dsp->mutex);
@@ -85,9 +384,13 @@ struct sst_fw *sst_fw_new(struct sst_dsp *dsp,
        return sst_fw;
 
 parse_err:
-       dma_free_coherent(dsp->dev, sst_fw->size,
+       if (dsp->fw_use_dma)
+               sst_dsp_dma_put_channel(dsp);
+chan_err:
+       dma_free_coherent(dsp->dma_dev, sst_fw->size,
                                sst_fw->dma_buf,
                                sst_fw->dmable_fw_paddr);
+       sst_fw->dma_buf = NULL;
        kfree(sst_fw);
        return NULL;
 }
@@ -111,21 +414,37 @@ EXPORT_SYMBOL_GPL(sst_fw_reload);
 
 void sst_fw_unload(struct sst_fw *sst_fw)
 {
-        struct sst_dsp *dsp = sst_fw->dsp;
-        struct sst_module *module, *tmp;
+       struct sst_dsp *dsp = sst_fw->dsp;
+       struct sst_module *module, *mtmp;
+       struct sst_module_runtime *runtime, *rtmp;
+
+       dev_dbg(dsp->dev, "unloading firmware\n");
+
+       mutex_lock(&dsp->mutex);
+
+       /* check module by module */
+       list_for_each_entry_safe(module, mtmp, &dsp->module_list, list) {
+               if (module->sst_fw == sst_fw) {
+
+                       /* remove runtime modules */
+                       list_for_each_entry_safe(runtime, rtmp, &module->runtime_list, list) {
 
-        dev_dbg(dsp->dev, "unloading firmware\n");
+                               block_list_remove(dsp, &runtime->block_list);
+                               list_del(&runtime->list);
+                               kfree(runtime);
+                       }
+
+                       /* now remove the module */
+                       block_list_remove(dsp, &module->block_list);
+                       list_del(&module->list);
+                       kfree(module);
+               }
+       }
 
-        mutex_lock(&dsp->mutex);
-        list_for_each_entry_safe(module, tmp, &dsp->module_list, list) {
-                if (module->sst_fw == sst_fw) {
-                        block_module_remove(module);
-                        list_del(&module->list);
-                        kfree(module);
-                }
-        }
+       /* remove all scratch blocks */
+       block_list_remove(dsp, &dsp->scratch_block_list);
 
-        mutex_unlock(&dsp->mutex);
+       mutex_unlock(&dsp->mutex);
 }
 EXPORT_SYMBOL_GPL(sst_fw_unload);
 
@@ -138,7 +457,8 @@ void sst_fw_free(struct sst_fw *sst_fw)
        list_del(&sst_fw->list);
        mutex_unlock(&dsp->mutex);
 
-       dma_free_coherent(dsp->dma_dev, sst_fw->size, sst_fw->dma_buf,
+       if (sst_fw->dma_buf)
+               dma_free_coherent(dsp->dma_dev, sst_fw->size, sst_fw->dma_buf,
                        sst_fw->dmable_fw_paddr);
        kfree(sst_fw);
 }
@@ -175,11 +495,11 @@ struct sst_module *sst_module_new(struct sst_fw *sst_fw,
        sst_module->id = template->id;
        sst_module->dsp = dsp;
        sst_module->sst_fw = sst_fw;
-
-       memcpy(&sst_module->s, &template->s, sizeof(struct sst_module_data));
-       memcpy(&sst_module->p, &template->p, sizeof(struct sst_module_data));
+       sst_module->scratch_size = template->scratch_size;
+       sst_module->persistent_size = template->persistent_size;
 
        INIT_LIST_HEAD(&sst_module->block_list);
+       INIT_LIST_HEAD(&sst_module->runtime_list);
 
        mutex_lock(&dsp->mutex);
        list_add(&sst_module->list, &dsp->module_list);
@@ -202,73 +522,122 @@ void sst_module_free(struct sst_module *sst_module)
 }
 EXPORT_SYMBOL_GPL(sst_module_free);
 
-static struct sst_mem_block *find_block(struct sst_dsp *dsp, int type,
-       u32 offset)
+struct sst_module_runtime *sst_module_runtime_new(struct sst_module *module,
+       int id, void *private)
+{
+       struct sst_dsp *dsp = module->dsp;
+       struct sst_module_runtime *runtime;
+
+       runtime = kzalloc(sizeof(*runtime), GFP_KERNEL);
+       if (runtime == NULL)
+               return NULL;
+
+       runtime->id = id;
+       runtime->dsp = dsp;
+       runtime->module = module;
+       INIT_LIST_HEAD(&runtime->block_list);
+
+       mutex_lock(&dsp->mutex);
+       list_add(&runtime->list, &module->runtime_list);
+       mutex_unlock(&dsp->mutex);
+
+       return runtime;
+}
+EXPORT_SYMBOL_GPL(sst_module_runtime_new);
+
+void sst_module_runtime_free(struct sst_module_runtime *runtime)
+{
+       struct sst_dsp *dsp = runtime->dsp;
+
+       mutex_lock(&dsp->mutex);
+       list_del(&runtime->list);
+       mutex_unlock(&dsp->mutex);
+
+       kfree(runtime);
+}
+EXPORT_SYMBOL_GPL(sst_module_runtime_free);
+
+static struct sst_mem_block *find_block(struct sst_dsp *dsp,
+       struct sst_block_allocator *ba)
 {
        struct sst_mem_block *block;
 
        list_for_each_entry(block, &dsp->free_block_list, list) {
-               if (block->type == type && block->offset == offset)
+               if (block->type == ba->type && block->offset == ba->offset)
                        return block;
        }
 
        return NULL;
 }
 
-static int block_alloc_contiguous(struct sst_module *module,
-       struct sst_module_data *data, u32 offset, int size)
+/* Block allocator must be on block boundary */
+static int block_alloc_contiguous(struct sst_dsp *dsp,
+       struct sst_block_allocator *ba, struct list_head *block_list)
 {
        struct list_head tmp = LIST_HEAD_INIT(tmp);
-       struct sst_dsp *dsp = module->dsp;
        struct sst_mem_block *block;
+       u32 block_start = SST_HSW_BLOCK_ANY;
+       int size = ba->size, offset = ba->offset;
+
+       while (ba->size > 0) {
 
-       while (size > 0) {
-               block = find_block(dsp, data->type, offset);
+               block = find_block(dsp, ba);
                if (!block) {
                        list_splice(&tmp, &dsp->free_block_list);
+
+                       ba->size = size;
+                       ba->offset = offset;
                        return -ENOMEM;
                }
 
                list_move_tail(&block->list, &tmp);
-               offset += block->size;
-               size -= block->size;
+               ba->offset += block->size;
+               ba->size -= block->size;
        }
+       ba->size = size;
+       ba->offset = offset;
+
+       list_for_each_entry(block, &tmp, list) {
+
+               if (block->offset < block_start)
+                       block_start = block->offset;
 
-       list_for_each_entry(block, &tmp, list)
-               list_add(&block->module_list, &module->block_list);
+               list_add(&block->module_list, block_list);
+
+               dev_dbg(dsp->dev, "block allocated %d:%d at offset 0x%x\n",
+                       block->type, block->index, block->offset);
+       }
 
        list_splice(&tmp, &dsp->used_block_list);
        return 0;
 }
 
-/* allocate free DSP blocks for module data - callers hold locks */
-static int block_alloc(struct sst_module *module,
-       struct sst_module_data *data)
+/* allocate first free DSP blocks for data - callers hold locks */
+static int block_alloc(struct sst_dsp *dsp, struct sst_block_allocator *ba,
+       struct list_head *block_list)
 {
-       struct sst_dsp *dsp = module->dsp;
        struct sst_mem_block *block, *tmp;
        int ret = 0;
 
-       if (data->size == 0)
+       if (ba->size == 0)
                return 0;
 
        /* find first free whole blocks that can hold module */
        list_for_each_entry_safe(block, tmp, &dsp->free_block_list, list) {
 
                /* ignore blocks with wrong type */
-               if (block->type != data->type)
+               if (block->type != ba->type)
                        continue;
 
-               if (data->size > block->size)
+               if (ba->size > block->size)
                        continue;
 
-               data->offset = block->offset;
-               block->data_type = data->data_type;
-               block->bytes_used = data->size % block->size;
-               list_add(&block->module_list, &module->block_list);
+               ba->offset = block->offset;
+               block->bytes_used = ba->size % block->size;
+               list_add(&block->module_list, block_list);
                list_move(&block->list, &dsp->used_block_list);
-               dev_dbg(dsp->dev, " *module %d added block %d:%d\n",
-                       module->id, block->type, block->index);
+               dev_dbg(dsp->dev, "block allocated %d:%d at offset 0x%x\n",
+                       block->type, block->index, block->offset);
                return 0;
        }
 
@@ -276,15 +645,19 @@ static int block_alloc(struct sst_module *module,
        list_for_each_entry_safe(block, tmp, &dsp->free_block_list, list) {
 
                /* ignore blocks with wrong type */
-               if (block->type != data->type)
+               if (block->type != ba->type)
                        continue;
 
                /* do we span > 1 blocks */
-               if (data->size > block->size) {
-                       ret = block_alloc_contiguous(module, data,
-                               block->offset, data->size);
+               if (ba->size > block->size) {
+
+                       /* align ba to block boundary */
+                       ba->offset = block->offset;
+
+                       ret = block_alloc_contiguous(dsp, ba, block_list);
                        if (ret == 0)
                                return ret;
+
                }
        }
 
@@ -292,93 +665,74 @@ static int block_alloc(struct sst_module *module,
        return -ENOMEM;
 }
 
-/* remove module from memory - callers hold locks */
-static void block_module_remove(struct sst_module *module)
+int sst_alloc_blocks(struct sst_dsp *dsp, struct sst_block_allocator *ba,
+       struct list_head *block_list)
 {
-       struct sst_mem_block *block, *tmp;
-       struct sst_dsp *dsp = module->dsp;
-       int err;
+       int ret;
 
-       /* disable each block  */
-       list_for_each_entry(block, &module->block_list, module_list) {
+       dev_dbg(dsp->dev, "block request 0x%x bytes at offset 0x%x type %d\n",
+               ba->size, ba->offset, ba->type);
 
-               if (block->ops && block->ops->disable) {
-                       err = block->ops->disable(block);
-                       if (err < 0)
-                               dev_err(dsp->dev,
-                                       "error: cant disable block %d:%d\n",
-                                       block->type, block->index);
-               }
-       }
+       mutex_lock(&dsp->mutex);
 
-       /* mark each block as free */
-       list_for_each_entry_safe(block, tmp, &module->block_list, module_list) {
-               list_del(&block->module_list);
-               list_move(&block->list, &dsp->free_block_list);
+       ret = block_alloc(dsp, ba, block_list);
+       if (ret < 0) {
+               dev_err(dsp->dev, "error: can't alloc blocks %d\n", ret);
+               goto out;
        }
-}
-
-/* prepare the memory block to receive data from host - callers hold locks */
-static int block_module_prepare(struct sst_module *module)
-{
-       struct sst_mem_block *block;
-       int ret = 0;
 
-       /* enable each block so that's it'e ready for module P/S data */
-       list_for_each_entry(block, &module->block_list, module_list) {
+       /* prepare DSP blocks for module usage */
+       ret = block_list_prepare(dsp, block_list);
+       if (ret < 0)
+               dev_err(dsp->dev, "error: prepare failed\n");
 
-               if (block->ops && block->ops->enable) {
-                       ret = block->ops->enable(block);
-                       if (ret < 0) {
-                               dev_err(module->dsp->dev,
-                                       "error: cant disable block %d:%d\n",
-                                       block->type, block->index);
-                               goto err;
-                       }
-               }
-       }
+out:
+       mutex_unlock(&dsp->mutex);
        return ret;
+}
+EXPORT_SYMBOL_GPL(sst_alloc_blocks);
 
-err:
-       list_for_each_entry(block, &module->block_list, module_list) {
-               if (block->ops && block->ops->disable)
-                       block->ops->disable(block);
-       }
-       return ret;
+int sst_free_blocks(struct sst_dsp *dsp, struct list_head *block_list)
+{
+       mutex_lock(&dsp->mutex);
+       block_list_remove(dsp, block_list);
+       mutex_unlock(&dsp->mutex);
+       return 0;
 }
+EXPORT_SYMBOL_GPL(sst_free_blocks);
 
 /* allocate memory blocks for static module addresses - callers hold locks */
-static int block_alloc_fixed(struct sst_module *module,
-       struct sst_module_data *data)
+static int block_alloc_fixed(struct sst_dsp *dsp, struct sst_block_allocator *ba,
+       struct list_head *block_list)
 {
-       struct sst_dsp *dsp = module->dsp;
        struct sst_mem_block *block, *tmp;
-       u32 end = data->offset + data->size, block_end;
+       u32 end = ba->offset + ba->size, block_end;
        int err;
 
        /* only IRAM/DRAM blocks are managed */
-       if (data->type != SST_MEM_IRAM && data->type != SST_MEM_DRAM)
+       if (ba->type != SST_MEM_IRAM && ba->type != SST_MEM_DRAM)
                return 0;
 
        /* are blocks already attached to this module */
-       list_for_each_entry_safe(block, tmp, &module->block_list, module_list) {
+       list_for_each_entry_safe(block, tmp, block_list, module_list) {
 
-               /* force compacting mem blocks of the same data_type */
-               if (block->data_type != data->data_type)
+               /* ignore blocks with wrong type */
+               if (block->type != ba->type)
                        continue;
 
                block_end = block->offset + block->size;
 
                /* find block that holds section */
-               if (data->offset >= block->offset && end < block_end)
+               if (ba->offset >= block->offset && end <= block_end)
                        return 0;
 
                /* does block span more than 1 section */
-               if (data->offset >= block->offset && data->offset < block_end) {
+               if (ba->offset >= block->offset && ba->offset < block_end) {
 
-                       err = block_alloc_contiguous(module, data,
-                               block->offset + block->size,
-                               data->size - block->size);
+                       /* align ba to block boundary */
+                       ba->size -= block_end - ba->offset;
+                       ba->offset = block_end;
+                       err = block_alloc_contiguous(dsp, ba, block_list);
                        if (err < 0)
                                return -ENOMEM;
 
@@ -391,82 +745,270 @@ static int block_alloc_fixed(struct sst_module *module,
        list_for_each_entry_safe(block, tmp, &dsp->free_block_list, list) {
                block_end = block->offset + block->size;
 
+               /* ignore blocks with wrong type */
+               if (block->type != ba->type)
+                       continue;
+
                /* find block that holds section */
-               if (data->offset >= block->offset && end < block_end) {
+               if (ba->offset >= block->offset && end <= block_end) {
 
                        /* add block */
-                       block->data_type = data->data_type;
                        list_move(&block->list, &dsp->used_block_list);
-                       list_add(&block->module_list, &module->block_list);
+                       list_add(&block->module_list, block_list);
+                       dev_dbg(dsp->dev, "block allocated %d:%d at offset 0x%x\n",
+                               block->type, block->index, block->offset);
                        return 0;
                }
 
                /* does block span more than 1 section */
-               if (data->offset >= block->offset && data->offset < block_end) {
+               if (ba->offset >= block->offset && ba->offset < block_end) {
 
-                       err = block_alloc_contiguous(module, data,
-                               block->offset, data->size);
+                       /* align ba to block boundary */
+                       ba->offset = block->offset;
+
+                       err = block_alloc_contiguous(dsp, ba, block_list);
                        if (err < 0)
                                return -ENOMEM;
 
                        return 0;
                }
-
        }
 
        return -ENOMEM;
 }
 
 /* Load fixed module data into DSP memory blocks */
-int sst_module_insert_fixed_block(struct sst_module *module,
-       struct sst_module_data *data)
+int sst_module_alloc_blocks(struct sst_module *module)
 {
        struct sst_dsp *dsp = module->dsp;
+       struct sst_fw *sst_fw = module->sst_fw;
+       struct sst_block_allocator ba;
        int ret;
 
+       ba.size = module->size;
+       ba.type = module->type;
+       ba.offset = module->offset;
+
+       dev_dbg(dsp->dev, "block request 0x%x bytes at offset 0x%x type %d\n",
+               ba.size, ba.offset, ba.type);
+
        mutex_lock(&dsp->mutex);
 
        /* alloc blocks that includes this section */
-       ret = block_alloc_fixed(module, data);
+       ret = block_alloc_fixed(dsp, &ba, &module->block_list);
        if (ret < 0) {
                dev_err(dsp->dev,
                        "error: no free blocks for section at offset 0x%x size 0x%x\n",
-                       data->offset, data->size);
+                       module->offset, module->size);
                mutex_unlock(&dsp->mutex);
                return -ENOMEM;
        }
 
        /* prepare DSP blocks for module copy */
-       ret = block_module_prepare(module);
+       ret = block_list_prepare(dsp, &module->block_list);
        if (ret < 0) {
                dev_err(dsp->dev, "error: fw module prepare failed\n");
                goto err;
        }
 
        /* copy partial module data to blocks */
-       sst_memcpy32(dsp->addr.lpe + data->offset, data->data, data->size);
+       if (dsp->fw_use_dma) {
+               ret = sst_dsp_dma_copyto(dsp,
+                       dsp->addr.lpe_base + module->offset,
+                       sst_fw->dmable_fw_paddr + module->data_offset,
+                       module->size);
+               if (ret < 0) {
+                       dev_err(dsp->dev, "error: module copy failed\n");
+                       goto err;
+               }
+       } else
+               sst_memcpy32(dsp->addr.lpe + module->offset, module->data,
+                       module->size);
 
        mutex_unlock(&dsp->mutex);
        return ret;
 
 err:
-       block_module_remove(module);
+       block_list_remove(dsp, &module->block_list);
        mutex_unlock(&dsp->mutex);
        return ret;
 }
-EXPORT_SYMBOL_GPL(sst_module_insert_fixed_block);
+EXPORT_SYMBOL_GPL(sst_module_alloc_blocks);
 
 /* Unload entire module from DSP memory */
-int sst_block_module_remove(struct sst_module *module)
+int sst_module_free_blocks(struct sst_module *module)
 {
        struct sst_dsp *dsp = module->dsp;
 
        mutex_lock(&dsp->mutex);
-       block_module_remove(module);
+       block_list_remove(dsp, &module->block_list);
        mutex_unlock(&dsp->mutex);
        return 0;
 }
-EXPORT_SYMBOL_GPL(sst_block_module_remove);
+EXPORT_SYMBOL_GPL(sst_module_free_blocks);
+
+int sst_module_runtime_alloc_blocks(struct sst_module_runtime *runtime,
+       int offset)
+{
+       struct sst_dsp *dsp = runtime->dsp;
+       struct sst_module *module = runtime->module;
+       struct sst_block_allocator ba;
+       int ret;
+
+       if (module->persistent_size == 0)
+               return 0;
+
+       ba.size = module->persistent_size;
+       ba.type = SST_MEM_DRAM;
+
+       mutex_lock(&dsp->mutex);
+
+       /* do we need to allocate at a fixed address ? */
+       if (offset != 0) {
+
+               ba.offset = offset;
+
+               dev_dbg(dsp->dev, "persistent fixed block request 0x%x bytes type %d offset 0x%x\n",
+                       ba.size, ba.type, ba.offset);
+
+               /* alloc blocks that includes this section */
+               ret = block_alloc_fixed(dsp, &ba, &runtime->block_list);
+
+       } else {
+               dev_dbg(dsp->dev, "persistent block request 0x%x bytes type %d\n",
+                       ba.size, ba.type);
+
+               /* alloc blocks that includes this section */
+               ret = block_alloc(dsp, &ba, &runtime->block_list);
+       }
+       if (ret < 0) {
+               dev_err(dsp->dev,
+               "error: no free blocks for runtime module size 0x%x\n",
+                       module->persistent_size);
+               mutex_unlock(&dsp->mutex);
+               return -ENOMEM;
+       }
+       runtime->persistent_offset = ba.offset;
+
+       /* prepare DSP blocks for module copy */
+       ret = block_list_prepare(dsp, &runtime->block_list);
+       if (ret < 0) {
+               dev_err(dsp->dev, "error: runtime block prepare failed\n");
+               goto err;
+       }
+
+       mutex_unlock(&dsp->mutex);
+       return ret;
+
+err:
+       block_list_remove(dsp, &module->block_list);
+       mutex_unlock(&dsp->mutex);
+       return ret;
+}
+EXPORT_SYMBOL_GPL(sst_module_runtime_alloc_blocks);
+
+int sst_module_runtime_free_blocks(struct sst_module_runtime *runtime)
+{
+       struct sst_dsp *dsp = runtime->dsp;
+
+       mutex_lock(&dsp->mutex);
+       block_list_remove(dsp, &runtime->block_list);
+       mutex_unlock(&dsp->mutex);
+       return 0;
+}
+EXPORT_SYMBOL_GPL(sst_module_runtime_free_blocks);
+
+int sst_module_runtime_save(struct sst_module_runtime *runtime,
+       struct sst_module_runtime_context *context)
+{
+       struct sst_dsp *dsp = runtime->dsp;
+       struct sst_module *module = runtime->module;
+       int ret = 0;
+
+       dev_dbg(dsp->dev, "saving runtime %d memory at 0x%x size 0x%x\n",
+               runtime->id, runtime->persistent_offset,
+               module->persistent_size);
+
+       context->buffer = dma_alloc_coherent(dsp->dma_dev,
+               module->persistent_size,
+               &context->dma_buffer, GFP_DMA | GFP_KERNEL);
+       if (!context->buffer) {
+               dev_err(dsp->dev, "error: DMA context alloc failed\n");
+               return -ENOMEM;
+       }
+
+       mutex_lock(&dsp->mutex);
+
+       if (dsp->fw_use_dma) {
+
+               ret = sst_dsp_dma_get_channel(dsp, 0);
+               if (ret < 0)
+                       goto err;
+
+               ret = sst_dsp_dma_copyfrom(dsp, context->dma_buffer,
+                       dsp->addr.lpe_base + runtime->persistent_offset,
+                       module->persistent_size);
+               sst_dsp_dma_put_channel(dsp);
+               if (ret < 0) {
+                       dev_err(dsp->dev, "error: context copy failed\n");
+                       goto err;
+               }
+       } else
+               sst_memcpy32(context->buffer, dsp->addr.lpe +
+                       runtime->persistent_offset,
+                       module->persistent_size);
+
+err:
+       mutex_unlock(&dsp->mutex);
+       return ret;
+}
+EXPORT_SYMBOL_GPL(sst_module_runtime_save);
+
+int sst_module_runtime_restore(struct sst_module_runtime *runtime,
+       struct sst_module_runtime_context *context)
+{
+       struct sst_dsp *dsp = runtime->dsp;
+       struct sst_module *module = runtime->module;
+       int ret = 0;
+
+       dev_dbg(dsp->dev, "restoring runtime %d memory at 0x%x size 0x%x\n",
+               runtime->id, runtime->persistent_offset,
+               module->persistent_size);
+
+       mutex_lock(&dsp->mutex);
+
+       if (!context->buffer) {
+               dev_info(dsp->dev, "no context buffer need to restore!\n");
+               goto err;
+       }
+
+       if (dsp->fw_use_dma) {
+
+               ret = sst_dsp_dma_get_channel(dsp, 0);
+               if (ret < 0)
+                       goto err;
+
+               ret = sst_dsp_dma_copyto(dsp,
+                       dsp->addr.lpe_base + runtime->persistent_offset,
+                       context->dma_buffer, module->persistent_size);
+               sst_dsp_dma_put_channel(dsp);
+               if (ret < 0) {
+                       dev_err(dsp->dev, "error: module copy failed\n");
+                       goto err;
+               }
+       } else
+               sst_memcpy32(dsp->addr.lpe + runtime->persistent_offset,
+                       context->buffer, module->persistent_size);
+
+       dma_free_coherent(dsp->dma_dev, module->persistent_size,
+                               context->buffer, context->dma_buffer);
+       context->buffer = NULL;
+
+err:
+       mutex_unlock(&dsp->mutex);
+       return ret;
+}
+EXPORT_SYMBOL_GPL(sst_module_runtime_restore);
 
 /* register a DSP memory block for use with FW based modules */
 struct sst_mem_block *sst_mem_block_register(struct sst_dsp *dsp, u32 offset,
@@ -519,80 +1061,84 @@ void sst_mem_block_unregister_all(struct sst_dsp *dsp)
 EXPORT_SYMBOL_GPL(sst_mem_block_unregister_all);
 
 /* allocate scratch buffer blocks */
-struct sst_module *sst_mem_block_alloc_scratch(struct sst_dsp *dsp)
+int sst_block_alloc_scratch(struct sst_dsp *dsp)
 {
-       struct sst_module *sst_module, *scratch;
-       struct sst_mem_block *block, *tmp;
-       u32 block_size;
-       int ret = 0;
-
-       scratch = kzalloc(sizeof(struct sst_module), GFP_KERNEL);
-       if (scratch == NULL)
-               return NULL;
+       struct sst_module *module;
+       struct sst_block_allocator ba;
+       int ret;
 
        mutex_lock(&dsp->mutex);
 
        /* calculate required scratch size */
-       list_for_each_entry(sst_module, &dsp->module_list, list) {
-               if (scratch->s.size < sst_module->s.size)
-                       scratch->s.size = sst_module->s.size;
+       dsp->scratch_size = 0;
+       list_for_each_entry(module, &dsp->module_list, list) {
+               dev_dbg(dsp->dev, "module %d scratch req 0x%x bytes\n",
+                       module->id, module->scratch_size);
+               if (dsp->scratch_size < module->scratch_size)
+                       dsp->scratch_size = module->scratch_size;
        }
 
-       dev_dbg(dsp->dev, "scratch buffer required is %d bytes\n",
-               scratch->s.size);
-
-       /* init scratch module */
-       scratch->dsp = dsp;
-       scratch->s.type = SST_MEM_DRAM;
-       scratch->s.data_type = SST_DATA_S;
-       INIT_LIST_HEAD(&scratch->block_list);
+       dev_dbg(dsp->dev, "scratch buffer required is 0x%x bytes\n",
+               dsp->scratch_size);
 
-       /* check free blocks before looking at used blocks for space */
-       if (!list_empty(&dsp->free_block_list))
-               block = list_first_entry(&dsp->free_block_list,
-                       struct sst_mem_block, list);
-       else
-               block = list_first_entry(&dsp->used_block_list,
-                       struct sst_mem_block, list);
-       block_size = block->size;
+       if (dsp->scratch_size == 0) {
+               dev_info(dsp->dev, "no modules need scratch buffer\n");
+               mutex_unlock(&dsp->mutex);
+               return 0;
+       }
 
        /* allocate blocks for module scratch buffers */
        dev_dbg(dsp->dev, "allocating scratch blocks\n");
-       ret = block_alloc(scratch, &scratch->s);
+
+       ba.size = dsp->scratch_size;
+       ba.type = SST_MEM_DRAM;
+
+       /* do we need to allocate at fixed offset */
+       if (dsp->scratch_offset != 0) {
+
+               dev_dbg(dsp->dev, "block request 0x%x bytes type %d at 0x%x\n",
+                       ba.size, ba.type, ba.offset);
+
+               ba.offset = dsp->scratch_offset;
+
+               /* alloc blocks that includes this section */
+               ret = block_alloc_fixed(dsp, &ba, &dsp->scratch_block_list);
+
+       } else {
+               dev_dbg(dsp->dev, "block request 0x%x bytes type %d\n",
+                       ba.size, ba.type);
+
+               ba.offset = 0;
+               ret = block_alloc(dsp, &ba, &dsp->scratch_block_list);
+       }
        if (ret < 0) {
                dev_err(dsp->dev, "error: can't alloc scratch blocks\n");
-               goto err;
+               mutex_unlock(&dsp->mutex);
+               return ret;
        }
 
-       /* assign the same offset of scratch to each module */
-       list_for_each_entry(sst_module, &dsp->module_list, list)
-               sst_module->s.offset = scratch->s.offset;
-
-       mutex_unlock(&dsp->mutex);
-       return scratch;
+       ret = block_list_prepare(dsp, &dsp->scratch_block_list);
+       if (ret < 0) {
+               dev_err(dsp->dev, "error: scratch block prepare failed\n");
+               mutex_unlock(&dsp->mutex);
+               return ret;
+       }
 
-err:
-       list_for_each_entry_safe(block, tmp, &scratch->block_list, module_list)
-               list_del(&block->module_list);
+       /* assign the same offset of scratch to each module */
+       dsp->scratch_offset = ba.offset;
        mutex_unlock(&dsp->mutex);
-       return NULL;
+       return dsp->scratch_size;
 }
-EXPORT_SYMBOL_GPL(sst_mem_block_alloc_scratch);
+EXPORT_SYMBOL_GPL(sst_block_alloc_scratch);
 
 /* free all scratch blocks */
-void sst_mem_block_free_scratch(struct sst_dsp *dsp,
-       struct sst_module *scratch)
+void sst_block_free_scratch(struct sst_dsp *dsp)
 {
-       struct sst_mem_block *block, *tmp;
-
        mutex_lock(&dsp->mutex);
-
-       list_for_each_entry_safe(block, tmp, &scratch->block_list, module_list)
-               list_del(&block->module_list);
-
+       block_list_remove(dsp, &dsp->scratch_block_list);
        mutex_unlock(&dsp->mutex);
 }
-EXPORT_SYMBOL_GPL(sst_mem_block_free_scratch);
+EXPORT_SYMBOL_GPL(sst_block_free_scratch);
 
 /* get a module from it's unique ID */
 struct sst_module *sst_module_get_from_id(struct sst_dsp *dsp, u32 id)
@@ -612,3 +1158,40 @@ struct sst_module *sst_module_get_from_id(struct sst_dsp *dsp, u32 id)
        return NULL;
 }
 EXPORT_SYMBOL_GPL(sst_module_get_from_id);
+
+struct sst_module_runtime *sst_module_runtime_get_from_id(
+       struct sst_module *module, u32 id)
+{
+       struct sst_module_runtime *runtime;
+       struct sst_dsp *dsp = module->dsp;
+
+       mutex_lock(&dsp->mutex);
+
+       list_for_each_entry(runtime, &module->runtime_list, list) {
+               if (runtime->id == id) {
+                       mutex_unlock(&dsp->mutex);
+                       return runtime;
+               }
+       }
+
+       mutex_unlock(&dsp->mutex);
+       return NULL;
+}
+EXPORT_SYMBOL_GPL(sst_module_runtime_get_from_id);
+
+/* returns block address in DSP address space */
+u32 sst_dsp_get_offset(struct sst_dsp *dsp, u32 offset,
+       enum sst_mem_type type)
+{
+       switch (type) {
+       case SST_MEM_IRAM:
+               return offset - dsp->addr.iram_offset +
+                       dsp->addr.dsp_iram_offset;
+       case SST_MEM_DRAM:
+               return offset - dsp->addr.dram_offset +
+                       dsp->addr.dsp_dram_offset;
+       default:
+               return 0;
+       }
+}
+EXPORT_SYMBOL_GPL(sst_dsp_get_offset);
index 4b6c163..57039b0 100644 (file)
 #define SST_LP_SHIM_OFFSET     0xE7000
 #define SST_WPT_IRAM_OFFSET    0xA0000
 #define SST_LP_IRAM_OFFSET     0x80000
+#define SST_WPT_DSP_DRAM_OFFSET        0x400000
+#define SST_WPT_DSP_IRAM_OFFSET        0x00000
+#define SST_LPT_DSP_DRAM_OFFSET        0x400000
+#define SST_LPT_DSP_IRAM_OFFSET        0x00000
 
 #define SST_SHIM_PM_REG                0x84
 
@@ -86,9 +90,8 @@ static int hsw_parse_module(struct sst_dsp *dsp, struct sst_fw *fw,
 {
        struct dma_block_info *block;
        struct sst_module *mod;
-       struct sst_module_data block_data;
        struct sst_module_template template;
-       int count;
+       int count, ret;
        void __iomem *ram;
 
        /* TODO: allowed module types need to be configurable */
@@ -109,13 +112,9 @@ static int hsw_parse_module(struct sst_dsp *dsp, struct sst_fw *fw,
 
        memset(&template, 0, sizeof(template));
        template.id = module->type;
-       template.entry = module->entry_point;
-       template.p.size = module->info.persistent_size;
-       template.p.type = SST_MEM_DRAM;
-       template.p.data_type = SST_DATA_P;
-       template.s.size = module->info.scratch_size;
-       template.s.type = SST_MEM_DRAM;
-       template.s.data_type = SST_DATA_S;
+       template.entry = module->entry_point - 4;
+       template.persistent_size = module->info.persistent_size;
+       template.scratch_size = module->info.scratch_size;
 
        mod = sst_module_new(fw, &template, NULL);
        if (mod == NULL)
@@ -135,14 +134,14 @@ static int hsw_parse_module(struct sst_dsp *dsp, struct sst_fw *fw,
                switch (block->type) {
                case SST_HSW_IRAM:
                        ram = dsp->addr.lpe;
-                       block_data.offset =
+                       mod->offset =
                                block->ram_offset + dsp->addr.iram_offset;
-                       block_data.type = SST_MEM_IRAM;
+                       mod->type = SST_MEM_IRAM;
                        break;
                case SST_HSW_DRAM:
                        ram = dsp->addr.lpe;
-                       block_data.offset = block->ram_offset;
-                       block_data.type = SST_MEM_DRAM;
+                       mod->offset = block->ram_offset;
+                       mod->type = SST_MEM_DRAM;
                        break;
                default:
                        dev_err(dsp->dev, "error: bad type 0x%x for block 0x%x\n",
@@ -151,30 +150,34 @@ static int hsw_parse_module(struct sst_dsp *dsp, struct sst_fw *fw,
                        return -EINVAL;
                }
 
-               block_data.size = block->size;
-               block_data.data_type = SST_DATA_M;
-               block_data.data = (void *)block + sizeof(*block);
-               block_data.data_offset = block_data.data - fw->dma_buf;
+               mod->size = block->size;
+               mod->data = (void *)block + sizeof(*block);
+               mod->data_offset = mod->data - fw->dma_buf;
 
-               dev_dbg(dsp->dev, "copy firmware block %d type 0x%x "
+               dev_dbg(dsp->dev, "module block %d type 0x%x "
                        "size 0x%x ==> ram %p offset 0x%x\n",
-                       count, block->type, block->size, ram,
+                       count, mod->type, block->size, ram,
                        block->ram_offset);
 
-               sst_module_insert_fixed_block(mod, &block_data);
+               ret = sst_module_alloc_blocks(mod);
+               if (ret < 0) {
+                       dev_err(dsp->dev, "error: could not allocate blocks for module %d\n",
+                               count);
+                       sst_module_free(mod);
+                       return ret;
+               }
 
                block = (void *)block + sizeof(*block) + block->size;
        }
+
        return 0;
 }
 
 static int hsw_parse_fw_image(struct sst_fw *sst_fw)
 {
        struct fw_header *header;
-       struct sst_module *scratch;
        struct fw_module_header *module;
        struct sst_dsp *dsp = sst_fw->dsp;
-       struct sst_hsw *hsw = sst_fw->private;
        int ret, count;
 
        /* Read the header information from the data pointer */
@@ -204,12 +207,8 @@ static int hsw_parse_fw_image(struct sst_fw *sst_fw)
                module = (void *)module + sizeof(*module) + module->mod_size;
        }
 
-       /* allocate persistent/scratch mem regions */
-       scratch = sst_mem_block_alloc_scratch(dsp);
-       if (scratch == NULL)
-               return -ENOMEM;
-
-       sst_hsw_set_scratch_module(hsw, scratch);
+       /* allocate scratch mem regions */
+       sst_block_alloc_scratch(dsp);
 
        return 0;
 }
@@ -248,8 +247,94 @@ static irqreturn_t hsw_irq(int irq, void *context)
        return ret;
 }
 
-static void hsw_boot(struct sst_dsp *sst)
+static void hsw_set_dsp_D3(struct sst_dsp *sst)
+{
+       u32 val;
+       u32 reg;
+
+       /* Disable core clock gating (VDRTCTL2.DCLCGE = 0) */
+       reg = readl(sst->addr.pci_cfg + SST_VDRTCTL2);
+       reg &= ~(SST_VDRTCL2_DCLCGE | SST_VDRTCL2_DTCGE);
+       writel(reg, sst->addr.pci_cfg + SST_VDRTCTL2);
+
+       /* enable power gating and switch off DRAM & IRAM blocks */
+       val = readl(sst->addr.pci_cfg + SST_VDRTCTL0);
+       val |= SST_VDRTCL0_DSRAMPGE_MASK |
+               SST_VDRTCL0_ISRAMPGE_MASK;
+       val &= ~(SST_VDRTCL0_D3PGD | SST_VDRTCL0_D3SRAMPGD);
+       writel(val, sst->addr.pci_cfg + SST_VDRTCTL0);
+
+       /* switch off audio PLL */
+       val = readl(sst->addr.pci_cfg + SST_VDRTCTL2);
+       val |= SST_VDRTCL2_APLLSE_MASK;
+       writel(val, sst->addr.pci_cfg + SST_VDRTCTL2);
+
+       /* disable MCLK(clkctl.smos = 0) */
+       sst_dsp_shim_update_bits_unlocked(sst, SST_CLKCTL,
+               SST_CLKCTL_MASK, 0);
+
+       /* Set D3 state, delay 50 us */
+       val = readl(sst->addr.pci_cfg + SST_PMCS);
+       val |= SST_PMCS_PS_MASK;
+       writel(val, sst->addr.pci_cfg + SST_PMCS);
+       udelay(50);
+
+       /* Enable core clock gating (VDRTCTL2.DCLCGE = 1), delay 50 us */
+       reg = readl(sst->addr.pci_cfg + SST_VDRTCTL2);
+       reg |= SST_VDRTCL2_DCLCGE | SST_VDRTCL2_DTCGE;
+       writel(reg, sst->addr.pci_cfg + SST_VDRTCTL2);
+
+       udelay(50);
+
+}
+
+static void hsw_reset(struct sst_dsp *sst)
 {
+       /* put DSP into reset and stall */
+       sst_dsp_shim_update_bits_unlocked(sst, SST_CSR,
+               SST_CSR_RST | SST_CSR_STALL,
+               SST_CSR_RST | SST_CSR_STALL);
+
+       /* keep in reset for 10ms */
+       mdelay(10);
+
+       /* take DSP out of reset and keep stalled for FW loading */
+       sst_dsp_shim_update_bits_unlocked(sst, SST_CSR,
+               SST_CSR_RST | SST_CSR_STALL, SST_CSR_STALL);
+}
+
+static int hsw_set_dsp_D0(struct sst_dsp *sst)
+{
+       int tries = 10;
+       u32 reg;
+
+       /* Disable core clock gating (VDRTCTL2.DCLCGE = 0) */
+       reg = readl(sst->addr.pci_cfg + SST_VDRTCTL2);
+       reg &= ~(SST_VDRTCL2_DCLCGE | SST_VDRTCL2_DTCGE);
+       writel(reg, sst->addr.pci_cfg + SST_VDRTCTL2);
+
+       /* Disable D3PG (VDRTCTL0.D3PGD = 1) */
+       reg = readl(sst->addr.pci_cfg + SST_VDRTCTL0);
+       reg |= SST_VDRTCL0_D3PGD;
+       writel(reg, sst->addr.pci_cfg + SST_VDRTCTL0);
+
+       /* Set D0 state */
+       reg = readl(sst->addr.pci_cfg + SST_PMCS);
+       reg &= ~SST_PMCS_PS_MASK;
+       writel(reg, sst->addr.pci_cfg + SST_PMCS);
+
+       /* check that ADSP shim is enabled */
+       while (tries--) {
+               reg = readl(sst->addr.pci_cfg + SST_PMCS) & SST_PMCS_PS_MASK;
+               if (reg == 0)
+                       goto finish;
+
+               msleep(1);
+       }
+
+       return -ENODEV;
+
+finish:
        /* select SSP1 19.2MHz base clock, SSP clock 0, turn off Low Power Clock */
        sst_dsp_shim_update_bits_unlocked(sst, SST_CSR,
                SST_CSR_S1IOCS | SST_CSR_SBCS1 | SST_CSR_LPCS, 0x0);
@@ -264,34 +349,96 @@ static void hsw_boot(struct sst_dsp *sst)
                SST_CLKCTL_MASK | SST_CLKCTL_DCPLCG | SST_CLKCTL_SCOE0,
                SST_CLKCTL_MASK | SST_CLKCTL_DCPLCG | SST_CLKCTL_SCOE0);
 
+       /* Stall and reset core, set CSR */
+       hsw_reset(sst);
+
+       /* Enable core clock gating (VDRTCTL2.DCLCGE = 1), delay 50 us */
+       reg = readl(sst->addr.pci_cfg + SST_VDRTCTL2);
+       reg |= SST_VDRTCL2_DCLCGE | SST_VDRTCL2_DTCGE;
+       writel(reg, sst->addr.pci_cfg + SST_VDRTCTL2);
+
+       udelay(50);
+
+       /* switch on audio PLL */
+       reg = readl(sst->addr.pci_cfg + SST_VDRTCTL2);
+       reg &= ~SST_VDRTCL2_APLLSE_MASK;
+       writel(reg, sst->addr.pci_cfg + SST_VDRTCTL2);
+
+       /* set default power gating control, enable power gating control for all blocks. that is,
+       can't be accessed, please enable each block before accessing. */
+       reg = readl(sst->addr.pci_cfg + SST_VDRTCTL0);
+       reg |= SST_VDRTCL0_DSRAMPGE_MASK | SST_VDRTCL0_ISRAMPGE_MASK;
+       writel(reg, sst->addr.pci_cfg + SST_VDRTCTL0);
+
+
        /* disable DMA finish function for SSP0 & SSP1 */
        sst_dsp_shim_update_bits_unlocked(sst, SST_CSR2, SST_CSR2_SDFD_SSP1,
                SST_CSR2_SDFD_SSP1);
 
-       /* enable DMA engine 0,1 all channels to access host memory */
-       sst_dsp_shim_update_bits_unlocked(sst, SST_HMDC,
-               SST_HMDC_HDDA1(0xff) | SST_HMDC_HDDA0(0xff),
-               SST_HMDC_HDDA1(0xff) | SST_HMDC_HDDA0(0xff));
+       /* set on-demond mode on engine 0,1 for all channels */
+       sst_dsp_shim_update_bits(sst, SST_HMDC,
+                       SST_HMDC_HDDA_E0_ALLCH | SST_HMDC_HDDA_E1_ALLCH,
+                       SST_HMDC_HDDA_E0_ALLCH | SST_HMDC_HDDA_E1_ALLCH);
+
+       /* Enable Interrupt from both sides */
+       sst_dsp_shim_update_bits(sst, SST_IMRX, (SST_IMRX_BUSY | SST_IMRX_DONE),
+                                0x0);
+       sst_dsp_shim_update_bits(sst, SST_IMRD, (SST_IMRD_DONE | SST_IMRD_BUSY |
+                               SST_IMRD_SSP0 | SST_IMRD_DMAC), 0x0);
+
+       /* clear IPC registers */
+       sst_dsp_shim_write(sst, SST_IPCX, 0x0);
+       sst_dsp_shim_write(sst, SST_IPCD, 0x0);
+       sst_dsp_shim_write(sst, 0x80, 0x6);
+       sst_dsp_shim_write(sst, 0xe0, 0x300a);
+
+       return 0;
+}
 
-       /* disable all clock gating */
-       writel(0x0, sst->addr.pci_cfg + SST_VDRTCTL2);
+static void hsw_boot(struct sst_dsp *sst)
+{
+       /* set oportunistic mode on engine 0,1 for all channels */
+       sst_dsp_shim_update_bits(sst, SST_HMDC,
+                       SST_HMDC_HDDA_E0_ALLCH | SST_HMDC_HDDA_E1_ALLCH, 0);
 
        /* set DSP to RUN */
        sst_dsp_shim_update_bits_unlocked(sst, SST_CSR, SST_CSR_STALL, 0x0);
 }
 
-static void hsw_reset(struct sst_dsp *sst)
+static void hsw_stall(struct sst_dsp *sst)
+{
+       /* stall DSP */
+       sst_dsp_shim_update_bits(sst, SST_CSR,
+               SST_CSR_24MHZ_LPCS | SST_CSR_STALL,
+               SST_CSR_STALL | SST_CSR_24MHZ_LPCS);
+}
+
+static void hsw_sleep(struct sst_dsp *sst)
 {
+       dev_dbg(sst->dev, "HSW_PM dsp runtime suspend\n");
+
        /* put DSP into reset and stall */
-       sst_dsp_shim_update_bits_unlocked(sst, SST_CSR,
-               SST_CSR_RST | SST_CSR_STALL, SST_CSR_RST | SST_CSR_STALL);
+       sst_dsp_shim_update_bits(sst, SST_CSR,
+               SST_CSR_24MHZ_LPCS | SST_CSR_RST | SST_CSR_STALL,
+               SST_CSR_RST | SST_CSR_STALL | SST_CSR_24MHZ_LPCS);
 
-       /* keep in reset for 10ms */
-       mdelay(10);
+       hsw_set_dsp_D3(sst);
+       dev_dbg(sst->dev, "HSW_PM dsp runtime suspend exit\n");
+}
 
-       /* take DSP out of reset and keep stalled for FW loading */
-       sst_dsp_shim_update_bits_unlocked(sst, SST_CSR,
-               SST_CSR_RST | SST_CSR_STALL, SST_CSR_STALL);
+static int hsw_wake(struct sst_dsp *sst)
+{
+       int ret;
+
+       dev_dbg(sst->dev, "HSW_PM dsp runtime resume\n");
+
+       ret = hsw_set_dsp_D0(sst);
+       if (ret < 0)
+               return ret;
+
+       dev_dbg(sst->dev, "HSW_PM dsp runtime resume exit\n");
+
+       return 0;
 }
 
 struct sst_adsp_memregion {
@@ -396,6 +543,11 @@ static int hsw_block_enable(struct sst_mem_block *block)
        dev_dbg(block->dsp->dev, " enabled block %d:%d at offset 0x%x\n",
                block->type, block->index, block->offset);
 
+       /* Disable core clock gating (VDRTCTL2.DCLCGE = 0) */
+       val = readl(sst->addr.pci_cfg + SST_VDRTCTL2);
+       val &= ~SST_VDRTCL2_DCLCGE;
+       writel(val, sst->addr.pci_cfg + SST_VDRTCTL2);
+
        val = readl(sst->addr.pci_cfg + SST_VDRTCTL0);
        bit = hsw_block_get_bit(block);
        writel(val & ~bit, sst->addr.pci_cfg + SST_VDRTCTL0);
@@ -403,6 +555,13 @@ static int hsw_block_enable(struct sst_mem_block *block)
        /* wait 18 DSP clock ticks */
        udelay(10);
 
+       /* Enable core clock gating (VDRTCTL2.DCLCGE = 1), delay 50 us */
+       val = readl(sst->addr.pci_cfg + SST_VDRTCTL2);
+       val |= SST_VDRTCL2_DCLCGE;
+       writel(val, sst->addr.pci_cfg + SST_VDRTCTL2);
+
+       udelay(50);
+
        /*add a dummy read before the SRAM block is written, otherwise the writing may miss bytes sometimes.*/
        sst_mem_block_dummy_read(block);
        return 0;
@@ -420,10 +579,26 @@ static int hsw_block_disable(struct sst_mem_block *block)
        dev_dbg(block->dsp->dev, " disabled block %d:%d at offset 0x%x\n",
                block->type, block->index, block->offset);
 
+       /* Disable core clock gating (VDRTCTL2.DCLCGE = 0) */
+       val = readl(sst->addr.pci_cfg + SST_VDRTCTL2);
+       val &= ~SST_VDRTCL2_DCLCGE;
+       writel(val, sst->addr.pci_cfg + SST_VDRTCTL2);
+
+
        val = readl(sst->addr.pci_cfg + SST_VDRTCTL0);
        bit = hsw_block_get_bit(block);
        writel(val | bit, sst->addr.pci_cfg + SST_VDRTCTL0);
 
+       /* wait 18 DSP clock ticks */
+       udelay(10);
+
+       /* Enable core clock gating (VDRTCTL2.DCLCGE = 1), delay 50 us */
+       val = readl(sst->addr.pci_cfg + SST_VDRTCTL2);
+       val |= SST_VDRTCL2_DCLCGE;
+       writel(val, sst->addr.pci_cfg + SST_VDRTCTL2);
+
+       udelay(50);
+
        return 0;
 }
 
@@ -432,27 +607,6 @@ static struct sst_block_ops sst_hsw_ops = {
        .disable = hsw_block_disable,
 };
 
-static int hsw_enable_shim(struct sst_dsp *sst)
-{
-       int tries = 10;
-       u32 reg;
-
-       /* enable shim */
-       reg = readl(sst->addr.pci_cfg + SST_SHIM_PM_REG);
-       writel(reg & ~0x3, sst->addr.pci_cfg + SST_SHIM_PM_REG);
-
-       /* check that ADSP shim is enabled */
-       while (tries--) {
-               reg = sst_dsp_shim_read_unlocked(sst, SST_CSR);
-               if (reg != 0xffffffff)
-                       return 0;
-
-               msleep(1);
-       }
-
-       return -ENODEV;
-}
-
 static int hsw_init(struct sst_dsp *sst, struct sst_pdata *pdata)
 {
        const struct sst_adsp_memregion *region;
@@ -467,12 +621,16 @@ static int hsw_init(struct sst_dsp *sst, struct sst_pdata *pdata)
                region = lp_region;
                region_count = ARRAY_SIZE(lp_region);
                sst->addr.iram_offset = SST_LP_IRAM_OFFSET;
+               sst->addr.dsp_iram_offset = SST_LPT_DSP_IRAM_OFFSET;
+               sst->addr.dsp_dram_offset = SST_LPT_DSP_DRAM_OFFSET;
                sst->addr.shim_offset = SST_LP_SHIM_OFFSET;
                break;
        case SST_DEV_ID_WILDCAT_POINT:
                region = wpt_region;
                region_count = ARRAY_SIZE(wpt_region);
                sst->addr.iram_offset = SST_WPT_IRAM_OFFSET;
+               sst->addr.dsp_iram_offset = SST_WPT_DSP_IRAM_OFFSET;
+               sst->addr.dsp_dram_offset = SST_WPT_DSP_DRAM_OFFSET;
                sst->addr.shim_offset = SST_WPT_SHIM_OFFSET;
                break;
        default:
@@ -487,7 +645,7 @@ static int hsw_init(struct sst_dsp *sst, struct sst_pdata *pdata)
        }
 
        /* enable the DSP SHIM */
-       ret = hsw_enable_shim(sst);
+       ret = hsw_set_dsp_D0(sst);
        if (ret < 0) {
                dev_err(dev, "error: failed to set DSP D0 and reset SHIM\n");
                return ret;
@@ -497,10 +655,6 @@ static int hsw_init(struct sst_dsp *sst, struct sst_pdata *pdata)
        if (ret)
                return ret;
 
-       /* Enable Interrupt from both sides */
-       sst_dsp_shim_update_bits_unlocked(sst, SST_IMRX, 0x3, 0x0);
-       sst_dsp_shim_update_bits_unlocked(sst, SST_IMRD,
-               (0x3 | 0x1 << 16 | 0x3 << 21), 0x0);
 
        /* register DSP memory blocks - ideally we should get this from ACPI */
        for (i = 0; i < region_count; i++) {
@@ -532,6 +686,9 @@ static void hsw_free(struct sst_dsp *sst)
 struct sst_ops haswell_ops = {
        .reset = hsw_reset,
        .boot = hsw_boot,
+       .stall = hsw_stall,
+       .wake = hsw_wake,
+       .sleep = hsw_sleep,
        .write = sst_shim32_write,
        .read = sst_shim32_read,
        .write64 = sst_shim32_write64,
index b629151..3f8c482 100644 (file)
@@ -30,6 +30,7 @@
 #include <linux/firmware.h>
 #include <linux/dma-mapping.h>
 #include <linux/debugfs.h>
+#include <linux/pm_runtime.h>
 
 #include "sst-haswell-ipc.h"
 #include "sst-dsp.h"
@@ -276,6 +277,7 @@ struct sst_hsw {
        struct sst_hsw_ipc_fw_version version;
        struct sst_module *scratch;
        bool fw_done;
+       struct sst_fw *sst_fw;
 
        /* stream */
        struct list_head stream_list;
@@ -289,6 +291,8 @@ struct sst_hsw {
 
        /* DX */
        struct sst_hsw_ipc_dx_reply dx;
+       void *dx_context;
+       dma_addr_t dx_context_paddr;
 
        /* boot */
        wait_queue_head_t boot_wait;
@@ -1038,14 +1042,9 @@ int sst_hsw_stream_set_volume(struct sst_hsw *hsw,
 
        trace_ipc_request("set stream volume", stream->reply.stream_hw_id);
 
-       if (channel > 1)
+       if (channel >= 2 && channel != SST_HSW_CHANNELS_ALL)
                return -EINVAL;
 
-       if (stream->mute[channel]) {
-               stream->mute_volume[channel] = volume;
-               return 0;
-       }
-
        header = IPC_GLB_TYPE(IPC_GLB_STREAM_MESSAGE) |
                IPC_STR_TYPE(IPC_STR_STAGE_MESSAGE);
        header |= (stream->reply.stream_hw_id << IPC_STR_ID_SHIFT);
@@ -1053,9 +1052,28 @@ int sst_hsw_stream_set_volume(struct sst_hsw *hsw,
        header |= (stage_id << IPC_STG_ID_SHIFT);
 
        req = &stream->vol_req;
-       req->channel = channel;
        req->target_volume = volume;
 
+       /* set both at same time ? */
+       if (channel == SST_HSW_CHANNELS_ALL) {
+               if (hsw->mute[0] && hsw->mute[1]) {
+                       hsw->mute_volume[0] = hsw->mute_volume[1] = volume;
+                       return 0;
+               } else if (hsw->mute[0])
+                       req->channel = 1;
+               else if (hsw->mute[1])
+                       req->channel = 0;
+               else
+                       req->channel = SST_HSW_CHANNELS_ALL;
+       } else {
+               /* set only 1 channel */
+               if (hsw->mute[channel]) {
+                       hsw->mute_volume[channel] = volume;
+                       return 0;
+               }
+               req->channel = channel;
+       }
+
        ret = ipc_tx_message_wait(hsw, header, req, sizeof(*req), NULL, 0);
        if (ret < 0) {
                dev_err(hsw->dev, "error: set stream volume failed\n");
@@ -1134,8 +1152,11 @@ int sst_hsw_mixer_set_volume(struct sst_hsw *hsw, u32 stage_id, u32 channel,
 
        trace_ipc_request("set mixer volume", volume);
 
+       if (channel >= 2 && channel != SST_HSW_CHANNELS_ALL)
+               return -EINVAL;
+
        /* set both at same time ? */
-       if (channel == 2) {
+       if (channel == SST_HSW_CHANNELS_ALL) {
                if (hsw->mute[0] && hsw->mute[1]) {
                        hsw->mute_volume[0] = hsw->mute_volume[1] = volume;
                        return 0;
@@ -1144,7 +1165,7 @@ int sst_hsw_mixer_set_volume(struct sst_hsw *hsw, u32 stage_id, u32 channel,
                else if (hsw->mute[1])
                        req.channel = 0;
                else
-                       req.channel = 0xffffffff;
+                       req.channel = SST_HSW_CHANNELS_ALL;
        } else {
                /* set only 1 channel */
                if (hsw->mute[channel]) {
@@ -1256,10 +1277,6 @@ int sst_hsw_stream_set_channels(struct sst_hsw *hsw,
                return -EINVAL;
        }
 
-       /* stereo is only supported atm */
-       if (channels != 2)
-               return -EINVAL;
-
        stream->request.format.ch_num = channels;
        return 0;
 }
@@ -1355,10 +1372,11 @@ int sst_hsw_stream_buffer(struct sst_hsw *hsw, struct sst_hsw_stream *stream,
 }
 
 int sst_hsw_stream_set_module_info(struct sst_hsw *hsw,
-       struct sst_hsw_stream *stream, enum sst_hsw_module_id module_id,
-       u32 entry_point)
+       struct sst_hsw_stream *stream, struct sst_module_runtime *runtime)
 {
        struct sst_hsw_module_map *map = &stream->request.map;
+       struct sst_dsp *dsp = sst_hsw_get_dsp(hsw);
+       struct sst_module *module = runtime->module;
 
        if (stream->commited) {
                dev_err(hsw->dev, "error: stream committed for set module\n");
@@ -1367,36 +1385,25 @@ int sst_hsw_stream_set_module_info(struct sst_hsw *hsw,
 
        /* only support initial module atm */
        map->module_entries_count = 1;
-       map->module_entries[0].module_id = module_id;
-       map->module_entries[0].entry_point = entry_point;
-
-       return 0;
-}
-
-int sst_hsw_stream_set_pmemory_info(struct sst_hsw *hsw,
-       struct sst_hsw_stream *stream, u32 offset, u32 size)
-{
-       if (stream->commited) {
-               dev_err(hsw->dev, "error: stream committed for set pmem\n");
-               return -EINVAL;
-       }
-
-       stream->request.persistent_mem.offset = offset;
-       stream->request.persistent_mem.size = size;
-
-       return 0;
-}
-
-int sst_hsw_stream_set_smemory_info(struct sst_hsw *hsw,
-       struct sst_hsw_stream *stream, u32 offset, u32 size)
-{
-       if (stream->commited) {
-               dev_err(hsw->dev, "error: stream committed for set smem\n");
-               return -EINVAL;
-       }
-
-       stream->request.scratch_mem.offset = offset;
-       stream->request.scratch_mem.size = size;
+       map->module_entries[0].module_id = module->id;
+       map->module_entries[0].entry_point = module->entry;
+
+       stream->request.persistent_mem.offset =
+               sst_dsp_get_offset(dsp, runtime->persistent_offset, SST_MEM_DRAM);
+       stream->request.persistent_mem.size = module->persistent_size;
+
+       stream->request.scratch_mem.offset =
+               sst_dsp_get_offset(dsp, dsp->scratch_offset, SST_MEM_DRAM);
+       stream->request.scratch_mem.size = dsp->scratch_size;
+
+       dev_dbg(hsw->dev, "module %d runtime %d using:\n", module->id,
+               runtime->id);
+       dev_dbg(hsw->dev, " persistent offset 0x%x bytes 0x%x\n",
+               stream->request.persistent_mem.offset,
+               stream->request.persistent_mem.size);
+       dev_dbg(hsw->dev, " scratch offset 0x%x bytes 0x%x\n",
+               stream->request.scratch_mem.offset,
+               stream->request.scratch_mem.size);
 
        return 0;
 }
@@ -1630,6 +1637,10 @@ int sst_hsw_device_set_config(struct sst_hsw *hsw,
        config.clock_frequency = mclk;
        config.mode = mode;
        config.clock_divider = clock_divider;
+       if (mode == SST_HSW_DEVICE_TDM_CLOCK_MASTER)
+               config.channels = 4;
+       else
+               config.channels = 2;
 
        trace_hsw_device_config_req(&config);
 
@@ -1673,34 +1684,283 @@ int sst_hsw_dx_set_state(struct sst_hsw *hsw,
        dev_dbg(hsw->dev, "ipc: got %d entry numbers for state %d\n",
                dx->entries_no, state);
 
-       memcpy(&hsw->dx, dx, sizeof(*dx));
-       return 0;
+       return ret;
 }
 
-/* Used to save state into hsw->dx_reply */
-int sst_hsw_dx_get_state(struct sst_hsw *hsw, u32 item,
-       u32 *offset, u32 *size, u32 *source)
+struct sst_module_runtime *sst_hsw_runtime_module_create(struct sst_hsw *hsw,
+       int mod_id, int offset)
 {
-       struct sst_hsw_ipc_dx_memory_item *dx_mem;
-       struct sst_hsw_ipc_dx_reply *dx_reply;
-       int entry_no;
+       struct sst_dsp *dsp = hsw->dsp;
+       struct sst_module *module;
+       struct sst_module_runtime *runtime;
+       int err;
 
-       dx_reply = &hsw->dx;
-       entry_no = dx_reply->entries_no;
+       module = sst_module_get_from_id(dsp, mod_id);
+       if (module == NULL) {
+               dev_err(dsp->dev, "error: failed to get module %d for pcm\n",
+                       mod_id);
+               return NULL;
+       }
+
+       runtime = sst_module_runtime_new(module, mod_id, NULL);
+       if (runtime == NULL) {
+               dev_err(dsp->dev, "error: failed to create module %d runtime\n",
+                       mod_id);
+               return NULL;
+       }
+
+       err = sst_module_runtime_alloc_blocks(runtime, offset);
+       if (err < 0) {
+               dev_err(dsp->dev, "error: failed to alloc blocks for module %d runtime\n",
+                       mod_id);
+               sst_module_runtime_free(runtime);
+               return NULL;
+       }
+
+       dev_dbg(dsp->dev, "runtime id %d created for module %d\n", runtime->id,
+               mod_id);
+       return runtime;
+}
+
+void sst_hsw_runtime_module_free(struct sst_module_runtime *runtime)
+{
+       sst_module_runtime_free_blocks(runtime);
+       sst_module_runtime_free(runtime);
+}
+
+#ifdef CONFIG_PM
+static int sst_hsw_dx_state_dump(struct sst_hsw *hsw)
+{
+       struct sst_dsp *sst = hsw->dsp;
+       u32 item, offset, size;
+       int ret = 0;
 
-       trace_ipc_request("PM get Dx state", entry_no);
+       trace_ipc_request("PM state dump. Items #", SST_HSW_MAX_DX_REGIONS);
 
-       if (item >= entry_no)
+       if (hsw->dx.entries_no > SST_HSW_MAX_DX_REGIONS) {
+               dev_err(hsw->dev,
+                       "error: number of FW context regions greater than %d\n",
+                       SST_HSW_MAX_DX_REGIONS);
+               memset(&hsw->dx, 0, sizeof(hsw->dx));
                return -EINVAL;
+       }
+
+       ret = sst_dsp_dma_get_channel(sst, 0);
+       if (ret < 0) {
+               dev_err(hsw->dev, "error: cant allocate dma channel %d\n", ret);
+               return ret;
+       }
+
+       /* set on-demond mode on engine 0 channel 3 */
+       sst_dsp_shim_update_bits(sst, SST_HMDC,
+                       SST_HMDC_HDDA_E0_ALLCH | SST_HMDC_HDDA_E1_ALLCH,
+                       SST_HMDC_HDDA_E0_ALLCH | SST_HMDC_HDDA_E1_ALLCH);
+
+       for (item = 0; item < hsw->dx.entries_no; item++) {
+               if (hsw->dx.mem_info[item].source == SST_HSW_DX_TYPE_MEMORY_DUMP
+                       && hsw->dx.mem_info[item].offset > DSP_DRAM_ADDR_OFFSET
+                       && hsw->dx.mem_info[item].offset <
+                       DSP_DRAM_ADDR_OFFSET + SST_HSW_DX_CONTEXT_SIZE) {
+
+                       offset = hsw->dx.mem_info[item].offset
+                                       - DSP_DRAM_ADDR_OFFSET;
+                       size = (hsw->dx.mem_info[item].size + 3) & (~3);
+
+                       ret = sst_dsp_dma_copyfrom(sst, hsw->dx_context_paddr + offset,
+                               sst->addr.lpe_base + offset, size);
+                       if (ret < 0) {
+                               dev_err(hsw->dev,
+                                       "error: FW context dump failed\n");
+                               memset(&hsw->dx, 0, sizeof(hsw->dx));
+                               goto out;
+                       }
+               }
+       }
+
+out:
+       sst_dsp_dma_put_channel(sst);
+       return ret;
+}
+
+static int sst_hsw_dx_state_restore(struct sst_hsw *hsw)
+{
+       struct sst_dsp *sst = hsw->dsp;
+       u32 item, offset, size;
+       int ret;
+
+       for (item = 0; item < hsw->dx.entries_no; item++) {
+               if (hsw->dx.mem_info[item].source == SST_HSW_DX_TYPE_MEMORY_DUMP
+                       && hsw->dx.mem_info[item].offset > DSP_DRAM_ADDR_OFFSET
+                       && hsw->dx.mem_info[item].offset <
+                       DSP_DRAM_ADDR_OFFSET + SST_HSW_DX_CONTEXT_SIZE) {
+
+                       offset = hsw->dx.mem_info[item].offset
+                                       - DSP_DRAM_ADDR_OFFSET;
+                       size = (hsw->dx.mem_info[item].size + 3) & (~3);
+
+                       ret = sst_dsp_dma_copyto(sst, sst->addr.lpe_base + offset,
+                               hsw->dx_context_paddr + offset, size);
+                       if (ret < 0) {
+                               dev_err(hsw->dev,
+                                       "error: FW context restore failed\n");
+                               return ret;
+                       }
+               }
+       }
+
+       return 0;
+}
+
+static void sst_hsw_drop_all(struct sst_hsw *hsw)
+{
+       struct ipc_message *msg, *tmp;
+       unsigned long flags;
+       int tx_drop_cnt = 0, rx_drop_cnt = 0;
 
-       dx_mem = &dx_reply->mem_info[item];
-       *offset = dx_mem->offset;
-       *size = dx_mem->size;
-       *source = dx_mem->source;
+       /* drop all TX and Rx messages before we stall + reset DSP */
+       spin_lock_irqsave(&hsw->dsp->spinlock, flags);
+
+       list_for_each_entry_safe(msg, tmp, &hsw->tx_list, list) {
+               list_move(&msg->list, &hsw->empty_list);
+               tx_drop_cnt++;
+       }
+
+       list_for_each_entry_safe(msg, tmp, &hsw->rx_list, list) {
+               list_move(&msg->list, &hsw->empty_list);
+               rx_drop_cnt++;
+       }
+
+       spin_unlock_irqrestore(&hsw->dsp->spinlock, flags);
+
+       if (tx_drop_cnt || rx_drop_cnt)
+               dev_err(hsw->dev, "dropped IPC msg RX=%d, TX=%d\n",
+                       tx_drop_cnt, rx_drop_cnt);
+}
+
+int sst_hsw_dsp_load(struct sst_hsw *hsw)
+{
+       struct sst_dsp *dsp = hsw->dsp;
+       int ret;
+
+       dev_dbg(hsw->dev, "loading audio DSP....");
+
+       ret = sst_dsp_wake(dsp);
+       if (ret < 0) {
+               dev_err(hsw->dev, "error: failed to wake audio DSP\n");
+               return -ENODEV;
+       }
+
+       ret = sst_dsp_dma_get_channel(dsp, 0);
+       if (ret < 0) {
+               dev_err(hsw->dev, "error: cant allocate dma channel %d\n", ret);
+               return ret;
+       }
+
+       ret = sst_fw_reload(hsw->sst_fw);
+       if (ret < 0) {
+               dev_err(hsw->dev, "error: SST FW reload failed\n");
+               sst_dsp_dma_put_channel(dsp);
+               return -ENOMEM;
+       }
 
+       sst_dsp_dma_put_channel(dsp);
        return 0;
 }
 
+static int sst_hsw_dsp_restore(struct sst_hsw *hsw)
+{
+       struct sst_dsp *dsp = hsw->dsp;
+       int ret;
+
+       dev_dbg(hsw->dev, "restoring audio DSP....");
+
+       ret = sst_dsp_dma_get_channel(dsp, 0);
+       if (ret < 0) {
+               dev_err(hsw->dev, "error: cant allocate dma channel %d\n", ret);
+               return ret;
+       }
+
+       ret = sst_hsw_dx_state_restore(hsw);
+       if (ret < 0) {
+               dev_err(hsw->dev, "error: SST FW context restore failed\n");
+               sst_dsp_dma_put_channel(dsp);
+               return -ENOMEM;
+       }
+       sst_dsp_dma_put_channel(dsp);
+
+       /* wait for DSP boot completion */
+       sst_dsp_boot(dsp);
+
+       return ret;
+}
+
+int sst_hsw_dsp_runtime_suspend(struct sst_hsw *hsw)
+{
+       int ret;
+
+       dev_dbg(hsw->dev, "audio dsp runtime suspend\n");
+
+       ret = sst_hsw_dx_set_state(hsw, SST_HSW_DX_STATE_D3, &hsw->dx);
+       if (ret < 0)
+               return ret;
+
+       sst_dsp_stall(hsw->dsp);
+
+       ret = sst_hsw_dx_state_dump(hsw);
+       if (ret < 0)
+               return ret;
+
+       sst_hsw_drop_all(hsw);
+
+       return 0;
+}
+
+int sst_hsw_dsp_runtime_sleep(struct sst_hsw *hsw)
+{
+       sst_fw_unload(hsw->sst_fw);
+       sst_block_free_scratch(hsw->dsp);
+
+       hsw->boot_complete = false;
+
+       sst_dsp_sleep(hsw->dsp);
+
+       return 0;
+}
+
+int sst_hsw_dsp_runtime_resume(struct sst_hsw *hsw)
+{
+       struct device *dev = hsw->dev;
+       int ret;
+
+       dev_dbg(dev, "audio dsp runtime resume\n");
+
+       if (hsw->boot_complete)
+               return 1; /* tell caller no action is required */
+
+       ret = sst_hsw_dsp_restore(hsw);
+       if (ret < 0)
+               dev_err(dev, "error: audio DSP boot failure\n");
+
+       ret = wait_event_timeout(hsw->boot_wait, hsw->boot_complete,
+               msecs_to_jiffies(IPC_BOOT_MSECS));
+       if (ret == 0) {
+               dev_err(hsw->dev, "error: audio DSP boot timeout IPCD 0x%x IPCX 0x%x\n",
+                       sst_dsp_shim_read_unlocked(hsw->dsp, SST_IPCD),
+                       sst_dsp_shim_read_unlocked(hsw->dsp, SST_IPCX));
+               return -EIO;
+       }
+
+       /* Set ADSP SSP port settings */
+       ret = sst_hsw_device_set_config(hsw, SST_HSW_DEVICE_SSP_0,
+                                       SST_HSW_DEVICE_MCLK_FREQ_24_MHZ,
+                                       SST_HSW_DEVICE_CLOCK_MASTER, 9);
+       if (ret < 0)
+               dev_err(dev, "error: SSP re-initialization failed\n");
+
+       return ret;
+}
+#endif
+
 static int msg_empty_list_init(struct sst_hsw *hsw)
 {
        int i;
@@ -1718,12 +1978,6 @@ static int msg_empty_list_init(struct sst_hsw *hsw)
        return 0;
 }
 
-void sst_hsw_set_scratch_module(struct sst_hsw *hsw,
-       struct sst_module *scratch)
-{
-       hsw->scratch = scratch;
-}
-
 struct sst_dsp *sst_hsw_get_dsp(struct sst_hsw *hsw)
 {
        return hsw->dsp;
@@ -1738,7 +1992,6 @@ int sst_hsw_dsp_init(struct device *dev, struct sst_pdata *pdata)
 {
        struct sst_hsw_ipc_fw_version version;
        struct sst_hsw *hsw;
-       struct sst_fw *hsw_sst_fw;
        int ret;
 
        dev_dbg(dev, "initialising Audio DSP IPC\n");
@@ -1780,12 +2033,19 @@ int sst_hsw_dsp_init(struct device *dev, struct sst_pdata *pdata)
                goto dsp_err;
        }
 
+       /* allocate DMA buffer for context storage */
+       hsw->dx_context = dma_alloc_coherent(hsw->dsp->dma_dev,
+               SST_HSW_DX_CONTEXT_SIZE, &hsw->dx_context_paddr, GFP_KERNEL);
+       if (hsw->dx_context == NULL) {
+               ret = -ENOMEM;
+               goto dma_err;
+       }
+
        /* keep the DSP in reset state for base FW loading */
        sst_dsp_reset(hsw->dsp);
 
-       hsw_sst_fw = sst_fw_new(hsw->dsp, pdata->fw, hsw);
-
-       if (hsw_sst_fw == NULL) {
+       hsw->sst_fw = sst_fw_new(hsw->dsp, pdata->fw, hsw);
+       if (hsw->sst_fw == NULL) {
                ret = -ENODEV;
                dev_err(dev, "error: failed to load firmware\n");
                goto fw_err;
@@ -1797,7 +2057,9 @@ int sst_hsw_dsp_init(struct device *dev, struct sst_pdata *pdata)
                msecs_to_jiffies(IPC_BOOT_MSECS));
        if (ret == 0) {
                ret = -EIO;
-               dev_err(hsw->dev, "error: ADSP boot timeout\n");
+               dev_err(hsw->dev, "error: audio DSP boot timeout IPCD 0x%x IPCX 0x%x\n",
+                       sst_dsp_shim_read_unlocked(hsw->dsp, SST_IPCD),
+                       sst_dsp_shim_read_unlocked(hsw->dsp, SST_IPCX));
                goto boot_err;
        }
 
@@ -1816,8 +2078,11 @@ int sst_hsw_dsp_init(struct device *dev, struct sst_pdata *pdata)
 
 boot_err:
        sst_dsp_reset(hsw->dsp);
-       sst_fw_free(hsw_sst_fw);
+       sst_fw_free(hsw->sst_fw);
 fw_err:
+       dma_free_coherent(hsw->dsp->dma_dev, SST_HSW_DX_CONTEXT_SIZE,
+                       hsw->dx_context, hsw->dx_context_paddr);
+dma_err:
        sst_dsp_free(hsw->dsp);
 dsp_err:
        kthread_stop(hsw->tx_thread);
@@ -1834,6 +2099,8 @@ void sst_hsw_dsp_free(struct device *dev, struct sst_pdata *pdata)
 
        sst_dsp_reset(hsw->dsp);
        sst_fw_free_all(hsw->dsp);
+       dma_free_coherent(hsw->dsp->dma_dev, SST_HSW_DX_CONTEXT_SIZE,
+                       hsw->dx_context, hsw->dx_context_paddr);
        sst_dsp_free(hsw->dsp);
        kfree(hsw->scratch);
        kthread_stop(hsw->tx_thread);
index 2ac194a..138e894 100644 (file)
 #include <linux/kernel.h>
 #include <linux/platform_device.h>
 
-#define SST_HSW_NO_CHANNELS            2
+#define SST_HSW_NO_CHANNELS            4
 #define SST_HSW_MAX_DX_REGIONS         14
+#define SST_HSW_DX_CONTEXT_SIZE        (640 * 1024)
+#define SST_HSW_CHANNELS_ALL           0xffffffff
 
 #define SST_HSW_FW_LOG_CONFIG_DWORDS   12
 #define SST_HSW_GLOBAL_LOG             15
@@ -40,6 +42,7 @@ struct sst_hsw_stream;
 struct sst_hsw_log_stream;
 struct sst_pdata;
 struct sst_module;
+struct sst_module_runtime;
 extern struct sst_ops haswell_ops;
 
 /* Stream Allocate Path ID */
@@ -84,6 +87,7 @@ enum sst_hsw_device_mclk {
 enum sst_hsw_device_mode {
        SST_HSW_DEVICE_CLOCK_SLAVE   = 0,
        SST_HSW_DEVICE_CLOCK_MASTER  = 1,
+       SST_HSW_DEVICE_TDM_CLOCK_MASTER = 2,
 };
 
 /* DX Power State */
@@ -295,7 +299,8 @@ struct sst_hsw_ipc_device_config_req {
        u32 clock_frequency;
        u32 mode;
        u16 clock_divider;
-       u16 reserved;
+       u8 channels;
+       u8 reserved;
 } __attribute__((packed));
 
 /* Audio Data formats */
@@ -430,8 +435,7 @@ int sst_hsw_stream_set_map_config(struct sst_hsw *hsw,
 int sst_hsw_stream_set_style(struct sst_hsw *hsw, struct sst_hsw_stream *stream,
        enum sst_hsw_interleaving style);
 int sst_hsw_stream_set_module_info(struct sst_hsw *hsw,
-       struct sst_hsw_stream *stream, enum sst_hsw_module_id module_id,
-       u32 entry_point);
+       struct sst_hsw_stream *stream, struct sst_module_runtime *runtime);
 int sst_hsw_stream_set_pmemory_info(struct sst_hsw *hsw,
        struct sst_hsw_stream *stream, u32 offset, u32 size);
 int sst_hsw_stream_set_smemory_info(struct sst_hsw *hsw,
@@ -484,7 +488,16 @@ int sst_hsw_dx_get_state(struct sst_hsw *hsw, u32 item,
 int sst_hsw_dsp_init(struct device *dev, struct sst_pdata *pdata);
 void sst_hsw_dsp_free(struct device *dev, struct sst_pdata *pdata);
 struct sst_dsp *sst_hsw_get_dsp(struct sst_hsw *hsw);
-void sst_hsw_set_scratch_module(struct sst_hsw *hsw,
-       struct sst_module *scratch);
+
+/* runtime module management */
+struct sst_module_runtime *sst_hsw_runtime_module_create(struct sst_hsw *hsw,
+       int mod_id, int offset);
+void sst_hsw_runtime_module_free(struct sst_module_runtime *runtime);
+
+/* PM */
+int sst_hsw_dsp_runtime_resume(struct sst_hsw *hsw);
+int sst_hsw_dsp_runtime_suspend(struct sst_hsw *hsw);
+int sst_hsw_dsp_load(struct sst_hsw *hsw);
+int sst_hsw_dsp_runtime_sleep(struct sst_hsw *hsw);
 
 #endif
index 4df867c..0180b38 100644 (file)
@@ -18,6 +18,7 @@
 #include <linux/dma-mapping.h>
 #include <linux/slab.h>
 #include <linux/delay.h>
+#include <linux/pm_runtime.h>
 #include <asm/page.h>
 #include <asm/pgtable.h>
 #include <sound/core.h>
@@ -73,6 +74,13 @@ static const u32 volume_map[] = {
 #define HSW_PCM_PERIODS_MAX    64
 #define HSW_PCM_PERIODS_MIN    2
 
+#define HSW_PCM_DAI_ID_SYSTEM  0
+#define HSW_PCM_DAI_ID_OFFLOAD0        1
+#define HSW_PCM_DAI_ID_OFFLOAD1        2
+#define HSW_PCM_DAI_ID_LOOPBACK        3
+#define HSW_PCM_DAI_ID_CAPTURE 4
+
+
 static const struct snd_pcm_hardware hsw_pcm_hardware = {
        .info                   = SNDRV_PCM_INFO_MMAP |
                                  SNDRV_PCM_INFO_MMAP_VALID |
@@ -89,22 +97,39 @@ static const struct snd_pcm_hardware hsw_pcm_hardware = {
        .buffer_bytes_max       = HSW_PCM_PERIODS_MAX * PAGE_SIZE,
 };
 
+struct hsw_pcm_module_map {
+       int dai_id;
+       enum sst_hsw_module_id mod_id;
+};
+
 /* private data for each PCM DSP stream */
 struct hsw_pcm_data {
        int dai_id;
        struct sst_hsw_stream *stream;
+       struct sst_module_runtime *runtime;
+       struct sst_module_runtime_context context;
+       struct snd_pcm *hsw_pcm;
        u32 volume[2];
        struct snd_pcm_substream *substream;
        struct snd_compr_stream *cstream;
        unsigned int wpos;
        struct mutex mutex;
        bool allocated;
+       int persistent_offset;
+};
+
+enum hsw_pm_state {
+       HSW_PM_STATE_D3 = 0,
+       HSW_PM_STATE_D0 = 1,
 };
 
 /* private data for the driver */
 struct hsw_priv_data {
        /* runtime DSP */
        struct sst_hsw *hsw;
+       struct device *dev;
+       enum hsw_pm_state pm_state;
+       struct snd_soc_card *soc_card;
 
        /* page tables */
        struct snd_dma_buffer dmab[HSW_PCM_COUNT][2];
@@ -138,21 +163,25 @@ static inline unsigned int hsw_ipc_to_mixer(u32 value)
 static int hsw_stream_volume_put(struct snd_kcontrol *kcontrol,
                                struct snd_ctl_elem_value *ucontrol)
 {
-       struct snd_soc_component *cmpnt = snd_soc_kcontrol_component(kcontrol);
-       struct hsw_priv_data *pdata = snd_soc_component_get_drvdata(cmpnt);
+       struct snd_soc_platform *platform = snd_soc_kcontrol_platform(kcontrol);
        struct soc_mixer_control *mc =
                (struct soc_mixer_control *)kcontrol->private_value;
+       struct hsw_priv_data *pdata =
+               snd_soc_platform_get_drvdata(platform);
        struct hsw_pcm_data *pcm_data = &pdata->pcm[mc->reg];
        struct sst_hsw *hsw = pdata->hsw;
        u32 volume;
 
        mutex_lock(&pcm_data->mutex);
+       pm_runtime_get_sync(pdata->dev);
 
        if (!pcm_data->stream) {
                pcm_data->volume[0] =
                        hsw_mixer_to_ipc(ucontrol->value.integer.value[0]);
                pcm_data->volume[1] =
                        hsw_mixer_to_ipc(ucontrol->value.integer.value[1]);
+               pm_runtime_mark_last_busy(pdata->dev);
+               pm_runtime_put_autosuspend(pdata->dev);
                mutex_unlock(&pcm_data->mutex);
                return 0;
        }
@@ -160,7 +189,8 @@ static int hsw_stream_volume_put(struct snd_kcontrol *kcontrol,
        if (ucontrol->value.integer.value[0] ==
                ucontrol->value.integer.value[1]) {
                volume = hsw_mixer_to_ipc(ucontrol->value.integer.value[0]);
-               sst_hsw_stream_set_volume(hsw, pcm_data->stream, 0, 2, volume);
+               /* apply volume value to all channels */
+               sst_hsw_stream_set_volume(hsw, pcm_data->stream, 0, SST_HSW_CHANNELS_ALL, volume);
        } else {
                volume = hsw_mixer_to_ipc(ucontrol->value.integer.value[0]);
                sst_hsw_stream_set_volume(hsw, pcm_data->stream, 0, 0, volume);
@@ -168,6 +198,8 @@ static int hsw_stream_volume_put(struct snd_kcontrol *kcontrol,
                sst_hsw_stream_set_volume(hsw, pcm_data->stream, 0, 1, volume);
        }
 
+       pm_runtime_mark_last_busy(pdata->dev);
+       pm_runtime_put_autosuspend(pdata->dev);
        mutex_unlock(&pcm_data->mutex);
        return 0;
 }
@@ -175,21 +207,25 @@ static int hsw_stream_volume_put(struct snd_kcontrol *kcontrol,
 static int hsw_stream_volume_get(struct snd_kcontrol *kcontrol,
                                struct snd_ctl_elem_value *ucontrol)
 {
-       struct snd_soc_component *cmpnt = snd_soc_kcontrol_component(kcontrol);
-       struct hsw_priv_data *pdata = snd_soc_component_get_drvdata(cmpnt);
+       struct snd_soc_platform *platform = snd_soc_kcontrol_platform(kcontrol);
        struct soc_mixer_control *mc =
                (struct soc_mixer_control *)kcontrol->private_value;
+       struct hsw_priv_data *pdata =
+               snd_soc_platform_get_drvdata(platform);
        struct hsw_pcm_data *pcm_data = &pdata->pcm[mc->reg];
        struct sst_hsw *hsw = pdata->hsw;
        u32 volume;
 
        mutex_lock(&pcm_data->mutex);
+       pm_runtime_get_sync(pdata->dev);
 
        if (!pcm_data->stream) {
                ucontrol->value.integer.value[0] =
                        hsw_ipc_to_mixer(pcm_data->volume[0]);
                ucontrol->value.integer.value[1] =
                        hsw_ipc_to_mixer(pcm_data->volume[1]);
+               pm_runtime_mark_last_busy(pdata->dev);
+               pm_runtime_put_autosuspend(pdata->dev);
                mutex_unlock(&pcm_data->mutex);
                return 0;
        }
@@ -198,6 +234,9 @@ static int hsw_stream_volume_get(struct snd_kcontrol *kcontrol,
        ucontrol->value.integer.value[0] = hsw_ipc_to_mixer(volume);
        sst_hsw_stream_get_volume(hsw, pcm_data->stream, 0, 1, &volume);
        ucontrol->value.integer.value[1] = hsw_ipc_to_mixer(volume);
+
+       pm_runtime_mark_last_busy(pdata->dev);
+       pm_runtime_put_autosuspend(pdata->dev);
        mutex_unlock(&pcm_data->mutex);
 
        return 0;
@@ -206,16 +245,18 @@ static int hsw_stream_volume_get(struct snd_kcontrol *kcontrol,
 static int hsw_volume_put(struct snd_kcontrol *kcontrol,
                                struct snd_ctl_elem_value *ucontrol)
 {
-       struct snd_soc_component *cmpnt = snd_soc_kcontrol_component(kcontrol);
-       struct hsw_priv_data *pdata = snd_soc_component_get_drvdata(cmpnt);
+       struct snd_soc_platform *platform = snd_soc_kcontrol_platform(kcontrol);
+       struct hsw_priv_data *pdata = snd_soc_platform_get_drvdata(platform);
        struct sst_hsw *hsw = pdata->hsw;
        u32 volume;
 
+       pm_runtime_get_sync(pdata->dev);
+
        if (ucontrol->value.integer.value[0] ==
                ucontrol->value.integer.value[1]) {
 
                volume = hsw_mixer_to_ipc(ucontrol->value.integer.value[0]);
-               sst_hsw_mixer_set_volume(hsw, 0, 2, volume);
+               sst_hsw_mixer_set_volume(hsw, 0, SST_HSW_CHANNELS_ALL, volume);
 
        } else {
                volume = hsw_mixer_to_ipc(ucontrol->value.integer.value[0]);
@@ -225,23 +266,28 @@ static int hsw_volume_put(struct snd_kcontrol *kcontrol,
                sst_hsw_mixer_set_volume(hsw, 0, 1, volume);
        }
 
+       pm_runtime_mark_last_busy(pdata->dev);
+       pm_runtime_put_autosuspend(pdata->dev);
        return 0;
 }
 
 static int hsw_volume_get(struct snd_kcontrol *kcontrol,
                                struct snd_ctl_elem_value *ucontrol)
 {
-       struct snd_soc_component *cmpnt = snd_soc_kcontrol_component(kcontrol);
-       struct hsw_priv_data *pdata = snd_soc_component_get_drvdata(cmpnt);
+       struct snd_soc_platform *platform = snd_soc_kcontrol_platform(kcontrol);
+       struct hsw_priv_data *pdata = snd_soc_platform_get_drvdata(platform);
        struct sst_hsw *hsw = pdata->hsw;
        unsigned int volume = 0;
 
+       pm_runtime_get_sync(pdata->dev);
        sst_hsw_mixer_get_volume(hsw, 0, 0, &volume);
        ucontrol->value.integer.value[0] = hsw_ipc_to_mixer(volume);
 
        sst_hsw_mixer_get_volume(hsw, 0, 1, &volume);
        ucontrol->value.integer.value[1] = hsw_ipc_to_mixer(volume);
 
+       pm_runtime_mark_last_busy(pdata->dev);
+       pm_runtime_put_autosuspend(pdata->dev);
        return 0;
 }
 
@@ -252,23 +298,19 @@ static const DECLARE_TLV_DB_SCALE(hsw_vol_tlv, -9000, 300, 1);
 static const struct snd_kcontrol_new hsw_volume_controls[] = {
        /* Global DSP volume */
        SOC_DOUBLE_EXT_TLV("Master Playback Volume", 0, 0, 8,
-               ARRAY_SIZE(volume_map) -1, 0,
+               ARRAY_SIZE(volume_map) - 1, 0,
                hsw_volume_get, hsw_volume_put, hsw_vol_tlv),
        /* Offload 0 volume */
        SOC_DOUBLE_EXT_TLV("Media0 Playback Volume", 1, 0, 8,
-               ARRAY_SIZE(volume_map), 0,
+               ARRAY_SIZE(volume_map) - 1, 0,
                hsw_stream_volume_get, hsw_stream_volume_put, hsw_vol_tlv),
        /* Offload 1 volume */
        SOC_DOUBLE_EXT_TLV("Media1 Playback Volume", 2, 0, 8,
-               ARRAY_SIZE(volume_map), 0,
-               hsw_stream_volume_get, hsw_stream_volume_put, hsw_vol_tlv),
-       /* Loopback volume */
-       SOC_DOUBLE_EXT_TLV("Loopback Capture Volume", 3, 0, 8,
-               ARRAY_SIZE(volume_map), 0,
+               ARRAY_SIZE(volume_map) - 1, 0,
                hsw_stream_volume_get, hsw_stream_volume_put, hsw_vol_tlv),
        /* Mic Capture volume */
-       SOC_DOUBLE_EXT_TLV("Mic Capture Volume", 4, 0, 8,
-               ARRAY_SIZE(volume_map), 0,
+       SOC_DOUBLE_EXT_TLV("Mic Capture Volume", 0, 0, 8,
+               ARRAY_SIZE(volume_map) - 1, 0,
                hsw_stream_volume_get, hsw_stream_volume_put, hsw_vol_tlv),
 };
 
@@ -354,8 +396,14 @@ static int hsw_pcm_hw_params(struct snd_pcm_substream *substream,
        /* DSP stream type depends on DAI ID */
        switch (rtd->cpu_dai->id) {
        case 0:
-               stream_type = SST_HSW_STREAM_TYPE_SYSTEM;
-               module_id = SST_HSW_MODULE_PCM_SYSTEM;
+               if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+                       stream_type = SST_HSW_STREAM_TYPE_SYSTEM;
+                       module_id = SST_HSW_MODULE_PCM_SYSTEM;
+               }
+               else {
+                       stream_type = SST_HSW_STREAM_TYPE_CAPTURE;
+                       module_id = SST_HSW_MODULE_PCM_CAPTURE;
+               }
                break;
        case 1:
        case 2:
@@ -368,10 +416,6 @@ static int hsw_pcm_hw_params(struct snd_pcm_substream *substream,
                path_id = SST_HSW_STREAM_PATH_SSP0_OUT;
                module_id = SST_HSW_MODULE_PCM_REFERENCE;
                break;
-       case 4:
-               stream_type = SST_HSW_STREAM_TYPE_CAPTURE;
-               module_id = SST_HSW_MODULE_PCM_CAPTURE;
-               break;
        default:
                dev_err(rtd->dev, "error: invalid DAI ID %d\n",
                        rtd->cpu_dai->id);
@@ -421,13 +465,7 @@ static int hsw_pcm_hw_params(struct snd_pcm_substream *substream,
                return ret;
        }
 
-       /* we only support stereo atm */
        channels = params_channels(params);
-       if (channels != 2) {
-               dev_err(rtd->dev, "error: invalid channels %d\n", channels);
-               return -EINVAL;
-       }
-
        map = create_channel_map(SST_HSW_CHANNEL_CONFIG_STEREO);
        sst_hsw_stream_set_map_config(hsw, pcm_data->stream,
                        map, SST_HSW_CHANNEL_CONFIG_STEREO);
@@ -478,35 +516,23 @@ static int hsw_pcm_hw_params(struct snd_pcm_substream *substream,
                return -EINVAL;
        }
 
-       /* we use hardcoded memory offsets atm, will be updated for new FW */
-       if (stream_type == SST_HSW_STREAM_TYPE_CAPTURE) {
-               sst_hsw_stream_set_module_info(hsw, pcm_data->stream,
-                       SST_HSW_MODULE_PCM_CAPTURE, module_data->entry);
-               sst_hsw_stream_set_pmemory_info(hsw, pcm_data->stream,
-                       0x449400, 0x4000);
-               sst_hsw_stream_set_smemory_info(hsw, pcm_data->stream,
-                       0x400000, 0);
-       } else { /* stream_type == SST_HSW_STREAM_TYPE_SYSTEM */
-               sst_hsw_stream_set_module_info(hsw, pcm_data->stream,
-                       SST_HSW_MODULE_PCM_SYSTEM, module_data->entry);
-
-               sst_hsw_stream_set_pmemory_info(hsw, pcm_data->stream,
-                       module_data->offset, module_data->size);
-               sst_hsw_stream_set_pmemory_info(hsw, pcm_data->stream,
-                       0x44d400, 0x3800);
-
-               sst_hsw_stream_set_smemory_info(hsw, pcm_data->stream,
-                       module_data->offset, module_data->size);
-               sst_hsw_stream_set_smemory_info(hsw, pcm_data->stream,
-                       0x400000, 0);
-       }
+       sst_hsw_stream_set_module_info(hsw, pcm_data->stream,
+               pcm_data->runtime);
 
        ret = sst_hsw_stream_commit(hsw, pcm_data->stream);
        if (ret < 0) {
                dev_err(rtd->dev, "error: failed to commit stream %d\n", ret);
                return ret;
        }
-       pcm_data->allocated = true;
+
+       if (!pcm_data->allocated) {
+               /* Set previous saved volume */
+               sst_hsw_stream_set_volume(hsw, pcm_data->stream, 0,
+                               0, pcm_data->volume[0]);
+               sst_hsw_stream_set_volume(hsw, pcm_data->stream, 0,
+                               1, pcm_data->volume[1]);
+               pcm_data->allocated = true;
+       }
 
        ret = sst_hsw_stream_pause(hsw, pcm_data->stream, 1);
        if (ret < 0)
@@ -558,7 +584,7 @@ static u32 hsw_notify_pointer(struct sst_hsw_stream *stream, void *data)
        pos = frames_to_bytes(runtime,
                (runtime->control->appl_ptr % runtime->buffer_size));
 
-       dev_dbg(rtd->dev, "PCM: App pointer %d bytes\n", pos);
+       dev_vdbg(rtd->dev, "PCM: App pointer %d bytes\n", pos);
 
        /* let alsa know we have play a period */
        snd_pcm_period_elapsed(substream);
@@ -580,7 +606,7 @@ static snd_pcm_uframes_t hsw_pcm_pointer(struct snd_pcm_substream *substream)
        offset = bytes_to_frames(runtime, position);
        ppos = sst_hsw_get_dsp_presentation_position(hsw, pcm_data->stream);
 
-       dev_dbg(rtd->dev, "PCM: DMA pointer %du bytes, pos %llu\n",
+       dev_vdbg(rtd->dev, "PCM: DMA pointer %du bytes, pos %llu\n",
                position, ppos);
        return offset;
 }
@@ -596,6 +622,7 @@ static int hsw_pcm_open(struct snd_pcm_substream *substream)
        pcm_data = &pdata->pcm[rtd->cpu_dai->id];
 
        mutex_lock(&pcm_data->mutex);
+       pm_runtime_get_sync(pdata->dev);
 
        snd_soc_pcm_set_drvdata(rtd, pcm_data);
        pcm_data->substream = substream;
@@ -606,16 +633,12 @@ static int hsw_pcm_open(struct snd_pcm_substream *substream)
                hsw_notify_pointer, pcm_data);
        if (pcm_data->stream == NULL) {
                dev_err(rtd->dev, "error: failed to create stream\n");
+               pm_runtime_mark_last_busy(pdata->dev);
+               pm_runtime_put_autosuspend(pdata->dev);
                mutex_unlock(&pcm_data->mutex);
                return -EINVAL;
        }
 
-       /* Set previous saved volume */
-       sst_hsw_stream_set_volume(hsw, pcm_data->stream, 0,
-                       0, pcm_data->volume[0]);
-       sst_hsw_stream_set_volume(hsw, pcm_data->stream, 0,
-                       1, pcm_data->volume[1]);
-
        mutex_unlock(&pcm_data->mutex);
        return 0;
 }
@@ -645,6 +668,8 @@ static int hsw_pcm_close(struct snd_pcm_substream *substream)
        pcm_data->stream = NULL;
 
 out:
+       pm_runtime_mark_last_busy(pdata->dev);
+       pm_runtime_put_autosuspend(pdata->dev);
        mutex_unlock(&pcm_data->mutex);
        return ret;
 }
@@ -660,6 +685,56 @@ static struct snd_pcm_ops hsw_pcm_ops = {
        .page           = snd_pcm_sgbuf_ops_page,
 };
 
+/* static mappings between PCMs and modules - may be dynamic in future */
+static struct hsw_pcm_module_map mod_map[] = {
+       {HSW_PCM_DAI_ID_SYSTEM, SST_HSW_MODULE_PCM_SYSTEM},
+       {HSW_PCM_DAI_ID_OFFLOAD0, SST_HSW_MODULE_PCM},
+       {HSW_PCM_DAI_ID_OFFLOAD1, SST_HSW_MODULE_PCM},
+       {HSW_PCM_DAI_ID_LOOPBACK, SST_HSW_MODULE_PCM_REFERENCE},
+       {HSW_PCM_DAI_ID_CAPTURE, SST_HSW_MODULE_PCM_CAPTURE},
+};
+
+static int hsw_pcm_create_modules(struct hsw_priv_data *pdata)
+{
+       struct sst_hsw *hsw = pdata->hsw;
+       struct hsw_pcm_data *pcm_data;
+       int i;
+
+       for (i = 0; i < ARRAY_SIZE(mod_map); i++) {
+               pcm_data = &pdata->pcm[i];
+
+               /* create new runtime module, use same offset if recreated */
+               pcm_data->runtime = sst_hsw_runtime_module_create(hsw,
+                       mod_map[i].mod_id, pcm_data->persistent_offset);
+               if (pcm_data->runtime == NULL)
+                       goto err;
+               pcm_data->persistent_offset =
+                       pcm_data->runtime->persistent_offset;
+       }
+
+       return 0;
+
+err:
+       for (--i; i >= 0; i--) {
+               pcm_data = &pdata->pcm[i];
+               sst_hsw_runtime_module_free(pcm_data->runtime);
+       }
+
+       return -ENODEV;
+}
+
+static void hsw_pcm_free_modules(struct hsw_priv_data *pdata)
+{
+       struct hsw_pcm_data *pcm_data;
+       int i;
+
+       for (i = 0; i < ARRAY_SIZE(mod_map); i++) {
+               pcm_data = &pdata->pcm[i];
+
+               sst_hsw_runtime_module_free(pcm_data->runtime);
+       }
+}
+
 static void hsw_pcm_free(struct snd_pcm *pcm)
 {
        snd_pcm_lib_preallocate_free_for_all(pcm);
@@ -670,6 +745,7 @@ static int hsw_pcm_new(struct snd_soc_pcm_runtime *rtd)
        struct snd_pcm *pcm = rtd->pcm;
        struct snd_soc_platform *platform = rtd->platform;
        struct sst_pdata *pdata = dev_get_platdata(platform->dev);
+       struct hsw_priv_data *priv_data = dev_get_drvdata(platform->dev);
        struct device *dev = pdata->dma_dev;
        int ret = 0;
 
@@ -686,6 +762,7 @@ static int hsw_pcm_new(struct snd_soc_pcm_runtime *rtd)
                        return ret;
                }
        }
+       priv_data->pcm[rtd->cpu_dai->id].hsw_pcm = pcm;
 
        return ret;
 }
@@ -696,6 +773,7 @@ static int hsw_pcm_new(struct snd_soc_pcm_runtime *rtd)
 static struct snd_soc_dai_driver hsw_dais[] = {
        {
                .name  = "System Pin",
+               .id = HSW_PCM_DAI_ID_SYSTEM,
                .playback = {
                        .stream_name = "System Playback",
                        .channels_min = 2,
@@ -703,10 +781,18 @@ static struct snd_soc_dai_driver hsw_dais[] = {
                        .rates = SNDRV_PCM_RATE_48000,
                        .formats = SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S16_LE,
                },
+               .capture = {
+                       .stream_name = "Analog Capture",
+                       .channels_min = 2,
+                       .channels_max = 4,
+                       .rates = SNDRV_PCM_RATE_48000,
+                       .formats = SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S16_LE,
+               },
        },
        {
                /* PCM */
                .name  = "Offload0 Pin",
+               .id = HSW_PCM_DAI_ID_OFFLOAD0,
                .playback = {
                        .stream_name = "Offload0 Playback",
                        .channels_min = 2,
@@ -718,6 +804,7 @@ static struct snd_soc_dai_driver hsw_dais[] = {
        {
                /* PCM */
                .name  = "Offload1 Pin",
+               .id = HSW_PCM_DAI_ID_OFFLOAD1,
                .playback = {
                        .stream_name = "Offload1 Playback",
                        .channels_min = 2,
@@ -728,6 +815,7 @@ static struct snd_soc_dai_driver hsw_dais[] = {
        },
        {
                .name  = "Loopback Pin",
+               .id = HSW_PCM_DAI_ID_LOOPBACK,
                .capture = {
                        .stream_name = "Loopback Capture",
                        .channels_min = 2,
@@ -736,16 +824,6 @@ static struct snd_soc_dai_driver hsw_dais[] = {
                        .formats = SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S16_LE,
                },
        },
-       {
-               .name  = "Capture Pin",
-               .capture = {
-                       .stream_name = "Analog Capture",
-                       .channels_min = 2,
-                       .channels_max = 2,
-                       .rates = SNDRV_PCM_RATE_48000,
-                       .formats = SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S16_LE,
-               },
-       },
 };
 
 static const struct snd_soc_dapm_widget widgets[] = {
@@ -776,9 +854,20 @@ static int hsw_pcm_probe(struct snd_soc_platform *platform)
 {
        struct hsw_priv_data *priv_data = snd_soc_platform_get_drvdata(platform);
        struct sst_pdata *pdata = dev_get_platdata(platform->dev);
-       struct device *dma_dev = pdata->dma_dev;
+       struct device *dma_dev, *dev;
        int i, ret = 0;
 
+       if (!pdata)
+               return -ENODEV;
+
+       dev = platform->dev;
+       dma_dev = pdata->dma_dev;
+
+       priv_data->hsw = pdata->dsp;
+       priv_data->dev = platform->dev;
+       priv_data->pm_state = HSW_PM_STATE_D0;
+       priv_data->soc_card = platform->component.card;
+
        /* allocate DSP buffer page tables */
        for (i = 0; i < ARRAY_SIZE(hsw_dais); i++) {
 
@@ -801,6 +890,16 @@ static int hsw_pcm_probe(struct snd_soc_platform *platform)
                }
        }
 
+       /* allocate runtime modules */
+       hsw_pcm_create_modules(priv_data);
+
+       /* enable runtime PM with auto suspend */
+       pm_runtime_set_autosuspend_delay(platform->dev,
+               SST_RUNTIME_SUSPEND_DELAY);
+       pm_runtime_use_autosuspend(platform->dev);
+       pm_runtime_enable(platform->dev);
+       pm_runtime_idle(platform->dev);
+
        return 0;
 
 err:
@@ -819,6 +918,9 @@ static int hsw_pcm_remove(struct snd_soc_platform *platform)
                snd_soc_platform_get_drvdata(platform);
        int i;
 
+       pm_runtime_disable(platform->dev);
+       hsw_pcm_free_modules(priv_data);
+
        for (i = 0; i < ARRAY_SIZE(hsw_dais); i++) {
                if (hsw_dais[i].playback.channels_min)
                        snd_dma_free_pages(&priv_data->dmab[i][0]);
@@ -896,10 +998,181 @@ static int hsw_pcm_dev_remove(struct platform_device *pdev)
        return 0;
 }
 
+#ifdef CONFIG_PM_RUNTIME
+
+static int hsw_pcm_runtime_idle(struct device *dev)
+{
+       return 0;
+}
+
+static int hsw_pcm_runtime_suspend(struct device *dev)
+{
+       struct hsw_priv_data *pdata = dev_get_drvdata(dev);
+       struct sst_hsw *hsw = pdata->hsw;
+
+       if (pdata->pm_state == HSW_PM_STATE_D3)
+               return 0;
+
+       sst_hsw_dsp_runtime_suspend(hsw);
+       sst_hsw_dsp_runtime_sleep(hsw);
+       pdata->pm_state = HSW_PM_STATE_D3;
+
+       return 0;
+}
+
+static int hsw_pcm_runtime_resume(struct device *dev)
+{
+       struct hsw_priv_data *pdata = dev_get_drvdata(dev);
+       struct sst_hsw *hsw = pdata->hsw;
+       int ret;
+
+       if (pdata->pm_state == HSW_PM_STATE_D0)
+               return 0;
+
+       ret = sst_hsw_dsp_load(hsw);
+       if (ret < 0) {
+               dev_err(dev, "failed to reload %d\n", ret);
+               return ret;
+       }
+
+       ret = hsw_pcm_create_modules(pdata);
+       if (ret < 0) {
+               dev_err(dev, "failed to create modules %d\n", ret);
+               return ret;
+       }
+
+       ret = sst_hsw_dsp_runtime_resume(hsw);
+       if (ret < 0)
+               return ret;
+       else if (ret == 1) /* no action required */
+               return 0;
+
+       pdata->pm_state = HSW_PM_STATE_D0;
+       return ret;
+}
+
+#else
+#define hsw_pcm_runtime_idle           NULL
+#define hsw_pcm_runtime_suspend                NULL
+#define hsw_pcm_runtime_resume         NULL
+#endif
+
+#if defined(CONFIG_PM_SLEEP) && defined(CONFIG_PM_RUNTIME)
+
+static void hsw_pcm_complete(struct device *dev)
+{
+       struct hsw_priv_data *pdata = dev_get_drvdata(dev);
+       struct sst_hsw *hsw = pdata->hsw;
+       struct hsw_pcm_data *pcm_data;
+       int i, err;
+
+       if (pdata->pm_state == HSW_PM_STATE_D0)
+               return;
+
+       err = sst_hsw_dsp_load(hsw);
+       if (err < 0) {
+               dev_err(dev, "failed to reload %d\n", err);
+               return;
+       }
+
+       err = hsw_pcm_create_modules(pdata);
+       if (err < 0) {
+               dev_err(dev, "failed to create modules %d\n", err);
+               return;
+       }
+
+       for (i = 0; i < HSW_PCM_DAI_ID_CAPTURE + 1; i++) {
+               pcm_data = &pdata->pcm[i];
+
+               if (!pcm_data->substream)
+                       continue;
+
+               err = sst_module_runtime_restore(pcm_data->runtime,
+                       &pcm_data->context);
+               if (err < 0)
+                       dev_err(dev, "failed to restore context for PCM %d\n", i);
+       }
+
+       snd_soc_resume(pdata->soc_card->dev);
+
+       err = sst_hsw_dsp_runtime_resume(hsw);
+       if (err < 0)
+               return;
+       else if (err == 1) /* no action required */
+               return;
+
+       pdata->pm_state = HSW_PM_STATE_D0;
+       return;
+}
+
+static int hsw_pcm_prepare(struct device *dev)
+{
+       struct hsw_priv_data *pdata = dev_get_drvdata(dev);
+       struct sst_hsw *hsw = pdata->hsw;
+       struct hsw_pcm_data *pcm_data;
+       int i, err;
+
+       if (pdata->pm_state == HSW_PM_STATE_D3)
+               return 0;
+       /* suspend all active streams */
+       for (i = 0; i < HSW_PCM_DAI_ID_CAPTURE + 1; i++) {
+               pcm_data = &pdata->pcm[i];
+
+               if (!pcm_data->substream)
+                       continue;
+               dev_dbg(dev, "suspending pcm %d\n", i);
+               snd_pcm_suspend_all(pcm_data->hsw_pcm);
+
+               /* We need to wait until the DSP FW stops the streams */
+               msleep(2);
+       }
+
+       snd_soc_suspend(pdata->soc_card->dev);
+       snd_soc_poweroff(pdata->soc_card->dev);
+
+       /* enter D3 state and stall */
+       sst_hsw_dsp_runtime_suspend(hsw);
+
+       /* preserve persistent memory */
+       for (i = 0; i < HSW_PCM_DAI_ID_CAPTURE + 1; i++) {
+               pcm_data = &pdata->pcm[i];
+
+               if (!pcm_data->substream)
+                       continue;
+
+               dev_dbg(dev, "saving context pcm %d\n", i);
+               err = sst_module_runtime_save(pcm_data->runtime,
+                       &pcm_data->context);
+               if (err < 0)
+                       dev_err(dev, "failed to save context for PCM %d\n", i);
+       }
+
+       /* put the DSP to sleep */
+       sst_hsw_dsp_runtime_sleep(hsw);
+       pdata->pm_state = HSW_PM_STATE_D3;
+
+       return 0;
+}
+
+#else
+#define hsw_pcm_prepare                NULL
+#define hsw_pcm_complete       NULL
+#endif
+
+static const struct dev_pm_ops hsw_pcm_pm = {
+       .runtime_idle = hsw_pcm_runtime_idle,
+       .runtime_suspend = hsw_pcm_runtime_suspend,
+       .runtime_resume = hsw_pcm_runtime_resume,
+       .prepare = hsw_pcm_prepare,
+       .complete = hsw_pcm_complete,
+};
+
 static struct platform_driver hsw_pcm_driver = {
        .driver = {
                .name = "haswell-pcm-audio",
                .owner = THIS_MODULE,
+               .pm = &hsw_pcm_pm,
+
        },
 
        .probe = hsw_pcm_dev_probe,
index 5946777..3951689 100644 (file)
@@ -67,8 +67,11 @@ static int sst_platform_compr_open(struct snd_compr_stream *cstream)
                goto out_ops;
        }
        stream->compr_ops = sst->compr_ops;
-
        stream->id = 0;
+
+       /* Turn on LPE */
+       sst->compr_ops->power(sst->dev, true);
+
        sst_set_stream_status(stream, SST_PLATFORM_INIT);
        runtime->private_data = stream;
        return 0;
@@ -83,6 +86,9 @@ static int sst_platform_compr_free(struct snd_compr_stream *cstream)
        int ret_val = 0, str_id;
 
        stream = cstream->runtime->private_data;
+       /* Turn off LPE */
+       sst->compr_ops->power(sst->dev, false);
+
        /*need to check*/
        str_id = stream->id;
        if (str_id)
index aa9b600..6032f18 100644 (file)
@@ -101,35 +101,11 @@ static struct sst_dev_stream_map dpcm_strm_map[] = {
        {MERR_DPCM_AUDIO, 0, SNDRV_PCM_STREAM_CAPTURE, PIPE_PCM1_OUT, SST_TASK_ID_MEDIA, 0},
 };
 
-/* MFLD - MSIC */
-static struct snd_soc_dai_driver sst_platform_dai[] = {
+static int sst_media_digital_mute(struct snd_soc_dai *dai, int mute, int stream)
 {
-       .name = "Headset-cpu-dai",
-       .id = 0,
-       .playback = {
-               .channels_min = SST_STEREO,
-               .channels_max = SST_STEREO,
-               .rates = SNDRV_PCM_RATE_48000,
-               .formats = SNDRV_PCM_FMTBIT_S24_LE,
-       },
-       .capture = {
-               .channels_min = 1,
-               .channels_max = 5,
-               .rates = SNDRV_PCM_RATE_48000,
-               .formats = SNDRV_PCM_FMTBIT_S24_LE,
-       },
-},
-{
-       .name = "Compress-cpu-dai",
-       .compress_dai = 1,
-       .playback = {
-               .channels_min = SST_STEREO,
-               .channels_max = SST_STEREO,
-               .rates = SNDRV_PCM_RATE_44100|SNDRV_PCM_RATE_48000,
-               .formats = SNDRV_PCM_FMTBIT_S16_LE,
-       },
-},
-};
+
+       return sst_send_pipe_gains(dai, stream, mute);
+}
 
 /* helper functions */
 void sst_set_stream_status(struct sst_runtime_stream *stream,
@@ -451,12 +427,133 @@ static int sst_media_hw_free(struct snd_pcm_substream *substream,
        return snd_pcm_lib_free_pages(substream);
 }
 
+static int sst_enable_ssp(struct snd_pcm_substream *substream,
+                       struct snd_soc_dai *dai)
+{
+       int ret = 0;
+
+       if (!dai->active) {
+               ret = sst_handle_vb_timer(dai, true);
+               if (ret)
+                       return ret;
+               ret = send_ssp_cmd(dai, dai->name, 1);
+       }
+       return ret;
+}
+
+static void sst_disable_ssp(struct snd_pcm_substream *substream,
+                       struct snd_soc_dai *dai)
+{
+       if (!dai->active) {
+               send_ssp_cmd(dai, dai->name, 0);
+               sst_handle_vb_timer(dai, false);
+       }
+}
+
 static struct snd_soc_dai_ops sst_media_dai_ops = {
        .startup = sst_media_open,
        .shutdown = sst_media_close,
        .prepare = sst_media_prepare,
        .hw_params = sst_media_hw_params,
        .hw_free = sst_media_hw_free,
+       .mute_stream = sst_media_digital_mute,
+};
+
+static struct snd_soc_dai_ops sst_compr_dai_ops = {
+       .mute_stream = sst_media_digital_mute,
+};
+
+static struct snd_soc_dai_ops sst_be_dai_ops = {
+       .startup = sst_enable_ssp,
+       .shutdown = sst_disable_ssp,
+};
+
+static struct snd_soc_dai_driver sst_platform_dai[] = {
+{
+       .name = "media-cpu-dai",
+       .ops = &sst_media_dai_ops,
+       .playback = {
+               .stream_name = "Headset Playback",
+               .channels_min = SST_STEREO,
+               .channels_max = SST_STEREO,
+               .rates = SNDRV_PCM_RATE_44100|SNDRV_PCM_RATE_48000,
+               .formats = SNDRV_PCM_FMTBIT_S16_LE,
+       },
+       .capture = {
+               .stream_name = "Headset Capture",
+               .channels_min = 1,
+               .channels_max = 2,
+               .rates = SNDRV_PCM_RATE_44100|SNDRV_PCM_RATE_48000,
+               .formats = SNDRV_PCM_FMTBIT_S16_LE,
+       },
+},
+{
+       .name = "compress-cpu-dai",
+       .compress_dai = 1,
+       .ops = &sst_compr_dai_ops,
+       .playback = {
+               .stream_name = "Compress Playback",
+               .channels_min = SST_STEREO,
+               .channels_max = SST_STEREO,
+               .rates = SNDRV_PCM_RATE_48000,
+               .formats = SNDRV_PCM_FMTBIT_S16_LE,
+       },
+},
+/* BE CPU  Dais */
+{
+       .name = "ssp0-port",
+       .ops = &sst_be_dai_ops,
+       .playback = {
+               .stream_name = "ssp0 Tx",
+               .channels_min = SST_STEREO,
+               .channels_max = SST_STEREO,
+               .rates = SNDRV_PCM_RATE_48000,
+               .formats = SNDRV_PCM_FMTBIT_S16_LE,
+       },
+       .capture = {
+               .stream_name = "ssp0 Rx",
+               .channels_min = SST_STEREO,
+               .channels_max = SST_STEREO,
+               .rates = SNDRV_PCM_RATE_48000,
+               .formats = SNDRV_PCM_FMTBIT_S16_LE,
+       },
+},
+{
+       .name = "ssp1-port",
+       .ops = &sst_be_dai_ops,
+       .playback = {
+               .stream_name = "ssp1 Tx",
+               .channels_min = SST_STEREO,
+               .channels_max = SST_STEREO,
+               .rates = SNDRV_PCM_RATE_8000|SNDRV_PCM_RATE_16000|SNDRV_PCM_RATE_48000,
+               .formats = SNDRV_PCM_FMTBIT_S16_LE,
+       },
+       .capture = {
+               .stream_name = "ssp1 Rx",
+               .channels_min = SST_STEREO,
+               .channels_max = SST_STEREO,
+               .rates = SNDRV_PCM_RATE_8000|SNDRV_PCM_RATE_16000|SNDRV_PCM_RATE_48000,
+               .formats = SNDRV_PCM_FMTBIT_S16_LE,
+       },
+},
+{
+       .name = "ssp2-port",
+       .ops = &sst_be_dai_ops,
+       .playback = {
+               .stream_name = "ssp2 Tx",
+               .channels_min = SST_STEREO,
+               .channels_max = SST_STEREO,
+               .rates = SNDRV_PCM_RATE_48000,
+               .formats = SNDRV_PCM_FMTBIT_S16_LE,
+       },
+       .capture = {
+               .stream_name = "ssp2 Rx",
+               .channels_min = SST_STEREO,
+               .channels_max = SST_STEREO,
+               .rates = SNDRV_PCM_RATE_48000,
+               .formats = SNDRV_PCM_FMTBIT_S16_LE,
+       },
+},
 };
 
 static int sst_platform_open(struct snd_pcm_substream *substream)
@@ -609,6 +706,7 @@ static int sst_platform_probe(struct platform_device *pdev)
        pdata->pdev_strm_map = dpcm_strm_map;
        pdata->strm_map_size = ARRAY_SIZE(dpcm_strm_map);
        drv->pdata = pdata;
+       drv->pdev = pdev;
        mutex_init(&drv->lock);
        dev_set_drvdata(&pdev->dev, drv);
 
index 19f83ec..79c8d12 100644 (file)
@@ -117,6 +117,7 @@ struct compress_sst_ops {
        int (*get_codec_caps)(struct snd_compr_codec_caps *codec);
        int (*set_metadata)(struct device *dev, unsigned int str_id,
                        struct snd_compr_metadata *mdata);
+       int (*power)(struct device *dev, bool state);
 };
 
 struct sst_ops {
@@ -153,6 +154,10 @@ struct sst_device {
 struct sst_data;
 
 int sst_dsp_init_v2_dpcm(struct snd_soc_platform *platform);
+int sst_send_pipe_gains(struct snd_soc_dai *dai, int stream, int mute);
+int send_ssp_cmd(struct snd_soc_dai *dai, const char *id, bool enable);
+int sst_handle_vb_timer(struct snd_soc_dai *dai, bool enable);
+
 void sst_set_stream_status(struct sst_runtime_stream *stream, int state);
 int sst_fill_stream_params(void *substream, const struct sst_data *ctx,
                           struct snd_sst_params *str_params, bool is_compress);
diff --git a/sound/soc/intel/sst/Makefile b/sound/soc/intel/sst/Makefile
new file mode 100644 (file)
index 0000000..fd21726
--- /dev/null
@@ -0,0 +1,7 @@
+snd-intel-sst-core-objs := sst.o sst_ipc.o sst_stream.o sst_drv_interface.o sst_loader.o sst_pvt.o
+snd-intel-sst-pci-objs += sst_pci.o
+snd-intel-sst-acpi-objs += sst_acpi.o
+
+obj-$(CONFIG_SND_SST_IPC) += snd-intel-sst-core.o
+obj-$(CONFIG_SND_SST_IPC_PCI) += snd-intel-sst-pci.o
+obj-$(CONFIG_SND_SST_IPC_ACPI) += snd-intel-sst-acpi.o
diff --git a/sound/soc/intel/sst/sst.c b/sound/soc/intel/sst/sst.c
new file mode 100644 (file)
index 0000000..8a8d56a
--- /dev/null
@@ -0,0 +1,437 @@
+/*
+ *  sst.c - Intel SST Driver for audio engine
+ *
+ *  Copyright (C) 2008-14      Intel Corp
+ *  Authors:   Vinod Koul <vinod.koul@intel.com>
+ *             Harsha Priya <priya.harsha@intel.com>
+ *             Dharageswari R <dharageswari.r@intel.com>
+ *             KP Jeeja <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 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 <linux/module.h>
+#include <linux/fs.h>
+#include <linux/interrupt.h>
+#include <linux/firmware.h>
+#include <linux/pm_runtime.h>
+#include <linux/pm_qos.h>
+#include <linux/async.h>
+#include <linux/acpi.h>
+#include <sound/core.h>
+#include <sound/soc.h>
+#include <asm/platform_sst_audio.h>
+#include "../sst-mfld-platform.h"
+#include "sst.h"
+#include "../sst-dsp.h"
+
+MODULE_AUTHOR("Vinod Koul <vinod.koul@intel.com>");
+MODULE_AUTHOR("Harsha Priya <priya.harsha@intel.com>");
+MODULE_DESCRIPTION("Intel (R) SST(R) Audio Engine Driver");
+MODULE_LICENSE("GPL v2");
+
+static inline bool sst_is_process_reply(u32 msg_id)
+{
+       return ((msg_id & PROCESS_MSG) ? true : false);
+}
+
+static inline bool sst_validate_mailbox_size(unsigned int size)
+{
+       return ((size <= SST_MAILBOX_SIZE) ? true : false);
+}
+
+static irqreturn_t intel_sst_interrupt_mrfld(int irq, void *context)
+{
+       union interrupt_reg_mrfld isr;
+       union ipc_header_mrfld header;
+       union sst_imr_reg_mrfld imr;
+       struct ipc_post *msg = NULL;
+       unsigned int size = 0;
+       struct intel_sst_drv *drv = (struct intel_sst_drv *) context;
+       irqreturn_t retval = IRQ_HANDLED;
+
+       /* Interrupt arrived, check src */
+       isr.full = sst_shim_read64(drv->shim, SST_ISRX);
+
+       if (isr.part.done_interrupt) {
+               /* Clear done bit */
+               spin_lock(&drv->ipc_spin_lock);
+               header.full = sst_shim_read64(drv->shim,
+                                       drv->ipc_reg.ipcx);
+               header.p.header_high.part.done = 0;
+               sst_shim_write64(drv->shim, drv->ipc_reg.ipcx, header.full);
+
+               /* write 1 to clear status register */;
+               isr.part.done_interrupt = 1;
+               sst_shim_write64(drv->shim, SST_ISRX, isr.full);
+               spin_unlock(&drv->ipc_spin_lock);
+
+               /* we can send more messages to DSP so trigger work */
+               queue_work(drv->post_msg_wq, &drv->ipc_post_msg_wq);
+               retval = IRQ_HANDLED;
+       }
+
+       if (isr.part.busy_interrupt) {
+               /* message from dsp so copy that */
+               spin_lock(&drv->ipc_spin_lock);
+               imr.full = sst_shim_read64(drv->shim, SST_IMRX);
+               imr.part.busy_interrupt = 1;
+               sst_shim_write64(drv->shim, SST_IMRX, imr.full);
+               spin_unlock(&drv->ipc_spin_lock);
+               header.full =  sst_shim_read64(drv->shim, drv->ipc_reg.ipcd);
+
+               if (sst_create_ipc_msg(&msg, header.p.header_high.part.large)) {
+                       drv->ops->clear_interrupt(drv);
+                       return IRQ_HANDLED;
+               }
+
+               if (header.p.header_high.part.large) {
+                       size = header.p.header_low_payload;
+                       if (sst_validate_mailbox_size(size)) {
+                               memcpy_fromio(msg->mailbox_data,
+                                       drv->mailbox + drv->mailbox_recv_offset, size);
+                       } else {
+                               dev_err(drv->dev,
+                                       "Mailbox not copied, payload size is: %u\n", size);
+                               header.p.header_low_payload = 0;
+                       }
+               }
+
+               msg->mrfld_header = header;
+               msg->is_process_reply =
+                       sst_is_process_reply(header.p.header_high.part.msg_id);
+               spin_lock(&drv->rx_msg_lock);
+               list_add_tail(&msg->node, &drv->rx_list);
+               spin_unlock(&drv->rx_msg_lock);
+               drv->ops->clear_interrupt(drv);
+               retval = IRQ_WAKE_THREAD;
+       }
+       return retval;
+}
+
+static irqreturn_t intel_sst_irq_thread_mrfld(int irq, void *context)
+{
+       struct intel_sst_drv *drv = (struct intel_sst_drv *) context;
+       struct ipc_post *__msg, *msg = NULL;
+       unsigned long irq_flags;
+
+       spin_lock_irqsave(&drv->rx_msg_lock, irq_flags);
+       if (list_empty(&drv->rx_list)) {
+               spin_unlock_irqrestore(&drv->rx_msg_lock, irq_flags);
+               return IRQ_HANDLED;
+       }
+
+       list_for_each_entry_safe(msg, __msg, &drv->rx_list, node) {
+               list_del(&msg->node);
+               spin_unlock_irqrestore(&drv->rx_msg_lock, irq_flags);
+               if (msg->is_process_reply)
+                       drv->ops->process_message(msg);
+               else
+                       drv->ops->process_reply(drv, msg);
+
+               if (msg->is_large)
+                       kfree(msg->mailbox_data);
+               kfree(msg);
+               spin_lock_irqsave(&drv->rx_msg_lock, irq_flags);
+       }
+       spin_unlock_irqrestore(&drv->rx_msg_lock, irq_flags);
+       return IRQ_HANDLED;
+}
+
+static int sst_save_dsp_context_v2(struct intel_sst_drv *sst)
+{
+       int ret = 0;
+
+       ret = sst_prepare_and_post_msg(sst, SST_TASK_ID_MEDIA, IPC_CMD,
+                       IPC_PREP_D3, PIPE_RSVD, 0, NULL, NULL,
+                       true, true, false, true);
+
+       if (ret < 0) {
+               dev_err(sst->dev, "not suspending FW!!, Err: %d\n", ret);
+               return -EIO;
+       }
+
+       return 0;
+}
+
+
+static struct intel_sst_ops mrfld_ops = {
+       .interrupt = intel_sst_interrupt_mrfld,
+       .irq_thread = intel_sst_irq_thread_mrfld,
+       .clear_interrupt = intel_sst_clear_intr_mrfld,
+       .start = sst_start_mrfld,
+       .reset = intel_sst_reset_dsp_mrfld,
+       .post_message = sst_post_message_mrfld,
+       .process_reply = sst_process_reply_mrfld,
+       .save_dsp_context =  sst_save_dsp_context_v2,
+       .alloc_stream = sst_alloc_stream_mrfld,
+       .post_download = sst_post_download_mrfld,
+};
+
+int sst_driver_ops(struct intel_sst_drv *sst)
+{
+
+       switch (sst->dev_id) {
+       case SST_MRFLD_PCI_ID:
+       case SST_BYT_ACPI_ID:
+       case SST_CHV_ACPI_ID:
+               sst->tstamp = SST_TIME_STAMP_MRFLD;
+               sst->ops = &mrfld_ops;
+               return 0;
+
+       default:
+               dev_err(sst->dev,
+                       "SST Driver capablities missing for dev_id: %x", sst->dev_id);
+               return -EINVAL;
+       };
+}
+
+void sst_process_pending_msg(struct work_struct *work)
+{
+       struct intel_sst_drv *ctx = container_of(work,
+                       struct intel_sst_drv, ipc_post_msg_wq);
+
+       ctx->ops->post_message(ctx, NULL, false);
+}
+
+static int sst_workqueue_init(struct intel_sst_drv *ctx)
+{
+       INIT_LIST_HEAD(&ctx->memcpy_list);
+       INIT_LIST_HEAD(&ctx->rx_list);
+       INIT_LIST_HEAD(&ctx->ipc_dispatch_list);
+       INIT_LIST_HEAD(&ctx->block_list);
+       INIT_WORK(&ctx->ipc_post_msg_wq, sst_process_pending_msg);
+       init_waitqueue_head(&ctx->wait_queue);
+
+       ctx->post_msg_wq =
+               create_singlethread_workqueue("sst_post_msg_wq");
+       if (!ctx->post_msg_wq)
+               return -EBUSY;
+       return 0;
+}
+
+static void sst_init_locks(struct intel_sst_drv *ctx)
+{
+       mutex_init(&ctx->sst_lock);
+       spin_lock_init(&ctx->rx_msg_lock);
+       spin_lock_init(&ctx->ipc_spin_lock);
+       spin_lock_init(&ctx->block_lock);
+}
+
+int sst_alloc_drv_context(struct intel_sst_drv **ctx,
+               struct device *dev, unsigned int dev_id)
+{
+       *ctx = devm_kzalloc(dev, sizeof(struct intel_sst_drv), GFP_KERNEL);
+       if (!(*ctx))
+               return -ENOMEM;
+
+       (*ctx)->dev = dev;
+       (*ctx)->dev_id = dev_id;
+
+       return 0;
+}
+EXPORT_SYMBOL_GPL(sst_alloc_drv_context);
+
+int sst_context_init(struct intel_sst_drv *ctx)
+{
+       int ret = 0, i;
+
+       if (!ctx->pdata)
+               return -EINVAL;
+
+       if (!ctx->pdata->probe_data)
+               return -EINVAL;
+
+       memcpy(&ctx->info, ctx->pdata->probe_data, sizeof(ctx->info));
+
+       ret = sst_driver_ops(ctx);
+       if (ret != 0)
+               return -EINVAL;
+
+       sst_init_locks(ctx);
+       sst_set_fw_state_locked(ctx, SST_RESET);
+
+       /* pvt_id 0 reserved for async messages */
+       ctx->pvt_id = 1;
+       ctx->stream_cnt = 0;
+       ctx->fw_in_mem = NULL;
+       /* we use memcpy, so set to 0 */
+       ctx->use_dma = 0;
+       ctx->use_lli = 0;
+
+       if (sst_workqueue_init(ctx))
+               return -EINVAL;
+
+       ctx->mailbox_recv_offset = ctx->pdata->ipc_info->mbox_recv_off;
+       ctx->ipc_reg.ipcx = SST_IPCX + ctx->pdata->ipc_info->ipc_offset;
+       ctx->ipc_reg.ipcd = SST_IPCD + ctx->pdata->ipc_info->ipc_offset;
+
+       dev_info(ctx->dev, "Got drv data max stream %d\n",
+                               ctx->info.max_streams);
+
+       for (i = 1; i <= ctx->info.max_streams; i++) {
+               struct stream_info *stream = &ctx->streams[i];
+
+               memset(stream, 0, sizeof(*stream));
+               stream->pipe_id = PIPE_RSVD;
+               mutex_init(&stream->lock);
+       }
+
+       /* Register the ISR */
+       ret = devm_request_threaded_irq(ctx->dev, ctx->irq_num, ctx->ops->interrupt,
+                                       ctx->ops->irq_thread, 0, SST_DRV_NAME,
+                                       ctx);
+       if (ret)
+               goto do_free_mem;
+
+       dev_dbg(ctx->dev, "Registered IRQ %#x\n", ctx->irq_num);
+
+       /* default intr are unmasked so set this as masked */
+       sst_shim_write64(ctx->shim, SST_IMRX, 0xFFFF0038);
+
+       ctx->qos = devm_kzalloc(ctx->dev,
+               sizeof(struct pm_qos_request), GFP_KERNEL);
+       if (!ctx->qos) {
+               ret = -ENOMEM;
+               goto do_free_mem;
+       }
+       pm_qos_add_request(ctx->qos, PM_QOS_CPU_DMA_LATENCY,
+                               PM_QOS_DEFAULT_VALUE);
+
+       dev_dbg(ctx->dev, "Requesting FW %s now...\n", ctx->firmware_name);
+       ret = request_firmware_nowait(THIS_MODULE, true, ctx->firmware_name,
+                                     ctx->dev, GFP_KERNEL, ctx, sst_firmware_load_cb);
+       if (ret) {
+               dev_err(ctx->dev, "Firmware download failed:%d\n", ret);
+               goto do_free_mem;
+       }
+       sst_register(ctx->dev);
+       return 0;
+
+do_free_mem:
+       destroy_workqueue(ctx->post_msg_wq);
+       return ret;
+}
+EXPORT_SYMBOL_GPL(sst_context_init);
+
+void sst_context_cleanup(struct intel_sst_drv *ctx)
+{
+       pm_runtime_get_noresume(ctx->dev);
+       pm_runtime_disable(ctx->dev);
+       sst_unregister(ctx->dev);
+       sst_set_fw_state_locked(ctx, SST_SHUTDOWN);
+       flush_scheduled_work();
+       destroy_workqueue(ctx->post_msg_wq);
+       pm_qos_remove_request(ctx->qos);
+       kfree(ctx->fw_sg_list.src);
+       kfree(ctx->fw_sg_list.dst);
+       ctx->fw_sg_list.list_len = 0;
+       kfree(ctx->fw_in_mem);
+       ctx->fw_in_mem = NULL;
+       sst_memcpy_free_resources(ctx);
+       ctx = NULL;
+}
+EXPORT_SYMBOL_GPL(sst_context_cleanup);
+
+static inline void sst_save_shim64(struct intel_sst_drv *ctx,
+                           void __iomem *shim,
+                           struct sst_shim_regs64 *shim_regs)
+{
+       unsigned long irq_flags;
+
+       spin_lock_irqsave(&ctx->ipc_spin_lock, irq_flags);
+
+       shim_regs->imrx = sst_shim_read64(shim, SST_IMRX),
+
+       spin_unlock_irqrestore(&ctx->ipc_spin_lock, irq_flags);
+}
+
+static inline void sst_restore_shim64(struct intel_sst_drv *ctx,
+                                     void __iomem *shim,
+                                     struct sst_shim_regs64 *shim_regs)
+{
+       unsigned long irq_flags;
+
+       /*
+        * we only need to restore IMRX for this case, rest will be
+        * initialize by FW or driver when firmware is loaded
+        */
+       spin_lock_irqsave(&ctx->ipc_spin_lock, irq_flags);
+       sst_shim_write64(shim, SST_IMRX, shim_regs->imrx),
+       spin_unlock_irqrestore(&ctx->ipc_spin_lock, irq_flags);
+}
+
+void sst_configure_runtime_pm(struct intel_sst_drv *ctx)
+{
+       pm_runtime_set_autosuspend_delay(ctx->dev, SST_SUSPEND_DELAY);
+       pm_runtime_use_autosuspend(ctx->dev);
+       /*
+        * For acpi devices, the actual physical device state is
+        * initially active. So change the state to active before
+        * enabling the pm
+        */
+       pm_runtime_enable(ctx->dev);
+
+       if (acpi_disabled)
+               pm_runtime_set_active(ctx->dev);
+       else
+               pm_runtime_put_noidle(ctx->dev);
+
+       sst_save_shim64(ctx, ctx->shim, ctx->shim_regs64);
+}
+EXPORT_SYMBOL_GPL(sst_configure_runtime_pm);
+
+static int intel_sst_runtime_suspend(struct device *dev)
+{
+       int ret = 0;
+       struct intel_sst_drv *ctx = dev_get_drvdata(dev);
+
+       if (ctx->sst_state == SST_RESET) {
+               dev_dbg(dev, "LPE is already in RESET state, No action\n");
+               return 0;
+       }
+       /* save fw context */
+       if (ctx->ops->save_dsp_context(ctx))
+               return -EBUSY;
+
+       /* Move the SST state to Reset */
+       sst_set_fw_state_locked(ctx, SST_RESET);
+
+       synchronize_irq(ctx->irq_num);
+       flush_workqueue(ctx->post_msg_wq);
+
+       /* save the shim registers because PMC doesn't save state */
+       sst_save_shim64(ctx, ctx->shim, ctx->shim_regs64);
+
+       return ret;
+}
+
+static int intel_sst_runtime_resume(struct device *dev)
+{
+       int ret = 0;
+       struct intel_sst_drv *ctx = dev_get_drvdata(dev);
+
+       if (ctx->sst_state == SST_RESET) {
+               ret = sst_load_fw(ctx);
+               if (ret) {
+                       dev_err(dev, "FW download fail %d\n", ret);
+                       sst_set_fw_state_locked(ctx, SST_RESET);
+               }
+       }
+       return ret;
+}
+
+const struct dev_pm_ops intel_sst_pm = {
+       .runtime_suspend = intel_sst_runtime_suspend,
+       .runtime_resume = intel_sst_runtime_resume,
+};
+EXPORT_SYMBOL_GPL(intel_sst_pm);
diff --git a/sound/soc/intel/sst/sst.h b/sound/soc/intel/sst/sst.h
new file mode 100644 (file)
index 0000000..7f4bbfc
--- /dev/null
@@ -0,0 +1,546 @@
+/*
+ *  sst.h - Intel SST Driver for audio engine
+ *
+ *  Copyright (C) 2008-14 Intel Corporation
+ *  Authors:   Vinod Koul <vinod.koul@intel.com>
+ *             Harsha Priya <priya.harsha@intel.com>
+ *             Dharageswari R <dharageswari.r@intel.com>
+ *             KP Jeeja <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 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.
+ *
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ *
+ *  Common private declarations for SST
+ */
+#ifndef __SST_H__
+#define __SST_H__
+
+#include <linux/firmware.h>
+
+/* driver names */
+#define SST_DRV_NAME "intel_sst_driver"
+#define SST_MRFLD_PCI_ID 0x119A
+#define SST_BYT_ACPI_ID        0x80860F28
+#define SST_CHV_ACPI_ID        0x808622A8
+
+#define SST_SUSPEND_DELAY 2000
+#define FW_CONTEXT_MEM (64*1024)
+#define SST_ICCM_BOUNDARY 4
+#define SST_CONFIG_SSP_SIGN 0x7ffe8001
+
+#define MRFLD_FW_VIRTUAL_BASE 0xC0000000
+#define MRFLD_FW_DDR_BASE_OFFSET 0x0
+#define MRFLD_FW_FEATURE_BASE_OFFSET 0x4
+#define MRFLD_FW_BSS_RESET_BIT 0
+
+extern const struct dev_pm_ops intel_sst_pm;
+enum sst_states {
+       SST_FW_LOADING = 1,
+       SST_FW_RUNNING,
+       SST_RESET,
+       SST_SHUTDOWN,
+};
+
+enum sst_algo_ops {
+       SST_SET_ALGO = 0,
+       SST_GET_ALGO = 1,
+};
+
+#define SST_BLOCK_TIMEOUT      1000
+
+#define FW_SIGNATURE_SIZE      4
+
+/* stream states */
+enum sst_stream_states {
+       STREAM_UN_INIT  = 0,    /* Freed/Not used stream */
+       STREAM_RUNNING  = 1,    /* Running */
+       STREAM_PAUSED   = 2,    /* Paused stream */
+       STREAM_DECODE   = 3,    /* stream is in decoding only state */
+       STREAM_INIT     = 4,    /* stream init, waiting for data */
+       STREAM_RESET    = 5,    /* force reset on recovery */
+};
+
+enum sst_ram_type {
+       SST_IRAM        = 1,
+       SST_DRAM        = 2,
+       SST_DDR = 5,
+       SST_CUSTOM_INFO = 7,    /* consists of FW binary information */
+};
+
+/* SST shim registers to structure mapping */
+union interrupt_reg {
+       struct {
+               u64 done_interrupt:1;
+               u64 busy_interrupt:1;
+               u64 rsvd:62;
+       } part;
+       u64 full;
+};
+
+union sst_pisr_reg {
+       struct {
+               u32 pssp0:1;
+               u32 pssp1:1;
+               u32 rsvd0:3;
+               u32 dmac:1;
+               u32 rsvd1:26;
+       } part;
+       u32 full;
+};
+
+union sst_pimr_reg {
+       struct {
+               u32 ssp0:1;
+               u32 ssp1:1;
+               u32 rsvd0:3;
+               u32 dmac:1;
+               u32 rsvd1:10;
+               u32 ssp0_sc:1;
+               u32 ssp1_sc:1;
+               u32 rsvd2:3;
+               u32 dmac_sc:1;
+               u32 rsvd3:10;
+       } part;
+       u32 full;
+};
+
+union config_status_reg_mrfld {
+       struct {
+               u64 lpe_reset:1;
+               u64 lpe_reset_vector:1;
+               u64 runstall:1;
+               u64 pwaitmode:1;
+               u64 clk_sel:3;
+               u64 rsvd2:1;
+               u64 sst_clk:3;
+               u64 xt_snoop:1;
+               u64 rsvd3:4;
+               u64 clk_sel1:6;
+               u64 clk_enable:3;
+               u64 rsvd4:6;
+               u64 slim0baseclk:1;
+               u64 rsvd:32;
+       } part;
+       u64 full;
+};
+
+union interrupt_reg_mrfld {
+       struct {
+               u64 done_interrupt:1;
+               u64 busy_interrupt:1;
+               u64 rsvd:62;
+       } part;
+       u64 full;
+};
+
+union sst_imr_reg_mrfld {
+       struct {
+               u64 done_interrupt:1;
+               u64 busy_interrupt:1;
+               u64 rsvd:62;
+       } part;
+       u64 full;
+};
+
+/**
+ * struct sst_block - This structure is used to block a user/fw data call to another
+ * fw/user call
+ *
+ * @condition: condition for blocking check
+ * @ret_code: ret code when block is released
+ * @data: data ptr
+ * @size: size of data
+ * @on: block condition
+ * @msg_id: msg_id = msgid in mfld/ctp, mrfld = NULL
+ * @drv_id: str_id in mfld/ctp, = drv_id in mrfld
+ * @node: list head node
+ */
+struct sst_block {
+       bool    condition;
+       int     ret_code;
+       void    *data;
+       u32     size;
+       bool    on;
+       u32     msg_id;
+       u32     drv_id;
+       struct list_head node;
+};
+
+/**
+ * struct stream_info - structure that holds the stream information
+ *
+ * @status : stream current state
+ * @prev : stream prev state
+ * @ops : stream operation pb/cp/drm...
+ * @bufs: stream buffer list
+ * @lock : stream mutex for protecting state
+ * @pcm_substream : PCM substream
+ * @period_elapsed : PCM period elapsed callback
+ * @sfreq : stream sampling freq
+ * @str_type : stream type
+ * @cumm_bytes : cummulative bytes decoded
+ * @str_type : stream type
+ * @src : stream source
+ */
+struct stream_info {
+       unsigned int            status;
+       unsigned int            prev;
+       unsigned int            ops;
+       struct mutex            lock;
+
+       void                    *pcm_substream;
+       void (*period_elapsed)(void *pcm_substream);
+
+       unsigned int            sfreq;
+       u32                     cumm_bytes;
+
+       void                    *compr_cb_param;
+       void (*compr_cb)(void *compr_cb_param);
+
+       void                    *drain_cb_param;
+       void (*drain_notify)(void *drain_cb_param);
+
+       unsigned int            num_ch;
+       unsigned int            pipe_id;
+       unsigned int            str_id;
+       unsigned int            task_id;
+};
+
+#define SST_FW_SIGN "$SST"
+#define SST_FW_LIB_SIGN "$LIB"
+
+/**
+ * struct sst_fw_header - FW file headers
+ *
+ * @signature : FW signature
+ * @file_size: size of fw image
+ * @modules : # of modules
+ * @file_format : version of header format
+ * @reserved : reserved fields
+ */
+struct sst_fw_header {
+       unsigned char signature[FW_SIGNATURE_SIZE];
+       u32 file_size;
+       u32 modules;
+       u32 file_format;
+       u32 reserved[4];
+};
+
+/**
+ * struct fw_module_header - module header in FW
+ *
+ * @signature: module signature
+ * @mod_size: size of module
+ * @blocks: block count
+ * @type: block type
+ * @entry_point: module netry point
+ */
+struct fw_module_header {
+       unsigned char signature[FW_SIGNATURE_SIZE];
+       u32 mod_size;
+       u32 blocks;
+       u32 type;
+       u32 entry_point;
+};
+
+/**
+ * struct fw_block_info - block header for FW
+ *
+ * @type: block ram type I/D
+ * @size: size of block
+ * @ram_offset: offset in ram
+ */
+struct fw_block_info {
+       enum sst_ram_type       type;
+       u32                     size;
+       u32                     ram_offset;
+       u32                     rsvd;
+};
+
+struct sst_runtime_param {
+       struct snd_sst_runtime_params param;
+};
+
+struct sst_sg_list {
+       struct scatterlist *src;
+       struct scatterlist *dst;
+       int list_len;
+       unsigned int sg_idx;
+};
+
+struct sst_memcpy_list {
+       struct list_head memcpylist;
+       void *dstn;
+       const void *src;
+       u32 size;
+       bool is_io;
+};
+
+/*Firmware Module Information*/
+enum sst_lib_dwnld_status {
+       SST_LIB_NOT_FOUND = 0,
+       SST_LIB_FOUND,
+       SST_LIB_DOWNLOADED,
+};
+
+struct sst_module_info {
+       const char *name; /*Library name*/
+       u32     id; /*Module ID*/
+       u32     entry_pt; /*Module entry point*/
+       u8      status; /*module status*/
+       u8      rsvd1;
+       u16     rsvd2;
+};
+
+/*
+ * Structure for managing the Library Region(1.5MB)
+ * in DDR in Merrifield
+ */
+struct sst_mem_mgr {
+       phys_addr_t current_base;
+       int avail;
+       unsigned int count;
+};
+
+struct sst_ipc_reg {
+       int ipcx;
+       int ipcd;
+};
+
+struct sst_shim_regs64 {
+       u64 csr;
+       u64 pisr;
+       u64 pimr;
+       u64 isrx;
+       u64 isrd;
+       u64 imrx;
+       u64 imrd;
+       u64 ipcx;
+       u64 ipcd;
+       u64 isrsc;
+       u64 isrlpesc;
+       u64 imrsc;
+       u64 imrlpesc;
+       u64 ipcsc;
+       u64 ipclpesc;
+       u64 clkctl;
+       u64 csr2;
+};
+
+/**
+ * struct intel_sst_drv - driver ops
+ *
+ * @sst_state : current sst device state
+ * @dev_id : device identifier, pci_id for pci devices and acpi_id for acpi
+ *          devices
+ * @shim : SST shim pointer
+ * @mailbox : SST mailbox pointer
+ * @iram : SST IRAM pointer
+ * @dram : SST DRAM pointer
+ * @pdata : SST info passed as a part of pci platform data
+ * @shim_phy_add : SST shim phy addr
+ * @shim_regs64: Struct to save shim registers
+ * @ipc_dispatch_list : ipc messages dispatched
+ * @rx_list : to copy the process_reply/process_msg from DSP
+ * @ipc_post_msg_wq : wq to post IPC messages context
+ * @mad_ops : MAD driver operations registered
+ * @mad_wq : MAD driver wq
+ * @post_msg_wq : wq to post IPC messages
+ * @streams : sst stream contexts
+ * @list_lock : sst driver list lock (deprecated)
+ * @ipc_spin_lock : spin lock to handle audio shim access and ipc queue
+ * @block_lock : spin lock to add block to block_list and assign pvt_id
+ * @rx_msg_lock : spin lock to handle the rx messages from the DSP
+ * @scard_ops : sst card ops
+ * @pci : sst pci device struture
+ * @dev : pointer to current device struct
+ * @sst_lock : sst device lock
+ * @pvt_id : sst private id
+ * @stream_cnt : total sst active stream count
+ * @pb_streams : total active pb streams
+ * @cp_streams : total active cp streams
+ * @audio_start : audio status
+ * @qos                : PM Qos struct
+ * firmware_name : Firmware / Library name
+ */
+struct intel_sst_drv {
+       int                     sst_state;
+       int                     irq_num;
+       unsigned int            dev_id;
+       void __iomem            *ddr;
+       void __iomem            *shim;
+       void __iomem            *mailbox;
+       void __iomem            *iram;
+       void __iomem            *dram;
+       unsigned int            mailbox_add;
+       unsigned int            iram_base;
+       unsigned int            dram_base;
+       unsigned int            shim_phy_add;
+       unsigned int            iram_end;
+       unsigned int            dram_end;
+       unsigned int            ddr_end;
+       unsigned int            ddr_base;
+       unsigned int            mailbox_recv_offset;
+       struct sst_shim_regs64  *shim_regs64;
+       struct list_head        block_list;
+       struct list_head        ipc_dispatch_list;
+       struct sst_platform_info *pdata;
+       struct list_head        rx_list;
+       struct work_struct      ipc_post_msg_wq;
+       wait_queue_head_t       wait_queue;
+       struct workqueue_struct *post_msg_wq;
+       unsigned int            tstamp;
+       /* str_id 0 is not used */
+       struct stream_info      streams[MAX_NUM_STREAMS+1];
+       spinlock_t              ipc_spin_lock;
+       spinlock_t              block_lock;
+       spinlock_t              rx_msg_lock;
+       struct pci_dev          *pci;
+       struct device           *dev;
+       volatile long unsigned          pvt_id;
+       struct mutex            sst_lock;
+       unsigned int            stream_cnt;
+       unsigned int            csr_value;
+       void                    *fw_in_mem;
+       struct sst_sg_list      fw_sg_list, library_list;
+       struct intel_sst_ops    *ops;
+       struct sst_info         info;
+       struct pm_qos_request   *qos;
+       unsigned int            use_dma;
+       unsigned int            use_lli;
+       atomic_t                fw_clear_context;
+       bool                    lib_dwnld_reqd;
+       struct list_head        memcpy_list;
+       struct sst_ipc_reg      ipc_reg;
+       struct sst_mem_mgr      lib_mem_mgr;
+       /*
+        * Holder for firmware name. Due to async call it needs to be
+        * persistent till worker thread gets called
+        */
+       char firmware_name[20];
+};
+
+/* misc definitions */
+#define FW_DWNL_ID 0x01
+
+struct intel_sst_ops {
+       irqreturn_t (*interrupt)(int, void *);
+       irqreturn_t (*irq_thread)(int, void *);
+       void (*clear_interrupt)(struct intel_sst_drv *ctx);
+       int (*start)(struct intel_sst_drv *ctx);
+       int (*reset)(struct intel_sst_drv *ctx);
+       void (*process_reply)(struct intel_sst_drv *ctx, struct ipc_post *msg);
+       int (*post_message)(struct intel_sst_drv *ctx,
+                       struct ipc_post *msg, bool sync);
+       void (*process_message)(struct ipc_post *msg);
+       void (*set_bypass)(bool set);
+       int (*save_dsp_context)(struct intel_sst_drv *sst);
+       void (*restore_dsp_context)(void);
+       int (*alloc_stream)(struct intel_sst_drv *ctx, void *params);
+       void (*post_download)(struct intel_sst_drv *sst);
+};
+
+int sst_pause_stream(struct intel_sst_drv *sst_drv_ctx, int id);
+int sst_resume_stream(struct intel_sst_drv *sst_drv_ctx, int id);
+int sst_drop_stream(struct intel_sst_drv *sst_drv_ctx, int id);
+int sst_free_stream(struct intel_sst_drv *sst_drv_ctx, int id);
+int sst_start_stream(struct intel_sst_drv *sst_drv_ctx, int str_id);
+int sst_send_byte_stream_mrfld(struct intel_sst_drv *ctx,
+                       struct snd_sst_bytes_v2 *sbytes);
+int sst_set_stream_param(int str_id, struct snd_sst_params *str_param);
+int sst_set_metadata(int str_id, char *params);
+int sst_get_stream(struct intel_sst_drv *sst_drv_ctx,
+               struct snd_sst_params *str_param);
+int sst_get_stream_allocated(struct intel_sst_drv *ctx,
+               struct snd_sst_params *str_param,
+               struct snd_sst_lib_download **lib_dnld);
+int sst_drain_stream(struct intel_sst_drv *sst_drv_ctx,
+               int str_id, bool partial_drain);
+int sst_post_message_mrfld(struct intel_sst_drv *ctx,
+               struct ipc_post *msg, bool sync);
+void sst_process_reply_mrfld(struct intel_sst_drv *ctx, struct ipc_post *msg);
+int sst_start_mrfld(struct intel_sst_drv *ctx);
+int intel_sst_reset_dsp_mrfld(struct intel_sst_drv *ctx);
+void intel_sst_clear_intr_mrfld(struct intel_sst_drv *ctx);
+
+int sst_load_fw(struct intel_sst_drv *ctx);
+int sst_load_library(struct snd_sst_lib_download *lib, u8 ops);
+void sst_post_download_mrfld(struct intel_sst_drv *ctx);
+int sst_get_block_stream(struct intel_sst_drv *sst_drv_ctx);
+void sst_memcpy_free_resources(struct intel_sst_drv *ctx);
+
+int sst_wait_interruptible(struct intel_sst_drv *sst_drv_ctx,
+                               struct sst_block *block);
+int sst_wait_timeout(struct intel_sst_drv *sst_drv_ctx,
+                       struct sst_block *block);
+int sst_create_ipc_msg(struct ipc_post **arg, bool large);
+int free_stream_context(struct intel_sst_drv *ctx, unsigned int str_id);
+void sst_clean_stream(struct stream_info *stream);
+int intel_sst_register_compress(struct intel_sst_drv *sst);
+int intel_sst_remove_compress(struct intel_sst_drv *sst);
+void sst_cdev_fragment_elapsed(struct intel_sst_drv *ctx, int str_id);
+int sst_send_sync_msg(int ipc, int str_id);
+int sst_get_num_channel(struct snd_sst_params *str_param);
+int sst_get_sfreq(struct snd_sst_params *str_param);
+int sst_alloc_stream_mrfld(struct intel_sst_drv *sst_drv_ctx, void *params);
+void sst_restore_fw_context(void);
+struct sst_block *sst_create_block(struct intel_sst_drv *ctx,
+                               u32 msg_id, u32 drv_id);
+int sst_create_block_and_ipc_msg(struct ipc_post **arg, bool large,
+               struct intel_sst_drv *sst_drv_ctx, struct sst_block **block,
+               u32 msg_id, u32 drv_id);
+int sst_free_block(struct intel_sst_drv *ctx, struct sst_block *freed);
+int sst_wake_up_block(struct intel_sst_drv *ctx, int result,
+               u32 drv_id, u32 ipc, void *data, u32 size);
+int sst_request_firmware_async(struct intel_sst_drv *ctx);
+int sst_driver_ops(struct intel_sst_drv *sst);
+struct sst_platform_info *sst_get_acpi_driver_data(const char *hid);
+void sst_firmware_load_cb(const struct firmware *fw, void *context);
+int sst_prepare_and_post_msg(struct intel_sst_drv *sst,
+               int task_id, int ipc_msg, int cmd_id, int pipe_id,
+               size_t mbox_data_len, const void *mbox_data, void **data,
+               bool large, bool fill_dsp, bool sync, bool response);
+
+void sst_process_pending_msg(struct work_struct *work);
+int sst_assign_pvt_id(struct intel_sst_drv *sst_drv_ctx);
+void sst_init_stream(struct stream_info *stream,
+               int codec, int sst_id, int ops, u8 slot);
+int sst_validate_strid(struct intel_sst_drv *sst_drv_ctx, int str_id);
+struct stream_info *get_stream_info(struct intel_sst_drv *sst_drv_ctx,
+               int str_id);
+int get_stream_id_mrfld(struct intel_sst_drv *sst_drv_ctx,
+               u32 pipe_id);
+u32 relocate_imr_addr_mrfld(u32 base_addr);
+void sst_add_to_dispatch_list_and_post(struct intel_sst_drv *sst,
+                                       struct ipc_post *msg);
+int sst_pm_runtime_put(struct intel_sst_drv *sst_drv);
+int sst_shim_write(void __iomem *addr, int offset, int value);
+u32 sst_shim_read(void __iomem *addr, int offset);
+u64 sst_reg_read64(void __iomem *addr, int offset);
+int sst_shim_write64(void __iomem *addr, int offset, u64 value);
+u64 sst_shim_read64(void __iomem *addr, int offset);
+void sst_set_fw_state_locked(
+               struct intel_sst_drv *sst_drv_ctx, int sst_state);
+void sst_fill_header_mrfld(union ipc_header_mrfld *header,
+                               int msg, int task_id, int large, int drv_id);
+void sst_fill_header_dsp(struct ipc_dsp_hdr *dsp, int msg,
+                                       int pipe_id, int len);
+
+int sst_register(struct device *);
+int sst_unregister(struct device *);
+
+int sst_alloc_drv_context(struct intel_sst_drv **ctx,
+               struct device *dev, unsigned int dev_id);
+int sst_context_init(struct intel_sst_drv *ctx);
+void sst_context_cleanup(struct intel_sst_drv *ctx);
+void sst_configure_runtime_pm(struct intel_sst_drv *ctx);
+#endif
diff --git a/sound/soc/intel/sst/sst_acpi.c b/sound/soc/intel/sst/sst_acpi.c
new file mode 100644 (file)
index 0000000..31124aa
--- /dev/null
@@ -0,0 +1,383 @@
+/*
+ * sst_acpi.c - SST (LPE) driver init file for ACPI enumeration.
+ *
+ * Copyright (c) 2013, Intel Corporation.
+ *
+ *  Authors:   Ramesh Babu K V <Ramesh.Babu@intel.com>
+ *  Authors:   Omair Mohammed Abdullah <omair.m.abdullah@intel.com>
+ *
+ * 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 <linux/module.h>
+#include <linux/fs.h>
+#include <linux/interrupt.h>
+#include <linux/slab.h>
+#include <linux/io.h>
+#include <linux/miscdevice.h>
+#include <linux/platform_device.h>
+#include <linux/firmware.h>
+#include <linux/pm_runtime.h>
+#include <linux/pm_qos.h>
+#include <linux/acpi.h>
+#include <asm/platform_sst_audio.h>
+#include <sound/core.h>
+#include <sound/soc.h>
+#include <sound/compress_driver.h>
+#include <acpi/acbuffer.h>
+#include <acpi/platform/acenv.h>
+#include <acpi/platform/aclinux.h>
+#include <acpi/actypes.h>
+#include <acpi/acpi_bus.h>
+#include "../sst-mfld-platform.h"
+#include "../sst-dsp.h"
+#include "sst.h"
+
+struct sst_machines {
+       char codec_id[32];
+       char board[32];
+       char machine[32];
+       void (*machine_quirk)(void);
+       char firmware[32];
+       struct sst_platform_info *pdata;
+
+};
+
+/* LPE viewpoint addresses */
+#define SST_BYT_IRAM_PHY_START 0xff2c0000
+#define SST_BYT_IRAM_PHY_END   0xff2d4000
+#define SST_BYT_DRAM_PHY_START 0xff300000
+#define SST_BYT_DRAM_PHY_END   0xff320000
+#define SST_BYT_IMR_VIRT_START 0xc0000000 /* virtual addr in LPE */
+#define SST_BYT_IMR_VIRT_END   0xc01fffff
+#define SST_BYT_SHIM_PHY_ADDR  0xff340000
+#define SST_BYT_MBOX_PHY_ADDR  0xff344000
+#define SST_BYT_DMA0_PHY_ADDR  0xff298000
+#define SST_BYT_DMA1_PHY_ADDR  0xff29c000
+#define SST_BYT_SSP0_PHY_ADDR  0xff2a0000
+#define SST_BYT_SSP2_PHY_ADDR  0xff2a2000
+
+#define BYT_FW_MOD_TABLE_OFFSET        0x80000
+#define BYT_FW_MOD_TABLE_SIZE  0x100
+#define BYT_FW_MOD_OFFSET      (BYT_FW_MOD_TABLE_OFFSET + BYT_FW_MOD_TABLE_SIZE)
+
+static const struct sst_info byt_fwparse_info = {
+       .use_elf        = false,
+       .max_streams    = 25,
+       .iram_start     = SST_BYT_IRAM_PHY_START,
+       .iram_end       = SST_BYT_IRAM_PHY_END,
+       .iram_use       = true,
+       .dram_start     = SST_BYT_DRAM_PHY_START,
+       .dram_end       = SST_BYT_DRAM_PHY_END,
+       .dram_use       = true,
+       .imr_start      = SST_BYT_IMR_VIRT_START,
+       .imr_end        = SST_BYT_IMR_VIRT_END,
+       .imr_use        = true,
+       .mailbox_start  = SST_BYT_MBOX_PHY_ADDR,
+       .num_probes     = 0,
+       .lpe_viewpt_rqd  = true,
+};
+
+static const struct sst_ipc_info byt_ipc_info = {
+       .ipc_offset = 0,
+       .mbox_recv_off = 0x400,
+};
+
+static const struct sst_lib_dnld_info  byt_lib_dnld_info = {
+       .mod_base           = SST_BYT_IMR_VIRT_START,
+       .mod_end            = SST_BYT_IMR_VIRT_END,
+       .mod_table_offset   = BYT_FW_MOD_TABLE_OFFSET,
+       .mod_table_size     = BYT_FW_MOD_TABLE_SIZE,
+       .mod_ddr_dnld       = false,
+};
+
+static const struct sst_res_info byt_rvp_res_info = {
+       .shim_offset = 0x140000,
+       .shim_size = 0x000100,
+       .shim_phy_addr = SST_BYT_SHIM_PHY_ADDR,
+       .ssp0_offset = 0xa0000,
+       .ssp0_size = 0x1000,
+       .dma0_offset = 0x98000,
+       .dma0_size = 0x4000,
+       .dma1_offset = 0x9c000,
+       .dma1_size = 0x4000,
+       .iram_offset = 0x0c0000,
+       .iram_size = 0x14000,
+       .dram_offset = 0x100000,
+       .dram_size = 0x28000,
+       .mbox_offset = 0x144000,
+       .mbox_size = 0x1000,
+       .acpi_lpe_res_index = 0,
+       .acpi_ddr_index = 2,
+       .acpi_ipc_irq_index = 5,
+};
+
+static struct sst_platform_info byt_rvp_platform_data = {
+       .probe_data = &byt_fwparse_info,
+       .ipc_info = &byt_ipc_info,
+       .lib_info = &byt_lib_dnld_info,
+       .res_info = &byt_rvp_res_info,
+       .platform = "sst-mfld-platform",
+};
+
+/* Cherryview (Cherrytrail and Braswell) uses same mrfld dpcm fw as Baytrail,
+ * so pdata is same as Baytrail.
+ */
+static struct sst_platform_info chv_platform_data = {
+       .probe_data = &byt_fwparse_info,
+       .ipc_info = &byt_ipc_info,
+       .lib_info = &byt_lib_dnld_info,
+       .res_info = &byt_rvp_res_info,
+       .platform = "sst-mfld-platform",
+};
+
+static int sst_platform_get_resources(struct intel_sst_drv *ctx)
+{
+       struct resource *rsrc;
+       struct platform_device *pdev = to_platform_device(ctx->dev);
+
+       /* All ACPI resource request here */
+       /* Get Shim addr */
+       rsrc = platform_get_resource(pdev, IORESOURCE_MEM,
+                                       ctx->pdata->res_info->acpi_lpe_res_index);
+       if (!rsrc) {
+               dev_err(ctx->dev, "Invalid SHIM base from IFWI");
+               return -EIO;
+       }
+       dev_info(ctx->dev, "LPE base: %#x size:%#x", (unsigned int) rsrc->start,
+                                       (unsigned int)resource_size(rsrc));
+
+       ctx->iram_base = rsrc->start + ctx->pdata->res_info->iram_offset;
+       ctx->iram_end =  ctx->iram_base + ctx->pdata->res_info->iram_size - 1;
+       dev_info(ctx->dev, "IRAM base: %#x", ctx->iram_base);
+       ctx->iram = devm_ioremap_nocache(ctx->dev, ctx->iram_base,
+                                        ctx->pdata->res_info->iram_size);
+       if (!ctx->iram) {
+               dev_err(ctx->dev, "unable to map IRAM");
+               return -EIO;
+       }
+
+       ctx->dram_base = rsrc->start + ctx->pdata->res_info->dram_offset;
+       ctx->dram_end = ctx->dram_base + ctx->pdata->res_info->dram_size - 1;
+       dev_info(ctx->dev, "DRAM base: %#x", ctx->dram_base);
+       ctx->dram = devm_ioremap_nocache(ctx->dev, ctx->dram_base,
+                                        ctx->pdata->res_info->dram_size);
+       if (!ctx->dram) {
+               dev_err(ctx->dev, "unable to map DRAM");
+               return -EIO;
+       }
+
+       ctx->shim_phy_add = rsrc->start + ctx->pdata->res_info->shim_offset;
+       dev_info(ctx->dev, "SHIM base: %#x", ctx->shim_phy_add);
+       ctx->shim = devm_ioremap_nocache(ctx->dev, ctx->shim_phy_add,
+                                       ctx->pdata->res_info->shim_size);
+       if (!ctx->shim) {
+               dev_err(ctx->dev, "unable to map SHIM");
+               return -EIO;
+       }
+
+       /* reassign physical address to LPE viewpoint address */
+       ctx->shim_phy_add = ctx->pdata->res_info->shim_phy_addr;
+
+       /* Get mailbox addr */
+       ctx->mailbox_add = rsrc->start + ctx->pdata->res_info->mbox_offset;
+       dev_info(ctx->dev, "Mailbox base: %#x", ctx->mailbox_add);
+       ctx->mailbox = devm_ioremap_nocache(ctx->dev, ctx->mailbox_add,
+                                           ctx->pdata->res_info->mbox_size);
+       if (!ctx->mailbox) {
+               dev_err(ctx->dev, "unable to map mailbox");
+               return -EIO;
+       }
+
+       /* reassign physical address to LPE viewpoint address */
+       ctx->mailbox_add = ctx->info.mailbox_start;
+
+       rsrc = platform_get_resource(pdev, IORESOURCE_MEM,
+                                       ctx->pdata->res_info->acpi_ddr_index);
+       if (!rsrc) {
+               dev_err(ctx->dev, "Invalid DDR base from IFWI");
+               return -EIO;
+       }
+       ctx->ddr_base = rsrc->start;
+       ctx->ddr_end = rsrc->end;
+       dev_info(ctx->dev, "DDR base: %#x", ctx->ddr_base);
+       ctx->ddr = devm_ioremap_nocache(ctx->dev, ctx->ddr_base,
+                                       resource_size(rsrc));
+       if (!ctx->ddr) {
+               dev_err(ctx->dev, "unable to map DDR");
+               return -EIO;
+       }
+
+       /* Find the IRQ */
+       ctx->irq_num = platform_get_irq(pdev,
+                               ctx->pdata->res_info->acpi_ipc_irq_index);
+       return 0;
+}
+
+static acpi_status sst_acpi_mach_match(acpi_handle handle, u32 level,
+                                      void *context, void **ret)
+{
+       *(bool *)context = true;
+       return AE_OK;
+}
+
+static struct sst_machines *sst_acpi_find_machine(
+       struct sst_machines *machines)
+{
+       struct sst_machines *mach;
+       bool found = false;
+
+       for (mach = machines; mach->codec_id; mach++)
+               if (ACPI_SUCCESS(acpi_get_devices(mach->codec_id,
+                                                 sst_acpi_mach_match,
+                                                 &found, NULL)) && found)
+                       return mach;
+
+       return NULL;
+}
+
+int sst_acpi_probe(struct platform_device *pdev)
+{
+       struct device *dev = &pdev->dev;
+       int ret = 0;
+       struct intel_sst_drv *ctx;
+       const struct acpi_device_id *id;
+       struct sst_machines *mach;
+       struct platform_device *mdev;
+       struct platform_device *plat_dev;
+       unsigned int dev_id;
+
+       id = acpi_match_device(dev->driver->acpi_match_table, dev);
+       if (!id)
+               return -ENODEV;
+       dev_dbg(dev, "for %s", id->id);
+
+       mach = (struct sst_machines *)id->driver_data;
+       mach = sst_acpi_find_machine(mach);
+       if (mach == NULL) {
+               dev_err(dev, "No matching machine driver found\n");
+               return -ENODEV;
+       }
+
+       ret = kstrtouint(id->id, 16, &dev_id);
+       if (ret < 0) {
+               dev_err(dev, "Unique device id conversion error: %d\n", ret);
+               return ret;
+       }
+
+       dev_dbg(dev, "ACPI device id: %x\n", dev_id);
+
+       plat_dev = platform_device_register_data(dev, mach->pdata->platform, -1, NULL, 0);
+       if (plat_dev == NULL) {
+               dev_err(dev, "Failed to create machine device: %s\n", mach->pdata->platform);
+               return -ENODEV;
+       }
+
+       /* Create platform device for sst machine driver */
+       mdev = platform_device_register_data(dev, mach->machine, -1, NULL, 0);
+       if (mdev == NULL) {
+               dev_err(dev, "Failed to create machine device: %s\n", mach->machine);
+               return -ENODEV;
+       }
+
+       ret = sst_alloc_drv_context(&ctx, dev, dev_id);
+       if (ret < 0)
+               return ret;
+
+       /* Fill sst platform data */
+       ctx->pdata = mach->pdata;
+       strcpy(ctx->firmware_name, mach->firmware);
+
+       ret = sst_platform_get_resources(ctx);
+       if (ret)
+               return ret;
+
+       ret = sst_context_init(ctx);
+       if (ret < 0)
+               return ret;
+
+       /* need to save shim registers in BYT */
+       ctx->shim_regs64 = devm_kzalloc(ctx->dev, sizeof(*ctx->shim_regs64),
+                                       GFP_KERNEL);
+       if (!ctx->shim_regs64) {
+               return -ENOMEM;
+               goto do_sst_cleanup;
+       }
+
+       sst_configure_runtime_pm(ctx);
+       platform_set_drvdata(pdev, ctx);
+       return ret;
+
+do_sst_cleanup:
+       sst_context_cleanup(ctx);
+       platform_set_drvdata(pdev, NULL);
+       dev_err(ctx->dev, "failed with %d\n", ret);
+       return ret;
+}
+
+/**
+* intel_sst_remove - remove function
+*
+* @pdev:       platform device structure
+*
+* This function is called by OS when a device is unloaded
+* This frees the interrupt etc
+*/
+int sst_acpi_remove(struct platform_device *pdev)
+{
+       struct intel_sst_drv *ctx;
+
+       ctx = platform_get_drvdata(pdev);
+       sst_context_cleanup(ctx);
+       platform_set_drvdata(pdev, NULL);
+       return 0;
+}
+
+static struct sst_machines sst_acpi_bytcr[] = {
+       {"10EC5640", "T100", "bytt100_rt5640", NULL, "fw_sst_0f28.bin",
+                                               &byt_rvp_platform_data },
+       {},
+};
+
+/* Cherryview-based platforms: CherryTrail and Braswell */
+static struct sst_machines sst_acpi_chv[] = {
+       {"10EC5670", "cht-bsw", "cht-bsw-rt5672", NULL, "fw_sst_22a8.bin",
+                                               &chv_platform_data },
+       {},
+};
+
+static const struct acpi_device_id sst_acpi_ids[] = {
+       { "80860F28", (unsigned long)&sst_acpi_bytcr},
+       { "808622A8", (unsigned long) &sst_acpi_chv},
+       { },
+};
+
+MODULE_DEVICE_TABLE(acpi, sst_acpi_ids);
+
+static struct platform_driver sst_acpi_driver = {
+       .driver = {
+               .name                   = "intel_sst_acpi",
+               .owner                  = THIS_MODULE,
+               .acpi_match_table       = ACPI_PTR(sst_acpi_ids),
+               .pm                     = &intel_sst_pm,
+       },
+       .probe  = sst_acpi_probe,
+       .remove = sst_acpi_remove,
+};
+
+module_platform_driver(sst_acpi_driver);
+
+MODULE_DESCRIPTION("Intel (R) SST(R) Audio Engine ACPI Driver");
+MODULE_AUTHOR("Ramesh Babu K V");
+MODULE_AUTHOR("Omair Mohammed Abdullah");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("sst");
diff --git a/sound/soc/intel/sst/sst_drv_interface.c b/sound/soc/intel/sst/sst_drv_interface.c
new file mode 100644 (file)
index 0000000..5f75ef3
--- /dev/null
@@ -0,0 +1,686 @@
+/*
+ *  sst_drv_interface.c - Intel SST Driver for audio engine
+ *
+ *  Copyright (C) 2008-14 Intel Corp
+ *  Authors:   Vinod Koul <vinod.koul@intel.com>
+ *             Harsha Priya <priya.harsha@intel.com>
+ *             Dharageswari R <dharageswari.r@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 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 <linux/delay.h>
+#include <linux/pci.h>
+#include <linux/fs.h>
+#include <linux/firmware.h>
+#include <linux/pm_runtime.h>
+#include <linux/pm_qos.h>
+#include <linux/math64.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/soc.h>
+#include <sound/compress_driver.h>
+#include <asm/platform_sst_audio.h>
+#include "../sst-mfld-platform.h"
+#include "sst.h"
+#include "../sst-dsp.h"
+
+
+
+#define NUM_CODEC 2
+#define MIN_FRAGMENT 2
+#define MAX_FRAGMENT 4
+#define MIN_FRAGMENT_SIZE (50 * 1024)
+#define MAX_FRAGMENT_SIZE (1024 * 1024)
+#define SST_GET_BYTES_PER_SAMPLE(pcm_wd_sz)  (((pcm_wd_sz + 15) >> 4) << 1)
+
+int free_stream_context(struct intel_sst_drv *ctx, unsigned int str_id)
+{
+       struct stream_info *stream;
+       int ret = 0;
+
+       stream = get_stream_info(ctx, str_id);
+       if (stream) {
+               /* str_id is valid, so stream is alloacted */
+               ret = sst_free_stream(ctx, str_id);
+               if (ret)
+                       sst_clean_stream(&ctx->streams[str_id]);
+               return ret;
+       } else {
+               dev_err(ctx->dev, "we tried to free stream context %d which was freed!!!\n", str_id);
+       }
+       return ret;
+}
+
+int sst_get_stream_allocated(struct intel_sst_drv *ctx,
+       struct snd_sst_params *str_param,
+       struct snd_sst_lib_download **lib_dnld)
+{
+       int retval;
+
+       retval = ctx->ops->alloc_stream(ctx, str_param);
+       if (retval > 0)
+               dev_dbg(ctx->dev, "Stream allocated %d\n", retval);
+       return retval;
+
+}
+
+/*
+ * sst_get_sfreq - this function returns the frequency of the stream
+ *
+ * @str_param : stream params
+ */
+int sst_get_sfreq(struct snd_sst_params *str_param)
+{
+       switch (str_param->codec) {
+       case SST_CODEC_TYPE_PCM:
+               return str_param->sparams.uc.pcm_params.sfreq;
+       case SST_CODEC_TYPE_AAC:
+               return str_param->sparams.uc.aac_params.externalsr;
+       case SST_CODEC_TYPE_MP3:
+               return 0;
+       default:
+               return -EINVAL;
+       }
+}
+
+/*
+ * sst_get_num_channel - get number of channels for the stream
+ *
+ * @str_param : stream params
+ */
+int sst_get_num_channel(struct snd_sst_params *str_param)
+{
+       switch (str_param->codec) {
+       case SST_CODEC_TYPE_PCM:
+               return str_param->sparams.uc.pcm_params.num_chan;
+       case SST_CODEC_TYPE_MP3:
+               return str_param->sparams.uc.mp3_params.num_chan;
+       case SST_CODEC_TYPE_AAC:
+               return str_param->sparams.uc.aac_params.num_chan;
+       default:
+               return -EINVAL;
+       }
+}
+
+/*
+ * sst_get_stream - this function prepares for stream allocation
+ *
+ * @str_param : stream param
+ */
+int sst_get_stream(struct intel_sst_drv *ctx,
+                       struct snd_sst_params *str_param)
+{
+       int retval;
+       struct stream_info *str_info;
+
+       /* stream is not allocated, we are allocating */
+       retval = ctx->ops->alloc_stream(ctx, str_param);
+       if (retval <= 0) {
+               return -EIO;
+       }
+       /* store sampling freq */
+       str_info = &ctx->streams[retval];
+       str_info->sfreq = sst_get_sfreq(str_param);
+
+       return retval;
+}
+
+static int sst_power_control(struct device *dev, bool state)
+{
+       struct intel_sst_drv *ctx = dev_get_drvdata(dev);
+
+       dev_dbg(ctx->dev, "state:%d", state);
+       if (state == true)
+               return pm_runtime_get_sync(dev);
+       else
+               return sst_pm_runtime_put(ctx);
+}
+
+/*
+ * sst_open_pcm_stream - Open PCM interface
+ *
+ * @str_param: parameters of pcm stream
+ *
+ * This function is called by MID sound card driver to open
+ * a new pcm interface
+ */
+static int sst_open_pcm_stream(struct device *dev,
+               struct snd_sst_params *str_param)
+{
+       int retval;
+       struct intel_sst_drv *ctx = dev_get_drvdata(dev);
+
+       if (!str_param)
+               return -EINVAL;
+
+       retval = sst_get_stream(ctx, str_param);
+       if (retval > 0)
+               ctx->stream_cnt++;
+       else
+               dev_err(ctx->dev, "sst_get_stream returned err %d\n", retval);
+
+       return retval;
+}
+
+static int sst_cdev_open(struct device *dev,
+               struct snd_sst_params *str_params, struct sst_compress_cb *cb)
+{
+       int str_id, retval;
+       struct stream_info *stream;
+       struct intel_sst_drv *ctx = dev_get_drvdata(dev);
+
+       retval = pm_runtime_get_sync(ctx->dev);
+       if (retval < 0)
+               return retval;
+
+       str_id = sst_get_stream(ctx, str_params);
+       if (str_id > 0) {
+               dev_dbg(dev, "stream allocated in sst_cdev_open %d\n", str_id);
+               stream = &ctx->streams[str_id];
+               stream->compr_cb = cb->compr_cb;
+               stream->compr_cb_param = cb->param;
+               stream->drain_notify = cb->drain_notify;
+               stream->drain_cb_param = cb->drain_cb_param;
+       } else {
+               dev_err(dev, "stream encountered error during alloc %d\n", str_id);
+               str_id = -EINVAL;
+               sst_pm_runtime_put(ctx);
+       }
+       return str_id;
+}
+
+static int sst_cdev_close(struct device *dev, unsigned int str_id)
+{
+       int retval;
+       struct stream_info *stream;
+       struct intel_sst_drv *ctx = dev_get_drvdata(dev);
+
+       stream = get_stream_info(ctx, str_id);
+       if (!stream) {
+               dev_err(dev, "stream info is NULL for str %d!!!\n", str_id);
+               return -EINVAL;
+       }
+
+       if (stream->status == STREAM_RESET) {
+               dev_dbg(dev, "stream in reset state...\n");
+               stream->status = STREAM_UN_INIT;
+
+               retval = 0;
+               goto put;
+       }
+
+       retval = sst_free_stream(ctx, str_id);
+put:
+       stream->compr_cb_param = NULL;
+       stream->compr_cb = NULL;
+
+       if (retval)
+               dev_err(dev, "free stream returned err %d\n", retval);
+
+       dev_dbg(dev, "End\n");
+       return retval;
+
+}
+
+static int sst_cdev_ack(struct device *dev, unsigned int str_id,
+               unsigned long bytes)
+{
+       struct stream_info *stream;
+       struct snd_sst_tstamp fw_tstamp = {0,};
+       int offset;
+       void __iomem *addr;
+       struct intel_sst_drv *ctx = dev_get_drvdata(dev);
+
+       stream = get_stream_info(ctx, str_id);
+       if (!stream)
+               return -EINVAL;
+
+       /* update bytes sent */
+       stream->cumm_bytes += bytes;
+       dev_dbg(dev, "bytes copied %d inc by %ld\n", stream->cumm_bytes, bytes);
+
+       memcpy_fromio(&fw_tstamp,
+               ((void *)(ctx->mailbox + ctx->tstamp)
+               +(str_id * sizeof(fw_tstamp))),
+               sizeof(fw_tstamp));
+
+       fw_tstamp.bytes_copied = stream->cumm_bytes;
+       dev_dbg(dev, "bytes sent to fw %llu inc by %ld\n",
+                       fw_tstamp.bytes_copied, bytes);
+
+       addr =  ((void *)(ctx->mailbox + ctx->tstamp)) +
+                       (str_id * sizeof(fw_tstamp));
+       offset =  offsetof(struct snd_sst_tstamp, bytes_copied);
+       sst_shim_write(addr, offset, fw_tstamp.bytes_copied);
+       return 0;
+}
+
+static int sst_cdev_set_metadata(struct device *dev,
+               unsigned int str_id, struct snd_compr_metadata *metadata)
+{
+       int retval = 0;
+       struct stream_info *str_info;
+       struct intel_sst_drv *ctx = dev_get_drvdata(dev);
+
+       dev_dbg(dev, "set metadata for stream %d\n", str_id);
+
+       str_info = get_stream_info(ctx, str_id);
+       if (!str_info)
+               return -EINVAL;
+
+       dev_dbg(dev, "pipe id = %d\n", str_info->pipe_id);
+       retval = sst_prepare_and_post_msg(ctx, str_info->task_id, IPC_CMD,
+                       IPC_IA_SET_STREAM_PARAMS_MRFLD, str_info->pipe_id,
+                       sizeof(*metadata), metadata, NULL,
+                       true, true, true, false);
+
+       return retval;
+}
+
+static int sst_cdev_stream_pause(struct device *dev, unsigned int str_id)
+{
+       struct intel_sst_drv *ctx = dev_get_drvdata(dev);
+
+       return sst_pause_stream(ctx, str_id);
+}
+
+static int sst_cdev_stream_pause_release(struct device *dev,
+               unsigned int str_id)
+{
+       struct intel_sst_drv *ctx = dev_get_drvdata(dev);
+
+       return sst_resume_stream(ctx, str_id);
+}
+
+static int sst_cdev_stream_start(struct device *dev, unsigned int str_id)
+{
+       struct stream_info *str_info;
+       struct intel_sst_drv *ctx = dev_get_drvdata(dev);
+
+       str_info = get_stream_info(ctx, str_id);
+       if (!str_info)
+               return -EINVAL;
+       str_info->prev = str_info->status;
+       str_info->status = STREAM_RUNNING;
+       return sst_start_stream(ctx, str_id);
+}
+
+static int sst_cdev_stream_drop(struct device *dev, unsigned int str_id)
+{
+       struct intel_sst_drv *ctx = dev_get_drvdata(dev);
+
+       return sst_drop_stream(ctx, str_id);
+}
+
+static int sst_cdev_stream_drain(struct device *dev, unsigned int str_id)
+{
+       struct intel_sst_drv *ctx = dev_get_drvdata(dev);
+
+       return sst_drain_stream(ctx, str_id, false);
+}
+
+static int sst_cdev_stream_partial_drain(struct device *dev,
+               unsigned int str_id)
+{
+       struct intel_sst_drv *ctx = dev_get_drvdata(dev);
+
+       return sst_drain_stream(ctx, str_id, true);
+}
+
+static int sst_cdev_tstamp(struct device *dev, unsigned int str_id,
+               struct snd_compr_tstamp *tstamp)
+{
+       struct snd_sst_tstamp fw_tstamp = {0,};
+       struct stream_info *stream;
+       struct intel_sst_drv *ctx = dev_get_drvdata(dev);
+
+       memcpy_fromio(&fw_tstamp,
+               ((void *)(ctx->mailbox + ctx->tstamp)
+               +(str_id * sizeof(fw_tstamp))),
+               sizeof(fw_tstamp));
+
+       stream = get_stream_info(ctx, str_id);
+       if (!stream)
+               return -EINVAL;
+       dev_dbg(dev, "rb_counter %llu in bytes\n", fw_tstamp.ring_buffer_counter);
+
+       tstamp->copied_total = fw_tstamp.ring_buffer_counter;
+       tstamp->pcm_frames = fw_tstamp.frames_decoded;
+       tstamp->pcm_io_frames = div_u64(fw_tstamp.hardware_counter,
+                       (u64)((stream->num_ch) * SST_GET_BYTES_PER_SAMPLE(24)));
+       tstamp->sampling_rate = fw_tstamp.sampling_frequency;
+
+       dev_dbg(dev, "PCM  = %u\n", tstamp->pcm_io_frames);
+       dev_dbg(dev, "Ptr Query on strid = %d  copied_total %d, decodec %d\n",
+               str_id, tstamp->copied_total, tstamp->pcm_frames);
+       dev_dbg(dev, "rendered %d\n", tstamp->pcm_io_frames);
+
+       return 0;
+}
+
+static int sst_cdev_caps(struct snd_compr_caps *caps)
+{
+       caps->num_codecs = NUM_CODEC;
+       caps->min_fragment_size = MIN_FRAGMENT_SIZE;  /* 50KB */
+       caps->max_fragment_size = MAX_FRAGMENT_SIZE;  /* 1024KB */
+       caps->min_fragments = MIN_FRAGMENT;
+       caps->max_fragments = MAX_FRAGMENT;
+       caps->codecs[0] = SND_AUDIOCODEC_MP3;
+       caps->codecs[1] = SND_AUDIOCODEC_AAC;
+       return 0;
+}
+
+static struct snd_compr_codec_caps caps_mp3 = {
+       .num_descriptors = 1,
+       .descriptor[0].max_ch = 2,
+       .descriptor[0].sample_rates[0] = 48000,
+       .descriptor[0].sample_rates[1] = 44100,
+       .descriptor[0].sample_rates[2] = 32000,
+       .descriptor[0].sample_rates[3] = 16000,
+       .descriptor[0].sample_rates[4] = 8000,
+       .descriptor[0].num_sample_rates = 5,
+       .descriptor[0].bit_rate[0] = 320,
+       .descriptor[0].bit_rate[1] = 192,
+       .descriptor[0].num_bitrates = 2,
+       .descriptor[0].profiles = 0,
+       .descriptor[0].modes = SND_AUDIOCHANMODE_MP3_STEREO,
+       .descriptor[0].formats = 0,
+};
+
+static struct snd_compr_codec_caps caps_aac = {
+       .num_descriptors = 2,
+       .descriptor[1].max_ch = 2,
+       .descriptor[0].sample_rates[0] = 48000,
+       .descriptor[0].sample_rates[1] = 44100,
+       .descriptor[0].sample_rates[2] = 32000,
+       .descriptor[0].sample_rates[3] = 16000,
+       .descriptor[0].sample_rates[4] = 8000,
+       .descriptor[0].num_sample_rates = 5,
+       .descriptor[1].bit_rate[0] = 320,
+       .descriptor[1].bit_rate[1] = 192,
+       .descriptor[1].num_bitrates = 2,
+       .descriptor[1].profiles = 0,
+       .descriptor[1].modes = 0,
+       .descriptor[1].formats =
+                       (SND_AUDIOSTREAMFORMAT_MP4ADTS |
+                               SND_AUDIOSTREAMFORMAT_RAW),
+};
+
+static int sst_cdev_codec_caps(struct snd_compr_codec_caps *codec)
+{
+       if (codec->codec == SND_AUDIOCODEC_MP3)
+               *codec = caps_mp3;
+       else if (codec->codec == SND_AUDIOCODEC_AAC)
+               *codec = caps_aac;
+       else
+               return -EINVAL;
+
+       return 0;
+}
+
+void sst_cdev_fragment_elapsed(struct intel_sst_drv *ctx, int str_id)
+{
+       struct stream_info *stream;
+
+       dev_dbg(ctx->dev, "fragment elapsed from firmware for str_id %d\n",
+                       str_id);
+       stream = &ctx->streams[str_id];
+       if (stream->compr_cb)
+               stream->compr_cb(stream->compr_cb_param);
+}
+
+/*
+ * sst_close_pcm_stream - Close PCM interface
+ *
+ * @str_id: stream id to be closed
+ *
+ * This function is called by MID sound card driver to close
+ * an existing pcm interface
+ */
+static int sst_close_pcm_stream(struct device *dev, unsigned int str_id)
+{
+       struct stream_info *stream;
+       int retval = 0;
+       struct intel_sst_drv *ctx = dev_get_drvdata(dev);
+
+       stream = get_stream_info(ctx, str_id);
+       if (!stream) {
+               dev_err(ctx->dev, "stream info is NULL for str %d!!!\n", str_id);
+               return -EINVAL;
+       }
+
+       if (stream->status == STREAM_RESET) {
+               /* silently fail here as we have cleaned the stream earlier */
+               dev_dbg(ctx->dev, "stream in reset state...\n");
+
+               retval = 0;
+               goto put;
+       }
+
+       retval = free_stream_context(ctx, str_id);
+put:
+       stream->pcm_substream = NULL;
+       stream->status = STREAM_UN_INIT;
+       stream->period_elapsed = NULL;
+       ctx->stream_cnt--;
+
+       if (retval)
+               dev_err(ctx->dev, "free stream returned err %d\n", retval);
+
+       dev_dbg(ctx->dev, "Exit\n");
+       return 0;
+}
+
+static inline int sst_calc_tstamp(struct intel_sst_drv *ctx,
+               struct pcm_stream_info *info,
+               struct snd_pcm_substream *substream,
+               struct snd_sst_tstamp *fw_tstamp)
+{
+       size_t delay_bytes, delay_frames;
+       size_t buffer_sz;
+       u32 pointer_bytes, pointer_samples;
+
+       dev_dbg(ctx->dev, "mrfld ring_buffer_counter %llu in bytes\n",
+                       fw_tstamp->ring_buffer_counter);
+       dev_dbg(ctx->dev, "mrfld hardware_counter %llu in bytes\n",
+                        fw_tstamp->hardware_counter);
+       if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+               delay_bytes = (size_t) (fw_tstamp->ring_buffer_counter -
+                                       fw_tstamp->hardware_counter);
+       else
+               delay_bytes = (size_t) (fw_tstamp->hardware_counter -
+                                       fw_tstamp->ring_buffer_counter);
+       delay_frames = bytes_to_frames(substream->runtime, delay_bytes);
+       buffer_sz = snd_pcm_lib_buffer_bytes(substream);
+       div_u64_rem(fw_tstamp->ring_buffer_counter, buffer_sz, &pointer_bytes);
+       pointer_samples = bytes_to_samples(substream->runtime, pointer_bytes);
+
+       dev_dbg(ctx->dev, "pcm delay %zu in bytes\n", delay_bytes);
+
+       info->buffer_ptr = pointer_samples / substream->runtime->channels;
+
+       info->pcm_delay = delay_frames / substream->runtime->channels;
+       dev_dbg(ctx->dev, "buffer ptr %llu pcm_delay rep: %llu\n",
+                       info->buffer_ptr, info->pcm_delay);
+       return 0;
+}
+
+static int sst_read_timestamp(struct device *dev, struct pcm_stream_info *info)
+{
+       struct stream_info *stream;
+       struct snd_pcm_substream *substream;
+       struct snd_sst_tstamp fw_tstamp;
+       unsigned int str_id;
+       struct intel_sst_drv *ctx = dev_get_drvdata(dev);
+
+       str_id = info->str_id;
+       stream = get_stream_info(ctx, str_id);
+       if (!stream)
+               return -EINVAL;
+
+       if (!stream->pcm_substream)
+               return -EINVAL;
+       substream = stream->pcm_substream;
+
+       memcpy_fromio(&fw_tstamp,
+               ((void *)(ctx->mailbox + ctx->tstamp)
+                       + (str_id * sizeof(fw_tstamp))),
+               sizeof(fw_tstamp));
+       return sst_calc_tstamp(ctx, info, substream, &fw_tstamp);
+}
+
+static int sst_stream_start(struct device *dev, int str_id)
+{
+       struct stream_info *str_info;
+       struct intel_sst_drv *ctx = dev_get_drvdata(dev);
+
+       if (ctx->sst_state != SST_FW_RUNNING)
+               return 0;
+       str_info = get_stream_info(ctx, str_id);
+       if (!str_info)
+               return -EINVAL;
+       str_info->prev = str_info->status;
+       str_info->status = STREAM_RUNNING;
+       sst_start_stream(ctx, str_id);
+
+       return 0;
+}
+
+static int sst_stream_drop(struct device *dev, int str_id)
+{
+       struct stream_info *str_info;
+       struct intel_sst_drv *ctx = dev_get_drvdata(dev);
+
+       if (ctx->sst_state != SST_FW_RUNNING)
+               return 0;
+
+       str_info = get_stream_info(ctx, str_id);
+       if (!str_info)
+               return -EINVAL;
+       str_info->prev = STREAM_UN_INIT;
+       str_info->status = STREAM_INIT;
+       return sst_drop_stream(ctx, str_id);
+}
+
+static int sst_stream_init(struct device *dev, struct pcm_stream_info *str_info)
+{
+       int str_id = 0;
+       struct stream_info *stream;
+       struct intel_sst_drv *ctx = dev_get_drvdata(dev);
+
+       str_id = str_info->str_id;
+
+       if (ctx->sst_state != SST_FW_RUNNING)
+               return 0;
+
+       stream = get_stream_info(ctx, str_id);
+       if (!stream)
+               return -EINVAL;
+
+       dev_dbg(ctx->dev, "setting the period ptrs\n");
+       stream->pcm_substream = str_info->arg;
+       stream->period_elapsed = str_info->period_elapsed;
+       stream->sfreq = str_info->sfreq;
+       stream->prev = stream->status;
+       stream->status = STREAM_INIT;
+       dev_dbg(ctx->dev,
+               "pcm_substream %p, period_elapsed %p, sfreq %d, status %d\n",
+               stream->pcm_substream, stream->period_elapsed,
+               stream->sfreq, stream->status);
+
+       return 0;
+}
+
+/*
+ * sst_set_byte_stream - Set generic params
+ *
+ * @cmd: control cmd to be set
+ * @arg: command argument
+ *
+ * This function is called by MID sound card driver to configure
+ * SST runtime params.
+ */
+static int sst_send_byte_stream(struct device *dev,
+               struct snd_sst_bytes_v2 *bytes)
+{
+       int ret_val = 0;
+       struct intel_sst_drv *ctx = dev_get_drvdata(dev);
+
+       if (NULL == bytes)
+               return -EINVAL;
+       ret_val = pm_runtime_get_sync(ctx->dev);
+       if (ret_val < 0)
+               return ret_val;
+
+       ret_val = sst_send_byte_stream_mrfld(ctx, bytes);
+       sst_pm_runtime_put(ctx);
+
+       return ret_val;
+}
+
+static struct sst_ops pcm_ops = {
+       .open = sst_open_pcm_stream,
+       .stream_init = sst_stream_init,
+       .stream_start = sst_stream_start,
+       .stream_drop = sst_stream_drop,
+       .stream_read_tstamp = sst_read_timestamp,
+       .send_byte_stream = sst_send_byte_stream,
+       .close = sst_close_pcm_stream,
+       .power = sst_power_control,
+};
+
+static struct compress_sst_ops compr_ops = {
+       .open = sst_cdev_open,
+       .close = sst_cdev_close,
+       .stream_pause = sst_cdev_stream_pause,
+       .stream_pause_release = sst_cdev_stream_pause_release,
+       .stream_start = sst_cdev_stream_start,
+       .stream_drop = sst_cdev_stream_drop,
+       .stream_drain = sst_cdev_stream_drain,
+       .stream_partial_drain = sst_cdev_stream_partial_drain,
+       .tstamp = sst_cdev_tstamp,
+       .ack = sst_cdev_ack,
+       .get_caps = sst_cdev_caps,
+       .get_codec_caps = sst_cdev_codec_caps,
+       .set_metadata = sst_cdev_set_metadata,
+       .power = sst_power_control,
+};
+
+static struct sst_device sst_dsp_device = {
+       .name = "Intel(R) SST LPE",
+       .dev = NULL,
+       .ops = &pcm_ops,
+       .compr_ops = &compr_ops,
+};
+
+/*
+ * sst_register - function to register DSP
+ *
+ * This functions registers DSP with the platform driver
+ */
+int sst_register(struct device *dev)
+{
+       int ret_val;
+
+       sst_dsp_device.dev = dev;
+       ret_val = sst_register_dsp(&sst_dsp_device);
+       if (ret_val)
+               dev_err(dev, "Unable to register DSP with platform driver\n");
+
+       return ret_val;
+}
+
+int sst_unregister(struct device *dev)
+{
+       return sst_unregister_dsp(&sst_dsp_device);
+}
diff --git a/sound/soc/intel/sst/sst_ipc.c b/sound/soc/intel/sst/sst_ipc.c
new file mode 100644 (file)
index 0000000..484e609
--- /dev/null
@@ -0,0 +1,373 @@
+/*
+ *  sst_ipc.c - Intel SST Driver for audio engine
+ *
+ *  Copyright (C) 2008-14 Intel Corporation
+ *  Authors:   Vinod Koul <vinod.koul@intel.com>
+ *             Harsha Priya <priya.harsha@intel.com>
+ *             Dharageswari R <dharageswari.r@intel.com>
+ *             KP Jeeja <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 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 <linux/pci.h>
+#include <linux/firmware.h>
+#include <linux/sched.h>
+#include <linux/delay.h>
+#include <linux/pm_runtime.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/soc.h>
+#include <sound/compress_driver.h>
+#include <asm/intel-mid.h>
+#include <asm/platform_sst_audio.h>
+#include "../sst-mfld-platform.h"
+#include "sst.h"
+#include "../sst-dsp.h"
+
+struct sst_block *sst_create_block(struct intel_sst_drv *ctx,
+                                       u32 msg_id, u32 drv_id)
+{
+       struct sst_block *msg = NULL;
+
+       dev_dbg(ctx->dev, "Enter\n");
+       msg = kzalloc(sizeof(*msg), GFP_KERNEL);
+       if (!msg)
+               return NULL;
+       msg->condition = false;
+       msg->on = true;
+       msg->msg_id = msg_id;
+       msg->drv_id = drv_id;
+       spin_lock_bh(&ctx->block_lock);
+       list_add_tail(&msg->node, &ctx->block_list);
+       spin_unlock_bh(&ctx->block_lock);
+
+       return msg;
+}
+
+/*
+ * while handling the interrupts, we need to check for message status and
+ * then if we are blocking for a message
+ *
+ * here we are unblocking the blocked ones, this is based on id we have
+ * passed and search that for block threads.
+ * We will not find block in two cases
+ *  a) when its small message and block in not there, so silently ignore
+ *  them
+ *  b) when we are actually not able to find the block (bug perhaps)
+ *
+ *  Since we have bit of small messages we can spam kernel log with err
+ *  print on above so need to keep as debug prints which should be enabled
+ *  via dynamic debug while debugging IPC issues
+ */
+int sst_wake_up_block(struct intel_sst_drv *ctx, int result,
+               u32 drv_id, u32 ipc, void *data, u32 size)
+{
+       struct sst_block *block = NULL;
+
+       dev_dbg(ctx->dev, "Enter\n");
+
+       spin_lock_bh(&ctx->block_lock);
+       list_for_each_entry(block, &ctx->block_list, node) {
+               dev_dbg(ctx->dev, "Block ipc %d, drv_id %d\n", block->msg_id,
+                                                       block->drv_id);
+               if (block->msg_id == ipc && block->drv_id == drv_id) {
+                       dev_dbg(ctx->dev, "free up the block\n");
+                       block->ret_code = result;
+                       block->data = data;
+                       block->size = size;
+                       block->condition = true;
+                       spin_unlock_bh(&ctx->block_lock);
+                       wake_up(&ctx->wait_queue);
+                       return 0;
+               }
+       }
+       spin_unlock_bh(&ctx->block_lock);
+       dev_dbg(ctx->dev,
+               "Block not found or a response received for a short msg for ipc %d, drv_id %d\n",
+               ipc, drv_id);
+       return -EINVAL;
+}
+
+int sst_free_block(struct intel_sst_drv *ctx, struct sst_block *freed)
+{
+       struct sst_block *block = NULL, *__block;
+
+       dev_dbg(ctx->dev, "Enter\n");
+       spin_lock_bh(&ctx->block_lock);
+       list_for_each_entry_safe(block, __block, &ctx->block_list, node) {
+               if (block == freed) {
+                       pr_debug("pvt_id freed --> %d\n", freed->drv_id);
+                       /* toggle the index position of pvt_id */
+                       list_del(&freed->node);
+                       spin_unlock_bh(&ctx->block_lock);
+                       kfree(freed->data);
+                       freed->data = NULL;
+                       kfree(freed);
+                       return 0;
+               }
+       }
+       spin_unlock_bh(&ctx->block_lock);
+       dev_err(ctx->dev, "block is already freed!!!\n");
+       return -EINVAL;
+}
+
+int sst_post_message_mrfld(struct intel_sst_drv *sst_drv_ctx,
+               struct ipc_post *ipc_msg, bool sync)
+{
+       struct ipc_post *msg = ipc_msg;
+       union ipc_header_mrfld header;
+       unsigned int loop_count = 0;
+       int retval = 0;
+       unsigned long irq_flags;
+
+       dev_dbg(sst_drv_ctx->dev, "Enter: sync: %d\n", sync);
+       spin_lock_irqsave(&sst_drv_ctx->ipc_spin_lock, irq_flags);
+       header.full = sst_shim_read64(sst_drv_ctx->shim, SST_IPCX);
+       if (sync) {
+               while (header.p.header_high.part.busy) {
+                       if (loop_count > 25) {
+                               dev_err(sst_drv_ctx->dev,
+                                       "sst: Busy wait failed, cant send this msg\n");
+                               retval = -EBUSY;
+                               goto out;
+                       }
+                       cpu_relax();
+                       loop_count++;
+                       header.full = sst_shim_read64(sst_drv_ctx->shim, SST_IPCX);
+               }
+       } else {
+               if (list_empty(&sst_drv_ctx->ipc_dispatch_list)) {
+                       /* queue is empty, nothing to send */
+                       spin_unlock_irqrestore(&sst_drv_ctx->ipc_spin_lock, irq_flags);
+                       dev_dbg(sst_drv_ctx->dev,
+                                       "Empty msg queue... NO Action\n");
+                       return 0;
+               }
+
+               if (header.p.header_high.part.busy) {
+                       spin_unlock_irqrestore(&sst_drv_ctx->ipc_spin_lock, irq_flags);
+                       dev_dbg(sst_drv_ctx->dev, "Busy not free... post later\n");
+                       return 0;
+               }
+
+               /* copy msg from list */
+               msg = list_entry(sst_drv_ctx->ipc_dispatch_list.next,
+                               struct ipc_post, node);
+               list_del(&msg->node);
+       }
+       dev_dbg(sst_drv_ctx->dev, "sst: Post message: header = %x\n",
+                               msg->mrfld_header.p.header_high.full);
+       dev_dbg(sst_drv_ctx->dev, "sst: size = 0x%x\n",
+                       msg->mrfld_header.p.header_low_payload);
+
+       if (msg->mrfld_header.p.header_high.part.large)
+               memcpy_toio(sst_drv_ctx->mailbox + SST_MAILBOX_SEND,
+                       msg->mailbox_data,
+                       msg->mrfld_header.p.header_low_payload);
+
+       sst_shim_write64(sst_drv_ctx->shim, SST_IPCX, msg->mrfld_header.full);
+
+out:
+       spin_unlock_irqrestore(&sst_drv_ctx->ipc_spin_lock, irq_flags);
+       kfree(msg->mailbox_data);
+       kfree(msg);
+       return retval;
+}
+
+void intel_sst_clear_intr_mrfld(struct intel_sst_drv *sst_drv_ctx)
+{
+       union interrupt_reg_mrfld isr;
+       union interrupt_reg_mrfld imr;
+       union ipc_header_mrfld clear_ipc;
+       unsigned long irq_flags;
+
+       spin_lock_irqsave(&sst_drv_ctx->ipc_spin_lock, irq_flags);
+       imr.full = sst_shim_read64(sst_drv_ctx->shim, SST_IMRX);
+       isr.full = sst_shim_read64(sst_drv_ctx->shim, SST_ISRX);
+
+       /* write 1 to clear*/
+       isr.part.busy_interrupt = 1;
+       sst_shim_write64(sst_drv_ctx->shim, SST_ISRX, isr.full);
+
+       /* Set IA done bit */
+       clear_ipc.full = sst_shim_read64(sst_drv_ctx->shim, SST_IPCD);
+
+       clear_ipc.p.header_high.part.busy = 0;
+       clear_ipc.p.header_high.part.done = 1;
+       clear_ipc.p.header_low_payload = IPC_ACK_SUCCESS;
+       sst_shim_write64(sst_drv_ctx->shim, SST_IPCD, clear_ipc.full);
+       /* un mask busy interrupt */
+       imr.part.busy_interrupt = 0;
+       sst_shim_write64(sst_drv_ctx->shim, SST_IMRX, imr.full);
+       spin_unlock_irqrestore(&sst_drv_ctx->ipc_spin_lock, irq_flags);
+}
+
+
+/*
+ * process_fw_init - process the FW init msg
+ *
+ * @msg: IPC message mailbox data from FW
+ *
+ * This function processes the FW init msg from FW
+ * marks FW state and prints debug info of loaded FW
+ */
+static void process_fw_init(struct intel_sst_drv *sst_drv_ctx,
+                       void *msg)
+{
+       struct ipc_header_fw_init *init =
+               (struct ipc_header_fw_init *)msg;
+       int retval = 0;
+
+       dev_dbg(sst_drv_ctx->dev, "*** FW Init msg came***\n");
+       if (init->result) {
+               sst_set_fw_state_locked(sst_drv_ctx, SST_RESET);
+               dev_err(sst_drv_ctx->dev, "FW Init failed, Error %x\n",
+                               init->result);
+               retval = init->result;
+               goto ret;
+       }
+
+ret:
+       sst_wake_up_block(sst_drv_ctx, retval, FW_DWNL_ID, 0 , NULL, 0);
+}
+
+static void process_fw_async_msg(struct intel_sst_drv *sst_drv_ctx,
+                       struct ipc_post *msg)
+{
+       u32 msg_id;
+       int str_id;
+       u32 data_size, i;
+       void *data_offset;
+       struct stream_info *stream;
+       union ipc_header_high msg_high;
+       u32 msg_low, pipe_id;
+
+       msg_high = msg->mrfld_header.p.header_high;
+       msg_low = msg->mrfld_header.p.header_low_payload;
+       msg_id = ((struct ipc_dsp_hdr *)msg->mailbox_data)->cmd_id;
+       data_offset = (msg->mailbox_data + sizeof(struct ipc_dsp_hdr));
+       data_size =  msg_low - (sizeof(struct ipc_dsp_hdr));
+
+       switch (msg_id) {
+       case IPC_SST_PERIOD_ELAPSED_MRFLD:
+               pipe_id = ((struct ipc_dsp_hdr *)msg->mailbox_data)->pipe_id;
+               str_id = get_stream_id_mrfld(sst_drv_ctx, pipe_id);
+               if (str_id > 0) {
+                       dev_dbg(sst_drv_ctx->dev,
+                               "Period elapsed rcvd for pipe id 0x%x\n",
+                               pipe_id);
+                       stream = &sst_drv_ctx->streams[str_id];
+                       if (stream->period_elapsed)
+                               stream->period_elapsed(stream->pcm_substream);
+                       if (stream->compr_cb)
+                               stream->compr_cb(stream->compr_cb_param);
+               }
+               break;
+
+       case IPC_IA_DRAIN_STREAM_MRFLD:
+               pipe_id = ((struct ipc_dsp_hdr *)msg->mailbox_data)->pipe_id;
+               str_id = get_stream_id_mrfld(sst_drv_ctx, pipe_id);
+               if (str_id > 0) {
+                       stream = &sst_drv_ctx->streams[str_id];
+                       if (stream->drain_notify)
+                               stream->drain_notify(stream->drain_cb_param);
+               }
+               break;
+
+       case IPC_IA_FW_ASYNC_ERR_MRFLD:
+               dev_err(sst_drv_ctx->dev, "FW sent async error msg:\n");
+               for (i = 0; i < (data_size/4); i++)
+                       print_hex_dump(KERN_DEBUG, NULL, DUMP_PREFIX_NONE,
+                                       16, 4, data_offset, data_size, false);
+               break;
+
+       case IPC_IA_FW_INIT_CMPLT_MRFLD:
+               process_fw_init(sst_drv_ctx, data_offset);
+               break;
+
+       case IPC_IA_BUF_UNDER_RUN_MRFLD:
+               pipe_id = ((struct ipc_dsp_hdr *)msg->mailbox_data)->pipe_id;
+               str_id = get_stream_id_mrfld(sst_drv_ctx, pipe_id);
+               if (str_id > 0)
+                       dev_err(sst_drv_ctx->dev,
+                               "Buffer under-run for pipe:%#x str_id:%d\n",
+                               pipe_id, str_id);
+               break;
+
+       default:
+               dev_err(sst_drv_ctx->dev,
+                       "Unrecognized async msg from FW msg_id %#x\n", msg_id);
+       }
+}
+
+void sst_process_reply_mrfld(struct intel_sst_drv *sst_drv_ctx,
+               struct ipc_post *msg)
+{
+       unsigned int drv_id;
+       void *data;
+       union ipc_header_high msg_high;
+       u32 msg_low;
+       struct ipc_dsp_hdr *dsp_hdr;
+       unsigned int cmd_id;
+
+       msg_high = msg->mrfld_header.p.header_high;
+       msg_low = msg->mrfld_header.p.header_low_payload;
+
+       dev_dbg(sst_drv_ctx->dev, "IPC process message header %x payload %x\n",
+                       msg->mrfld_header.p.header_high.full,
+                       msg->mrfld_header.p.header_low_payload);
+
+       drv_id = msg_high.part.drv_id;
+
+       /* Check for async messages first */
+       if (drv_id == SST_ASYNC_DRV_ID) {
+               /*FW sent async large message*/
+               process_fw_async_msg(sst_drv_ctx, msg);
+               return;
+       }
+
+       /* FW sent short error response for an IPC */
+       if (msg_high.part.result && drv_id && !msg_high.part.large) {
+               /* 32-bit FW error code in msg_low */
+               dev_err(sst_drv_ctx->dev, "FW sent error response 0x%x", msg_low);
+               sst_wake_up_block(sst_drv_ctx, msg_high.part.result,
+                       msg_high.part.drv_id,
+                       msg_high.part.msg_id, NULL, 0);
+               return;
+       }
+
+       /*
+        * Process all valid responses
+        * if it is a large message, the payload contains the size to
+        * copy from mailbox
+        **/
+       if (msg_high.part.large) {
+               data = kzalloc(msg_low, GFP_KERNEL);
+               if (!data)
+                       return;
+               memcpy(data, (void *) msg->mailbox_data, msg_low);
+               /* Copy command id so that we can use to put sst to reset */
+               dsp_hdr = (struct ipc_dsp_hdr *)data;
+               cmd_id = dsp_hdr->cmd_id;
+               dev_dbg(sst_drv_ctx->dev, "cmd_id %d\n", dsp_hdr->cmd_id);
+               if (sst_wake_up_block(sst_drv_ctx, msg_high.part.result,
+                               msg_high.part.drv_id,
+                               msg_high.part.msg_id, data, msg_low))
+                       kfree(data);
+       } else {
+               sst_wake_up_block(sst_drv_ctx, msg_high.part.result,
+                               msg_high.part.drv_id,
+                               msg_high.part.msg_id, NULL, 0);
+       }
+
+}
diff --git a/sound/soc/intel/sst/sst_loader.c b/sound/soc/intel/sst/sst_loader.c
new file mode 100644 (file)
index 0000000..b580f96
--- /dev/null
@@ -0,0 +1,456 @@
+/*
+ *  sst_dsp.c - Intel SST Driver for audio engine
+ *
+ *  Copyright (C) 2008-14      Intel Corp
+ *  Authors:   Vinod Koul <vinod.koul@intel.com>
+ *             Harsha Priya <priya.harsha@intel.com>
+ *             Dharageswari R <dharageswari.r@intel.com>
+ *             KP Jeeja <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 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.
+ *
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ *
+ *  This file contains all dsp controlling functions like firmware download,
+ * setting/resetting dsp cores, etc
+ */
+#include <linux/pci.h>
+#include <linux/delay.h>
+#include <linux/fs.h>
+#include <linux/sched.h>
+#include <linux/firmware.h>
+#include <linux/dmaengine.h>
+#include <linux/pm_runtime.h>
+#include <linux/pm_qos.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/soc.h>
+#include <sound/compress_driver.h>
+#include <asm/platform_sst_audio.h>
+#include "../sst-mfld-platform.h"
+#include "sst.h"
+#include "../sst-dsp.h"
+
+static inline void memcpy32_toio(void __iomem *dst, const void *src, int count)
+{
+       /* __iowrite32_copy uses 32-bit count values so divide by 4 for
+        * right count in words
+        */
+       __iowrite32_copy(dst, src, count/4);
+}
+
+/**
+ * intel_sst_reset_dsp_mrfld - Resetting SST DSP
+ *
+ * This resets DSP in case of MRFLD platfroms
+ */
+int intel_sst_reset_dsp_mrfld(struct intel_sst_drv *sst_drv_ctx)
+{
+       union config_status_reg_mrfld csr;
+
+       dev_dbg(sst_drv_ctx->dev, "sst: Resetting the DSP in mrfld\n");
+       csr.full = sst_shim_read64(sst_drv_ctx->shim, SST_CSR);
+
+       dev_dbg(sst_drv_ctx->dev, "value:0x%llx\n", csr.full);
+
+       csr.full |= 0x7;
+       sst_shim_write64(sst_drv_ctx->shim, SST_CSR, csr.full);
+       csr.full = sst_shim_read64(sst_drv_ctx->shim, SST_CSR);
+
+       dev_dbg(sst_drv_ctx->dev, "value:0x%llx\n", csr.full);
+
+       csr.full &= ~(0x1);
+       sst_shim_write64(sst_drv_ctx->shim, SST_CSR, csr.full);
+
+       csr.full = sst_shim_read64(sst_drv_ctx->shim, SST_CSR);
+       dev_dbg(sst_drv_ctx->dev, "value:0x%llx\n", csr.full);
+       return 0;
+}
+
+/**
+ * sst_start_merrifield - Start the SST DSP processor
+ *
+ * This starts the DSP in MERRIFIELD platfroms
+ */
+int sst_start_mrfld(struct intel_sst_drv *sst_drv_ctx)
+{
+       union config_status_reg_mrfld csr;
+
+       dev_dbg(sst_drv_ctx->dev, "sst: Starting the DSP in mrfld LALALALA\n");
+       csr.full = sst_shim_read64(sst_drv_ctx->shim, SST_CSR);
+       dev_dbg(sst_drv_ctx->dev, "value:0x%llx\n", csr.full);
+
+       csr.full |= 0x7;
+       sst_shim_write64(sst_drv_ctx->shim, SST_CSR, csr.full);
+
+       csr.full = sst_shim_read64(sst_drv_ctx->shim, SST_CSR);
+       dev_dbg(sst_drv_ctx->dev, "value:0x%llx\n", csr.full);
+
+       csr.part.xt_snoop = 1;
+       csr.full &= ~(0x5);
+       sst_shim_write64(sst_drv_ctx->shim, SST_CSR, csr.full);
+
+       csr.full = sst_shim_read64(sst_drv_ctx->shim, SST_CSR);
+       dev_dbg(sst_drv_ctx->dev, "sst: Starting the DSP_merrifield:%llx\n",
+                       csr.full);
+       return 0;
+}
+
+static int sst_validate_fw_image(struct intel_sst_drv *ctx, unsigned long size,
+               struct fw_module_header **module, u32 *num_modules)
+{
+       struct sst_fw_header *header;
+       const void *sst_fw_in_mem = ctx->fw_in_mem;
+
+       dev_dbg(ctx->dev, "Enter\n");
+
+       /* Read the header information from the data pointer */
+       header = (struct sst_fw_header *)sst_fw_in_mem;
+       dev_dbg(ctx->dev,
+               "header sign=%s size=%x modules=%x fmt=%x size=%zx\n",
+               header->signature, header->file_size, header->modules,
+               header->file_format, sizeof(*header));
+
+       /* verify FW */
+       if ((strncmp(header->signature, SST_FW_SIGN, 4) != 0) ||
+               (size != header->file_size + sizeof(*header))) {
+               /* Invalid FW signature */
+               dev_err(ctx->dev, "InvalidFW sign/filesize mismatch\n");
+               return -EINVAL;
+       }
+       *num_modules = header->modules;
+       *module = (void *)sst_fw_in_mem + sizeof(*header);
+
+       return 0;
+}
+
+/*
+ * sst_fill_memcpy_list - Fill the memcpy list
+ *
+ * @memcpy_list: List to be filled
+ * @destn: Destination addr to be filled in the list
+ * @src: Source addr to be filled in the list
+ * @size: Size to be filled in the list
+ *
+ * Adds the node to the list after required fields
+ * are populated in the node
+ */
+static int sst_fill_memcpy_list(struct list_head *memcpy_list,
+                       void *destn, const void *src, u32 size, bool is_io)
+{
+       struct sst_memcpy_list *listnode;
+
+       listnode = kzalloc(sizeof(*listnode), GFP_KERNEL);
+       if (listnode == NULL)
+               return -ENOMEM;
+       listnode->dstn = destn;
+       listnode->src = src;
+       listnode->size = size;
+       listnode->is_io = is_io;
+       list_add_tail(&listnode->memcpylist, memcpy_list);
+
+       return 0;
+}
+
+/**
+ * sst_parse_module_memcpy - Parse audio FW modules and populate the memcpy list
+ *
+ * @sst_drv_ctx                : driver context
+ * @module             : FW module header
+ * @memcpy_list        : Pointer to the list to be populated
+ * Create the memcpy list as the number of block to be copied
+ * returns error or 0 if module sizes are proper
+ */
+static int sst_parse_module_memcpy(struct intel_sst_drv *sst_drv_ctx,
+               struct fw_module_header *module, struct list_head *memcpy_list)
+{
+       struct fw_block_info *block;
+       u32 count;
+       int ret_val = 0;
+       void __iomem *ram_iomem;
+
+       dev_dbg(sst_drv_ctx->dev, "module sign %s size %x blocks %x type %x\n",
+                       module->signature, module->mod_size,
+                       module->blocks, module->type);
+       dev_dbg(sst_drv_ctx->dev, "module entrypoint 0x%x\n", module->entry_point);
+
+       block = (void *)module + sizeof(*module);
+
+       for (count = 0; count < module->blocks; count++) {
+               if (block->size <= 0) {
+                       dev_err(sst_drv_ctx->dev, "block size invalid\n");
+                       return -EINVAL;
+               }
+               switch (block->type) {
+               case SST_IRAM:
+                       ram_iomem = sst_drv_ctx->iram;
+                       break;
+               case SST_DRAM:
+                       ram_iomem = sst_drv_ctx->dram;
+                       break;
+               case SST_DDR:
+                       ram_iomem = sst_drv_ctx->ddr;
+                       break;
+               case SST_CUSTOM_INFO:
+                       block = (void *)block + sizeof(*block) + block->size;
+                       continue;
+               default:
+                       dev_err(sst_drv_ctx->dev, "wrong ram type0x%x in block0x%x\n",
+                                       block->type, count);
+                       return -EINVAL;
+               }
+
+               ret_val = sst_fill_memcpy_list(memcpy_list,
+                               ram_iomem + block->ram_offset,
+                               (void *)block + sizeof(*block), block->size, 1);
+               if (ret_val)
+                       return ret_val;
+
+               block = (void *)block + sizeof(*block) + block->size;
+       }
+       return 0;
+}
+
+/**
+ * sst_parse_fw_memcpy - parse the firmware image & populate the list for memcpy
+ *
+ * @ctx                        : pointer to drv context
+ * @size               : size of the firmware
+ * @fw_list            : pointer to list_head to be populated
+ * This function parses the FW image and saves the parsed image in the list
+ * for memcpy
+ */
+static int sst_parse_fw_memcpy(struct intel_sst_drv *ctx, unsigned long size,
+                               struct list_head *fw_list)
+{
+       struct fw_module_header *module;
+       u32 count, num_modules;
+       int ret_val;
+
+       ret_val = sst_validate_fw_image(ctx, size, &module, &num_modules);
+       if (ret_val)
+               return ret_val;
+
+       for (count = 0; count < num_modules; count++) {
+               ret_val = sst_parse_module_memcpy(ctx, module, fw_list);
+               if (ret_val)
+                       return ret_val;
+               module = (void *)module + sizeof(*module) + module->mod_size;
+       }
+
+       return 0;
+}
+
+/**
+ * sst_do_memcpy - function initiates the memcpy
+ *
+ * @memcpy_list: Pter to memcpy list on which the memcpy needs to be initiated
+ *
+ * Triggers the memcpy
+ */
+static void sst_do_memcpy(struct list_head *memcpy_list)
+{
+       struct sst_memcpy_list *listnode;
+
+       list_for_each_entry(listnode, memcpy_list, memcpylist) {
+               if (listnode->is_io == true)
+                       memcpy32_toio((void __iomem *)listnode->dstn,
+                                       listnode->src, listnode->size);
+               else
+                       memcpy(listnode->dstn, listnode->src, listnode->size);
+       }
+}
+
+void sst_memcpy_free_resources(struct intel_sst_drv *sst_drv_ctx)
+{
+       struct sst_memcpy_list *listnode, *tmplistnode;
+
+       /* Free the list */
+       if (!list_empty(&sst_drv_ctx->memcpy_list)) {
+               list_for_each_entry_safe(listnode, tmplistnode,
+                               &sst_drv_ctx->memcpy_list, memcpylist) {
+                       list_del(&listnode->memcpylist);
+                       kfree(listnode);
+               }
+       }
+}
+
+static int sst_cache_and_parse_fw(struct intel_sst_drv *sst,
+               const struct firmware *fw)
+{
+       int retval = 0;
+
+       sst->fw_in_mem = kzalloc(fw->size, GFP_KERNEL);
+       if (!sst->fw_in_mem) {
+               retval = -ENOMEM;
+               goto end_release;
+       }
+       dev_dbg(sst->dev, "copied fw to %p", sst->fw_in_mem);
+       dev_dbg(sst->dev, "phys: %lx", (unsigned long)virt_to_phys(sst->fw_in_mem));
+       memcpy(sst->fw_in_mem, fw->data, fw->size);
+       retval = sst_parse_fw_memcpy(sst, fw->size, &sst->memcpy_list);
+       if (retval) {
+               dev_err(sst->dev, "Failed to parse fw\n");
+               kfree(sst->fw_in_mem);
+               sst->fw_in_mem = NULL;
+       }
+
+end_release:
+       release_firmware(fw);
+       return retval;
+
+}
+
+void sst_firmware_load_cb(const struct firmware *fw, void *context)
+{
+       struct intel_sst_drv *ctx = context;
+
+       dev_dbg(ctx->dev, "Enter\n");
+
+       if (fw == NULL) {
+               dev_err(ctx->dev, "request fw failed\n");
+               return;
+       }
+
+       mutex_lock(&ctx->sst_lock);
+
+       if (ctx->sst_state != SST_RESET ||
+                       ctx->fw_in_mem != NULL) {
+               if (fw != NULL)
+                       release_firmware(fw);
+               mutex_unlock(&ctx->sst_lock);
+               return;
+       }
+
+       dev_dbg(ctx->dev, "Request Fw completed\n");
+       sst_cache_and_parse_fw(ctx, fw);
+       mutex_unlock(&ctx->sst_lock);
+}
+
+/*
+ * sst_request_fw - requests audio fw from kernel and saves a copy
+ *
+ * This function requests the SST FW from the kernel, parses it and
+ * saves a copy in the driver context
+ */
+static int sst_request_fw(struct intel_sst_drv *sst)
+{
+       int retval = 0;
+       const struct firmware *fw;
+
+       retval = request_firmware(&fw, sst->firmware_name, sst->dev);
+       if (fw == NULL) {
+               dev_err(sst->dev, "fw is returning as null\n");
+               return -EINVAL;
+       }
+       if (retval) {
+               dev_err(sst->dev, "request fw failed %d\n", retval);
+               return retval;
+       }
+       mutex_lock(&sst->sst_lock);
+       retval = sst_cache_and_parse_fw(sst, fw);
+       mutex_unlock(&sst->sst_lock);
+
+       return retval;
+}
+
+/*
+ * Writing the DDR physical base to DCCM offset
+ * so that FW can use it to setup TLB
+ */
+static void sst_dccm_config_write(void __iomem *dram_base,
+               unsigned int ddr_base)
+{
+       void __iomem *addr;
+       u32 bss_reset = 0;
+
+       addr = (void __iomem *)(dram_base + MRFLD_FW_DDR_BASE_OFFSET);
+       memcpy32_toio(addr, (void *)&ddr_base, sizeof(u32));
+       bss_reset |= (1 << MRFLD_FW_BSS_RESET_BIT);
+       addr = (void __iomem *)(dram_base + MRFLD_FW_FEATURE_BASE_OFFSET);
+       memcpy32_toio(addr, &bss_reset, sizeof(u32));
+
+}
+
+void sst_post_download_mrfld(struct intel_sst_drv *ctx)
+{
+       sst_dccm_config_write(ctx->dram, ctx->ddr_base);
+       dev_dbg(ctx->dev, "config written to DCCM\n");
+}
+
+/**
+ * sst_load_fw - function to load FW into DSP
+ * Transfers the FW to DSP using dma/memcpy
+ */
+int sst_load_fw(struct intel_sst_drv *sst_drv_ctx)
+{
+       int ret_val = 0;
+       struct sst_block *block;
+
+       dev_dbg(sst_drv_ctx->dev, "sst_load_fw\n");
+
+       if (sst_drv_ctx->sst_state !=  SST_RESET ||
+                       sst_drv_ctx->sst_state == SST_SHUTDOWN)
+               return -EAGAIN;
+
+       if (!sst_drv_ctx->fw_in_mem) {
+               dev_dbg(sst_drv_ctx->dev, "sst: FW not in memory retry to download\n");
+               ret_val = sst_request_fw(sst_drv_ctx);
+               if (ret_val)
+                       return ret_val;
+       }
+
+       BUG_ON(!sst_drv_ctx->fw_in_mem);
+       block = sst_create_block(sst_drv_ctx, 0, FW_DWNL_ID);
+       if (block == NULL)
+               return -ENOMEM;
+
+       /* Prevent C-states beyond C6 */
+       pm_qos_update_request(sst_drv_ctx->qos, 0);
+
+       sst_drv_ctx->sst_state = SST_FW_LOADING;
+
+       ret_val = sst_drv_ctx->ops->reset(sst_drv_ctx);
+       if (ret_val)
+               goto restore;
+
+       sst_do_memcpy(&sst_drv_ctx->memcpy_list);
+
+       /* Write the DRAM/DCCM config before enabling FW */
+       if (sst_drv_ctx->ops->post_download)
+               sst_drv_ctx->ops->post_download(sst_drv_ctx);
+
+       /* bring sst out of reset */
+       ret_val = sst_drv_ctx->ops->start(sst_drv_ctx);
+       if (ret_val)
+               goto restore;
+
+       ret_val = sst_wait_timeout(sst_drv_ctx, block);
+       if (ret_val) {
+               dev_err(sst_drv_ctx->dev, "fw download failed %d\n" , ret_val);
+               /* FW download failed due to timeout */
+               ret_val = -EBUSY;
+
+       }
+
+
+restore:
+       /* Re-enable Deeper C-states beyond C6 */
+       pm_qos_update_request(sst_drv_ctx->qos, PM_QOS_DEFAULT_VALUE);
+       sst_free_block(sst_drv_ctx, block);
+       dev_dbg(sst_drv_ctx->dev, "fw load successful!!!\n");
+
+       if (sst_drv_ctx->ops->restore_dsp_context)
+               sst_drv_ctx->ops->restore_dsp_context();
+       sst_drv_ctx->sst_state = SST_FW_RUNNING;
+       return ret_val;
+}
+
diff --git a/sound/soc/intel/sst/sst_pci.c b/sound/soc/intel/sst/sst_pci.c
new file mode 100644 (file)
index 0000000..3a0b3bf
--- /dev/null
@@ -0,0 +1,209 @@
+/*
+ *  sst_pci.c - SST (LPE) driver init file for pci enumeration.
+ *
+ *  Copyright (C) 2008-14      Intel Corp
+ *  Authors:   Vinod Koul <vinod.koul@intel.com>
+ *             Harsha Priya <priya.harsha@intel.com>
+ *             Dharageswari R <dharageswari.r@intel.com>
+ *             KP Jeeja <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 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 <linux/module.h>
+#include <linux/pci.h>
+#include <linux/fs.h>
+#include <linux/firmware.h>
+#include <linux/pm_runtime.h>
+#include <sound/core.h>
+#include <sound/soc.h>
+#include <asm/platform_sst_audio.h>
+#include "../sst-mfld-platform.h"
+#include "sst.h"
+
+static int sst_platform_get_resources(struct intel_sst_drv *ctx)
+{
+       int ddr_base, ret = 0;
+       struct pci_dev *pci = ctx->pci;
+
+       ret = pci_request_regions(pci, SST_DRV_NAME);
+       if (ret)
+               return ret;
+
+       /* map registers */
+       /* DDR base */
+       if (ctx->dev_id == SST_MRFLD_PCI_ID) {
+               ctx->ddr_base = pci_resource_start(pci, 0);
+               /* check that the relocated IMR base matches with FW Binary */
+               ddr_base = relocate_imr_addr_mrfld(ctx->ddr_base);
+               if (!ctx->pdata->lib_info) {
+                       dev_err(ctx->dev, "lib_info pointer NULL\n");
+                       ret = -EINVAL;
+                       goto do_release_regions;
+               }
+               if (ddr_base != ctx->pdata->lib_info->mod_base) {
+                       dev_err(ctx->dev,
+                                       "FW LSP DDR BASE does not match with IFWI\n");
+                       ret = -EINVAL;
+                       goto do_release_regions;
+               }
+               ctx->ddr_end = pci_resource_end(pci, 0);
+
+               ctx->ddr = pcim_iomap(pci, 0,
+                                       pci_resource_len(pci, 0));
+               if (!ctx->ddr) {
+                       ret = -EINVAL;
+                       goto do_release_regions;
+               }
+               dev_dbg(ctx->dev, "sst: DDR Ptr %p\n", ctx->ddr);
+       } else {
+               ctx->ddr = NULL;
+       }
+       /* SHIM */
+       ctx->shim_phy_add = pci_resource_start(pci, 1);
+       ctx->shim = pcim_iomap(pci, 1, pci_resource_len(pci, 1));
+       if (!ctx->shim) {
+               ret = -EINVAL;
+               goto do_release_regions;
+       }
+       dev_dbg(ctx->dev, "SST Shim Ptr %p\n", ctx->shim);
+
+       /* Shared SRAM */
+       ctx->mailbox_add = pci_resource_start(pci, 2);
+       ctx->mailbox = pcim_iomap(pci, 2, pci_resource_len(pci, 2));
+       if (!ctx->mailbox) {
+               ret = -EINVAL;
+               goto do_release_regions;
+       }
+       dev_dbg(ctx->dev, "SRAM Ptr %p\n", ctx->mailbox);
+
+       /* IRAM */
+       ctx->iram_end = pci_resource_end(pci, 3);
+       ctx->iram_base = pci_resource_start(pci, 3);
+       ctx->iram = pcim_iomap(pci, 3, pci_resource_len(pci, 3));
+       if (!ctx->iram) {
+               ret = -EINVAL;
+               goto do_release_regions;
+       }
+       dev_dbg(ctx->dev, "IRAM Ptr %p\n", ctx->iram);
+
+       /* DRAM */
+       ctx->dram_end = pci_resource_end(pci, 4);
+       ctx->dram_base = pci_resource_start(pci, 4);
+       ctx->dram = pcim_iomap(pci, 4, pci_resource_len(pci, 4));
+       if (!ctx->dram) {
+               ret = -EINVAL;
+               goto do_release_regions;
+       }
+       dev_dbg(ctx->dev, "DRAM Ptr %p\n", ctx->dram);
+do_release_regions:
+       pci_release_regions(pci);
+       return 0;
+}
+
+/*
+ * intel_sst_probe - PCI probe function
+ *
+ * @pci:       PCI device structure
+ * @pci_id: PCI device ID structure
+ *
+ */
+static int intel_sst_probe(struct pci_dev *pci,
+                       const struct pci_device_id *pci_id)
+{
+       int ret = 0;
+       struct intel_sst_drv *sst_drv_ctx;
+       struct sst_platform_info *sst_pdata = pci->dev.platform_data;
+
+       dev_dbg(&pci->dev, "Probe for DID %x\n", pci->device);
+       ret = sst_alloc_drv_context(&sst_drv_ctx, &pci->dev, pci->device);
+       if (ret < 0)
+               return ret;
+
+       sst_drv_ctx->pdata = sst_pdata;
+       sst_drv_ctx->irq_num = pci->irq;
+       snprintf(sst_drv_ctx->firmware_name, sizeof(sst_drv_ctx->firmware_name),
+                       "%s%04x%s", "fw_sst_",
+                       sst_drv_ctx->dev_id, ".bin");
+
+       ret = sst_context_init(sst_drv_ctx);
+       if (ret < 0)
+               return ret;
+
+       /* Init the device */
+       ret = pcim_enable_device(pci);
+       if (ret) {
+               dev_err(sst_drv_ctx->dev,
+                       "device can't be enabled. Returned err: %d\n", ret);
+               goto do_free_drv_ctx;
+       }
+       sst_drv_ctx->pci = pci_dev_get(pci);
+       ret = sst_platform_get_resources(sst_drv_ctx);
+       if (ret < 0)
+               goto do_free_drv_ctx;
+
+       pci_set_drvdata(pci, sst_drv_ctx);
+       sst_configure_runtime_pm(sst_drv_ctx);
+
+       return ret;
+
+do_free_drv_ctx:
+       sst_context_cleanup(sst_drv_ctx);
+       dev_err(sst_drv_ctx->dev, "Probe failed with %d\n", ret);
+       return ret;
+}
+
+/**
+ * intel_sst_remove - PCI remove function
+ *
+ * @pci:       PCI device structure
+ *
+ * This function is called by OS when a device is unloaded
+ * This frees the interrupt etc
+ */
+static void intel_sst_remove(struct pci_dev *pci)
+{
+       struct intel_sst_drv *sst_drv_ctx = pci_get_drvdata(pci);
+
+       sst_context_cleanup(sst_drv_ctx);
+       pci_dev_put(sst_drv_ctx->pci);
+       pci_release_regions(pci);
+       pci_set_drvdata(pci, NULL);
+}
+
+/* PCI Routines */
+static struct pci_device_id intel_sst_ids[] = {
+       { PCI_VDEVICE(INTEL, SST_MRFLD_PCI_ID), 0},
+       { 0, }
+};
+
+static struct pci_driver sst_driver = {
+       .name = SST_DRV_NAME,
+       .id_table = intel_sst_ids,
+       .probe = intel_sst_probe,
+       .remove = intel_sst_remove,
+#ifdef CONFIG_PM
+       .driver = {
+               .pm = &intel_sst_pm,
+       },
+#endif
+};
+
+module_pci_driver(sst_driver);
+
+MODULE_DESCRIPTION("Intel (R) SST(R) Audio Engine PCI Driver");
+MODULE_AUTHOR("Vinod Koul <vinod.koul@intel.com>");
+MODULE_AUTHOR("Harsha Priya <priya.harsha@intel.com>");
+MODULE_AUTHOR("Dharageswari R <dharageswari.r@intel.com>");
+MODULE_AUTHOR("KP Jeeja <jeeja.kp@intel.com>");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("sst");
diff --git a/sound/soc/intel/sst/sst_pvt.c b/sound/soc/intel/sst/sst_pvt.c
new file mode 100644 (file)
index 0000000..4b77208
--- /dev/null
@@ -0,0 +1,449 @@
+/*
+ *  sst_pvt.c - Intel SST Driver for audio engine
+ *
+ *  Copyright (C) 2008-14      Intel Corp
+ *  Authors:   Vinod Koul <vinod.koul@intel.com>
+ *             Harsha Priya <priya.harsha@intel.com>
+ *             Dharageswari R <dharageswari.r@intel.com>
+ *             KP Jeeja <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 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 <linux/kobject.h>
+#include <linux/pci.h>
+#include <linux/fs.h>
+#include <linux/firmware.h>
+#include <linux/pm_runtime.h>
+#include <linux/sched.h>
+#include <linux/delay.h>
+#include <sound/asound.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/soc.h>
+#include <sound/compress_driver.h>
+#include <asm/platform_sst_audio.h>
+#include "../sst-mfld-platform.h"
+#include "sst.h"
+#include "../sst-dsp.h"
+
+int sst_shim_write(void __iomem *addr, int offset, int value)
+{
+       writel(value, addr + offset);
+       return 0;
+}
+
+u32 sst_shim_read(void __iomem *addr, int offset)
+{
+       return readl(addr + offset);
+}
+
+u64 sst_reg_read64(void __iomem *addr, int offset)
+{
+       u64 val = 0;
+
+       memcpy_fromio(&val, addr + offset, sizeof(val));
+
+       return val;
+}
+
+int sst_shim_write64(void __iomem *addr, int offset, u64 value)
+{
+       memcpy_toio(addr + offset, &value, sizeof(value));
+       return 0;
+}
+
+u64 sst_shim_read64(void __iomem *addr, int offset)
+{
+       u64 val = 0;
+
+       memcpy_fromio(&val, addr + offset, sizeof(val));
+       return val;
+}
+
+void sst_set_fw_state_locked(
+               struct intel_sst_drv *sst_drv_ctx, int sst_state)
+{
+       mutex_lock(&sst_drv_ctx->sst_lock);
+       sst_drv_ctx->sst_state = sst_state;
+       mutex_unlock(&sst_drv_ctx->sst_lock);
+}
+
+/*
+ * sst_wait_interruptible - wait on event
+ *
+ * @sst_drv_ctx: Driver context
+ * @block: Driver block to wait on
+ *
+ * This function waits without a timeout (and is interruptable) for a
+ * given block event
+ */
+int sst_wait_interruptible(struct intel_sst_drv *sst_drv_ctx,
+                               struct sst_block *block)
+{
+       int retval = 0;
+
+       if (!wait_event_interruptible(sst_drv_ctx->wait_queue,
+                               block->condition)) {
+               /* event wake */
+               if (block->ret_code < 0) {
+                       dev_err(sst_drv_ctx->dev,
+                               "stream failed %d\n", block->ret_code);
+                       retval = -EBUSY;
+               } else {
+                       dev_dbg(sst_drv_ctx->dev, "event up\n");
+                       retval = 0;
+               }
+       } else {
+               dev_err(sst_drv_ctx->dev, "signal interrupted\n");
+               retval = -EINTR;
+       }
+       return retval;
+
+}
+
+unsigned long long read_shim_data(struct intel_sst_drv *sst, int addr)
+{
+       unsigned long long val = 0;
+
+       switch (sst->dev_id) {
+       case SST_MRFLD_PCI_ID:
+       case SST_BYT_ACPI_ID:
+               val = sst_shim_read64(sst->shim, addr);
+               break;
+       }
+       return val;
+}
+
+void write_shim_data(struct intel_sst_drv *sst, int addr,
+                               unsigned long long data)
+{
+       switch (sst->dev_id) {
+       case SST_MRFLD_PCI_ID:
+       case SST_BYT_ACPI_ID:
+               sst_shim_write64(sst->shim, addr, (u64) data);
+               break;
+       }
+}
+
+/*
+ * sst_wait_timeout - wait on event for timeout
+ *
+ * @sst_drv_ctx: Driver context
+ * @block: Driver block to wait on
+ *
+ * This function waits with a timeout value (and is not interruptible) on a
+ * given block event
+ */
+int sst_wait_timeout(struct intel_sst_drv *sst_drv_ctx, struct sst_block *block)
+{
+       int retval = 0;
+
+       /*
+        * NOTE:
+        * Observed that FW processes the alloc msg and replies even
+        * before the alloc thread has finished execution
+        */
+       dev_dbg(sst_drv_ctx->dev,
+               "waiting for condition %x ipc %d drv_id %d\n",
+               block->condition, block->msg_id, block->drv_id);
+       if (wait_event_timeout(sst_drv_ctx->wait_queue,
+                               block->condition,
+                               msecs_to_jiffies(SST_BLOCK_TIMEOUT))) {
+               /* event wake */
+               dev_dbg(sst_drv_ctx->dev, "Event wake %x\n",
+                               block->condition);
+               dev_dbg(sst_drv_ctx->dev, "message ret: %d\n",
+                               block->ret_code);
+               retval = -block->ret_code;
+       } else {
+               block->on = false;
+               dev_err(sst_drv_ctx->dev,
+                       "Wait timed-out condition:%#x, msg_id:%#x fw_state %#x\n",
+                       block->condition, block->msg_id, sst_drv_ctx->sst_state);
+               sst_drv_ctx->sst_state = SST_RESET;
+
+               retval = -EBUSY;
+       }
+       return retval;
+}
+
+/*
+ * sst_create_ipc_msg - create a IPC message
+ *
+ * @arg: ipc message
+ * @large: large or short message
+ *
+ * this function allocates structures to send a large or short
+ * message to the firmware
+ */
+int sst_create_ipc_msg(struct ipc_post **arg, bool large)
+{
+       struct ipc_post *msg;
+
+       msg = kzalloc(sizeof(struct ipc_post), GFP_ATOMIC);
+       if (!msg)
+               return -ENOMEM;
+       if (large) {
+               msg->mailbox_data = kzalloc(SST_MAILBOX_SIZE, GFP_ATOMIC);
+               if (!msg->mailbox_data) {
+                       kfree(msg);
+                       return -ENOMEM;
+               }
+       } else {
+               msg->mailbox_data = NULL;
+       }
+       msg->is_large = large;
+       *arg = msg;
+       return 0;
+}
+
+/*
+ * sst_create_block_and_ipc_msg - Creates IPC message and sst block
+ * @arg: passed to sst_create_ipc_message API
+ * @large: large or short message
+ * @sst_drv_ctx: sst driver context
+ * @block: return block allocated
+ * @msg_id: IPC
+ * @drv_id: stream id or private id
+ */
+int sst_create_block_and_ipc_msg(struct ipc_post **arg, bool large,
+               struct intel_sst_drv *sst_drv_ctx, struct sst_block **block,
+               u32 msg_id, u32 drv_id)
+{
+       int retval = 0;
+
+       retval = sst_create_ipc_msg(arg, large);
+       if (retval)
+               return retval;
+       *block = sst_create_block(sst_drv_ctx, msg_id, drv_id);
+       if (*block == NULL) {
+               kfree(*arg);
+               return -ENOMEM;
+       }
+       return retval;
+}
+
+/*
+ * sst_clean_stream - clean the stream context
+ *
+ * @stream: stream structure
+ *
+ * this function resets the stream contexts
+ * should be called in free
+ */
+void sst_clean_stream(struct stream_info *stream)
+{
+       stream->status = STREAM_UN_INIT;
+       stream->prev = STREAM_UN_INIT;
+       mutex_lock(&stream->lock);
+       stream->cumm_bytes = 0;
+       mutex_unlock(&stream->lock);
+}
+
+int sst_prepare_and_post_msg(struct intel_sst_drv *sst,
+               int task_id, int ipc_msg, int cmd_id, int pipe_id,
+               size_t mbox_data_len, const void *mbox_data, void **data,
+               bool large, bool fill_dsp, bool sync, bool response)
+{
+       struct ipc_post *msg = NULL;
+       struct ipc_dsp_hdr dsp_hdr;
+       struct sst_block *block;
+       int ret = 0, pvt_id;
+
+       pvt_id = sst_assign_pvt_id(sst);
+       if (pvt_id < 0)
+               return pvt_id;
+
+       if (response)
+               ret = sst_create_block_and_ipc_msg(
+                               &msg, large, sst, &block, ipc_msg, pvt_id);
+       else
+               ret = sst_create_ipc_msg(&msg, large);
+
+       if (ret < 0) {
+               test_and_clear_bit(pvt_id, &sst->pvt_id);
+               return -ENOMEM;
+       }
+
+       dev_dbg(sst->dev, "pvt_id = %d, pipe id = %d, task = %d ipc_msg: %d\n",
+                pvt_id, pipe_id, task_id, ipc_msg);
+       sst_fill_header_mrfld(&msg->mrfld_header, ipc_msg,
+                                       task_id, large, pvt_id);
+       msg->mrfld_header.p.header_low_payload = sizeof(dsp_hdr) + mbox_data_len;
+       msg->mrfld_header.p.header_high.part.res_rqd = !sync;
+       dev_dbg(sst->dev, "header:%x\n",
+                       msg->mrfld_header.p.header_high.full);
+       dev_dbg(sst->dev, "response rqd: %x",
+                       msg->mrfld_header.p.header_high.part.res_rqd);
+       dev_dbg(sst->dev, "msg->mrfld_header.p.header_low_payload:%d",
+                       msg->mrfld_header.p.header_low_payload);
+       if (fill_dsp) {
+               sst_fill_header_dsp(&dsp_hdr, cmd_id, pipe_id, mbox_data_len);
+               memcpy(msg->mailbox_data, &dsp_hdr, sizeof(dsp_hdr));
+               if (mbox_data_len) {
+                       memcpy(msg->mailbox_data + sizeof(dsp_hdr),
+                                       mbox_data, mbox_data_len);
+               }
+       }
+
+       if (sync)
+               sst->ops->post_message(sst, msg, true);
+       else
+               sst_add_to_dispatch_list_and_post(sst, msg);
+
+       if (response) {
+               ret = sst_wait_timeout(sst, block);
+               if (ret < 0) {
+                       goto out;
+               } else if(block->data) {
+                       if (!data)
+                               goto out;
+                       *data = kzalloc(block->size, GFP_KERNEL);
+                       if (!(*data)) {
+                               ret = -ENOMEM;
+                               goto out;
+                       } else
+                               memcpy(data, (void *) block->data, block->size);
+               }
+       }
+out:
+       if (response)
+               sst_free_block(sst, block);
+       test_and_clear_bit(pvt_id, &sst->pvt_id);
+       return ret;
+}
+
+int sst_pm_runtime_put(struct intel_sst_drv *sst_drv)
+{
+       int ret;
+
+       pm_runtime_mark_last_busy(sst_drv->dev);
+       ret = pm_runtime_put_autosuspend(sst_drv->dev);
+       if (ret < 0)
+               return ret;
+       return 0;
+}
+
+void sst_fill_header_mrfld(union ipc_header_mrfld *header,
+                               int msg, int task_id, int large, int drv_id)
+{
+       header->full = 0;
+       header->p.header_high.part.msg_id = msg;
+       header->p.header_high.part.task_id = task_id;
+       header->p.header_high.part.large = large;
+       header->p.header_high.part.drv_id = drv_id;
+       header->p.header_high.part.done = 0;
+       header->p.header_high.part.busy = 1;
+       header->p.header_high.part.res_rqd = 1;
+}
+
+void sst_fill_header_dsp(struct ipc_dsp_hdr *dsp, int msg,
+                                       int pipe_id, int len)
+{
+       dsp->cmd_id = msg;
+       dsp->mod_index_id = 0xff;
+       dsp->pipe_id = pipe_id;
+       dsp->length = len;
+       dsp->mod_id = 0;
+}
+
+#define SST_MAX_BLOCKS 15
+/*
+ * sst_assign_pvt_id - assign a pvt id for stream
+ *
+ * @sst_drv_ctx : driver context
+ *
+ * this function assigns a private id for calls that dont have stream
+ * context yet, should be called with lock held
+ * uses bits for the id, and finds first free bits and assigns that
+ */
+int sst_assign_pvt_id(struct intel_sst_drv *drv)
+{
+       int local;
+
+       spin_lock(&drv->block_lock);
+       /* find first zero index from lsb */
+       local = ffz(drv->pvt_id);
+       dev_dbg(drv->dev, "pvt_id assigned --> %d\n", local);
+       if (local >= SST_MAX_BLOCKS){
+               spin_unlock(&drv->block_lock);
+               dev_err(drv->dev, "PVT _ID error: no free id blocks ");
+               return -EINVAL;
+       }
+       /* toggle the index */
+       change_bit(local, &drv->pvt_id);
+       spin_unlock(&drv->block_lock);
+       return local;
+}
+
+void sst_init_stream(struct stream_info *stream,
+               int codec, int sst_id, int ops, u8 slot)
+{
+       stream->status = STREAM_INIT;
+       stream->prev = STREAM_UN_INIT;
+       stream->ops = ops;
+}
+
+int sst_validate_strid(
+               struct intel_sst_drv *sst_drv_ctx, int str_id)
+{
+       if (str_id <= 0 || str_id > sst_drv_ctx->info.max_streams) {
+               dev_err(sst_drv_ctx->dev,
+                       "SST ERR: invalid stream id : %d, max %d\n",
+                       str_id, sst_drv_ctx->info.max_streams);
+               return -EINVAL;
+       }
+
+       return 0;
+}
+
+struct stream_info *get_stream_info(
+               struct intel_sst_drv *sst_drv_ctx, int str_id)
+{
+       if (sst_validate_strid(sst_drv_ctx, str_id))
+               return NULL;
+       return &sst_drv_ctx->streams[str_id];
+}
+
+int get_stream_id_mrfld(struct intel_sst_drv *sst_drv_ctx,
+               u32 pipe_id)
+{
+       int i;
+
+       for (i = 1; i <= sst_drv_ctx->info.max_streams; i++)
+               if (pipe_id == sst_drv_ctx->streams[i].pipe_id)
+                       return i;
+
+       dev_dbg(sst_drv_ctx->dev, "no such pipe_id(%u)", pipe_id);
+       return -1;
+}
+
+u32 relocate_imr_addr_mrfld(u32 base_addr)
+{
+       /* Get the difference from 512MB aligned base addr */
+       /* relocate the base */
+       base_addr = MRFLD_FW_VIRTUAL_BASE + (base_addr % (512 * 1024 * 1024));
+       return base_addr;
+}
+EXPORT_SYMBOL_GPL(relocate_imr_addr_mrfld);
+
+void sst_add_to_dispatch_list_and_post(struct intel_sst_drv *sst,
+                                               struct ipc_post *msg)
+{
+       unsigned long irq_flags;
+
+       spin_lock_irqsave(&sst->ipc_spin_lock, irq_flags);
+       list_add_tail(&msg->node, &sst->ipc_dispatch_list);
+       spin_unlock_irqrestore(&sst->ipc_spin_lock, irq_flags);
+       sst->ops->post_message(sst, NULL, false);
+}
diff --git a/sound/soc/intel/sst/sst_stream.c b/sound/soc/intel/sst/sst_stream.c
new file mode 100644 (file)
index 0000000..dae2a41
--- /dev/null
@@ -0,0 +1,437 @@
+/*
+ *  sst_stream.c - Intel SST Driver for audio engine
+ *
+ *  Copyright (C) 2008-14 Intel Corp
+ *  Authors:   Vinod Koul <vinod.koul@intel.com>
+ *             Harsha Priya <priya.harsha@intel.com>
+ *             Dharageswari R <dharageswari.r@intel.com>
+ *             KP Jeeja <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 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 <linux/pci.h>
+#include <linux/firmware.h>
+#include <linux/sched.h>
+#include <linux/delay.h>
+#include <linux/pm_runtime.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/soc.h>
+#include <sound/compress_driver.h>
+#include <asm/platform_sst_audio.h>
+#include "../sst-mfld-platform.h"
+#include "sst.h"
+#include "../sst-dsp.h"
+
+int sst_alloc_stream_mrfld(struct intel_sst_drv *sst_drv_ctx, void *params)
+{
+       struct snd_sst_alloc_mrfld alloc_param;
+       struct snd_sst_params *str_params;
+       struct snd_sst_tstamp fw_tstamp;
+       struct stream_info *str_info;
+       struct snd_sst_alloc_response *response;
+       unsigned int str_id, pipe_id, task_id;
+       int i, num_ch, ret = 0;
+       void *data = NULL;
+
+       dev_dbg(sst_drv_ctx->dev, "Enter\n");
+       BUG_ON(!params);
+
+       str_params = (struct snd_sst_params *)params;
+       memset(&alloc_param, 0, sizeof(alloc_param));
+       alloc_param.operation = str_params->ops;
+       alloc_param.codec_type = str_params->codec;
+       alloc_param.sg_count = str_params->aparams.sg_count;
+       alloc_param.ring_buf_info[0].addr =
+               str_params->aparams.ring_buf_info[0].addr;
+       alloc_param.ring_buf_info[0].size =
+               str_params->aparams.ring_buf_info[0].size;
+       alloc_param.frag_size = str_params->aparams.frag_size;
+
+       memcpy(&alloc_param.codec_params, &str_params->sparams,
+                       sizeof(struct snd_sst_stream_params));
+
+       /*
+        * fill channel map params for multichannel support.
+        * Ideally channel map should be received from upper layers
+        * for multichannel support.
+        * Currently hardcoding as per FW reqm.
+        */
+       num_ch = sst_get_num_channel(str_params);
+       for (i = 0; i < 8; i++) {
+               if (i < num_ch)
+                       alloc_param.codec_params.uc.pcm_params.channel_map[i] = i;
+               else
+                       alloc_param.codec_params.uc.pcm_params.channel_map[i] = 0xFF;
+       }
+
+       str_id = str_params->stream_id;
+       str_info = get_stream_info(sst_drv_ctx, str_id);
+       if (str_info == NULL) {
+               dev_err(sst_drv_ctx->dev, "get stream info returned null\n");
+               return -EINVAL;
+       }
+
+       pipe_id = str_params->device_type;
+       task_id = str_params->task;
+       sst_drv_ctx->streams[str_id].pipe_id = pipe_id;
+       sst_drv_ctx->streams[str_id].task_id = task_id;
+       sst_drv_ctx->streams[str_id].num_ch = num_ch;
+
+       if (sst_drv_ctx->info.lpe_viewpt_rqd)
+               alloc_param.ts = sst_drv_ctx->info.mailbox_start +
+                       sst_drv_ctx->tstamp + (str_id * sizeof(fw_tstamp));
+       else
+               alloc_param.ts = sst_drv_ctx->mailbox_add +
+                       sst_drv_ctx->tstamp + (str_id * sizeof(fw_tstamp));
+
+       dev_dbg(sst_drv_ctx->dev, "alloc tstamp location = 0x%x\n",
+                       alloc_param.ts);
+       dev_dbg(sst_drv_ctx->dev, "assigned pipe id 0x%x to task %d\n",
+                       pipe_id, task_id);
+
+       /* allocate device type context */
+       sst_init_stream(&sst_drv_ctx->streams[str_id], alloc_param.codec_type,
+                       str_id, alloc_param.operation, 0);
+
+       dev_info(sst_drv_ctx->dev, "Alloc for str %d pipe %#x\n",
+                       str_id, pipe_id);
+       ret = sst_prepare_and_post_msg(sst_drv_ctx, task_id, IPC_CMD,
+                       IPC_IA_ALLOC_STREAM_MRFLD, pipe_id, sizeof(alloc_param),
+                       &alloc_param, data, true, true, false, true);
+
+       if (ret < 0) {
+               dev_err(sst_drv_ctx->dev, "FW alloc failed ret %d\n", ret);
+               /* alloc failed, so reset the state to uninit */
+               str_info->status = STREAM_UN_INIT;
+               str_id = ret;
+       } else if (data) {
+               response = (struct snd_sst_alloc_response *)data;
+               ret = response->str_type.result;
+               if (!ret)
+                       goto out;
+               dev_err(sst_drv_ctx->dev, "FW alloc failed ret %d\n", ret);
+               if (ret == SST_ERR_STREAM_IN_USE) {
+                       dev_err(sst_drv_ctx->dev,
+                               "FW not in clean state, send free for:%d\n", str_id);
+                       sst_free_stream(sst_drv_ctx, str_id);
+               }
+               str_id = -ret;
+       }
+out:
+       kfree(data);
+       return str_id;
+}
+
+/**
+* sst_start_stream - Send msg for a starting stream
+* @str_id:      stream ID
+*
+* This function is called by any function which wants to start
+* a stream.
+*/
+int sst_start_stream(struct intel_sst_drv *sst_drv_ctx, int str_id)
+{
+       int retval = 0;
+       struct stream_info *str_info;
+       u16 data = 0;
+
+       dev_dbg(sst_drv_ctx->dev, "sst_start_stream for %d\n", str_id);
+       str_info = get_stream_info(sst_drv_ctx, str_id);
+       if (!str_info)
+               return -EINVAL;
+       if (str_info->status != STREAM_RUNNING)
+               return -EBADRQC;
+
+       retval = sst_prepare_and_post_msg(sst_drv_ctx, str_info->task_id,
+                       IPC_CMD, IPC_IA_START_STREAM_MRFLD, str_info->pipe_id,
+                       sizeof(u16), &data, NULL, true, true, true, false);
+
+       return retval;
+}
+
+int sst_send_byte_stream_mrfld(struct intel_sst_drv *sst_drv_ctx,
+               struct snd_sst_bytes_v2 *bytes)
+{      struct ipc_post *msg = NULL;
+       u32 length;
+       int pvt_id, ret = 0;
+       struct sst_block *block = NULL;
+
+       dev_dbg(sst_drv_ctx->dev,
+               "type:%u ipc_msg:%u block:%u task_id:%u pipe: %#x length:%#x\n",
+               bytes->type, bytes->ipc_msg, bytes->block, bytes->task_id,
+               bytes->pipe_id, bytes->len);
+
+       if (sst_create_ipc_msg(&msg, true))
+               return -ENOMEM;
+
+       pvt_id = sst_assign_pvt_id(sst_drv_ctx);
+       sst_fill_header_mrfld(&msg->mrfld_header, bytes->ipc_msg,
+                       bytes->task_id, 1, pvt_id);
+       msg->mrfld_header.p.header_high.part.res_rqd = bytes->block;
+       length = bytes->len;
+       msg->mrfld_header.p.header_low_payload = length;
+       dev_dbg(sst_drv_ctx->dev, "length is %d\n", length);
+       memcpy(msg->mailbox_data, &bytes->bytes, bytes->len);
+       if (bytes->block) {
+               block = sst_create_block(sst_drv_ctx, bytes->ipc_msg, pvt_id);
+               if (block == NULL) {
+                       kfree(msg);
+                       ret = -ENOMEM;
+                       goto out;
+               }
+       }
+
+       sst_add_to_dispatch_list_and_post(sst_drv_ctx, msg);
+       dev_dbg(sst_drv_ctx->dev, "msg->mrfld_header.p.header_low_payload:%d",
+                       msg->mrfld_header.p.header_low_payload);
+
+       if (bytes->block) {
+               ret = sst_wait_timeout(sst_drv_ctx, block);
+               if (ret) {
+                       dev_err(sst_drv_ctx->dev, "fw returned err %d\n", ret);
+                       sst_free_block(sst_drv_ctx, block);
+                       goto out;
+               }
+       }
+       if (bytes->type == SND_SST_BYTES_GET) {
+               /*
+                * copy the reply and send back
+                * we need to update only sz and payload
+                */
+               if (bytes->block) {
+                       unsigned char *r = block->data;
+
+                       dev_dbg(sst_drv_ctx->dev, "read back %d bytes",
+                                       bytes->len);
+                       memcpy(bytes->bytes, r, bytes->len);
+               }
+       }
+       if (bytes->block)
+               sst_free_block(sst_drv_ctx, block);
+out:
+       test_and_clear_bit(pvt_id, &sst_drv_ctx->pvt_id);
+       return 0;
+}
+
+/*
+ * sst_pause_stream - Send msg for a pausing stream
+ * @str_id:     stream ID
+ *
+ * This function is called by any function which wants to pause
+ * an already running stream.
+ */
+int sst_pause_stream(struct intel_sst_drv *sst_drv_ctx, int str_id)
+{
+       int retval = 0;
+       struct stream_info *str_info;
+
+       dev_dbg(sst_drv_ctx->dev, "SST DBG:sst_pause_stream for %d\n", str_id);
+       str_info = get_stream_info(sst_drv_ctx, str_id);
+       if (!str_info)
+               return -EINVAL;
+       if (str_info->status == STREAM_PAUSED)
+               return 0;
+       if (str_info->status == STREAM_RUNNING ||
+               str_info->status == STREAM_INIT) {
+               if (str_info->prev == STREAM_UN_INIT)
+                       return -EBADRQC;
+
+               retval = sst_prepare_and_post_msg(sst_drv_ctx, str_info->task_id, IPC_CMD,
+                               IPC_IA_PAUSE_STREAM_MRFLD, str_info->pipe_id,
+                               0, NULL, NULL, true, true, false, true);
+
+               if (retval == 0) {
+                       str_info->prev = str_info->status;
+                       str_info->status = STREAM_PAUSED;
+               } else if (retval == SST_ERR_INVALID_STREAM_ID) {
+                       retval = -EINVAL;
+                       mutex_lock(&sst_drv_ctx->sst_lock);
+                       sst_clean_stream(str_info);
+                       mutex_unlock(&sst_drv_ctx->sst_lock);
+               }
+       } else {
+               retval = -EBADRQC;
+               dev_dbg(sst_drv_ctx->dev, "SST DBG:BADRQC for stream\n ");
+       }
+
+       return retval;
+}
+
+/**
+ * sst_resume_stream - Send msg for resuming stream
+ * @str_id:            stream ID
+ *
+ * This function is called by any function which wants to resume
+ * an already paused stream.
+ */
+int sst_resume_stream(struct intel_sst_drv *sst_drv_ctx, int str_id)
+{
+       int retval = 0;
+       struct stream_info *str_info;
+
+       dev_dbg(sst_drv_ctx->dev, "SST DBG:sst_resume_stream for %d\n", str_id);
+       str_info = get_stream_info(sst_drv_ctx, str_id);
+       if (!str_info)
+               return -EINVAL;
+       if (str_info->status == STREAM_RUNNING)
+                       return 0;
+       if (str_info->status == STREAM_PAUSED) {
+               retval = sst_prepare_and_post_msg(sst_drv_ctx, str_info->task_id,
+                               IPC_CMD, IPC_IA_RESUME_STREAM_MRFLD,
+                               str_info->pipe_id, 0, NULL, NULL,
+                               true, true, false, true);
+
+               if (!retval) {
+                       if (str_info->prev == STREAM_RUNNING)
+                               str_info->status = STREAM_RUNNING;
+                       else
+                               str_info->status = STREAM_INIT;
+                       str_info->prev = STREAM_PAUSED;
+               } else if (retval == -SST_ERR_INVALID_STREAM_ID) {
+                       retval = -EINVAL;
+                       mutex_lock(&sst_drv_ctx->sst_lock);
+                       sst_clean_stream(str_info);
+                       mutex_unlock(&sst_drv_ctx->sst_lock);
+               }
+       } else {
+               retval = -EBADRQC;
+               dev_err(sst_drv_ctx->dev, "SST ERR: BADQRC for stream\n");
+       }
+
+       return retval;
+}
+
+
+/**
+ * sst_drop_stream - Send msg for stopping stream
+ * @str_id:            stream ID
+ *
+ * This function is called by any function which wants to stop
+ * a stream.
+ */
+int sst_drop_stream(struct intel_sst_drv *sst_drv_ctx, int str_id)
+{
+       int retval = 0;
+       struct stream_info *str_info;
+
+       dev_dbg(sst_drv_ctx->dev, "SST DBG:sst_drop_stream for %d\n", str_id);
+       str_info = get_stream_info(sst_drv_ctx, str_id);
+       if (!str_info)
+               return -EINVAL;
+
+       if (str_info->status != STREAM_UN_INIT) {
+               str_info->prev = STREAM_UN_INIT;
+               str_info->status = STREAM_INIT;
+               str_info->cumm_bytes = 0;
+               retval = sst_prepare_and_post_msg(sst_drv_ctx, str_info->task_id,
+                               IPC_CMD, IPC_IA_DROP_STREAM_MRFLD,
+                               str_info->pipe_id, 0, NULL, NULL,
+                               true, true, true, false);
+       } else {
+               retval = -EBADRQC;
+               dev_dbg(sst_drv_ctx->dev, "BADQRC for stream, state %x\n",
+                               str_info->status);
+       }
+       return retval;
+}
+
+/**
+* sst_drain_stream - Send msg for draining stream
+* @str_id:             stream ID
+*
+* This function is called by any function which wants to drain
+* a stream.
+*/
+int sst_drain_stream(struct intel_sst_drv *sst_drv_ctx,
+                       int str_id, bool partial_drain)
+{
+       int retval = 0;
+       struct stream_info *str_info;
+
+       dev_dbg(sst_drv_ctx->dev, "SST DBG:sst_drain_stream for %d\n", str_id);
+       str_info = get_stream_info(sst_drv_ctx, str_id);
+       if (!str_info)
+               return -EINVAL;
+       if (str_info->status != STREAM_RUNNING &&
+               str_info->status != STREAM_INIT &&
+               str_info->status != STREAM_PAUSED) {
+                       dev_err(sst_drv_ctx->dev, "SST ERR: BADQRC for stream = %d\n",
+                                      str_info->status);
+                       return -EBADRQC;
+       }
+
+       retval = sst_prepare_and_post_msg(sst_drv_ctx, str_info->task_id, IPC_CMD,
+                       IPC_IA_DRAIN_STREAM_MRFLD, str_info->pipe_id,
+                       sizeof(u8), &partial_drain, NULL, true, true, false, false);
+       /*
+        * with new non blocked drain implementation in core we dont need to
+        * wait for respsonse, and need to only invoke callback for drain
+        * complete
+        */
+
+       return retval;
+}
+
+/**
+ * sst_free_stream - Frees a stream
+ * @str_id:            stream ID
+ *
+ * This function is called by any function which wants to free
+ * a stream.
+ */
+int sst_free_stream(struct intel_sst_drv *sst_drv_ctx, int str_id)
+{
+       int retval = 0;
+       struct stream_info *str_info;
+       struct intel_sst_ops *ops;
+
+       dev_dbg(sst_drv_ctx->dev, "SST DBG:sst_free_stream for %d\n", str_id);
+
+       mutex_lock(&sst_drv_ctx->sst_lock);
+       if (sst_drv_ctx->sst_state == SST_RESET) {
+               mutex_unlock(&sst_drv_ctx->sst_lock);
+               return -ENODEV;
+       }
+       mutex_unlock(&sst_drv_ctx->sst_lock);
+       str_info = get_stream_info(sst_drv_ctx, str_id);
+       if (!str_info)
+               return -EINVAL;
+       ops = sst_drv_ctx->ops;
+
+       mutex_lock(&str_info->lock);
+       if (str_info->status != STREAM_UN_INIT) {
+               str_info->prev =  str_info->status;
+               str_info->status = STREAM_UN_INIT;
+               mutex_unlock(&str_info->lock);
+
+               dev_info(sst_drv_ctx->dev, "Free for str %d pipe %#x\n",
+                               str_id, str_info->pipe_id);
+               retval = sst_prepare_and_post_msg(sst_drv_ctx, str_info->task_id, IPC_CMD,
+                               IPC_IA_FREE_STREAM_MRFLD, str_info->pipe_id, 0,
+                               NULL, NULL, true, true, false, true);
+
+               dev_dbg(sst_drv_ctx->dev, "sst: wait for free returned %d\n",
+                               retval);
+               mutex_lock(&sst_drv_ctx->sst_lock);
+               sst_clean_stream(str_info);
+               mutex_unlock(&sst_drv_ctx->sst_lock);
+               dev_dbg(sst_drv_ctx->dev, "SST DBG:Stream freed\n");
+       } else {
+               mutex_unlock(&str_info->lock);
+               retval = -EBADRQC;
+               dev_dbg(sst_drv_ctx->dev, "SST DBG:BADQRC for stream\n");
+       }
+
+       return retval;
+}
index 5cb91f9..0fb7d2a 100644 (file)
@@ -77,25 +77,18 @@ static int qi_lb60_probe(struct platform_device *pdev)
 {
        struct qi_lb60 *qi_lb60;
        struct snd_soc_card *card = &qi_lb60_card;
-       int ret;
 
        qi_lb60 = devm_kzalloc(&pdev->dev, sizeof(*qi_lb60), GFP_KERNEL);
        if (!qi_lb60)
                return -ENOMEM;
 
-       qi_lb60->snd_gpio = devm_gpiod_get(&pdev->dev, "snd");
+       qi_lb60->snd_gpio = devm_gpiod_get(&pdev->dev, "snd", GPIOD_OUT_LOW);
        if (IS_ERR(qi_lb60->snd_gpio))
                return PTR_ERR(qi_lb60->snd_gpio);
-       ret = gpiod_direction_output(qi_lb60->snd_gpio, 0);
-       if (ret)
-               return ret;
 
-       qi_lb60->amp_gpio = devm_gpiod_get(&pdev->dev, "amp");
+       qi_lb60->amp_gpio = devm_gpiod_get(&pdev->dev, "amp", GPIOD_OUT_LOW);
        if (IS_ERR(qi_lb60->amp_gpio))
                return PTR_ERR(qi_lb60->amp_gpio);
-       ret = gpiod_direction_output(qi_lb60->amp_gpio, 0);
-       if (ret)
-               return ret;
 
        card->dev = &pdev->dev;
 
index ef1d42d..4380dcc 100644 (file)
@@ -309,7 +309,7 @@ int snd_soc_jack_add_gpios(struct snd_soc_jack *jack, int count,
                        /* GPIO descriptor */
                        gpios[i].desc = gpiod_get_index(gpios[i].gpiod_dev,
                                                        gpios[i].name,
-                                                       gpios[i].idx);
+                                                       gpios[i].idx, GPIOD_IN);
                        if (IS_ERR(gpios[i].desc)) {
                                ret = PTR_ERR(gpios[i].desc);
                                dev_err(gpios[i].gpiod_dev,
@@ -327,17 +327,14 @@ int snd_soc_jack_add_gpios(struct snd_soc_jack *jack, int count,
                                goto undo;
                        }
 
-                       ret = gpio_request(gpios[i].gpio, gpios[i].name);
+                       ret = gpio_request_one(gpios[i].gpio, GPIOF_IN,
+                                              gpios[i].name);
                        if (ret)
                                goto undo;
 
                        gpios[i].desc = gpio_to_desc(gpios[i].gpio);
                }
 
-               ret = gpiod_direction_input(gpios[i].desc);
-               if (ret)
-                       goto err;
-
                INIT_DELAYED_WORK(&gpios[i].work, gpio_work);
                gpios[i].jack = jack;