diff mbox

[RFC,2/9] ASoC: hda: Add IPC library for SKL platform

Message ID 1429276567-29007-3-git-send-email-vinod.koul@intel.com (mailing list archive)
State New, archived
Headers show

Commit Message

Vinod Koul April 17, 2015, 1:16 p.m. UTC
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

Comments

Mark Brown April 20, 2015, 9:56 p.m. UTC | #1
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.
Vinod Koul April 22, 2015, 3:54 a.m. UTC | #2
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
Mark Brown April 24, 2015, 5:34 p.m. UTC | #3
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.
Vinod Koul April 26, 2015, 2:36 p.m. UTC | #4
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 :)
diff mbox

Patch

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);