The patch
ASoC: Intel: Add DSP init and boot up functionality for SKL
has been applied to the asoc tree at
git://git.kernel.org/pub/scm/linux/kernel/git/broonie/sound.git
All being well this means that it will be integrated into the linux-next tree (usually sometime in the next 24 hours) and sent to Linus during the next merge window (or sooner if it is a bug fix), however if problems are discovered then the patch may be dropped or reverted.
You may get further e-mails resulting from automated or manual testing and review of the tree, please engage with people reporting problems and send followup patches addressing any issues that are reported if needed.
If any updates are required or you are submitting further changes they should be sent as incremental updates against current git, existing patches will not be replaced.
Please add any relevant lists and maintainers to the CCs when replying to this mail.
Thanks, Mark
From e973e31a02c32fc830986e62f82e69934134f5ce Mon Sep 17 00:00:00 2001
From: "Subhransu S. Prusty" subhransu.s.prusty@intel.com Date: Thu, 9 Jul 2015 21:38:55 +0530 Subject: [PATCH] ASoC: Intel: Add DSP init and boot up functionality for SKL
This patch adds code to enable, disable and boot DSP core. Also provide some helpers to reset and power up/down the core.
Signed-off-by: Subhransu S. Prusty subhransu.s.prusty@intel.com Signed-off-by: Jeeja KP jeeja.kp@intel.com Signed-off-by: Vinod Koul vinod.koul@intel.com Signed-off-by: Mark Brown broonie@kernel.org --- sound/soc/intel/common/sst-dsp-priv.h | 4 + sound/soc/intel/skylake/Makefile | 2 +- sound/soc/intel/skylake/skl-sst-dsp.c | 337 ++++++++++++++++++++++++++++++++++ sound/soc/intel/skylake/skl-sst-dsp.h | 57 ++++++ 4 files changed, 399 insertions(+), 1 deletion(-) create mode 100644 sound/soc/intel/skylake/skl-sst-dsp.c
diff --git a/sound/soc/intel/common/sst-dsp-priv.h b/sound/soc/intel/common/sst-dsp-priv.h index cd6c7ecf6288..dd79648dfb3a 100644 --- a/sound/soc/intel/common/sst-dsp-priv.h +++ b/sound/soc/intel/common/sst-dsp-priv.h @@ -22,6 +22,8 @@ #include <linux/interrupt.h> #include <linux/firmware.h>
+#include "../skylake/skl-sst-dsp.h" + struct sst_mem_block; struct sst_module; struct sst_fw; @@ -306,6 +308,8 @@ struct sst_dsp {
/* SKL data */
+ struct skl_dsp_fw_ops fw_ops; + int sst_state; u32 intr_status; };
diff --git a/sound/soc/intel/skylake/Makefile b/sound/soc/intel/skylake/Makefile index 18fbe647abca..10c1319f7ed5 100644 --- a/sound/soc/intel/skylake/Makefile +++ b/sound/soc/intel/skylake/Makefile @@ -3,6 +3,6 @@ snd-soc-skl-objs := skl.o skl-pcm.o obj-$(CONFIG_SND_SOC_INTEL_SKYLAKE) += snd-soc-skl.o
# Skylake IPC Support -snd-soc-skl-ipc-objs := skl-sst-ipc.o +snd-soc-skl-ipc-objs := skl-sst-ipc.o skl-sst-dsp.o
obj-$(CONFIG_SND_SOC_INTEL_SKYLAKE) += snd-soc-skl-ipc.o diff --git a/sound/soc/intel/skylake/skl-sst-dsp.c b/sound/soc/intel/skylake/skl-sst-dsp.c new file mode 100644 index 000000000000..313ca7c6ca7f --- /dev/null +++ b/sound/soc/intel/skylake/skl-sst-dsp.c @@ -0,0 +1,337 @@ +/* + * skl-sst-dsp.c - SKL SST library generic function + * + * Copyright (C) 2014-15, Intel Corporation. + * Author:Rafal Redzimski rafal.f.redzimski@intel.com + * Jeeja KP jeeja.kp@intel.com + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as 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 <sound/pcm.h> + +#include "../common/sst-dsp.h" +#include "../common/sst-ipc.h" +#include "../common/sst-dsp-priv.h" +#include "skl-sst-ipc.h" + +/* various timeout values */ +#define SKL_DSP_PU_TO 50 +#define SKL_DSP_PD_TO 50 +#define SKL_DSP_RESET_TO 50 + +void skl_dsp_set_state_locked(struct sst_dsp *ctx, int state) +{ + mutex_lock(&ctx->mutex); + ctx->sst_state = state; + mutex_unlock(&ctx->mutex); +} + +static int skl_dsp_core_set_reset_state(struct sst_dsp *ctx) +{ + int ret; + + /* update bits */ + sst_dsp_shim_update_bits_unlocked(ctx, + SKL_ADSP_REG_ADSPCS, SKL_ADSPCS_CRST_MASK, + SKL_ADSPCS_CRST(SKL_DSP_CORES_MASK)); + + /* poll with timeout to check if operation successful */ + ret = sst_dsp_register_poll(ctx, + SKL_ADSP_REG_ADSPCS, + SKL_ADSPCS_CRST_MASK, + SKL_ADSPCS_CRST(SKL_DSP_CORES_MASK), + SKL_DSP_RESET_TO, + "Set reset"); + if ((sst_dsp_shim_read_unlocked(ctx, SKL_ADSP_REG_ADSPCS) & + SKL_ADSPCS_CRST(SKL_DSP_CORES_MASK)) != + SKL_ADSPCS_CRST(SKL_DSP_CORES_MASK)) { + dev_err(ctx->dev, "Set reset state failed\n"); + ret = -EIO; + } + + return ret; +} + +static int skl_dsp_core_unset_reset_state(struct sst_dsp *ctx) +{ + int ret; + + dev_dbg(ctx->dev, "In %s\n", __func__); + + /* update bits */ + sst_dsp_shim_update_bits_unlocked(ctx, SKL_ADSP_REG_ADSPCS, + SKL_ADSPCS_CRST_MASK, 0); + + /* poll with timeout to check if operation successful */ + ret = sst_dsp_register_poll(ctx, + SKL_ADSP_REG_ADSPCS, + SKL_ADSPCS_CRST_MASK, + 0, + SKL_DSP_RESET_TO, + "Unset reset"); + + if ((sst_dsp_shim_read_unlocked(ctx, SKL_ADSP_REG_ADSPCS) & + SKL_ADSPCS_CRST(SKL_DSP_CORES_MASK)) != 0) { + dev_err(ctx->dev, "Unset reset state failed\n"); + ret = -EIO; + } + + return ret; +} + +static bool is_skl_dsp_core_enable(struct sst_dsp *ctx) +{ + int val; + bool is_enable; + + val = sst_dsp_shim_read_unlocked(ctx, SKL_ADSP_REG_ADSPCS); + + is_enable = ((val & SKL_ADSPCS_CPA(SKL_DSP_CORES_MASK)) && + (val & SKL_ADSPCS_SPA(SKL_DSP_CORES_MASK)) && + !(val & SKL_ADSPCS_CRST(SKL_DSP_CORES_MASK)) && + !(val & SKL_ADSPCS_CSTALL(SKL_DSP_CORES_MASK))); + + dev_dbg(ctx->dev, "DSP core is enabled=%d\n", is_enable); + return is_enable; +} + +static int skl_dsp_reset_core(struct sst_dsp *ctx) +{ + /* stall core */ + sst_dsp_shim_write_unlocked(ctx, SKL_ADSP_REG_ADSPCS, + sst_dsp_shim_read_unlocked(ctx, SKL_ADSP_REG_ADSPCS) & + SKL_ADSPCS_CSTALL(SKL_DSP_CORES_MASK)); + + /* set reset state */ + return skl_dsp_core_set_reset_state(ctx); +} + +static int skl_dsp_start_core(struct sst_dsp *ctx) +{ + int ret; + + /* unset reset state */ + ret = skl_dsp_core_unset_reset_state(ctx); + if (ret < 0) { + dev_dbg(ctx->dev, "dsp unset reset fails\n"); + return ret; + } + + /* run core */ + dev_dbg(ctx->dev, "run core...\n"); + sst_dsp_shim_write_unlocked(ctx, SKL_ADSP_REG_ADSPCS, + sst_dsp_shim_read_unlocked(ctx, SKL_ADSP_REG_ADSPCS) & + ~SKL_ADSPCS_CSTALL(SKL_DSP_CORES_MASK)); + + if (!is_skl_dsp_core_enable(ctx)) { + skl_dsp_reset_core(ctx); + dev_err(ctx->dev, "DSP core enable failed\n"); + ret = -EIO; + } + + return ret; +} + +static int skl_dsp_core_power_up(struct sst_dsp *ctx) +{ + int ret; + + /* update bits */ + sst_dsp_shim_update_bits_unlocked(ctx, SKL_ADSP_REG_ADSPCS, + SKL_ADSPCS_SPA_MASK, SKL_ADSPCS_SPA(SKL_DSP_CORES_MASK)); + + /* poll with timeout to check if operation successful */ + ret = sst_dsp_register_poll(ctx, + SKL_ADSP_REG_ADSPCS, + SKL_ADSPCS_CPA_MASK, + SKL_ADSPCS_CPA(SKL_DSP_CORES_MASK), + SKL_DSP_PU_TO, + "Power up"); + + if ((sst_dsp_shim_read_unlocked(ctx, SKL_ADSP_REG_ADSPCS) & + SKL_ADSPCS_CPA(SKL_DSP_CORES_MASK)) != + SKL_ADSPCS_CPA(SKL_DSP_CORES_MASK)) { + dev_err(ctx->dev, "DSP core power up failed\n"); + ret = -EIO; + } + + return ret; +} + +static int skl_dsp_core_power_down(struct sst_dsp *ctx) +{ + /* update bits */ + sst_dsp_shim_update_bits_unlocked(ctx, SKL_ADSP_REG_ADSPCS, + SKL_ADSPCS_SPA_MASK, 0); + + /* poll with timeout to check if operation successful */ + return sst_dsp_register_poll(ctx, + SKL_ADSP_REG_ADSPCS, + SKL_ADSPCS_SPA_MASK, + 0, + SKL_DSP_PD_TO, + "Power down"); +} + +static int skl_dsp_enable_core(struct sst_dsp *ctx) +{ + int ret; + + /* power up */ + ret = skl_dsp_core_power_up(ctx); + if (ret < 0) { + dev_dbg(ctx->dev, "dsp core power up failed\n"); + return ret; + } + + return skl_dsp_start_core(ctx); +} + +int skl_dsp_disable_core(struct sst_dsp *ctx) +{ + int ret; + + ret = skl_dsp_reset_core(ctx); + if (ret < 0) { + dev_err(ctx->dev, "dsp core reset failed\n"); + return ret; + } + + /* power down core*/ + ret = skl_dsp_core_power_down(ctx); + if (ret < 0) { + dev_err(ctx->dev, "dsp core power down failed\n"); + return ret; + } + + if (is_skl_dsp_core_enable(ctx)) { + dev_err(ctx->dev, "DSP core disable failed\n"); + ret = -EIO; + } + + return ret; +} + +int skl_dsp_boot(struct sst_dsp *ctx) +{ + int ret; + + if (is_skl_dsp_core_enable(ctx)) { + dev_dbg(ctx->dev, "dsp core is already enabled, so reset the dap core\n"); + ret = skl_dsp_reset_core(ctx); + if (ret < 0) { + dev_err(ctx->dev, "dsp reset failed\n"); + return ret; + } + + ret = skl_dsp_start_core(ctx); + if (ret < 0) { + dev_err(ctx->dev, "dsp start failed\n"); + return ret; + } + } else { + dev_dbg(ctx->dev, "disable and enable to make sure DSP is invalid state\n"); + ret = skl_dsp_disable_core(ctx); + + if (ret < 0) { + dev_err(ctx->dev, "dsp disable core failes\n"); + return ret; + } + ret = skl_dsp_enable_core(ctx); + } + + return ret; +} + +irqreturn_t skl_dsp_sst_interrupt(int irq, void *dev_id) +{ + struct sst_dsp *ctx = dev_id; + u32 val; + irqreturn_t result = IRQ_NONE; + + spin_lock(&ctx->spinlock); + + val = sst_dsp_shim_read_unlocked(ctx, SKL_ADSP_REG_ADSPIS); + ctx->intr_status = val; + + if (val & SKL_ADSPIS_IPC) { + skl_ipc_int_disable(ctx); + result = IRQ_WAKE_THREAD; + } + + spin_unlock(&ctx->spinlock); + + return result; +} + +int skl_dsp_wake(struct sst_dsp *ctx) +{ + return ctx->fw_ops.set_state_D0(ctx); +} +EXPORT_SYMBOL_GPL(skl_dsp_wake); + +int skl_dsp_sleep(struct sst_dsp *ctx) +{ + return ctx->fw_ops.set_state_D3(ctx); +} +EXPORT_SYMBOL_GPL(skl_dsp_sleep); + +struct sst_dsp *skl_dsp_ctx_init(struct device *dev, + struct sst_dsp_device *sst_dev, int irq) +{ + int ret; + struct sst_dsp *sst; + + sst = devm_kzalloc(dev, sizeof(*sst), GFP_KERNEL); + if (sst == NULL) + return NULL; + + spin_lock_init(&sst->spinlock); + mutex_init(&sst->mutex); + sst->dev = dev; + sst->sst_dev = sst_dev; + sst->irq = irq; + sst->ops = sst_dev->ops; + sst->thread_context = sst_dev->thread_context; + + /* Initialise SST Audio DSP */ + if (sst->ops->init) { + ret = sst->ops->init(sst, NULL); + if (ret < 0) + return NULL; + } + + /* Register the ISR */ + ret = request_threaded_irq(sst->irq, sst->ops->irq_handler, + sst_dev->thread, IRQF_SHARED, "AudioDSP", sst); + if (ret) { + dev_err(sst->dev, "unable to grab threaded IRQ %d, disabling device\n", + sst->irq); + return NULL; + } + + return sst; +} + +void skl_dsp_free(struct sst_dsp *dsp) +{ + skl_ipc_int_disable(dsp); + + free_irq(dsp->irq, dsp); + skl_dsp_disable_core(dsp); +} +EXPORT_SYMBOL_GPL(skl_dsp_free); + +bool is_skl_dsp_running(struct sst_dsp *ctx) +{ + return (ctx->sst_state == SKL_DSP_RUNNING); +} +EXPORT_SYMBOL_GPL(is_skl_dsp_running); diff --git a/sound/soc/intel/skylake/skl-sst-dsp.h b/sound/soc/intel/skylake/skl-sst-dsp.h index 2ac120c18bea..cdfca9be4120 100644 --- a/sound/soc/intel/skylake/skl-sst-dsp.h +++ b/sound/soc/intel/skylake/skl-sst-dsp.h @@ -16,6 +16,8 @@ #ifndef __SKL_SST_DSP_H__ #define __SKL_SST_DSP_H__
+struct sst_dsp_device; + /* Intel HD Audio General DSP Registers */ #define SKL_ADSP_GEN_BASE 0x0 #define SKL_ADSP_REG_ADSPCS (SKL_ADSP_GEN_BASE + 0x04) @@ -59,4 +61,59 @@ #define SKL_ADSPIC_IPC 1 #define SKL_ADSPIS_IPC 1
+/* ADSPCS - Audio DSP Control & Status */ +#define SKL_DSP_CORES 1 +#define SKL_DSP_CORE0_MASK 1 +#define SKL_DSP_CORES_MASK ((1 << SKL_DSP_CORES) - 1) + +/* Core Reset - asserted high */ +#define SKL_ADSPCS_CRST_SHIFT 0 +#define SKL_ADSPCS_CRST_MASK (SKL_DSP_CORES_MASK << SKL_ADSPCS_CRST_SHIFT) +#define SKL_ADSPCS_CRST(x) ((x << SKL_ADSPCS_CRST_SHIFT) & SKL_ADSPCS_CRST_MASK) + +/* Core run/stall - when set to '1' core is stalled */ +#define SKL_ADSPCS_CSTALL_SHIFT 8 +#define SKL_ADSPCS_CSTALL_MASK (SKL_DSP_CORES_MASK << \ + SKL_ADSPCS_CSTALL_SHIFT) +#define SKL_ADSPCS_CSTALL(x) ((x << SKL_ADSPCS_CSTALL_SHIFT) & \ + SKL_ADSPCS_CSTALL_MASK) + +/* Set Power Active - when set to '1' turn cores on */ +#define SKL_ADSPCS_SPA_SHIFT 16 +#define SKL_ADSPCS_SPA_MASK (SKL_DSP_CORES_MASK << SKL_ADSPCS_SPA_SHIFT) +#define SKL_ADSPCS_SPA(x) ((x << SKL_ADSPCS_SPA_SHIFT) & SKL_ADSPCS_SPA_MASK) + +/* Current Power Active - power status of cores, set by hardware */ +#define SKL_ADSPCS_CPA_SHIFT 24 +#define SKL_ADSPCS_CPA_MASK (SKL_DSP_CORES_MASK << SKL_ADSPCS_CPA_SHIFT) +#define SKL_ADSPCS_CPA(x) ((x << SKL_ADSPCS_CPA_SHIFT) & SKL_ADSPCS_CPA_MASK) + +#define SST_DSP_POWER_D0 0x0 /* full On */ +#define SST_DSP_POWER_D3 0x3 /* Off */ + +enum skl_dsp_states { + SKL_DSP_RUNNING = 1, + SKL_DSP_RESET, +}; + +struct skl_dsp_fw_ops { + int (*load_fw)(struct sst_dsp *ctx); + /* FW module parser/loader */ + int (*parse_fw)(struct sst_dsp *ctx); + int (*set_state_D0)(struct sst_dsp *ctx); + int (*set_state_D3)(struct sst_dsp *ctx); +}; + +void skl_dsp_set_state_locked(struct sst_dsp *ctx, int state); +struct sst_dsp *skl_dsp_ctx_init(struct device *dev, + struct sst_dsp_device *sst_dev, int irq); +int skl_dsp_disable_core(struct sst_dsp *ctx); +bool is_skl_dsp_running(struct sst_dsp *ctx); +irqreturn_t skl_dsp_sst_interrupt(int irq, void *dev_id); +int skl_dsp_wake(struct sst_dsp *ctx); +int skl_dsp_sleep(struct sst_dsp *ctx); +void skl_dsp_free(struct sst_dsp *dsp); + +int skl_dsp_boot(struct sst_dsp *ctx); + #endif /*__SKL_SST_DSP_H__*/