]> git.kernelconcepts.de Git - karo-tx-linux.git/commitdiff
Merge remote-tracking branch 'asoc/topic/intel' into asoc-next
authorMark Brown <broonie@kernel.org>
Sun, 12 Apr 2015 18:48:33 +0000 (19:48 +0100)
committerMark Brown <broonie@kernel.org>
Sun, 12 Apr 2015 18:48:33 +0000 (19:48 +0100)
48 files changed:
include/sound/rt5670.h
sound/soc/codecs/rt5670.c
sound/soc/codecs/rt5670.h
sound/soc/intel/Makefile
sound/soc/intel/atom/Makefile [new file with mode: 0644]
sound/soc/intel/atom/sst-atom-controls.c [moved from sound/soc/intel/sst-atom-controls.c with 100% similarity]
sound/soc/intel/atom/sst-atom-controls.h [moved from sound/soc/intel/sst-atom-controls.h with 100% similarity]
sound/soc/intel/atom/sst-mfld-dsp.h [moved from sound/soc/intel/sst-mfld-dsp.h with 100% similarity]
sound/soc/intel/atom/sst-mfld-platform-compress.c [moved from sound/soc/intel/sst-mfld-platform-compress.c with 100% similarity]
sound/soc/intel/atom/sst-mfld-platform-pcm.c [moved from sound/soc/intel/sst-mfld-platform-pcm.c with 100% similarity]
sound/soc/intel/atom/sst-mfld-platform.h [moved from sound/soc/intel/sst-mfld-platform.h with 100% similarity]
sound/soc/intel/atom/sst/Makefile [moved from sound/soc/intel/sst/Makefile with 100% similarity]
sound/soc/intel/atom/sst/sst.c [moved from sound/soc/intel/sst/sst.c with 99% similarity]
sound/soc/intel/atom/sst/sst.h [moved from sound/soc/intel/sst/sst.h with 100% similarity]
sound/soc/intel/atom/sst/sst_acpi.c [moved from sound/soc/intel/sst/sst_acpi.c with 99% similarity]
sound/soc/intel/atom/sst/sst_drv_interface.c [moved from sound/soc/intel/sst/sst_drv_interface.c with 99% similarity]
sound/soc/intel/atom/sst/sst_ipc.c [moved from sound/soc/intel/sst/sst_ipc.c with 99% similarity]
sound/soc/intel/atom/sst/sst_loader.c [moved from sound/soc/intel/sst/sst_loader.c with 99% similarity]
sound/soc/intel/atom/sst/sst_pci.c [moved from sound/soc/intel/sst/sst_pci.c with 100% similarity]
sound/soc/intel/atom/sst/sst_pvt.c [moved from sound/soc/intel/sst/sst_pvt.c with 95% similarity]
sound/soc/intel/atom/sst/sst_stream.c [moved from sound/soc/intel/sst/sst_stream.c with 99% similarity]
sound/soc/intel/baytrail/Makefile [new file with mode: 0644]
sound/soc/intel/baytrail/sst-baytrail-dsp.c [moved from sound/soc/intel/sst-baytrail-dsp.c with 99% similarity]
sound/soc/intel/baytrail/sst-baytrail-ipc.c [moved from sound/soc/intel/sst-baytrail-ipc.c with 71% similarity]
sound/soc/intel/baytrail/sst-baytrail-ipc.h [moved from sound/soc/intel/sst-baytrail-ipc.h with 100% similarity]
sound/soc/intel/baytrail/sst-baytrail-pcm.c [moved from sound/soc/intel/sst-baytrail-pcm.c with 99% similarity]
sound/soc/intel/boards/Makefile [new file with mode: 0644]
sound/soc/intel/boards/broadwell.c [moved from sound/soc/intel/broadwell.c with 89% similarity]
sound/soc/intel/boards/byt-max98090.c [moved from sound/soc/intel/byt-max98090.c with 99% similarity]
sound/soc/intel/boards/byt-rt5640.c [moved from sound/soc/intel/byt-rt5640.c with 98% similarity]
sound/soc/intel/boards/bytcr_rt5640.c [moved from sound/soc/intel/bytcr_dpcm_rt5640.c with 98% similarity]
sound/soc/intel/boards/cht_bsw_rt5645.c [moved from sound/soc/intel/cht_bsw_rt5645.c with 99% similarity]
sound/soc/intel/boards/cht_bsw_rt5672.c [moved from sound/soc/intel/cht_bsw_rt5672.c with 76% similarity]
sound/soc/intel/boards/haswell.c [moved from sound/soc/intel/haswell.c with 98% similarity]
sound/soc/intel/boards/mfld_machine.c [moved from sound/soc/intel/mfld_machine.c with 100% similarity]
sound/soc/intel/common/Makefile [new file with mode: 0644]
sound/soc/intel/common/sst-acpi.c [moved from sound/soc/intel/sst-acpi.c with 100% similarity]
sound/soc/intel/common/sst-dsp-priv.h [moved from sound/soc/intel/sst-dsp-priv.h with 97% similarity]
sound/soc/intel/common/sst-dsp.c [moved from sound/soc/intel/sst-dsp.c with 100% similarity]
sound/soc/intel/common/sst-dsp.h [moved from sound/soc/intel/sst-dsp.h with 99% similarity]
sound/soc/intel/common/sst-firmware.c [moved from sound/soc/intel/sst-firmware.c with 99% similarity]
sound/soc/intel/common/sst-ipc.c [new file with mode: 0644]
sound/soc/intel/common/sst-ipc.h [new file with mode: 0644]
sound/soc/intel/haswell/Makefile [new file with mode: 0644]
sound/soc/intel/haswell/sst-haswell-dsp.c [moved from sound/soc/intel/sst-haswell-dsp.c with 98% similarity]
sound/soc/intel/haswell/sst-haswell-ipc.c [moved from sound/soc/intel/sst-haswell-ipc.c with 77% similarity]
sound/soc/intel/haswell/sst-haswell-ipc.h [moved from sound/soc/intel/sst-haswell-ipc.h with 87% similarity]
sound/soc/intel/haswell/sst-haswell-pcm.c [moved from sound/soc/intel/sst-haswell-pcm.c with 88% similarity]

index bd311197a3b5750c48b4bbf0b6471231a13f34ac..b7d60510819b020038ece185977db1d6ea33af93 100644 (file)
@@ -14,6 +14,7 @@
 struct rt5670_platform_data {
        int jd_mode;
        bool in2_diff;
+       bool dev_gpio;
 
        bool dmic_en;
        unsigned int dmic1_data_pin;
index fd102613d20d8747c7554f5a3882c027fd0024c8..cc7f84a150a7f03878795a7a96f991a6db5b1de5 100644 (file)
@@ -403,6 +403,189 @@ static bool rt5670_readable_register(struct device *dev, unsigned int reg)
        }
 }
 
+/**
+ * rt5670_headset_detect - Detect headset.
+ * @codec: SoC audio codec device.
+ * @jack_insert: Jack insert or not.
+ *
+ * Detect whether is headset or not when jack inserted.
+ *
+ * Returns detect status.
+ */
+
+static int rt5670_headset_detect(struct snd_soc_codec *codec, int jack_insert)
+{
+       int val;
+       struct rt5670_priv *rt5670 = snd_soc_codec_get_drvdata(codec);
+
+       if (jack_insert) {
+               snd_soc_dapm_force_enable_pin(&codec->dapm,
+                                                      "Mic Det Power");
+               snd_soc_dapm_sync(&codec->dapm);
+               snd_soc_update_bits(codec, RT5670_GEN_CTRL3, 0x4, 0x0);
+               snd_soc_update_bits(codec, RT5670_CJ_CTRL2,
+                       RT5670_CBJ_DET_MODE | RT5670_CBJ_MN_JD,
+                       RT5670_CBJ_MN_JD);
+               snd_soc_write(codec, RT5670_GPIO_CTRL2, 0x0004);
+               snd_soc_update_bits(codec, RT5670_GPIO_CTRL1,
+                       RT5670_GP1_PIN_MASK, RT5670_GP1_PIN_IRQ);
+               snd_soc_update_bits(codec, RT5670_CJ_CTRL1,
+                       RT5670_CBJ_BST1_EN, RT5670_CBJ_BST1_EN);
+               snd_soc_write(codec, RT5670_JD_CTRL3, 0x00f0);
+               snd_soc_update_bits(codec, RT5670_CJ_CTRL2,
+                       RT5670_CBJ_MN_JD, RT5670_CBJ_MN_JD);
+               snd_soc_update_bits(codec, RT5670_CJ_CTRL2,
+                       RT5670_CBJ_MN_JD, 0);
+               msleep(300);
+               val = snd_soc_read(codec, RT5670_CJ_CTRL3) & 0x7;
+               if (val == 0x1 || val == 0x2) {
+                       rt5670->jack_type = SND_JACK_HEADSET;
+                       /* for push button */
+                       snd_soc_update_bits(codec, RT5670_INT_IRQ_ST, 0x8, 0x8);
+                       snd_soc_update_bits(codec, RT5670_IL_CMD, 0x40, 0x40);
+                       snd_soc_read(codec, RT5670_IL_CMD);
+               } else {
+                       snd_soc_update_bits(codec, RT5670_GEN_CTRL3, 0x4, 0x4);
+                       rt5670->jack_type = SND_JACK_HEADPHONE;
+                       snd_soc_dapm_disable_pin(&codec->dapm, "Mic Det Power");
+                       snd_soc_dapm_sync(&codec->dapm);
+               }
+       } else {
+               snd_soc_update_bits(codec, RT5670_INT_IRQ_ST, 0x8, 0x0);
+               snd_soc_update_bits(codec, RT5670_GEN_CTRL3, 0x4, 0x4);
+               rt5670->jack_type = 0;
+               snd_soc_dapm_disable_pin(&codec->dapm, "Mic Det Power");
+               snd_soc_dapm_sync(&codec->dapm);
+       }
+
+       return rt5670->jack_type;
+}
+
+void rt5670_jack_suspend(struct snd_soc_codec *codec)
+{
+       struct rt5670_priv *rt5670 = snd_soc_codec_get_drvdata(codec);
+
+       rt5670->jack_type_saved = rt5670->jack_type;
+       rt5670_headset_detect(codec, 0);
+}
+EXPORT_SYMBOL_GPL(rt5670_jack_suspend);
+
+void rt5670_jack_resume(struct snd_soc_codec *codec)
+{
+       struct rt5670_priv *rt5670 = snd_soc_codec_get_drvdata(codec);
+
+       if (rt5670->jack_type_saved)
+               rt5670_headset_detect(codec, 1);
+}
+EXPORT_SYMBOL_GPL(rt5670_jack_resume);
+
+static int rt5670_button_detect(struct snd_soc_codec *codec)
+{
+       int btn_type, val;
+
+       val = snd_soc_read(codec, RT5670_IL_CMD);
+       btn_type = val & 0xff80;
+       snd_soc_write(codec, RT5670_IL_CMD, val);
+       if (btn_type != 0) {
+               msleep(20);
+               val = snd_soc_read(codec, RT5670_IL_CMD);
+               snd_soc_write(codec, RT5670_IL_CMD, val);
+       }
+
+       return btn_type;
+}
+
+static int rt5670_irq_detection(void *data)
+{
+       struct rt5670_priv *rt5670 = (struct rt5670_priv *)data;
+       struct snd_soc_jack_gpio *gpio = &rt5670->hp_gpio;
+       struct snd_soc_jack *jack = rt5670->jack;
+       int val, btn_type, report = jack->status;
+
+       if (rt5670->pdata.jd_mode == 1) /* 2 port */
+               val = snd_soc_read(rt5670->codec, RT5670_A_JD_CTRL1) & 0x0070;
+       else
+               val = snd_soc_read(rt5670->codec, RT5670_A_JD_CTRL1) & 0x0020;
+
+       switch (val) {
+       /* jack in */
+       case 0x30: /* 2 port */
+       case 0x0: /* 1 port or 2 port */
+               if (rt5670->jack_type == 0) {
+                       report = rt5670_headset_detect(rt5670->codec, 1);
+                       /* for push button and jack out */
+                       gpio->debounce_time = 25;
+                       break;
+               }
+               btn_type = 0;
+               if (snd_soc_read(rt5670->codec, RT5670_INT_IRQ_ST) & 0x4) {
+                       /* button pressed */
+                       report = SND_JACK_HEADSET;
+                       btn_type = rt5670_button_detect(rt5670->codec);
+                       switch (btn_type) {
+                       case 0x2000: /* up */
+                               report |= SND_JACK_BTN_1;
+                               break;
+                       case 0x0400: /* center */
+                               report |= SND_JACK_BTN_0;
+                               break;
+                       case 0x0080: /* down */
+                               report |= SND_JACK_BTN_2;
+                               break;
+                       default:
+                               dev_err(rt5670->codec->dev,
+                                       "Unexpected button code 0x%04x\n",
+                                       btn_type);
+                               break;
+                       }
+               }
+               if (btn_type == 0)/* button release */
+                       report =  rt5670->jack_type;
+
+               break;
+       /* jack out */
+       case 0x70: /* 2 port */
+       case 0x10: /* 2 port */
+       case 0x20: /* 1 port */
+               report = 0;
+               snd_soc_update_bits(rt5670->codec, RT5670_INT_IRQ_ST, 0x1, 0x0);
+               rt5670_headset_detect(rt5670->codec, 0);
+               gpio->debounce_time = 150; /* for jack in */
+               break;
+       default:
+               break;
+       }
+
+       return report;
+}
+
+int rt5670_set_jack_detect(struct snd_soc_codec *codec,
+       struct snd_soc_jack *jack)
+{
+       struct rt5670_priv *rt5670 = snd_soc_codec_get_drvdata(codec);
+       int ret;
+
+       rt5670->jack = jack;
+       rt5670->hp_gpio.gpiod_dev = codec->dev;
+       rt5670->hp_gpio.name = "headphone detect";
+       rt5670->hp_gpio.report = SND_JACK_HEADSET |
+               SND_JACK_BTN_0 | SND_JACK_BTN_1 | SND_JACK_BTN_2;
+       rt5670->hp_gpio.debounce_time = 150;
+       rt5670->hp_gpio.wake = true;
+       rt5670->hp_gpio.data = (struct rt5670_priv *)rt5670;
+       rt5670->hp_gpio.jack_status_check = rt5670_irq_detection;
+
+       ret = snd_soc_jack_add_gpios(rt5670->jack, 1,
+                       &rt5670->hp_gpio);
+       if (ret) {
+               dev_err(codec->dev, "Adding jack GPIO failed\n");
+               return ret;
+       }
+
+       return 0;
+}
+EXPORT_SYMBOL_GPL(rt5670_set_jack_detect);
+
 static const DECLARE_TLV_DB_SCALE(out_vol_tlv, -4650, 150, 0);
 static const DECLARE_TLV_DB_SCALE(dac_vol_tlv, -65625, 375, 0);
 static const DECLARE_TLV_DB_SCALE(in_vol_tlv, -3450, 150, 0);
@@ -517,11 +700,9 @@ static int is_sys_clk_from_pll(struct snd_soc_dapm_widget *source,
                         struct snd_soc_dapm_widget *sink)
 {
        struct snd_soc_codec *codec = snd_soc_dapm_to_codec(source->dapm);
-       unsigned int val;
+       struct rt5670_priv *rt5670 = snd_soc_codec_get_drvdata(codec);
 
-       val = snd_soc_read(codec, RT5670_GLB_CLK);
-       val &= RT5670_SCLK_SRC_MASK;
-       if (val == RT5670_SCLK_SRC_PLL1)
+       if (rt5670->sysclk_src == RT5670_SCLK_S_PLL1)
                return 1;
        else
                return 0;
@@ -2271,16 +2452,6 @@ static int rt5670_set_dai_sysclk(struct snd_soc_dai *dai,
        struct rt5670_priv *rt5670 = snd_soc_codec_get_drvdata(codec);
        unsigned int reg_val = 0;
 
-       if (freq == rt5670->sysclk && clk_id == rt5670->sysclk_src)
-               return 0;
-
-       if (rt5670->pdata.jd_mode) {
-               if (clk_id == RT5670_SCLK_S_PLL1)
-                       snd_soc_dapm_force_enable_pin(&codec->dapm, "PLL1");
-               else
-                       snd_soc_dapm_disable_pin(&codec->dapm, "PLL1");
-               snd_soc_dapm_sync(&codec->dapm);
-       }
        switch (clk_id) {
        case RT5670_SCLK_S_MCLK:
                reg_val |= RT5670_SCLK_SRC_MCLK;
@@ -2298,7 +2469,8 @@ static int rt5670_set_dai_sysclk(struct snd_soc_dai *dai,
        snd_soc_update_bits(codec, RT5670_GLB_CLK,
                RT5670_SCLK_SRC_MASK, reg_val);
        rt5670->sysclk = freq;
-       rt5670->sysclk_src = clk_id;
+       if (clk_id != RT5670_SCLK_S_RCCLK)
+               rt5670->sysclk_src = clk_id;
 
        dev_dbg(dai->dev, "Sysclk is %dHz and clock id is %d\n", freq, clk_id);
 
@@ -2517,6 +2689,7 @@ static int rt5670_remove(struct snd_soc_codec *codec)
        struct rt5670_priv *rt5670 = snd_soc_codec_get_drvdata(codec);
 
        regmap_write(rt5670->regmap, RT5670_RESET, 0);
+       snd_soc_jack_free_gpios(rt5670->jack, 1, &rt5670->hp_gpio);
        return 0;
 }
 
@@ -2676,6 +2849,7 @@ static int rt5670_i2c_probe(struct i2c_client *i2c,
        if (dmi_check_system(dmi_platform_intel_braswell)) {
                rt5670->pdata.dmic_en = true;
                rt5670->pdata.dmic1_data_pin = RT5670_DMIC_DATA_IN2P;
+               rt5670->pdata.dev_gpio = true;
                rt5670->pdata.jd_mode = 1;
        }
 
@@ -2717,12 +2891,17 @@ static int rt5670_i2c_probe(struct i2c_client *i2c,
                regmap_update_bits(rt5670->regmap, RT5670_IN2,
                                        RT5670_IN_DF2, RT5670_IN_DF2);
 
-       if (i2c->irq) {
+       if (rt5670->pdata.dev_gpio) {
+               /* for push button */
+               regmap_write(rt5670->regmap, RT5670_IL_CMD, 0x0000);
+               regmap_write(rt5670->regmap, RT5670_IL_CMD2, 0x0010);
+               regmap_write(rt5670->regmap, RT5670_IL_CMD3, 0x0014);
+               /* for irq */
                regmap_update_bits(rt5670->regmap, RT5670_GPIO_CTRL1,
                                   RT5670_GP1_PIN_MASK, RT5670_GP1_PIN_IRQ);
                regmap_update_bits(rt5670->regmap, RT5670_GPIO_CTRL2,
                                   RT5670_GP1_PF_MASK, RT5670_GP1_PF_OUT);
-
+               regmap_update_bits(rt5670->regmap, RT5670_DIG_MISC, 0x8, 0x8);
        }
 
        if (rt5670->pdata.jd_mode) {
index 0a67adbcfbc37f394399735e82499f2a6ebdf238..dc2b46236c5cb5cd7ba12ec5fd9f311868978983 100644 (file)
@@ -1988,6 +1988,8 @@ struct rt5670_priv {
        struct snd_soc_codec *codec;
        struct rt5670_platform_data pdata;
        struct regmap *regmap;
+       struct snd_soc_jack *jack;
+       struct snd_soc_jack_gpio hp_gpio;
 
        int sysclk;
        int sysclk_src;
@@ -2002,6 +2004,11 @@ struct rt5670_priv {
        int dsp_sw; /* expected parameter setting */
        int dsp_rate;
        int jack_type;
+       int jack_type_saved;
 };
 
+void rt5670_jack_suspend(struct snd_soc_codec *codec);
+void rt5670_jack_resume(struct snd_soc_codec *codec);
+int rt5670_set_jack_detect(struct snd_soc_codec *codec,
+       struct snd_soc_jack *jack);
 #endif /* __RT5670_H__ */
index a8e53c45c6b655953ef93034bbc238707611b313..cd9aee9871a36a4400646441f1105d949fe5a2af 100644 (file)
@@ -1,42 +1,10 @@
 # Core support
-snd-soc-sst-dsp-objs := sst-dsp.o sst-firmware.o
-snd-soc-sst-acpi-objs := sst-acpi.o
-
-snd-soc-sst-mfld-platform-objs := sst-mfld-platform-pcm.o \
-       sst-mfld-platform-compress.o sst-atom-controls.o
-snd-soc-mfld-machine-objs := mfld_machine.o
-
-obj-$(CONFIG_SND_SST_MFLD_PLATFORM) += snd-soc-sst-mfld-platform.o
-obj-$(CONFIG_SND_MFLD_MACHINE) += snd-soc-mfld-machine.o
-
-obj-$(CONFIG_SND_SOC_INTEL_SST) += snd-soc-sst-dsp.o
-obj-$(CONFIG_SND_SOC_INTEL_SST_ACPI) += snd-soc-sst-acpi.o
+obj-$(CONFIG_SND_SOC_INTEL_SST) += common/
 
 # Platform Support
-snd-soc-sst-haswell-pcm-objs := \
-       sst-haswell-ipc.o sst-haswell-pcm.o sst-haswell-dsp.o
-snd-soc-sst-baytrail-pcm-objs := \
-       sst-baytrail-ipc.o sst-baytrail-pcm.o sst-baytrail-dsp.o
-
-obj-$(CONFIG_SND_SOC_INTEL_HASWELL) += snd-soc-sst-haswell-pcm.o
-obj-$(CONFIG_SND_SOC_INTEL_BAYTRAIL) += snd-soc-sst-baytrail-pcm.o
+obj-$(CONFIG_SND_SOC_INTEL_HASWELL) += haswell/
+obj-$(CONFIG_SND_SOC_INTEL_BAYTRAIL) += baytrail/
+obj-$(CONFIG_SND_SOC_INTEL_BAYTRAIL) += atom/
 
 # Machine support
-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
-snd-soc-sst-cht-bsw-rt5645-objs := cht_bsw_rt5645.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
-obj-$(CONFIG_SND_SOC_INTEL_CHT_BSW_RT5645_MACH) += snd-soc-sst-cht-bsw-rt5645.o
-
-# DSP driver
-obj-$(CONFIG_SND_SST_IPC) += sst/
+obj-$(CONFIG_SND_SOC_INTEL_SST) += boards/
diff --git a/sound/soc/intel/atom/Makefile b/sound/soc/intel/atom/Makefile
new file mode 100644 (file)
index 0000000..ce8074f
--- /dev/null
@@ -0,0 +1,7 @@
+snd-soc-sst-mfld-platform-objs := sst-mfld-platform-pcm.o \
+               sst-mfld-platform-compress.o sst-atom-controls.o
+
+obj-$(CONFIG_SND_SST_MFLD_PLATFORM) += snd-soc-sst-mfld-platform.o
+
+# DSP driver
+obj-$(CONFIG_SND_SST_IPC) += sst/
similarity index 99%
rename from sound/soc/intel/sst/sst.c
rename to sound/soc/intel/atom/sst/sst.c
index 1a7eeec444b14bc55ab8fcc434a23f39cdd91913..96c2e420cce61f57be72baa4031a29e3ee483c06 100644 (file)
@@ -32,7 +32,7 @@
 #include <asm/platform_sst_audio.h>
 #include "../sst-mfld-platform.h"
 #include "sst.h"
-#include "../sst-dsp.h"
+#include "../../common/sst-dsp.h"
 
 MODULE_AUTHOR("Vinod Koul <vinod.koul@intel.com>");
 MODULE_AUTHOR("Harsha Priya <priya.harsha@intel.com>");
similarity index 99%
rename from sound/soc/intel/sst/sst_acpi.c
rename to sound/soc/intel/atom/sst/sst_acpi.c
index b536ddd2517de40487f15c4a4098cae254d6b390..05f69308391114c5d76333c8818c44b66b276f03 100644 (file)
@@ -39,7 +39,7 @@
 #include <acpi/actypes.h>
 #include <acpi/acpi_bus.h>
 #include "../sst-mfld-platform.h"
-#include "../sst-dsp.h"
+#include "../../common/sst-dsp.h"
 #include "sst.h"
 
 struct sst_machines {
similarity index 99%
rename from sound/soc/intel/sst/sst_drv_interface.c
rename to sound/soc/intel/atom/sst/sst_drv_interface.c
index f0e4b99b3aeb13a61c1f5a5b5ad67de8c575d737..7b50a9d17ec1bc0db11634e153c807fc7692984a 100644 (file)
@@ -32,7 +32,7 @@
 #include <asm/platform_sst_audio.h>
 #include "../sst-mfld-platform.h"
 #include "sst.h"
-#include "../sst-dsp.h"
+#include "../../common/sst-dsp.h"
 
 
 
@@ -381,7 +381,7 @@ static int sst_cdev_tstamp(struct device *dev, unsigned int str_id,
        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)));
+                       (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);
similarity index 99%
rename from sound/soc/intel/sst/sst_ipc.c
rename to sound/soc/intel/atom/sst/sst_ipc.c
index 484e60978477bda7564077a2ebbe238f7558aff3..5a278618466c5a326fd23c92f4b45ff07e3b22f3 100644 (file)
@@ -32,7 +32,7 @@
 #include <asm/platform_sst_audio.h>
 #include "../sst-mfld-platform.h"
 #include "sst.h"
-#include "../sst-dsp.h"
+#include "../../common/sst-dsp.h"
 
 struct sst_block *sst_create_block(struct intel_sst_drv *ctx,
                                        u32 msg_id, u32 drv_id)
similarity index 99%
rename from sound/soc/intel/sst/sst_loader.c
rename to sound/soc/intel/atom/sst/sst_loader.c
index e88907ae8b154fb955037037e5c75aa62f67c8da..33917146d9c445d9919c3dacad2bf904f04b1d9a 100644 (file)
@@ -37,7 +37,7 @@
 #include <asm/platform_sst_audio.h>
 #include "../sst-mfld-platform.h"
 #include "sst.h"
-#include "../sst-dsp.h"
+#include "../../common/sst-dsp.h"
 
 void memcpy32_toio(void __iomem *dst, const void *src, int count)
 {
similarity index 95%
rename from sound/soc/intel/sst/sst_pvt.c
rename to sound/soc/intel/atom/sst/sst_pvt.c
index 4b7720864492adaec78da2460377c96d1a6df6ab..adb32fefd693a059574674b414abc7d2811985e7 100644 (file)
@@ -34,7 +34,7 @@
 #include <asm/platform_sst_audio.h>
 #include "../sst-mfld-platform.h"
 #include "sst.h"
-#include "../sst-dsp.h"
+#include "../../common/sst-dsp.h"
 
 int sst_shim_write(void __iomem *addr, int offset, int value)
 {
@@ -111,30 +111,6 @@ int sst_wait_interruptible(struct intel_sst_drv *sst_drv_ctx,
 
 }
 
-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
  *
similarity index 99%
rename from sound/soc/intel/sst/sst_stream.c
rename to sound/soc/intel/atom/sst/sst_stream.c
index dae2a41997aaa3f55ac29dede7efd41d2b45c0f1..a74c64c7053cfa47f41fbd0abb6ec4b144b8f21a 100644 (file)
@@ -31,7 +31,7 @@
 #include <asm/platform_sst_audio.h>
 #include "../sst-mfld-platform.h"
 #include "sst.h"
-#include "../sst-dsp.h"
+#include "../../common/sst-dsp.h"
 
 int sst_alloc_stream_mrfld(struct intel_sst_drv *sst_drv_ctx, void *params)
 {
diff --git a/sound/soc/intel/baytrail/Makefile b/sound/soc/intel/baytrail/Makefile
new file mode 100644 (file)
index 0000000..488408c
--- /dev/null
@@ -0,0 +1,4 @@
+snd-soc-sst-baytrail-pcm-objs := \
+               sst-baytrail-ipc.o sst-baytrail-pcm.o sst-baytrail-dsp.o
+
+obj-$(CONFIG_SND_SOC_INTEL_BAYTRAIL) += snd-soc-sst-baytrail-pcm.o
similarity index 99%
rename from sound/soc/intel/sst-baytrail-dsp.c
rename to sound/soc/intel/baytrail/sst-baytrail-dsp.c
index 5a9e56700f315e7a065f79733bbbb2b0467630ae..01d023cc05dd0a4096d6347daa61699eb7186b08 100644 (file)
@@ -22,8 +22,8 @@
 #include <linux/platform_device.h>
 #include <linux/firmware.h>
 
-#include "sst-dsp.h"
-#include "sst-dsp-priv.h"
+#include "../common/sst-dsp.h"
+#include "../common/sst-dsp-priv.h"
 #include "sst-baytrail-ipc.h"
 
 #define SST_BYT_FW_SIGNATURE_SIZE      4
similarity index 71%
rename from sound/soc/intel/sst-baytrail-ipc.c
rename to sound/soc/intel/baytrail/sst-baytrail-ipc.c
index b4ad98c43e5c6d53a1654e75bc4577d82f15b305..1efb33b36303ea8b5c3c725c4f35b65055744689 100644 (file)
@@ -29,8 +29,9 @@
 #include <asm/div64.h>
 
 #include "sst-baytrail-ipc.h"
-#include "sst-dsp.h"
-#include "sst-dsp-priv.h"
+#include "../common/sst-dsp.h"
+#include "../common/sst-dsp-priv.h"
+#include "../common/sst-ipc.h"
 
 /* IPC message timeout */
 #define IPC_TIMEOUT_MSECS      300
@@ -142,23 +143,6 @@ struct sst_byt_fw_init {
        u8 debug_info;
 } __packed;
 
-/* driver internal IPC message structure */
-struct ipc_message {
-       struct list_head list;
-       u64 header;
-
-       /* direction wrt host CPU */
-       char tx_data[SST_BYT_IPC_MAX_PAYLOAD_SIZE];
-       size_t tx_size;
-       char rx_data[SST_BYT_IPC_MAX_PAYLOAD_SIZE];
-       size_t rx_size;
-
-       wait_queue_head_t waitq;
-       bool complete;
-       bool wait;
-       int errno;
-};
-
 struct sst_byt_stream;
 struct sst_byt;
 
@@ -195,14 +179,7 @@ struct sst_byt {
        struct sst_fw *fw;
 
        /* IPC messaging */
-       struct list_head tx_list;
-       struct list_head rx_list;
-       struct list_head empty_list;
-       wait_queue_head_t wait_txq;
-       struct task_struct *tx_thread;
-       struct kthread_worker kworker;
-       struct kthread_work kwork;
-       struct ipc_message *msg;
+       struct sst_generic_ipc ipc;
 };
 
 static inline u64 sst_byt_header(int msg_id, int data, bool large, int str_id)
@@ -246,209 +223,6 @@ static struct sst_byt_stream *sst_byt_get_stream(struct sst_byt *byt,
        return NULL;
 }
 
-static void sst_byt_ipc_shim_dbg(struct sst_byt *byt, const char *text)
-{
-       struct sst_dsp *sst = byt->dsp;
-       u64 isr, ipcd, imrx, ipcx;
-
-       ipcx = sst_dsp_shim_read64_unlocked(sst, SST_IPCX);
-       isr = sst_dsp_shim_read64_unlocked(sst, SST_ISRX);
-       ipcd = sst_dsp_shim_read64_unlocked(sst, SST_IPCD);
-       imrx = sst_dsp_shim_read64_unlocked(sst, SST_IMRX);
-
-       dev_err(byt->dev,
-               "ipc: --%s-- ipcx 0x%llx isr 0x%llx ipcd 0x%llx imrx 0x%llx\n",
-               text, ipcx, isr, ipcd, imrx);
-}
-
-/* locks held by caller */
-static struct ipc_message *sst_byt_msg_get_empty(struct sst_byt *byt)
-{
-       struct ipc_message *msg = NULL;
-
-       if (!list_empty(&byt->empty_list)) {
-               msg = list_first_entry(&byt->empty_list,
-                                      struct ipc_message, list);
-               list_del(&msg->list);
-       }
-
-       return msg;
-}
-
-static void sst_byt_ipc_tx_msgs(struct kthread_work *work)
-{
-       struct sst_byt *byt =
-               container_of(work, struct sst_byt, kwork);
-       struct ipc_message *msg;
-       u64 ipcx;
-       unsigned long flags;
-
-       spin_lock_irqsave(&byt->dsp->spinlock, flags);
-       if (list_empty(&byt->tx_list)) {
-               spin_unlock_irqrestore(&byt->dsp->spinlock, flags);
-               return;
-       }
-
-       /* if the DSP is busy we will TX messages after IRQ */
-       ipcx = sst_dsp_shim_read64_unlocked(byt->dsp, SST_IPCX);
-       if (ipcx & SST_BYT_IPCX_BUSY) {
-               spin_unlock_irqrestore(&byt->dsp->spinlock, flags);
-               return;
-       }
-
-       msg = list_first_entry(&byt->tx_list, struct ipc_message, list);
-
-       list_move(&msg->list, &byt->rx_list);
-
-       /* send the message */
-       if (msg->header & IPC_HEADER_LARGE(true))
-               sst_dsp_outbox_write(byt->dsp, msg->tx_data, msg->tx_size);
-       sst_dsp_shim_write64_unlocked(byt->dsp, SST_IPCX, msg->header);
-
-       spin_unlock_irqrestore(&byt->dsp->spinlock, flags);
-}
-
-static inline void sst_byt_tx_msg_reply_complete(struct sst_byt *byt,
-                                                struct ipc_message *msg)
-{
-       msg->complete = true;
-
-       if (!msg->wait)
-               list_add_tail(&msg->list, &byt->empty_list);
-       else
-               wake_up(&msg->waitq);
-}
-
-static void sst_byt_drop_all(struct sst_byt *byt)
-{
-       struct ipc_message *msg, *tmp;
-       unsigned long flags;
-
-       /* drop all TX and Rx messages before we stall + reset DSP */
-       spin_lock_irqsave(&byt->dsp->spinlock, flags);
-       list_for_each_entry_safe(msg, tmp, &byt->tx_list, list) {
-               list_move(&msg->list, &byt->empty_list);
-       }
-
-       list_for_each_entry_safe(msg, tmp, &byt->rx_list, list) {
-               list_move(&msg->list, &byt->empty_list);
-       }
-
-       spin_unlock_irqrestore(&byt->dsp->spinlock, flags);
-}
-
-static int sst_byt_tx_wait_done(struct sst_byt *byt, struct ipc_message *msg,
-                               void *rx_data)
-{
-       unsigned long flags;
-       int ret;
-
-       /* wait for DSP completion */
-       ret = wait_event_timeout(msg->waitq, msg->complete,
-                                msecs_to_jiffies(IPC_TIMEOUT_MSECS));
-
-       spin_lock_irqsave(&byt->dsp->spinlock, flags);
-       if (ret == 0) {
-               list_del(&msg->list);
-               sst_byt_ipc_shim_dbg(byt, "message timeout");
-
-               ret = -ETIMEDOUT;
-       } else {
-
-               /* copy the data returned from DSP */
-               if (msg->rx_size)
-                       memcpy(rx_data, msg->rx_data, msg->rx_size);
-               ret = msg->errno;
-       }
-
-       list_add_tail(&msg->list, &byt->empty_list);
-       spin_unlock_irqrestore(&byt->dsp->spinlock, flags);
-       return ret;
-}
-
-static int sst_byt_ipc_tx_message(struct sst_byt *byt, u64 header,
-                                 void *tx_data, size_t tx_bytes,
-                                 void *rx_data, size_t rx_bytes, int wait)
-{
-       unsigned long flags;
-       struct ipc_message *msg;
-
-       spin_lock_irqsave(&byt->dsp->spinlock, flags);
-
-       msg = sst_byt_msg_get_empty(byt);
-       if (msg == NULL) {
-               spin_unlock_irqrestore(&byt->dsp->spinlock, flags);
-               return -EBUSY;
-       }
-
-       msg->header = header;
-       msg->tx_size = tx_bytes;
-       msg->rx_size = rx_bytes;
-       msg->wait = wait;
-       msg->errno = 0;
-       msg->complete = false;
-
-       if (tx_bytes) {
-               /* msg content = lower 32-bit of the header + data */
-               *(u32 *)msg->tx_data = (u32)(header & (u32)-1);
-               memcpy(msg->tx_data + sizeof(u32), tx_data, tx_bytes);
-               msg->tx_size += sizeof(u32);
-       }
-
-       list_add_tail(&msg->list, &byt->tx_list);
-       spin_unlock_irqrestore(&byt->dsp->spinlock, flags);
-
-       queue_kthread_work(&byt->kworker, &byt->kwork);
-
-       if (wait)
-               return sst_byt_tx_wait_done(byt, msg, rx_data);
-       else
-               return 0;
-}
-
-static inline int sst_byt_ipc_tx_msg_wait(struct sst_byt *byt, u64 header,
-                                         void *tx_data, size_t tx_bytes,
-                                         void *rx_data, size_t rx_bytes)
-{
-       return sst_byt_ipc_tx_message(byt, header, tx_data, tx_bytes,
-                                     rx_data, rx_bytes, 1);
-}
-
-static inline int sst_byt_ipc_tx_msg_nowait(struct sst_byt *byt, u64 header,
-                                               void *tx_data, size_t tx_bytes)
-{
-       return sst_byt_ipc_tx_message(byt, header, tx_data, tx_bytes,
-                                     NULL, 0, 0);
-}
-
-static struct ipc_message *sst_byt_reply_find_msg(struct sst_byt *byt,
-                                                 u64 header)
-{
-       struct ipc_message *msg = NULL, *_msg;
-       u64 mask;
-
-       /* match reply to message sent based on msg and stream IDs */
-       mask = IPC_HEADER_MSG_ID_MASK |
-              IPC_HEADER_STR_ID_MASK << IPC_HEADER_STR_ID_SHIFT;
-       header &= mask;
-
-       if (list_empty(&byt->rx_list)) {
-               dev_err(byt->dev,
-                       "ipc: rx list is empty but received 0x%llx\n", header);
-               goto out;
-       }
-
-       list_for_each_entry(_msg, &byt->rx_list, list) {
-               if ((_msg->header & mask) == header) {
-                       msg = _msg;
-                       break;
-               }
-       }
-
-out:
-       return msg;
-}
-
 static void sst_byt_stream_update(struct sst_byt *byt, struct ipc_message *msg)
 {
        struct sst_byt_stream *stream;
@@ -477,7 +251,7 @@ static int sst_byt_process_reply(struct sst_byt *byt, u64 header)
 {
        struct ipc_message *msg;
 
-       msg = sst_byt_reply_find_msg(byt, header);
+       msg = sst_ipc_reply_find_msg(&byt->ipc, header);
        if (msg == NULL)
                return 1;
 
@@ -491,7 +265,7 @@ static int sst_byt_process_reply(struct sst_byt *byt, u64 header)
 
        list_del(&msg->list);
        /* wake up */
-       sst_byt_tx_msg_reply_complete(byt, msg);
+       sst_ipc_tx_msg_reply_complete(&byt->ipc, msg);
 
        return 1;
 }
@@ -538,6 +312,7 @@ static irqreturn_t sst_byt_irq_thread(int irq, void *context)
 {
        struct sst_dsp *sst = (struct sst_dsp *) context;
        struct sst_byt *byt = sst_dsp_get_thread_context(sst);
+       struct sst_generic_ipc *ipc = &byt->ipc;
        u64 header;
        unsigned long flags;
 
@@ -569,7 +344,7 @@ static irqreturn_t sst_byt_irq_thread(int irq, void *context)
        spin_unlock_irqrestore(&sst->spinlock, flags);
 
        /* continue to send any remaining messages... */
-       queue_kthread_work(&byt->kworker, &byt->kwork);
+       queue_kthread_work(&ipc->kworker, &ipc->kwork);
 
        return IRQ_HANDLED;
 }
@@ -656,7 +431,8 @@ int sst_byt_stream_commit(struct sst_byt *byt, struct sst_byt_stream *stream)
        header = sst_byt_header(IPC_IA_ALLOC_STREAM,
                                sizeof(*str_req) + sizeof(u32),
                                true, stream->str_id);
-       ret = sst_byt_ipc_tx_msg_wait(byt, header, str_req, sizeof(*str_req),
+       ret = sst_ipc_tx_message_wait(&byt->ipc, header, str_req,
+                                     sizeof(*str_req),
                                      reply, sizeof(*reply));
        if (ret < 0) {
                dev_err(byt->dev, "ipc: error stream commit failed\n");
@@ -679,7 +455,7 @@ int sst_byt_stream_free(struct sst_byt *byt, struct sst_byt_stream *stream)
                goto out;
 
        header = sst_byt_header(IPC_IA_FREE_STREAM, 0, false, stream->str_id);
-       ret = sst_byt_ipc_tx_msg_wait(byt, header, NULL, 0, NULL, 0);
+       ret = sst_ipc_tx_message_wait(&byt->ipc, header, NULL, 0, NULL, 0);
        if (ret < 0) {
                dev_err(byt->dev, "ipc: free stream %d failed\n",
                        stream->str_id);
@@ -703,9 +479,11 @@ static int sst_byt_stream_operations(struct sst_byt *byt, int type,
 
        header = sst_byt_header(type, 0, false, stream_id);
        if (wait)
-               return sst_byt_ipc_tx_msg_wait(byt, header, NULL, 0, NULL, 0);
+               return sst_ipc_tx_message_wait(&byt->ipc, header, NULL,
+                                               0, NULL, 0);
        else
-               return sst_byt_ipc_tx_msg_nowait(byt, header, NULL, 0);
+               return sst_ipc_tx_message_nowait(&byt->ipc, header,
+                                               NULL, 0);
 }
 
 /* stream ALSA trigger operations */
@@ -725,7 +503,7 @@ int sst_byt_stream_start(struct sst_byt *byt, struct sst_byt_stream *stream,
        tx_msg = &start_stream;
        size = sizeof(start_stream);
 
-       ret = sst_byt_ipc_tx_msg_nowait(byt, header, tx_msg, size);
+       ret = sst_ipc_tx_message_nowait(&byt->ipc, header, tx_msg, size);
        if (ret < 0)
                dev_err(byt->dev, "ipc: error failed to start stream %d\n",
                        stream->str_id);
@@ -790,23 +568,6 @@ int sst_byt_get_dsp_position(struct sst_byt *byt,
        return do_div(fw_tstamp.ring_buffer_counter, buffer_size);
 }
 
-static int msg_empty_list_init(struct sst_byt *byt)
-{
-       struct ipc_message *msg;
-       int i;
-
-       byt->msg = kzalloc(sizeof(*msg) * IPC_EMPTY_LIST_SIZE, GFP_KERNEL);
-       if (byt->msg == NULL)
-               return -ENOMEM;
-
-       for (i = 0; i < IPC_EMPTY_LIST_SIZE; i++) {
-               init_waitqueue_head(&byt->msg[i].waitq);
-               list_add(&byt->msg[i].list, &byt->empty_list);
-       }
-
-       return 0;
-}
-
 struct sst_dsp *sst_byt_get_dsp(struct sst_byt *byt)
 {
        return byt->dsp;
@@ -823,7 +584,7 @@ int sst_byt_dsp_suspend_late(struct device *dev, struct sst_pdata *pdata)
 
        dev_dbg(byt->dev, "dsp reset\n");
        sst_dsp_reset(byt->dsp);
-       sst_byt_drop_all(byt);
+       sst_ipc_drop_all(&byt->ipc);
        dev_dbg(byt->dev, "dsp in reset\n");
 
        dev_dbg(byt->dev, "free all blocks and unload fw\n");
@@ -876,9 +637,52 @@ int sst_byt_dsp_wait_for_ready(struct device *dev, struct sst_pdata *pdata)
 }
 EXPORT_SYMBOL_GPL(sst_byt_dsp_wait_for_ready);
 
+static void byt_tx_msg(struct sst_generic_ipc *ipc, struct ipc_message *msg)
+{
+       if (msg->header & IPC_HEADER_LARGE(true))
+               sst_dsp_outbox_write(ipc->dsp, msg->tx_data, msg->tx_size);
+
+       sst_dsp_shim_write64_unlocked(ipc->dsp, SST_IPCX, msg->header);
+}
+
+static void byt_shim_dbg(struct sst_generic_ipc *ipc, const char *text)
+{
+       struct sst_dsp *sst = ipc->dsp;
+       u64 isr, ipcd, imrx, ipcx;
+
+       ipcx = sst_dsp_shim_read64_unlocked(sst, SST_IPCX);
+       isr = sst_dsp_shim_read64_unlocked(sst, SST_ISRX);
+       ipcd = sst_dsp_shim_read64_unlocked(sst, SST_IPCD);
+       imrx = sst_dsp_shim_read64_unlocked(sst, SST_IMRX);
+
+       dev_err(ipc->dev,
+               "ipc: --%s-- ipcx 0x%llx isr 0x%llx ipcd 0x%llx imrx 0x%llx\n",
+               text, ipcx, isr, ipcd, imrx);
+}
+
+static void byt_tx_data_copy(struct ipc_message *msg, char *tx_data,
+       size_t tx_size)
+{
+       /* msg content = lower 32-bit of the header + data */
+       *(u32 *)msg->tx_data = (u32)(msg->header & (u32)-1);
+       memcpy(msg->tx_data + sizeof(u32), tx_data, tx_size);
+       msg->tx_size += sizeof(u32);
+}
+
+static u64 byt_reply_msg_match(u64 header, u64 *mask)
+{
+       /* match reply to message sent based on msg and stream IDs */
+       *mask = IPC_HEADER_MSG_ID_MASK |
+              IPC_HEADER_STR_ID_MASK << IPC_HEADER_STR_ID_SHIFT;
+       header &= *mask;
+
+       return header;
+}
+
 int sst_byt_dsp_init(struct device *dev, struct sst_pdata *pdata)
 {
        struct sst_byt *byt;
+       struct sst_generic_ipc *ipc;
        struct sst_fw *byt_sst_fw;
        struct sst_byt_fw_init init;
        int err;
@@ -889,39 +693,30 @@ int sst_byt_dsp_init(struct device *dev, struct sst_pdata *pdata)
        if (byt == NULL)
                return -ENOMEM;
 
-       byt->dev = dev;
-       INIT_LIST_HEAD(&byt->stream_list);
-       INIT_LIST_HEAD(&byt->tx_list);
-       INIT_LIST_HEAD(&byt->rx_list);
-       INIT_LIST_HEAD(&byt->empty_list);
-       init_waitqueue_head(&byt->boot_wait);
-       init_waitqueue_head(&byt->wait_txq);
+       ipc = &byt->ipc;
+       ipc->dev = dev;
+       ipc->ops.tx_msg = byt_tx_msg;
+       ipc->ops.shim_dbg = byt_shim_dbg;
+       ipc->ops.tx_data_copy = byt_tx_data_copy;
+       ipc->ops.reply_msg_match = byt_reply_msg_match;
 
-       err = msg_empty_list_init(byt);
-       if (err < 0)
-               return -ENOMEM;
-
-       /* start the IPC message thread */
-       init_kthread_worker(&byt->kworker);
-       byt->tx_thread = kthread_run(kthread_worker_fn,
-                                    &byt->kworker, "%s",
-                                    dev_name(byt->dev));
-       if (IS_ERR(byt->tx_thread)) {
-               err = PTR_ERR(byt->tx_thread);
-               dev_err(byt->dev, "error failed to create message TX task\n");
-               goto err_free_msg;
-       }
-       init_kthread_work(&byt->kwork, sst_byt_ipc_tx_msgs);
+       err = sst_ipc_init(ipc);
+       if (err != 0)
+               goto ipc_init_err;
 
+       INIT_LIST_HEAD(&byt->stream_list);
+       init_waitqueue_head(&byt->boot_wait);
        byt_dev.thread_context = byt;
 
        /* init SST shim */
        byt->dsp = sst_dsp_new(dev, &byt_dev, pdata);
        if (byt->dsp == NULL) {
                err = -ENODEV;
-               goto dsp_err;
+               goto dsp_new_err;
        }
 
+       ipc->dsp = byt->dsp;
+
        /* keep the DSP in reset state for base FW loading */
        sst_dsp_reset(byt->dsp);
 
@@ -961,10 +756,10 @@ boot_err:
        sst_fw_free(byt_sst_fw);
 fw_err:
        sst_dsp_free(byt->dsp);
-dsp_err:
-       kthread_stop(byt->tx_thread);
-err_free_msg:
-       kfree(byt->msg);
+dsp_new_err:
+       sst_ipc_fini(ipc);
+ipc_init_err:
+       kfree(byt);
 
        return err;
 }
@@ -977,7 +772,6 @@ void sst_byt_dsp_free(struct device *dev, struct sst_pdata *pdata)
        sst_dsp_reset(byt->dsp);
        sst_fw_free_all(byt->dsp);
        sst_dsp_free(byt->dsp);
-       kthread_stop(byt->tx_thread);
-       kfree(byt->msg);
+       sst_ipc_fini(&byt->ipc);
 }
 EXPORT_SYMBOL_GPL(sst_byt_dsp_free);
similarity index 99%
rename from sound/soc/intel/sst-baytrail-pcm.c
rename to sound/soc/intel/baytrail/sst-baytrail-pcm.c
index 224c49c9f1354200672e2a3cf782dfbb16b6aa7f..79547bec558bc218c505be5a68321497c9dfd70e 100644 (file)
@@ -20,8 +20,8 @@
 #include <sound/pcm_params.h>
 #include <sound/soc.h>
 #include "sst-baytrail-ipc.h"
-#include "sst-dsp-priv.h"
-#include "sst-dsp.h"
+#include "../common/sst-dsp-priv.h"
+#include "../common/sst-dsp.h"
 
 #define BYT_PCM_COUNT          2
 
diff --git a/sound/soc/intel/boards/Makefile b/sound/soc/intel/boards/Makefile
new file mode 100644 (file)
index 0000000..f8237f0
--- /dev/null
@@ -0,0 +1,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-rt5640-objs := bytcr_rt5640.o
+snd-soc-sst-cht-bsw-rt5672-objs := cht_bsw_rt5672.o
+snd-soc-sst-cht-bsw-rt5645-objs := cht_bsw_rt5645.o
+
+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-rt5640.o
+obj-$(CONFIG_SND_SOC_INTEL_CHT_BSW_RT5672_MACH) += snd-soc-sst-cht-bsw-rt5672.o
+obj-$(CONFIG_SND_SOC_INTEL_CHT_BSW_RT5645_MACH) += snd-soc-sst-cht-bsw-rt5645.o
similarity index 89%
rename from sound/soc/intel/broadwell.c
rename to sound/soc/intel/boards/broadwell.c
index fc5542034b9bf5a38afbbc1383e56c104a074781..8bafaf6ceab1266f6b85a0d6ca1e4ee04bc5046f 100644 (file)
 #include <sound/jack.h>
 #include <sound/pcm_params.h>
 
-#include "sst-dsp.h"
-#include "sst-haswell-ipc.h"
+#include "../common/sst-dsp.h"
+#include "../haswell/sst-haswell-ipc.h"
 
-#include "../codecs/rt286.h"
+#include "../../codecs/rt286.h"
 
 static struct snd_soc_jack broadwell_headset;
 /* Headset jack detection DAPM pins */
@@ -219,6 +219,32 @@ static struct snd_soc_dai_link broadwell_rt286_dais[] = {
        },
 };
 
+static int broadwell_suspend(struct snd_soc_card *card){
+       struct snd_soc_codec *codec;
+
+       list_for_each_entry(codec, &card->codec_dev_list, card_list) {
+               if (!strcmp(codec->component.name, "i2c-INT343A:00")) {
+                       dev_dbg(codec->dev, "disabling jack detect before going to suspend.\n");
+                       rt286_mic_detect(codec, NULL);
+                       break;
+               }
+       }
+       return 0;
+}
+
+static int broadwell_resume(struct snd_soc_card *card){
+       struct snd_soc_codec *codec;
+
+       list_for_each_entry(codec, &card->codec_dev_list, card_list) {
+               if (!strcmp(codec->component.name, "i2c-INT343A:00")) {
+                       dev_dbg(codec->dev, "enabling jack detect for resume.\n");
+                       rt286_mic_detect(codec, &broadwell_headset);
+                       break;
+               }
+       }
+       return 0;
+}
+
 /* broadwell audio machine driver for WPT + RT286S */
 static struct snd_soc_card broadwell_rt286 = {
        .name = "broadwell-rt286",
@@ -232,6 +258,8 @@ static struct snd_soc_card broadwell_rt286 = {
        .dapm_routes = broadwell_rt286_map,
        .num_dapm_routes = ARRAY_SIZE(broadwell_rt286_map),
        .fully_routed = true,
+       .suspend_pre = broadwell_suspend,
+       .resume_post = broadwell_resume,
 };
 
 static int broadwell_audio_probe(struct platform_device *pdev)
similarity index 99%
rename from sound/soc/intel/byt-max98090.c
rename to sound/soc/intel/boards/byt-max98090.c
index d8b1f038da1c41ac9674b94efb072ca1e5c24f41..7ab8cc9fbfd53b33be8ad519a0d5ab5672c632c8 100644 (file)
@@ -24,7 +24,7 @@
 #include <sound/pcm_params.h>
 #include <sound/soc.h>
 #include <sound/jack.h>
-#include "../codecs/max98090.h"
+#include "../../codecs/max98090.h"
 
 struct byt_max98090_private {
        struct snd_soc_jack jack;
similarity index 98%
rename from sound/soc/intel/byt-rt5640.c
rename to sound/soc/intel/boards/byt-rt5640.c
index 354eaad886e1c09619b0d1e2f1269b9ef715aa3a..ae89b9b966d9ff3e46101777355044b2694e8815 100644 (file)
@@ -23,9 +23,9 @@
 #include <sound/pcm_params.h>
 #include <sound/soc.h>
 #include <sound/jack.h>
-#include "../codecs/rt5640.h"
+#include "../../codecs/rt5640.h"
 
-#include "sst-dsp.h"
+#include "../common/sst-dsp.h"
 
 static const struct snd_soc_dapm_widget byt_rt5640_widgets[] = {
        SND_SOC_DAPM_HP("Headphone", NULL),
similarity index 98%
rename from sound/soc/intel/bytcr_dpcm_rt5640.c
rename to sound/soc/intel/boards/bytcr_rt5640.c
index 3b262d01c1b370b9166d8e152486874d26f2aebd..7f55d59024a88bd7b53ddd6f66a00fc8adeeaabd 100644 (file)
@@ -26,8 +26,8 @@
 #include <sound/pcm.h>
 #include <sound/pcm_params.h>
 #include <sound/soc.h>
-#include "../codecs/rt5640.h"
-#include "sst-atom-controls.h"
+#include "../../codecs/rt5640.h"
+#include "../atom/sst-atom-controls.h"
 
 static const struct snd_soc_dapm_widget byt_dapm_widgets[] = {
        SND_SOC_DAPM_HP("Headphone", NULL),
similarity index 99%
rename from sound/soc/intel/cht_bsw_rt5645.c
rename to sound/soc/intel/boards/cht_bsw_rt5645.c
index 012227997ed9ca07a64311597fb07aeeeaa4199a..20a28b22e30fea84853f798f960130cbdf99bf7f 100644 (file)
@@ -27,8 +27,8 @@
 #include <sound/pcm_params.h>
 #include <sound/soc.h>
 #include <sound/jack.h>
-#include "../codecs/rt5645.h"
-#include "sst-atom-controls.h"
+#include "../../codecs/rt5645.h"
+#include "../atom/sst-atom-controls.h"
 
 #define CHT_PLAT_CLK_3_HZ      19200000
 #define CHT_CODEC_DAI  "rt5645-aif1"
similarity index 76%
rename from sound/soc/intel/cht_bsw_rt5672.c
rename to sound/soc/intel/boards/cht_bsw_rt5672.c
index bc8dcacd5e6a08d2a4e0093c5f4349699e6d3a9d..2c9cc5be439e654bccb875df435eb651cf25163f 100644 (file)
 #include <sound/pcm.h>
 #include <sound/pcm_params.h>
 #include <sound/soc.h>
-#include "../codecs/rt5670.h"
-#include "sst-atom-controls.h"
+#include <sound/jack.h>
+#include "../../codecs/rt5670.h"
+#include "../atom/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 struct snd_soc_jack cht_bsw_headset;
+
+/* Headset jack detection DAPM pins */
+static struct snd_soc_jack_pin cht_bsw_headset_pins[] = {
+       {
+               .pin = "Headset Mic",
+               .mask = SND_JACK_MICROPHONE,
+       },
+       {
+               .pin = "Headphone",
+               .mask = SND_JACK_HEADPHONE,
+       },
+};
+
 static inline struct snd_soc_dai *cht_get_codec_dai(struct snd_soc_card *card)
 {
        int i;
@@ -50,6 +65,7 @@ static int platform_clock_control(struct snd_soc_dapm_widget *w,
        struct snd_soc_dapm_context *dapm = w->dapm;
        struct snd_soc_card *card = dapm->card;
        struct snd_soc_dai *codec_dai;
+       int ret;
 
        codec_dai = cht_get_codec_dai(card);
        if (!codec_dai) {
@@ -57,17 +73,31 @@ static int platform_clock_control(struct snd_soc_dapm_widget *w,
                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);
-
+       if (SND_SOC_DAPM_EVENT_ON(event)) {
+               /* 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, 48000 * 512);
+               if (ret < 0) {
+                       dev_err(card->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,
+                       48000 * 512, SND_SOC_CLOCK_IN);
+               if (ret < 0) {
+                       dev_err(card->dev, "can't set codec sysclk: %d\n", ret);
+                       return ret;
+               }
+       } else {
+               /* 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,
+                                      48000 * 512, SND_SOC_CLOCK_IN);
+       }
        return 0;
 }
 
@@ -77,7 +107,8 @@ static const struct snd_soc_dapm_widget cht_dapm_widgets[] = {
        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),
+                       platform_clock_control, SND_SOC_DAPM_PRE_PMU |
+                       SND_SOC_DAPM_POST_PMD),
 };
 
 static const struct snd_soc_dapm_route cht_audio_map[] = {
@@ -162,6 +193,15 @@ static int cht_codec_init(struct snd_soc_pcm_runtime *runtime)
                                | RT5670_AD_MONO_L_FILTER
                                | RT5670_AD_MONO_R_FILTER,
                                RT5670_CLK_SEL_I2S1_ASRC);
+
+        ret = snd_soc_card_jack_new(runtime->card, "Headset",
+                SND_JACK_HEADSET | SND_JACK_BTN_0 |
+                SND_JACK_BTN_1 | SND_JACK_BTN_2, &cht_bsw_headset,
+                cht_bsw_headset_pins, ARRAY_SIZE(cht_bsw_headset_pins));
+        if (ret)
+                return ret;
+
+       rt5670_set_jack_detect(codec, &cht_bsw_headset);
        return 0;
 }
 
@@ -251,6 +291,35 @@ static struct snd_soc_dai_link cht_dailink[] = {
        },
 };
 
+static int cht_suspend_pre(struct snd_soc_card *card)
+{
+       struct snd_soc_codec *codec;
+
+       list_for_each_entry(codec, &card->codec_dev_list, card_list) {
+               if (!strcmp(codec->component.name, "i2c-10EC5670:00")) {
+                       dev_dbg(codec->dev, "disabling jack detect before going to suspend.\n");
+                       rt5670_jack_suspend(codec);
+                       break;
+               }
+       }
+       return 0;
+}
+
+static int cht_resume_post(struct snd_soc_card *card)
+{
+       struct snd_soc_codec *codec;
+
+       list_for_each_entry(codec, &card->codec_dev_list, card_list) {
+               if (!strcmp(codec->component.name, "i2c-10EC5670:00")) {
+                       dev_dbg(codec->dev, "enabling jack detect for resume.\n");
+                       rt5670_jack_resume(codec);
+                       break;
+               }
+       }
+
+       return 0;
+}
+
 /* SoC card */
 static struct snd_soc_card snd_soc_card_cht = {
        .name = "cherrytrailcraudio",
@@ -262,6 +331,8 @@ static struct snd_soc_card snd_soc_card_cht = {
        .num_dapm_routes = ARRAY_SIZE(cht_audio_map),
        .controls = cht_mc_controls,
        .num_controls = ARRAY_SIZE(cht_mc_controls),
+       .suspend_pre = cht_suspend_pre,
+       .resume_post = cht_resume_post,
 };
 
 static int snd_cht_mc_probe(struct platform_device *pdev)
similarity index 98%
rename from sound/soc/intel/haswell.c
rename to sound/soc/intel/boards/haswell.c
index 00fddd3f5dfb8f7350180b8b564d282d4918331b..22558572cb9ca12c8ae9e60d940473db35cb2434 100644 (file)
 #include <sound/soc.h>
 #include <sound/pcm_params.h>
 
-#include "sst-dsp.h"
-#include "sst-haswell-ipc.h"
+#include "../common/sst-dsp.h"
+#include "../haswell/sst-haswell-ipc.h"
 
-#include "../codecs/rt5640.h"
+#include "../../codecs/rt5640.h"
 
 /* Haswell ULT platforms have a Headphone and Mic jack */
 static const struct snd_soc_dapm_widget haswell_widgets[] = {
diff --git a/sound/soc/intel/common/Makefile b/sound/soc/intel/common/Makefile
new file mode 100644 (file)
index 0000000..f24154c
--- /dev/null
@@ -0,0 +1,7 @@
+snd-soc-sst-dsp-objs := sst-dsp.o sst-firmware.o
+snd-soc-sst-acpi-objs := sst-acpi.o
+snd-soc-sst-ipc-objs := sst-ipc.o
+
+obj-$(CONFIG_SND_SOC_INTEL_SST) += snd-soc-sst-dsp.o snd-soc-sst-ipc.o
+obj-$(CONFIG_SND_SOC_INTEL_SST_ACPI) += snd-soc-sst-acpi.o
+
similarity index 97%
rename from sound/soc/intel/sst-dsp-priv.h
rename to sound/soc/intel/common/sst-dsp-priv.h
index b9da030e312dc66339e48baab8f17d082b169a0c..396d54510350ed8e2cac6630ee6878f53e93825f 100644 (file)
@@ -172,6 +172,16 @@ struct sst_module_runtime_context {
        u32 *buffer;
 };
 
+/*
+ * Audio DSP Module State
+ */
+enum sst_module_state {
+       SST_MODULE_STATE_UNLOADED = 0,  /* default state */
+       SST_MODULE_STATE_LOADED,
+       SST_MODULE_STATE_INITIALIZED,   /* and inactive */
+       SST_MODULE_STATE_ACTIVE,
+};
+
 /*
  * Audio DSP Generic Module.
  *
@@ -203,6 +213,9 @@ struct sst_module {
        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*/
+
+       /* state */
+       enum sst_module_state state;
 };
 
 /*
similarity index 99%
rename from sound/soc/intel/sst-dsp.h
rename to sound/soc/intel/common/sst-dsp.h
index 148d8c589a43f6b03ad87c6af43adb0fd58c016e..96aeb2556ad40d3d4e8bc35f9e180555d8d72873 100644 (file)
@@ -28,7 +28,6 @@
 
 /* Supported SST DMA Devices */
 #define SST_DMA_TYPE_DW                1
-#define SST_DMA_TYPE_MID       2
 
 /* autosuspend delay 5s*/
 #define SST_RUNTIME_SUSPEND_DELAY      (5 * 1000)
similarity index 99%
rename from sound/soc/intel/sst-firmware.c
rename to sound/soc/intel/common/sst-firmware.c
index 4a993d16a23559384e2a862c73c853b4849fcb09..ebcca6dc48d189eb16b8903683db5460a3955357 100644 (file)
@@ -221,8 +221,6 @@ int sst_dsp_dma_get_channel(struct sst_dsp *dsp, int chan_id)
        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);
@@ -281,9 +279,6 @@ int sst_dma_new(struct sst_dsp *sst)
        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);
@@ -502,6 +497,7 @@ struct sst_module *sst_module_new(struct sst_fw *sst_fw,
        sst_module->scratch_size = template->scratch_size;
        sst_module->persistent_size = template->persistent_size;
        sst_module->entry = template->entry;
+       sst_module->state = SST_MODULE_STATE_UNLOADED;
 
        INIT_LIST_HEAD(&sst_module->block_list);
        INIT_LIST_HEAD(&sst_module->runtime_list);
diff --git a/sound/soc/intel/common/sst-ipc.c b/sound/soc/intel/common/sst-ipc.c
new file mode 100644 (file)
index 0000000..4b62a55
--- /dev/null
@@ -0,0 +1,294 @@
+/*
+ * Intel SST generic IPC Support
+ *
+ * Copyright (C) 2015, Intel Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License version
+ * 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/list.h>
+#include <linux/wait.h>
+#include <linux/module.h>
+#include <linux/spinlock.h>
+#include <linux/device.h>
+#include <linux/slab.h>
+#include <linux/workqueue.h>
+#include <linux/sched.h>
+#include <linux/delay.h>
+#include <linux/platform_device.h>
+#include <linux/kthread.h>
+#include <sound/asound.h>
+
+#include "sst-dsp.h"
+#include "sst-dsp-priv.h"
+#include "sst-ipc.h"
+
+/* IPC message timeout (msecs) */
+#define IPC_TIMEOUT_MSECS      300
+
+#define IPC_EMPTY_LIST_SIZE    8
+
+/* locks held by caller */
+static struct ipc_message *msg_get_empty(struct sst_generic_ipc *ipc)
+{
+       struct ipc_message *msg = NULL;
+
+       if (!list_empty(&ipc->empty_list)) {
+               msg = list_first_entry(&ipc->empty_list, struct ipc_message,
+                       list);
+               list_del(&msg->list);
+       }
+
+       return msg;
+}
+
+static int tx_wait_done(struct sst_generic_ipc *ipc,
+       struct ipc_message *msg, void *rx_data)
+{
+       unsigned long flags;
+       int ret;
+
+       /* wait for DSP completion (in all cases atm inc pending) */
+       ret = wait_event_timeout(msg->waitq, msg->complete,
+               msecs_to_jiffies(IPC_TIMEOUT_MSECS));
+
+       spin_lock_irqsave(&ipc->dsp->spinlock, flags);
+       if (ret == 0) {
+               if (ipc->ops.shim_dbg != NULL)
+                       ipc->ops.shim_dbg(ipc, "message timeout");
+
+               list_del(&msg->list);
+               ret = -ETIMEDOUT;
+       } else {
+
+               /* copy the data returned from DSP */
+               if (msg->rx_size)
+                       memcpy(rx_data, msg->rx_data, msg->rx_size);
+               ret = msg->errno;
+       }
+
+       list_add_tail(&msg->list, &ipc->empty_list);
+       spin_unlock_irqrestore(&ipc->dsp->spinlock, flags);
+       return ret;
+}
+
+static int ipc_tx_message(struct sst_generic_ipc *ipc, u64 header,
+       void *tx_data, size_t tx_bytes, void *rx_data,
+       size_t rx_bytes, int wait)
+{
+       struct ipc_message *msg;
+       unsigned long flags;
+
+       spin_lock_irqsave(&ipc->dsp->spinlock, flags);
+
+       msg = msg_get_empty(ipc);
+       if (msg == NULL) {
+               spin_unlock_irqrestore(&ipc->dsp->spinlock, flags);
+               return -EBUSY;
+       }
+
+       msg->header = header;
+       msg->tx_size = tx_bytes;
+       msg->rx_size = rx_bytes;
+       msg->wait = wait;
+       msg->errno = 0;
+       msg->pending = false;
+       msg->complete = false;
+
+       if ((tx_bytes) && (ipc->ops.tx_data_copy != NULL))
+               ipc->ops.tx_data_copy(msg, tx_data, tx_bytes);
+
+       list_add_tail(&msg->list, &ipc->tx_list);
+       spin_unlock_irqrestore(&ipc->dsp->spinlock, flags);
+
+       queue_kthread_work(&ipc->kworker, &ipc->kwork);
+
+       if (wait)
+               return tx_wait_done(ipc, msg, rx_data);
+       else
+               return 0;
+}
+
+static int msg_empty_list_init(struct sst_generic_ipc *ipc)
+{
+       int i;
+
+       ipc->msg = kzalloc(sizeof(struct ipc_message) *
+               IPC_EMPTY_LIST_SIZE, GFP_KERNEL);
+       if (ipc->msg == NULL)
+               return -ENOMEM;
+
+       for (i = 0; i < IPC_EMPTY_LIST_SIZE; i++) {
+               init_waitqueue_head(&ipc->msg[i].waitq);
+               list_add(&ipc->msg[i].list, &ipc->empty_list);
+       }
+
+       return 0;
+}
+
+static void ipc_tx_msgs(struct kthread_work *work)
+{
+       struct sst_generic_ipc *ipc =
+               container_of(work, struct sst_generic_ipc, kwork);
+       struct ipc_message *msg;
+       unsigned long flags;
+       u64 ipcx;
+
+       spin_lock_irqsave(&ipc->dsp->spinlock, flags);
+
+       if (list_empty(&ipc->tx_list) || ipc->pending) {
+               spin_unlock_irqrestore(&ipc->dsp->spinlock, flags);
+               return;
+       }
+
+       /* if the DSP is busy, we will TX messages after IRQ.
+        * also postpone if we are in the middle of procesing completion irq*/
+       ipcx = sst_dsp_shim_read_unlocked(ipc->dsp, SST_IPCX);
+       if (ipcx & (SST_IPCX_BUSY | SST_IPCX_DONE)) {
+               spin_unlock_irqrestore(&ipc->dsp->spinlock, flags);
+               return;
+       }
+
+       msg = list_first_entry(&ipc->tx_list, struct ipc_message, list);
+       list_move(&msg->list, &ipc->rx_list);
+
+       if (ipc->ops.tx_msg != NULL)
+               ipc->ops.tx_msg(ipc, msg);
+
+       spin_unlock_irqrestore(&ipc->dsp->spinlock, flags);
+}
+
+int sst_ipc_tx_message_wait(struct sst_generic_ipc *ipc, u64 header,
+       void *tx_data, size_t tx_bytes, void *rx_data, size_t rx_bytes)
+{
+       return ipc_tx_message(ipc, header, tx_data, tx_bytes,
+               rx_data, rx_bytes, 1);
+}
+EXPORT_SYMBOL_GPL(sst_ipc_tx_message_wait);
+
+int sst_ipc_tx_message_nowait(struct sst_generic_ipc *ipc, u64 header,
+       void *tx_data, size_t tx_bytes)
+{
+       return ipc_tx_message(ipc, header, tx_data, tx_bytes,
+               NULL, 0, 0);
+}
+EXPORT_SYMBOL_GPL(sst_ipc_tx_message_nowait);
+
+struct ipc_message *sst_ipc_reply_find_msg(struct sst_generic_ipc *ipc,
+       u64 header)
+{
+       struct ipc_message *msg;
+       u64 mask;
+
+       if (ipc->ops.reply_msg_match != NULL)
+               header = ipc->ops.reply_msg_match(header, &mask);
+
+       if (list_empty(&ipc->rx_list)) {
+               dev_err(ipc->dev, "error: rx list empty but received 0x%llx\n",
+                       header);
+               return NULL;
+       }
+
+       list_for_each_entry(msg, &ipc->rx_list, list) {
+               if ((msg->header & mask) == header)
+                       return msg;
+       }
+
+       return NULL;
+}
+EXPORT_SYMBOL_GPL(sst_ipc_reply_find_msg);
+
+/* locks held by caller */
+void sst_ipc_tx_msg_reply_complete(struct sst_generic_ipc *ipc,
+       struct ipc_message *msg)
+{
+       msg->complete = true;
+
+       if (!msg->wait)
+               list_add_tail(&msg->list, &ipc->empty_list);
+       else
+               wake_up(&msg->waitq);
+}
+EXPORT_SYMBOL_GPL(sst_ipc_tx_msg_reply_complete);
+
+void sst_ipc_drop_all(struct sst_generic_ipc *ipc)
+{
+       struct ipc_message *msg, *tmp;
+       unsigned long flags;
+       int tx_drop_cnt = 0, rx_drop_cnt = 0;
+
+       /* drop all TX and Rx messages before we stall + reset DSP */
+       spin_lock_irqsave(&ipc->dsp->spinlock, flags);
+
+       list_for_each_entry_safe(msg, tmp, &ipc->tx_list, list) {
+               list_move(&msg->list, &ipc->empty_list);
+               tx_drop_cnt++;
+       }
+
+       list_for_each_entry_safe(msg, tmp, &ipc->rx_list, list) {
+               list_move(&msg->list, &ipc->empty_list);
+               rx_drop_cnt++;
+       }
+
+       spin_unlock_irqrestore(&ipc->dsp->spinlock, flags);
+
+       if (tx_drop_cnt || rx_drop_cnt)
+               dev_err(ipc->dev, "dropped IPC msg RX=%d, TX=%d\n",
+                       tx_drop_cnt, rx_drop_cnt);
+}
+EXPORT_SYMBOL_GPL(sst_ipc_drop_all);
+
+int sst_ipc_init(struct sst_generic_ipc *ipc)
+{
+       int ret;
+
+       INIT_LIST_HEAD(&ipc->tx_list);
+       INIT_LIST_HEAD(&ipc->rx_list);
+       INIT_LIST_HEAD(&ipc->empty_list);
+       init_waitqueue_head(&ipc->wait_txq);
+
+       ret = msg_empty_list_init(ipc);
+       if (ret < 0)
+               return -ENOMEM;
+
+       /* start the IPC message thread */
+       init_kthread_worker(&ipc->kworker);
+       ipc->tx_thread = kthread_run(kthread_worker_fn,
+                                       &ipc->kworker, "%s",
+                                       dev_name(ipc->dev));
+       if (IS_ERR(ipc->tx_thread)) {
+               dev_err(ipc->dev, "error: failed to create message TX task\n");
+               ret = PTR_ERR(ipc->tx_thread);
+               kfree(ipc->msg);
+               return ret;
+       }
+
+       init_kthread_work(&ipc->kwork, ipc_tx_msgs);
+       return 0;
+}
+EXPORT_SYMBOL_GPL(sst_ipc_init);
+
+void sst_ipc_fini(struct sst_generic_ipc *ipc)
+{
+       if (ipc->tx_thread)
+               kthread_stop(ipc->tx_thread);
+
+       if (ipc->msg)
+               kfree(ipc->msg);
+}
+EXPORT_SYMBOL_GPL(sst_ipc_fini);
+
+/* Module information */
+MODULE_AUTHOR("Jin Yao");
+MODULE_DESCRIPTION("Intel SST IPC generic");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/intel/common/sst-ipc.h b/sound/soc/intel/common/sst-ipc.h
new file mode 100644 (file)
index 0000000..125ea45
--- /dev/null
@@ -0,0 +1,91 @@
+/*
+ * Intel SST generic IPC Support
+ *
+ * Copyright (C) 2015, Intel Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License version
+ * 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#ifndef __SST_GENERIC_IPC_H
+#define __SST_GENERIC_IPC_H
+
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/wait.h>
+#include <linux/list.h>
+#include <linux/workqueue.h>
+#include <linux/sched.h>
+#include <linux/kthread.h>
+
+#define IPC_MAX_MAILBOX_BYTES  256
+
+struct ipc_message {
+       struct list_head list;
+       u64 header;
+
+       /* direction wrt host CPU */
+       char tx_data[IPC_MAX_MAILBOX_BYTES];
+       size_t tx_size;
+       char rx_data[IPC_MAX_MAILBOX_BYTES];
+       size_t rx_size;
+
+       wait_queue_head_t waitq;
+       bool pending;
+       bool complete;
+       bool wait;
+       int errno;
+};
+
+struct sst_generic_ipc;
+
+struct sst_plat_ipc_ops {
+       void (*tx_msg)(struct sst_generic_ipc *, struct ipc_message *);
+       void (*shim_dbg)(struct sst_generic_ipc *, const char *);
+       void (*tx_data_copy)(struct ipc_message *, char *, size_t);
+       u64  (*reply_msg_match)(u64 header, u64 *mask);
+};
+
+/* SST generic IPC data */
+struct sst_generic_ipc {
+       struct device *dev;
+       struct sst_dsp *dsp;
+
+       /* IPC messaging */
+       struct list_head tx_list;
+       struct list_head rx_list;
+       struct list_head empty_list;
+       wait_queue_head_t wait_txq;
+       struct task_struct *tx_thread;
+       struct kthread_worker kworker;
+       struct kthread_work kwork;
+       bool pending;
+       struct ipc_message *msg;
+
+       struct sst_plat_ipc_ops ops;
+};
+
+int sst_ipc_tx_message_wait(struct sst_generic_ipc *ipc, u64 header,
+       void *tx_data, size_t tx_bytes, void *rx_data, size_t rx_bytes);
+
+int sst_ipc_tx_message_nowait(struct sst_generic_ipc *ipc, u64 header,
+       void *tx_data, size_t tx_bytes);
+
+struct ipc_message *sst_ipc_reply_find_msg(struct sst_generic_ipc *ipc,
+       u64 header);
+
+void sst_ipc_tx_msg_reply_complete(struct sst_generic_ipc *ipc,
+       struct ipc_message *msg);
+
+void sst_ipc_drop_all(struct sst_generic_ipc *ipc);
+int sst_ipc_init(struct sst_generic_ipc *ipc);
+void sst_ipc_fini(struct sst_generic_ipc *ipc);
+
+#endif
diff --git a/sound/soc/intel/haswell/Makefile b/sound/soc/intel/haswell/Makefile
new file mode 100644 (file)
index 0000000..9c17231
--- /dev/null
@@ -0,0 +1,4 @@
+snd-soc-sst-haswell-pcm-objs := \
+               sst-haswell-ipc.o sst-haswell-pcm.o sst-haswell-dsp.o
+
+obj-$(CONFIG_SND_SOC_INTEL_HASWELL) += snd-soc-sst-haswell-pcm.o
similarity index 98%
rename from sound/soc/intel/sst-haswell-dsp.c
rename to sound/soc/intel/haswell/sst-haswell-dsp.c
index 402b728c0a06479213425c2daf88eafe4caa2877..7f94920c8a4d85c33734f71a0980c85614a5d5c2 100644 (file)
@@ -28,9 +28,9 @@
 #include <linux/firmware.h>
 #include <linux/pm_runtime.h>
 
-#include "sst-dsp.h"
-#include "sst-dsp-priv.h"
-#include "sst-haswell-ipc.h"
+#include "../common/sst-dsp.h"
+#include "../common/sst-dsp-priv.h"
+#include "../haswell/sst-haswell-ipc.h"
 
 #include <trace/events/hswadsp.h>
 
@@ -100,6 +100,7 @@ static int hsw_parse_module(struct sst_dsp *dsp, struct sst_fw *fw,
                && module->type != SST_HSW_MODULE_PCM
                && module->type != SST_HSW_MODULE_PCM_REFERENCE
                && module->type != SST_HSW_MODULE_PCM_CAPTURE
+               && module->type != SST_HSW_MODULE_WAVES
                && module->type != SST_HSW_MODULE_LPAL)
                return 0;
 
@@ -139,6 +140,7 @@ static int hsw_parse_module(struct sst_dsp *dsp, struct sst_fw *fw,
                        mod->type = SST_MEM_IRAM;
                        break;
                case SST_HSW_DRAM:
+               case SST_HSW_REGS:
                        ram = dsp->addr.lpe;
                        mod->offset = block->ram_offset;
                        mod->type = SST_MEM_DRAM;
@@ -169,6 +171,7 @@ static int hsw_parse_module(struct sst_dsp *dsp, struct sst_fw *fw,
 
                block = (void *)block + sizeof(*block) + block->size;
        }
+       mod->state = SST_MODULE_STATE_LOADED;
 
        return 0;
 }
similarity index 77%
rename from sound/soc/intel/sst-haswell-ipc.c
rename to sound/soc/intel/haswell/sst-haswell-ipc.c
index 863a9ca34b8e37fee393930812845e672e717b8f..344a1e9bbce5794311ec95a24143581ebf715907 100644 (file)
@@ -34,8 +34,9 @@
 #include <sound/asound.h>
 
 #include "sst-haswell-ipc.h"
-#include "sst-dsp.h"
-#include "sst-dsp-priv.h"
+#include "../common/sst-dsp.h"
+#include "../common/sst-dsp-priv.h"
+#include "../common/sst-ipc.h"
 
 /* Global Message - Generic */
 #define IPC_GLB_TYPE_SHIFT     24
 #define IPC_LOG_ID_MASK                (0xf << IPC_LOG_ID_SHIFT)
 #define IPC_LOG_ID(x)          (x << IPC_LOG_ID_SHIFT)
 
+/* Module Message */
+#define IPC_MODULE_OPERATION_SHIFT     20
+#define IPC_MODULE_OPERATION_MASK      (0xf << IPC_MODULE_OPERATION_SHIFT)
+#define IPC_MODULE_OPERATION(x)        (x << IPC_MODULE_OPERATION_SHIFT)
+
+#define IPC_MODULE_ID_SHIFT    16
+#define IPC_MODULE_ID_MASK     (0xf << IPC_MODULE_ID_SHIFT)
+#define IPC_MODULE_ID(x)       (x << IPC_MODULE_ID_SHIFT)
+
 /* IPC message timeout (msecs) */
 #define IPC_TIMEOUT_MSECS      300
 #define IPC_BOOT_MSECS         200
@@ -115,6 +125,7 @@ enum ipc_glb_type {
        IPC_GLB_ENTER_DX_STATE = 12,
        IPC_GLB_GET_MIXER_STREAM_INFO = 13,     /* Request mixer stream params */
        IPC_GLB_DEBUG_LOG_MESSAGE = 14,         /* Message to or from the debug logger. */
+       IPC_GLB_MODULE_OPERATION = 15,          /* Message to loadable fw module */
        IPC_GLB_REQUEST_TRANSFER = 16,          /* < Request Transfer for host */
        IPC_GLB_MAX_IPC_MESSAGE_TYPE = 17,      /* Maximum message number */
 };
@@ -133,6 +144,16 @@ enum ipc_glb_reply {
        IPC_GLB_REPLY_SOURCE_NOT_STARTED = 10,  /* Source was not started. */
 };
 
+enum ipc_module_operation {
+       IPC_MODULE_NOTIFICATION = 0,
+       IPC_MODULE_ENABLE = 1,
+       IPC_MODULE_DISABLE = 2,
+       IPC_MODULE_GET_PARAMETER = 3,
+       IPC_MODULE_SET_PARAMETER = 4,
+       IPC_MODULE_GET_INFO = 5,
+       IPC_MODULE_MAX_MESSAGE
+};
+
 /* Stream Message - Types */
 enum ipc_str_operation {
        IPC_STR_RESET = 0,
@@ -190,23 +211,6 @@ struct sst_hsw_ipc_fw_ready {
        u8 fw_info[IPC_MAX_MAILBOX_BYTES - 5 * sizeof(u32)];
 } __attribute__((packed));
 
-struct ipc_message {
-       struct list_head list;
-       u32 header;
-
-       /* direction wrt host CPU */
-       char tx_data[IPC_MAX_MAILBOX_BYTES];
-       size_t tx_size;
-       char rx_data[IPC_MAX_MAILBOX_BYTES];
-       size_t rx_size;
-
-       wait_queue_head_t waitq;
-       bool pending;
-       bool complete;
-       bool wait;
-       int errno;
-};
-
 struct sst_hsw_stream;
 struct sst_hsw;
 
@@ -305,18 +309,19 @@ struct sst_hsw {
        bool shutdown;
 
        /* IPC messaging */
-       struct list_head tx_list;
-       struct list_head rx_list;
-       struct list_head empty_list;
-       wait_queue_head_t wait_txq;
-       struct task_struct *tx_thread;
-       struct kthread_worker kworker;
-       struct kthread_work kwork;
-       bool pending;
-       struct ipc_message *msg;
+       struct sst_generic_ipc ipc;
 
        /* FW log stream */
        struct sst_hsw_log_stream log_stream;
+
+       /* flags bit field to track module state when resume from RTD3,
+        * each bit represent state (enabled/disabled) of single module */
+       u32 enabled_modules_rtd3;
+
+       /* buffer to store parameter lines */
+       u32 param_idx_w;        /* write index */
+       u32 param_idx_r;        /* read index */
+       u8 param_buf[WAVES_PARAM_LINES][WAVES_PARAM_COUNT];
 };
 
 #define CREATE_TRACE_POINTS
@@ -352,6 +357,16 @@ static inline u32 msg_get_notify_reason(u32 msg)
        return (msg & IPC_STG_TYPE_MASK) >> IPC_STG_TYPE_SHIFT;
 }
 
+static inline u32 msg_get_module_operation(u32 msg)
+{
+       return (msg & IPC_MODULE_OPERATION_MASK) >> IPC_MODULE_OPERATION_SHIFT;
+}
+
+static inline u32 msg_get_module_id(u32 msg)
+{
+       return (msg & IPC_MODULE_ID_MASK) >> IPC_MODULE_ID_SHIFT;
+}
+
 u32 create_channel_map(enum sst_hsw_channel_config config)
 {
        switch (config) {
@@ -417,159 +432,6 @@ static struct sst_hsw_stream *get_stream_by_id(struct sst_hsw *hsw,
        return NULL;
 }
 
-static void ipc_shim_dbg(struct sst_hsw *hsw, const char *text)
-{
-       struct sst_dsp *sst = hsw->dsp;
-       u32 isr, ipcd, imrx, ipcx;
-
-       ipcx = sst_dsp_shim_read_unlocked(sst, SST_IPCX);
-       isr = sst_dsp_shim_read_unlocked(sst, SST_ISRX);
-       ipcd = sst_dsp_shim_read_unlocked(sst, SST_IPCD);
-       imrx = sst_dsp_shim_read_unlocked(sst, SST_IMRX);
-
-       dev_err(hsw->dev, "ipc: --%s-- ipcx 0x%8.8x isr 0x%8.8x ipcd 0x%8.8x imrx 0x%8.8x\n",
-               text, ipcx, isr, ipcd, imrx);
-}
-
-/* locks held by caller */
-static struct ipc_message *msg_get_empty(struct sst_hsw *hsw)
-{
-       struct ipc_message *msg = NULL;
-
-       if (!list_empty(&hsw->empty_list)) {
-               msg = list_first_entry(&hsw->empty_list, struct ipc_message,
-                       list);
-               list_del(&msg->list);
-       }
-
-       return msg;
-}
-
-static void ipc_tx_msgs(struct kthread_work *work)
-{
-       struct sst_hsw *hsw =
-               container_of(work, struct sst_hsw, kwork);
-       struct ipc_message *msg;
-       unsigned long flags;
-       u32 ipcx;
-
-       spin_lock_irqsave(&hsw->dsp->spinlock, flags);
-
-       if (list_empty(&hsw->tx_list) || hsw->pending) {
-               spin_unlock_irqrestore(&hsw->dsp->spinlock, flags);
-               return;
-       }
-
-       /* if the DSP is busy, we will TX messages after IRQ.
-        * also postpone if we are in the middle of procesing completion irq*/
-       ipcx = sst_dsp_shim_read_unlocked(hsw->dsp, SST_IPCX);
-       if (ipcx & (SST_IPCX_BUSY | SST_IPCX_DONE)) {
-               spin_unlock_irqrestore(&hsw->dsp->spinlock, flags);
-               return;
-       }
-
-       msg = list_first_entry(&hsw->tx_list, struct ipc_message, list);
-
-       list_move(&msg->list, &hsw->rx_list);
-
-       /* send the message */
-       sst_dsp_outbox_write(hsw->dsp, msg->tx_data, msg->tx_size);
-       sst_dsp_ipc_msg_tx(hsw->dsp, msg->header | SST_IPCX_BUSY);
-
-       spin_unlock_irqrestore(&hsw->dsp->spinlock, flags);
-}
-
-/* locks held by caller */
-static void tx_msg_reply_complete(struct sst_hsw *hsw, struct ipc_message *msg)
-{
-       msg->complete = true;
-       trace_ipc_reply("completed", msg->header);
-
-       if (!msg->wait)
-               list_add_tail(&msg->list, &hsw->empty_list);
-       else
-               wake_up(&msg->waitq);
-}
-
-static int tx_wait_done(struct sst_hsw *hsw, struct ipc_message *msg,
-       void *rx_data)
-{
-       unsigned long flags;
-       int ret;
-
-       /* wait for DSP completion (in all cases atm inc pending) */
-       ret = wait_event_timeout(msg->waitq, msg->complete,
-               msecs_to_jiffies(IPC_TIMEOUT_MSECS));
-
-       spin_lock_irqsave(&hsw->dsp->spinlock, flags);
-       if (ret == 0) {
-               ipc_shim_dbg(hsw, "message timeout");
-
-               trace_ipc_error("error message timeout for", msg->header);
-               list_del(&msg->list);
-               ret = -ETIMEDOUT;
-       } else {
-
-               /* copy the data returned from DSP */
-               if (msg->rx_size)
-                       memcpy(rx_data, msg->rx_data, msg->rx_size);
-               ret = msg->errno;
-       }
-
-       list_add_tail(&msg->list, &hsw->empty_list);
-       spin_unlock_irqrestore(&hsw->dsp->spinlock, flags);
-       return ret;
-}
-
-static int ipc_tx_message(struct sst_hsw *hsw, u32 header, void *tx_data,
-       size_t tx_bytes, void *rx_data, size_t rx_bytes, int wait)
-{
-       struct ipc_message *msg;
-       unsigned long flags;
-
-       spin_lock_irqsave(&hsw->dsp->spinlock, flags);
-
-       msg = msg_get_empty(hsw);
-       if (msg == NULL) {
-               spin_unlock_irqrestore(&hsw->dsp->spinlock, flags);
-               return -EBUSY;
-       }
-
-       if (tx_bytes)
-               memcpy(msg->tx_data, tx_data, tx_bytes);
-
-       msg->header = header;
-       msg->tx_size = tx_bytes;
-       msg->rx_size = rx_bytes;
-       msg->wait = wait;
-       msg->errno = 0;
-       msg->pending = false;
-       msg->complete = false;
-
-       list_add_tail(&msg->list, &hsw->tx_list);
-       spin_unlock_irqrestore(&hsw->dsp->spinlock, flags);
-
-       queue_kthread_work(&hsw->kworker, &hsw->kwork);
-
-       if (wait)
-               return tx_wait_done(hsw, msg, rx_data);
-       else
-               return 0;
-}
-
-static inline int ipc_tx_message_wait(struct sst_hsw *hsw, u32 header,
-       void *tx_data, size_t tx_bytes, void *rx_data, size_t rx_bytes)
-{
-       return ipc_tx_message(hsw, header, tx_data, tx_bytes, rx_data,
-               rx_bytes, 1);
-}
-
-static inline int ipc_tx_message_nowait(struct sst_hsw *hsw, u32 header,
-       void *tx_data, size_t tx_bytes)
-{
-       return ipc_tx_message(hsw, header, tx_data, tx_bytes, NULL, 0, 0);
-}
-
 static void hsw_fw_ready(struct sst_hsw *hsw, u32 header)
 {
        struct sst_hsw_ipc_fw_ready fw_ready;
@@ -604,7 +466,7 @@ static void hsw_fw_ready(struct sst_hsw *hsw, u32 header)
                /* log the FW version info got from the mailbox here. */
                memcpy(fw_info, fw_ready.fw_info, fw_ready.fw_info_size);
                pinfo = &fw_info[0];
-               for (i = 0; i < sizeof(tmp) / sizeof(char *); i++)
+               for (i = 0; i < ARRAY_SIZE(tmp); i++)
                        tmp[i] = strsep(&pinfo, " ");
                dev_info(hsw->dev, "FW loaded, mailbox readback FW info: type %s, - "
                        "version: %s.%s, build %s, source commit id: %s\n",
@@ -657,27 +519,6 @@ static void hsw_notification_work(struct work_struct *work)
        sst_dsp_shim_update_bits(hsw->dsp, SST_IMRX, SST_IMRX_BUSY, 0);
 }
 
-static struct ipc_message *reply_find_msg(struct sst_hsw *hsw, u32 header)
-{
-       struct ipc_message *msg;
-
-       /* clear reply bits & status bits */
-       header &= ~(IPC_STATUS_MASK | IPC_GLB_REPLY_MASK);
-
-       if (list_empty(&hsw->rx_list)) {
-               dev_err(hsw->dev, "error: rx list empty but received 0x%x\n",
-                       header);
-               return NULL;
-       }
-
-       list_for_each_entry(msg, &hsw->rx_list, list) {
-               if (msg->header == header)
-                       return msg;
-       }
-
-       return NULL;
-}
-
 static void hsw_stream_update(struct sst_hsw *hsw, struct ipc_message *msg)
 {
        struct sst_hsw_stream *stream;
@@ -716,7 +557,7 @@ static int hsw_process_reply(struct sst_hsw *hsw, u32 header)
 
        trace_ipc_reply("processing -->", header);
 
-       msg = reply_find_msg(hsw, header);
+       msg = sst_ipc_reply_find_msg(&hsw->ipc, header);
        if (msg == NULL) {
                trace_ipc_error("error: can't find message header", header);
                return -EIO;
@@ -727,14 +568,14 @@ static int hsw_process_reply(struct sst_hsw *hsw, u32 header)
        case IPC_GLB_REPLY_PENDING:
                trace_ipc_pending_reply("received", header);
                msg->pending = true;
-               hsw->pending = true;
+               hsw->ipc.pending = true;
                return 1;
        case IPC_GLB_REPLY_SUCCESS:
                if (msg->pending) {
                        trace_ipc_pending_reply("completed", header);
                        sst_dsp_inbox_read(hsw->dsp, msg->rx_data,
                                msg->rx_size);
-                       hsw->pending = false;
+                       hsw->ipc.pending = false;
                } else {
                        /* copy data from the DSP */
                        sst_dsp_outbox_read(hsw->dsp, msg->rx_data,
@@ -790,11 +631,36 @@ static int hsw_process_reply(struct sst_hsw *hsw, u32 header)
 
        /* wake up and return the error if we have waiters on this message ? */
        list_del(&msg->list);
-       tx_msg_reply_complete(hsw, msg);
+       sst_ipc_tx_msg_reply_complete(&hsw->ipc, msg);
 
        return 1;
 }
 
+static int hsw_module_message(struct sst_hsw *hsw, u32 header)
+{
+       u32 operation, module_id;
+       int handled = 0;
+
+       operation = msg_get_module_operation(header);
+       module_id = msg_get_module_id(header);
+       dev_dbg(hsw->dev, "received module message header: 0x%8.8x\n",
+                       header);
+       dev_dbg(hsw->dev, "operation: 0x%8.8x module_id: 0x%8.8x\n",
+                       operation, module_id);
+
+       switch (operation) {
+       case IPC_MODULE_NOTIFICATION:
+               dev_dbg(hsw->dev, "module notification received");
+               handled = 1;
+               break;
+       default:
+               handled = hsw_process_reply(hsw, header);
+               break;
+       }
+
+       return handled;
+}
+
 static int hsw_stream_message(struct sst_hsw *hsw, u32 header)
 {
        u32 stream_msg, stream_id, stage_type;
@@ -890,6 +756,9 @@ static int hsw_process_notification(struct sst_hsw *hsw)
        case IPC_GLB_DEBUG_LOG_MESSAGE:
                handled = hsw_log_message(hsw, header);
                break;
+       case IPC_GLB_MODULE_OPERATION:
+               handled = hsw_module_message(hsw, header);
+               break;
        default:
                dev_err(hsw->dev, "error: unexpected type %d hdr 0x%8.8x\n",
                        type, header);
@@ -903,6 +772,7 @@ static irqreturn_t hsw_irq_thread(int irq, void *context)
 {
        struct sst_dsp *sst = (struct sst_dsp *) context;
        struct sst_hsw *hsw = sst_dsp_get_thread_context(sst);
+       struct sst_generic_ipc *ipc = &hsw->ipc;
        u32 ipcx, ipcd;
        int handled;
        unsigned long flags;
@@ -949,7 +819,7 @@ static irqreturn_t hsw_irq_thread(int irq, void *context)
        spin_unlock_irqrestore(&sst->spinlock, flags);
 
        /* continue to send any remaining messages... */
-       queue_kthread_work(&hsw->kworker, &hsw->kwork);
+       queue_kthread_work(&ipc->kworker, &ipc->kwork);
 
        return IRQ_HANDLED;
 }
@@ -959,7 +829,8 @@ int sst_hsw_fw_get_version(struct sst_hsw *hsw,
 {
        int ret;
 
-       ret = ipc_tx_message_wait(hsw, IPC_GLB_TYPE(IPC_GLB_GET_FW_VERSION),
+       ret = sst_ipc_tx_message_wait(&hsw->ipc,
+               IPC_GLB_TYPE(IPC_GLB_GET_FW_VERSION),
                NULL, 0, version, sizeof(*version));
        if (ret < 0)
                dev_err(hsw->dev, "error: get version failed\n");
@@ -1023,7 +894,8 @@ int sst_hsw_stream_set_volume(struct sst_hsw *hsw,
                req->channel = channel;
        }
 
-       ret = ipc_tx_message_wait(hsw, header, req, sizeof(*req), NULL, 0);
+       ret = sst_ipc_tx_message_wait(&hsw->ipc, header, req,
+               sizeof(*req), NULL, 0);
        if (ret < 0) {
                dev_err(hsw->dev, "error: set stream volume failed\n");
                return ret;
@@ -1088,7 +960,8 @@ int sst_hsw_mixer_set_volume(struct sst_hsw *hsw, u32 stage_id, u32 channel,
        req.curve_type = hsw->curve_type;
        req.target_volume = volume;
 
-       ret = ipc_tx_message_wait(hsw, header, &req, sizeof(req), NULL, 0);
+       ret = sst_ipc_tx_message_wait(&hsw->ipc, header, &req,
+               sizeof(req), NULL, 0);
        if (ret < 0) {
                dev_err(hsw->dev, "error: set mixer volume failed\n");
                return ret;
@@ -1146,7 +1019,7 @@ int sst_hsw_stream_free(struct sst_hsw *hsw, struct sst_hsw_stream *stream)
        stream->free_req.stream_id = stream->reply.stream_hw_id;
        header = IPC_GLB_TYPE(IPC_GLB_FREE_STREAM);
 
-       ret = ipc_tx_message_wait(hsw, header, &stream->free_req,
+       ret = sst_ipc_tx_message_wait(&hsw->ipc, header, &stream->free_req,
                sizeof(stream->free_req), NULL, 0);
        if (ret < 0) {
                dev_err(hsw->dev, "error: free stream %d failed\n",
@@ -1338,8 +1211,8 @@ int sst_hsw_stream_commit(struct sst_hsw *hsw, struct sst_hsw_stream *stream)
 
        header = IPC_GLB_TYPE(IPC_GLB_ALLOCATE_STREAM);
 
-       ret = ipc_tx_message_wait(hsw, header, str_req, sizeof(*str_req),
-               reply, sizeof(*reply));
+       ret = sst_ipc_tx_message_wait(&hsw->ipc, header, str_req,
+               sizeof(*str_req), reply, sizeof(*reply));
        if (ret < 0) {
                dev_err(hsw->dev, "error: stream commit failed\n");
                return ret;
@@ -1388,7 +1261,8 @@ int sst_hsw_mixer_get_info(struct sst_hsw *hsw)
 
        trace_ipc_request("get global mixer info", 0);
 
-       ret = ipc_tx_message_wait(hsw, header, NULL, 0, reply, sizeof(*reply));
+       ret = sst_ipc_tx_message_wait(&hsw->ipc, header, NULL, 0,
+               reply, sizeof(*reply));
        if (ret < 0) {
                dev_err(hsw->dev, "error: get stream info failed\n");
                return ret;
@@ -1409,9 +1283,10 @@ static int sst_hsw_stream_operations(struct sst_hsw *hsw, int type,
        header |= (stream_id << IPC_STR_ID_SHIFT);
 
        if (wait)
-               return ipc_tx_message_wait(hsw, header, NULL, 0, NULL, 0);
+               return sst_ipc_tx_message_wait(&hsw->ipc, header,
+                       NULL, 0, NULL, 0);
        else
-               return ipc_tx_message_nowait(hsw, header, NULL, 0);
+               return sst_ipc_tx_message_nowait(&hsw->ipc, header, NULL, 0);
 }
 
 /* Stream ALSA trigger operations */
@@ -1538,8 +1413,8 @@ int sst_hsw_device_set_config(struct sst_hsw *hsw,
 
        header = IPC_GLB_TYPE(IPC_GLB_SET_DEVICE_FORMATS);
 
-       ret = ipc_tx_message_wait(hsw, header, &config, sizeof(config),
-               NULL, 0);
+       ret = sst_ipc_tx_message_wait(&hsw->ipc, header, &config,
+               sizeof(config), NULL, 0);
        if (ret < 0)
                dev_err(hsw->dev, "error: set device formats failed\n");
 
@@ -1559,8 +1434,8 @@ int sst_hsw_dx_set_state(struct sst_hsw *hsw,
 
        trace_ipc_request("PM enter Dx state", state);
 
-       ret = ipc_tx_message_wait(hsw, header, &state_, sizeof(state_),
-               dx, sizeof(*dx));
+       ret = sst_ipc_tx_message_wait(&hsw->ipc, header, &state_,
+               sizeof(state_), dx, sizeof(*dx));
        if (ret < 0) {
                dev_err(hsw->dev, "ipc: error set dx state %d failed\n", state);
                return ret;
@@ -1703,32 +1578,6 @@ static int sst_hsw_dx_state_restore(struct sst_hsw *hsw)
        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;
-
-       /* 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;
@@ -1808,7 +1657,7 @@ int sst_hsw_dsp_runtime_suspend(struct sst_hsw *hsw)
        if (ret < 0)
                return ret;
 
-       sst_hsw_drop_all(hsw);
+       sst_ipc_drop_all(&hsw->ipc);
 
        return 0;
 }
@@ -1844,6 +1693,8 @@ int sst_hsw_dsp_runtime_resume(struct sst_hsw *hsw)
        if (ret < 0)
                dev_err(dev, "error: audio DSP boot failure\n");
 
+       sst_hsw_init_module_state(hsw);
+
        ret = wait_event_timeout(hsw->boot_wait, hsw->boot_complete,
                msecs_to_jiffies(IPC_BOOT_MSECS));
        if (ret == 0) {
@@ -1864,26 +1715,345 @@ int sst_hsw_dsp_runtime_resume(struct sst_hsw *hsw)
 }
 #endif
 
-static int msg_empty_list_init(struct sst_hsw *hsw)
+struct sst_dsp *sst_hsw_get_dsp(struct sst_hsw *hsw)
 {
-       int i;
+       return hsw->dsp;
+}
 
-       hsw->msg = kzalloc(sizeof(struct ipc_message) *
-               IPC_EMPTY_LIST_SIZE, GFP_KERNEL);
-       if (hsw->msg == NULL)
-               return -ENOMEM;
+void sst_hsw_init_module_state(struct sst_hsw *hsw)
+{
+       struct sst_module *module;
+       enum sst_hsw_module_id id;
+
+       /* the base fw contains several modules */
+       for (id = SST_HSW_MODULE_BASE_FW; id < SST_HSW_MAX_MODULE_ID; id++) {
+               module = sst_module_get_from_id(hsw->dsp, id);
+               if (module) {
+                       /* module waves is active only after being enabled */
+                       if (id == SST_HSW_MODULE_WAVES)
+                               module->state = SST_MODULE_STATE_INITIALIZED;
+                       else
+                               module->state = SST_MODULE_STATE_ACTIVE;
+               }
+       }
+}
+
+bool sst_hsw_is_module_loaded(struct sst_hsw *hsw, u32 module_id)
+{
+       struct sst_module *module;
+
+       module = sst_module_get_from_id(hsw->dsp, module_id);
+       if (module == NULL || module->state == SST_MODULE_STATE_UNLOADED)
+               return false;
+       else
+               return true;
+}
+
+bool sst_hsw_is_module_active(struct sst_hsw *hsw, u32 module_id)
+{
+       struct sst_module *module;
+
+       module = sst_module_get_from_id(hsw->dsp, module_id);
+       if (module != NULL && module->state == SST_MODULE_STATE_ACTIVE)
+               return true;
+       else
+               return false;
+}
 
-       for (i = 0; i < IPC_EMPTY_LIST_SIZE; i++) {
-               init_waitqueue_head(&hsw->msg[i].waitq);
-               list_add(&hsw->msg[i].list, &hsw->empty_list);
+void sst_hsw_set_module_enabled_rtd3(struct sst_hsw *hsw, u32 module_id)
+{
+       hsw->enabled_modules_rtd3 |= (1 << module_id);
+}
+
+void sst_hsw_set_module_disabled_rtd3(struct sst_hsw *hsw, u32 module_id)
+{
+       hsw->enabled_modules_rtd3 &= ~(1 << module_id);
+}
+
+bool sst_hsw_is_module_enabled_rtd3(struct sst_hsw *hsw, u32 module_id)
+{
+       return hsw->enabled_modules_rtd3 & (1 << module_id);
+}
+
+void sst_hsw_reset_param_buf(struct sst_hsw *hsw)
+{
+       hsw->param_idx_w = 0;
+       hsw->param_idx_r = 0;
+       memset((void *)hsw->param_buf, 0, sizeof(hsw->param_buf));
+}
+
+int sst_hsw_store_param_line(struct sst_hsw *hsw, u8 *buf)
+{
+       /* save line to the first available position of param buffer */
+       if (hsw->param_idx_w > WAVES_PARAM_LINES - 1) {
+               dev_warn(hsw->dev, "warning: param buffer overflow!\n");
+               return -EPERM;
+       }
+       memcpy(hsw->param_buf[hsw->param_idx_w], buf, WAVES_PARAM_COUNT);
+       hsw->param_idx_w++;
+       return 0;
+}
+
+int sst_hsw_load_param_line(struct sst_hsw *hsw, u8 *buf)
+{
+       u8 id = 0;
+
+       /* read the first matching line from param buffer */
+       while (hsw->param_idx_r < WAVES_PARAM_LINES) {
+               id = hsw->param_buf[hsw->param_idx_r][0];
+               hsw->param_idx_r++;
+               if (buf[0] == id) {
+                       memcpy(buf, hsw->param_buf[hsw->param_idx_r],
+                               WAVES_PARAM_COUNT);
+                       break;
+               }
        }
+       if (hsw->param_idx_r > WAVES_PARAM_LINES - 1) {
+               dev_dbg(hsw->dev, "end of buffer, roll to the beginning\n");
+               hsw->param_idx_r = 0;
+               return 0;
+       }
+       return 0;
+}
+
+int sst_hsw_launch_param_buf(struct sst_hsw *hsw)
+{
+       int ret, idx;
 
+       if (!sst_hsw_is_module_active(hsw, SST_HSW_MODULE_WAVES)) {
+               dev_dbg(hsw->dev, "module waves is not active\n");
+               return 0;
+       }
+
+       /* put all param lines to DSP through ipc */
+       for (idx = 0; idx < hsw->param_idx_w; idx++) {
+               ret = sst_hsw_module_set_param(hsw,
+                       SST_HSW_MODULE_WAVES, 0, hsw->param_buf[idx][0],
+                       WAVES_PARAM_COUNT, hsw->param_buf[idx]);
+               if (ret < 0)
+                       return ret;
+       }
        return 0;
 }
 
-struct sst_dsp *sst_hsw_get_dsp(struct sst_hsw *hsw)
+int sst_hsw_module_load(struct sst_hsw *hsw,
+       u32 module_id, u32 instance_id, char *name)
 {
-       return hsw->dsp;
+       int ret = 0;
+       const struct firmware *fw = NULL;
+       struct sst_fw *hsw_sst_fw;
+       struct sst_module *module;
+       struct device *dev = hsw->dev;
+       struct sst_dsp *dsp = hsw->dsp;
+
+       dev_dbg(dev, "sst_hsw_module_load id=%d, name='%s'", module_id, name);
+
+       module = sst_module_get_from_id(dsp, module_id);
+       if (module == NULL) {
+               /* loading for the first time */
+               if (module_id == SST_HSW_MODULE_BASE_FW) {
+                       /* for base module: use fw requested in acpi probe */
+                       fw = dsp->pdata->fw;
+                       if (!fw) {
+                               dev_err(dev, "request Base fw failed\n");
+                               return -ENODEV;
+                       }
+               } else {
+                       /* try and load any other optional modules if they are
+                        * available. Use dev_info instead of dev_err in case
+                        * request firmware failed */
+                       ret = request_firmware(&fw, name, dev);
+                       if (ret) {
+                               dev_info(dev, "fw image %s not available(%d)\n",
+                                               name, ret);
+                               return ret;
+                       }
+               }
+               hsw_sst_fw = sst_fw_new(dsp, fw, hsw);
+               if (hsw_sst_fw  == NULL) {
+                       dev_err(dev, "error: failed to load firmware\n");
+                       ret = -ENOMEM;
+                       goto out;
+               }
+               module = sst_module_get_from_id(dsp, module_id);
+               if (module == NULL) {
+                       dev_err(dev, "error: no module %d in firmware %s\n",
+                                       module_id, name);
+               }
+       } else
+               dev_info(dev, "module %d (%s) already loaded\n",
+                               module_id, name);
+out:
+       /* release fw, but base fw should be released by acpi driver */
+       if (fw && module_id != SST_HSW_MODULE_BASE_FW)
+               release_firmware(fw);
+
+       return ret;
+}
+
+int sst_hsw_module_enable(struct sst_hsw *hsw,
+       u32 module_id, u32 instance_id)
+{
+       int ret;
+       u32 header = 0;
+       struct sst_hsw_ipc_module_config config;
+       struct sst_module *module;
+       struct sst_module_runtime *runtime;
+       struct device *dev = hsw->dev;
+       struct sst_dsp *dsp = hsw->dsp;
+
+       if (!sst_hsw_is_module_loaded(hsw, module_id)) {
+               dev_dbg(dev, "module %d not loaded\n", module_id);
+               return 0;
+       }
+
+       if (sst_hsw_is_module_active(hsw, module_id)) {
+               dev_info(dev, "module %d already enabled\n", module_id);
+               return 0;
+       }
+
+       module = sst_module_get_from_id(dsp, module_id);
+       if (module == NULL) {
+               dev_err(dev, "module %d not valid\n", module_id);
+               return -ENXIO;
+       }
+
+       runtime = sst_module_runtime_get_from_id(module, module_id);
+       if (runtime == NULL) {
+               dev_err(dev, "runtime %d not valid", module_id);
+               return -ENXIO;
+       }
+
+       header = IPC_GLB_TYPE(IPC_GLB_MODULE_OPERATION) |
+                       IPC_MODULE_OPERATION(IPC_MODULE_ENABLE) |
+                       IPC_MODULE_ID(module_id);
+       dev_dbg(dev, "module enable header: %x\n", header);
+
+       config.map.module_entries_count = 1;
+       config.map.module_entries[0].module_id = module->id;
+       config.map.module_entries[0].entry_point = module->entry;
+
+       config.persistent_mem.offset =
+               sst_dsp_get_offset(dsp,
+                       runtime->persistent_offset, SST_MEM_DRAM);
+       config.persistent_mem.size = module->persistent_size;
+
+       config.scratch_mem.offset =
+               sst_dsp_get_offset(dsp,
+                       dsp->scratch_offset, SST_MEM_DRAM);
+       config.scratch_mem.size = module->scratch_size;
+       dev_dbg(dev, "mod %d enable p:%d @ %x, s:%d @ %x, ep: %x",
+               config.map.module_entries[0].module_id,
+               config.persistent_mem.size,
+               config.persistent_mem.offset,
+               config.scratch_mem.size, config.scratch_mem.offset,
+               config.map.module_entries[0].entry_point);
+
+       ret = sst_ipc_tx_message_wait(&hsw->ipc, header,
+                       &config, sizeof(config), NULL, 0);
+       if (ret < 0)
+               dev_err(dev, "ipc: module enable failed - %d\n", ret);
+       else
+               module->state = SST_MODULE_STATE_ACTIVE;
+
+       return ret;
+}
+
+int sst_hsw_module_disable(struct sst_hsw *hsw,
+       u32 module_id, u32 instance_id)
+{
+       int ret;
+       u32 header;
+       struct sst_module *module;
+       struct device *dev = hsw->dev;
+       struct sst_dsp *dsp = hsw->dsp;
+
+       if (!sst_hsw_is_module_loaded(hsw, module_id)) {
+               dev_dbg(dev, "module %d not loaded\n", module_id);
+               return 0;
+       }
+
+       if (!sst_hsw_is_module_active(hsw, module_id)) {
+               dev_info(dev, "module %d already disabled\n", module_id);
+               return 0;
+       }
+
+       module = sst_module_get_from_id(dsp, module_id);
+       if (module == NULL) {
+               dev_err(dev, "module %d not valid\n", module_id);
+               return -ENXIO;
+       }
+
+       header = IPC_GLB_TYPE(IPC_GLB_MODULE_OPERATION) |
+                       IPC_MODULE_OPERATION(IPC_MODULE_DISABLE) |
+                       IPC_MODULE_ID(module_id);
+
+       ret = sst_ipc_tx_message_wait(&hsw->ipc, header,  NULL, 0, NULL, 0);
+       if (ret < 0)
+               dev_err(dev, "module disable failed - %d\n", ret);
+       else
+               module->state = SST_MODULE_STATE_INITIALIZED;
+
+       return ret;
+}
+
+int sst_hsw_module_set_param(struct sst_hsw *hsw,
+       u32 module_id, u32 instance_id, u32 parameter_id,
+       u32 param_size, char *param)
+{
+       int ret;
+       unsigned char *data = NULL;
+       u32 header = 0;
+       u32 payload_size = 0, transfer_parameter_size = 0;
+       dma_addr_t dma_addr = 0;
+       struct sst_hsw_transfer_parameter *parameter;
+       struct device *dev = hsw->dev;
+
+       header = IPC_GLB_TYPE(IPC_GLB_MODULE_OPERATION) |
+                       IPC_MODULE_OPERATION(IPC_MODULE_SET_PARAMETER) |
+                       IPC_MODULE_ID(module_id);
+       dev_dbg(dev, "sst_hsw_module_set_param header=%x\n", header);
+
+       payload_size = param_size +
+               sizeof(struct sst_hsw_transfer_parameter) -
+               sizeof(struct sst_hsw_transfer_list);
+       dev_dbg(dev, "parameter size : %d\n", param_size);
+       dev_dbg(dev, "payload size   : %d\n", payload_size);
+
+       if (payload_size <= SST_HSW_IPC_MAX_SHORT_PARAMETER_SIZE) {
+               /* short parameter, mailbox can contain data */
+               dev_dbg(dev, "transfer parameter size : %d\n",
+                       transfer_parameter_size);
+
+               transfer_parameter_size = ALIGN(payload_size, 4);
+               dev_dbg(dev, "transfer parameter aligned size : %d\n",
+                       transfer_parameter_size);
+
+               parameter = kzalloc(transfer_parameter_size, GFP_KERNEL);
+               if (parameter == NULL)
+                       return -ENOMEM;
+
+               memcpy(parameter->data, param, param_size);
+       } else {
+               dev_warn(dev, "transfer parameter size too large!");
+               return 0;
+       }
+
+       parameter->parameter_id = parameter_id;
+       parameter->data_size = param_size;
+
+       ret = sst_ipc_tx_message_wait(&hsw->ipc, header,
+               parameter, transfer_parameter_size , NULL, 0);
+       if (ret < 0)
+               dev_err(dev, "ipc: module set parameter failed - %d\n", ret);
+
+       kfree(parameter);
+
+       if (data)
+               dma_free_coherent(hsw->dsp->dma_dev,
+                       param_size, (void *)data, dma_addr);
+
+       return ret;
 }
 
 static struct sst_dsp_device hsw_dev = {
@@ -1891,10 +2061,48 @@ static struct sst_dsp_device hsw_dev = {
        .ops = &haswell_ops,
 };
 
+static void hsw_tx_msg(struct sst_generic_ipc *ipc, struct ipc_message *msg)
+{
+       /* send the message */
+       sst_dsp_outbox_write(ipc->dsp, msg->tx_data, msg->tx_size);
+       sst_dsp_ipc_msg_tx(ipc->dsp, msg->header);
+}
+
+static void hsw_shim_dbg(struct sst_generic_ipc *ipc, const char *text)
+{
+       struct sst_dsp *sst = ipc->dsp;
+       u32 isr, ipcd, imrx, ipcx;
+
+       ipcx = sst_dsp_shim_read_unlocked(sst, SST_IPCX);
+       isr = sst_dsp_shim_read_unlocked(sst, SST_ISRX);
+       ipcd = sst_dsp_shim_read_unlocked(sst, SST_IPCD);
+       imrx = sst_dsp_shim_read_unlocked(sst, SST_IMRX);
+
+       dev_err(ipc->dev,
+               "ipc: --%s-- ipcx 0x%8.8x isr 0x%8.8x ipcd 0x%8.8x imrx 0x%8.8x\n",
+               text, ipcx, isr, ipcd, imrx);
+}
+
+static void hsw_tx_data_copy(struct ipc_message *msg, char *tx_data,
+       size_t tx_size)
+{
+       memcpy(msg->tx_data, tx_data, tx_size);
+}
+
+static u64 hsw_reply_msg_match(u64 header, u64 *mask)
+{
+       /* clear reply bits & status bits */
+       header &= ~(IPC_STATUS_MASK | IPC_GLB_REPLY_MASK);
+       *mask = (u64)-1;
+
+       return header;
+}
+
 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_generic_ipc *ipc;
        int ret;
 
        dev_dbg(dev, "initialising Audio DSP IPC\n");
@@ -1903,39 +2111,30 @@ int sst_hsw_dsp_init(struct device *dev, struct sst_pdata *pdata)
        if (hsw == NULL)
                return -ENOMEM;
 
-       hsw->dev = dev;
-       INIT_LIST_HEAD(&hsw->stream_list);
-       INIT_LIST_HEAD(&hsw->tx_list);
-       INIT_LIST_HEAD(&hsw->rx_list);
-       INIT_LIST_HEAD(&hsw->empty_list);
-       init_waitqueue_head(&hsw->boot_wait);
-       init_waitqueue_head(&hsw->wait_txq);
-
-       ret = msg_empty_list_init(hsw);
-       if (ret < 0)
-               return -ENOMEM;
+       ipc = &hsw->ipc;
+       ipc->dev = dev;
+       ipc->ops.tx_msg = hsw_tx_msg;
+       ipc->ops.shim_dbg = hsw_shim_dbg;
+       ipc->ops.tx_data_copy = hsw_tx_data_copy;
+       ipc->ops.reply_msg_match = hsw_reply_msg_match;
 
-       /* start the IPC message thread */
-       init_kthread_worker(&hsw->kworker);
-       hsw->tx_thread = kthread_run(kthread_worker_fn,
-                                          &hsw->kworker, "%s",
-                                          dev_name(hsw->dev));
-       if (IS_ERR(hsw->tx_thread)) {
-               ret = PTR_ERR(hsw->tx_thread);
-               dev_err(hsw->dev, "error: failed to create message TX task\n");
-               goto err_free_msg;
-       }
-       init_kthread_work(&hsw->kwork, ipc_tx_msgs);
+       ret = sst_ipc_init(ipc);
+       if (ret != 0)
+               goto ipc_init_err;
 
+       INIT_LIST_HEAD(&hsw->stream_list);
+       init_waitqueue_head(&hsw->boot_wait);
        hsw_dev.thread_context = hsw;
 
        /* init SST shim */
        hsw->dsp = sst_dsp_new(dev, &hsw_dev, pdata);
        if (hsw->dsp == NULL) {
                ret = -ENODEV;
-               goto dsp_err;
+               goto dsp_new_err;
        }
 
+       ipc->dsp = hsw->dsp;
+
        /* 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);
@@ -1947,18 +2146,22 @@ int sst_hsw_dsp_init(struct device *dev, struct sst_pdata *pdata)
        /* 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) {
-               ret = -ENODEV;
-               dev_err(dev, "error: failed to load firmware\n");
+       /* load base module and other modules in base firmware image */
+       ret = sst_hsw_module_load(hsw, SST_HSW_MODULE_BASE_FW, 0, "Base");
+       if (ret < 0)
                goto fw_err;
-       }
+
+       /* try to load module waves */
+       sst_hsw_module_load(hsw, SST_HSW_MODULE_WAVES, 0, "intel/IntcPP01.bin");
 
        /* allocate scratch mem regions */
        ret = sst_block_alloc_scratch(hsw->dsp);
        if (ret < 0)
                goto boot_err;
 
+       /* init param buffer */
+       sst_hsw_reset_param_buf(hsw);
+
        /* wait for DSP boot completion */
        sst_dsp_boot(hsw->dsp);
        ret = wait_event_timeout(hsw->boot_wait, hsw->boot_complete,
@@ -1971,6 +2174,9 @@ int sst_hsw_dsp_init(struct device *dev, struct sst_pdata *pdata)
                goto boot_err;
        }
 
+       /* init module state after boot */
+       sst_hsw_init_module_state(hsw);
+
        /* get the FW version */
        sst_hsw_fw_get_version(hsw, &version);
 
@@ -1986,17 +2192,16 @@ 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_all(hsw->dsp);
 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);
-err_free_msg:
-       kfree(hsw->msg);
-
+dsp_new_err:
+       sst_ipc_fini(ipc);
+ipc_init_err:
+       kfree(hsw);
        return ret;
 }
 EXPORT_SYMBOL_GPL(sst_hsw_dsp_init);
@@ -2010,7 +2215,6 @@ void sst_hsw_dsp_free(struct device *dev, struct sst_pdata *pdata)
        dma_free_coherent(hsw->dsp->dma_dev, SST_HSW_DX_CONTEXT_SIZE,
                        hsw->dx_context, hsw->dx_context_paddr);
        sst_dsp_free(hsw->dsp);
-       kthread_stop(hsw->tx_thread);
-       kfree(hsw->msg);
+       sst_ipc_fini(&hsw->ipc);
 }
 EXPORT_SYMBOL_GPL(sst_hsw_dsp_free);
similarity index 87%
rename from sound/soc/intel/sst-haswell-ipc.h
rename to sound/soc/intel/haswell/sst-haswell-ipc.h
index 858096041cb1bb2ca604c4823f1b7e521f240494..06d71aefa1fe795e6354defa38b21789f2a40e39 100644 (file)
@@ -37,6 +37,9 @@
 #define SST_HSW_IPC_MAX_PAYLOAD_SIZE   400
 #define SST_HSW_MAX_INFO_SIZE          64
 #define SST_HSW_BUILD_HASH_LENGTH      40
+#define SST_HSW_IPC_MAX_SHORT_PARAMETER_SIZE   500
+#define WAVES_PARAM_COUNT              128
+#define WAVES_PARAM_LINES              160
 
 struct sst_hsw;
 struct sst_hsw_stream;
@@ -187,6 +190,28 @@ enum sst_hsw_performance_action {
        SST_HSW_PERF_STOP = 1,
 };
 
+struct sst_hsw_transfer_info {
+       uint32_t destination;       /* destination address */
+       uint32_t reverse:1;         /* if 1 data flows from destination */
+       uint32_t size:31;           /* transfer size in bytes.*/
+       uint16_t first_page_offset; /* offset to data in the first page. */
+       uint8_t  packed_pages;   /* page addresses. Each occupies 20 bits */
+} __attribute__((packed));
+
+struct sst_hsw_transfer_list {
+       uint32_t transfers_count;
+       struct sst_hsw_transfer_info transfers;
+} __attribute__((packed));
+
+struct sst_hsw_transfer_parameter {
+       uint32_t parameter_id;
+       uint32_t data_size;
+       union {
+               uint8_t data[1];
+               struct sst_hsw_transfer_list transfer_list;
+       };
+} __attribute__((packed));
+
 /* SST firmware module info */
 struct sst_hsw_module_info {
        u8 name[SST_HSW_MAX_INFO_SIZE];
@@ -215,6 +240,12 @@ struct sst_hsw_fx_enable {
        struct sst_hsw_memory_info persistent_mem;
 } __attribute__((packed));
 
+struct sst_hsw_ipc_module_config {
+       struct sst_hsw_module_map map;
+       struct sst_hsw_memory_info persistent_mem;
+       struct sst_hsw_memory_info scratch_mem;
+} __attribute__((packed));
+
 struct sst_hsw_get_fx_param {
        u32 parameter_id;
        u32 param_size;
@@ -467,6 +498,28 @@ 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);
 
+/* fw module function */
+void sst_hsw_init_module_state(struct sst_hsw *hsw);
+bool sst_hsw_is_module_loaded(struct sst_hsw *hsw, u32 module_id);
+bool sst_hsw_is_module_active(struct sst_hsw *hsw, u32 module_id);
+void sst_hsw_set_module_enabled_rtd3(struct sst_hsw *hsw, u32 module_id);
+void sst_hsw_set_module_disabled_rtd3(struct sst_hsw *hsw, u32 module_id);
+bool sst_hsw_is_module_enabled_rtd3(struct sst_hsw *hsw, u32 module_id);
+void sst_hsw_reset_param_buf(struct sst_hsw *hsw);
+int sst_hsw_store_param_line(struct sst_hsw *hsw, u8 *buf);
+int sst_hsw_load_param_line(struct sst_hsw *hsw, u8 *buf);
+int sst_hsw_launch_param_buf(struct sst_hsw *hsw);
+
+int sst_hsw_module_load(struct sst_hsw *hsw,
+       u32 module_id, u32 instance_id, char *name);
+int sst_hsw_module_enable(struct sst_hsw *hsw,
+       u32 module_id, u32 instance_id);
+int sst_hsw_module_disable(struct sst_hsw *hsw,
+       u32 module_id, u32 instance_id);
+int sst_hsw_module_set_param(struct sst_hsw *hsw,
+       u32 module_id, u32 instance_id, u32 parameter_id,
+       u32 param_size, char *param);
+
 /* runtime module management */
 struct sst_module_runtime *sst_hsw_runtime_module_create(struct sst_hsw *hsw,
        int mod_id, int offset);
similarity index 88%
rename from sound/soc/intel/sst-haswell-pcm.c
rename to sound/soc/intel/haswell/sst-haswell-pcm.c
index 7e21e8f85885e436cb896a806dde544d3cd8ec4d..23ae0400d6db99668b3d2b48b47eb003d2fc2e46 100644 (file)
@@ -29,9 +29,9 @@
 #include <sound/tlv.h>
 #include <sound/compress_driver.h>
 
-#include "sst-haswell-ipc.h"
-#include "sst-dsp-priv.h"
-#include "sst-dsp.h"
+#include "../haswell/sst-haswell-ipc.h"
+#include "../common/sst-dsp-priv.h"
+#include "../common/sst-dsp.h"
 
 #define HSW_PCM_COUNT          6
 #define HSW_VOLUME_MAX         0x7FFFFFFF      /* 0dB */
@@ -137,6 +137,7 @@ struct hsw_priv_data {
        struct device *dev;
        enum hsw_pm_state pm_state;
        struct snd_soc_card *soc_card;
+       struct sst_module_runtime *runtime_waves; /* sound effect module */
 
        /* page tables */
        struct snd_dma_buffer dmab[HSW_PCM_COUNT][2];
@@ -318,6 +319,93 @@ static int hsw_volume_get(struct snd_kcontrol *kcontrol,
        return 0;
 }
 
+static int hsw_waves_switch_get(struct snd_kcontrol *kcontrol,
+                               struct snd_ctl_elem_value *ucontrol)
+{
+       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;
+       enum sst_hsw_module_id id = SST_HSW_MODULE_WAVES;
+
+       ucontrol->value.integer.value[0] =
+               (sst_hsw_is_module_active(hsw, id) ||
+               sst_hsw_is_module_enabled_rtd3(hsw, id));
+       return 0;
+}
+
+static int hsw_waves_switch_put(struct snd_kcontrol *kcontrol,
+                               struct snd_ctl_elem_value *ucontrol)
+{
+       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;
+       int ret = 0;
+       enum sst_hsw_module_id id = SST_HSW_MODULE_WAVES;
+       bool switch_on = (bool)ucontrol->value.integer.value[0];
+
+       /* if module is in RAM on the DSP, apply user settings to module through
+        * ipc. If module is not in RAM on the DSP, store user setting for
+        * track */
+       if (sst_hsw_is_module_loaded(hsw, id)) {
+               if (switch_on == sst_hsw_is_module_active(hsw, id))
+                       return 0;
+
+               if (switch_on)
+                       ret = sst_hsw_module_enable(hsw, id, 0);
+               else
+                       ret = sst_hsw_module_disable(hsw, id, 0);
+       } else {
+               if (switch_on == sst_hsw_is_module_enabled_rtd3(hsw, id))
+                       return 0;
+
+               if (switch_on)
+                       sst_hsw_set_module_enabled_rtd3(hsw, id);
+               else
+                       sst_hsw_set_module_disabled_rtd3(hsw, id);
+       }
+
+       return ret;
+}
+
+static int hsw_waves_param_get(struct snd_kcontrol *kcontrol,
+                               struct snd_ctl_elem_value *ucontrol)
+{
+       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;
+
+       /* return a matching line from param buffer */
+       return sst_hsw_load_param_line(hsw, ucontrol->value.bytes.data);
+}
+
+static int hsw_waves_param_put(struct snd_kcontrol *kcontrol,
+                               struct snd_ctl_elem_value *ucontrol)
+{
+       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;
+       int ret;
+       enum sst_hsw_module_id id = SST_HSW_MODULE_WAVES;
+       int param_id = ucontrol->value.bytes.data[0];
+       int param_size = WAVES_PARAM_COUNT;
+
+       /* clear param buffer and reset buffer index */
+       if (param_id == 0xFF) {
+               sst_hsw_reset_param_buf(hsw);
+               return 0;
+       }
+
+       /* store params into buffer */
+       ret = sst_hsw_store_param_line(hsw, ucontrol->value.bytes.data);
+       if (ret < 0)
+               return ret;
+
+       if (sst_hsw_is_module_active(hsw, id))
+               ret = sst_hsw_module_set_param(hsw, id, 0, param_id,
+                               param_size, ucontrol->value.bytes.data);
+       return ret;
+}
+
 /* TLV used by both global and stream volumes */
 static const DECLARE_TLV_DB_SCALE(hsw_vol_tlv, -9000, 300, 1);
 
@@ -339,6 +427,12 @@ static const struct snd_kcontrol_new hsw_volume_controls[] = {
        SOC_DOUBLE_EXT_TLV("Mic Capture Volume", 4, 0, 8,
                ARRAY_SIZE(volume_map) - 1, 0,
                hsw_stream_volume_get, hsw_stream_volume_put, hsw_vol_tlv),
+       /* enable/disable module waves */
+       SOC_SINGLE_BOOL_EXT("Waves Switch", 0,
+               hsw_waves_switch_get, hsw_waves_switch_put),
+       /* set parameters to module waves */
+       SND_SOC_BYTES_EXT("Waves Set Param", WAVES_PARAM_COUNT,
+               hsw_waves_param_get, hsw_waves_param_put),
 };
 
 /* Create DMA buffer page table for DSP */
@@ -807,6 +901,14 @@ static int hsw_pcm_create_modules(struct hsw_priv_data *pdata)
                        pcm_data->runtime->persistent_offset;
        }
 
+       /* create runtime blocks for module waves */
+       if (sst_hsw_is_module_loaded(hsw, SST_HSW_MODULE_WAVES)) {
+               pdata->runtime_waves = sst_hsw_runtime_module_create(hsw,
+                       SST_HSW_MODULE_WAVES, 0);
+               if (pdata->runtime_waves == NULL)
+                       goto err;
+       }
+
        return 0;
 
 err:
@@ -820,14 +922,17 @@ err:
 
 static void hsw_pcm_free_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[mod_map[i].dai_id][mod_map[i].stream];
-
                sst_hsw_runtime_module_free(pcm_data->runtime);
        }
+       if (sst_hsw_is_module_loaded(hsw, SST_HSW_MODULE_WAVES)) {
+               sst_hsw_runtime_module_free(pdata->runtime_waves);
+       }
 }
 
 static int hsw_pcm_new(struct snd_soc_pcm_runtime *rtd)
@@ -984,7 +1089,9 @@ static int hsw_pcm_probe(struct snd_soc_platform *platform)
        }
 
        /* allocate runtime modules */
-       hsw_pcm_create_modules(priv_data);
+       ret = hsw_pcm_create_modules(priv_data);
+       if (ret < 0)
+               goto err;
 
        /* enable runtime PM with auto suspend */
        pm_runtime_set_autosuspend_delay(platform->dev,
@@ -996,7 +1103,7 @@ static int hsw_pcm_probe(struct snd_soc_platform *platform)
        return 0;
 
 err:
-       for (;i >= 0; i--) {
+       for (--i; i >= 0; i--) {
                if (hsw_dais[i].playback.channels_min)
                        snd_dma_free_pages(&priv_data->dmab[i][0]);
                if (hsw_dais[i].capture.channels_min)
@@ -1101,10 +1208,18 @@ static int hsw_pcm_runtime_suspend(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_RTD3)
                return 0;
 
+       /* fw modules will be unloaded on RTD3, set flag to track */
+       if (sst_hsw_is_module_active(hsw, SST_HSW_MODULE_WAVES)) {
+               ret = sst_hsw_module_disable(hsw, SST_HSW_MODULE_WAVES, 0);
+               if (ret < 0)
+                       return ret;
+               sst_hsw_set_module_enabled_rtd3(hsw, SST_HSW_MODULE_WAVES);
+       }
        sst_hsw_dsp_runtime_suspend(hsw);
        sst_hsw_dsp_runtime_sleep(hsw);
        pdata->pm_state = HSW_PM_STATE_RTD3;
@@ -1139,6 +1254,19 @@ static int hsw_pcm_runtime_resume(struct device *dev)
        else if (ret == 1) /* no action required */
                return 0;
 
+       /* check flag when resume */
+       if (sst_hsw_is_module_enabled_rtd3(hsw, SST_HSW_MODULE_WAVES)) {
+               ret = sst_hsw_module_enable(hsw, SST_HSW_MODULE_WAVES, 0);
+               if (ret < 0)
+                       return ret;
+               /* put parameters from buffer to dsp */
+               ret = sst_hsw_launch_param_buf(hsw);
+               if (ret < 0)
+                       return ret;
+               /* unset flag */
+               sst_hsw_set_module_disabled_rtd3(hsw, SST_HSW_MODULE_WAVES);
+       }
+
        pdata->pm_state = HSW_PM_STATE_D0;
        return ret;
 }