From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 From: "R, Dharageswari" Date: Thu, 28 Dec 2017 08:31:45 +0530 Subject: [PATCH] ASoC: Intel: Skylake: Implement recovery for cAVS platforms This patch implements the Audio dsp crash recovery for cAVS platforms for single and multiple streams. As a part of recovery, the firmware needs to be re-downloaded which requires the DMA channel 0. The patch does the necessary changes to reuse the DMA channel 0 for firmware download Change-Id: Icb09bca1525759d45a7739b42aa4404556087922 Signed-off-by: R, Dharageswari Signed-off-by: Pradeep Tewani Reviewed-on: Reviewed-by: Prusty, Subhransu S Tested-by: Madiwalar, MadiwalappaX --- sound/soc/intel/common/sst-dsp-priv.h | 1 + sound/soc/intel/skylake/bxt-sst.c | 3 ++ sound/soc/intel/skylake/cnl-sst.c | 7 ++++ sound/soc/intel/skylake/skl-messages.c | 54 ++++++++++++++++++++++++-- sound/soc/intel/skylake/skl-pcm.c | 2 + sound/soc/intel/skylake/skl-sst-dsp.h | 2 + sound/soc/intel/skylake/skl-topology.c | 1 + sound/soc/intel/skylake/skl-topology.h | 2 + sound/soc/intel/skylake/skl.c | 3 ++ 9 files changed, 71 insertions(+), 4 deletions(-) diff --git a/sound/soc/intel/common/sst-dsp-priv.h b/sound/soc/intel/common/sst-dsp-priv.h index 196bb7d7ebf0..b9935fdd0910 100644 --- a/sound/soc/intel/common/sst-dsp-priv.h +++ b/sound/soc/intel/common/sst-dsp-priv.h @@ -355,6 +355,7 @@ struct sst_dsp { /* To allocate CL dma buffers */ struct skl_dsp_loader_ops dsp_ops; struct skl_dsp_fw_ops fw_ops; + bool is_recovery; struct skl_cl_dev cl_dev; u32 intr_status; const struct firmware *fw; diff --git a/sound/soc/intel/skylake/bxt-sst.c b/sound/soc/intel/skylake/bxt-sst.c index dd5453daa562..2eb57d75f1b1 100644 --- a/sound/soc/intel/skylake/bxt-sst.c +++ b/sound/soc/intel/skylake/bxt-sst.c @@ -680,6 +680,9 @@ int bxt_sst_init_fw(struct device *dev, struct skl_sst *ctx) int ret; struct sst_dsp *sst = ctx->dsp; + if (sst->is_recovery) + skl_dsp_disable_core(sst, SKL_DSP_CORE0_MASK); + ret = sst->fw_ops.load_fw(sst); if (ret < 0) { dev_err(dev, "Load base fw failed: %x\n", ret); diff --git a/sound/soc/intel/skylake/cnl-sst.c b/sound/soc/intel/skylake/cnl-sst.c index bfc4da85c043..59448c1ba37c 100644 --- a/sound/soc/intel/skylake/cnl-sst.c +++ b/sound/soc/intel/skylake/cnl-sst.c @@ -796,6 +796,13 @@ int cnl_sst_init_fw(struct device *dev, struct skl_sst *ctx) struct sst_dsp *sst = ctx->dsp; int ret; + if (sst->is_recovery) { + cnl_dsp_disable_core(sst, SKL_DSP_CORE0_MASK); + ret = cnl_load_base_firmware(sst); + if (ret < 0) + return ret; + } + skl_dsp_init_core_state(sst); if (ctx->lib_count > 1) { diff --git a/sound/soc/intel/skylake/skl-messages.c b/sound/soc/intel/skylake/skl-messages.c index 5fb1093cdfa2..bc8d3afcf7d1 100644 --- a/sound/soc/intel/skylake/skl-messages.c +++ b/sound/soc/intel/skylake/skl-messages.c @@ -293,7 +293,8 @@ static const struct skl_dsp_ops dsp_ops[] = { .loader_ops = bxt_get_loader_ops, .init = bxt_sst_dsp_init, .init_fw = bxt_sst_init_fw, - .cleanup = bxt_sst_dsp_cleanup + .cleanup = bxt_sst_dsp_cleanup, + .do_recovery = skl_do_recovery }, { .id = 0x3198, @@ -301,7 +302,8 @@ static const struct skl_dsp_ops dsp_ops[] = { .loader_ops = bxt_get_loader_ops, .init = bxt_sst_dsp_init, .init_fw = bxt_sst_init_fw, - .cleanup = bxt_sst_dsp_cleanup + .cleanup = bxt_sst_dsp_cleanup, + .do_recovery = skl_do_recovery }, { .id = 0x9dc8, @@ -309,7 +311,8 @@ static const struct skl_dsp_ops dsp_ops[] = { .loader_ops = bxt_get_loader_ops, .init = cnl_sst_dsp_init, .init_fw = cnl_sst_init_fw, - .cleanup = cnl_sst_dsp_cleanup + .cleanup = cnl_sst_dsp_cleanup, + .do_recovery = skl_do_recovery }, { .id = 0x34c8, @@ -317,7 +320,8 @@ static const struct skl_dsp_ops dsp_ops[] = { .loader_ops = bxt_get_loader_ops, .init = cnl_sst_dsp_init, .init_fw = cnl_sst_init_fw, - .cleanup = cnl_sst_dsp_cleanup + .cleanup = cnl_sst_dsp_cleanup, + .do_recovery = skl_do_recovery }, }; @@ -364,6 +368,48 @@ static int cnl_sdw_bra_pipe_trigger(struct skl_sst *ctx, bool enable, return ret; } +void skl_do_recovery(struct skl *skl) +{ + struct snd_soc_component *soc_component = skl->component; + const struct skl_dsp_ops *ops; + struct snd_soc_card *card; + struct hdac_stream *azx_dev; + struct hdac_ext_bus *ebus = &skl->ebus; + struct hdac_bus *bus = ebus_to_hbus(ebus); + struct snd_pcm_substream *substream = NULL; + struct hdac_ext_stream *stream; + + skl->skl_sst->dsp->is_recovery = true; + skl_dsp_reset_core_state(skl->skl_sst->dsp); + card = soc_component->card; + snd_soc_suspend(card->dev); + skl_cleanup_resources(skl); + skl_reset_instance_id(skl->skl_sst); + + /* Free up DMA channel 0 for firmware re-download */ + list_for_each_entry(azx_dev, &bus->stream_list, list) { + if (azx_dev->stream_tag == 1 && + azx_dev->direction == SNDRV_PCM_STREAM_PLAYBACK) { + if (azx_dev->opened) { + substream = azx_dev->substream; + stream = stream_to_hdac_ext_stream(azx_dev); + snd_hdac_ext_stream_release(stream, + skl_get_host_stream_type(ebus)); + } + break; + } + } + ops = skl_get_dsp_ops(skl->pci->device); + if (ops->init_fw(soc_component->dev, skl->skl_sst) < 0) + dev_err(skl->skl_sst->dev, "Recovery failed\n"); + if (substream != NULL) { + stream = snd_hdac_ext_stream_assign(ebus, substream, + skl_get_host_stream_type(ebus)); + } + snd_soc_resume(card->dev); + skl->skl_sst->dsp->is_recovery = false; +} + void skl_trigger_recovery(struct work_struct *work) { struct skl_monitor *monitor_dsp = container_of(work, diff --git a/sound/soc/intel/skylake/skl-pcm.c b/sound/soc/intel/skylake/skl-pcm.c index a2315144d7e3..15fbded46dcb 100644 --- a/sound/soc/intel/skylake/skl-pcm.c +++ b/sound/soc/intel/skylake/skl-pcm.c @@ -189,6 +189,7 @@ int skl_pcm_host_dma_prepare(struct device *dev, struct skl_pipe_params *params) params->host_dma_id + 1); if (!hstream) return -EINVAL; + hstream->substream = params->substream; stream = stream_to_hdac_ext_stream(hstream); snd_hdac_ext_stream_decouple(bus, stream, true); @@ -377,6 +378,7 @@ static int skl_pcm_hw_params(struct snd_pcm_substream *substream, p_params.host_dma_id = dma_id; p_params.stream = substream->stream; p_params.format = params_format(params); + p_params.substream = substream; if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) p_params.host_bps = dai->driver->playback.sig_bits; else diff --git a/sound/soc/intel/skylake/skl-sst-dsp.h b/sound/soc/intel/skylake/skl-sst-dsp.h index ef9bf4a4a1b7..dc793d503115 100644 --- a/sound/soc/intel/skylake/skl-sst-dsp.h +++ b/sound/soc/intel/skylake/skl-sst-dsp.h @@ -22,6 +22,7 @@ #include #include #include "skl-sst-cldma.h" +#include "skl.h" struct sst_dsp; struct skl_sst; @@ -270,6 +271,7 @@ struct sst_dsp *skl_dsp_ctx_init(struct device *dev, struct sst_dsp_device *sst_dev, int irq); int skl_dsp_acquire_irq(struct sst_dsp *sst); bool is_skl_dsp_running(struct sst_dsp *ctx); +void skl_do_recovery(struct skl *skl); unsigned int skl_dsp_get_enabled_cores(struct sst_dsp *ctx); void skl_dsp_init_core_state(struct sst_dsp *ctx); diff --git a/sound/soc/intel/skylake/skl-topology.c b/sound/soc/intel/skylake/skl-topology.c index e7c86d4b2439..1606aee09306 100644 --- a/sound/soc/intel/skylake/skl-topology.c +++ b/sound/soc/intel/skylake/skl-topology.c @@ -2517,6 +2517,7 @@ static void skl_tplg_fill_dma_id(struct skl_module_cfg *mcfg, pipe->p_params->s_freq = params->s_freq; pipe->p_params->stream = params->stream; pipe->p_params->format = params->format; + pipe->p_params->substream = params->substream; } else { memcpy(pipe->p_params, params, sizeof(*params)); diff --git a/sound/soc/intel/skylake/skl-topology.h b/sound/soc/intel/skylake/skl-topology.h index 97b9614f57af..e86b84e9868d 100644 --- a/sound/soc/intel/skylake/skl-topology.h +++ b/sound/soc/intel/skylake/skl-topology.h @@ -317,6 +317,7 @@ struct skl_pipe_params { int stream; unsigned int host_bps; unsigned int link_bps; + struct snd_pcm_substream *substream; }; struct skl_pipe_fmt { @@ -616,6 +617,7 @@ int skl_pcm_host_dma_prepare(struct device *dev, struct skl_pipe_params *params); int skl_pcm_link_dma_prepare(struct device *dev, struct skl_pipe_params *params); +enum hdac_ext_stream_type skl_get_host_stream_type(struct hdac_ext_bus *ebus); int skl_dai_load(struct snd_soc_component *cmp, int index, struct snd_soc_dai_driver *dai_drv, diff --git a/sound/soc/intel/skylake/skl.c b/sound/soc/intel/skylake/skl.c index 40cd27fad180..44718e849bd0 100644 --- a/sound/soc/intel/skylake/skl.c +++ b/sound/soc/intel/skylake/skl.c @@ -389,6 +389,9 @@ static int skl_suspend(struct device *dev) struct skl *skl = bus_to_skl(bus); int ret = 0; + if (skl->skl_sst->dsp->is_recovery) + return -EBUSY; + /* * Do not suspend if streams which are marked ignore suspend are * running, we need to save the state for these and continue -- https://clearlinux.org