Merge branch 'topic/rt5670' of git://git.kernel.org/pub/scm/linux/kernel/git/broonie...
authorMark Brown <broonie@kernel.org>
Tue, 17 Mar 2015 12:16:16 +0000 (12:16 +0000)
committerMark Brown <broonie@kernel.org>
Tue, 17 Mar 2015 12:16:16 +0000 (12:16 +0000)
20 files changed:
include/sound/pcm_params.h
include/sound/soc.h
sound/soc/intel/broadwell.c
sound/soc/intel/bytcr_dpcm_rt5640.c
sound/soc/intel/cht_bsw_rt5645.c
sound/soc/intel/cht_bsw_rt5672.c
sound/soc/intel/haswell.c
sound/soc/intel/sst-dsp-priv.h
sound/soc/intel/sst-firmware.c
sound/soc/intel/sst-haswell-dsp.c
sound/soc/intel/sst-haswell-ipc.c
sound/soc/intel/sst-haswell-ipc.h
sound/soc/intel/sst-haswell-pcm.c
sound/soc/intel/sst-mfld-platform-pcm.c
sound/soc/intel/sst-mfld-platform.h
sound/soc/intel/sst/sst.c
sound/soc/intel/sst/sst.h
sound/soc/intel/sst/sst_drv_interface.c
sound/soc/intel/sst/sst_loader.c
sound/soc/soc-pcm.c

index 3c45f3924ba7e779a4d32a357665d0359abcbaaf..c704357775fc4ef077ef52fb27cfb97c75b378ee 100644 (file)
@@ -366,4 +366,11 @@ static inline int params_physical_width(const struct snd_pcm_hw_params *p)
        return snd_pcm_format_physical_width(params_format(p));
 }
 
+static inline void
+params_set_format(struct snd_pcm_hw_params *p, snd_pcm_format_t fmt)
+{
+       snd_mask_set(hw_param_mask(p, SNDRV_PCM_HW_PARAM_FORMAT),
+               (__force int)fmt);
+}
+
 #endif /* __SOUND_PCM_PARAMS_H */
index 0d1ade19562857c7b65157636ac37b2f0222e202..76bc944dcb5cbf55adf51d04f776a8dd1ae09cb5 100644 (file)
@@ -954,6 +954,9 @@ struct snd_soc_dai_link {
        unsigned int symmetric_channels:1;
        unsigned int symmetric_samplebits:1;
 
+       /* Mark this pcm with non atomic ops */
+       bool nonatomic;
+
        /* Do not create a PCM for this DAI link (Backend link) */
        unsigned int no_pcm:1;
 
index 9cf7d01479adcbd82661c70344b5f5d4ad0969fe..af5d73070f60f9ef5215a9a0c50727e92442bea1 100644 (file)
@@ -110,9 +110,7 @@ static int broadwell_ssp0_fixup(struct snd_soc_pcm_runtime *rtd,
        channels->min = channels->max = 2;
 
        /* set SSP0 to 16 bit */
-       snd_mask_set(&params->masks[SNDRV_PCM_HW_PARAM_FORMAT -
-                                   SNDRV_PCM_HW_PARAM_FIRST_MASK],
-                                   SNDRV_PCM_FORMAT_S16_LE);
+       params_set_format(params, SNDRV_PCM_FORMAT_S16_LE);
        return 0;
 }
 
@@ -227,6 +225,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",
@@ -240,6 +264,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)
index 59308629043e62dd2c2fa0bcc9e4cb126d7a74ba..3b262d01c1b370b9166d8e152486874d26f2aebd 100644 (file)
@@ -113,9 +113,7 @@ static int byt_codec_fixup(struct snd_soc_pcm_runtime *rtd,
        channels->min = channels->max = 2;
 
        /* set SSP2 to 24-bit */
-       snd_mask_set(&params->masks[SNDRV_PCM_HW_PARAM_FORMAT -
-                                   SNDRV_PCM_HW_PARAM_FIRST_MASK],
-                                   SNDRV_PCM_FORMAT_S24_LE);
+       params_set_format(params, SNDRV_PCM_FORMAT_S24_LE);
        return 0;
 }
 
index bd29617a9ab9d2a2c396b05e25a858855529bb45..dd935255a0206f8e123e881c34eb94a8ac5be561 100644 (file)
@@ -203,9 +203,7 @@ static int cht_codec_fixup(struct snd_soc_pcm_runtime *rtd,
        channels->min = channels->max = 2;
 
        /* set SSP2 to 24-bit */
-       snd_mask_set(&params->masks[SNDRV_PCM_HW_PARAM_FORMAT -
-                                   SNDRV_PCM_HW_PARAM_FIRST_MASK],
-                                   SNDRV_PCM_FORMAT_S24_LE);
+       params_set_format(params, SNDRV_PCM_FORMAT_S24_LE);
        return 0;
 }
 
index ff016621583a63c85c96689270405cbc5640ab77..279df4c43de16cdc1de5ce443088255b0e9b4ba6 100644 (file)
@@ -50,6 +50,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 +58,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 +92,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[] = {
@@ -178,9 +194,7 @@ static int cht_codec_fixup(struct snd_soc_pcm_runtime *rtd,
        channels->min = channels->max = 2;
 
        /* set SSP2 to 24-bit */
-       snd_mask_set(&params->masks[SNDRV_PCM_HW_PARAM_FORMAT -
-                                   SNDRV_PCM_HW_PARAM_FIRST_MASK],
-                                   SNDRV_PCM_FORMAT_S24_LE);
+       params_set_format(params, SNDRV_PCM_FORMAT_S24_LE);
        return 0;
 }
 
@@ -217,7 +231,7 @@ static struct snd_soc_dai_link cht_dailink[] = {
                .codec_dai_name = "snd-soc-dummy-dai",
                .codec_name = "snd-soc-dummy",
                .platform_name = "sst-mfld-platform",
-               .ignore_suspend = 1,
+               .nonatomic = true,
                .dynamic = 1,
                .dpcm_playback = 1,
                .dpcm_capture = 1,
@@ -240,13 +254,13 @@ static struct snd_soc_dai_link cht_dailink[] = {
                .cpu_dai_name = "ssp2-port",
                .platform_name = "sst-mfld-platform",
                .no_pcm = 1,
+               .nonatomic = true,
                .codec_dai_name = "rt5670-aif1",
                .codec_name = "i2c-10EC5670:00",
                .dai_fmt = SND_SOC_DAIFMT_DSP_B | SND_SOC_DAIFMT_IB_NF
                                        | SND_SOC_DAIFMT_CBS_CFS,
                .init = cht_codec_init,
                .be_hw_params_fixup = cht_codec_fixup,
-               .ignore_suspend = 1,
                .dpcm_playback = 1,
                .dpcm_capture = 1,
                .ops = &cht_be_ssp2_ops,
@@ -285,7 +299,6 @@ static int snd_cht_mc_probe(struct platform_device *pdev)
 static struct platform_driver snd_cht_mc_driver = {
        .driver = {
                .name = "cht-bsw-rt5672",
-               .pm = &snd_soc_pm_ops,
        },
        .probe = snd_cht_mc_probe,
 };
index 35edf51a52aa92b3ace9156d4b3557f3db2282aa..00fddd3f5dfb8f7350180b8b564d282d4918331b 100644 (file)
@@ -56,9 +56,7 @@ static int haswell_ssp0_fixup(struct snd_soc_pcm_runtime *rtd,
        channels->min = channels->max = 2;
 
        /* set SSP0 to 16 bit */
-       snd_mask_set(&params->masks[SNDRV_PCM_HW_PARAM_FORMAT -
-                                   SNDRV_PCM_HW_PARAM_FIRST_MASK],
-                                   SNDRV_PCM_FORMAT_S16_LE);
+       params_set_format(params, SNDRV_PCM_FORMAT_S16_LE);
        return 0;
 }
 
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;
 };
 
 /*
index 5f71ef607a57e417715154b1697ee04ef0a13772..5e5800897da220b6f4001cdca2fb828201da24a6 100644 (file)
@@ -498,6 +498,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);
index c42ffae5fe9f6acab9ebe5723862c4e3a3b4bf8d..b3e957d469336599c01d573ff4341afa1e891c28 100644 (file)
@@ -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;
 }
@@ -207,9 +210,6 @@ static int hsw_parse_fw_image(struct sst_fw *sst_fw)
                module = (void *)module + sizeof(*module) + module->mod_size;
        }
 
-       /* allocate scratch mem regions */
-       sst_block_alloc_scratch(dsp);
-
        return 0;
 }
 
index 394af5684c05e37403b50b67ebaa7ead69d14f1f..43fb5f3391683607bb30f999a8d2fd9906009d65 100644 (file)
 #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 +124,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 +143,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,
@@ -317,6 +337,15 @@ struct sst_hsw {
 
        /* 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 +381,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) {
@@ -795,6 +834,31 @@ static int hsw_process_reply(struct sst_hsw *hsw, u32 header)
        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 +954,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);
@@ -1732,6 +1799,7 @@ static void sst_hsw_drop_all(struct sst_hsw *hsw)
 int sst_hsw_dsp_load(struct sst_hsw *hsw)
 {
        struct sst_dsp *dsp = hsw->dsp;
+       struct sst_fw *sst_fw, *t;
        int ret;
 
        dev_dbg(hsw->dev, "loading audio DSP....");
@@ -1748,12 +1816,17 @@ int sst_hsw_dsp_load(struct sst_hsw *hsw)
                return ret;
        }
 
-       ret = sst_fw_reload(hsw->sst_fw);
-       if (ret < 0) {
-               dev_err(hsw->dev, "error: SST FW reload failed\n");
-               sst_dsp_dma_put_channel(dsp);
-               return -ENOMEM;
+       list_for_each_entry_safe_reverse(sst_fw, t, &dsp->fw_list, list) {
+               ret = sst_fw_reload(sst_fw);
+               if (ret < 0) {
+                       dev_err(hsw->dev, "error: SST FW reload failed\n");
+                       sst_dsp_dma_put_channel(dsp);
+                       return -ENOMEM;
+               }
        }
+       ret = sst_block_alloc_scratch(hsw->dsp);
+       if (ret < 0)
+               return -EINVAL;
 
        sst_dsp_dma_put_channel(dsp);
        return 0;
@@ -1809,12 +1882,17 @@ int sst_hsw_dsp_runtime_suspend(struct sst_hsw *hsw)
 
 int sst_hsw_dsp_runtime_sleep(struct sst_hsw *hsw)
 {
-       sst_fw_unload(hsw->sst_fw);
-       sst_block_free_scratch(hsw->dsp);
+       struct sst_fw *sst_fw, *t;
+       struct sst_dsp *dsp = hsw->dsp;
+
+       list_for_each_entry_safe(sst_fw, t, &dsp->fw_list, list) {
+               sst_fw_unload(sst_fw);
+       }
+       sst_block_free_scratch(dsp);
 
        hsw->boot_complete = false;
 
-       sst_dsp_sleep(hsw->dsp);
+       sst_dsp_sleep(dsp);
 
        return 0;
 }
@@ -1833,6 +1911,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) {
@@ -1875,6 +1955,337 @@ struct sst_dsp *sst_hsw_get_dsp(struct sst_hsw *hsw)
        return hsw->dsp;
 }
 
+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;
+}
+
+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;
+
+       /* 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;
+}
+
+int sst_hsw_module_load(struct sst_hsw *hsw,
+       u32 module_id, u32 instance_id, char *name)
+{
+       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 = ipc_tx_message_wait(hsw, 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 = ipc_tx_message_wait(hsw, 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 = ipc_tx_message_wait(hsw, 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 = {
        .thread = hsw_irq_thread,
        .ops = &haswell_ops,
@@ -1936,12 +2347,21 @@ 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);
@@ -1955,6 +2375,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);
 
@@ -1970,7 +2393,7 @@ 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);
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);
index 7e21e8f85885e436cb896a806dde544d3cd8ec4d..b40ec746bc19f558fc65a8ca33f7ff6ebbc91f13 100644 (file)
@@ -318,6 +318,97 @@ 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_loaded(hsw, id)) {
+               if (!sst_hsw_is_module_active(hsw, id))
+                       return 0;
+
+               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 +430,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 +904,17 @@ 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)) {
+               pcm_data = &pdata->pcm[HSW_PCM_COUNT-1][0];
+               pcm_data->runtime = sst_hsw_runtime_module_create(hsw,
+                       SST_HSW_MODULE_WAVES, pcm_data->persistent_offset);
+               if (pcm_data->runtime == NULL)
+                       goto err;
+               pcm_data->persistent_offset =
+                       pcm_data->runtime->persistent_offset;
+       }
+
        return 0;
 
 err:
@@ -820,12 +928,16 @@ 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)) {
+               pcm_data = &pdata->pcm[HSW_PCM_COUNT-1][0];
                sst_hsw_runtime_module_free(pcm_data->runtime);
        }
 }
@@ -984,7 +1096,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,
@@ -1101,10 +1215,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 +1261,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;
 }
index 7523cbef87807f2b3ac0d4209e939a5b1bda7567..2fbaf2c75d1709e517a5fb1b1d8012f58865992c 100644 (file)
@@ -594,11 +594,13 @@ static int sst_platform_pcm_trigger(struct snd_pcm_substream *substream,
                ret_val = stream->ops->stream_drop(sst->dev, str_id);
                break;
        case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+       case SNDRV_PCM_TRIGGER_SUSPEND:
                dev_dbg(rtd->dev, "sst: in pause\n");
                status = SST_PLATFORM_PAUSED;
                ret_val = stream->ops->stream_pause(sst->dev, str_id);
                break;
        case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+       case SNDRV_PCM_TRIGGER_RESUME:
                dev_dbg(rtd->dev, "sst: in pause release\n");
                status = SST_PLATFORM_RUNNING;
                ret_val = stream->ops->stream_pause_release(sst->dev, str_id);
@@ -665,6 +667,9 @@ static int sst_pcm_new(struct snd_soc_pcm_runtime *rtd)
 
 static int sst_soc_probe(struct snd_soc_platform *platform)
 {
+       struct sst_data *drv = dev_get_drvdata(platform->dev);
+
+       drv->soc_card = platform->component.card;
        return sst_dsp_init_v2_dpcm(platform);
 }
 
@@ -727,9 +732,64 @@ static int sst_platform_remove(struct platform_device *pdev)
        return 0;
 }
 
+#ifdef CONFIG_PM_SLEEP
+
+static int sst_soc_prepare(struct device *dev)
+{
+       struct sst_data *drv = dev_get_drvdata(dev);
+       int i;
+
+       /* suspend all pcms first */
+       snd_soc_suspend(drv->soc_card->dev);
+       snd_soc_poweroff(drv->soc_card->dev);
+
+       /* set the SSPs to idle */
+       for (i = 0; i < drv->soc_card->num_rtd; i++) {
+               struct snd_soc_dai *dai = drv->soc_card->rtd[i].cpu_dai;
+
+               if (dai->active) {
+                       send_ssp_cmd(dai, dai->name, 0);
+                       sst_handle_vb_timer(dai, false);
+               }
+       }
+
+       return 0;
+}
+
+static void sst_soc_complete(struct device *dev)
+{
+       struct sst_data *drv = dev_get_drvdata(dev);
+       int i;
+
+       /* restart SSPs */
+       for (i = 0; i < drv->soc_card->num_rtd; i++) {
+               struct snd_soc_dai *dai = drv->soc_card->rtd[i].cpu_dai;
+
+               if (dai->active) {
+                       sst_handle_vb_timer(dai, true);
+                       send_ssp_cmd(dai, dai->name, 1);
+               }
+       }
+       snd_soc_resume(drv->soc_card->dev);
+}
+
+#else
+
+#define sst_soc_prepare NULL
+#define sst_soc_complete NULL
+
+#endif
+
+
+static const struct dev_pm_ops sst_platform_pm = {
+       .prepare        = sst_soc_prepare,
+       .complete       = sst_soc_complete,
+};
+
 static struct platform_driver sst_platform_driver = {
        .driver         = {
                .name           = "sst-mfld-platform",
+               .pm             = &sst_platform_pm,
        },
        .probe          = sst_platform_probe,
        .remove         = sst_platform_remove,
index 79c8d1246a8fb396a4e703694f85e26fb378203b..9094314be2b043e724bfcf3db3f0f54b10dd61d4 100644 (file)
@@ -174,6 +174,7 @@ struct sst_data {
        struct sst_platform_data *pdata;
        struct snd_sst_bytes_v2 *byte_stream;
        struct mutex lock;
+       struct snd_soc_card *soc_card;
 };
 int sst_register_dsp(struct sst_device *sst);
 int sst_unregister_dsp(struct sst_device *sst);
index 11c578651c1c8cc778904ebb7665587cde44beef..1a7eeec444b14bc55ab8fcc434a23f39cdd91913 100644 (file)
@@ -423,23 +423,135 @@ static int intel_sst_runtime_suspend(struct device *dev)
        return ret;
 }
 
-static int intel_sst_runtime_resume(struct device *dev)
+static int intel_sst_suspend(struct device *dev)
 {
-       int ret = 0;
        struct intel_sst_drv *ctx = dev_get_drvdata(dev);
+       struct sst_fw_save *fw_save;
+       int i, ret = 0;
 
-       if (ctx->sst_state == SST_RESET) {
-               ret = sst_load_fw(ctx);
-               if (ret) {
-                       dev_err(dev, "FW download fail %d\n", ret);
-                       sst_set_fw_state_locked(ctx, SST_RESET);
+       /* check first if we are already in SW reset */
+       if (ctx->sst_state == SST_RESET)
+               return 0;
+
+       /*
+        * check if any stream is active and running
+        * they should already by suspend by soc_suspend
+        */
+       for (i = 1; i <= ctx->info.max_streams; i++) {
+               struct stream_info *stream = &ctx->streams[i];
+
+               if (stream->status == STREAM_RUNNING) {
+                       dev_err(dev, "stream %d is running, cant susupend, abort\n", i);
+                       return -EBUSY;
                }
        }
+       synchronize_irq(ctx->irq_num);
+       flush_workqueue(ctx->post_msg_wq);
+
+       /* Move the SST state to Reset */
+       sst_set_fw_state_locked(ctx, SST_RESET);
+
+       /* tell DSP we are suspending */
+       if (ctx->ops->save_dsp_context(ctx))
+               return -EBUSY;
+
+       /* save the memories */
+       fw_save = kzalloc(sizeof(*fw_save), GFP_KERNEL);
+       if (!fw_save)
+               return -ENOMEM;
+       fw_save->iram = kzalloc(ctx->iram_end - ctx->iram_base, GFP_KERNEL);
+       if (!fw_save->iram) {
+               ret = -ENOMEM;
+               goto iram;
+       }
+       fw_save->dram = kzalloc(ctx->dram_end - ctx->dram_base, GFP_KERNEL);
+       if (!fw_save->dram) {
+               ret = -ENOMEM;
+               goto dram;
+       }
+       fw_save->sram = kzalloc(SST_MAILBOX_SIZE, GFP_KERNEL);
+       if (!fw_save->sram) {
+               ret = -ENOMEM;
+               goto sram;
+       }
+
+       fw_save->ddr = kzalloc(ctx->ddr_end - ctx->ddr_base, GFP_KERNEL);
+       if (!fw_save->ddr) {
+               ret = -ENOMEM;
+               goto ddr;
+       }
+
+       memcpy32_fromio(fw_save->iram, ctx->iram, ctx->iram_end - ctx->iram_base);
+       memcpy32_fromio(fw_save->dram, ctx->dram, ctx->dram_end - ctx->dram_base);
+       memcpy32_fromio(fw_save->sram, ctx->mailbox, SST_MAILBOX_SIZE);
+       memcpy32_fromio(fw_save->ddr, ctx->ddr, ctx->ddr_end - ctx->ddr_base);
+
+       ctx->fw_save = fw_save;
+       ctx->ops->reset(ctx);
+       return 0;
+ddr:
+       kfree(fw_save->sram);
+sram:
+       kfree(fw_save->dram);
+dram:
+       kfree(fw_save->iram);
+iram:
+       kfree(fw_save);
+       return ret;
+}
+
+static int intel_sst_resume(struct device *dev)
+{
+       struct intel_sst_drv *ctx = dev_get_drvdata(dev);
+       struct sst_fw_save *fw_save = ctx->fw_save;
+       int ret = 0;
+       struct sst_block *block;
+
+       if (!fw_save)
+               return 0;
+
+       sst_set_fw_state_locked(ctx, SST_FW_LOADING);
+
+       /* we have to restore the memory saved */
+       ctx->ops->reset(ctx);
+
+       ctx->fw_save = NULL;
+
+       memcpy32_toio(ctx->iram, fw_save->iram, ctx->iram_end - ctx->iram_base);
+       memcpy32_toio(ctx->dram, fw_save->dram, ctx->dram_end - ctx->dram_base);
+       memcpy32_toio(ctx->mailbox, fw_save->sram, SST_MAILBOX_SIZE);
+       memcpy32_toio(ctx->ddr, fw_save->ddr, ctx->ddr_end - ctx->ddr_base);
+
+       kfree(fw_save->sram);
+       kfree(fw_save->dram);
+       kfree(fw_save->iram);
+       kfree(fw_save->ddr);
+       kfree(fw_save);
+
+       block = sst_create_block(ctx, 0, FW_DWNL_ID);
+       if (block == NULL)
+               return -ENOMEM;
+
+
+       /* start and wait for ack */
+       ctx->ops->start(ctx);
+       ret = sst_wait_timeout(ctx, block);
+       if (ret) {
+               dev_err(ctx->dev, "fw download failed %d\n", ret);
+               /* FW download failed due to timeout */
+               ret = -EBUSY;
+
+       } else {
+               sst_set_fw_state_locked(ctx, SST_FW_RUNNING);
+       }
+
+       sst_free_block(ctx, block);
        return ret;
 }
 
 const struct dev_pm_ops intel_sst_pm = {
+       .suspend = intel_sst_suspend,
+       .resume = intel_sst_resume,
        .runtime_suspend = intel_sst_runtime_suspend,
-       .runtime_resume = intel_sst_runtime_resume,
 };
 EXPORT_SYMBOL_GPL(intel_sst_pm);
index 562bc483d6b7f73924c6cd25fd2770b8c48bdc6b..3f493862e98dd0bd26b9e1733c4b05b729c963f5 100644 (file)
@@ -337,6 +337,13 @@ struct sst_shim_regs64 {
        u64 csr2;
 };
 
+struct sst_fw_save {
+       void *iram;
+       void *dram;
+       void *sram;
+       void *ddr;
+};
+
 /**
  * struct intel_sst_drv - driver ops
  *
@@ -428,6 +435,8 @@ struct intel_sst_drv {
         * persistent till worker thread gets called
         */
        char firmware_name[FW_NAME_SIZE];
+
+       struct sst_fw_save      *fw_save;
 };
 
 /* misc definitions */
@@ -544,4 +553,7 @@ int sst_alloc_drv_context(struct intel_sst_drv **ctx,
 int sst_context_init(struct intel_sst_drv *ctx);
 void sst_context_cleanup(struct intel_sst_drv *ctx);
 void sst_configure_runtime_pm(struct intel_sst_drv *ctx);
+void memcpy32_toio(void __iomem *dst, const void *src, int count);
+void memcpy32_fromio(void *dst, const void __iomem *src, int count);
+
 #endif
index 5f75ef3cdd229613cd37a11ade1d3d3831385763..f0e4b99b3aeb13a61c1f5a5b5ad67de8c575d737 100644 (file)
@@ -138,12 +138,36 @@ int sst_get_stream(struct intel_sst_drv *ctx,
 static int sst_power_control(struct device *dev, bool state)
 {
        struct intel_sst_drv *ctx = dev_get_drvdata(dev);
-
-       dev_dbg(ctx->dev, "state:%d", state);
-       if (state == true)
-               return pm_runtime_get_sync(dev);
-       else
+       int ret = 0;
+       int usage_count = 0;
+
+#ifdef CONFIG_PM
+       usage_count = atomic_read(&dev->power.usage_count);
+#else
+       usage_count = 1;
+#endif
+
+       if (state == true) {
+               ret = pm_runtime_get_sync(dev);
+
+               dev_dbg(ctx->dev, "Enable: pm usage count: %d\n", usage_count);
+               if (ret < 0) {
+                       dev_err(ctx->dev, "Runtime get failed with err: %d\n", ret);
+                       return ret;
+               }
+               if ((ctx->sst_state == SST_RESET) && (usage_count == 1)) {
+                       ret = sst_load_fw(ctx);
+                       if (ret) {
+                               dev_err(dev, "FW download fail %d\n", ret);
+                               sst_set_fw_state_locked(ctx, SST_RESET);
+                               ret = sst_pm_runtime_put(ctx);
+                       }
+               }
+       } else {
+               dev_dbg(ctx->dev, "Disable: pm usage count: %d\n", usage_count);
                return sst_pm_runtime_put(ctx);
+       }
+       return ret;
 }
 
 /*
@@ -572,6 +596,35 @@ static int sst_stream_drop(struct device *dev, int str_id)
        return sst_drop_stream(ctx, str_id);
 }
 
+static int sst_stream_pause(struct device *dev, int str_id)
+{
+       struct stream_info *str_info;
+       struct intel_sst_drv *ctx = dev_get_drvdata(dev);
+
+       if (ctx->sst_state != SST_FW_RUNNING)
+               return 0;
+
+       str_info = get_stream_info(ctx, str_id);
+       if (!str_info)
+               return -EINVAL;
+
+       return sst_pause_stream(ctx, str_id);
+}
+
+static int sst_stream_resume(struct device *dev, int str_id)
+{
+       struct stream_info *str_info;
+       struct intel_sst_drv *ctx = dev_get_drvdata(dev);
+
+       if (ctx->sst_state != SST_FW_RUNNING)
+               return 0;
+
+       str_info = get_stream_info(ctx, str_id);
+       if (!str_info)
+               return -EINVAL;
+       return sst_resume_stream(ctx, str_id);
+}
+
 static int sst_stream_init(struct device *dev, struct pcm_stream_info *str_info)
 {
        int str_id = 0;
@@ -633,6 +686,8 @@ static struct sst_ops pcm_ops = {
        .stream_init = sst_stream_init,
        .stream_start = sst_stream_start,
        .stream_drop = sst_stream_drop,
+       .stream_pause = sst_stream_pause,
+       .stream_pause_release = sst_stream_resume,
        .stream_read_tstamp = sst_read_timestamp,
        .send_byte_stream = sst_send_byte_stream,
        .close = sst_close_pcm_stream,
index 7888cd707853db0a9819125d71cb063629514d9d..e88907ae8b154fb955037037e5c75aa62f67c8da 100644 (file)
 #include "sst.h"
 #include "../sst-dsp.h"
 
-static inline void memcpy32_toio(void __iomem *dst, const void *src, int count)
+void memcpy32_toio(void __iomem *dst, const void *src, int count)
+{
+       /* __iowrite32_copy uses 32-bit count values so divide by 4 for
+        * right count in words
+        */
+       __iowrite32_copy(dst, src, count/4);
+}
+
+void memcpy32_fromio(void *dst, const void __iomem *src, int count)
 {
        /* __iowrite32_copy uses 32-bit count values so divide by 4 for
         * right count in words
index 6b0136e7cb88c2cebd96b12a6a85dc200f9838d3..6e3781e88f9adf52a35d79a45a785badd5d17709 100644 (file)
@@ -2511,6 +2511,7 @@ int soc_new_pcm(struct snd_soc_pcm_runtime *rtd, int num)
        /* DAPM dai link stream work */
        INIT_DELAYED_WORK(&rtd->delayed_work, close_delayed_work);
 
+       pcm->nonatomic = rtd->dai_link->nonatomic;
        rtd->pcm = pcm;
        pcm->private_data = rtd;