clear-pkgs-linux-iot-lts2018/0410-ASoC-Intel-SKL-Impleme...

317 lines
9.5 KiB
Diff

From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: "R, Dharageswari" <dharageswari.r@intel.com>
Date: Mon, 25 Dec 2017 03:03:35 +0530
Subject: [PATCH] ASoC: Intel: SKL: Implement the timer to trigger firmware
crash recovery
This patch implements timer to trigger firmware crash recovery
when there is no period elapsed for the period boundary of a stream.
Change-Id: I500d0307f5367e30bf28b37f356e2f63d648c5ff
Signed-off-by: R, Dharageswari <dharageswari.r@intel.com>
Signed-off-by: Pradeep Tewani <pradeep.d.tewani@intel.com>
Reviewed-on:
Reviewed-by: Kale, Sanyog R <sanyog.r.kale@intel.com>
Reviewed-by: Prakash, Divya1
Reviewed-by: Prusty, Subhransu S <subhransu.s.prusty@intel.com>
Tested-by: Madiwalar, MadiwalappaX <madiwalappax.madiwalar@intel.com>
---
sound/soc/intel/skylake/skl-messages.c | 33 +++++++++++++++++
sound/soc/intel/skylake/skl-pcm.c | 51 +++++++++++++++++++++++++-
sound/soc/intel/skylake/skl.c | 28 +++++++++++++-
sound/soc/intel/skylake/skl.h | 16 ++++++++
4 files changed, 125 insertions(+), 3 deletions(-)
diff --git a/sound/soc/intel/skylake/skl-messages.c b/sound/soc/intel/skylake/skl-messages.c
index c6d9cb3..5fb1093 100644
--- a/sound/soc/intel/skylake/skl-messages.c
+++ b/sound/soc/intel/skylake/skl-messages.c
@@ -22,6 +22,7 @@
#include <sound/core.h>
#include <sound/pcm.h>
#include <uapi/sound/skl-tplg-interface.h>
+#include <linux/timer.h>
#include <linux/delay.h>
#include "skl-sst-dsp.h"
#include "cnl-sst-dsp.h"
@@ -363,6 +364,38 @@ static int cnl_sdw_bra_pipe_trigger(struct skl_sst *ctx, bool enable,
return ret;
}
+void skl_trigger_recovery(struct work_struct *work)
+{
+ struct skl_monitor *monitor_dsp = container_of(work,
+ struct skl_monitor, mwork);
+ struct skl *skl = container_of(monitor_dsp,
+ struct skl, monitor_dsp);
+ const struct skl_dsp_ops *ops;
+
+ ops = skl_get_dsp_ops(skl->pci->device);
+
+ if (ops->do_recovery)
+ ops->do_recovery(skl);
+ return;
+
+}
+
+void skl_timer_cb(struct timer_list *t)
+{
+ struct skl *skl = from_timer(skl, t, monitor_dsp.timer);
+ struct skl_sst *ctx = skl->skl_sst;
+ const struct skl_dsp_ops *ops;
+
+ ops = skl_get_dsp_ops(skl->pci->device);
+ ctx->cores.state[SKL_DSP_CORE0_ID] = SKL_DSP_RESET;
+
+ if (ops->do_recovery) {
+ schedule_work(&skl->monitor_dsp.mwork);
+ del_timer(&skl->monitor_dsp.timer);
+ }
+
+}
+
static int cnl_sdw_bra_pipe_cfg_pb(struct skl_sst *ctx,
unsigned int mstr_num)
{
diff --git a/sound/soc/intel/skylake/skl-pcm.c b/sound/soc/intel/skylake/skl-pcm.c
index 7b83ee7..a231514 100644
--- a/sound/soc/intel/skylake/skl-pcm.c
+++ b/sound/soc/intel/skylake/skl-pcm.c
@@ -22,6 +22,7 @@
#include <linux/pci.h>
#include <linux/pm_runtime.h>
#include <linux/delay.h>
+#include <linux/timer.h>
#include <sound/pcm_params.h>
#include <sound/soc.h>
#include "skl.h"
@@ -523,12 +524,19 @@ static int skl_pcm_trigger(struct snd_pcm_substream *substream, int cmd,
struct snd_soc_dai *dai)
{
struct skl *skl = get_skl_ctx(dai->dev);
+ struct skl_monitor *monitor = &skl->monitor_dsp;
struct skl_sst *ctx = skl->skl_sst;
struct skl_module_cfg *mconfig;
struct hdac_bus *bus = get_bus_ctx(substream);
struct hdac_ext_stream *stream = get_hdac_ext_stream(substream);
- struct snd_pcm_runtime *runtime = substream->runtime;
struct snd_soc_dapm_widget *w;
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct hdac_stream *azx_dev;
+#if !IS_ENABLED(CONFIG_SND_SOC_INTEL_CNL_FPGA)
+ u32 interval;
+ int i;
+#endif
+ bool is_running = false;
int ret;
mconfig = skl_tplg_fe_get_cpr_module(dai, substream->stream);
@@ -558,7 +566,7 @@ static int skl_pcm_trigger(struct snd_pcm_substream *substream, int cmd,
stream->lpib);
snd_hdac_ext_stream_set_lpib(stream, stream->lpib);
if (runtime->no_rewinds)
- snd_hdac_ext_stream_set_spib(ebus,
+ snd_hdac_ext_stream_set_spib(bus,
stream, stream->spib);
}
case SNDRV_PCM_TRIGGER_START:
@@ -572,6 +580,25 @@ static int skl_pcm_trigger(struct snd_pcm_substream *substream, int cmd,
ret = skl_decoupled_trigger(substream, cmd);
if (ret < 0)
return ret;
+ /*
+ * Period elapsed interrupts with multiple streams are not
+ * consistent on FPGA. However, it works without any issues on
+ * RVP. So, using the default max value for FPGA
+ */
+#if !IS_ENABLED(CONFIG_SND_SOC_INTEL_CNL_FPGA)
+ /*
+ * To be on the safer side, restricting the minimal interval to
+ * 10ms
+ */
+ interval = SKL_MIN_TIME_INTERVAL +
+ ((2 * runtime->period_size * 1000) /
+ runtime->rate);
+ monitor->intervals[hdac_stream(stream)->index] = interval;
+ if (interval > monitor->interval)
+ monitor->interval = interval;
+#else
+ monitor->interval = SKL_MAX_TIME_INTERVAL;
+#endif
return skl_run_pipe(ctx, mconfig->pipe);
break;
@@ -599,6 +626,26 @@ static int skl_pcm_trigger(struct snd_pcm_substream *substream, int cmd,
hdac_stream(stream));
snd_hdac_ext_stream_decouple(bus, stream, false);
}
+
+ list_for_each_entry(azx_dev, &bus->stream_list, list) {
+ if (azx_dev->running) {
+ is_running = true;
+ break;
+ }
+ }
+ monitor->intervals[hdac_stream(stream)->index] = 0;
+ if (!is_running)
+ del_timer(&skl->monitor_dsp.timer);
+#if !IS_ENABLED(CONFIG_SND_SOC_INTEL_CNL_FPGA)
+ else {
+ interval = SKL_MIN_TIME_INTERVAL;
+ for (i = 0; i < bus->num_streams; i++) {
+ if (monitor->intervals[i] > interval)
+ interval = monitor->intervals[i];
+ }
+ monitor->interval = interval;
+ }
+#endif
break;
default:
diff --git a/sound/soc/intel/skylake/skl.c b/sound/soc/intel/skylake/skl.c
index dbff6a4..40cd27f 100644
--- a/sound/soc/intel/skylake/skl.c
+++ b/sound/soc/intel/skylake/skl.c
@@ -21,6 +21,7 @@
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
*/
+#include <linux/timer.h>
#include <linux/module.h>
#include <linux/pci.h>
#include <linux/pm_runtime.h>
@@ -35,6 +36,8 @@
#include <sound/hda_i915.h>
#include <sound/compress_driver.h>
#include "skl.h"
+#include "../common/sst-dsp.h"
+#include "../common/sst-dsp-priv.h"
#include "skl-sst-dsp.h"
#include "skl-sst-ipc.h"
#include "skl-topology.h"
@@ -245,6 +248,7 @@ static void skl_stream_update(struct hdac_bus *bus, struct hdac_stream *hstr)
static irqreturn_t skl_interrupt(int irq, void *dev_id)
{
struct hdac_bus *bus = dev_id;
+ struct skl *skl = bus_to_skl(ebus);
u32 status;
u32 mask, int_enable;
int ret = IRQ_NONE;
@@ -268,7 +272,7 @@ static irqreturn_t skl_interrupt(int irq, void *dev_id)
snd_hdac_chip_writeb(bus, RIRBSTS, RIRB_INT_MASK);
}
- mask = (0x1 << ebus->num_streams) - 1;
+ mask = (0x1 << bus->num_streams) - 1;
status = snd_hdac_chip_readl(bus, INTSTS);
status &= mask;
@@ -276,6 +280,8 @@ static irqreturn_t skl_interrupt(int irq, void *dev_id)
/* Disable stream interrupts; Re-enable in bottom half */
int_enable = snd_hdac_chip_readl(bus, INTCTL);
snd_hdac_chip_writel(bus, INTCTL, (int_enable & (~mask)));
+ mod_timer(&skl->monitor_dsp.timer, jiffies +
+ msecs_to_jiffies(skl->monitor_dsp.interval));
ret = IRQ_WAKE_THREAD;
} else
ret = IRQ_HANDLED;
@@ -858,6 +864,22 @@ static void skl_probe_work(struct work_struct *work)
err = snd_hdac_display_power(bus, false);
}
+static int skl_init_recovery(struct skl *skl)
+{
+ struct skl_monitor *monitor = &skl->monitor_dsp;
+
+ INIT_WORK(&monitor->mwork, skl_trigger_recovery);
+ monitor->interval = SKL_MIN_TIME_INTERVAL;
+
+ monitor->intervals = devm_kzalloc(&skl->pci->dev,
+ skl->ebus.num_streams * sizeof(u32),
+ GFP_KERNEL);
+ if (!monitor->intervals)
+ return -ENOMEM;
+ timer_setup(&monitor->timer, skl_timer_cb, 0);
+ return 0;
+}
+
/*
* constructor
*/
@@ -981,6 +1003,10 @@ static int skl_probe(struct pci_dev *pci,
if (err < 0)
goto out_free;
+ err = skl_init_recovery(skl);
+ if (err < 0)
+ return err;
+
skl->pci_id = pci->device;
device_disable_async_suspend(bus->dev);
diff --git a/sound/soc/intel/skylake/skl.h b/sound/soc/intel/skylake/skl.h
index b75dc47..01053c4 100644
--- a/sound/soc/intel/skylake/skl.h
+++ b/sound/soc/intel/skylake/skl.h
@@ -23,6 +23,7 @@
#include <sound/hda_register.h>
#include <sound/hdaudio_ext.h>
+#include <linux/timer.h>
#include <sound/soc.h>
#include "skl-nhlt.h"
#include "skl-ssp-clk.h"
@@ -52,6 +53,9 @@
#define BXT_INSTANCE_ID 0
#define BXT_BASE_FW_MODULE_ID 0
+#define SKL_MAX_TIME_INTERVAL 1000
+#define SKL_MIN_TIME_INTERVAL 10
+
struct skl_dsp_resource {
u32 max_mcps;
u32 max_mem;
@@ -122,6 +126,14 @@ struct ep_group_cnt {
int *vbus_id;
};
+/* For crash recovery */
+struct skl_monitor {
+ struct work_struct mwork;
+ struct timer_list timer;
+ u32 interval;
+ u32 *intervals;
+};
+
struct skl {
struct hdac_bus hbus;
struct pci_dev *pci;
@@ -137,6 +149,7 @@ struct skl {
struct nhlt_acpi_table *nhlt; /* nhlt ptr */
struct skl_sst *skl_sst; /* sst skl ctx */
+ struct skl_monitor monitor_dsp;
struct skl_dsp_resource resource;
struct list_head ppl_list;
struct list_head bind_list;
@@ -185,6 +198,7 @@ struct skl_dsp_ops {
struct skl_sst **skl_sst, void *ptr);
int (*init_fw)(struct device *dev, struct skl_sst *ctx);
void (*cleanup)(struct device *dev, struct skl_sst *ctx);
+ void (*do_recovery)(struct skl *skl);
};
int skl_platform_unregister(struct device *dev);
@@ -218,6 +232,8 @@ struct skl_clk_parent_src *skl_get_parent_clk(u8 clk_id);
int skl_dsp_set_dma_control(struct skl_sst *ctx, u32 *caps,
u32 caps_size, u32 node_id);
+void skl_timer_cb(struct timer_list *t);
+void skl_trigger_recovery(struct work_struct *work);
struct skl_module_cfg;
#ifdef CONFIG_DEBUG_FS
--
https://clearlinux.org