[alsa-devel] [RFC 0/9] Add SKL ipc handling code
This series adds support for managing DSP and posting IPC and processing IPC replies.
Subhransu S. Prusty (9): ASoC: hda: Add helper to read/write to dsp mmio space ASoC: hda: Add IPC library for SKL platform ASoC: hda: Add config option for SKL ipc library ASoC: hda: Add DSP init and boot up functionality ASoC: hda: Add dsp loader ops ASoC: hda: Add Code Loader DMA support ASoC: hda: Add DSP library functions for SKL platform ASoC: hda: Add for CL DMA interrupt handling ASoC: hda: Export API to change DSP power state
include/sound/soc-hda-sst-cldma.h | 234 ++++++++ include/sound/soc-hda-sst-dsp.h | 266 +++++++++ include/sound/soc-hda-sst-ipc.h | 134 +++++ sound/soc/hda/Kconfig | 7 + sound/soc/hda/Makefile | 3 + sound/soc/hda/intel/Makefile | 6 + sound/soc/hda/intel/soc-hda-sst-cldma.c | 297 +++++++++ sound/soc/hda/intel/soc-hda-sst-dsp.c | 510 ++++++++++++++++ sound/soc/hda/intel/soc-hda-sst-ipc.c | 963 ++++++++++++++++++++++++++++++ sound/soc/hda/intel/soc-hda-sst-skl-fw.h | 163 +++++ sound/soc/hda/intel/soc-hda-sst-skl.c | 292 +++++++++ 11 files changed, 2875 insertions(+) create mode 100644 include/sound/soc-hda-sst-cldma.h create mode 100644 include/sound/soc-hda-sst-dsp.h create mode 100644 include/sound/soc-hda-sst-ipc.h create mode 100644 sound/soc/hda/intel/Makefile create mode 100644 sound/soc/hda/intel/soc-hda-sst-cldma.c create mode 100644 sound/soc/hda/intel/soc-hda-sst-dsp.c create mode 100644 sound/soc/hda/intel/soc-hda-sst-ipc.c create mode 100644 sound/soc/hda/intel/soc-hda-sst-skl-fw.h create mode 100644 sound/soc/hda/intel/soc-hda-sst-skl.c
From: "Subhransu S. Prusty" subhransu.s.prusty@intel.com
Adding some helper APIs which will be used in the DSP driver and ipc library to read mmio.
Signed-off-by: Subhransu S. Prusty subhransu.s.prusty@intel.com Signed-off-by: Vinod Koul vinod.koul@intel.com --- include/sound/soc-hda-sst-dsp.h | 169 +++++++++++++++++++++++++++++++++ sound/soc/hda/intel/soc-hda-sst-dsp.c | 139 +++++++++++++++++++++++++++ 2 files changed, 308 insertions(+) create mode 100644 include/sound/soc-hda-sst-dsp.h create mode 100644 sound/soc/hda/intel/soc-hda-sst-dsp.c
diff --git a/include/sound/soc-hda-sst-dsp.h b/include/sound/soc-hda-sst-dsp.h new file mode 100644 index 000000000000..daeee1840b94 --- /dev/null +++ b/include/sound/soc-hda-sst-dsp.h @@ -0,0 +1,169 @@ +/* + * Intel SST DSP Support + * + * Copyright (C) 2014-15, 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 __HDA_SST_DSP_H__ +#define __HDA_SST_DSP_H__ + +#include <linux/spinlock.h> +#include <sound/memalloc.h> + +#define ssth_writel_andor(ctx, reg, mask_and, mask_or) \ + ssth_writel_traced( \ + ctx, \ + reg, \ + (ssth_readl_traced(ctx, (reg)) & (mask_and) | (mask_or))) + +#define ssth_readb(ctx, reg) \ + ssth_readb_traced(ctx, HDA_ADSP_REG_##reg) +#define ssth_readw(ctx, reg) \ + ssth_readw_traced(ctx, HDA_ADSP_REG_##reg) +#define ssth_readl(ctx, reg) \ + ssth_readl_traced(ctx, HDA_ADSP_REG_##reg) +#define ssth_readl_alt(ctx, reg) \ + ssth_readl_traced(ctx, reg) + +#define ssth_writeb(ctx, reg, value) \ + ssth_writeb_traced(ctx, HDA_ADSP_REG_##reg, (value)) +#define ssth_writew(ctx, reg, value) \ + ssth_writew_traced(ctx, HDA_ADSP_REG_##reg, (value)) +#define ssth_writel(ctx, reg, value) \ + ssth_writel_traced(ctx, HDA_ADSP_REG_##reg, (value)) + +#define ssth_updatel(ctx, reg, mask, value) \ + ssth_updatel_bits(ctx, HDA_ADSP_REG_##reg, reg##_##mask, (value)) + +#define ssth_updatel_locked(ctx, reg, mask, value) \ + ssth_updatel_bits_locked(ctx, HDA_ADSP_REG_##reg, \ + reg##_##mask, (value)) + +/* Intel HD Audio General DSP Registers */ +#define HDA_ADSP_GEN_BASE 0x0 +#define HDA_ADSP_REG_ADSPCS (HDA_ADSP_GEN_BASE + 0x04) +#define HDA_ADSP_REG_ADSPIC (HDA_ADSP_GEN_BASE + 0x08) +#define HDA_ADSP_REG_ADSPIS (HDA_ADSP_GEN_BASE + 0x0C) +#define HDA_ADSP_REG_ADSPIC2 (HDA_ADSP_GEN_BASE + 0x10) +#define HDA_ADSP_REG_ADSPIS2 (HDA_ADSP_GEN_BASE + 0x14) + +/* Intel HD Audio Inter-Processor Communication Registers */ +#define HDA_ADSP_IPC_BASE 0x40 +#define HDA_ADSP_REG_HIPCT (HDA_ADSP_IPC_BASE + 0x00) +#define HDA_ADSP_REG_HIPCTE (HDA_ADSP_IPC_BASE + 0x04) +#define HDA_ADSP_REG_HIPCI (HDA_ADSP_IPC_BASE + 0x08) +#define HDA_ADSP_REG_HIPCIE (HDA_ADSP_IPC_BASE + 0x0C) +#define HDA_ADSP_REG_HIPCCTL (HDA_ADSP_IPC_BASE + 0x10) + +/* HIPCI */ +#define HDA_ADSP_REG_HIPCI_BUSY BIT(31) + +/* HIPCIE */ +#define HDA_ADSP_REG_HIPCIE_DONE BIT(30) + +/* HIPCCTL */ +#define HDA_ADSP_REG_HIPCCTL_DONE BIT(1) +#define HDA_ADSP_REG_HIPCCTL_BUSY BIT(0) + +/* HIPCT */ +#define HDA_ADSP_REG_HIPCT_BUSY BIT(31) + +/* Intel HD Audio Code Loader DMA Registers */ +#define HDA_ADSP_LOADER_BASE 0x80 + +/* Intel HD Audio SRAM Window 1 */ +#define HDA_ADSP_SRAM1_BASE 0xA000 + +/* Intel HD Audio SRAM Window 2 */ +#define HDA_ADSP_SRAM2_BASE 0xC000 + +/* Intel HD Audio SRAM Window 3 */ +#define HDA_ADSP_SRAM3_BASE 0xE000 + +#define HDA_ADSP_MMIO_LEN 0x10000 + +#define WINDOW0_STAT_SIZE 0x800 + +#define WINDOW0_UP_SIZE 0x800 + +#define WINDOW1_SIZE 0x1000 + +#define ADSPIC_IPC 1 +#define ADSPIS_IPC 1 + +struct ssth_window { + void __iomem *w0stat; + void __iomem *w0up; + void __iomem *w1; + size_t w0stat_size; + size_t w0up_size; + size_t w1_size; +}; + +struct ssth_lib { + struct device *dev; + void __iomem *mmio_base; + struct ssth_window window; + int irq; + struct snd_dma_buffer dsp_fw_buf; + int sst_state; + struct mutex sst_lock; + spinlock_t reg_lock; + const struct firmware *fw; +}; + +enum ssth_states { + SST_DSP_RUNNING = 1, + SST_DSP_RESET +}; + +static inline void +ssth_dsp_set_state_locked(struct ssth_lib *ctx, int state) +{ + mutex_lock(&ctx->sst_lock); + ctx->sst_state = state; + mutex_unlock(&ctx->sst_lock); +} + +void ssth_updatel_bits(struct ssth_lib *ctx, u32 offset, u32 mask, u32 value); + +void ssth_mailbox_write(struct ssth_lib *ctx, void *message, size_t bytes); +void ssth_mailbox_read(struct ssth_lib *ctx, void *message, size_t bytes); + +u8 ssth_readb_traced( + struct ssth_lib *ctx, + u32 offset); + +u16 ssth_readw_traced( + struct ssth_lib *ctx, + u32 offset); + +u32 ssth_readl_traced( + struct ssth_lib *ctx, + u32 offset); + +void ssth_writeb_traced( + struct ssth_lib *ctx, + u32 offset, + u8 val); + +void ssth_writew_traced( + struct ssth_lib *ctx, + u32 offset, u16 val); + +void ssth_writel_traced( + struct ssth_lib *ctx, + u32 offset, + u32 val); + +#endif /*__HDA_SST_DSP_H__*/ diff --git a/sound/soc/hda/intel/soc-hda-sst-dsp.c b/sound/soc/hda/intel/soc-hda-sst-dsp.c new file mode 100644 index 000000000000..6e85645b3a3e --- /dev/null +++ b/sound/soc/hda/intel/soc-hda-sst-dsp.c @@ -0,0 +1,139 @@ +/* + * soc_hda_sst-dsp.c - HDA DSP library generic function + * + * Copyright (C) 2014-15 Intel Corp + * 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 published by + * the Free Software Foundation; version 2 of the License. + * + * 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/io.h> +#include <linux/init.h> +#include <linux/slab.h> +#include <linux/interrupt.h> +#include <linux/delay.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/spinlock_types.h> +#include <linux/mutex.h> +#include <sound/core.h> +#include <sound/pcm.h> +#include <sound/memalloc.h> +#include <sound/soc-hda-sst-dsp.h> +#include <sound/soc-hda-sst-ipc.h> + +u8 ssth_readb_traced(struct ssth_lib *dsp, u32 offset) +{ + u8 val; + + val = readb((dsp)->mmio_base + offset); + dev_dbg(dsp->dev, "readb offset=%#x val=%#x\n", offset, val); + + return val; +} + +u16 ssth_readw_traced(struct ssth_lib *dsp, u32 offset) +{ + u16 val; + + val = readw((dsp)->mmio_base + offset); + dev_dbg(dsp->dev, "readw offset=%#x val=%#x\n", offset, val); + return val; +} + +u32 ssth_readl_traced(struct ssth_lib *dsp, u32 offset) +{ + u32 val; + + val = readl((dsp)->mmio_base + offset); + dev_dbg(dsp->dev, "readl offset=%#x val=%#x\n", offset, val); + return val; +} + +void ssth_writeb_traced(struct ssth_lib *dsp, u32 offset, u8 val) +{ + writeb(val, (dsp)->mmio_base + offset); + dev_dbg(dsp->dev, "writeb offset=%#x val=%#x\n", offset, val); +} + +void ssth_writew_traced(struct ssth_lib *dsp, u32 offset, u16 val) +{ + dev_dbg(dsp->dev, "writew offset=%#x val=%#x\n", offset, val); + writew(val, (dsp)->mmio_base + offset); +} +void ssth_writel_traced(struct ssth_lib *dsp, u32 offset, u32 val) +{ + dev_dbg(dsp->dev, "writel offset=%#x val=%#x\n", offset, val); + writel(val, (dsp)->mmio_base + offset); +} + +void ssth_updatel_bits( + struct ssth_lib *ctx, + u32 offset, u32 mask, u32 value) +{ + u32 val; + + val = (readl((ctx)->mmio_base + offset) & ~mask) | (value & mask); + + writel(val, (ctx)->mmio_base + offset); + + dev_dbg(ctx->dev, "update bits offset=%#x val=%#x\n", offset, val); +} + +static void ssth_updatel_bits_locked( + struct ssth_lib *ctx, + u32 offset, u32 mask, u32 value) +{ + unsigned long flags; + + spin_lock_irqsave(&ctx->reg_lock, flags); + ssth_updatel_bits(ctx, offset, mask, value); + spin_unlock_irqrestore(&ctx->reg_lock, flags); +} + +static inline void _ssth_memcpy_toio_32(u32 __iomem *dest, + u32 *src, size_t bytes) +{ + + /* + * __iowrite32_copy uses 32-bit count values so divide by 4 for + * right count in words + */ + __iowrite32_copy(dest, src, bytes/4); +} + +static inline void _ssth_memcpy_fromio_32(u32 *dest, + __iomem u32 *src, size_t bytes) +{ + + /* + * __iowrite32_copy uses 32-bit count values so divide by 4 for + * right count in words + */ + __iowrite32_copy(dest, src, bytes/4); +} + +void ssth_mailbox_write(struct ssth_lib *ctx, void *msg, size_t bytes) +{ + _ssth_memcpy_toio_32(ctx->window.w1, msg, bytes); +} + +void ssth_mailbox_read(struct ssth_lib *ctx, void *msg, size_t bytes) +{ + _ssth_memcpy_fromio_32(msg, ctx->window.w0up, bytes); +} + +MODULE_DESCRIPTION("HDA SST/IPC Library"); +MODULE_LICENSE("GPL v2");
On Fri, Apr 17, 2015 at 06:45:59PM +0530, Vinod Koul wrote:
+u8 ssth_readb_traced(struct ssth_lib *dsp, u32 offset) +{
- u8 val;
- val = readb((dsp)->mmio_base + offset);
- dev_dbg(dsp->dev, "readb offset=%#x val=%#x\n", offset, val);
- return val;
+}
Much of this looks like a MMIO regmap, there's the memcpy() bits too which aren't but otherwise...
On Mon, Apr 20, 2015 at 10:46:38PM +0100, Mark Brown wrote:
On Fri, Apr 17, 2015 at 06:45:59PM +0530, Vinod Koul wrote:
+u8 ssth_readb_traced(struct ssth_lib *dsp, u32 offset) +{
- u8 val;
- val = readb((dsp)->mmio_base + offset);
- dev_dbg(dsp->dev, "readb offset=%#x val=%#x\n", offset, val);
- return val;
+}
Much of this looks like a MMIO regmap, there's the memcpy() bits too which aren't but otherwise...
Yes they are. memcpy bits are to copy the IPC mailbox payload data to aDSP RAM which can range from few bytes to couple of KBs
Thanks
On Wed, Apr 22, 2015 at 09:20:14AM +0530, Vinod Koul wrote:
On Mon, Apr 20, 2015 at 10:46:38PM +0100, Mark Brown wrote:
Much of this looks like a MMIO regmap, there's the memcpy() bits too which aren't but otherwise...
Yes they are. memcpy bits are to copy the IPC mailbox payload data to aDSP RAM which can range from few bytes to couple of KBs
Seems like we could use the block I/O stuff in regmap for that?
On Fri, Apr 24, 2015 at 06:33:31PM +0100, Mark Brown wrote:
On Wed, Apr 22, 2015 at 09:20:14AM +0530, Vinod Koul wrote:
On Mon, Apr 20, 2015 at 10:46:38PM +0100, Mark Brown wrote:
Much of this looks like a MMIO regmap, there's the memcpy() bits too which aren't but otherwise...
Yes they are. memcpy bits are to copy the IPC mailbox payload data to aDSP RAM which can range from few bytes to couple of KBs
Seems like we could use the block I/O stuff in regmap for that?
Yes we could, but not sure if we really benefit from using regmap here.
On Sun, Apr 26, 2015 at 08:04:52PM +0530, Vinod Koul wrote:
On Fri, Apr 24, 2015 at 06:33:31PM +0100, Mark Brown wrote:
Seems like we could use the block I/O stuff in regmap for that?
Yes we could, but not sure if we really benefit from using regmap here.
I'd look at it the other way around - what would it cost you given that you're implementing regmap like wrappers already? You do get all the debugging features if you use it...
On Mon, Apr 27, 2015 at 10:02:02PM +0100, Mark Brown wrote:
On Sun, Apr 26, 2015 at 08:04:52PM +0530, Vinod Koul wrote:
On Fri, Apr 24, 2015 at 06:33:31PM +0100, Mark Brown wrote:
Seems like we could use the block I/O stuff in regmap for that?
Yes we could, but not sure if we really benefit from using regmap here.
I'd look at it the other way around - what would it cost you given that you're implementing regmap like wrappers already? You do get all the debugging features if you use it...
it won't cost much :) If we had a register map I would not have questions. Btw HDA codec will sue regmap as core does that
Most used here is a single IPC register for messaging and then 2K memory for payload. Each message has different values. We dont have register interface for DSP :(
Anyway discussed this with Liam as well, we might do something with our common amilbox code which the SKL driver will also use in next series
Thanks
From: "Subhransu S. Prusty" subhransu.s.prusty@intel.com
This adds APIs for Tx/Rx of IPC to dsp, manager DSP pipeline.
Signed-off-by: Subhransu S. Prusty subhransu.s.prusty@intel.com Signed-off-by: Vinod Koul vinod.koul@intel.com --- include/sound/soc-hda-sst-dsp.h | 4 + include/sound/soc-hda-sst-ipc.h | 134 +++++ sound/soc/hda/intel/Makefile | 5 + sound/soc/hda/intel/soc-hda-sst-ipc.c | 963 +++++++++++++++++++++++++++++++++ 4 files changed, 1106 insertions(+) create mode 100644 include/sound/soc-hda-sst-ipc.h create mode 100644 sound/soc/hda/intel/Makefile create mode 100644 sound/soc/hda/intel/soc-hda-sst-ipc.c
diff --git a/include/sound/soc-hda-sst-dsp.h b/include/sound/soc-hda-sst-dsp.h index daeee1840b94..44e8d67aab3a 100644 --- a/include/sound/soc-hda-sst-dsp.h +++ b/include/sound/soc-hda-sst-dsp.h @@ -112,6 +112,7 @@ struct ssth_window {
struct ssth_lib { struct device *dev; + struct ssth_ipc *ipc; void __iomem *mmio_base; struct ssth_window window; int irq; @@ -120,6 +121,9 @@ struct ssth_lib { struct mutex sst_lock; spinlock_t reg_lock; const struct firmware *fw; + + struct workqueue_struct *intr_wq; + struct work_struct ipc_process_msg_work; };
enum ssth_states { diff --git a/include/sound/soc-hda-sst-ipc.h b/include/sound/soc-hda-sst-ipc.h new file mode 100644 index 000000000000..2553a027a51a --- /dev/null +++ b/include/sound/soc-hda-sst-ipc.h @@ -0,0 +1,134 @@ +/* + * Intel SST IPC Support + * + * Copyright (C) 2014-15, 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 __HDA_SST_IPC_H +#define __HDA_SST_IPC_H + +#include <linux/kthread.h> +#include <linux/irqreturn.h> + + +struct ssth_lib; + +enum ssth_pipeline_state { + PPL_INVALID_STATE = 0, + PPL_UNINITIALIZED = 1, + PPL_RESET = 2, + PPL_PAUSED = 3, + PPL_RUNNING = 4, + PPL_ERROR_STOP = 5, + PPL_SAVED = 6, + PPL_RESTORED = 7 +}; + +struct ssth_ipc_header { + u32 primary; + u32 extension; +}; + +struct ssth_ipc *ssth_ipc_init( + struct device *dev, + struct ssth_lib *dsp); + +/* IPC data */ +struct ssth_ipc { + struct device *dev; + struct ssth_lib *dsp; + +/* IPC messaging */ + struct list_head tx_list; + struct list_head rx_list; + struct list_head empty_list; + wait_queue_head_t wait_txq; + spinlock_t ipc_lock; + struct task_struct *tx_thread; + struct kthread_worker kworker; + struct kthread_work kwork; + + /* boot */ + wait_queue_head_t boot_wait; + bool boot_complete; + bool shutdown; +}; + +struct ssth_init_instance_msg { + u32 module_id; + u32 instance_id; + u16 param_data_size; + u8 ppl_instance_id; + u8 core_id; +}; + +struct ssth_bind_unbind_msg { + u32 module_id; + u32 instance_id; + u32 dst_module_id; + u32 dst_instance_id; + u8 src_queue; + u8 dst_queue; + bool bind; +}; + +struct ssth_large_config_msg { + u32 module_id; + u32 instance_id; + u32 large_param_id; + u32 param_data_size; +}; + +#define IPC_BOOT_MSECS 3000 + +struct ssth_dxstate_info { + u32 core_mask; + u32 dx_mask; +}; + +#define ADSP_IPC_D3_MASK 0 +#define ADSP_IPC_D0_MASK 3 + +void ssth_ipc_process_msg(struct work_struct *work); + +int ssth_ipc_tx_message_wait(struct ssth_ipc *sst_ipc, + struct ssth_ipc_header header, void *tx_data, size_t tx_bytes, void *rx_data, + size_t rx_bytes); + +int ssth_ipc_create_pipeline(struct ssth_ipc *sst_ipc, u16 ppl_mem_size, + u8 ppl_type, u8 instance_id); + +int ssth_ipc_delete_pipeline(struct ssth_ipc *sst_ipc, u8 instance_id); + +int ssth_ipc_set_pipeline_state(struct ssth_ipc *sst_ipc, u8 instance_id, + enum ssth_pipeline_state state); + +int ssth_ipc_init_instance(struct ssth_ipc *sst_ipc, struct ssth_init_instance_msg *msg, + void *param_data); + +int ssth_ipc_bind_unbind(struct ssth_ipc *sst_ipc, struct ssth_bind_unbind_msg *msg); + +int ssth_ipc_set_dx(struct ssth_ipc *ipc, u8 instance_id, u16 module_id, + struct ssth_dxstate_info *dx); + +int ssth_ipc_set_large_config(struct ssth_ipc *ipc, struct ssth_large_config_msg *msg, + u32 *param); + +void ssth_ipc_int_enable(struct ssth_lib *dsp); +void ssth_ipc_op_int_enable(struct ssth_lib *ctx); +void ssth_ipc_int_disable(struct ssth_lib *dsp); + +bool ssth_ipc_int_status(struct ssth_lib *dsp); +void ssth_ipc_free(struct ssth_ipc *ipc); + +#endif /* __HDA_SST_IPC_H */ diff --git a/sound/soc/hda/intel/Makefile b/sound/soc/hda/intel/Makefile new file mode 100644 index 000000000000..77c44428f40d --- /dev/null +++ b/sound/soc/hda/intel/Makefile @@ -0,0 +1,5 @@ + +snd-soc-hda-sst-dsp-objs := soc-hda-sst-ipc.o soc-hda-sst-dsp.o + +# SST DSP Library +obj-$(CONFIG_SND_SOC_HDA_SST_DSP) += snd-soc-hda-sst-dsp.o diff --git a/sound/soc/hda/intel/soc-hda-sst-ipc.c b/sound/soc/hda/intel/soc-hda-sst-ipc.c new file mode 100644 index 000000000000..b99f90f23244 --- /dev/null +++ b/sound/soc/hda/intel/soc-hda-sst-ipc.c @@ -0,0 +1,963 @@ +/* + * Intel skl IPC Support + * + * Copyright (C) 2014-15, 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/device.h> +#include <linux/wait.h> +#include <linux/spinlock.h> +#include <linux/workqueue.h> +#include <linux/export.h> +#include <linux/slab.h> +#include <linux/delay.h> +#include <linux/sched.h> +#include <linux/list.h> +#include <linux/platform_device.h> +#include <linux/kthread.h> +#include <linux/firmware.h> +#include <linux/dma-mapping.h> +#include <linux/debugfs.h> +#include <linux/irqreturn.h> +#include <sound/soc-hda-sst-dsp.h> +#include <sound/soc-hda-sst-ipc.h> + +#define IXC_STATUS_BITS 24 + +/* Global Message - Generic */ +#define IPC_GLB_TYPE_SHIFT 24 +#define IPC_GLB_TYPE_MASK (0xf << IPC_GLB_TYPE_SHIFT) +#define IPC_GLB_TYPE(x) ((x) << IPC_GLB_TYPE_SHIFT) + +/* Global Message - Reply */ +#define IPC_GLB_REPLY_STATUS_SHIFT 24 +#define IPC_GLB_REPLY_STATUS_MASK ((0x1 << IPC_GLB_REPLY_STATUS_SHIFT) - 1) +#define IPC_GLB_REPLY_STATUS(x) ((x) << IPC_GLB_REPLY_STATUS_SHIFT) + +#define IPC_TIMEOUT_MSECS 3000 + +#define IPC_EMPTY_LIST_SIZE 8 + +#define IPC_MSG_TARGET_SHIFT 30 +#define IPC_MSG_TARGET_MASK 0x1 +#define IPC_MSG_TARGET(x) (((x) & IPC_MSG_TARGET_MASK) \ + << IPC_MSG_TARGET_SHIFT) + +#define IPC_MSG_DIR_SHIFT 29 +#define IPC_MSG_DIR_MASK 0x1 +#define IPC_MSG_DIR(x) (((x) & IPC_MSG_DIR_MASK) \ + << IPC_MSG_DIR_SHIFT) +/*Global Notofication Message*/ +#define IPC_GLB_NOTIFI_TYPE_SHIFT 16 +#define IPC_GLB_NOTIFI_TYPE_MASK 0xFF +#define IPC_GLB_NOTIFI_TYPE(x) (((x) >> IPC_GLB_NOTIFI_TYPE_SHIFT) \ + & IPC_GLB_NOTIFI_TYPE_MASK) + +#define IPC_GLB_NOTIFI_MSG_TYPE_SHIFT 24 +#define IPC_GLB_NOTIFI_MSG_TYPE_MASK 0x1F +#define IPC_GLB_NOTIFI_MSG_TYPE(x) (((x) >> IPC_GLB_NOTIFI_MSG_TYPE_SHIFT) & IPC_GLB_NOTIFI_MSG_TYPE_MASK) + +#define IPC_GLB_NOTIFI_RSP_SHIFT 29 +#define IPC_GLB_NOTIFI_RSP_MASK 0x1 +#define IPC_GLB_NOTIFI_RSP_TYPE(x) (((x) >> IPC_GLB_NOTIFI_RSP_SHIFT) \ + & IPC_GLB_NOTIFI_RSP_MASK) + +/** Pipeline operations **/ +/* Create pipeline message */ +#define IPC_PPL_MEM_SIZE_SHIFT 0 +#define IPC_PPL_MEM_SIZE_MASK 0x7FF +#define IPC_PPL_MEM_SIZE(x) (((x) & IPC_PPL_MEM_SIZE_MASK) \ + << IPC_PPL_MEM_SIZE_SHIFT) + +#define IPC_PPL_TYPE_SHIFT 11 +#define IPC_PPL_TYPE_MASK 0x1F +#define IPC_PPL_TYPE(x) (((x) & IPC_PPL_TYPE_MASK) \ + << IPC_PPL_TYPE_SHIFT) + +#define IPC_INSTANCE_ID_SHIFT 16 +#define IPC_INSTANCE_ID_MASK 0xFF +#define IPC_INSTANCE_ID(x) (((x) & IPC_INSTANCE_ID_MASK) \ + << IPC_INSTANCE_ID_SHIFT) + +/* Set pipeline state message */ +#define IPC_PPL_STATE_SHIFT 0 +#define IPC_PPL_STATE_MASK 0x1F +#define IPC_PPL_STATE(x) (((x) & IPC_PPL_STATE_MASK) \ + << IPC_PPL_STATE_SHIFT) + +/** Module operations **/ +/* primary register */ +#define IPC_MODULE_ID_SHIFT 0 +#define IPC_MODULE_ID_MASK 0xFFFF +#define IPC_MODULE_ID(x) (((x) & IPC_MODULE_ID_MASK) \ + << IPC_MODULE_ID_SHIFT) + +#define IPC_MODULE_INSTANCE_ID_SHIFT 16 +#define IPC_MODULE_INSTANCE_ID_MASK 0xFF +#define IPC_MODULE_INSTANCE_ID(x) (((x) & IPC_MODULE_INSTANCE_ID_MASK) \ + << IPC_MODULE_INSTANCE_ID_SHIFT) + +/* Init instance message */ +/* extension register */ +#define IPC_PARAM_BLOCK_SIZE_SHIFT 0 +#define IPC_PARAM_BLOCK_SIZE_MASK 0xFFFF +#define IPC_PARAM_BLOCK_SIZE(x) (((x) & IPC_PARAM_BLOCK_SIZE_MASK) \ + << IPC_PARAM_BLOCK_SIZE_SHIFT) + +#define IPC_PPL_INSTANCE_ID_SHIFT 16 +#define IPC_PPL_INSTANCE_ID_MASK 0xFF +#define IPC_PPL_INSTANCE_ID(x) (((x) & IPC_PPL_INSTANCE_ID_MASK) \ + << IPC_PPL_INSTANCE_ID_SHIFT) + +#define IPC_CORE_ID_SHIFT 24 +#define IPC_CORE_ID_MASK 0x1F +#define IPC_CORE_ID(x) (((x) & IPC_CORE_ID_MASK) \ + << IPC_CORE_ID_SHIFT) + +/* Bind/Unbind message */ +/* extension register */ +#define IPC_DST_MODULE_ID_SHIFT 0 +#define IPC_DST_MODULE_ID(x) (((x) & IPC_MODULE_ID_MASK) \ + << IPC_DST_MODULE_ID_SHIFT) + +#define IPC_DST_MODULE_INSTANCE_ID_SHIFT 16 +#define IPC_DST_MODULE_INSTANCE_ID(x) (((x) & IPC_MODULE_INSTANCE_ID_MASK) \ + << IPC_DST_MODULE_INSTANCE_ID_SHIFT) + +#define IPC_DST_QUEUE_SHIFT 24 +#define IPC_DST_QUEUE_MASK 0x7 +#define IPC_DST_QUEUE(x) (((x) & IPC_DST_QUEUE_MASK) \ + << IPC_DST_QUEUE_SHIFT) + +#define IPC_SRC_QUEUE_SHIFT 27 +#define IPC_SRC_QUEUE_MASK 0x7 +#define IPC_SRC_QUEUE(x) (((x) & IPC_SRC_QUEUE_MASK) \ + << IPC_SRC_QUEUE_SHIFT) + +/*Save pipeline messgae extension register */ +#define IPC_DMA_ID_SHIFT 0 +#define IPC_DMA_ID_MASK 0x1F +#define IPC_DMA_ID(x) (((x) & IPC_DMA_ID_MASK) \ + << IPC_DMA_ID_SHIFT) +/* Large Config message */ +/* extension register */ +#define IPC_DATA_OFFSET_SZ_SHIFT 0 +#define IPC_DATA_OFFSET_SZ_MASK 0xFFFFF +#define IPC_DATA_OFFSET_SZ(x) (((x) & IPC_DATA_OFFSET_SZ_MASK) \ + << IPC_DATA_OFFSET_SZ_SHIFT) +#define IPC_DATA_OFFSET_SZ_CLEAR ~(IPC_DATA_OFFSET_SZ_MASK \ + << IPC_DATA_OFFSET_SZ_SHIFT) + +#define IPC_LARGE_PARAM_ID_SHIFT 20 +#define IPC_LARGE_PARAM_ID_MASK 0xFF +#define IPC_LARGE_PARAM_ID(x) (((x) & IPC_LARGE_PARAM_ID_MASK) \ + << IPC_LARGE_PARAM_ID_SHIFT) + +#define IPC_FINAL_BLOCK_SHIFT 28 +#define IPC_FINAL_BLOCK_MASK 0x1 +#define IPC_FINAL_BLOCK(x) (((x) & IPC_FINAL_BLOCK_MASK) \ + << IPC_FINAL_BLOCK_SHIFT) + +#define IPC_INITIAL_BLOCK_SHIFT 29 +#define IPC_INITIAL_BLOCK_MASK 0x1 +#define IPC_INITIAL_BLOCK(x) (((x) & IPC_INITIAL_BLOCK_MASK) \ + << IPC_INITIAL_BLOCK_SHIFT) +#define IPC_INITIAL_BLOCK_CLEAR ~(IPC_INITIAL_BLOCK_MASK \ + << IPC_INITIAL_BLOCK_SHIFT) + +#define W0_SIZE 2048 +#define W1_SIZE 4096 + +enum ssth_ipc_msg_target { + IPC_FW_GEN_MSG = 0, + IPC_MODULE_MSG = 1 +}; + +enum ssth_ipc_msg_direction { + IPC_MSG_REQUEST = 0, + IPC_MSG_REPLY = 1 +}; + +/* Global Message Types */ +enum ssth_ipc_glb_type { + IPC_GLB_GET_FW_VERSION = 0, /**< Retrieves firmware version */ + IPC_GLB_LOAD_MULTIPLE_MODULES = 15, + IPC_GLB_UNLOAD_MULTIPLE_MODULES = 16, + IPC_GLB_CREATE_PIPELINE = 17, + IPC_GLB_DELETE_PIPELINE = 18, + IPC_GLB_SET_PIPELINE_STATE = 19, + IPC_GLB_GET_PIPELINE_STATE = 20, + IPC_GLB_GET_PIPELINE_CONTEXT_SIZE = 21, + IPC_GLB_SAVE_PIPELINE = 22, + IPC_GLB_RESTORE_PIPELINE = 23, + IPC_GLB_NOTIFICATION = 26, + IPC_GLB_MAX_IPC_MESSAGE_TYPE = 31 /**< Maximum message number */ +}; + +enum ssth_ipc_glb_reply { + IPC_GLB_REPLY_SUCCESS = 0, + + IPC_GLB_REPLY_UNKNOWN_MESSAGE_TYPE = 1, + IPC_GLB_REPLY_ERROR_INVALID_PARAM = 2, + + IPC_GLB_REPLY_BUSY = 3, + IPC_GLB_REPLY_PENDING = 4, + IPC_GLB_REPLY_FAILURE = 5, + IPC_GLB_REPLY_INVALID_REQUEST = 6, + + IPC_GLB_REPLY_OUT_OF_MEMORY = 7, + IPC_GLB_REPLY_OUT_OF_MIPS = 8, + + IPC_GLB_REPLY_INVALID_RESOURCE_ID = 9, + IPC_GLB_REPLY_INVALID_RESOURCE_STATE = 10, + + IPC_GLB_REPLY_MOD_MGMT_ERROR = 100, + IPC_GLB_REPLY_MOD_LOAD_CL_FAILED = 101, + IPC_GLB_REPLY_MOD_LOAD_INVALID_HASH = 102, + + IPC_GLB_REPLY_MOD_UNLOAD_INST_EXIST = 103, + IPC_GLB_REPLY_MOD_NOT_INITIALIZED = 104, + + IPC_GLB_REPLY_INVALID_CONFIG_PARAM_ID = 120, + IPC_GLB_REPLY_INVALID_CONFIG_DATA_LEN = 121, + IPC_GLB_REPLY_GATEWAY_NOT_INITIALIZED = 140, + IPC_GLB_REPLY_GATEWAY_NOT_EXIST = 141, + + IPC_GLB_REPLY_PIPELINE_NOT_INITIALIZED = 160, + IPC_GLB_REPLY_PIPELINE_NOT_EXIST = 161, + IPC_GLB_REPLY_PIPELINE_SAVE_FAILED = 162, + IPC_GLB_REPLY_PIPELINE_RESTORE_FAILED = 163, + + IPC_MAX_STATUS = ((1<<IXC_STATUS_BITS)-1) +}; + +enum ssth_ipc_notification_type { + IPC_GLB_NOTIFCATION_GLITCH = 0, + IPC_GLB_NOTIFCATION_OVERRUN = 1, + IPC_GLB_NOTIFCATION_UNDERRUN = 2, + IPC_GLB_NOTIFCATION_END_STREAM = 3, + IPC_GLB_NOTIFCATION_PHRASE_DETECTED = 4, + IPC_GLB_NOTIFCATION_RESOURCE_EVENT = 5, + IPC_GLB_NOTIFCATION_LOG_BUFFER_STATUS = 6, + IPC_GLB_NOTIFCATION_TIMESTAMP_CAPTURED = 7, + IPC_GLB_NOTIFCATION_FW_READY = 8 +}; + +/* Module Message Types */ +enum ssth_ipc_module_msg { + IPC_MODULE_INIT_INSTANCE = 0, + IPC_MODULE_CONFIG_GET = 1, + IPC_MODULE_CONFIG_SET = 2, + IPC_MODULE_LARGE_CONFIG_GET = 3, + IPC_MODULE_LARGE_CONFIG_SET = 4, + IPC_MODULE_BIND = 5, + IPC_MODULE_UNBIND = 6, + IPC_MODULE_SET_DX = 7 +}; + +struct ssth_ipc_message { + struct list_head list; + struct ssth_ipc_header header; + char tx_data[WINDOW1_SIZE]; + size_t tx_size; + char rx_data[WINDOW0_UP_SIZE]; + size_t rx_size; + + wait_queue_head_t waitq; + bool complete; + bool wait; + int errno; +}; + +/* locks held by caller */ +static struct ssth_ipc_message *ssth_ipc_msg_get_empty(struct ssth_ipc *ipc) +{ + struct ssth_ipc_message *msg = NULL; + + if (!list_empty(&ipc->empty_list)) { + msg = list_first_entry(&ipc->empty_list, + struct ssth_ipc_message, list); + list_del(&msg->list); + } + + return msg; +} + +/* locks held by caller */ +static void ssth_ipc_msg_put_empty(struct ssth_ipc *ipc, + struct ssth_ipc_message *msg) +{ + list_add_tail(&msg->list, &ipc->empty_list); +} + +static int ssth_ipc_tx_wait_done(struct ssth_ipc *ipc, struct ssth_ipc_message *msg, + void *rx_data) +{ + int ret; + unsigned long irq_flags; + + /* 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->ipc_lock, irq_flags); + + if (ret == 0) + ret = -ETIMEDOUT; + else { + /* copy the data returned from DSP */ + if (msg->rx_size) { + if (rx_data) + memcpy(rx_data, msg->rx_data, msg->rx_size); + else + dev_err(ipc->dev, "error: no output buffer"); + } + ret = msg->errno; + } + + ssth_ipc_msg_put_empty(ipc, msg); + + spin_unlock_irqrestore(&ipc->ipc_lock, irq_flags); + return ret; +} + +static int ssth_ipc_tx_message(struct ssth_ipc *ipc, struct ssth_ipc_header header, + void *tx_data, size_t tx_bytes, void *rx_data, size_t rx_bytes, + int wait) +{ + struct ssth_ipc_message *msg; + unsigned long irq_flags; + + spin_lock_irqsave(&ipc->ipc_lock, irq_flags); + + msg = ssth_ipc_msg_get_empty(ipc); + dev_dbg(ipc->dev, "msg pointer send %p", msg); + if (msg == NULL) { + spin_unlock_irqrestore(&ipc->ipc_lock, irq_flags); + return -EBUSY; + } + + if (tx_bytes) + memcpy(msg->tx_data, tx_data, tx_bytes); + + msg->header.primary = header.primary; + msg->header.extension = header.extension; + msg->tx_size = tx_bytes; + msg->rx_size = rx_bytes; + msg->wait = wait; + msg->errno = 0; + msg->complete = false; + + list_add_tail(&msg->list, &ipc->tx_list); + + spin_unlock_irqrestore(&ipc->ipc_lock, irq_flags); + + queue_kthread_work(&ipc->kworker, &ipc->kwork); + + if (wait) + return ssth_ipc_tx_wait_done(ipc, msg, rx_data); + else + return 0; +} + +int ssth_ipc_tx_message_wait(struct ssth_ipc *ipc, + struct ssth_ipc_header header, void *tx_data, size_t tx_bytes, void *rx_data, + size_t rx_bytes) +{ + return ssth_ipc_tx_message(ipc, header, tx_data, tx_bytes, rx_data, + rx_bytes, 1); +} + +static inline int ssth_ipc_tx_message_nowait(struct ssth_ipc *ipc, + struct ssth_ipc_header header, void *tx_data, size_t tx_bytes) +{ + return ssth_ipc_tx_message(ipc, header, tx_data, tx_bytes, NULL, 0, 0); +} + +static void ssth_ipc_tx_msgs(struct kthread_work *work) +{ + struct ssth_ipc *ipc = + container_of(work, struct ssth_ipc, kwork); + struct ssth_ipc_message *msg; + unsigned long irq_flags; + u32 hipci; + + + spin_lock_irqsave(&ipc->ipc_lock, irq_flags); + if (list_empty(&ipc->tx_list)) { + spin_unlock_irqrestore(&ipc->ipc_lock, irq_flags); + return; + } + + /* if the DSP is busy we will TX messages after IRQ */ + hipci = ssth_readl(ipc->dsp, HIPCI); + if (hipci & HDA_ADSP_REG_HIPCI_BUSY) { + dev_dbg(ipc->dev, "ipc_tx_msgs dsp busy HIPCI(48)=%x\n", hipci); + spin_unlock_irqrestore(&ipc->ipc_lock, irq_flags); + return; + } + + msg = list_first_entry(&ipc->tx_list, struct ssth_ipc_message, list); + + list_move(&msg->list, &ipc->rx_list); + + /* send the message */ + ssth_mailbox_write(ipc->dsp, msg->tx_data, msg->tx_size); + ssth_writel(ipc->dsp, HIPCIE, + msg->header.extension); + ssth_writel(ipc->dsp, HIPCI, + msg->header.primary | HDA_ADSP_REG_HIPCI_BUSY); + + dev_dbg(ipc->dev, "sending msg HIPCI(48)=%x\n", + ssth_readl(ipc->dsp, HIPCI)); + spin_unlock_irqrestore(&ipc->ipc_lock, irq_flags); +} + +static inline void ssth_ipc_tx_msg_reply_complete(struct ssth_ipc *ipc, + struct ssth_ipc_message *msg) +{ + unsigned long irq_flags; + + spin_lock_irqsave(&ipc->ipc_lock, irq_flags); + msg->complete = true; + + if (!msg->wait) { + ssth_ipc_msg_put_empty(ipc, msg); + spin_unlock_irqrestore(&ipc->ipc_lock, irq_flags); + } else { + spin_unlock_irqrestore(&ipc->ipc_lock, irq_flags); + wake_up(&msg->waitq); + } +} + +static struct ssth_ipc_message *ssth_ipc_reply_find_msg(struct ssth_ipc *ipc, u32 header) +{ + struct ssth_ipc_message *msg = NULL; + unsigned long irq_flags; + + spin_lock_irqsave(&ipc->ipc_lock, irq_flags); + + if (list_empty(&ipc->rx_list)) { + dev_err(ipc->dev, "ipc: rx list is empty but received 0x%x\n", + header); + goto out; + } + + msg = list_first_entry(&ipc->rx_list, struct ssth_ipc_message, list); + +out: + spin_unlock_irqrestore(&ipc->ipc_lock, irq_flags); + return msg; +} + +static void ssth_ipc_reply_remove(struct ssth_ipc *ipc, struct ssth_ipc_message *msg) +{ + unsigned long irq_flags; + + spin_lock_irqsave(&ipc->ipc_lock, irq_flags); + if (list_empty(&ipc->rx_list)) { + dev_dbg(ipc->dev, "empty rx list"); + goto out; + } + list_del(&msg->list); + +out: + spin_unlock_irqrestore(&ipc->ipc_lock, irq_flags); +} + +static int ssth_ipc_process_notification(struct ssth_ipc *ipc, struct ssth_ipc_header header) +{ + if (IPC_GLB_NOTIFI_MSG_TYPE(header.primary)) { + switch (IPC_GLB_NOTIFI_TYPE(header.primary)) { + case IPC_GLB_NOTIFCATION_GLITCH: + break; + case IPC_GLB_NOTIFCATION_OVERRUN: + break; + case IPC_GLB_NOTIFCATION_UNDERRUN: + dev_dbg(ipc->dev, "FW UNDERRUN\n"); + break; + case IPC_GLB_NOTIFCATION_END_STREAM: + break; + case IPC_GLB_NOTIFCATION_PHRASE_DETECTED: + break; + case IPC_GLB_NOTIFCATION_RESOURCE_EVENT: + dev_dbg(ipc->dev, "MCPS Budget Violation\n"); + break; + case IPC_GLB_NOTIFCATION_LOG_BUFFER_STATUS: + break; + case IPC_GLB_NOTIFCATION_TIMESTAMP_CAPTURED: + break; + case IPC_GLB_NOTIFCATION_FW_READY: + ipc->boot_complete = true; + wake_up(&ipc->boot_wait); + break; + default: + dev_err(ipc->dev, "ipc: error received unexpected msg=%x", header.primary); + break; + } + } + return 0; +} + +static void ssth_ipc_process_reply(struct ssth_ipc *ipc, struct ssth_ipc_header header) +{ + struct ssth_ipc_message *msg; + u32 reply = header.primary & IPC_GLB_REPLY_STATUS_MASK; + + msg = ssth_ipc_reply_find_msg(ipc, header.primary); + if (msg == NULL) { + dev_dbg(ipc->dev, "ipc: rx list is empty\n"); + return; + } + + /* first process the header */ + switch (reply) { + case IPC_GLB_REPLY_SUCCESS: + dev_dbg(ipc->dev, "ipc FW reply: success\n"); + break; + case IPC_GLB_REPLY_OUT_OF_MEMORY: + msg->errno = -ENOMEM; + break; + case IPC_GLB_REPLY_BUSY: + msg->errno = -EBUSY; + break; + case IPC_GLB_REPLY_FAILURE: + case IPC_GLB_REPLY_INVALID_REQUEST: + case IPC_GLB_REPLY_ERROR_INVALID_PARAM: + case IPC_GLB_REPLY_PIPELINE_NOT_INITIALIZED: + msg->errno = -EINVAL; + break; + default: + dev_err(ipc->dev, "ipc reply: 0x%x", reply); + msg->errno = -EINVAL; + break; + } + + dev_err(ipc->dev, "********ipc FW reply: 0x%x", reply); + if (msg->errno != IPC_GLB_REPLY_SUCCESS) + dev_err(ipc->dev, "ipc FW reply: 0x%x reply=0x%x", msg->errno, reply); + + ssth_ipc_reply_remove(ipc, msg); + ssth_ipc_tx_msg_reply_complete(ipc, msg); +} + +void ssth_ipc_process_msg(struct work_struct *work) +{ + + struct ssth_lib *dsp = container_of(work, + struct ssth_lib, ipc_process_msg_work); + struct ssth_ipc *ipc = dsp->ipc; + struct ssth_ipc_header header = {0}; + u32 hipcie, hipct, hipcte; + int ipc_irq = 0; + + hipcie = ssth_readl(dsp, HIPCIE); + hipct = ssth_readl(dsp, HIPCT); + + /* reply message from DSP */ + if (hipcie & HDA_ADSP_REG_HIPCIE_DONE) { + ssth_updatel_bits(dsp, HDA_ADSP_REG_HIPCCTL, + HDA_ADSP_REG_HIPCCTL_DONE, 0); + /* clear DONE bit - tell DSP we have completed the operation */ + + ssth_updatel_bits(dsp, HDA_ADSP_REG_HIPCIE, + HDA_ADSP_REG_HIPCIE_DONE, HDA_ADSP_REG_HIPCIE_DONE); + + ipc_irq = 1; + + /* unmask Done interrupt */ + ssth_updatel_bits(dsp, HDA_ADSP_REG_HIPCCTL, + HDA_ADSP_REG_HIPCCTL_DONE, HDA_ADSP_REG_HIPCCTL_DONE); + } + + /* to do: new message from DSP */ + if (hipct & HDA_ADSP_REG_HIPCT_BUSY) { + dev_dbg(dsp->dev, "IPC irq: Firmware respond"); + hipcte = ssth_readl(dsp, HIPCTE); + header.primary = hipct; + header.extension = hipcte; + + if (header.primary & 0x20000000) { + /* Handle Immediate reply from DSP Core */ + ssth_ipc_process_reply(ipc, header); + } else { + trace_printk("IPC irq: Notification from firmware\n"); + ssth_ipc_process_notification(ipc, header); + } + /* clear busy interrupt */ + ssth_updatel_bits(dsp, HDA_ADSP_REG_HIPCT, + HDA_ADSP_REG_HIPCT_BUSY, HDA_ADSP_REG_HIPCT_BUSY); + ipc_irq = 1; + } + + if (ipc_irq == 0) + return; + + ssth_ipc_int_enable(dsp); + /* continue to send any remaining messages... */ + queue_kthread_work(&ipc->kworker, &ipc->kwork); +} + +static int ssth_ipc_msg_empty_list_init(struct ssth_ipc *ipc) +{ + struct ssth_ipc_message *msg; + int i; + + msg = kzalloc(sizeof(*msg) * IPC_EMPTY_LIST_SIZE, GFP_KERNEL); + if (msg == NULL) + return -ENOMEM; + + for (i = 0; i < IPC_EMPTY_LIST_SIZE; i++) { + init_waitqueue_head(&msg[i].waitq); + list_add(&msg[i].list, &ipc->empty_list); + } + + return 0; +} + +void ssth_ipc_int_enable(struct ssth_lib *ctx) +{ + ssth_updatel_bits(ctx, HDA_ADSP_REG_ADSPIC, + ADSPIC_IPC, ADSPIC_IPC); +} + +void ssth_ipc_int_disable(struct ssth_lib *ctx) +{ + ssth_updatel_bits(ctx, HDA_ADSP_REG_ADSPIC, + ADSPIC_IPC, 0); +} + +void ssth_ipc_op_int_enable(struct ssth_lib *ctx) +{ + /* enable IPC DONE interrupt */ + ssth_updatel_bits(ctx, HDA_ADSP_REG_HIPCCTL, + HDA_ADSP_REG_HIPCCTL_DONE, HDA_ADSP_REG_HIPCCTL_DONE); + /* Enable IPC BUSY interrupt */ + ssth_updatel_bits(ctx, HDA_ADSP_REG_HIPCCTL, + HDA_ADSP_REG_HIPCCTL_BUSY, HDA_ADSP_REG_HIPCCTL_BUSY); +} + +bool ssth_ipc_int_status(struct ssth_lib *ctx) +{ + return ssth_readl(ctx, ADSPIS) & ADSPIS_IPC; +} + + +struct ssth_ipc *ssth_ipc_init(struct device *dev, + struct ssth_lib *dsp) +{ + struct ssth_ipc *ipc; + int err; + + dev_dbg(dev, "In %s\n", __func__); + + ipc = kzalloc(sizeof(*ipc), GFP_KERNEL); + if (ipc == NULL) + return NULL; + + ipc->dev = dev; + ipc->dsp = dsp; + + INIT_LIST_HEAD(&ipc->tx_list); + INIT_LIST_HEAD(&ipc->rx_list); + INIT_LIST_HEAD(&ipc->empty_list); + spin_lock_init(&ipc->ipc_lock); + init_waitqueue_head(&ipc->wait_txq); + init_waitqueue_head(&ipc->boot_wait); + + err = ssth_ipc_msg_empty_list_init(ipc); + if (err < 0) + goto list_err; + + /* start the IPC message thread */ + init_kthread_worker(&ipc->kworker); + ipc->tx_thread = kthread_run(kthread_worker_fn, + &ipc->kworker, + dev_name(ipc->dev)); + if (IS_ERR(ipc->tx_thread)) { + dev_err(ipc->dev, "error failed to create message TX task\n"); + goto list_err; + } + init_kthread_work(&ipc->kwork, ssth_ipc_tx_msgs); + + return ipc; + +list_err: + kfree(ipc); + return NULL; +} + +void ssth_ipc_free(struct ssth_ipc *ipc) +{ + /* Disable IPC DONE interrupt */ + ssth_updatel_bits(ipc->dsp, HDA_ADSP_REG_HIPCCTL, + HDA_ADSP_REG_HIPCCTL_DONE, 0); + /* Disable IPC BUSY interrupt */ + ssth_updatel_bits(ipc->dsp, HDA_ADSP_REG_HIPCCTL, + HDA_ADSP_REG_HIPCCTL_BUSY, 0); + + kfree(ipc); +} + +int ssth_ipc_create_pipeline(struct ssth_ipc *ipc, u16 ppl_mem_size, + u8 ppl_type, u8 instance_id) +{ + struct ssth_ipc_header header = {0}; + int ret = 0; + + header.primary = IPC_MSG_TARGET(IPC_FW_GEN_MSG); + header.primary |= IPC_MSG_DIR(IPC_MSG_REQUEST); + header.primary |= IPC_GLB_TYPE(IPC_GLB_CREATE_PIPELINE); + header.primary |= IPC_INSTANCE_ID(instance_id); + header.primary |= IPC_PPL_TYPE(ppl_type); + header.primary |= IPC_PPL_MEM_SIZE(ppl_mem_size); + + dev_dbg(ipc->dev, "In %s header=%d\n", __func__, header.primary); + ret = ssth_ipc_tx_message_wait(ipc, header, NULL, 0, NULL, 0); + if (ret < 0) { + dev_err(ipc->dev, "ipc: create pipeline failed, err: 0x%x\n", ret); + return ret; + } + + return ret; +} +EXPORT_SYMBOL_GPL(ssth_ipc_create_pipeline); + +int ssth_ipc_delete_pipeline(struct ssth_ipc *ipc, u8 instance_id) +{ + struct ssth_ipc_header header = {0}; + int ret = 0; + + header.primary = IPC_MSG_TARGET(IPC_FW_GEN_MSG); + header.primary |= IPC_MSG_DIR(IPC_MSG_REQUEST); + header.primary |= IPC_GLB_TYPE(IPC_GLB_DELETE_PIPELINE); + header.primary |= IPC_INSTANCE_ID(instance_id); + + dev_dbg(ipc->dev, "In %s header=%d\n", __func__, header.primary); + ret = ssth_ipc_tx_message_wait(ipc, header, NULL, 0, NULL, 0); + if (ret < 0) { + dev_err(ipc->dev, "ipc: delete pipeline failed\n"); + return ret; + } + + return 0; +} +EXPORT_SYMBOL_GPL(ssth_ipc_delete_pipeline); + +int ssth_ipc_set_pipeline_state(struct ssth_ipc *ipc, u8 instance_id, + enum ssth_pipeline_state state) +{ + struct ssth_ipc_header header = {0}; + int ret = 0; + + header.primary = IPC_MSG_TARGET(IPC_FW_GEN_MSG); + header.primary |= IPC_MSG_DIR(IPC_MSG_REQUEST); + header.primary |= IPC_GLB_TYPE(IPC_GLB_SET_PIPELINE_STATE); + header.primary |= IPC_INSTANCE_ID(instance_id); + header.primary |= IPC_PPL_STATE(state); + + dev_dbg(ipc->dev, "In %s header=%d\n", __func__, header.primary); + ret = ssth_ipc_tx_message_wait(ipc, header, NULL, 0, NULL, 0); + if (ret < 0) { + dev_err(ipc->dev, "ipc: set pipeline state failed\n"); + return ret; + } + return ret; +} +EXPORT_SYMBOL_GPL(ssth_ipc_set_pipeline_state); + +int ssth_ipc_save_pipeline(struct ssth_ipc *ipc, u8 instance_id, int dma_id) +{ + struct ssth_ipc_header header = {0}; + int ret = 0; + + header.primary = IPC_MSG_TARGET(IPC_FW_GEN_MSG); + header.primary |= IPC_MSG_DIR(IPC_MSG_REQUEST); + header.primary |= IPC_GLB_TYPE(IPC_GLB_SAVE_PIPELINE); + header.primary |= IPC_INSTANCE_ID(instance_id); + + header.extension = IPC_DMA_ID(dma_id); + dev_dbg(ipc->dev, "In %s header=%d\n", __func__, header.primary); + ret = ssth_ipc_tx_message_wait(ipc, header, NULL, 0, NULL, 0); + if (ret < 0) { + dev_err(ipc->dev, "ipc: save pipeline failed, err: 0x%x\n", ret); + return ret; + } + + return ret; +} +EXPORT_SYMBOL_GPL(ssth_ipc_save_pipeline); + +int ssth_ipc_restore_pipeline(struct ssth_ipc *ipc, u8 instance_id) +{ + struct ssth_ipc_header header = {0}; + int ret = 0; + + header.primary = IPC_MSG_TARGET(IPC_FW_GEN_MSG); + header.primary |= IPC_MSG_DIR(IPC_MSG_REQUEST); + header.primary |= IPC_GLB_TYPE(IPC_GLB_RESTORE_PIPELINE); + header.primary |= IPC_INSTANCE_ID(instance_id); + + dev_dbg(ipc->dev, "In %s header=%d\n", __func__, header.primary); + ret = ssth_ipc_tx_message_wait(ipc, header, NULL, 0, NULL, 0); + if (ret < 0) { + dev_err(ipc->dev, "ipc: restore pipeline failed, err: 0x%x\n", ret); + return ret; + } + + return ret; +} +EXPORT_SYMBOL_GPL(ssth_ipc_restore_pipeline); + +int ssth_ipc_set_dx(struct ssth_ipc *ipc, u8 instance_id, u16 module_id, + struct ssth_dxstate_info *dx) +{ + struct ssth_ipc_header header = {0}; + int ret = 0; + + header.primary = IPC_MSG_TARGET(IPC_MODULE_MSG); + header.primary |= IPC_MSG_DIR(IPC_MSG_REQUEST); + header.primary |= IPC_GLB_TYPE(IPC_MODULE_SET_DX); + header.primary |= IPC_MODULE_INSTANCE_ID(instance_id); + header.primary |= IPC_MODULE_ID(module_id); + + dev_dbg(ipc->dev, "In %s primary =%x ext=%x\n", __func__, + header.primary, header.extension); + ret = ssth_ipc_tx_message_wait(ipc, header, (void *)dx, sizeof(dx), + NULL, 0); + if (ret < 0) { + dev_err(ipc->dev, "ipc: set dx failed\n"); + return ret; + } + + return ret; +} +EXPORT_SYMBOL_GPL(ssth_ipc_set_dx); + +int ssth_ipc_init_instance(struct ssth_ipc *ipc, struct ssth_init_instance_msg *msg, + void *param_data) +{ + struct ssth_ipc_header header = {0}; + /* param_block_size must be in dwords */ + u16 param_block_size = msg->param_data_size / sizeof(u32); + int ret = 0; + u32 *buffer = (u32 *)param_data; + int i = 0; + + dev_dbg(ipc->dev, "param size: %d bytes\n", msg->param_data_size); + for (i = 0; i < param_block_size; ++i) + dev_dbg(ipc->dev, "%x\n ", buffer[i]); + + header.primary = IPC_MSG_TARGET(IPC_MODULE_MSG); + header.primary |= IPC_MSG_DIR(IPC_MSG_REQUEST); + header.primary |= IPC_GLB_TYPE(IPC_MODULE_INIT_INSTANCE); + header.primary |= IPC_MODULE_INSTANCE_ID(msg->instance_id); + header.primary |= IPC_MODULE_ID(msg->module_id); + + header.extension = IPC_CORE_ID(msg->core_id); + header.extension |= IPC_PPL_INSTANCE_ID(msg->ppl_instance_id); + header.extension |= IPC_PARAM_BLOCK_SIZE(param_block_size); + + dev_dbg(ipc->dev, "In %s primary =%x ext=%x\n", __func__, + header.primary, header.extension); + ret = ssth_ipc_tx_message_wait(ipc, header, param_data, msg->param_data_size, + NULL, 0); + + if (ret < 0) { + dev_err(ipc->dev, "ipc: init instance failed\n"); + return ret; + } + return ret; +} +EXPORT_SYMBOL_GPL(ssth_ipc_init_instance); + +int ssth_ipc_bind_unbind(struct ssth_ipc *ipc, struct ssth_bind_unbind_msg *msg) +{ + struct ssth_ipc_header header = {0}; + u8 bind_unbind = msg->bind ? IPC_MODULE_BIND : IPC_MODULE_UNBIND; + int ret = 0; + + header.primary = IPC_MSG_TARGET(IPC_MODULE_MSG); + header.primary |= IPC_MSG_DIR(IPC_MSG_REQUEST); + header.primary |= IPC_GLB_TYPE(bind_unbind); + header.primary |= IPC_MODULE_INSTANCE_ID(msg->instance_id); + header.primary |= IPC_MODULE_ID(msg->module_id); + + header.extension = IPC_DST_MODULE_ID(msg->dst_module_id); + header.extension |= IPC_DST_MODULE_INSTANCE_ID(msg->dst_instance_id); + header.extension |= IPC_DST_QUEUE(msg->dst_queue); + header.extension |= IPC_SRC_QUEUE(msg->src_queue); + + dev_dbg(ipc->dev, "In %s hdr=%x ext=%x\n", __func__, header.primary, + header.extension); + ret = ssth_ipc_tx_message_wait(ipc, header, NULL, 0, NULL, 0); + if (ret < 0) { + dev_err(ipc->dev, "ipc: bind/unbind failed\n"); + return ret; + } + return ret; +} +EXPORT_SYMBOL_GPL(ssth_ipc_bind_unbind); + +int ssth_ipc_set_large_config(struct ssth_ipc *ipc, struct ssth_large_config_msg *msg, + u32 *param) +{ + struct ssth_ipc_header header = {0}; + int ret = 0; + size_t sz_remaining, tx_size, data_offset; + + header.primary = IPC_MSG_TARGET(IPC_MODULE_MSG); + header.primary |= IPC_MSG_DIR(IPC_MSG_REQUEST); + header.primary |= IPC_GLB_TYPE(IPC_MODULE_LARGE_CONFIG_SET); + header.primary |= IPC_MODULE_INSTANCE_ID(msg->instance_id); + header.primary |= IPC_MODULE_ID(msg->module_id); + + header.extension = IPC_DATA_OFFSET_SZ(msg->param_data_size); + header.extension |= IPC_LARGE_PARAM_ID(msg->large_param_id); + header.extension |= IPC_FINAL_BLOCK(0); + header.extension |= IPC_INITIAL_BLOCK(1); + + sz_remaining = msg->param_data_size; + data_offset = 0; + while (sz_remaining != 0) { + tx_size = sz_remaining > WINDOW1_SIZE + ? WINDOW1_SIZE : sz_remaining; + if (tx_size == sz_remaining) + header.extension |= IPC_FINAL_BLOCK(1); + + dev_dbg(ipc->dev, "In %s primary=%#x ext=%#x\n", __func__, + header.primary, header.extension); + dev_dbg(ipc->dev, "transmitting offset: %#x, size: %#x\n", + (unsigned)data_offset, (unsigned)tx_size); + ret = ssth_ipc_tx_message_wait(ipc, header, + ((char *)param) + data_offset, + tx_size, NULL, 0); + if (ret < 0) { + dev_err(ipc->dev, "ipc: set large config failed\n"); + return ret; + } + sz_remaining -= tx_size; + data_offset = msg->param_data_size - sz_remaining; + + /* clear the fields */ + header.extension &= IPC_INITIAL_BLOCK_CLEAR; + header.extension &= IPC_DATA_OFFSET_SZ_CLEAR; + /* fill the fields */ + header.extension |= IPC_INITIAL_BLOCK(0); + header.extension |= IPC_DATA_OFFSET_SZ(data_offset); + } + return ret; +} +EXPORT_SYMBOL_GPL(ssth_ipc_set_large_config);
On Fri, Apr 17, 2015 at 06:46:00PM +0530, Vinod Koul wrote:
+/* IPC data */ +struct ssth_ipc {
- struct device *dev;
- struct ssth_lib *dsp;
+/* IPC messaging */
- struct list_head tx_list;
Lots of odd indentation of comments in this code (well, several examples I noticed so far anyway).
- if (ret == 0)
ret = -ETIMEDOUT;
- else {
- /* copy the data returned from DSP */
Coding style - { } on both sides of the if (and the comments again).
if (msg->rx_size) {
if (rx_data)
memcpy(rx_data, msg->rx_data, msg->rx_size);
else
dev_err(ipc->dev, "error: no output buffer");
}
ret = msg->errno;
- }
- ssth_ipc_msg_put_empty(ipc, msg);
- spin_unlock_irqrestore(&ipc->ipc_lock, irq_flags);
Can we pop the message off the list, release the lock and then copy? That way we can avoid having interrupts disabled while we do the memcpy(). In general there seems to be a lot of interrupts disabled copying going on (which is there for some of the other drivers too) which might be avoidable.
+static void ssth_ipc_reply_remove(struct ssth_ipc *ipc, struct ssth_ipc_message *msg) +{
- unsigned long irq_flags;
- spin_lock_irqsave(&ipc->ipc_lock, irq_flags);
- if (list_empty(&ipc->rx_list)) {
dev_dbg(ipc->dev, "empty rx list");
goto out;
- }
- list_del(&msg->list);
+out:
- spin_unlock_irqrestore(&ipc->ipc_lock, irq_flags);
+}
Are we expecting to not find the message/
- if (IPC_GLB_NOTIFI_MSG_TYPE(header.primary)) {
switch (IPC_GLB_NOTIFI_TYPE(header.primary)) {
case IPC_GLB_NOTIFCATION_GLITCH:
break;
case IPC_GLB_NOTIFCATION_OVERRUN:
break;
case IPC_GLB_NOTIFCATION_UNDERRUN:
dev_dbg(ipc->dev, "FW UNDERRUN\n");
break;
case IPC_GLB_NOTIFCATION_END_STREAM:
break;
case IPC_GLB_NOTIFCATION_PHRASE_DETECTED:
break;
case IPC_GLB_NOTIFCATION_RESOURCE_EVENT:
dev_dbg(ipc->dev, "MCPS Budget Violation\n");
break;
case IPC_GLB_NOTIFCATION_LOG_BUFFER_STATUS:
break;
case IPC_GLB_NOTIFCATION_TIMESTAMP_CAPTURED:
break;
case IPC_GLB_NOTIFCATION_FW_READY:
ipc->boot_complete = true;
wake_up(&ipc->boot_wait);
break;
More prints perhaps? _PHRASE_DETECTED looks interesting, as does _OVERRUN.
On Mon, Apr 20, 2015 at 10:56:39PM +0100, Mark Brown wrote:
On Fri, Apr 17, 2015 at 06:46:00PM +0530, Vinod Koul wrote:
+/* IPC data */ +struct ssth_ipc {
- struct device *dev;
- struct ssth_lib *dsp;
+/* IPC messaging */
- struct list_head tx_list;
Lots of odd indentation of comments in this code (well, several examples I noticed so far anyway).
- if (ret == 0)
ret = -ETIMEDOUT;
- else {
- /* copy the data returned from DSP */
Coding style - { } on both sides of the if (and the comments again).
Ah sorry about that, I did try to fix style issues in code but looks like few were missed. I will fix them in the patch series.
if (msg->rx_size) {
if (rx_data)
memcpy(rx_data, msg->rx_data, msg->rx_size);
else
dev_err(ipc->dev, "error: no output buffer");
}
ret = msg->errno;
- }
- ssth_ipc_msg_put_empty(ipc, msg);
- spin_unlock_irqrestore(&ipc->ipc_lock, irq_flags);
Can we pop the message off the list, release the lock and then copy? That way we can avoid having interrupts disabled while we do the memcpy(). In general there seems to be a lot of interrupts disabled copying going on (which is there for some of the other drivers too) which might be avoidable.
Thanks for pointing, yes this should be done.
+static void ssth_ipc_reply_remove(struct ssth_ipc *ipc, struct ssth_ipc_message *msg) +{
- unsigned long irq_flags;
- spin_lock_irqsave(&ipc->ipc_lock, irq_flags);
- if (list_empty(&ipc->rx_list)) {
dev_dbg(ipc->dev, "empty rx list");
goto out;
- }
- list_del(&msg->list);
+out:
- spin_unlock_irqrestore(&ipc->ipc_lock, irq_flags);
+}
Are we expecting to not find the message/
For reply case, I am not sure. I think if thats true would make sense to complain loudly..
- if (IPC_GLB_NOTIFI_MSG_TYPE(header.primary)) {
switch (IPC_GLB_NOTIFI_TYPE(header.primary)) {
case IPC_GLB_NOTIFCATION_GLITCH:
break;
case IPC_GLB_NOTIFCATION_OVERRUN:
break;
case IPC_GLB_NOTIFCATION_UNDERRUN:
dev_dbg(ipc->dev, "FW UNDERRUN\n");
break;
case IPC_GLB_NOTIFCATION_END_STREAM:
break;
case IPC_GLB_NOTIFCATION_PHRASE_DETECTED:
break;
case IPC_GLB_NOTIFCATION_RESOURCE_EVENT:
dev_dbg(ipc->dev, "MCPS Budget Violation\n");
break;
case IPC_GLB_NOTIFCATION_LOG_BUFFER_STATUS:
break;
case IPC_GLB_NOTIFCATION_TIMESTAMP_CAPTURED:
break;
case IPC_GLB_NOTIFCATION_FW_READY:
ipc->boot_complete = true;
wake_up(&ipc->boot_wait);
break;
More prints perhaps? _PHRASE_DETECTED looks interesting, as does _OVERRUN.
Overrun is FW detectiong its pipelines are overunning the buffer, no the ALSA ring buffer though. _PHRASE_DETECTED is for keyword detection which can run on dsp
Thanks
On Wed, Apr 22, 2015 at 09:24:53AM +0530, Vinod Koul wrote:
On Mon, Apr 20, 2015 at 10:56:39PM +0100, Mark Brown wrote:
More prints perhaps? _PHRASE_DETECTED looks interesting, as does _OVERRUN.
Overrun is FW detectiong its pipelines are overunning the buffer, no the ALSA ring buffer though. _PHRASE_DETECTED is for keyword detection which can run on dsp
Sure, it was fairly clear what the messages were for - it was more that they seemed like things we'd want to do more with than just ignore.
On Fri, Apr 24, 2015 at 06:34:27PM +0100, Mark Brown wrote:
On Wed, Apr 22, 2015 at 09:24:53AM +0530, Vinod Koul wrote:
On Mon, Apr 20, 2015 at 10:56:39PM +0100, Mark Brown wrote:
More prints perhaps? _PHRASE_DETECTED looks interesting, as does _OVERRUN.
Overrun is FW detectiong its pipelines are overunning the buffer, no the ALSA ring buffer though. _PHRASE_DETECTED is for keyword detection which can run on dsp
Sure, it was fairly clear what the messages were for - it was more that they seemed like things we'd want to do more with than just ignore.
Oh yes, that will be added after base driver is upstreamed. So I will remove as these are unused atm, and adding them when used makes more sense :)
From: "Subhransu S. Prusty" subhransu.s.prusty@intel.com
Signed-off-by: Subhransu S. Prusty subhransu.s.prusty@intel.com Signed-off-by: Vinod Koul vinod.koul@intel.com --- sound/soc/hda/Kconfig | 7 +++++++ sound/soc/hda/Makefile | 3 +++ 2 files changed, 10 insertions(+)
diff --git a/sound/soc/hda/Kconfig b/sound/soc/hda/Kconfig index 8781f63abcf6..841ea5276fe3 100644 --- a/sound/soc/hda/Kconfig +++ b/sound/soc/hda/Kconfig @@ -40,4 +40,11 @@ config SND_SOC_I2S_SKL_MACH Say Y if you have such a device If unsure select "N".
+ +config SND_SOC_HDA_SST_DSP + tristate "Enable SST DSP Library" + depends on (X86 || COMPILE_TEST) + help + Say Y here to enable the HD-audio SST dsp library. + endmenu diff --git a/sound/soc/hda/Makefile b/sound/soc/hda/Makefile index d9bd33259233..d318d4acb8a7 100644 --- a/sound/soc/hda/Makefile +++ b/sound/soc/hda/Makefile @@ -4,3 +4,6 @@ obj-$(CONFIG_SND_SOC_HDA_SKL) += snd-soc-hda-skl.o
# Machine support obj-$(CONFIG_SND_SOC_HDA_SKL) += boards/ + +# DSP library +obj-$(CONFIG_SND_SOC_HDA_SST_DSP) += intel/
From: "Subhransu S. Prusty" subhransu.s.prusty@intel.com
Signed-off-by: Subhransu S. Prusty subhransu.s.prusty@intel.com Signed-off-by: Vinod Koul vinod.koul@intel.com --- include/sound/soc-hda-sst-dsp.h | 28 +++ sound/soc/hda/intel/soc-hda-sst-dsp.c | 350 +++++++++++++++++++++++++++++++++ 2 files changed, 378 insertions(+)
diff --git a/include/sound/soc-hda-sst-dsp.h b/include/sound/soc-hda-sst-dsp.h index 44e8d67aab3a..4a89c3dda5ab 100644 --- a/include/sound/soc-hda-sst-dsp.h +++ b/include/sound/soc-hda-sst-dsp.h @@ -101,6 +101,34 @@ #define ADSPIC_IPC 1 #define ADSPIS_IPC 1
+/* ADSPCS - Audio DSP Control & Status */ +#define DSP_CORES 1 +#define DSP_CORE0_MASK 1 +#define DSP_CORES_MASK ((1 << DSP_CORES) - 1) + +/* Core Reset - asserted high */ +#define ADSPCS_CRST_SHIFT 0 +#define ADSPCS_CRST_MASK (DSP_CORES_MASK << ADSPCS_CRST_SHIFT) +#define ADSPCS_CRST(x) ((x << ADSPCS_CRST_SHIFT) & ADSPCS_CRST_MASK) + +/* Core run/stall - when set to '1' core is stalled */ +#define ADSPCS_CSTALL_SHIFT 8 +#define ADSPCS_CSTALL_MASK (DSP_CORES_MASK << ADSPCS_CSTALL_SHIFT) +#define ADSPCS_CSTALL(x) ((x << ADSPCS_CSTALL_SHIFT) & ADSPCS_CSTALL_MASK) + +/* Set Power Active - when set to '1' turn cores on */ +#define ADSPCS_SPA_SHIFT 16 +#define ADSPCS_SPA_MASK (DSP_CORES_MASK << ADSPCS_SPA_SHIFT) +#define ADSPCS_SPA(x) ((x << ADSPCS_SPA_SHIFT) & ADSPCS_SPA_MASK) + +/* Current Power Active - power status of cores, set by hardware */ +#define ADSPCS_CPA_SHIFT 24 +#define ADSPCS_CPA_MASK (DSP_CORES_MASK << ADSPCS_CPA_SHIFT) +#define ADSPCS_CPA(x) ((x << ADSPCS_CPA_SHIFT) & ADSPCS_CPA_MASK) + +#define SST_DSP_POWER_D0 0x0 /* full On */ +#define SST_DSP_POWER_D3 0x3 /* Off */ + struct ssth_window { void __iomem *w0stat; void __iomem *w0up; diff --git a/sound/soc/hda/intel/soc-hda-sst-dsp.c b/sound/soc/hda/intel/soc-hda-sst-dsp.c index 6e85645b3a3e..6285a6772e73 100644 --- a/sound/soc/hda/intel/soc-hda-sst-dsp.c +++ b/sound/soc/hda/intel/soc-hda-sst-dsp.c @@ -34,6 +34,11 @@ #include <sound/soc-hda-sst-dsp.h> #include <sound/soc-hda-sst-ipc.h>
+#define DSP_CORE_POWER_UP_TIMEOUT 50 +#define DSP_CORE_POWER_DOWN_TIMEOUT 50 +#define DSP_CORE_SET_RESET_STATE_TIMEOUT 50 +#define DSP_CORE_UNSET_RESET_STATE_TIMEOUT 50 + u8 ssth_readb_traced(struct ssth_lib *dsp, u32 offset) { u8 val; @@ -135,5 +140,350 @@ void ssth_mailbox_read(struct ssth_lib *ctx, void *msg, size_t bytes) _ssth_memcpy_fromio_32(msg, ctx->window.w0up, bytes); }
+int ssth_register_poll(struct ssth_lib *ctx, u32 offset, u32 mask, + u32 expected_value, u32 timeout, char *operation) +{ + int time = 0; + int ret = 0; + u32 reg; + + dev_dbg(ctx->dev, "In %s\n", __func__); + + /* check if set state successful */ + for (time = 0; time < timeout; time++) { + if ((ssth_readl_alt(ctx, offset) & mask) == expected_value) + break; + + mdelay(1); + } + reg = ssth_readl_alt(ctx, offset); + dev_dbg(ctx->dev, "FW Poll Status: reg=%#x %s %s\n", reg, operation, + (time < timeout) ? "successful" : "timedout"); + ret = time < timeout ? 0 : -ETIME; + + return ret; +} + +static int ssth_dsp_core_set_reset_state(struct ssth_lib *ctx) +{ + int ret = 0; + + dev_dbg(ctx->dev, "In %s\n", __func__); + + /* update bits */ + ssth_updatel_locked(ctx, ADSPCS, CRST_MASK, + ADSPCS_CRST(DSP_CORES_MASK)); + + /* poll with timeout to check if operation successful */ + ret = ssth_register_poll(ctx, + HDA_ADSP_REG_ADSPCS, + ADSPCS_CRST_MASK, + ADSPCS_CRST(DSP_CORES_MASK), + DSP_CORE_SET_RESET_STATE_TIMEOUT, + "Set reset"); + if ((ssth_readl(ctx, ADSPCS) & ADSPCS_CRST(DSP_CORES_MASK)) != + ADSPCS_CRST(DSP_CORES_MASK)) { + dev_err(ctx->dev, "Set reset state failed\n"); + ret = -EIO; + } + + return ret; +} + +static int ssth_dsp_core_unset_reset_state(struct ssth_lib *ctx) +{ + int ret = 0; + + dev_dbg(ctx->dev, "In %s\n", __func__); + + /* update bits */ + ssth_updatel_locked(ctx, ADSPCS, CRST_MASK, 0); + + /* poll with timeout to check if operation successful */ + ret = ssth_register_poll(ctx, + HDA_ADSP_REG_ADSPCS, + ADSPCS_CRST_MASK, + 0, + DSP_CORE_UNSET_RESET_STATE_TIMEOUT, + "Unset reset"); + + if ((ssth_readl(ctx, ADSPCS) & ADSPCS_CRST(DSP_CORES_MASK)) != 0) { + dev_err(ctx->dev, "Unset reset state failed\n"); + ret = -EIO; + } + + return ret; +} + +static int ssth_dsp_core_power_up(struct ssth_lib *ctx) +{ + int ret = 0; + + dev_dbg(ctx->dev, "In %s\n", __func__); + /* update bits */ + ssth_updatel_locked(ctx, + ADSPCS, SPA_MASK, ADSPCS_SPA(DSP_CORES_MASK)); + + /* poll with timeout to check if operation successful */ + ret = ssth_register_poll(ctx, + HDA_ADSP_REG_ADSPCS, + ADSPCS_CPA_MASK, + ADSPCS_CPA(DSP_CORES_MASK), + DSP_CORE_POWER_UP_TIMEOUT, + "Power up"); + + if ((ssth_readl(ctx, ADSPCS) & ADSPCS_CPA(DSP_CORES_MASK)) != + ADSPCS_CPA(DSP_CORES_MASK)) { + dev_err(ctx->dev, "DSP core power up failed\n"); + ret = -EIO; + } + + return ret; +} + +static int ssth_dsp_core_power_down(struct ssth_lib *ctx) +{ + int ret = 0; + + dev_dbg(ctx->dev, "In %s\n", __func__); + + /* update bits */ + ssth_updatel_locked(ctx, ADSPCS, SPA_MASK, 0); + + /* poll with timeout to check if operation successful */ + ret = ssth_register_poll(ctx, + HDA_ADSP_REG_ADSPCS, + ADSPCS_SPA_MASK, + 0, + DSP_CORE_POWER_DOWN_TIMEOUT, + "Power down"); + + return ret; +} + +static bool ssth_is_dsp_core_enable(struct ssth_lib *ctx) +{ + int val = 0; + bool is_enable; + + val = ssth_readl(ctx, ADSPCS); + + is_enable = ((val & ADSPCS_CPA(DSP_CORES_MASK)) && + (val & ADSPCS_SPA(DSP_CORES_MASK)) && + !(val & ADSPCS_CRST(DSP_CORES_MASK)) && + !(val & ADSPCS_CSTALL(DSP_CORES_MASK))); + + dev_dbg(ctx->dev, "DSP core is enabled=%d\n", is_enable); + return is_enable; +} + + +int ssth_enable_dsp_core(struct ssth_lib *ctx) +{ + int ret = 0; + + dev_dbg(ctx->dev, "In %s\n", __func__); + + /* power up */ + ret = ssth_dsp_core_power_up(ctx); + if (ret < 0) { + dev_dbg(ctx->dev, "dsp core power up failed\n"); + return ret; + } + + /* unset reset state */ + ret = ssth_dsp_core_unset_reset_state(ctx); + if (ret < 0) { + dev_dbg(ctx->dev, "dsp core reset failed\n"); + return ret; + } + + /* run core */ + dev_dbg(ctx->dev, "run core...\n"); + ssth_writel(ctx, ADSPCS, ssth_readl(ctx, ADSPCS) & + ~ADSPCS_CSTALL(DSP_CORES_MASK)); + + if (!ssth_is_dsp_core_enable(ctx)) { + dev_err(ctx->dev, "DSP core enable failed\n"); + ret = -EIO; + } + + return ret; +} + +int ssth_disable_dsp_core(struct ssth_lib *ctx) +{ + int ret = 0; + + dev_dbg(ctx->dev, "In %s\n", __func__); + + /* stall core */ + ssth_writel(ctx, ADSPCS, ssth_readl(ctx, ADSPCS) | + ADSPCS_CSTALL(DSP_CORES_MASK)); + + /* set reset state */ + ret = ssth_dsp_core_set_reset_state(ctx); + if (ret < 0) { + dev_err(ctx->dev, "dsp core reset failed\n"); + return ret; + } + + /* power down core*/ + ret = ssth_dsp_core_power_down(ctx); + if (ret < 0) { + dev_err(ctx->dev, "dsp core power down failed\n"); + return ret; + } + + if (ssth_is_dsp_core_enable(ctx)) { + dev_err(ctx->dev, "DSP core disable failed\n"); + ret = -EIO; + } + + return ret; +} + +static int ssth_reset_dsp_core(struct ssth_lib *ctx) +{ + int ret = 0; + + dev_dbg(ctx->dev, "In %s\n", __func__); + + /* stall core */ + ssth_writel(ctx, ADSPCS, ssth_readl(ctx, ADSPCS) & + ADSPCS_CSTALL(DSP_CORES_MASK)); + + /* set reset state */ + ret = ssth_dsp_core_set_reset_state(ctx); + if (ret < 0) { + dev_err(ctx->dev, "dsp reset failed\n"); + return ret; + } + + /* unset reset state */ + ret = ssth_dsp_core_unset_reset_state(ctx); + if (ret < 0) { + dev_dbg(ctx->dev, "dsp unset reset failes\n"); + return ret; + } + + /* run core */ + dev_dbg(ctx->dev, "run core...\n"); + ssth_writel(ctx, ADSPCS, ssth_readl(ctx, ADSPCS) & + ~ADSPCS_CSTALL(DSP_CORES_MASK)); + + if (ssth_is_dsp_core_enable(ctx)) { + dev_err(ctx->dev, "DSP core reset failed\n"); + ret = -EIO; + } + + return ret; +} + +int ssth_boot_dsp(struct ssth_lib *ctx) +{ + int ret = 0; + + dev_dbg(ctx->dev, "In %s\n", __func__); + + if (ssth_is_dsp_core_enable(ctx)) { + /* Dsp core powered up - simply reset core */ + dev_dbg(ctx->dev, "dsp core is already enabled, so reset the dap core\n"); + ret = ssth_reset_dsp_core(ctx); + } else { + /*disable and enable to make sure DSP is invalid state */ + ret = ssth_disable_dsp_core(ctx); + + if (ret < 0) { + dev_err(ctx->dev, "dsp disable core failes\n"); + return ret; + } + ret = ssth_enable_dsp_core(ctx); + } + + return ret; +} + +/* + * interrupt handler + */ +static irqreturn_t ssth_interrupt(int irq, void *dev_id) +{ + struct ssth_lib *ctx = (struct ssth_lib *) dev_id; + u32 val; + irqreturn_t result = IRQ_NONE; + + spin_lock(&ctx->reg_lock); + + val = ssth_readl(ctx, ADSPIS); + + if (val & ADSPIS_IPC) { + ssth_ipc_int_disable(ctx); + queue_work(ctx->intr_wq, &ctx->ipc_process_msg_work); + result = IRQ_HANDLED; + } + + spin_unlock(&ctx->reg_lock); + return result; +} + +static int ssth_acquire_irq(struct ssth_lib *ctx) +{ + if (request_threaded_irq(ctx->irq, ssth_interrupt, + NULL, IRQF_SHARED, KBUILD_MODNAME, ctx)) { + dev_err(ctx->dev, "unable to grab threaded IRQ %d, disabling device\n", ctx->irq); + return -1; + } + return 0; +} + +int ssth_dsp_init(struct ssth_lib *ctx) +{ + int ret = 0; + + mutex_init(&ctx->sst_lock); + spin_lock_init(&ctx->reg_lock); + + dev_dbg(ctx->dev, "In %s\n", __func__); + + /* initialize IPC */ + ctx->ipc = ssth_ipc_init(ctx->dev, ctx); + if (ctx->ipc == NULL) + ret = -ENODEV; + + /* Now let's request the IRQ */ + ssth_acquire_irq(ctx); + + return ret; +} + +int ssth_dsp_free0(struct ssth_lib *dsp) +{ + int ret = 0; + + dev_dbg(dsp->dev, "In %s\n", __func__); + + ssth_ipc_int_disable(dsp); + + free_irq(dsp->irq, dsp); + ssth_ipc_free(dsp->ipc); + ssth_disable_dsp_core(dsp); + kfree(dsp); + return ret; +} +EXPORT_SYMBOL_GPL(ssth_dsp_free0); + +bool ssth_dsp_is_running(struct ssth_lib *ctx) +{ + bool ret = 0; + + mutex_lock(&ctx->sst_lock); + ret = (ctx->sst_state == SST_DSP_RUNNING) ? 1 : 0; + mutex_unlock(&ctx->sst_lock); + + return ret; +} +EXPORT_SYMBOL_GPL(ssth_dsp_is_running); + MODULE_DESCRIPTION("HDA SST/IPC Library"); MODULE_LICENSE("GPL v2");
On Fri, Apr 17, 2015 at 06:46:02PM +0530, Vinod Koul wrote:
+static int ssth_acquire_irq(struct ssth_lib *ctx) +{
- if (request_threaded_irq(ctx->irq, ssth_interrupt,
NULL, IRQF_SHARED, KBUILD_MODNAME, ctx)) {
dev_err(ctx->dev, "unable to grab threaded IRQ %d, disabling device\n", ctx->irq);
return -1;
Don't discard the return code, pass it back. I'm pretty sure the error wasn't -EPERM anyway. Though...
- /* initialize IPC */
- ctx->ipc = ssth_ipc_init(ctx->dev, ctx);
- if (ctx->ipc == NULL)
ret = -ENODEV;
- /* Now let's request the IRQ */
- ssth_acquire_irq(ctx);
..of course we ignore errors anyway. Why not just inline this?
+int ssth_dsp_free0(struct ssth_lib *dsp) +{
- int ret = 0;
- dev_dbg(dsp->dev, "In %s\n", __func__);
- ssth_ipc_int_disable(dsp);
- free_irq(dsp->irq, dsp);
- ssth_ipc_free(dsp->ipc);
- ssth_disable_dsp_core(dsp);
- kfree(dsp);
- return ret;
+} +EXPORT_SYMBOL_GPL(ssth_dsp_free0);
free0?
+bool ssth_dsp_is_running(struct ssth_lib *ctx) +{
- bool ret = 0;
- mutex_lock(&ctx->sst_lock);
- ret = (ctx->sst_state == SST_DSP_RUNNING) ? 1 : 0;
- mutex_unlock(&ctx->sst_lock);
How does this get used? The state could change immediately after returning. Also no need for the ternery operator there, logic operations generate logic results anyway.
On Fri, Apr 24, 2015 at 06:11:40PM +0100, Mark Brown wrote:
On Fri, Apr 17, 2015 at 06:46:02PM +0530, Vinod Koul wrote:
+static int ssth_acquire_irq(struct ssth_lib *ctx) +{
- if (request_threaded_irq(ctx->irq, ssth_interrupt,
NULL, IRQF_SHARED, KBUILD_MODNAME, ctx)) {
dev_err(ctx->dev, "unable to grab threaded IRQ %d, disabling device\n", ctx->irq);
return -1;
Don't discard the return code, pass it back. I'm pretty sure the error wasn't -EPERM anyway. Though...
yes, we will fix it
- /* initialize IPC */
- ctx->ipc = ssth_ipc_init(ctx->dev, ctx);
- if (ctx->ipc == NULL)
ret = -ENODEV;
- /* Now let's request the IRQ */
- ssth_acquire_irq(ctx);
..of course we ignore errors anyway. Why not just inline this?
thats not right, we should do that...
+int ssth_dsp_free0(struct ssth_lib *dsp) +{
- int ret = 0;
- dev_dbg(dsp->dev, "In %s\n", __func__);
- ssth_ipc_int_disable(dsp);
- free_irq(dsp->irq, dsp);
- ssth_ipc_free(dsp->ipc);
- ssth_disable_dsp_core(dsp);
- kfree(dsp);
- return ret;
+} +EXPORT_SYMBOL_GPL(ssth_dsp_free0);
free0?
crap, will fix
+bool ssth_dsp_is_running(struct ssth_lib *ctx) +{
- bool ret = 0;
- mutex_lock(&ctx->sst_lock);
- ret = (ctx->sst_state == SST_DSP_RUNNING) ? 1 : 0;
- mutex_unlock(&ctx->sst_lock);
How does this get used? The state could change immediately after returning. Also no need for the ternery operator there, logic operations generate logic results anyway.
yes you are right, we should hold the lock and check, will fix this
Thanks
From: "Subhransu S. Prusty" subhransu.s.prusty@intel.com
This will be assigned with the required bus io ops.
Signed-off-by: Subhransu S. Prusty subhransu.s.prusty@intel.com Signed-off-by: Vinod Koul vinod.koul@intel.com --- include/sound/soc-hda-sst-dsp.h | 16 ++++++++++++++++ 1 file changed, 16 insertions(+)
diff --git a/include/sound/soc-hda-sst-dsp.h b/include/sound/soc-hda-sst-dsp.h index 4a89c3dda5ab..3d8b0c065002 100644 --- a/include/sound/soc-hda-sst-dsp.h +++ b/include/sound/soc-hda-sst-dsp.h @@ -138,12 +138,28 @@ struct ssth_window { size_t w1_size; };
+struct ssth_dsp_loader_ops { + int (*init)(struct device *dev); + int (*prepare)(struct device *dev, unsigned int format, + unsigned int byte_size, + struct snd_dma_buffer *bufp); + void (*trigger)(struct device *dev, bool start); + + void (*cleanup)(struct device *dev, + struct snd_dma_buffer *dmab); + int (*alloc_dma_buf)(struct device *dev, + struct snd_dma_buffer *dmab, u32 size); + int (*free_dma_buf)(struct device *dev, + struct snd_dma_buffer *dmab); +}; + struct ssth_lib { struct device *dev; struct ssth_ipc *ipc; void __iomem *mmio_base; struct ssth_window window; int irq; + struct ssth_dsp_loader_ops dsp_ops; struct snd_dma_buffer dsp_fw_buf; int sst_state; struct mutex sst_lock;
From: "Subhransu S. Prusty" subhransu.s.prusty@intel.com
For skylake the code loader dma will be used for fw download, module download, and sending mailbox.
Signed-off-by: Subhransu S. Prusty subhransu.s.prusty@intel.com Signed-off-by: Vinod Koul vinod.koul@intel.com --- include/sound/soc-hda-sst-cldma.h | 234 ++++++++++++++++++++++++ include/sound/soc-hda-sst-dsp.h | 9 + sound/soc/hda/intel/Makefile | 2 +- sound/soc/hda/intel/soc-hda-sst-cldma.c | 297 +++++++++++++++++++++++++++++++ 4 files changed, 541 insertions(+), 1 deletion(-) create mode 100644 include/sound/soc-hda-sst-cldma.h create mode 100644 sound/soc/hda/intel/soc-hda-sst-cldma.c
diff --git a/include/sound/soc-hda-sst-cldma.h b/include/sound/soc-hda-sst-cldma.h new file mode 100644 index 000000000000..a1e0c48ae5d5 --- /dev/null +++ b/include/sound/soc-hda-sst-cldma.h @@ -0,0 +1,234 @@ +/* + * Intel Code Loader DMA 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. + * + */ + +#define FW_CL_STREAM_NUMBER 0xf + +#define DMA_ADDRESS_128_BITS_ALIGNMENT 7 +#define BDL_ALIGN(x) (x >> DMA_ADDRESS_128_BITS_ALIGNMENT) + +#define ADSPIC_CL_DMA 0x2 +#define ADSPIS_CL_DMA 0x2 +#define CL_DMA_SD_INT_DESC_ERR 0x10 /* Descriptor error interrupt */ +#define CL_DMA_SD_INT_FIFO_ERR 0x08 /* FIFO error interrupt */ +#define CL_DMA_SD_INT_COMPLETE 0x04 /* Buffer completion interrupt */ + +/* Code Loader - + * Stream Registers */ +#define HDA_ADSP_REG_CL_SD_CTL (HDA_ADSP_LOADER_BASE + 0x00) +#define HDA_ADSP_REG_CL_SD_STS (HDA_ADSP_LOADER_BASE + 0x03) +#define HDA_ADSP_REG_CL_SD_LPIB (HDA_ADSP_LOADER_BASE + 0x04) +#define HDA_ADSP_REG_CL_SD_CBL (HDA_ADSP_LOADER_BASE + 0x08) +#define HDA_ADSP_REG_CL_SD_LVI (HDA_ADSP_LOADER_BASE + 0x0c) +#define HDA_ADSP_REG_CL_SD_FIFOW (HDA_ADSP_LOADER_BASE + 0x0e) +#define HDA_ADSP_REG_CL_SD_FIFOSIZE (HDA_ADSP_LOADER_BASE + 0x10) +#define HDA_ADSP_REG_CL_SD_FORMAT (HDA_ADSP_LOADER_BASE + 0x12) +#define HDA_ADSP_REG_CL_SD_FIFOL (HDA_ADSP_LOADER_BASE + 0x14) +#define HDA_ADSP_REG_CL_SD_BDLPL (HDA_ADSP_LOADER_BASE + 0x18) +#define HDA_ADSP_REG_CL_SD_BDLPU (HDA_ADSP_LOADER_BASE + 0x1c) + +/* Code Loader - + * Software Position Based FIFO Capability Registers */ +#define HDA_ADSP_REG_CL_SPBFIFO (HDA_ADSP_LOADER_BASE + 0x20) +#define HDA_ADSP_REG_CL_SPBFIFO_SPBFCH (HDA_ADSP_REG_CL_SPBFIFO + 0x0) +#define HDA_ADSP_REG_CL_SPBFIFO_SPBFCCTL (HDA_ADSP_REG_CL_SPBFIFO + 0x4) +#define HDA_ADSP_REG_CL_SPBFIFO_SPIB (HDA_ADSP_REG_CL_SPBFIFO + 0x8) +#define HDA_ADSP_REG_CL_SPBFIFO_MAXFIFOS (HDA_ADSP_REG_CL_SPBFIFO + 0xc) + + +/* Code Loader - + * Stream Descriptor x Control */ +/* Stream Reset */ +#define CL_SD_CTL_SRST_SHIFT 0 +#define CL_SD_CTL_SRST_MASK (1 << CL_SD_CTL_SRST_SHIFT) +#define CL_SD_CTL_SRST(x) \ + ((x << CL_SD_CTL_SRST_SHIFT) & CL_SD_CTL_SRST_MASK) + +/* Stream Run */ +#define CL_SD_CTL_RUN_SHIFT 1 +#define CL_SD_CTL_RUN_MASK (1 << CL_SD_CTL_RUN_SHIFT) +#define CL_SD_CTL_RUN(x) \ + ((x << CL_SD_CTL_RUN_SHIFT) & CL_SD_CTL_RUN_MASK) + +/* Interrupt On Completion Enable */ +#define CL_SD_CTL_IOCE_SHIFT 2 +#define CL_SD_CTL_IOCE_MASK (1 << CL_SD_CTL_IOCE_SHIFT) +#define CL_SD_CTL_IOCE(x) \ + ((x << CL_SD_CTL_IOCE_SHIFT) & CL_SD_CTL_IOCE_MASK) + +/* FIFO Error Interrupt Enable */ +#define CL_SD_CTL_FEIE_SHIFT 3 +#define CL_SD_CTL_FEIE_MASK (1 << CL_SD_CTL_FEIE_SHIFT) +#define CL_SD_CTL_FEIE(x) \ + ((x << CL_SD_CTL_FEIE_SHIFT) & CL_SD_CTL_FEIE_MASK) + +/* Descriptor Error Interrupt Enable */ +#define CL_SD_CTL_DEIE_SHIFT 4 +#define CL_SD_CTL_DEIE_MASK (1 << CL_SD_CTL_DEIE_SHIFT) +#define CL_SD_CTL_DEIE(x) \ + ((x << CL_SD_CTL_DEIE_SHIFT) & CL_SD_CTL_DEIE_MASK) + +/* FIFO Limit Change */ +#define CL_SD_CTL_FIFOLC_SHIFT 5 +#define CL_SD_CTL_FIFOLC_MASK (1 << CL_SD_CTL_FIFOLC_SHIFT) +#define CL_SD_CTL_FIFOLC(x) \ + ((x << CL_SD_CTL_FIFOLC_SHIFT) & CL_SD_CTL_FIFOLC_MASK) + +/* Stripe Control */ +#define CL_SD_CTL_STRIPE_SHIFT 16 +#define CL_SD_CTL_STRIPE_MASK (0x3 << CL_SD_CTL_STRIPE_SHIFT) +#define CL_SD_CTL_STRIPE(x) \ + ((x << CL_SD_CTL_STRIPE_SHIFT) & CL_SD_CTL_STRIPE_MASK) + +/* Traffic Priority */ +#define CL_SD_CTL_TP_SHIFT 18 +#define CL_SD_CTL_TP_MASK (1 << CL_SD_CTL_TP_SHIFT) +#define CL_SD_CTL_TP(x) \ + ((x << CL_SD_CTL_TP_SHIFT) & CL_SD_CTL_TP_MASK) + +/* Bidirectional Direction Control */ +#define CL_SD_CTL_DIR_SHIFT 19 +#define CL_SD_CTL_DIR_MASK (1 << CL_SD_CTL_DIR_SHIFT) +#define CL_SD_CTL_DIR(x) \ + ((x << CL_SD_CTL_DIR_SHIFT) & CL_SD_CTL_DIR_MASK) + +/* Stream Number */ +#define CL_SD_CTL_STRM_SHIFT 20 +#define CL_SD_CTL_STRM_MASK (0xf << CL_SD_CTL_STRM_SHIFT) +#define CL_SD_CTL_STRM(x) \ + ((x << CL_SD_CTL_STRM_SHIFT) & CL_SD_CTL_STRM_MASK) + + +/* Code Loader - + * Stream Descriptor x Status */ +/* Buffer Completion Interrupt Status */ +#define CL_SD_STS_BCIS(x) CL_SD_CTL_IOCE(x) + +/* FIFO Error */ +#define CL_SD_STS_FIFOE(x) CL_SD_CTL_FEIE(x) + +/* Descriptor Error */ +#define CL_SD_STS_DESE(x) CL_SD_CTL_DEIE(x) + +/* FIFO Ready */ +#define CL_SD_STS_FIFORDY(x) CL_SD_CTL_FIFOLC(x) + + +/* Code Loader - + * Stream Descriptor x Last Valid Index */ +#define CL_SD_LVI_SHIFT 0 +#define CL_SD_LVI_MASK (0xff << CL_SD_LVI_SHIFT) +#define CL_SD_LVI(x) ((x << CL_SD_LVI_SHIFT) & CL_SD_LVI_MASK) + + +/* Code Loader - + * Stream Descriptor x FIFO Eviction Watermark */ +#define CL_SD_FIFOW_SHIFT 0 +#define CL_SD_FIFOW_MASK (0x7 << CL_SD_FIFOW_SHIFT) +#define CL_SD_FIFOW(x) ((x << CL_SD_FIFOW_SHIFT) & CL_SD_FIFOW_MASK) + + +/* Code Loader - + * Stream Descriptor x Buffer Descriptor List Pointer Lower Base Address */ +/* Protect */ +#define CL_SD_BDLPLBA_PROT_SHIFT 0 +#define CL_SD_BDLPLBA_PROT_MASK (1 << CL_SD_BDLPLBA_PROT_SHIFT) +#define CL_SD_BDLPLBA_PROT(x) \ + ((x << CL_SD_BDLPLBA_PROT_SHIFT) & CL_SD_BDLPLBA_PROT_MASK) + +/* Buffer Descriptor List Lower Base Address */ +#define CL_SD_BDLPLBA_SHIFT 7 +#define CL_SD_BDLPLBA_MASK (0x1ffffff << CL_SD_BDLPLBA_SHIFT) +#define CL_SD_BDLPLBA(x) \ + ((BDL_ALIGN(lower_32_bits(x)) << CL_SD_BDLPLBA_SHIFT) & CL_SD_BDLPLBA_MASK) + +/* Buffer Descriptor List Upper Base Address */ +#define CL_SD_BDLPUBA_SHIFT 0 +#define CL_SD_BDLPUBA_MASK (0xffffffff << CL_SD_BDLPUBA_SHIFT) +#define CL_SD_BDLPUBA(x) \ + ((upper_32_bits(x) << CL_SD_BDLPUBA_SHIFT) & CL_SD_BDLPUBA_MASK) + +/* Code Loader - Software Position Based FIFO + * Capability Registers x Software Position Based FIFO Header */ + +/* Next Capability Pointer */ +#define CL_SPBFIFO_SPBFCH_PTR_SHIFT 0 +#define CL_SPBFIFO_SPBFCH_PTR_MASK (0xff << CL_SPBFIFO_SPBFCH_PTR_SHIFT) +#define CL_SPBFIFO_SPBFCH_PTR(x) \ + ((x << CL_SPBFIFO_SPBFCH_PTR_SHIFT) & CL_SPBFIFO_SPBFCH_PTR_MASK) + +/* Capability Identifier */ +#define CL_SPBFIFO_SPBFCH_ID_SHIFT 16 +#define CL_SPBFIFO_SPBFCH_ID_MASK (0xfff << CL_SPBFIFO_SPBFCH_ID_SHIFT) +#define CL_SPBFIFO_SPBFCH_ID(x) \ + ((x << CL_SPBFIFO_SPBFCH_ID_SHIFT) & CL_SPBFIFO_SPBFCH_ID_MASK) + +/* Capability Version */ +#define CL_SPBFIFO_SPBFCH_VER_SHIFT 28 +#define CL_SPBFIFO_SPBFCH_VER_MASK (0xf << CL_SPBFIFO_SPBFCH_VER_SHIFT) +#define CL_SPBFIFO_SPBFCH_VER(x) \ + ((x << CL_SPBFIFO_SPBFCH_VER_SHIFT) & CL_SPBFIFO_SPBFCH_VER_MASK) + + +/* Code Loader - + Software Position Based FIFO Control */ +/* Software Position in Buffer Enable */ +#define CL_SPBFIFO_SPBFCCTL_SPIBE_SHIFT 0 +#define CL_SPBFIFO_SPBFCCTL_SPIBE_MASK (1 << CL_SPBFIFO_SPBFCCTL_SPIBE_SHIFT) +#define CL_SPBFIFO_SPBFCCTL_SPIBE(x) \ + ((x << CL_SPBFIFO_SPBFCCTL_SPIBE_SHIFT) & CL_SPBFIFO_SPBFCCTL_SPIBE_MASK) + +#define SSTH_WAIT_TIMEOUT 1000 /* 1 sec */ +#define SSTH_MAX_BUFFER_SIZE (4 * PAGE_SIZE) + +enum ssth_cl_dma_wake_states { + SSTH_CL_DMA_STATUS_NONE = 0, + SSTH_CL_DMA_BUF_COMPLETE, + SSTH_CL_DMA_ERR, /* TODO: Expand the error states */ +}; + +struct ssth_lib; +struct ssth_cl_dev_ops { + void (*cl_setup_bdle)(struct snd_dma_buffer *dmab_data, + u32 **bdlp, u32 page_count); + void (*cl_setup_controller)(struct ssth_lib *ctx, + struct snd_dma_buffer *dmab_bdl, + unsigned int max_size, u32 page_count); + void (*cl_setup_spb)(struct ssth_lib *ctx, unsigned int size); + void (*cl_cleanup_spb)(struct ssth_lib *ctx); + void (*cl_trigger)(struct ssth_lib *ctx, bool enable); + void (*cl_cleaup_controller)(struct ssth_lib *ctx); + int (*cl_copy_to_bdl)(struct ssth_lib *ctx, const void *bin, u32 size); +}; + +/* Code loader device */ +struct ssth_cl_dev { + /* TODO: Add for synchroinization mechanism */ + struct snd_dma_buffer dmab_data; + struct snd_dma_buffer dmab_bdl; + + unsigned int bufsize; /* Ring buffer size = period_count * period_size */ + unsigned int period_count; + unsigned int period_size; + u32 *curr_pos; /* Current possition in the blob to transferred */ + unsigned int bytes_left; /* Bytes left to be transferred */ + unsigned int curr_spib_pos; /* Current position in ring buffer */ + bool cl_ops; + struct ssth_cl_dev_ops ops; + + wait_queue_head_t wait_queue; + int wake_status; + bool wait_condition; +}; diff --git a/include/sound/soc-hda-sst-dsp.h b/include/sound/soc-hda-sst-dsp.h index 3d8b0c065002..82ba1c9a68c4 100644 --- a/include/sound/soc-hda-sst-dsp.h +++ b/include/sound/soc-hda-sst-dsp.h @@ -19,6 +19,7 @@
#include <linux/spinlock.h> #include <sound/memalloc.h> +#include <sound/soc-hda-sst-cldma.h>
#define ssth_writel_andor(ctx, reg, mask_and, mask_or) \ ssth_writel_traced( \ @@ -101,6 +102,12 @@ #define ADSPIC_IPC 1 #define ADSPIS_IPC 1
+#define ADSPIC_CL_DMA 0x2 +#define ADSPIS_CL_DMA 0x2 +#define CL_DMA_SD_INT_DESC_ERR 0x10 /* Descriptor error interrupt */ +#define CL_DMA_SD_INT_FIFO_ERR 0x08 /* FIFO error interrupt */ +#define CL_DMA_SD_INT_COMPLETE 0x04 /* Buffer completion interrupt */ + /* ADSPCS - Audio DSP Control & Status */ #define DSP_CORES 1 #define DSP_CORE0_MASK 1 @@ -168,6 +175,8 @@ struct ssth_lib {
struct workqueue_struct *intr_wq; struct work_struct ipc_process_msg_work; + struct ssth_cl_dev cl_dev; + struct work_struct cl_dma_process_work; };
enum ssth_states { diff --git a/sound/soc/hda/intel/Makefile b/sound/soc/hda/intel/Makefile index 77c44428f40d..a31db94b2dde 100644 --- a/sound/soc/hda/intel/Makefile +++ b/sound/soc/hda/intel/Makefile @@ -1,5 +1,5 @@
-snd-soc-hda-sst-dsp-objs := soc-hda-sst-ipc.o soc-hda-sst-dsp.o +snd-soc-hda-sst-dsp-objs := soc-hda-sst-ipc.o soc-hda-sst-dsp.o soc-hda-sst-cldma.o
# SST DSP Library obj-$(CONFIG_SND_SOC_HDA_SST_DSP) += snd-soc-hda-sst-dsp.o diff --git a/sound/soc/hda/intel/soc-hda-sst-cldma.c b/sound/soc/hda/intel/soc-hda-sst-cldma.c new file mode 100644 index 000000000000..0222ca9f6fdc --- /dev/null +++ b/sound/soc/hda/intel/soc-hda-sst-cldma.c @@ -0,0 +1,297 @@ +/* + * soc-hda-sst-cldma.c - Code Loader DMA handler + * + * Copyright (C) 2015 Intel Corp + * Author: Subhransu S. Prusty subhransu.s.prusty@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 published by + * the Free Software Foundation; version 2 of the License. + * + * 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/slab.h> +#include <linux/delay.h> +#include <linux/kernel.h> +#include <linux/device.h> +#include <linux/kthread.h> +#include <linux/irqreturn.h> +#include <sound/soc-hda-sst-dsp.h> + +void ssth_cldma_int_enable(struct ssth_lib *ctx) +{ + ssth_updatel_bits(ctx, HDA_ADSP_REG_ADSPIC, + ADSPIC_CL_DMA, 0x2); +} +void ssth_cldma_int_disable(struct ssth_lib *ctx) +{ + ssth_updatel_bits(ctx, HDA_ADSP_REG_ADSPIC, + ADSPIC_CL_DMA, 0); +} + +/* Code loader helper APIs */ +static void ssth_skl_cl_setup_bdle(struct snd_dma_buffer *dmab_data, + u32 **bdlp, u32 count) +{ + u32 *bdl = *bdlp; + int i = 0; + + for (i = 0; i < count; i++) { + phys_addr_t addr = virt_to_phys(dmab_data->area + i * PAGE_SIZE); + + bdl[0] = cpu_to_le32(lower_32_bits(addr)); + bdl[1] = cpu_to_le32(upper_32_bits(addr)); + bdl[2] = cpu_to_le32(PAGE_SIZE); + bdl[3] = 0; + bdl += 4; + } +} + +/* Setup controller*/ +static void ssth_skl_cl_setup_controller(struct ssth_lib *ctx, + struct snd_dma_buffer *dmab_bdl, unsigned int max_size, + u32 count) +{ + ssth_writel(ctx, CL_SD_BDLPL, CL_SD_BDLPLBA(dmab_bdl->addr)); + ssth_writel(ctx, CL_SD_BDLPU, CL_SD_BDLPUBA(dmab_bdl->addr)); + + ssth_writel(ctx, CL_SD_CBL, max_size); + ssth_writel(ctx, CL_SD_LVI, count - 1); + ssth_updatel(ctx, CL_SD_CTL, IOCE_MASK, CL_SD_CTL_IOCE(1)); + ssth_updatel(ctx, CL_SD_CTL, FEIE_MASK, CL_SD_CTL_FEIE(1)); + ssth_updatel(ctx, CL_SD_CTL, DEIE_MASK, CL_SD_CTL_DEIE(1)); + ssth_updatel(ctx, CL_SD_CTL, STRM_MASK, + CL_SD_CTL_STRM(FW_CL_STREAM_NUMBER)); +} + +static void ssth_skl_setup_spb(struct ssth_lib *ctx, unsigned int size) +{ + ssth_updatel(ctx, CL_SPBFIFO_SPBFCCTL, + SPIBE_MASK, CL_SPBFIFO_SPBFCCTL_SPIBE(1)); + ssth_writel(ctx, CL_SPBFIFO_SPIB, size); +} + +static void ssth_skl_cleanup_spb(struct ssth_lib *ctx) +{ + ssth_updatel(ctx, CL_SPBFIFO_SPBFCCTL, + SPIBE_MASK, CL_SPBFIFO_SPBFCCTL_SPIBE(0)); + ssth_writel(ctx, CL_SPBFIFO_SPIB, 0); +} + +static void ssth_skl_cl_trigger(struct ssth_lib *ctx, bool enable) +{ + if (enable) + ssth_updatel(ctx, CL_SD_CTL, RUN_MASK, CL_SD_CTL_RUN(1)); + else + ssth_updatel(ctx, CL_SD_CTL, RUN_MASK, CL_SD_CTL_RUN(0)); +} + +static void ssth_skl_cl_cleaup(struct ssth_lib *ctx) +{ + ssth_updatel(ctx, CL_SD_CTL, IOCE_MASK, CL_SD_CTL_IOCE(0)); + ssth_updatel(ctx, CL_SD_CTL, FEIE_MASK, CL_SD_CTL_FEIE(0)); + ssth_updatel(ctx, CL_SD_CTL, DEIE_MASK, CL_SD_CTL_DEIE(0)); + ssth_updatel(ctx, CL_SD_CTL, STRM_MASK, CL_SD_CTL_STRM(0)); + + ssth_writel(ctx, CL_SD_BDLPL, CL_SD_BDLPLBA(0)); + ssth_writel(ctx, CL_SD_BDLPU, 0); + + ssth_writel(ctx, CL_SD_CBL, 0); + ssth_writel(ctx, CL_SD_LVI, 0); +} + +int ssth_cl_dma_fill_buffer(struct ssth_lib *ctx, unsigned int size, + bool wait, bool int_enable, int buf_pos_index, + bool update_pos, bool trigger) +{ + unsigned int link_pos = 0; + + memcpy((ctx->cl_dev.dmab_data.area + (buf_pos_index * ctx->cl_dev.period_size)), + ctx->cl_dev.curr_pos, size); + + if (update_pos) { + ctx->cl_dev.bytes_left = ctx->cl_dev.bytes_left - size; + ctx->cl_dev.curr_pos = ctx->cl_dev.curr_pos + size; + } + + if (wait) { + ctx->cl_dev.wait_condition = false; + ssth_cldma_int_enable(ctx); + } + + ctx->cl_dev.ops.cl_setup_spb(ctx, ctx->cl_dev.curr_spib_pos); + if (trigger) + ctx->cl_dev.ops.cl_trigger(ctx, true); + + if (wait) { + if (wait_event_timeout(ctx->cl_dev.wait_queue, + ctx->cl_dev.wait_condition, + msecs_to_jiffies(SSTH_WAIT_TIMEOUT))) { + dev_dbg(ctx->dev, "%s: Event wake\n", __func__); + if (ctx->cl_dev.wake_status == SSTH_CL_DMA_BUF_COMPLETE) { + ctx->cl_dev.wake_status = SSTH_CL_DMA_STATUS_NONE; + return 0; + } else { + dev_err(ctx->dev, "%s: DMA Error\n", __func__); + /* TODO: Handle DMA error scenario */ + return -EIO; + } + } else { + link_pos = ssth_readl(ctx, CL_SD_LPIB); + dev_err(ctx->dev, "%s: Wait timeout with lpib status: %u\n", + __func__, link_pos); + + /* TODO: Handle wait timeout error scenario */ + return -EIO; + } + } + return 0; +} + +static int ssth_cl_copy_to_bdl(struct ssth_lib *ctx, const void *bin, u32 size) +{ + unsigned int link_pos = 0; + unsigned int bdl_index = 0; + int ret = 0; + + if (size <= 0) + return -EINVAL; + + ctx->cl_dev.bytes_left = size; + ctx->cl_dev.curr_pos = (u32 *)bin; + ctx->cl_dev.curr_spib_pos = 0; + + if (ctx->cl_dev.bytes_left <= ctx->cl_dev.bufsize && + ctx->cl_dev.bytes_left > ctx->cl_dev.period_size) { + + dev_dbg(ctx->dev, "%s: size less than buffer size: %u\n", + __func__, ctx->cl_dev.bytes_left); + ssth_cldma_int_disable(ctx); + ctx->cl_dev.curr_spib_pos = ctx->cl_dev.bytes_left; + ssth_cl_dma_fill_buffer(ctx, size, false, false, 0, false, true); + do { + mdelay(5); + link_pos = ssth_readl(ctx, CL_SD_LPIB); + } while (link_pos < size); + goto cleanup; + } + + dev_dbg(ctx->dev, "%s: Total binary size: %u\n", + __func__, ctx->cl_dev.bytes_left); + ctx->cl_dev.curr_spib_pos = + ctx->cl_dev.curr_spib_pos + ctx->cl_dev.bufsize; + dev_dbg(ctx->dev, "%s: spib pos: %u\n", __func__, ctx->cl_dev.curr_spib_pos); + ret = ssth_cl_dma_fill_buffer(ctx, ctx->cl_dev.bufsize, true, + true, 0, true, true); + if (ret < 0) + goto cleanup; + + do { + if (ctx->cl_dev.bytes_left > ctx->cl_dev.period_size) { + ctx->cl_dev.curr_spib_pos = + ctx->cl_dev.curr_spib_pos + ctx->cl_dev.period_size; + bdl_index = (ctx->cl_dev.curr_spib_pos / ctx->cl_dev.period_size) - 1; + ret = ssth_cl_dma_fill_buffer(ctx, ctx->cl_dev.period_size, true, + true, bdl_index, true, false); + if (ret < 0) + goto cleanup; + } else { + ctx->cl_dev.curr_spib_pos = + ctx->cl_dev.curr_spib_pos + ctx->cl_dev.bufsize; + bdl_index = ctx->cl_dev.curr_spib_pos / ctx->cl_dev.period_size; + ssth_cl_dma_fill_buffer(ctx, ctx->cl_dev.bytes_left, + false, false, bdl_index, false, false); + do { + mdelay(5); + link_pos = ssth_readl(ctx, CL_SD_LPIB); + } while (link_pos < size); + } + } while (ctx->cl_dev.bytes_left > 0); + +cleanup: + ctx->cl_dev.ops.cl_trigger(ctx, false); + ctx->cl_dev.ops.cl_cleanup_spb(ctx); + + return ret; +} +void ssth_process_cl_dma(struct work_struct *work) +{ + u8 cl_dma_intr_status; + struct ssth_lib *ctx = container_of(work, + struct ssth_lib, cl_dma_process_work); + + cl_dma_intr_status = ssth_readb(ctx, CL_SD_STS); + + if (!(cl_dma_intr_status & CL_DMA_SD_INT_COMPLETE)) { + /* TODO: Handle error scenarios */ + } else { + ctx->cl_dev.wake_status = SSTH_CL_DMA_BUF_COMPLETE; + ctx->cl_dev.wait_condition = true; + wake_up(&ctx->cl_dev.wait_queue); + } +} + +int ssth_cl_dma_prepare(struct ssth_lib *ctx) +{ + int ret = 0; + u32 *bdl; + + ctx->cl_dev.period_size = PAGE_SIZE; + ctx->cl_dev.bufsize = SSTH_MAX_BUFFER_SIZE; + + ctx->cl_dev.period_count = + (ctx->cl_dev.bufsize/ctx->cl_dev.period_size); + + /* Make sure buffer size is mutliple of period size */ + if (ctx->cl_dev.bufsize % ctx->cl_dev.period_size) { + dev_err(ctx->dev, "Buffer size is not mutltiple of Period size\n"); + return -EINVAL; + } + dev_dbg(ctx->dev, "%s: buffer size: %u\n", __func__, ctx->cl_dev.bufsize); + dev_dbg(ctx->dev, "%s: period count: %u\n", __func__, ctx->cl_dev.period_count); + + /* Allocate cl ops */ + ctx->cl_dev.cl_ops = true, + ctx->cl_dev.ops.cl_setup_bdle = ssth_skl_cl_setup_bdle, + ctx->cl_dev.ops.cl_setup_controller = ssth_skl_cl_setup_controller, + ctx->cl_dev.ops.cl_setup_spb = ssth_skl_setup_spb, + ctx->cl_dev.ops.cl_cleanup_spb = ssth_skl_cleanup_spb, + ctx->cl_dev.ops.cl_trigger = ssth_skl_cl_trigger, + ctx->cl_dev.ops.cl_cleaup_controller = ssth_skl_cl_cleaup, + ctx->cl_dev.ops.cl_copy_to_bdl = ssth_cl_copy_to_bdl, + + /* Allocate buffer*/ + ret = ctx->dsp_ops.alloc_dma_buf(ctx->dev, + &ctx->cl_dev.dmab_data, ctx->cl_dev.bufsize); + if (ret < 0) { + dev_err(ctx->dev, "Alloc buffer for base fw failed: %x", ret); + return ret; + } + /* Setup CL DMA ring buffer */ + /* Allocate bdl */ + ret = ctx->dsp_ops.alloc_dma_buf(ctx->dev, + &ctx->cl_dev.dmab_bdl, PAGE_SIZE); + if (ret < 0) { + dev_err(ctx->dev, "Alloc buffer for blde failed: %x", ret); + ctx->dsp_ops.free_dma_buf(ctx->dev, &ctx->cl_dev.dmab_data); + return ret; + } + bdl = (u32 *)ctx->cl_dev.dmab_bdl.area; + + /* Allocate BDLs */ + ctx->cl_dev.ops.cl_setup_bdle(&ctx->cl_dev.dmab_data, + &bdl, ctx->cl_dev.period_count); + ctx->cl_dev.ops.cl_setup_controller(ctx, &ctx->cl_dev.dmab_bdl, + ctx->cl_dev.bufsize, ctx->cl_dev.period_count); + + return ret; +} +EXPORT_SYMBOL_GPL(ssth_cl_dma_prepare);
On Fri, Apr 17, 2015 at 06:46:04PM +0530, Vinod Koul wrote:
+void ssth_cldma_int_enable(struct ssth_lib *ctx) +{
- ssth_updatel_bits(ctx, HDA_ADSP_REG_ADSPIC,
ADSPIC_CL_DMA, 0x2);
+} +void ssth_cldma_int_disable(struct ssth_lib *ctx) +{
- ssth_updatel_bits(ctx, HDA_ADSP_REG_ADSPIC,
ADSPIC_CL_DMA, 0);
+}
Blank lines between functions. Seems to be an Intel coding style thing? :P
+/* Code loader helper APIs */ +static void ssth_skl_cl_setup_bdle(struct snd_dma_buffer *dmab_data,
u32 **bdlp, u32 count)
+{
- u32 *bdl = *bdlp;
- int i = 0;
- for (i = 0; i < count; i++) {
phys_addr_t addr = virt_to_phys(dmab_data->area + i * PAGE_SIZE);
So this we index by i and...
bdl[0] = cpu_to_le32(lower_32_bits(addr));
bdl[1] = cpu_to_le32(upper_32_bits(addr));
bdl[2] = cpu_to_le32(PAGE_SIZE);
bdl[3] = 0;
bdl += 4;
- }
...this we index by stepping through the array with increments in the body of the loop. Consistency would be nice (and more obviously correct).
+static void ssth_skl_cl_cleaup(struct ssth_lib *ctx) +{
Can't we clean it up instead?
- if (ctx->cl_dev.bytes_left <= ctx->cl_dev.bufsize &&
ctx->cl_dev.bytes_left > ctx->cl_dev.period_size) {
dev_dbg(ctx->dev, "%s: size less than buffer size: %u\n",
__func__, ctx->cl_dev.bytes_left);
ssth_cldma_int_disable(ctx);
ctx->cl_dev.curr_spib_pos = ctx->cl_dev.bytes_left;
ssth_cl_dma_fill_buffer(ctx, size, false, false, 0, false, true);
do {
mdelay(5);
link_pos = ssth_readl(ctx, CL_SD_LPIB);
} while (link_pos < size);
Should time out in case the DMA got stuck somehow.
goto cleanup;
- }
What if the buffer is just too big? Looks like this would loop for ever.
On Fri, Apr 24, 2015 at 06:18:38PM +0100, Mark Brown wrote:
On Fri, Apr 17, 2015 at 06:46:04PM +0530, Vinod Koul wrote:
+void ssth_cldma_int_enable(struct ssth_lib *ctx) +{
- ssth_updatel_bits(ctx, HDA_ADSP_REG_ADSPIC,
ADSPIC_CL_DMA, 0x2);
+} +void ssth_cldma_int_disable(struct ssth_lib *ctx) +{
- ssth_updatel_bits(ctx, HDA_ADSP_REG_ADSPIC,
ADSPIC_CL_DMA, 0);
+}
Blank lines between functions. Seems to be an Intel coding style thing? :P
sure seems, will fix
+/* Code loader helper APIs */ +static void ssth_skl_cl_setup_bdle(struct snd_dma_buffer *dmab_data,
u32 **bdlp, u32 count)
+{
- u32 *bdl = *bdlp;
- int i = 0;
- for (i = 0; i < count; i++) {
phys_addr_t addr = virt_to_phys(dmab_data->area + i * PAGE_SIZE);
So this we index by i and...
bdl[0] = cpu_to_le32(lower_32_bits(addr));
bdl[1] = cpu_to_le32(upper_32_bits(addr));
bdl[2] = cpu_to_le32(PAGE_SIZE);
bdl[3] = 0;
bdl += 4;
- }
...this we index by stepping through the array with increments in the body of the loop. Consistency would be nice (and more obviously correct).
ok
+static void ssth_skl_cl_cleaup(struct ssth_lib *ctx) +{
Can't we clean it up instead?
yes should have fixed earlier
- if (ctx->cl_dev.bytes_left <= ctx->cl_dev.bufsize &&
ctx->cl_dev.bytes_left > ctx->cl_dev.period_size) {
dev_dbg(ctx->dev, "%s: size less than buffer size: %u\n",
__func__, ctx->cl_dev.bytes_left);
ssth_cldma_int_disable(ctx);
ctx->cl_dev.curr_spib_pos = ctx->cl_dev.bytes_left;
ssth_cl_dma_fill_buffer(ctx, size, false, false, 0, false, true);
do {
mdelay(5);
link_pos = ssth_readl(ctx, CL_SD_LPIB);
} while (link_pos < size);
Should time out in case the DMA got stuck somehow.
yes will add
goto cleanup;
- }
What if the buffer is just too big? Looks like this would loop for ever.
DMA is started, so link_pos get updated and we keep reading it. Since its DMA a big buffer will get done fairly soon.
Thanks
On Sun, Apr 26, 2015 at 07:58:22PM +0530, Vinod Koul wrote:
On Fri, Apr 24, 2015 at 06:18:38PM +0100, Mark Brown wrote:
goto cleanup;
- }
What if the buffer is just too big? Looks like this would loop for ever.
DMA is started, so link_pos get updated and we keep reading it. Since its DMA a big buffer will get done fairly soon.
No, two separate questions there - what if the buffer is bigger than the maximum size (it looked like there was one) and what about error handling (if some clock gets stopped and we don't transfer data for example)?
On Mon, Apr 27, 2015 at 03:17:17PM +0100, Mark Brown wrote:
On Sun, Apr 26, 2015 at 07:58:22PM +0530, Vinod Koul wrote:
On Fri, Apr 24, 2015 at 06:18:38PM +0100, Mark Brown wrote:
goto cleanup;
- }
What if the buffer is just too big? Looks like this would loop for ever.
DMA is started, so link_pos get updated and we keep reading it. Since its DMA a big buffer will get done fairly soon.
No, two separate questions there - what if the buffer is bigger than the maximum size (it looked like there was one) and what about error handling (if some clock gets stopped and we don't transfer data for example)?
Okay got it. So for former, yes code doesn't handle it, we should split it up in that case or error out, will add that.
On latter one, the error handling in general would be beefed up but specifically since this is memory copy I am not expecting timeout. If clock is gone then whole audio cluster is gone bonkers so even simple read registers will fail.
Thanks
From: "Subhransu S. Prusty" subhransu.s.prusty@intel.com
Adding functionality to parse and load fw and DSP power management in SKL.
Signed-off-by: Subhransu S. Prusty subhransu.s.prusty@intel.com Signed-off-by: Vinod Koul vinod.koul@intel.com --- include/sound/soc-hda-sst-dsp.h | 39 ++++ sound/soc/hda/intel/Makefile | 3 +- sound/soc/hda/intel/soc-hda-sst-skl-fw.h | 163 +++++++++++++++++ sound/soc/hda/intel/soc-hda-sst-skl.c | 292 ++++++++++++++++++++++++++++++ 4 files changed, 496 insertions(+), 1 deletion(-) create mode 100644 sound/soc/hda/intel/soc-hda-sst-skl-fw.h create mode 100644 sound/soc/hda/intel/soc-hda-sst-skl.c
diff --git a/include/sound/soc-hda-sst-dsp.h b/include/sound/soc-hda-sst-dsp.h index 82ba1c9a68c4..4549d6df2ed2 100644 --- a/include/sound/soc-hda-sst-dsp.h +++ b/include/sound/soc-hda-sst-dsp.h @@ -99,6 +99,27 @@
#define WINDOW1_SIZE 0x1000
+#define FW_STATUS_MASK 0xf + +#define FW_ROM_INIT 0x0 +#define FW_ROM_INIT_DONE 0x1 +#define FW_ROM_MEM_PWR_DONE 0x2 +#define FW_ROM_MANIFEST_LOADED 0X3 +#define FW_ROM_MANIFEST_VERIFIED 0x4 +#define FW_ROM_FEAT_MASK_VERIFIED 0x5 +#define FW_ROM_BASEFW_FOUND 0x6 +#define FW_ROM_BASEFW_BA_VALID 0x7 +#define FW_ROM_BASEFW_TOTAL_PWR 0x8 +#define FW_ROM_BASEFW_TEXT_LOADED 0x9 +#define FW_ROM_BASEFW_TEXT_HASHED 0xa +#define FW_ROM_BASEFW_RODA_LOADED 0xb +#define FW_ROM_BASEFW_LOAD_HASHED 0xc +#define FW_ROM_BASEFW_HASH_VER 0xd +#define FW_ROM_BASEFW_START_FOUND 0xe +#define FW_ROM_BASEFW_ENTERED 0xf + + + #define ADSPIC_IPC 1 #define ADSPIS_IPC 1
@@ -160,6 +181,14 @@ struct ssth_dsp_loader_ops { struct snd_dma_buffer *dmab); };
+struct ssth_ops { + int (*load_fw)(struct ssth_lib *ctx); + /* FW module parser/loader */ + int (*parse_fw)(struct ssth_lib *ctx); + int (*set_state_D0)(struct ssth_lib *ctx); + int (*set_state_D3)(struct ssth_lib *ctx); +}; + struct ssth_lib { struct device *dev; struct ssth_ipc *ipc; @@ -167,6 +196,7 @@ struct ssth_lib { struct ssth_window window; int irq; struct ssth_dsp_loader_ops dsp_ops; + struct ssth_ops ops; struct snd_dma_buffer dsp_fw_buf; int sst_state; struct mutex sst_lock; @@ -223,4 +253,13 @@ void ssth_writel_traced( u32 offset, u32 val);
+int ssth_register_poll(struct ssth_lib *ctx, u32 offset, u32 mask, + u32 expected_value, u32 timeout, char *operation); +int ssth_boot_dsp(struct ssth_lib *ctx); +int ssth_dsp_init(struct ssth_lib *ctx); +int ssth_disable_dsp_core(struct ssth_lib *ctx); +bool ssth_dsp_is_running(struct ssth_lib *ctx); +int ssth_cl_dma_prepare(struct ssth_lib *ctx); +void ssth_process_cl_dma(struct work_struct *work); + #endif /*__HDA_SST_DSP_H__*/ diff --git a/sound/soc/hda/intel/Makefile b/sound/soc/hda/intel/Makefile index a31db94b2dde..d37275f13aac 100644 --- a/sound/soc/hda/intel/Makefile +++ b/sound/soc/hda/intel/Makefile @@ -1,5 +1,6 @@
-snd-soc-hda-sst-dsp-objs := soc-hda-sst-ipc.o soc-hda-sst-dsp.o soc-hda-sst-cldma.o +snd-soc-hda-sst-dsp-objs := soc-hda-sst-ipc.o soc-hda-sst-dsp.o soc-hda-sst-cldma.o \ + soc-hda-sst-skl.o
# SST DSP Library obj-$(CONFIG_SND_SOC_HDA_SST_DSP) += snd-soc-hda-sst-dsp.o diff --git a/sound/soc/hda/intel/soc-hda-sst-skl-fw.h b/sound/soc/hda/intel/soc-hda-sst-skl-fw.h new file mode 100644 index 000000000000..b5fd94479e2e --- /dev/null +++ b/sound/soc/hda/intel/soc-hda-sst-skl-fw.h @@ -0,0 +1,163 @@ +/* + * Intel SST Firmware Loader + * + * Copyright (C) 2013-15, 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_FIRMWARE_SKYLAKE_H_ +#define SST_FIRMWARE_SKYLAKE_H_ + +#include <linux/types.h> +#include <linux/bug.h> + +#define RSA_KEY_DEFAULT_LENGTH 256 +#define RSA_EXPONENT_DEFAULT_LENGTH 4 + +#define SHA256_HASH_DEFAULT_LENGTH 32 + +#define MODULE_SIGNATURE_DEFAULT_SIZE 256 +#define MODULE_NAME_DEFAULT_LENGTH 8 +#define MODULE_UUID_DEFAULT_LENGTH 4 + +struct ssth_segment_flags { + uint32_t contents:1; + uint32_t alloc:1; + uint32_t load:1; + uint32_t readonly:1; + uint32_t code:1; + uint32_t data:1; + uint32_t rsvd0:2; + uint32_t type:4; + uint32_t rsvd1:4; + uint32_t length:16; +} __packed; + +enum ssth_segment_type { + ST_TEXT, + ST_RODATA, + ST_BSS +}; + +struct ssth_segment_desc { + struct ssth_segment_flags flags; + uint32_t v_base_addr; + uint32_t file_offset; +} __packed; + +struct ssth_module_cfg { + uint32_t par[4]; + uint32_t ip_pages; + uint32_t cps; + uint32_t ibs; + uint32_t obs; + uint32_t module_flags; + uint32_t cpc; + uint32_t obls; +} __packed; + +enum ssth_module_type { + MT_BUILTIN, + MT_LOADABLE +}; + +struct ssth_module_entry { + uint32_t struct_id; + uint8_t name[MODULE_NAME_DEFAULT_LENGTH]; + uint32_t uuid[MODULE_UUID_DEFAULT_LENGTH]; + enum ssth_module_type type; + uint8_t hash[SHA256_HASH_DEFAULT_LENGTH]; + uint32_t entry_point; + uint16_t cfg_offset; + uint16_t cfg_count; + uint32_t affinity_mask; + uint16_t instance_max_count; + uint16_t instance_bss_size; + struct ssth_segment_desc segments[3]; +} __packed; + +struct ssth_adsp_fw_binary_header { + uint32_t header_id; + uint32_t header_len; + uint8_t name[MODULE_NAME_DEFAULT_LENGTH]; + uint32_t preload_page_count; + uint32_t fw_image_size; + uint32_t feature_mask; + uint16_t major_version; + uint16_t minor_version; + uint16_t hotfix_version; + uint16_t build_version; + uint32_t num_module_entries; + uint32_t hw_buf_base_addr; + uint32_t hw_buf_length; +} __packed; + +struct ssth_adsp_fw_binary_desc { + struct ssth_adsp_fw_binary_header header; + struct ssth_module_entry module_entries[1]; + /* address of module_cfg depend on + * sizeof(struct ssth_module_entry) * header.num_module_entries + */ + struct ssth_module_cfg module_cfg[1]; +} __packed; + +struct ssth_css_module_id { + uint32_t _res_ls_bits:31; + uint32_t debug_manifest:1; +} __packed; + +struct ssth_css_header { + uint32_t module_type; + uint32_t header_len; + uint32_t header_version; + struct ssth_css_module_id module_id; + uint32_t module_vendor; + uint32_t date; + uint32_t size; + uint32_t key_size; + uint32_t modules_size; + uint32_t exponent_size; + uint32_t reserved[22]; +} __packed; + +struct ssth_manifest_rsa_keys { + uint8_t modules[RSA_KEY_DEFAULT_LENGTH]; + uint8_t exponent[RSA_EXPONENT_DEFAULT_LENGTH]; +} __packed; + +struct ssth_manifest_crypto_block { + struct ssth_manifest_rsa_keys pub_key; + uint8_t signature[MODULE_SIGNATURE_DEFAULT_SIZE]; +} __packed; + +struct ssth_fw_image_manifest { + struct ssth_css_header header; + struct ssth_manifest_crypto_block crypto_block; + struct ssth_adsp_fw_binary_desc adsp_fw_bin_desc; +} __packed; + + +static inline void ssth_fw_structs_check_sizes(void) +{ + BUILD_BUG_ON(sizeof(struct ssth_segment_flags) != 4); + BUILD_BUG_ON(sizeof(struct ssth_segment_desc) != 12); + BUILD_BUG_ON(sizeof(struct ssth_module_cfg) != 44); + BUILD_BUG_ON(sizeof(struct ssth_module_entry) != 116); + BUILD_BUG_ON(sizeof(struct ssth_adsp_fw_binary_header) != 48); + BUILD_BUG_ON(sizeof(struct ssth_adsp_fw_binary_desc) != 208); + BUILD_BUG_ON(sizeof(struct ssth_css_module_id) != 4); + BUILD_BUG_ON(sizeof(struct ssth_css_header) != 128); + BUILD_BUG_ON(sizeof(struct ssth_manifest_rsa_keys) != 260); + BUILD_BUG_ON(sizeof(struct ssth_manifest_crypto_block) != 516); +} + +#endif /* HDA_SST_FIRMWARE_H_ */ diff --git a/sound/soc/hda/intel/soc-hda-sst-skl.c b/sound/soc/hda/intel/soc-hda-sst-skl.c new file mode 100644 index 000000000000..71add5891c93 --- /dev/null +++ b/sound/soc/hda/intel/soc-hda-sst-skl.c @@ -0,0 +1,292 @@ +/* + * soc_hda_sst-skl.c - HDA DSP library functions for SKL platform + * + * Copyright (C) 2014-15 Intel Corp + * 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 published by + * the Free Software Foundation; version 2 of the License. + * + * 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/slab.h> +#include <linux/delay.h> +#include <linux/firmware.h> +#include <linux/kernel.h> +#include <linux/device.h> +#include <sound/soc-hda-sst-ipc.h> +#include <sound/soc-hda-sst-dsp.h> +#include "soc-hda-sst-skl-fw.h" + +#define SKL_FW_ROM_BASEFW_ENTERED_TIMEOUT 300 +#define SKL_ROM_INIT_DONE_TIMEOUT 1000 +#define SKL_FWLOAD_DONE_TIMEOUT 10000 + +/* Intel HD Audio SRAM Windoww 0*/ +#define SKL_HDA_ADSP_SRAM0_BASE 0x8000 + +/* Firmware status window */ +#define SKL_HDA_ADSP_REG_FW_STATUS SKL_HDA_ADSP_SRAM0_BASE + +static int ssth_skl_load_base_firmware(struct ssth_lib *ctx); +static int ssth_skl_set_dsp_D0(struct ssth_lib *ctx); +static int ssth_skl_set_dsp_D3(struct ssth_lib *ctx); + +struct ssth_ops skl_ops = { + /*.parse_fw = ssth_skl_parse_fw_image,*/ + .set_state_D0 = ssth_skl_set_dsp_D0, + .set_state_D3 = ssth_skl_set_dsp_D3, + .load_fw = ssth_skl_load_base_firmware, +}; + +#define INSTANCE_ID 0 +#define BASE_FW_MODULE_ID 0 + +static int set_dsp_dx_state(struct ssth_ipc *ipc, struct ssth_dxstate_info *dx) +{ + struct ssth_large_config_msg msg; + + msg.module_id = BASE_FW_MODULE_ID; + msg.instance_id = INSTANCE_ID; + msg.large_param_id = 2; + msg.param_data_size = sizeof(*dx); + + return ssth_ipc_set_large_config(ipc, &msg, (u32 *)dx); +} + +static int ssth_skl_set_dsp_D0(struct ssth_lib *ctx) +{ + int ret; + + dev_dbg(ctx->dev, "In %s:\n", __func__); + + ret = ssth_skl_load_base_firmware(ctx); + if (ret < 0) { + dev_err(ctx->dev, "unable to load firmware\n"); + return ret; + } + + /* FIXME: wait for sometime so that FW boots. Remove this once FW_BOOT + * notification is implemented */ + msleep(200); + + ssth_dsp_set_state_locked(ctx, SST_DSP_RUNNING); + return ret; +} + +static int ssth_skl_set_dsp_D3(struct ssth_lib *ctx) +{ + int ret; + struct ssth_dxstate_info dx; + + dev_dbg(ctx->dev, "In %s:\n", __func__); + if (!ssth_dsp_is_running(ctx)) + return 0; + + dx.core_mask = DSP_CORE0_MASK; + dx.dx_mask = ADSP_IPC_D3_MASK; + ret = set_dsp_dx_state(ctx->ipc, &dx); + if (ret < 0) { + dev_err(ctx->dev, "Failed to set DSP to D3 state\n"); + return ret; + } + + ret = ssth_disable_dsp_core(ctx); + if (ret < 0) { + dev_err(ctx->dev, "disable dsp core failed ret: %d\n", ret); + ret = -EIO; + } + ssth_dsp_set_state_locked(ctx, SST_DSP_RESET); + return ret; +} + +static bool check_fw_status(struct ssth_lib *ctx, u32 status) +{ + u32 cur_sts; + + cur_sts = ssth_readl_alt(ctx, SKL_HDA_ADSP_REG_FW_STATUS) & FW_STATUS_MASK; + return (cur_sts == status) ? true : false; +} + +static int ssth_ctx_init(struct ssth_lib *ctx) +{ + /* TODO: Move common context initialization here */ + ctx->intr_wq = create_singlethread_workqueue("sst_dsp_interrupt_wq"); + if (!ctx->intr_wq) + return -EBUSY; + + INIT_WORK(&ctx->ipc_process_msg_work, ssth_ipc_process_msg); + INIT_WORK(&ctx->cl_dma_process_work, ssth_process_cl_dma); + init_waitqueue_head(&ctx->cl_dev.wait_queue); + + return 0; +} +int ssth_skl_init(struct device *dev, void __iomem *mmio_base, int irq, + struct ssth_dsp_loader_ops dsp_ops, struct ssth_lib **dsp) +{ + struct ssth_lib *ctx; + int ret = 0; + + ctx = devm_kzalloc(dev, sizeof(*ctx), GFP_KERNEL); + if (ctx == NULL) + return -ENOMEM; + ctx->mmio_base = mmio_base; + ctx->irq = irq; + ctx->dev = dev; + + dev_dbg(dev, "mmio_base: 0x%p\n", ctx->mmio_base); + ctx->dsp_ops = dsp_ops; + ctx->window.w0stat = mmio_base + SKL_HDA_ADSP_SRAM0_BASE; + ctx->window.w0up = mmio_base + SKL_HDA_ADSP_SRAM0_BASE + WINDOW0_STAT_SIZE; + ctx->window.w1 = mmio_base + HDA_ADSP_SRAM1_BASE; + ctx->window.w0stat_size = WINDOW0_STAT_SIZE; + ctx->window.w0up_size = WINDOW0_UP_SIZE; + ctx->window.w1_size = WINDOW1_SIZE; + + ctx->ops = skl_ops; + + ret = ssth_dsp_init(ctx); + if (ret < 0) + return ret; + + + ret = ssth_ctx_init(ctx); + if (ret < 0) + return ret; + + ctx->ipc->boot_complete = false; + ret = ssth_boot_dsp(ctx); + if (ret < 0) { + dev_err(ctx->dev, "Boot dsp core failed ret: %d", ret); + return ret; + } + + ssth_cl_dma_prepare(ctx); + + ret = ctx->ops.load_fw(ctx); + if (ret < 0) { + dev_err(dev, "Load base fw failed : %x", ret); + return ret; + } + + if (dsp) + *dsp = ctx; + + return 0; +} +EXPORT_SYMBOL_GPL(ssth_skl_init); + +void ssth_skl_cleanup(struct device *dev, struct ssth_lib *ctx) +{ + ctx->cl_dev.ops.cl_cleaup_controller(ctx); +} +EXPORT_SYMBOL_GPL(ssth_skl_cleanup); + +static int ssth_skl_transfer_firmware(struct ssth_lib *ctx, + const void *basefw, u32 base_fw_size) +{ + int ret = 0; + + if (!ctx->cl_dev.cl_ops) { + dev_err(ctx->dev, "No cl ops\n"); + return -EIO; + } + + ret = ctx->cl_dev.ops.cl_copy_to_bdl(ctx, basefw, base_fw_size); + + if (ret) + return ret; + + mdelay(1000); + + ret = ssth_register_poll(ctx, + SKL_HDA_ADSP_REG_FW_STATUS, + FW_STATUS_MASK, + FW_ROM_BASEFW_ENTERED, + SKL_FW_ROM_BASEFW_ENTERED_TIMEOUT, + "Firmware boot"); + + return 0; +} + +static int ssth_skl_load_base_firmware(struct ssth_lib *ctx) +{ + int ret = 0, i; + const struct firmware *fw = NULL; + u32 fw_preload_page_count = 0; + u32 base_fw_size = 0; + struct ssth_fw_image_manifest *manifest; + u32 reg; + + ret = request_firmware(&fw, "dsp_fw_release.bin", ctx->dev); + if (ret < 0) { + dev_err(ctx->dev, "Request firmware failed %d\n", ret); + ssth_disable_dsp_core(ctx); + return -EIO; + } + + /*enable Interrupt */ + ssth_ipc_int_enable(ctx); + ssth_ipc_op_int_enable(ctx); + + /*check ROM Status */ + for (i = SKL_ROM_INIT_DONE_TIMEOUT; i > 0; --i) { + if (check_fw_status(ctx, FW_ROM_INIT_DONE)) { + dev_dbg(ctx->dev, "ROM loaded, we can continue with FW loading\n"); + break; + } + mdelay(1); + } + if (!i) { + reg = ssth_readl_alt(ctx, SKL_HDA_ADSP_REG_FW_STATUS); + dev_err(ctx->dev, "Timeout waiting for ROM init done, reg:0x%x\n", reg); + ret = -EIO; + goto sst_load_base_firmware_failed; + } + + manifest = (struct ssth_fw_image_manifest *)fw->data; + fw_preload_page_count = + manifest->adsp_fw_bin_desc.header.preload_page_count; + base_fw_size = fw_preload_page_count * PAGE_SIZE; + + if (base_fw_size > fw->size) { + dev_err(ctx->dev, "Preloaded base fw size is bigger then whole fw image"); + ret = -EIO; + goto sst_load_base_firmware_failed; + } + + ret = ssth_skl_transfer_firmware(ctx, fw->data, + base_fw_size); + if (ret < 0) { + dev_err(ctx->dev, "Transfer firmware failed%d\n", ret); + goto sst_load_base_firmware_failed; + } else { + dev_dbg(ctx->dev, "Download firmware successful%d\n", ret); + /*FIXME - remove once firmware implementation is done + ret = wait_event_timeout(ctx->ipc->boot_wait, ctx->ipc->boot_complete, + msecs_to_jiffies(IPC_BOOT_MSECS)); + if (ret == 0) { + dev_err(ctx->dev, "DSP boot failed, FW Ready timed-out\n"); + ret = -EIO; + goto sst_load_base_firmware_failed; + } */ + ssth_dsp_set_state_locked(ctx, SST_DSP_RUNNING); + } + release_firmware(fw); + return 0; + +sst_load_base_firmware_failed: + ssth_disable_dsp_core(ctx); + release_firmware(fw); + return ret; +}
From: "Subhransu S. Prusty" subhransu.s.prusty@intel.com
DSP handles the Code Loader DMA interrupt.
Signed-off-by: Subhransu S. Prusty subhransu.s.prusty@intel.com Signed-off-by: Vinod Koul vinod.koul@intel.com --- include/sound/soc-hda-sst-dsp.h | 1 + sound/soc/hda/intel/soc-hda-sst-dsp.c | 7 +++++++ 2 files changed, 8 insertions(+)
diff --git a/include/sound/soc-hda-sst-dsp.h b/include/sound/soc-hda-sst-dsp.h index 4549d6df2ed2..9adeb49cf126 100644 --- a/include/sound/soc-hda-sst-dsp.h +++ b/include/sound/soc-hda-sst-dsp.h @@ -261,5 +261,6 @@ int ssth_disable_dsp_core(struct ssth_lib *ctx); bool ssth_dsp_is_running(struct ssth_lib *ctx); int ssth_cl_dma_prepare(struct ssth_lib *ctx); void ssth_process_cl_dma(struct work_struct *work); +void ssth_cldma_int_disable(struct ssth_lib *ctx);
#endif /*__HDA_SST_DSP_H__*/ diff --git a/sound/soc/hda/intel/soc-hda-sst-dsp.c b/sound/soc/hda/intel/soc-hda-sst-dsp.c index 6285a6772e73..b133c63a0c20 100644 --- a/sound/soc/hda/intel/soc-hda-sst-dsp.c +++ b/sound/soc/hda/intel/soc-hda-sst-dsp.c @@ -423,6 +423,13 @@ static irqreturn_t ssth_interrupt(int irq, void *dev_id) result = IRQ_HANDLED; }
+ if (val & ADSPIS_CL_DMA) { + ssth_cldma_int_disable(ctx); + queue_work(ctx->intr_wq, &ctx->cl_dma_process_work); + result = IRQ_HANDLED; + } + + spin_unlock(&ctx->reg_lock); return result; }
On Fri, Apr 17, 2015 at 06:46:06PM +0530, Vinod Koul wrote:
From: "Subhransu S. Prusty" subhransu.s.prusty@intel.com
DSP handles the Code Loader DMA interrupt.
Could this be squashed in with the thing that adds the work so we see the whole picture at once please?
On Fri, Apr 24, 2015 at 06:30:11PM +0100, Mark Brown wrote:
On Fri, Apr 17, 2015 at 06:46:06PM +0530, Vinod Koul wrote:
From: "Subhransu S. Prusty" subhransu.s.prusty@intel.com
DSP handles the Code Loader DMA interrupt.
Could this be squashed in with the thing that adds the work so we see the whole picture at once please?
Sure...
Thanks
From: "Subhransu S. Prusty" subhransu.s.prusty@intel.com
Signed-off-by: Subhransu S. Prusty subhransu.s.prusty@intel.com Signed-off-by: Vinod Koul vinod.koul@intel.com --- sound/soc/hda/intel/soc-hda-sst-dsp.c | 14 ++++++++++++++ 1 file changed, 14 insertions(+)
diff --git a/sound/soc/hda/intel/soc-hda-sst-dsp.c b/sound/soc/hda/intel/soc-hda-sst-dsp.c index b133c63a0c20..4d4d01b496b3 100644 --- a/sound/soc/hda/intel/soc-hda-sst-dsp.c +++ b/sound/soc/hda/intel/soc-hda-sst-dsp.c @@ -480,6 +480,20 @@ int ssth_dsp_free0(struct ssth_lib *dsp) } EXPORT_SYMBOL_GPL(ssth_dsp_free0);
+int ssth_dsp_set_power_state(struct ssth_lib *ctx, int state) +{ + int ret = 0; + + if (state == SST_DSP_POWER_D0) + ret = ctx->ops.set_state_D0(ctx); + else if (state == SST_DSP_POWER_D3) + ret = ctx->ops.set_state_D3(ctx); + else + dev_err(ctx->dev, "Power State=%x not supported", state); + return ret; +} +EXPORT_SYMBOL_GPL(ssth_dsp_set_power_state); + bool ssth_dsp_is_running(struct ssth_lib *ctx) { bool ret = 0;
On Fri, Apr 17, 2015 at 06:46:07PM +0530, Vinod Koul wrote:
- if (state == SST_DSP_POWER_D0)
ret = ctx->ops.set_state_D0(ctx);
- else if (state == SST_DSP_POWER_D3)
ret = ctx->ops.set_state_D3(ctx);
- else
dev_err(ctx->dev, "Power State=%x not supported", state);
Can we have a switch statement please? I'm a bit unclear why this is being exported as it is, shouldn't things be wrapped up more so that the DSP code owns its own power state.
On Mon, Apr 20, 2015 at 10:44:41PM +0100, Mark Brown wrote:
On Fri, Apr 17, 2015 at 06:45:58PM +0530, Vinod Koul wrote:
This series adds support for managing DSP and posting IPC and processing IPC replies.
Any dependencies on the generic HDA stuff you've been posting?
Yes.
So first we need to merge the SKL PCM driver series. Can you please review that [1] one as well. Then this series. Lastly the SKL aDSP support series [2] which uses the IPC code in this series.
One more would be when toplogy is merged, for supporting the dsp toplogy
Thanks
On Wed, 2015-04-22 at 09:30 +0530, Vinod Koul wrote:
On Mon, Apr 20, 2015 at 10:44:41PM +0100, Mark Brown wrote:
On Fri, Apr 17, 2015 at 06:45:58PM +0530, Vinod Koul wrote:
This series adds support for managing DSP and posting IPC and processing IPC replies.
Any dependencies on the generic HDA stuff you've been posting?
Yes.
So first we need to merge the SKL PCM driver series. Can you please review that [1] one as well. Then this series. Lastly the SKL aDSP support series [2] which uses the IPC code in this series.
One more would be when toplogy is merged, for supporting the dsp toplogy
We also need to port the SKL IPC code to use the converged mailbox code that's now used by BDW and BYT before upstreaming.
Liam
On Wed, Apr 22, 2015 at 12:19:58PM +0100, Liam Girdwood wrote:
On Wed, 2015-04-22 at 09:30 +0530, Vinod Koul wrote:
On Mon, Apr 20, 2015 at 10:44:41PM +0100, Mark Brown wrote:
On Fri, Apr 17, 2015 at 06:45:58PM +0530, Vinod Koul wrote:
This series adds support for managing DSP and posting IPC and processing IPC replies.
Any dependencies on the generic HDA stuff you've been posting?
Yes.
So first we need to merge the SKL PCM driver series. Can you please review that [1] one as well. Then this series. Lastly the SKL aDSP support series [2] which uses the IPC code in this series.
One more would be when toplogy is merged, for supporting the dsp toplogy
We also need to port the SKL IPC code to use the converged mailbox code that's now used by BDW and BYT before upstreaming.
Yes that wil be done for next patch series. If you guys could give comments on the other tow series, I will spin them and resubmit as well
On Wed, Apr 22, 2015 at 09:23:08PM +0530, Vinod Koul wrote:
On Wed, Apr 22, 2015 at 12:19:58PM +0100, Liam Girdwood wrote:
We also need to port the SKL IPC code to use the converged mailbox code that's now used by BDW and BYT before upstreaming.
Yes that wil be done for next patch series. If you guys could give comments on the other tow series, I will spin them and resubmit as well
I'm not entirely sure which serieses are what at the minute, we're getting a lot of things posted from you guys many of which are just RFCs in various versions and some of which are getting applied or didn't seem relevant (like the HDA ones).
participants (3)
-
Liam Girdwood
-
Mark Brown
-
Vinod Koul