ASoC: sof - start

Signed-off-by: Liam Girdwood <liam.r.girdwood@linux.intel.com>
diff --git a/sound/soc/sof/Kconfig b/sound/soc/sof/Kconfig
new file mode 100644
index 0000000..da69d81
--- /dev/null
+++ b/sound/soc/sof/Kconfig
@@ -0,0 +1,9 @@
+config SND_SOC_SOF
+	tristate "Sound Open Firmware Support"
+	default m
+	help
+          This adds support for SOF
+          Say Y if you have such a device.
+          If unsure select "N".
+
+
diff --git a/sound/soc/sof/Makefile b/sound/soc/sof/Makefile
new file mode 100644
index 0000000..0aed39d
--- /dev/null
+++ b/sound/soc/sof/Makefile
@@ -0,0 +1,6 @@
+snd-soc-sof-objs := \
+	        loader.o ipc.o pcm.o
+snd-soc-sof-haswell-objs := \
+		hw-byt.o hw-hsw.o
+
+obj-$(CONFIG_SND_SOC_SOF) += snd-soc-sof.o snd-soc-sof-haswell.o
diff --git a/sound/soc/sof/control.c b/sound/soc/sof/control.c
new file mode 100644
index 0000000..f10f68d
--- /dev/null
+++ b/sound/soc/sof/control.c
@@ -0,0 +1,67 @@
+/* Mixer Controls */
+int sst_hsw_stream_get_volume(struct sst_hsw *hsw, struct sst_hsw_stream *stream,
+	u32 stage_id, u32 channel, u32 *volume)
+{
+	if (channel > 1)
+		return -EINVAL;
+
+	sst_dsp_read(hsw->dsp, volume,
+		stream->reply.volume_register_address[channel],
+		sizeof(*volume));
+
+	return 0;
+}
+
+/* stream volume */
+int sst_hsw_stream_set_volume(struct sst_hsw *hsw,
+	struct sst_hsw_stream *stream, u32 stage_id, u32 channel, u32 volume)
+{
+	struct sst_hsw_ipc_volume_req *req;
+	u32 header;
+	int ret;
+
+	trace_ipc_request("set stream volume", stream->reply.stream_hw_id);
+
+	if (channel >= 2 && channel != SST_HSW_CHANNELS_ALL)
+		return -EINVAL;
+
+	header = IPC_GLB_TYPE(IPC_GLB_STREAM_MESSAGE) |
+		IPC_STR_TYPE(IPC_STR_STAGE_MESSAGE);
+	header |= (stream->reply.stream_hw_id << IPC_STR_ID_SHIFT);
+	header |= (IPC_STG_SET_VOLUME << IPC_STG_TYPE_SHIFT);
+	header |= (stage_id << IPC_STG_ID_SHIFT);
+
+	req = &stream->vol_req;
+	req->target_volume = volume;
+
+	/* set both at same time ? */
+	if (channel == SST_HSW_CHANNELS_ALL) {
+		if (hsw->mute[0] && hsw->mute[1]) {
+			hsw->mute_volume[0] = hsw->mute_volume[1] = volume;
+			return 0;
+		} else if (hsw->mute[0])
+			req->channel = 1;
+		else if (hsw->mute[1])
+			req->channel = 0;
+		else
+			req->channel = SST_HSW_CHANNELS_ALL;
+	} else {
+		/* set only 1 channel */
+		if (hsw->mute[channel]) {
+			hsw->mute_volume[channel] = volume;
+			return 0;
+		}
+		req->channel = channel;
+	}
+
+	ret = sst_ipc_tx_message_wait(&hsw->ipc, header, req,
+		sizeof(*req), NULL, 0);
+	if (ret < 0) {
+		dev_err(hsw->dev, "error: set stream volume failed\n");
+		return ret;
+	}
+
+	return 0;
+}
+
+
diff --git a/sound/soc/sof/debug.c b/sound/soc/sof/debug.c
new file mode 100644
index 0000000..ad39832
--- /dev/null
+++ b/sound/soc/sof/debug.c
@@ -0,0 +1,151 @@
+struct sst_dfsentry {
+	struct dentry *dfsentry;
+	size_t size;
+	void *buf;
+	struct sst_dsp *sst;
+};
+
+static int sst_dfsentry_open(struct inode *inode, struct file *file)
+{
+	file->private_data = inode->i_private;
+
+	return 0;
+}
+
+static ssize_t sst_dfsentry_read(struct file *file, char __user *buffer,
+				 size_t count, loff_t *ppos)
+{
+	struct sst_dfsentry *dfse = file->private_data;
+	int size;
+	u32 *buf;
+	loff_t pos = *ppos;
+	size_t ret;
+
+	//dev_dbg(dfse->sst->dev, "pbuf: %p, *ppos: 0x%llx\n", buffer, *ppos);
+
+	size = dfse->size;
+
+	if (pos < 0)
+		return -EINVAL;
+	if (pos >= size || !count)
+		return 0;
+	if (count > size - pos)
+		count = size - pos;
+
+	size = (count + 3) & (~3);
+	buf = kzalloc(size, GFP_KERNEL);
+	if (!buf)
+		return -ENOMEM;
+
+	pm_runtime_get(dfse->sst->dev);
+	sst_memcpy_fromio_32(dfse->sst, buf, dfse->buf + pos, size);
+	pm_runtime_put(dfse->sst->dev);
+
+	ret = copy_to_user(buffer, buf, count);
+	kfree(buf);
+
+	if (ret == count)
+		return -EFAULT;
+	count -= ret;
+	*ppos = pos + count;
+
+	//dev_dbg(dfse->sst->dev, "*ppos: 0x%llx, count: %zu\n", *ppos, count);
+
+	return count;
+}
+
+static const struct file_operations sst_dfs_fops = {
+	.open = sst_dfsentry_open,
+	.read = sst_dfsentry_read,
+	.llseek = default_llseek,
+};
+
+
+
+static int hsw_debugfs_entry_create(struct sst_dsp *sst, void __iomem *base,
+	size_t size, const char *name)
+{
+	struct sst_dfsentry *dfse;
+
+	if (!sst)
+		return -EINVAL;
+
+	dfse = kzalloc(sizeof(*dfse), GFP_KERNEL);
+
+	if (!dfse)
+		return -ENOMEM;
+	dfse->buf = base;
+	dfse->size = size;//mbox size
+
+	dfse->dfsentry = debugfs_create_file(name, 0444, sst->debugfs_root,
+					     dfse, &sst_dfs_fops);
+	if (!dfse->dfsentry) {
+		dev_err(sst->dev, "cannot create debugfs entry.\n");
+		kfree(dfse);
+		return -ENODEV;
+	}
+
+	dfse->sst = sst;
+
+	return 0;
+}
+
+struct sst_debugfs_map {
+	const char *name;
+	u32 offset;
+	u32 size;
+};
+
+static int hsw_debugfs_init(struct sst_hsw *hsw)
+{
+	struct sst_dsp *sst = hsw->dsp;
+	struct sst_pdata *pdata = sst->pdata;
+	int err = 0, i;
+	struct dentry *d;
+
+	for (i = 0; i < ARRAY_SIZE(debugfs_byt); i++) {
+		err = hsw_debugfs_entry_create(sst,
+			(void __iomem *)((char *)sst->addr.lpe + debugfs_byt[i].offset),
+			debugfs_byt[i].size, debugfs_byt[i].name);
+		if (err < 0)
+			dev_err(sst->dev, "cannot create debugfs for %s\n", debugfs_byt[i].name);
+	}
+
+	INIT_LIST_HEAD(&hsw->host_buffer.elem_list);
+	/* 1. alloc host buffer */
+	err = snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, pdata->dma_dev,
+				  PAGE_SIZE, &hsw->trace_dma_descriptor);
+	if (err < 0)
+		return err;
+
+	err = snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV_SG, pdata->dma_dev,
+				  PAGE_SIZE * DMA_TRACE_PAGE_NUMBER,
+				  &hsw->dtrace_buffer);
+	if (err < 0) {
+		snd_dma_free_pages(&hsw->trace_dma_descriptor);
+		return err;
+	}
+
+	err = hsw_setup_dma_trace_page_table(hsw);
+	if (err < 0) {
+		snd_dma_free_pages(&hsw->trace_dma_descriptor);
+		snd_dma_free_pages(&hsw->dtrace_buffer);
+		return err;
+	}
+
+	hsw->dt_enable_request.ringinfo.ring_pt_address =
+		hsw->trace_dma_descriptor.addr;
+	hsw->dt_enable_request.ringinfo.num_pages = DMA_TRACE_PAGE_NUMBER;
+	hsw->dt_enable_request.ringinfo.ring_offset = 0;
+
+	/* 2. init debugfs */
+	d = debugfs_create_file("dma_trace", 0444, sst->debugfs_root,
+					hsw, &sst_dma_trace_fops);
+	if (!d) {
+		snd_dma_free_pages(&hsw->trace_dma_descriptor);
+		snd_dma_free_pages(&hsw->dtrace_buffer);
+		return -ENODEV;
+	}
+
+	return err;
+}
diff --git a/sound/soc/sof/hw-byt.c b/sound/soc/sof/hw-byt.c
new file mode 100644
index 0000000..ec6cf9e
--- /dev/null
+++ b/sound/soc/sof/hw-byt.c
@@ -0,0 +1,209 @@
+/*
+ * Intel Baytrail SST DSP driver
+ * Copyright (c) 2014, Intel Corporation.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope 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/delay.h>
+#include <linux/fs.h>
+#include <linux/slab.h>
+#include <linux/device.h>
+#include <linux/interrupt.h>
+#include <linux/module.h>
+#include <linux/dma-mapping.h>
+#include <linux/platform_device.h>
+#include <linux/firmware.h>
+
+#include "../intel/common/sst-dsp.h"
+#include "../intel/common/sst-dsp-priv.h"
+#include "../haswell/sst-haswell-ipc.h"
+
+#include <trace/events/hswadsp.h>
+
+#define SST_BYT_IRAM_OFFSET	0xC0000
+#define SST_BYT_DRAM_OFFSET	0x100000
+#define SST_BYT_SHIM_OFFSET	0x140000
+
+#define SST_BYT_SHIM_OFFSET	0x140000
+#define SST_BYT_DSP_DRAM_OFFSET	0x28000
+#define SST_BYT_DSP_IRAM_OFFSET	0x00000
+
+#define SST_SHIM_PM_REG		0x84
+
+static irqreturn_t sst_byt_irq(int irq, void *context)
+{
+	struct sst_dsp *sst = (struct sst_dsp *) context;
+	u64 isr;
+	int ret = IRQ_NONE;
+
+	spin_lock(&sst->spinlock);
+
+	/* Interrupt arrived, check src */
+	isr = sst_dsp_shim_read64_unlocked(sst, SST_ISRX);
+	if (isr & SST_ISRX_DONE) {
+		//trace_sst_irq_done(isr,
+		//	sst_dsp_shim_read_unlocked(sst, SST_IMRX));
+
+		/* Mask Done interrupt before return */
+		sst_dsp_shim_update_bits64_unlocked(sst, SST_IMRX,
+			SST_IMRX_DONE, SST_IMRX_DONE);
+		ret = IRQ_WAKE_THREAD;
+	}
+
+	if (isr & SST_ISRX_BUSY) {
+		//trace_sst_irq_busy(isr,
+		//	sst_dsp_shim_read_unlocked(sst, SST_IMRX));
+
+		/* Mask Busy interrupt before return */
+		sst_dsp_shim_update_bits64_unlocked(sst, SST_IMRX,
+			SST_IMRX_BUSY, SST_IMRX_BUSY);
+		ret = IRQ_WAKE_THREAD;
+	}
+
+	spin_unlock(&sst->spinlock);
+	return ret;
+}
+
+static void byt_notify(struct sst_dsp *dsp)
+{
+	sst_dsp_shim_update_bits64(dsp, SST_IPCD,
+		SST_BYT_IPCD_BUSY | SST_BYT_IPCD_DONE,
+		SST_BYT_IPCD_DONE);
+}
+
+static bool byt_is_dsp_busy(struct sst_dsp *dsp)
+{
+	u64 ipcx;
+
+	ipcx = sst_dsp_shim_read64_unlocked(dsp, SST_IPCX);
+	return (ipcx & (SST_BYT_IPCX_BUSY | SST_BYT_IPCX_DONE));
+}
+
+static void byt_shim_dbg(struct sst_generic_ipc *ipc, const char *text)
+{
+	struct sst_dsp *sst = ipc->dsp;
+	u64 ipcd, ipcx ,isrd, imrx, isrx, imrd;
+	int i;
+
+	ipcx = sst_dsp_shim_read64_unlocked(sst, SST_IPCX);
+	ipcd = sst_dsp_shim_read64_unlocked(sst, SST_IPCD);
+	isrx = sst_dsp_shim_read64_unlocked(sst, SST_ISRX);
+	isrd = sst_dsp_shim_read64_unlocked(sst, SST_ISRD);
+	imrx = sst_dsp_shim_read64_unlocked(sst, SST_IMRX);
+	imrd = sst_dsp_shim_read64_unlocked(sst, SST_IMRD);
+
+	dev_err(ipc->dev,
+		"ipc: --%s--\n ipcx 0x%16llx\n ipcd 0x%16llx\n"
+		" imrx 0x%16llx\n imrd 0x%16llx\n"
+		" isrx 0x%16llx\n isrd 0x%16llx\n",
+		text, ipcx, ipcd, imrx, imrd, isrx, isrd);
+
+	for (i = 0; i < 0xff; i+=8 ) {
+		dev_err(ipc->dev, "shim 0x%2.2x value 0x%16.16llx\n",i, 
+			sst_dsp_shim_read64_unlocked(sst, i));
+	}
+
+	for (i = 10; i < 30; i++) {
+		dev_err(sst->dev, "mbox: %d value 0x%8.8x\n", i,
+			readl(sst->addr.lpe + i * 4 + 0x144000 + 0x900));
+	}
+}
+
+static void byt_tx_msg(struct sst_generic_ipc *ipc, struct ipc_message *msg)
+{
+	/* send the message */
+	sst_dsp_outbox_write(ipc->dsp, msg->tx_data, msg->tx_size);
+	sst_dsp_ipc_msg64_tx(ipc->dsp, msg->header);
+}
+
+static irqreturn_t byt_irq_thread(int irq, void *context)
+{
+	struct sst_dsp *sst = (struct sst_dsp *) context;
+	struct sst_hsw *hsw = sst_dsp_get_thread_context(sst);
+	struct sst_generic_ipc *ipc = &hsw->ipc;
+	u64 ipcx, ipcd;
+	unsigned long flags;
+
+	spin_lock_irqsave(&sst->spinlock, flags);
+
+	ipcx = sst_dsp_ipc_msg64_rx(hsw->dsp);
+	ipcd = sst_dsp_shim_read64_unlocked(sst, SST_IPCD);
+
+	/* reply message from DSP */
+	if (ipcx & SST_BYT_IPCX_DONE) {
+
+		/* Handle Immediate reply from DSP Core */
+		hsw_process_reply(hsw, ipcx);
+
+		/* clear DONE bit - tell DSP we have completed */
+		sst_dsp_shim_update_bits64_unlocked(sst, SST_IPCX,
+			SST_BYT_IPCX_DONE, 0);
+
+		/* unmask Done interrupt */
+		sst_dsp_shim_update_bits64_unlocked(sst, SST_IMRX,
+			SST_IMRX_DONE, 0);
+	}
+
+	/* new message from DSP */
+	if (ipcd & SST_BYT_IPCD_BUSY) {
+
+		/* Handle Notification and Delayed reply from DSP Core */
+		hsw_process_notification(hsw, ipcd);
+
+		/* clear BUSY bit and set DONE bit - accept new messages */
+		sst_dsp_shim_update_bits64_unlocked(sst, SST_IPCD,
+			SST_BYT_IPCD_BUSY | SST_BYT_IPCD_DONE,
+			SST_BYT_IPCD_DONE);
+
+		/* unmask busy interrupt */
+		sst_dsp_shim_update_bits64_unlocked(sst, SST_IMRX,
+			SST_IMRX_BUSY, 0);
+	}
+
+	spin_unlock_irqrestore(&sst->spinlock, flags);
+
+	/* continue to send any remaining messages... */
+	queue_kthread_work(&ipc->kworker, &ipc->kwork);
+
+	return IRQ_HANDLED;
+}
+
+static const struct sst_debugfs_map debugfs_byt[] = {
+	{"dmac0", 0x98000, 0x420},
+	{"dmac1", 0x9c000, 0x420},
+	{"ssp0", 0xa0000, 0x100},
+	{"ssp1", 0xa1000, 0x100},
+	{"ssp2", 0xa2000, 0x100},
+	{"iram", 0xc0000, 80 * 1024},
+	{"dram", 0x100000, 160 * 1024},
+	{"shim", 0x140000, 0x100},
+	{"mbox", 0x144000, 0x1000},
+};
+
+static struct sst_dsp_device byt_dev = {
+	.thread = byt_irq_thread,
+	.ops = &sst_baytrail_ops,
+};
+
+struct sst_ops sst_baytrail_ops = {
+	.reset = sst_byt_reset,
+	.boot = sst_byt_boot,
+	.write = sst_shim32_write,
+	.read = sst_shim32_read,
+	.write64 = sst_shim32_write64,
+	.read64 = sst_shim32_read64,
+	.ram_read = sst_memcpy_fromio_32,
+	.ram_write = sst_memcpy_toio_32,
+	.irq_handler = sst_byt_irq,
+	.init = sst_byt_init,
+	.free = sst_byt_free,
+	.parse_fw = hsw_parse_fw_image,
+};
diff --git a/sound/soc/sof/hw-hsw.c b/sound/soc/sof/hw-hsw.c
new file mode 100644
index 0000000..96631d5
--- /dev/null
+++ b/sound/soc/sof/hw-hsw.c
@@ -0,0 +1,166 @@
+/*
+ * Intel Baytrail SST DSP driver
+ * Copyright (c) 2014, Intel Corporation.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope 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/delay.h>
+#include <linux/fs.h>
+#include <linux/slab.h>
+#include <linux/device.h>
+#include <linux/interrupt.h>
+#include <linux/module.h>
+#include <linux/dma-mapping.h>
+#include <linux/platform_device.h>
+#include <linux/firmware.h>
+
+#include "../intel/common/sst-dsp.h"
+#include "../intel/common/sst-dsp-priv.h"
+#include "../haswell/sst-haswell-ipc.h"
+
+#include <trace/events/hswadsp.h>
+
+static void hsw_notify(struct sst_dsp *dsp)
+{
+	sst_dsp_shim_update_bits(dsp, SST_IPCD,
+		SST_IPCD_BUSY | SST_IPCD_DONE, SST_IPCD_DONE);
+}
+
+static bool hsw_is_dsp_busy(struct sst_dsp *dsp)
+{
+	u32 ipcx;
+
+	ipcx = sst_dsp_shim_read64_unlocked(dsp, SST_IPCX);
+	return (ipcx & (SST_IPCX_BUSY | SST_IPCX_DONE));
+}
+
+
+static void hsw_shim_dbg(struct sst_generic_ipc *ipc, const char *text)
+{
+	struct sst_dsp *sst = ipc->dsp;
+	u32 ipcd, ipcx ,isrd, imrx, isrx, imrd;
+	int i;
+
+	ipcx = sst_dsp_shim_read_unlocked(sst, SST_IPCX);
+	ipcd = sst_dsp_shim_read_unlocked(sst, SST_IPCD);
+	isrx = sst_dsp_shim_read_unlocked(sst, SST_ISRX);
+	isrd = sst_dsp_shim_read_unlocked(sst, SST_ISRD);
+	imrx = sst_dsp_shim_read_unlocked(sst, SST_IMRX);
+	imrd = sst_dsp_shim_read_unlocked(sst, SST_IMRD);
+
+	dev_err(ipc->dev,
+		"ipc: --%s--\n ipcx 0x%8x\n ipcd 0x%8x\n"
+		" imrx 0x%8x\n imrd 0x%8x\n"
+		" isrx 0x%8x\n isrd 0x%8x\n",
+		text, ipcx, ipcd, imrx, imrd, isrx, isrd);
+
+	for (i = 0; i < 0xff; i+=8 ) {
+		dev_err(ipc->dev, "shim 0x%2.2x value 0x%16.16llx\n",i, 
+			sst_dsp_shim_read64_unlocked(sst, i));
+	}
+
+	for (i = 0xa0000; i < 0xa0100; i+=4) {
+		dev_err(sst->dev, "iram: 0x%x value 0x%8.8x\n", i - 0xa0000,
+			readl(sst->addr.lpe + i));
+	}
+
+	for (i = 0x0; i < 0x100; i+=4) {
+		dev_err(sst->dev, "dram: 0x%x value 0x%8.8x\n", i,
+			readl(sst->addr.lpe + i));
+	}
+
+	for (i = 0x00; i < 0xff; i+=4) {
+		dev_err(sst->dev, "pci: 0x%x value 0x%8.8x\n", i,
+			readl(sst->addr.pci_cfg + i));
+	}
+
+	//TODO: need correct mailbox offset
+	for (i = 0; i < 30; i++) {
+		dev_err(sst->dev, "mbox: 0x%x value 0x%8.8x\n", i,
+			readl(sst->addr.lpe + i * 4 + 0x9e000));
+	}
+}
+
+static void hsw_tx_msg(struct sst_generic_ipc *ipc, struct ipc_message *msg)
+{
+	/* send the message */
+	sst_dsp_outbox_write(ipc->dsp, msg->tx_data, msg->tx_size);
+	sst_dsp_ipc_msg_tx(ipc->dsp, msg->header);
+}
+
+static irqreturn_t hsw_irq_thread(int irq, void *context)
+{
+	struct sst_dsp *sst = (struct sst_dsp *) context;
+	struct sst_hsw *hsw = sst_dsp_get_thread_context(sst);
+	struct sst_generic_ipc *ipc = &hsw->ipc;
+	u32 ipcx, ipcd;
+	unsigned long flags;
+
+	spin_lock_irqsave(&sst->spinlock, flags);
+
+	ipcx = sst_dsp_ipc_msg_rx(hsw->dsp);
+	ipcd = sst_dsp_shim_read_unlocked(sst, SST_IPCD);
+
+	/* reply message from DSP */
+	if (ipcx & SST_IPCX_DONE) {
+
+		/* Handle Immediate reply from DSP Core */
+		hsw_process_reply(hsw, ipcx);
+
+		/* clear DONE bit - tell DSP we have completed */
+		sst_dsp_shim_update_bits_unlocked(sst, SST_IPCX,
+			SST_IPCX_DONE, 0);
+
+		/* unmask Done interrupt */
+		sst_dsp_shim_update_bits_unlocked(sst, SST_IMRX,
+			SST_IMRX_DONE, 0);
+	}
+
+	/* new message from DSP */
+	if (ipcd & SST_IPCD_BUSY) {
+
+		/* Handle Notification and Delayed reply from DSP Core */
+		hsw_process_notification(hsw, ipcd);
+
+		/* clear BUSY bit and set DONE bit - accept new messages */
+		sst_dsp_shim_update_bits_unlocked(sst, SST_IPCD,
+			SST_IPCD_BUSY | SST_IPCD_DONE, SST_IPCD_DONE);
+
+		/* unmask busy interrupt */
+		sst_dsp_shim_update_bits_unlocked(sst, SST_IMRX,
+			SST_IMRX_BUSY, 0);
+	}
+
+	spin_unlock_irqrestore(&sst->spinlock, flags);
+
+	/* continue to send any remaining messages... */
+	queue_kthread_work(&ipc->kworker, &ipc->kwork);
+
+	return IRQ_HANDLED;
+}
+
+static const struct sst_debugfs_map debugfs_bdw[] = {
+	{"dmac0", 0x98000, 0x420},
+	{"dmac1", 0x9c000, 0x420},
+	{"ssp0", 0xa0000, 0x100},
+	{"ssp1", 0xa1000, 0x100},
+	{"ssp2", 0xa2000, 0x100},
+	{"iram", 0xc0000, 80 * 1024},
+	{"dram", 0x100000, 160 * 1024},
+	{"shim", 0x140000, 0x100},
+	{"mbox", 0x144000, 0x1000},
+};
+
+static struct sst_dsp_device hsw_dev = {
+	.thread = hsw_irq_thread,
+	.ops = &haswell_ops,
+};
+
diff --git a/sound/soc/sof/ipc.c b/sound/soc/sof/ipc.c
new file mode 100644
index 0000000..99b50f5
--- /dev/null
+++ b/sound/soc/sof/ipc.c
@@ -0,0 +1,1163 @@
+/*
+ *  Intel SST Haswell/Broadwell IPC Support
+ *
+ * Copyright (C) 2013, 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/platform_device.h>
+#include <linux/kthread.h>
+#include <linux/firmware.h>
+#include <linux/dma-mapping.h>
+#include <linux/debugfs.h>
+#include <linux/pm_runtime.h>
+#include <sound/asound.h>
+
+#include "../intel/haswell/sst-haswell-ipc.h"
+#include "../intel/common/sst-dsp.h"
+#include "../intel/common/sst-dsp-priv.h"
+#include "../intel/common/sst-ipc.h"
+
+
+
+struct sst_hsw_stream;
+struct sst_hsw;
+
+/* Stream infomation */
+struct sst_hsw_stream {
+	/* configuration */
+	struct sst_hsw_ipc_stream_alloc_req request;
+	struct sst_hsw_ipc_stream_alloc_reply reply;
+	struct sst_hsw_ipc_stream_free_req free_req;
+
+	/* Mixer info */
+	u32 mute_volume[SST_HSW_NO_CHANNELS];
+	u32 mute[SST_HSW_NO_CHANNELS];
+
+	/* runtime info */
+	struct sst_hsw *hsw;
+	int host_id;
+	bool commited;
+	bool running;
+
+	/* Notification work */
+	struct work_struct notify_work;
+	u32 header;
+
+	/* Position info from DSP */
+	struct sst_hsw_ipc_stream_set_position wpos;
+	struct sst_hsw_ipc_stream_get_position rpos;
+	struct sst_hsw_ipc_stream_glitch_position glitch;
+
+	/* Volume info */
+	struct sst_hsw_ipc_volume_req vol_req;
+
+	/* driver callback */
+	u32 (*notify_position)(struct sst_hsw_stream *stream, void *data);
+	void *pdata;
+
+	/* record the fw read position when playback */
+	snd_pcm_uframes_t old_position;
+	bool play_silence;
+	struct list_head node;
+};
+
+/* FW log ring information */
+struct sst_hsw_log_stream {
+	dma_addr_t dma_addr;
+	unsigned char *dma_area;
+	unsigned char *ring_descr;
+	int pages;
+	int size;
+
+	/* Notification work */
+	struct work_struct notify_work;
+	wait_queue_head_t readers_wait_q;
+	struct mutex rw_mutex;
+
+	u32 last_pos;
+	u32 curr_pos;
+	u32 reader_pos;
+
+	/* fw log config */
+	u32 config[SST_HSW_FW_LOG_CONFIG_DWORDS];
+
+	struct sst_hsw *hsw;
+};
+
+struct dma_trace_sg {
+	int size;
+	void *buf;
+	struct list_head list;
+};
+
+
+
+/* SST Haswell IPC data */
+struct sst_hsw {
+	struct device *dev;
+	struct sst_dsp *dsp;
+	struct platform_device *pdev_pcm;
+
+	/* FW config */
+	struct sst_hsw_ipc_fw_ready fw_ready;
+	struct sst_hsw_ipc_fw_version version;
+	bool fw_done;
+	struct sst_fw *sst_fw;
+
+	/* stream */
+	struct list_head stream_list;
+
+	/* global mixer */
+	struct sst_hsw_ipc_stream_info_reply mixer_info;
+	enum sst_hsw_volume_curve curve_type;
+	u32 curve_duration;
+	u32 mute[SST_HSW_NO_CHANNELS];
+	u32 mute_volume[SST_HSW_NO_CHANNELS];
+	bool lbm; /* loopback mode */
+
+	/* DX */
+	struct sst_hsw_ipc_dx_reply dx;
+	void *dx_context;
+	dma_addr_t dx_context_paddr;
+	enum sst_hsw_device_id dx_dev;
+	enum sst_hsw_device_mclk dx_mclk;
+	enum sst_hsw_device_mode dx_mode;
+	u32 dx_clock_divider;
+
+	/* boot */
+	wait_queue_head_t boot_wait;
+	bool boot_complete;
+	bool shutdown;
+
+	/* IPC messaging */
+	struct sst_generic_ipc ipc;
+
+	/* dma_trace buffer */
+	struct snd_dma_buffer trace_dma_descriptor;
+	struct sst_hsw_ipc_debug_log_enable_req dt_enable_request;
+	struct snd_dma_buffer dtrace_buffer;
+	struct dma_trace_buffer host_buffer;
+
+	/* FW log stream */
+	struct sst_hsw_log_stream log_stream;
+};
+
+#define CREATE_TRACE_POINTS
+#include <trace/events/hswadsp.h>
+
+static inline u32 msg_get_global_type(u32 msg)
+{
+	return (msg & IPC_GLB_TYPE_MASK) >> IPC_GLB_TYPE_SHIFT;
+}
+
+static inline u32 msg_get_global_reply(u32 msg)
+{
+	return (msg & IPC_GLB_REPLY_MASK) >> IPC_GLB_REPLY_SHIFT;
+}
+
+static inline u32 msg_get_stream_type(u32 msg)
+{
+	return (msg & IPC_STR_TYPE_MASK) >>  IPC_STR_TYPE_SHIFT;
+}
+
+static inline u32 msg_get_stage_type(u32 msg)
+{
+	return (msg & IPC_STG_TYPE_MASK) >>  IPC_STG_TYPE_SHIFT;
+}
+
+static inline u32 msg_get_stream_id(u32 msg)
+{
+	return (msg & IPC_STR_ID_MASK) >>  IPC_STR_ID_SHIFT;
+}
+
+static inline u32 msg_get_notify_reason(u32 msg)
+{
+	return (msg & IPC_STG_TYPE_MASK) >> IPC_STG_TYPE_SHIFT;
+}
+
+static inline u32 msg_get_module_operation(u32 msg)
+{
+	return (msg & IPC_MODULE_OPERATION_MASK) >> IPC_MODULE_OPERATION_SHIFT;
+}
+
+static inline u32 msg_get_module_id(u32 msg)
+{
+	return (msg & IPC_MODULE_ID_MASK) >> IPC_MODULE_ID_SHIFT;
+}
+
+u32 create_channel_map(enum sst_hsw_channel_config config)
+{
+	switch (config) {
+	case SST_HSW_CHANNEL_CONFIG_MONO:
+		return (0xFFFFFFF0 | SST_HSW_CHANNEL_CENTER);
+	case SST_HSW_CHANNEL_CONFIG_STEREO:
+		return (0xFFFFFF00 | SST_HSW_CHANNEL_LEFT
+			| (SST_HSW_CHANNEL_RIGHT << 4));
+	case SST_HSW_CHANNEL_CONFIG_2_POINT_1:
+		return (0xFFFFF000 | SST_HSW_CHANNEL_LEFT
+			| (SST_HSW_CHANNEL_RIGHT << 4)
+			| (SST_HSW_CHANNEL_LFE << 8 ));
+	case SST_HSW_CHANNEL_CONFIG_3_POINT_0:
+		return (0xFFFFF000 | SST_HSW_CHANNEL_LEFT
+			| (SST_HSW_CHANNEL_CENTER << 4)
+			| (SST_HSW_CHANNEL_RIGHT << 8));
+	case SST_HSW_CHANNEL_CONFIG_3_POINT_1:
+		return (0xFFFF0000 | SST_HSW_CHANNEL_LEFT
+			| (SST_HSW_CHANNEL_CENTER << 4)
+			| (SST_HSW_CHANNEL_RIGHT << 8)
+			| (SST_HSW_CHANNEL_LFE << 12));
+	case SST_HSW_CHANNEL_CONFIG_QUATRO:
+		return (0xFFFF0000 | SST_HSW_CHANNEL_LEFT
+			| (SST_HSW_CHANNEL_RIGHT << 4)
+			| (SST_HSW_CHANNEL_LEFT_SURROUND << 8)
+			| (SST_HSW_CHANNEL_RIGHT_SURROUND << 12));
+	case SST_HSW_CHANNEL_CONFIG_4_POINT_0:
+		return (0xFFFF0000 | SST_HSW_CHANNEL_LEFT
+			| (SST_HSW_CHANNEL_CENTER << 4)
+			| (SST_HSW_CHANNEL_RIGHT << 8)
+			| (SST_HSW_CHANNEL_CENTER_SURROUND << 12));
+	case SST_HSW_CHANNEL_CONFIG_5_POINT_0:
+		return (0xFFF00000 | SST_HSW_CHANNEL_LEFT
+			| (SST_HSW_CHANNEL_CENTER << 4)
+			| (SST_HSW_CHANNEL_RIGHT << 8)
+			| (SST_HSW_CHANNEL_LEFT_SURROUND << 12)
+			| (SST_HSW_CHANNEL_RIGHT_SURROUND << 16));
+	case SST_HSW_CHANNEL_CONFIG_5_POINT_1:
+		return (0xFF000000 | SST_HSW_CHANNEL_CENTER
+			| (SST_HSW_CHANNEL_LEFT << 4)
+			| (SST_HSW_CHANNEL_RIGHT << 8)
+			| (SST_HSW_CHANNEL_LEFT_SURROUND << 12)
+			| (SST_HSW_CHANNEL_RIGHT_SURROUND << 16)
+			| (SST_HSW_CHANNEL_LFE << 20));
+	case SST_HSW_CHANNEL_CONFIG_DUAL_MONO:
+		return (0xFFFFFF00 | SST_HSW_CHANNEL_LEFT
+			| (SST_HSW_CHANNEL_LEFT << 4));
+	default:
+		return 0xFFFFFFFF;
+	}
+}
+
+static struct sst_hsw_stream *get_stream_by_id(struct sst_hsw *hsw,
+	int stream_id)
+{
+	struct sst_hsw_stream *stream;
+
+	list_for_each_entry(stream, &hsw->stream_list, node) {
+		if (stream->reply.stream_hw_id == stream_id)
+			return stream;
+	}
+
+	return NULL;
+}
+
+
+
+
+
+#define DMA_TRACE_PAGE_NUMBER 4
+static int hsw_setup_dma_trace_page_table(struct sst_hsw *hsw)
+{
+	struct snd_dma_buffer *dmab = &hsw->dtrace_buffer;
+	struct dma_trace_buffer *hbuf = &hsw->host_buffer;
+	struct dma_trace_sg *sg_elem;
+	int i, pages;
+
+	hbuf->read_offset = 0;
+	hbuf->write_offset = 0;
+	hbuf->rcurrent = hbuf->wcurrent = hbuf->elem_list.next;
+	pages = DMA_TRACE_PAGE_NUMBER;
+	hbuf->size = pages * PAGE_SIZE;
+
+	for (i = 0; i < pages; i++) {
+		u32 idx = (((i << 2) + i)) >> 1;
+		u32 pfn = snd_sgbuf_get_addr(dmab, i * PAGE_SIZE) >> PAGE_SHIFT;
+		u32 *pg_table;
+
+		sg_elem = devm_kzalloc(hsw->dev, sizeof(*sg_elem), GFP_KERNEL);
+		sg_elem->size = PAGE_SIZE;
+		sg_elem->buf = snd_sgbuf_get_ptr(dmab, i * PAGE_SIZE);
+		list_add_tail(&sg_elem->list, &hbuf->elem_list);
+		pg_table = (u32 *)(hsw->trace_dma_descriptor.area + idx);
+
+		if (i & 1)
+			*pg_table |= (pfn << 4);
+		else
+			*pg_table |= pfn;
+	}
+
+	hbuf->rcurrent = hbuf->wcurrent = hbuf->elem_list.next;
+
+	return 0;
+}
+
+
+
+
+static void hsw_fw_ready(struct sst_hsw *hsw, u32 header)
+{
+	struct sst_hsw_ipc_fw_ready fw_ready;
+	u32 offset;
+	u8 fw_info[IPC_MAX_MAILBOX_BYTES - 5 * sizeof(u32)];
+	char *tmp[5], *pinfo;
+	int i = 0;
+
+	offset = (header & 0x1FFFFFFF) << 3;
+
+	dev_dbg(hsw->dev, "ipc: DSP is ready 0x%8.8x offset %d\n",
+		header, offset);
+
+	/* copy data from the DSP FW ready offset */
+	sst_dsp_read(hsw->dsp, &fw_ready, offset, sizeof(fw_ready));
+
+	sst_dsp_mailbox_init(hsw->dsp, fw_ready.inbox_offset,
+		fw_ready.inbox_size, fw_ready.outbox_offset,
+		fw_ready.outbox_size);
+
+	hsw->boot_complete = true;
+	wake_up(&hsw->boot_wait);
+
+	dev_dbg(hsw->dev, " mailbox upstream 0x%x - size 0x%x\n",
+		fw_ready.inbox_offset, fw_ready.inbox_size);
+	dev_dbg(hsw->dev, " mailbox downstream 0x%x - size 0x%x\n",
+		fw_ready.outbox_offset, fw_ready.outbox_size);
+	if (fw_ready.fw_info_size < sizeof(fw_ready.fw_info)) {
+		fw_ready.fw_info[fw_ready.fw_info_size] = 0;
+		dev_dbg(hsw->dev, " Firmware info: %s \n", fw_ready.fw_info);
+
+		/* log the FW version info got from the mailbox here. */
+		memcpy(fw_info, fw_ready.fw_info, fw_ready.fw_info_size);
+		pinfo = &fw_info[0];
+		for (i = 0; i < ARRAY_SIZE(tmp); i++)
+			tmp[i] = strsep(&pinfo, " ");
+		dev_info(hsw->dev, "FW loaded, mailbox readback FW info: type %s, - "
+			"version: %s.%s, build %s, source commit id: %s\n",
+			tmp[0], tmp[1], tmp[2], tmp[3], tmp[4]);
+	}
+}
+
+static void hsw_notification_work(struct work_struct *work)
+{
+	struct sst_hsw_stream *stream = container_of(work,
+			struct sst_hsw_stream, notify_work);
+	struct sst_hsw_ipc_stream_glitch_position *glitch = &stream->glitch;
+	struct sst_hsw_ipc_stream_get_position *pos = &stream->rpos;
+	struct sst_hsw *hsw = stream->hsw;
+	u32 reason;
+
+	reason = msg_get_notify_reason(stream->header);
+
+	switch (reason) {
+	case IPC_STG_GLITCH:
+		trace_ipc_notification("DSP stream under/overrun",
+			stream->reply.stream_hw_id);
+		sst_dsp_inbox_read(hsw->dsp, glitch, sizeof(*glitch));
+
+		dev_err(hsw->dev, "glitch %d pos 0x%x write pos 0x%x\n",
+			glitch->glitch_type, glitch->present_pos,
+			glitch->write_pos);
+		break;
+
+	case IPC_POSITION_CHANGED:
+		trace_ipc_notification("DSP stream position changed for",
+			stream->reply.stream_hw_id);
+		sst_dsp_inbox_read(hsw->dsp, pos, sizeof(*pos));
+
+		if (stream->notify_position)
+			stream->notify_position(stream, stream->pdata);
+
+		break;
+	default:
+		dev_err(hsw->dev, "error: unknown notification 0x%x\n",
+			stream->header);
+		break;
+	}
+
+	/* tell DSP that notification has been handled */
+	hsw->ipc.ops.dsp_notify(hsw->dsp);
+
+	/* unmask busy interrupt */
+	sst_dsp_shim_update_bits(hsw->dsp, SST_IMRX, SST_IMRX_BUSY, 0);
+}
+
+static void hsw_stream_update(struct sst_hsw *hsw, struct ipc_message *msg)
+{
+	struct sst_hsw_stream *stream;
+	u32 header = msg->header & ~(IPC_STATUS_MASK | IPC_GLB_REPLY_MASK);
+	u32 stream_id = msg_get_stream_id(header);
+	u32 stream_msg = msg_get_stream_type(header);
+
+	stream = get_stream_by_id(hsw, stream_id);
+	if (stream == NULL)
+		return;
+
+	switch (stream_msg) {
+	case IPC_STR_STAGE_MESSAGE:
+	case IPC_STR_NOTIFICATION:
+		break;
+	case IPC_STR_RESET:
+		trace_ipc_notification("stream reset", stream->reply.stream_hw_id);
+		break;
+	case IPC_STR_PAUSE:
+		stream->running = false;
+		trace_ipc_notification("stream paused",
+			stream->reply.stream_hw_id);
+		break;
+	case IPC_STR_RESUME:
+		stream->running = true;
+		trace_ipc_notification("stream running",
+			stream->reply.stream_hw_id);
+		break;
+	}
+}
+
+static int hsw_process_reply(struct sst_hsw *hsw, u32 header)
+{
+	struct ipc_message *msg;
+	u32 reply = msg_get_global_reply(header);
+
+	trace_ipc_reply("processing -->", header);
+
+	msg = sst_ipc_reply_find_msg(&hsw->ipc, header);
+	if (msg == NULL) {
+		trace_ipc_error("error: can't find message header", header);
+		return -EIO;
+	}
+
+	/* first process the header */
+	switch (reply) {
+	case IPC_GLB_REPLY_PENDING:
+		trace_ipc_pending_reply("received", header);
+		msg->pending = true;
+		hsw->ipc.pending = true;
+		return 1;
+	case IPC_GLB_REPLY_SUCCESS:
+		if (msg->pending) {
+			trace_ipc_pending_reply("completed", header);
+			sst_dsp_inbox_read(hsw->dsp, msg->rx_data,
+				msg->rx_size);
+			hsw->ipc.pending = false;
+		} else {
+			/* copy data from the DSP */
+			sst_dsp_inbox_read(hsw->dsp, msg->rx_data,
+				msg->rx_size);
+		}
+		break;
+	/* these will be rare - but useful for debug */
+	case IPC_GLB_REPLY_UNKNOWN_MESSAGE_TYPE:
+		trace_ipc_error("error: unknown message type", header);
+		msg->errno = -EBADMSG;
+		break;
+	case IPC_GLB_REPLY_OUT_OF_RESOURCES:
+		trace_ipc_error("error: out of resources", header);
+		msg->errno = -ENOMEM;
+		break;
+	case IPC_GLB_REPLY_BUSY:
+		trace_ipc_error("error: reply busy", header);
+		msg->errno = -EBUSY;
+		break;
+	case IPC_GLB_REPLY_FAILURE:
+		trace_ipc_error("error: reply failure", header);
+		msg->errno = -EINVAL;
+		break;
+	case IPC_GLB_REPLY_STAGE_UNINITIALIZED:
+		trace_ipc_error("error: stage uninitialized", header);
+		msg->errno = -EINVAL;
+		break;
+	case IPC_GLB_REPLY_NOT_FOUND:
+		trace_ipc_error("error: reply not found", header);
+		msg->errno = -EINVAL;
+		break;
+	case IPC_GLB_REPLY_SOURCE_NOT_STARTED:
+		trace_ipc_error("error: source not started", header);
+		msg->errno = -EINVAL;
+		break;
+	case IPC_GLB_REPLY_INVALID_REQUEST:
+		trace_ipc_error("error: invalid request", header);
+		msg->errno = -EINVAL;
+		break;
+	case IPC_GLB_REPLY_ERROR_INVALID_PARAM:
+		trace_ipc_error("error: invalid parameter", header);
+		msg->errno = -EINVAL;
+		break;
+	default:
+		trace_ipc_error("error: unknown reply", header);
+		msg->errno = -EINVAL;
+		break;
+	}
+
+	/* update any stream states */
+	if (msg_get_global_type(header) == IPC_GLB_STREAM_MESSAGE)
+		hsw_stream_update(hsw, msg);
+
+	/* wake up and return the error if we have waiters on this message ? */
+	list_del(&msg->list);
+	sst_ipc_tx_msg_reply_complete(&hsw->ipc, msg);
+
+	return 1;
+}
+
+static int hsw_module_message(struct sst_hsw *hsw, u32 header)
+{
+	u32 operation, module_id;
+	int handled = 0;
+
+	operation = msg_get_module_operation(header);
+	module_id = msg_get_module_id(header);
+	dev_dbg(hsw->dev, "received module message header: 0x%8.8x\n",
+			header);
+	dev_dbg(hsw->dev, "operation: 0x%8.8x module_id: 0x%8.8x\n",
+			operation, module_id);
+
+	switch (operation) {
+	case IPC_MODULE_NOTIFICATION:
+		dev_dbg(hsw->dev, "module notification received");
+		handled = 1;
+		break;
+	default:
+		handled = hsw_process_reply(hsw, header);
+		break;
+	}
+
+	return handled;
+}
+
+static int hsw_stream_message(struct sst_hsw *hsw, u32 header)
+{
+	u32 stream_msg, stream_id, stage_type;
+	struct sst_hsw_stream *stream;
+	int handled = 0;
+
+	stream_msg = msg_get_stream_type(header);
+	stream_id = msg_get_stream_id(header);
+	stage_type = msg_get_stage_type(header);
+
+	stream = get_stream_by_id(hsw, stream_id);
+	if (stream == NULL)
+		return handled;
+
+	stream->header = header;
+
+	switch (stream_msg) {
+	case IPC_STR_STAGE_MESSAGE:
+		dev_err(hsw->dev, "error: stage msg not implemented 0x%8.8x\n",
+			header);
+		break;
+	case IPC_STR_NOTIFICATION:
+		schedule_work(&stream->notify_work);
+		break;
+	default:
+		/* handle pending message complete request */
+		handled = hsw_process_reply(hsw, header);
+		break;
+	}
+
+	return handled;
+}
+
+static int hsw_log_message(struct sst_hsw *hsw, u32 header)
+{
+	u32 operation = (header & IPC_LOG_OP_MASK) >>  IPC_LOG_OP_SHIFT;
+	struct sst_hsw_log_stream *stream = &hsw->log_stream;
+	struct sst_hsw_ipc_debug_log_pos_update pos;
+	struct dma_trace_buffer *hbuf = &hsw->host_buffer;
+	int ret = 1;
+
+	sst_dsp_shim_update_bits64_unlocked(hsw->dsp, SST_IPCD,
+			SST_BYT_IPCD_BUSY | SST_BYT_IPCD_DONE,
+				SST_BYT_IPCD_DONE);
+	sst_dsp_shim_update_bits_unlocked(hsw->dsp, SST_IMRX, SST_IMRX_BUSY, 0);
+
+	if (operation != IPC_DEBUG_REQUEST_LOG_DUMP) {
+		dev_err(hsw->dev,
+			"error: log msg not implemented 0x%8.8x\n", header);
+		return 0;
+	}
+
+	stream->last_pos = stream->curr_pos;
+	pos.log_size = 0;
+	sst_dsp_inbox_read(hsw->dsp, &pos, sizeof(pos));
+	hbuf->pending_size += pos.log_size;
+	/*
+	 * if pending_size > hbuf->size, it means
+	 * some msg data has been overwritten
+	 */
+	if (hbuf->pending_size > hbuf->size)
+		hbuf->pending_size = hbuf->size;
+
+	return ret;
+}
+
+static int hsw_process_notification(struct sst_hsw *hsw, u64 header)
+{
+	u32 type;
+	int handled = 1;
+
+	/* upper 32 bits not used atm */
+	type = msg_get_global_type(header);
+
+	trace_ipc_request("processing -->", header);
+
+	/* FW Ready is a special case */
+	if (!hsw->boot_complete && header & IPC_FW_READY) {
+		hsw_fw_ready(hsw, header);
+		return handled;
+	}
+
+	switch (type) {
+	case IPC_GLB_GET_FW_VERSION:
+	case IPC_GLB_ALLOCATE_STREAM:
+	case IPC_GLB_FREE_STREAM:
+	case IPC_GLB_GET_FW_CAPABILITIES:
+	case IPC_GLB_REQUEST_DUMP:
+	case IPC_GLB_GET_DEVICE_FORMATS:
+	case IPC_GLB_SET_DEVICE_FORMATS:
+	case IPC_GLB_ENTER_DX_STATE:
+	case IPC_GLB_GET_MIXER_STREAM_INFO:
+	case IPC_GLB_MAX_IPC_MESSAGE_TYPE:
+	case IPC_GLB_RESTORE_CONTEXT:
+	case IPC_GLB_SHORT_REPLY:
+		dev_err(hsw->dev, "error: message type %d header 0x%16llx\n",
+				type, header);
+		break;
+	case IPC_GLB_STREAM_MESSAGE:
+		handled = hsw_stream_message(hsw, header);
+		break;
+	case IPC_GLB_DEBUG_LOG_MESSAGE:
+		handled = hsw_log_message(hsw, header);
+		break;
+	case IPC_GLB_MODULE_OPERATION:
+		handled = hsw_module_message(hsw, header);
+		break;
+	default:
+		dev_err(hsw->dev, "error: unexpected type %d hdr 0x%16llx\n",
+			type, header);
+		break;
+	}
+
+	return handled;
+}
+
+int sst_hsw_fw_get_version(struct sst_hsw *hsw,
+	struct sst_hsw_ipc_fw_version *version)
+{
+	int ret;
+
+	ret = sst_ipc_tx_message_wait(&hsw->ipc,
+		IPC_GLB_TYPE(IPC_GLB_GET_FW_VERSION),
+		NULL, 0, version, sizeof(*version));
+	if (ret < 0)
+		dev_err(hsw->dev, "error: get version failed\n");
+
+	return ret;
+}
+
+
+
+
+
+/* physical BE config */
+int sst_hsw_device_set_config(struct sst_hsw *hsw,
+	enum sst_hsw_device_id dev, enum sst_hsw_device_mclk mclk,
+	enum sst_hsw_device_mode mode, u32 clock_divider)
+{
+	struct sst_hsw_ipc_device_config_req config;
+	u32 header;
+	int ret;
+
+	trace_ipc_request("set device config", dev);
+
+	hsw->dx_dev = config.ssp_interface = dev;
+	hsw->dx_mclk = config.clock_frequency = mclk;
+	hsw->dx_mode = config.mode = mode;
+	hsw->dx_clock_divider = config.clock_divider = clock_divider;
+	if (mode == SST_HSW_DEVICE_TDM_CLOCK_MASTER)
+		config.channels = 4;
+	else
+		config.channels = 2;
+
+	trace_hsw_device_config_req(&config);
+
+	header = IPC_GLB_TYPE(IPC_GLB_SET_DEVICE_FORMATS);
+
+	ret = sst_ipc_tx_message_wait(&hsw->ipc, header, &config,
+		sizeof(config), NULL, 0);
+	if (ret < 0)
+		dev_err(hsw->dev, "error: set device formats failed\n");
+
+	return ret;
+}
+EXPORT_SYMBOL_GPL(sst_hsw_device_set_config);
+
+/* loopback mode config, for loopback debug */
+int sst_hsw_device_set_loopback(struct sst_hsw *hsw, bool lbm)
+{
+	int ret;
+
+	trace_ipc_request("set loopback", lbm);
+
+	ret = sst_ipc_tx_message_wait(&hsw->ipc,
+		IPC_GLB_TYPE((lbm ? IPC_GLB_ENABLE_LOOPBACK : IPC_GLB_DISABLE_LOOPBACK)),
+		NULL, 0, NULL, 0);
+	if (ret < 0) {
+		dev_err(hsw->dev, "error: set loopback failed\n");
+		return ret;
+	}
+	hsw->lbm = lbm;
+	return ret;
+}
+EXPORT_SYMBOL_GPL(sst_hsw_device_set_loopback);
+
+/* loopback mode config, for loopback debug */
+bool sst_hsw_device_get_loopback(struct sst_hsw *hsw)
+{
+	return hsw->lbm;
+}
+EXPORT_SYMBOL_GPL(sst_hsw_device_get_loopback);
+
+/* DX Config */
+int sst_hsw_dx_set_state(struct sst_hsw *hsw,
+	enum sst_hsw_dx_state state, struct sst_hsw_ipc_dx_reply *dx)
+{
+	u32 header, state_;
+	int ret, item;
+
+	header = IPC_GLB_TYPE(IPC_GLB_ENTER_DX_STATE);
+	state_ = state;
+
+	trace_ipc_request("PM enter Dx state", state);
+
+	ret = sst_ipc_tx_message_wait(&hsw->ipc, header, &state_,
+		sizeof(state_), dx, sizeof(*dx));
+	if (ret < 0) {
+		dev_err(hsw->dev, "ipc: error set dx state %d failed\n", state);
+		return ret;
+	}
+
+	for (item = 0; item < dx->entries_no; item++) {
+		dev_dbg(hsw->dev,
+			"Item[%d] offset[%x] - size[%x] - source[%x]\n",
+			item, dx->mem_info[item].offset,
+			dx->mem_info[item].size,
+			dx->mem_info[item].source);
+	}
+	dev_dbg(hsw->dev, "ipc: got %d entry numbers for state %d\n",
+		dx->entries_no, state);
+
+	return ret;
+}
+
+#ifdef CONFIG_PM
+static int sst_hsw_dx_state_dump(struct sst_hsw *hsw)
+{
+	struct sst_dsp *sst = hsw->dsp;
+	u32 item, offset, size;
+	int ret = 0;
+
+	trace_ipc_request("PM state dump. Items #", SST_HSW_MAX_DX_REGIONS);
+
+	if (hsw->dx.entries_no > SST_HSW_MAX_DX_REGIONS) {
+		dev_err(hsw->dev,
+			"error: number of FW context regions greater than %d\n",
+			SST_HSW_MAX_DX_REGIONS);
+		memset(&hsw->dx, 0, sizeof(hsw->dx));
+		return -EINVAL;
+	}
+
+	ret = sst_dsp_dma_get_channel(sst, 0);
+	if (ret < 0) {
+		dev_err(hsw->dev, "error: cant allocate dma channel %d\n", ret);
+		return ret;
+	}
+
+	/* set on-demond mode on engine 0 channel 3 */
+	sst_dsp_shim_update_bits(sst, SST_HMDC,
+			SST_HMDC_HDDA_E0_ALLCH | SST_HMDC_HDDA_E1_ALLCH,
+			SST_HMDC_HDDA_E0_ALLCH | SST_HMDC_HDDA_E1_ALLCH);
+
+	for (item = 0; item < hsw->dx.entries_no; item++) {
+		if (hsw->dx.mem_info[item].source == SST_HSW_DX_TYPE_MEMORY_DUMP
+			&& hsw->dx.mem_info[item].offset > DSP_DRAM_ADDR_OFFSET
+			&& hsw->dx.mem_info[item].offset <
+			DSP_DRAM_ADDR_OFFSET + SST_HSW_DX_CONTEXT_SIZE) {
+
+			offset = hsw->dx.mem_info[item].offset
+					- DSP_DRAM_ADDR_OFFSET;
+			size = (hsw->dx.mem_info[item].size + 3) & (~3);
+
+			ret = sst_dsp_dma_copyfrom(sst, hsw->dx_context_paddr + offset,
+				sst->addr.lpe_base + offset, size);
+			if (ret < 0) {
+				dev_err(hsw->dev,
+					"error: FW context dump failed\n");
+				memset(&hsw->dx, 0, sizeof(hsw->dx));
+				goto out;
+			}
+		}
+	}
+
+out:
+	sst_dsp_dma_put_channel(sst);
+	return ret;
+}
+
+static int sst_hsw_dx_state_restore(struct sst_hsw *hsw)
+{
+	struct sst_dsp *sst = hsw->dsp;
+	u32 item, offset, size;
+	int ret;
+
+	for (item = 0; item < hsw->dx.entries_no; item++) {
+		if (hsw->dx.mem_info[item].source == SST_HSW_DX_TYPE_MEMORY_DUMP
+			&& hsw->dx.mem_info[item].offset > DSP_DRAM_ADDR_OFFSET
+			&& hsw->dx.mem_info[item].offset <
+			DSP_DRAM_ADDR_OFFSET + SST_HSW_DX_CONTEXT_SIZE) {
+
+			offset = hsw->dx.mem_info[item].offset
+					- DSP_DRAM_ADDR_OFFSET;
+			size = (hsw->dx.mem_info[item].size + 3) & (~3);
+
+			ret = sst_dsp_dma_copyto(sst, sst->addr.lpe_base + offset,
+				hsw->dx_context_paddr + offset, size);
+			if (ret < 0) {
+				dev_err(hsw->dev,
+					"error: FW context restore failed\n");
+				return ret;
+			}
+		}
+	}
+
+	return 0;
+}
+
+int sst_hsw_dsp_load(struct sst_hsw *hsw)
+{
+	struct sst_dsp *dsp = hsw->dsp;
+	struct sst_fw *sst_fw, *t;
+	int ret;
+
+	dev_dbg(hsw->dev, "loading audio DSP....");
+
+	ret = sst_dsp_wake(dsp);
+	if (ret < 0) {
+		dev_err(hsw->dev, "error: failed to wake audio DSP\n");
+		return -ENODEV;
+	}
+
+	ret = sst_dsp_dma_get_channel(dsp, 0);
+	if (ret < 0) {
+		dev_err(hsw->dev, "error: cant allocate dma channel %d\n", ret);
+		return ret;
+	}
+
+	list_for_each_entry_safe_reverse(sst_fw, t, &dsp->fw_list, list) {
+		ret = sst_fw_reload(sst_fw);
+		if (ret < 0) {
+			dev_err(hsw->dev, "error: SST FW reload failed\n");
+			sst_dsp_dma_put_channel(dsp);
+			return -ENOMEM;
+		}
+	}
+	ret = sst_block_alloc_scratch(hsw->dsp);
+	if (ret < 0)
+		return -EINVAL;
+
+	sst_dsp_dma_put_channel(dsp);
+	return 0;
+}
+
+static int sst_hsw_dsp_restore(struct sst_hsw *hsw)
+{
+	struct sst_dsp *dsp = hsw->dsp;
+	int ret = 0;
+
+	dev_dbg(hsw->dev, "restoring audio DSP....");
+
+	ret = sst_dsp_dma_get_channel(dsp, 0);
+	if (ret < 0) {
+		dev_err(hsw->dev, "error: cant allocate dma channel %d\n", ret);
+		return ret;
+	}
+
+	ret = sst_hsw_dx_state_restore(hsw);
+	if (ret < 0) {
+		dev_err(hsw->dev, "error: SST FW context restore failed\n");
+		sst_dsp_dma_put_channel(dsp);
+		return -ENOMEM;
+	}
+	sst_dsp_dma_put_channel(dsp);
+
+	/* wait for DSP boot completion */
+	sst_dsp_boot(dsp);
+
+	return ret;
+}
+
+int sst_hsw_dsp_runtime_suspend(struct sst_hsw *hsw)
+{
+	int ret;
+
+	dev_dbg(hsw->dev, "audio dsp runtime suspend\n");
+
+	ret = sst_hsw_dx_set_state(hsw, SST_HSW_DX_STATE_D3, &hsw->dx);
+	if (ret < 0)
+		return ret;
+
+	sst_dsp_stall(hsw->dsp);
+
+	ret = sst_hsw_dx_state_dump(hsw);
+	if (ret < 0)
+		return ret;
+
+	sst_ipc_drop_all(&hsw->ipc);
+
+	return 0;
+}
+
+int sst_hsw_dsp_runtime_sleep(struct sst_hsw *hsw)
+{
+	struct sst_fw *sst_fw, *t;
+	struct sst_dsp *dsp = hsw->dsp;
+
+	list_for_each_entry_safe(sst_fw, t, &dsp->fw_list, list) {
+		sst_fw_unload(sst_fw);
+	}
+	sst_block_free_scratch(dsp);
+
+	hsw->boot_complete = false;
+
+	sst_dsp_sleep(dsp);
+
+	return 0;
+}
+
+int sst_hsw_dsp_runtime_resume(struct sst_hsw *hsw)
+{
+	struct device *dev = hsw->dev;
+	int ret;
+
+	dev_dbg(dev, "audio dsp runtime resume\n");
+
+	if (hsw->boot_complete)
+		return 1; /* tell caller no action is required */
+
+	ret = sst_hsw_dsp_restore(hsw);
+	if (ret < 0)
+		dev_err(dev, "error: audio DSP boot failure\n");
+
+	sst_hsw_init_module_state(hsw);
+
+	ret = wait_event_timeout(hsw->boot_wait, hsw->boot_complete,
+		msecs_to_jiffies(IPC_BOOT_MSECS));
+	if (ret == 0) {
+		dev_err(hsw->dev, "error: audio DSP boot timeout IPCD 0x%x IPCX 0x%x\n",
+			sst_dsp_shim_read_unlocked(hsw->dsp, SST_IPCD),
+			sst_dsp_shim_read_unlocked(hsw->dsp, SST_IPCX));
+		return -EIO;
+	}
+
+	/* Set ADSP SSP port settings - sadly the FW does not store SSP port
+	   settings as part of the PM context. */
+	ret = sst_hsw_device_set_config(hsw, hsw->dx_dev, hsw->dx_mclk,
+					hsw->dx_mode, hsw->dx_clock_divider);
+	if (ret < 0)
+		dev_err(dev, "error: SSP re-initialization failed\n");
+
+	return ret;
+}
+#endif
+
+struct sst_dsp *sst_hsw_get_dsp(struct sst_hsw *hsw)
+{
+	return hsw->dsp;
+}
+
+static void hsw_tx_data_copy(struct ipc_message *msg, char *tx_data,
+	size_t tx_size)
+{
+	memcpy(msg->tx_data, tx_data, tx_size);
+}
+
+static u64 hsw_reply_msg_match(u64 header, u64 *mask)
+{
+	/* clear reply bits & status bits */
+	header &= ~(IPC_STATUS_MASK | IPC_GLB_REPLY_MASK);
+	*mask = (u64)-1;
+
+	return header;
+}
+
+
+int sst_hsw_dsp_init(struct device *dev, struct sst_pdata *pdata)
+{
+	struct sst_hsw_ipc_fw_version version;
+	struct sst_hsw *hsw;
+	struct sst_generic_ipc *ipc;
+	struct sst_dsp_device *dsp_dev;
+	int ret;
+
+	dev_dbg(dev, "initialising Audio DSP IPC\n");
+
+	hsw = devm_kzalloc(dev, sizeof(*hsw), GFP_KERNEL);
+	if (hsw == NULL)
+		return -ENOMEM;
+
+	hsw->dev = dev;
+
+	ipc = &hsw->ipc;
+	ipc->dev = dev;
+
+	/* set up ops depending on hardware */
+	switch (pdata->id) {
+	case SST_DEV_ID_BYT:
+		/* Baytrail */
+		dsp_dev = &byt_dev;
+		dsp_dev->thread_context = hsw;
+		ipc->ops.tx_msg = byt_tx_msg;
+		ipc->ops.shim_dbg = byt_shim_dbg;
+		ipc->ops.tx_data_copy = hsw_tx_data_copy;
+		ipc->ops.reply_msg_match = hsw_reply_msg_match;
+		ipc->ops.is_dsp_busy = byt_is_dsp_busy;
+		ipc->ops.dsp_notify = byt_notify;
+		break;
+	case SST_DEV_ID_LYNX_POINT:
+	case SST_DEV_ID_WILDCAT_POINT:
+		/* Haswell / Broadwell */
+		dsp_dev = &hsw_dev;
+		dsp_dev->thread_context = hsw;
+		ipc->ops.tx_msg = hsw_tx_msg;
+		ipc->ops.shim_dbg = hsw_shim_dbg;
+		ipc->ops.tx_data_copy = hsw_tx_data_copy;
+		ipc->ops.reply_msg_match = hsw_reply_msg_match;
+		ipc->ops.is_dsp_busy = hsw_is_dsp_busy;
+		ipc->ops.dsp_notify = hsw_notify;
+		break;
+	default:
+		ret = -EINVAL;
+		dev_err(dev, "error: unsupported DSP ID 0x%x\n", pdata->id);
+		goto ipc_init_err;
+	}
+
+	ipc->tx_data_max_size = IPC_MAX_MAILBOX_BYTES;
+	ipc->rx_data_max_size = IPC_MAX_MAILBOX_BYTES;
+
+	ret = sst_ipc_init(ipc);
+	if (ret != 0)
+		goto ipc_init_err;
+
+	INIT_LIST_HEAD(&hsw->stream_list);
+	init_waitqueue_head(&hsw->boot_wait);
+
+	/* init SST shim */
+	hsw->dsp = sst_dsp_new(dev, dsp_dev, pdata);
+	if (hsw->dsp == NULL) {
+		ret = -ENODEV;
+		goto dsp_new_err;
+	}
+
+	ipc->dsp = hsw->dsp;
+
+	/* allocate DMA buffer for context storage */
+	hsw->dx_context = dma_alloc_coherent(hsw->dsp->dma_dev,
+		SST_HSW_DX_CONTEXT_SIZE, &hsw->dx_context_paddr, GFP_KERNEL);
+	if (hsw->dx_context == NULL) {
+		ret = -ENOMEM;
+		goto dma_err;
+	}
+
+	/* keep the DSP in reset state for base FW loading */
+	sst_dsp_reset(hsw->dsp);
+
+	/* load base module and other modules in base firmware image */
+	ret = sst_hsw_module_load(hsw, SST_HSW_MODULE_BASE_FW, 0, "Base");
+	if (ret < 0)
+		goto fw_err;
+
+	/* try to load module waves */
+	sst_hsw_module_load(hsw, SST_HSW_MODULE_WAVES, 0, "intel/IntcPP01.bin");
+
+	/* allocate scratch mem regions */
+	ret = sst_block_alloc_scratch(hsw->dsp);
+	if (ret < 0)
+		goto boot_err;
+
+	/* init param buffer */
+	sst_hsw_reset_param_buf(hsw);
+
+	/* wait for DSP boot completion */
+	sst_dsp_boot(hsw->dsp);
+	ret = wait_event_timeout(hsw->boot_wait, hsw->boot_complete,
+		msecs_to_jiffies(IPC_BOOT_MSECS));
+	if (ret == 0) {
+		ret = -EIO;
+		ipc->ops.shim_dbg(ipc, "DSP boot timeout");
+		goto boot_err;
+	}
+
+	hsw_debugfs_init(hsw);
+
+	/* init module state after boot */
+	sst_hsw_init_module_state(hsw);
+
+	/* get the FW version */
+	sst_hsw_fw_get_version(hsw, &version);
+
+	/* get the globalmixer */
+	ret = sst_hsw_mixer_get_info(hsw);
+	if (ret < 0) {
+		dev_err(hsw->dev, "error: failed to get stream info\n");
+		goto boot_err;
+	}
+
+	pdata->dsp = hsw;
+	return 0;
+
+boot_err:
+	sst_dsp_reset(hsw->dsp);
+	sst_fw_free_all(hsw->dsp);
+fw_err:
+	dma_free_coherent(hsw->dsp->dma_dev, SST_HSW_DX_CONTEXT_SIZE,
+			hsw->dx_context, hsw->dx_context_paddr);
+dma_err:
+	sst_dsp_free(hsw->dsp);
+dsp_new_err:
+	sst_ipc_fini(ipc);
+ipc_init_err:
+	return ret;
+}
+EXPORT_SYMBOL_GPL(sst_hsw_dsp_init);
+
+void sst_hsw_dsp_free(struct device *dev, struct sst_pdata *pdata)
+{
+	struct sst_hsw *hsw = pdata->dsp;
+
+	snd_dma_free_pages(&hsw->trace_dma_descriptor);
+	snd_dma_free_pages(&hsw->dtrace_buffer);
+	sst_dsp_reset(hsw->dsp);
+	sst_fw_free_all(hsw->dsp);
+	dma_free_coherent(hsw->dsp->dma_dev, SST_HSW_DX_CONTEXT_SIZE,
+			hsw->dx_context, hsw->dx_context_paddr);
+	sst_dsp_free(hsw->dsp);
+	sst_ipc_fini(&hsw->ipc);
+}
+EXPORT_SYMBOL_GPL(sst_hsw_dsp_free);
diff --git a/sound/soc/sof/loader.c b/sound/soc/sof/loader.c
new file mode 100644
index 0000000..5ef9c43
--- /dev/null
+++ b/sound/soc/sof/loader.c
@@ -0,0 +1,126 @@
+/*
+ * Intel Baytrail SST DSP driver
+ * Copyright (c) 2014, Intel Corporation.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope 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/delay.h>
+#include <linux/fs.h>
+#include <linux/slab.h>
+#include <linux/device.h>
+#include <linux/interrupt.h>
+#include <linux/module.h>
+#include <linux/dma-mapping.h>
+#include <linux/platform_device.h>
+#include <linux/firmware.h>
+
+#include "sof.h"
+
+#include <trace/events/hswadsp.h>
+
+static int hsw_parse_module(struct snd_soc_sof *sof,
+	struct fw_module_header *module)
+{
+	struct dma_block_info *block;
+	int count, ret;
+	void __iomem *ram;
+
+
+	dev_dbg(sof->dev, "new module sign 0x%s size 0x%x blocks 0x%x type 0x%x\n",
+		module->signature, module->mod_size,
+		module->blocks, module->type);
+	dev_dbg(sof->dev, " entrypoint 0x%x\n", module->entry_point);
+	dev_dbg(sof->dev, " persistent 0x%x scratch 0x%x\n",
+		module->info.persistent_size, module->info.scratch_size);
+
+	block = (void *)module + sizeof(*module);
+
+	for (count = 0; count < module->blocks; count++) {
+
+		if (block->size <= 0) {
+			dev_err(sof->dev,
+				"error: block %d size invalid\n", count);
+			return -EINVAL;
+		}
+
+		switch (block->type) {
+		case SST_HSW_IRAM:
+			ram = dsp->addr.lpe;
+			mod->offset =
+				block->ram_offset + dsp->addr.iram_offset;
+			mod->type = SST_MEM_IRAM;
+			break;
+		case SST_HSW_DRAM:
+		case SST_HSW_REGS:
+			ram = dsp->addr.lpe;
+			mod->offset = block->ram_offset + dsp->addr.dram_offset;
+			mod->type = SST_MEM_DRAM;
+			break;
+		default:
+			dev_err(sof->dev, "error: bad type 0x%x for block 0x%x\n",
+				block->type, count);
+			return -EINVAL;
+		}
+
+		mod->size = block->size;
+		mod->data = (void *)block + sizeof(*block);
+		mod->data_offset = mod->data - fw->dma_buf;
+
+		dev_dbg(sof->dev, "module block %d type 0x%x "
+			"size 0x%x ==> ram %p offset 0x%x\n",
+			count, mod->type, block->size, ram,
+			block->ram_offset);
+
+		// COPY block
+
+		block = (void *)block + sizeof(*block) + block->size;
+	}
+	mod->state = SST_MODULE_STATE_LOADED;
+
+	return 0;
+}
+
+static int hsw_parse_fw_image(struct snd_soc_sof *sof)
+{
+	struct fw_header *header;
+	struct fw_module_header *module;
+	int ret, count;
+
+	/* Read the header information from the data pointer */
+	header = (struct fw_header *)sst_fw->dma_buf;
+
+	/* verify FW */
+	if ((strncmp(header->signature, SST_HSW_FW_SIGN, 4) != 0) ||
+		(sst_fw->size != header->file_size + sizeof(*header))) {
+		dev_err(sof->dev, "error: invalid fw sign/filesize mismatch got 0x%x expected 0x%lx\n",
+			sst_fw->size, header->file_size + sizeof(*header));
+		return -EINVAL;
+	}
+
+	dev_dbg(sof->dev, "header size=0x%x modules=0x%x fmt=0x%x size=%zu\n",
+		header->file_size, header->modules,
+		header->file_format, sizeof(*header));
+
+	/* parse each module */
+	module = (void *)sst_fw->dma_buf + sizeof(*header);
+	for (count = 0; count < header->modules; count++) {
+
+		/* module */
+		ret = hsw_parse_module(sof, module);
+		if (ret < 0) {
+			dev_err(sof->dev, "error: invalid module %d\n", count);
+			return ret;
+		}
+		module = (void *)module + sizeof(*module) + module->mod_size;
+	}
+
+	return 0;
+}
diff --git a/sound/soc/sof/pcm.c b/sound/soc/sof/pcm.c
new file mode 100644
index 0000000..73c9efd
--- /dev/null
+++ b/sound/soc/sof/pcm.c
@@ -0,0 +1,1413 @@
+/*
+ * Intel SST Haswell/Broadwell PCM Support
+ *
+ * Copyright (C) 2013, 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/module.h>
+#include <linux/dma-mapping.h>
+#include <linux/slab.h>
+#include <linux/delay.h>
+#include <linux/pm_runtime.h>
+#include <asm/page.h>
+#include <asm/pgtable.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/dmaengine_pcm.h>
+#include <sound/soc.h>
+#include <sound/tlv.h>
+#include <sound/compress_driver.h>
+
+#include "../haswell/sst-haswell-ipc.h"
+#include "../common/sst-dsp-priv.h"
+#include "../common/sst-dsp.h"
+
+#define HSW_PCM_COUNT		2
+#define HSW_VOLUME_MAX		(1 << 16)	/* 0dB */
+
+#define SST_OLD_POSITION(d, r, o) ((d) +		\
+			frames_to_bytes(r, o))
+#define SST_SAMPLES(r, x) (bytes_to_samples(r,	\
+			frames_to_bytes(r, (x))))
+
+/* simple volume table */
+static const u32 volume_map[] = {
+	HSW_VOLUME_MAX >> 16,
+	HSW_VOLUME_MAX >> 15,
+	HSW_VOLUME_MAX >> 14,
+	HSW_VOLUME_MAX >> 13,
+	HSW_VOLUME_MAX >> 12,
+	HSW_VOLUME_MAX >> 11,
+	HSW_VOLUME_MAX >> 10,
+	HSW_VOLUME_MAX >> 9,
+	HSW_VOLUME_MAX >> 8,
+	HSW_VOLUME_MAX >> 7,
+	HSW_VOLUME_MAX >> 6,
+	HSW_VOLUME_MAX >> 5,
+	HSW_VOLUME_MAX >> 4,
+	HSW_VOLUME_MAX >> 3,
+	HSW_VOLUME_MAX >> 2,
+	HSW_VOLUME_MAX >> 1,
+	HSW_VOLUME_MAX >> 0,
+};
+
+#define HSW_PCM_PERIODS_MAX	64
+#define HSW_PCM_PERIODS_MIN	2
+
+#define HSW_PCM_DAI_ID_SYSTEM	0
+#define HSW_PCM_DAI_ID_OFFLOAD0	1
+#define HSW_PCM_DAI_ID_OFFLOAD1	2
+#define HSW_PCM_DAI_ID_LOOPBACK	3
+
+
+static const struct snd_pcm_hardware hsw_pcm_hardware = {
+	.info			= SNDRV_PCM_INFO_MMAP |
+				  SNDRV_PCM_INFO_MMAP_VALID |
+				  SNDRV_PCM_INFO_INTERLEAVED |
+				  SNDRV_PCM_INFO_PAUSE |
+				  SNDRV_PCM_INFO_RESUME |
+				  SNDRV_PCM_INFO_NO_PERIOD_WAKEUP |
+				  SNDRV_PCM_INFO_DRAIN_TRIGGER,
+	.formats		= SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE |
+				  SNDRV_PCM_FMTBIT_S32_LE,
+	.period_bytes_min	= PAGE_SIZE,
+	.period_bytes_max	= (HSW_PCM_PERIODS_MAX / HSW_PCM_PERIODS_MIN) * PAGE_SIZE,
+	.periods_min		= HSW_PCM_PERIODS_MIN,
+	.periods_max		= HSW_PCM_PERIODS_MAX,
+	.buffer_bytes_max	= HSW_PCM_PERIODS_MAX * PAGE_SIZE,
+};
+
+struct hsw_pcm_module_map {
+	int dai_id;
+	int stream;
+	enum sst_hsw_module_id mod_id;
+};
+
+/* private data for each PCM DSP stream */
+struct hsw_pcm_data {
+	int dai_id;
+	struct sst_hsw_stream *stream;
+	struct sst_module_runtime *runtime;
+	struct sst_module_runtime_context context;
+	struct snd_pcm *hsw_pcm;
+	u32 volume[2];
+	struct snd_pcm_substream *substream;
+	struct snd_compr_stream *cstream;
+	unsigned int wpos;
+	struct mutex mutex;
+	bool allocated;
+	int persistent_offset;
+};
+
+enum hsw_pm_state {
+	HSW_PM_STATE_D0 = 0,
+	HSW_PM_STATE_RTD3 = 1,
+	HSW_PM_STATE_D3 = 2,
+};
+
+/* private data for the driver */
+struct hsw_priv_data {
+	/* runtime DSP */
+	struct sst_hsw *hsw;
+	struct device *dev;
+	enum hsw_pm_state pm_state;
+	struct snd_soc_card *soc_card;
+	struct sst_module_runtime *runtime_waves; /* sound effect module */
+
+	/* page tables */
+	struct snd_dma_buffer dmab[HSW_PCM_COUNT][2];
+
+	/* DAI data */
+	struct hsw_pcm_data pcm[HSW_PCM_COUNT][2];
+};
+
+
+/* static mappings between PCMs and modules - may be dynamic in future */
+static struct hsw_pcm_module_map mod_map[] = {
+	{HSW_PCM_DAI_ID_SYSTEM, 0, SST_HSW_MODULE_PCM_SYSTEM},
+	{HSW_PCM_DAI_ID_OFFLOAD0, 0, SST_HSW_MODULE_PCM},
+	{HSW_PCM_DAI_ID_OFFLOAD1, 0, SST_HSW_MODULE_PCM},
+	{HSW_PCM_DAI_ID_LOOPBACK, 1, SST_HSW_MODULE_PCM_REFERENCE},
+	{HSW_PCM_DAI_ID_SYSTEM, 1, SST_HSW_MODULE_PCM_CAPTURE},
+};
+
+static u32 hsw_notify_pointer(struct sst_hsw_stream *stream, void *data);
+
+static inline u32 hsw_mixer_to_ipc(unsigned int value)
+{
+	if (value >= ARRAY_SIZE(volume_map))
+		return volume_map[0];
+	else
+		return volume_map[value];
+}
+
+static inline unsigned int hsw_ipc_to_mixer(u32 value)
+{
+	int i;
+
+	for (i = 0; i < ARRAY_SIZE(volume_map); i++) {
+		if (volume_map[i] >= value)
+			return i;
+	}
+
+	return i - 1;
+}
+
+static int hsw_stream_volume_put(struct snd_kcontrol *kcontrol,
+				struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_soc_platform *platform = snd_soc_kcontrol_platform(kcontrol);
+	struct soc_mixer_control *mc =
+		(struct soc_mixer_control *)kcontrol->private_value;
+	struct hsw_priv_data *pdata =
+		snd_soc_platform_get_drvdata(platform);
+	struct hsw_pcm_data *pcm_data;
+	struct sst_hsw *hsw = pdata->hsw;
+	u32 volume;
+	int dai, stream;
+
+	dai = mod_map[mc->reg].dai_id;
+	stream = mod_map[mc->reg].stream;
+	pcm_data = &pdata->pcm[dai][stream];
+
+	mutex_lock(&pcm_data->mutex);
+	pm_runtime_get_sync(pdata->dev);
+
+	if (!pcm_data->stream) {
+		pcm_data->volume[0] =
+			hsw_mixer_to_ipc(ucontrol->value.integer.value[0]);
+		pcm_data->volume[1] =
+			hsw_mixer_to_ipc(ucontrol->value.integer.value[1]);
+		pm_runtime_mark_last_busy(pdata->dev);
+		pm_runtime_put_autosuspend(pdata->dev);
+		mutex_unlock(&pcm_data->mutex);
+		return 0;
+	}
+
+	if (ucontrol->value.integer.value[0] ==
+		ucontrol->value.integer.value[1]) {
+		volume = hsw_mixer_to_ipc(ucontrol->value.integer.value[0]);
+		/* apply volume value to all channels */
+		sst_hsw_stream_set_volume(hsw, pcm_data->stream, 0, SST_HSW_CHANNELS_ALL, volume);
+	} else {
+		volume = hsw_mixer_to_ipc(ucontrol->value.integer.value[0]);
+		sst_hsw_stream_set_volume(hsw, pcm_data->stream, 0, 0, volume);
+		volume = hsw_mixer_to_ipc(ucontrol->value.integer.value[1]);
+		sst_hsw_stream_set_volume(hsw, pcm_data->stream, 0, 1, volume);
+	}
+
+	pm_runtime_mark_last_busy(pdata->dev);
+	pm_runtime_put_autosuspend(pdata->dev);
+	mutex_unlock(&pcm_data->mutex);
+	return 0;
+}
+
+static int hsw_stream_volume_get(struct snd_kcontrol *kcontrol,
+				struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_soc_platform *platform = snd_soc_kcontrol_platform(kcontrol);
+	struct soc_mixer_control *mc =
+		(struct soc_mixer_control *)kcontrol->private_value;
+	struct hsw_priv_data *pdata =
+		snd_soc_platform_get_drvdata(platform);
+	struct hsw_pcm_data *pcm_data;
+	struct sst_hsw *hsw = pdata->hsw;
+	u32 volume;
+	int dai, stream;
+
+	dai = mod_map[mc->reg].dai_id;
+	stream = mod_map[mc->reg].stream;
+	pcm_data = &pdata->pcm[dai][stream];
+
+	mutex_lock(&pcm_data->mutex);
+	pm_runtime_get_sync(pdata->dev);
+
+	if (!pcm_data->stream) {
+		ucontrol->value.integer.value[0] =
+			hsw_ipc_to_mixer(pcm_data->volume[0]);
+		ucontrol->value.integer.value[1] =
+			hsw_ipc_to_mixer(pcm_data->volume[1]);
+		pm_runtime_mark_last_busy(pdata->dev);
+		pm_runtime_put_autosuspend(pdata->dev);
+		mutex_unlock(&pcm_data->mutex);
+		return 0;
+	}
+
+	sst_hsw_stream_get_volume(hsw, pcm_data->stream, 0, 0, &volume);
+	ucontrol->value.integer.value[0] = hsw_ipc_to_mixer(volume);
+	sst_hsw_stream_get_volume(hsw, pcm_data->stream, 0, 1, &volume);
+	ucontrol->value.integer.value[1] = hsw_ipc_to_mixer(volume);
+
+	pm_runtime_mark_last_busy(pdata->dev);
+	pm_runtime_put_autosuspend(pdata->dev);
+	mutex_unlock(&pcm_data->mutex);
+
+	return 0;
+}
+
+static int hsw_volume_put(struct snd_kcontrol *kcontrol,
+				struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_soc_platform *platform = snd_soc_kcontrol_platform(kcontrol);
+	struct hsw_priv_data *pdata = snd_soc_platform_get_drvdata(platform);
+	struct sst_hsw *hsw = pdata->hsw;
+	u32 volume;
+
+	pm_runtime_get_sync(pdata->dev);
+
+	if (ucontrol->value.integer.value[0] ==
+		ucontrol->value.integer.value[1]) {
+
+		volume = hsw_mixer_to_ipc(ucontrol->value.integer.value[0]);
+		sst_hsw_mixer_set_volume(hsw, 0, SST_HSW_CHANNELS_ALL, volume);
+
+	} else {
+		volume = hsw_mixer_to_ipc(ucontrol->value.integer.value[0]);
+		sst_hsw_mixer_set_volume(hsw, 0, 0, volume);
+
+		volume = hsw_mixer_to_ipc(ucontrol->value.integer.value[1]);
+		sst_hsw_mixer_set_volume(hsw, 0, 1, volume);
+	}
+
+	pm_runtime_mark_last_busy(pdata->dev);
+	pm_runtime_put_autosuspend(pdata->dev);
+	return 0;
+}
+
+static int hsw_volume_get(struct snd_kcontrol *kcontrol,
+				struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_soc_platform *platform = snd_soc_kcontrol_platform(kcontrol);
+	struct hsw_priv_data *pdata = snd_soc_platform_get_drvdata(platform);
+	struct sst_hsw *hsw = pdata->hsw;
+	unsigned int volume = 0;
+
+	pm_runtime_get_sync(pdata->dev);
+	sst_hsw_mixer_get_volume(hsw, 0, 0, &volume);
+	ucontrol->value.integer.value[0] = hsw_ipc_to_mixer(volume);
+
+	sst_hsw_mixer_get_volume(hsw, 0, 1, &volume);
+	ucontrol->value.integer.value[1] = hsw_ipc_to_mixer(volume);
+
+	pm_runtime_mark_last_busy(pdata->dev);
+	pm_runtime_put_autosuspend(pdata->dev);
+	return 0;
+}
+
+static int hsw_waves_switch_get(struct snd_kcontrol *kcontrol,
+				struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_soc_platform *platform = snd_soc_kcontrol_platform(kcontrol);
+	struct hsw_priv_data *pdata = snd_soc_platform_get_drvdata(platform);
+	struct sst_hsw *hsw = pdata->hsw;
+	enum sst_hsw_module_id id = SST_HSW_MODULE_WAVES;
+
+	ucontrol->value.integer.value[0] =
+		(sst_hsw_is_module_active(hsw, id) ||
+		sst_hsw_is_module_enabled_rtd3(hsw, id));
+	return 0;
+}
+
+static int hsw_waves_switch_put(struct snd_kcontrol *kcontrol,
+				struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_soc_platform *platform = snd_soc_kcontrol_platform(kcontrol);
+	struct hsw_priv_data *pdata = snd_soc_platform_get_drvdata(platform);
+	struct sst_hsw *hsw = pdata->hsw;
+	int ret = 0;
+	enum sst_hsw_module_id id = SST_HSW_MODULE_WAVES;
+	bool switch_on = (bool)ucontrol->value.integer.value[0];
+
+	/* if module is in RAM on the DSP, apply user settings to module through
+	 * ipc. If module is not in RAM on the DSP, store user setting for
+	 * track */
+	if (sst_hsw_is_module_loaded(hsw, id)) {
+		if (switch_on == sst_hsw_is_module_active(hsw, id))
+			return 0;
+
+		if (switch_on)
+			ret = sst_hsw_module_enable(hsw, id, 0);
+		else
+			ret = sst_hsw_module_disable(hsw, id, 0);
+	} else {
+		if (switch_on == sst_hsw_is_module_enabled_rtd3(hsw, id))
+			return 0;
+
+		if (switch_on)
+			sst_hsw_set_module_enabled_rtd3(hsw, id);
+		else
+			sst_hsw_set_module_disabled_rtd3(hsw, id);
+	}
+
+	return ret;
+}
+
+static int hsw_waves_param_get(struct snd_kcontrol *kcontrol,
+				struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_soc_platform *platform = snd_soc_kcontrol_platform(kcontrol);
+	struct hsw_priv_data *pdata = snd_soc_platform_get_drvdata(platform);
+	struct sst_hsw *hsw = pdata->hsw;
+
+	/* return a matching line from param buffer */
+	return sst_hsw_load_param_line(hsw, ucontrol->value.bytes.data);
+}
+
+static int hsw_waves_param_put(struct snd_kcontrol *kcontrol,
+				struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_soc_platform *platform = snd_soc_kcontrol_platform(kcontrol);
+	struct hsw_priv_data *pdata = snd_soc_platform_get_drvdata(platform);
+	struct sst_hsw *hsw = pdata->hsw;
+	int ret;
+	enum sst_hsw_module_id id = SST_HSW_MODULE_WAVES;
+	int param_id = ucontrol->value.bytes.data[0];
+	int param_size = WAVES_PARAM_COUNT;
+
+	/* clear param buffer and reset buffer index */
+	if (param_id == 0xFF) {
+		sst_hsw_reset_param_buf(hsw);
+		return 0;
+	}
+
+	/* store params into buffer */
+	ret = sst_hsw_store_param_line(hsw, ucontrol->value.bytes.data);
+	if (ret < 0)
+		return ret;
+
+	if (sst_hsw_is_module_active(hsw, id))
+		ret = sst_hsw_module_set_param(hsw, id, 0, param_id,
+				param_size, ucontrol->value.bytes.data);
+	return ret;
+}
+
+static int hsw_loopback_switch_get(struct snd_kcontrol *kcontrol,
+				struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_soc_platform *platform = snd_soc_kcontrol_platform(kcontrol);
+	struct hsw_priv_data *pdata = snd_soc_platform_get_drvdata(platform);
+	struct sst_hsw *hsw = pdata->hsw;
+
+	ucontrol->value.integer.value[0] = sst_hsw_device_get_loopback(hsw);
+	return 0;
+}
+
+static int hsw_loopback_switch_put(struct snd_kcontrol *kcontrol,
+				struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_soc_platform *platform = snd_soc_kcontrol_platform(kcontrol);
+	struct hsw_priv_data *pdata = snd_soc_platform_get_drvdata(platform);
+	struct sst_hsw *hsw = pdata->hsw;
+	bool switch_on = (bool)ucontrol->value.integer.value[0];
+
+	return sst_hsw_device_set_loopback(hsw, switch_on);
+}
+
+/* TLV used by both global and stream volumes */
+static const DECLARE_TLV_DB_SCALE(hsw_vol_tlv, -4800, 300, 1);
+
+/* System Pin has no volume control */
+static const struct snd_kcontrol_new hsw_volume_controls[] = {
+	/* Global DSP volume */
+	SOC_DOUBLE_EXT_TLV("Master Playback Volume", 0, 0, 8,
+		ARRAY_SIZE(volume_map) - 1, 0,
+		hsw_volume_get, hsw_volume_put, hsw_vol_tlv),
+	/* PCM0 volume */
+	SOC_DOUBLE_EXT_TLV("PCM0 Playback Volume", 0, 0, 8,
+		ARRAY_SIZE(volume_map) - 1, 0,
+		hsw_stream_volume_get, hsw_stream_volume_put, hsw_vol_tlv),
+	/* PCM1 volume */
+	SOC_DOUBLE_EXT_TLV("PCM1 Playback Volume", 1, 0, 8,
+		ARRAY_SIZE(volume_map) - 1, 0,
+		hsw_stream_volume_get, hsw_stream_volume_put, hsw_vol_tlv),
+	/* Mic Capture volume */
+	SOC_DOUBLE_EXT_TLV("Mic Capture Volume", 4, 0, 8,
+		ARRAY_SIZE(volume_map) - 1, 0,
+		hsw_stream_volume_get, hsw_stream_volume_put, hsw_vol_tlv),
+	/* enable/disable module waves */
+	SOC_SINGLE_BOOL_EXT("Loopback Switch", 0,
+		hsw_loopback_switch_get, hsw_loopback_switch_put),
+};
+
+/* Create DMA buffer page table for DSP */
+static int create_adsp_page_table(struct snd_pcm_substream *substream,
+	struct hsw_priv_data *pdata, struct snd_soc_pcm_runtime *rtd,
+	unsigned char *dma_area, size_t size, int pcm)
+{
+	struct snd_dma_buffer *dmab = snd_pcm_get_dma_buf(substream);
+	int i, pages, stream = substream->stream;
+
+	pages = snd_sgbuf_aligned_pages(size);
+
+	dev_dbg(rtd->dev, "generating page table for %p size 0x%zx pages %d\n",
+		dma_area, size, pages);
+
+	for (i = 0; i < pages; i++) {
+		u32 idx = (((i << 2) + i)) >> 1;
+		u32 pfn = snd_sgbuf_get_addr(dmab, i * PAGE_SIZE) >> PAGE_SHIFT;
+		u32 *pg_table;
+
+		dev_dbg(rtd->dev, "pfn i %i idx %d pfn %x\n", i, idx, pfn);
+
+		pg_table = (u32 *)(pdata->dmab[pcm][stream].area + idx);
+
+		if (i & 1)
+			*pg_table |= (pfn << 4);
+		else
+			*pg_table |= pfn;
+	}
+
+	return 0;
+}
+
+/* this may get called several times by oss emulation */
+static int hsw_pcm_hw_params(struct snd_pcm_substream *substream,
+			      struct snd_pcm_hw_params *params)
+{
+	struct snd_soc_pcm_runtime *rtd = substream->private_data;
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	struct hsw_priv_data *pdata =
+		snd_soc_platform_get_drvdata(rtd->platform);
+	struct hsw_pcm_data *pcm_data;
+	struct sst_hsw *hsw = pdata->hsw;
+	struct sst_module *module_data;
+	struct sst_dsp *dsp;
+	struct snd_dma_buffer *dmab;
+	enum sst_hsw_stream_type stream_type;
+	enum sst_hsw_stream_path_id path_id;
+	u32 rate, bits, map, pages, module_id;
+	u8 channels;
+	int ret, dai;
+
+	dai = mod_map[rtd->cpu_dai->id].dai_id;
+	pcm_data = &pdata->pcm[dai][substream->stream];
+
+	/* check if we are being called a subsequent time */
+	if (pcm_data->allocated) {
+		ret = sst_hsw_stream_reset(hsw, pcm_data->stream);
+		if (ret < 0)
+			dev_dbg(rtd->dev, "error: reset stream failed %d\n",
+				ret);
+
+		ret = sst_hsw_stream_free(hsw, pcm_data->stream);
+		if (ret < 0) {
+			dev_dbg(rtd->dev, "error: free stream failed %d\n",
+				ret);
+			return ret;
+		}
+		pcm_data->allocated = false;
+
+		pcm_data->stream = sst_hsw_stream_new(hsw, rtd->cpu_dai->id,
+			hsw_notify_pointer, pcm_data);
+		if (pcm_data->stream == NULL) {
+			dev_err(rtd->dev, "error: failed to create stream\n");
+			return -EINVAL;
+		}
+	}
+
+	/* stream direction */
+	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+		path_id = SST_HSW_STREAM_PATH_SSP0_OUT;
+	else
+		path_id = SST_HSW_STREAM_PATH_SSP0_IN;
+
+	/* DSP stream type depends on DAI ID */
+	switch (rtd->cpu_dai->id) {
+	case 0:
+		if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+			stream_type = SST_HSW_STREAM_TYPE_SYSTEM;
+			module_id = SST_HSW_MODULE_PCM_SYSTEM;
+		}
+		else {
+			stream_type = SST_HSW_STREAM_TYPE_CAPTURE;
+			module_id = SST_HSW_MODULE_PCM_CAPTURE;
+		}
+		break;
+	case 1:
+	case 2:
+		stream_type = SST_HSW_STREAM_TYPE_RENDER;
+		module_id = SST_HSW_MODULE_PCM;
+		break;
+	case 3:
+		/* path ID needs to be OUT for loopback */
+		stream_type = SST_HSW_STREAM_TYPE_LOOPBACK;
+		path_id = SST_HSW_STREAM_PATH_SSP0_OUT;
+		module_id = SST_HSW_MODULE_PCM_REFERENCE;
+		break;
+	default:
+		dev_err(rtd->dev, "error: invalid DAI ID %d\n",
+			rtd->cpu_dai->id);
+		return -EINVAL;
+	};
+
+	ret = sst_hsw_stream_format(hsw, pcm_data->stream,
+		path_id, stream_type, SST_HSW_STREAM_FORMAT_PCM_FORMAT);
+	if (ret < 0) {
+		dev_err(rtd->dev, "error: failed to set format %d\n", ret);
+		return ret;
+	}
+
+	rate = params_rate(params);
+	ret = sst_hsw_stream_set_rate(hsw, pcm_data->stream, rate);
+	if (ret < 0) {
+		dev_err(rtd->dev, "error: could not set rate %d\n", rate);
+		return ret;
+	}
+
+	switch (params_format(params)) {
+	case SNDRV_PCM_FORMAT_S16_LE:
+		bits = SST_HSW_DEPTH_16BIT;
+		sst_hsw_stream_set_valid(hsw, pcm_data->stream, 16);
+		break;
+	case SNDRV_PCM_FORMAT_S24_LE:
+		bits = SST_HSW_DEPTH_32BIT;
+		sst_hsw_stream_set_valid(hsw, pcm_data->stream, 24);
+		break;
+	case SNDRV_PCM_FORMAT_S8:
+		bits = SST_HSW_DEPTH_8BIT;
+		sst_hsw_stream_set_valid(hsw, pcm_data->stream, 8);
+		break;
+	case SNDRV_PCM_FORMAT_S32_LE:
+		bits = SST_HSW_DEPTH_32BIT;
+		sst_hsw_stream_set_valid(hsw, pcm_data->stream, 32);
+		break;
+	default:
+		dev_err(rtd->dev, "error: invalid format %d\n",
+			params_format(params));
+		return -EINVAL;
+	}
+
+	ret = sst_hsw_stream_set_bits(hsw, pcm_data->stream, bits);
+	if (ret < 0) {
+		dev_err(rtd->dev, "error: could not set bits %d\n", bits);
+		return ret;
+	}
+
+	channels = params_channels(params);
+	map = create_channel_map(SST_HSW_CHANNEL_CONFIG_STEREO);
+	sst_hsw_stream_set_map_config(hsw, pcm_data->stream,
+			map, SST_HSW_CHANNEL_CONFIG_STEREO);
+
+	ret = sst_hsw_stream_set_channels(hsw, pcm_data->stream, channels);
+	if (ret < 0) {
+		dev_err(rtd->dev, "error: could not set channels %d\n",
+			channels);
+		return ret;
+	}
+
+	ret = snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(params));
+	if (ret < 0) {
+		dev_err(rtd->dev, "error: could not allocate %d bytes for PCM %d\n",
+			params_buffer_bytes(params), ret);
+		return ret;
+	}
+
+	dmab = snd_pcm_get_dma_buf(substream);
+
+	ret = create_adsp_page_table(substream, pdata, rtd, runtime->dma_area,
+		runtime->dma_bytes, rtd->cpu_dai->id);
+	if (ret < 0)
+		return ret;
+
+	sst_hsw_stream_set_style(hsw, pcm_data->stream,
+		SST_HSW_INTERLEAVING_PER_CHANNEL);
+
+	sst_hsw_stream_set_period(hsw, pcm_data->stream, params_period_size(params));
+
+	if (runtime->dma_bytes % PAGE_SIZE)
+		pages = (runtime->dma_bytes / PAGE_SIZE) + 1;
+	else
+		pages = runtime->dma_bytes / PAGE_SIZE;
+
+	ret = sst_hsw_stream_buffer(hsw, pcm_data->stream,
+		pdata->dmab[rtd->cpu_dai->id][substream->stream].addr,
+		pages, runtime->dma_bytes, 0,
+		snd_sgbuf_get_addr(dmab, 0) >> PAGE_SHIFT);
+	if (ret < 0) {
+		dev_err(rtd->dev, "error: failed to set DMA buffer %d\n", ret);
+		return ret;
+	}
+
+	dsp = sst_hsw_get_dsp(hsw);
+
+	// TODO: we are not actually using the module config anymore ??
+	module_data = sst_module_get_from_id(dsp, module_id);
+	if (module_data == NULL) {
+		//dev_err(rtd->dev, "error: failed to get module config\n");
+		//return -EINVAL;
+	}
+
+	sst_hsw_stream_set_module_info(hsw, pcm_data->stream,
+		pcm_data->runtime);
+
+	ret = sst_hsw_stream_commit(hsw, pcm_data->stream);
+	if (ret < 0) {
+		dev_err(rtd->dev, "error: failed to commit stream %d\n", ret);
+		return ret;
+	}
+
+#if 0
+	if (!pcm_data->allocated) {
+		/* Set previous saved volume */
+		sst_hsw_stream_set_volume(hsw, pcm_data->stream, 0,
+				0, pcm_data->volume[0]);
+		sst_hsw_stream_set_volume(hsw, pcm_data->stream, 0,
+				1, pcm_data->volume[1]);
+		pcm_data->allocated = true;
+	}
+
+//	ret = sst_hsw_stream_pause(hsw, pcm_data->stream, 1);
+	//if (ret < 0)
+	//	dev_err(rtd->dev, "error: failed to pause %d\n", ret);
+#endif
+	return 0;
+}
+
+static int hsw_pcm_hw_free(struct snd_pcm_substream *substream)
+{
+	snd_pcm_lib_free_pages(substream);
+	return 0;
+}
+
+static int hsw_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
+{
+	struct snd_soc_pcm_runtime *rtd = substream->private_data;
+	struct hsw_priv_data *pdata =
+		snd_soc_platform_get_drvdata(rtd->platform);
+	struct hsw_pcm_data *pcm_data;
+	struct sst_hsw_stream *sst_stream;
+	struct sst_hsw *hsw = pdata->hsw;
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	snd_pcm_uframes_t pos;
+	int dai;
+
+	dai = mod_map[rtd->cpu_dai->id].dai_id;
+	pcm_data = &pdata->pcm[dai][substream->stream];
+	sst_stream = pcm_data->stream;
+
+	switch (cmd) {
+	case SNDRV_PCM_TRIGGER_START:
+	case SNDRV_PCM_TRIGGER_RESUME:
+	case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+		sst_hsw_stream_set_silence_start(hsw, sst_stream, false);
+		sst_hsw_stream_resume(hsw, pcm_data->stream, 0);
+		break;
+	case SNDRV_PCM_TRIGGER_STOP:
+	case SNDRV_PCM_TRIGGER_SUSPEND:
+	case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+		sst_hsw_stream_set_silence_start(hsw, sst_stream, false);
+		sst_hsw_stream_pause(hsw, pcm_data->stream, 0);
+		break;
+	case SNDRV_PCM_TRIGGER_DRAIN:
+		pos = runtime->control->appl_ptr % runtime->buffer_size;
+		sst_hsw_stream_set_old_position(hsw, pcm_data->stream, pos);
+		sst_hsw_stream_set_silence_start(hsw, sst_stream, true);
+		break;
+	default:
+		break;
+	}
+
+	return 0;
+}
+
+static u32 hsw_notify_pointer(struct sst_hsw_stream *stream, void *data)
+{
+	struct hsw_pcm_data *pcm_data = data;
+	struct snd_pcm_substream *substream = pcm_data->substream;
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	struct snd_soc_pcm_runtime *rtd = substream->private_data;
+	struct hsw_priv_data *pdata =
+		snd_soc_platform_get_drvdata(rtd->platform);
+	struct sst_hsw *hsw = pdata->hsw;
+	u32 pos;
+	snd_pcm_uframes_t position = bytes_to_frames(runtime,
+		 sst_hsw_get_dsp_position(hsw, pcm_data->stream));
+	unsigned char *dma_area = runtime->dma_area;
+	snd_pcm_uframes_t dma_frames =
+		bytes_to_frames(runtime, runtime->dma_bytes);
+	snd_pcm_uframes_t old_position;
+	ssize_t samples;
+
+	pos = frames_to_bytes(runtime,
+		(runtime->control->appl_ptr % runtime->buffer_size));
+
+	dev_vdbg(rtd->dev, "PCM: App pointer %d bytes\n", pos);
+
+	/* SST fw don't know where to stop dma
+	 * So, SST driver need to clean the data which has been consumed
+	 */
+	if (dma_area == NULL || dma_frames <= 0
+		|| (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
+		|| !sst_hsw_stream_get_silence_start(hsw, stream)) {
+		snd_pcm_period_elapsed(substream);
+		return pos;
+	}
+
+	old_position = sst_hsw_stream_get_old_position(hsw, stream);
+	if (position > old_position) {
+		if (position < dma_frames) {
+			samples = SST_SAMPLES(runtime, position - old_position);
+			snd_pcm_format_set_silence(runtime->format,
+				SST_OLD_POSITION(dma_area,
+					runtime, old_position),
+				samples);
+		} else
+			dev_err(rtd->dev, "PCM: position is wrong\n");
+	} else {
+		if (old_position < dma_frames) {
+			samples = SST_SAMPLES(runtime,
+				dma_frames - old_position);
+			snd_pcm_format_set_silence(runtime->format,
+				SST_OLD_POSITION(dma_area,
+					runtime, old_position),
+				samples);
+		} else
+			dev_err(rtd->dev, "PCM: dma_bytes is wrong\n");
+		if (position < dma_frames) {
+			samples = SST_SAMPLES(runtime, position);
+			snd_pcm_format_set_silence(runtime->format,
+				dma_area, samples);
+		} else
+			dev_err(rtd->dev, "PCM: position is wrong\n");
+	}
+	sst_hsw_stream_set_old_position(hsw, stream, position);
+
+	/* let alsa know we have play a period */
+	snd_pcm_period_elapsed(substream);
+	return pos;
+}
+
+static snd_pcm_uframes_t hsw_pcm_pointer(struct snd_pcm_substream *substream)
+{
+	struct snd_soc_pcm_runtime *rtd = substream->private_data;
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	struct hsw_priv_data *pdata =
+		snd_soc_platform_get_drvdata(rtd->platform);
+	struct hsw_pcm_data *pcm_data;
+	struct sst_hsw *hsw = pdata->hsw;
+	snd_pcm_uframes_t offset;
+	uint64_t ppos;
+	u32 position;
+	int dai;
+
+	dai = mod_map[rtd->cpu_dai->id].dai_id;
+	pcm_data = &pdata->pcm[dai][substream->stream];
+	position = sst_hsw_get_dsp_position(hsw, pcm_data->stream);
+
+	offset = bytes_to_frames(runtime, position);
+	ppos = sst_hsw_get_dsp_presentation_position(hsw, pcm_data->stream);
+
+	dev_vdbg(rtd->dev, "PCM: DMA pointer %du bytes, pos %llu\n",
+		position, ppos);
+	return offset;
+}
+
+static int hsw_pcm_open(struct snd_pcm_substream *substream)
+{
+	struct snd_soc_pcm_runtime *rtd = substream->private_data;
+	struct hsw_priv_data *pdata =
+		snd_soc_platform_get_drvdata(rtd->platform);
+	struct hsw_pcm_data *pcm_data;
+	struct sst_hsw *hsw = pdata->hsw;
+	int dai;
+
+	dai = mod_map[rtd->cpu_dai->id].dai_id;
+	pcm_data = &pdata->pcm[dai][substream->stream];
+
+	mutex_lock(&pcm_data->mutex);
+	pm_runtime_get_sync(pdata->dev);
+
+	pcm_data->substream = substream;
+
+	snd_pcm_hw_constraint_step(substream->runtime, 0,
+		SNDRV_PCM_HW_PARAM_BUFFER_SIZE, PAGE_SIZE);
+	snd_pcm_hw_constraint_step(substream->runtime, 0,
+		SNDRV_PCM_HW_PARAM_PERIOD_SIZE, 256);
+	snd_soc_set_runtime_hwparams(substream, &hsw_pcm_hardware);
+
+	pcm_data->stream = sst_hsw_stream_new(hsw, rtd->cpu_dai->id,
+		hsw_notify_pointer, pcm_data);
+	if (pcm_data->stream == NULL) {
+		dev_err(rtd->dev, "error: failed to create stream\n");
+		pm_runtime_mark_last_busy(pdata->dev);
+		pm_runtime_put_autosuspend(pdata->dev);
+		mutex_unlock(&pcm_data->mutex);
+		return -EINVAL;
+	}
+
+	mutex_unlock(&pcm_data->mutex);
+	return 0;
+}
+
+static int hsw_pcm_close(struct snd_pcm_substream *substream)
+{
+	struct snd_soc_pcm_runtime *rtd = substream->private_data;
+	struct hsw_priv_data *pdata =
+		snd_soc_platform_get_drvdata(rtd->platform);
+	struct hsw_pcm_data *pcm_data;
+	struct sst_hsw *hsw = pdata->hsw;
+	int ret, dai;
+
+	dai = mod_map[rtd->cpu_dai->id].dai_id;
+	pcm_data = &pdata->pcm[dai][substream->stream];
+
+	mutex_lock(&pcm_data->mutex);
+	ret = sst_hsw_stream_reset(hsw, pcm_data->stream);
+	if (ret < 0) {
+		dev_dbg(rtd->dev, "error: reset stream failed %d\n", ret);
+		goto out;
+	}
+
+	ret = sst_hsw_stream_free(hsw, pcm_data->stream);
+	if (ret < 0) {
+		dev_dbg(rtd->dev, "error: free stream failed %d\n", ret);
+		goto out;
+	}
+	pcm_data->allocated = 0;
+	pcm_data->stream = NULL;
+
+out:
+	pm_runtime_mark_last_busy(pdata->dev);
+	pm_runtime_put_autosuspend(pdata->dev);
+	mutex_unlock(&pcm_data->mutex);
+	return ret;
+}
+
+static struct snd_pcm_ops hsw_pcm_ops = {
+	.open		= hsw_pcm_open,
+	.close		= hsw_pcm_close,
+	.ioctl		= snd_pcm_lib_ioctl,
+	.hw_params	= hsw_pcm_hw_params,
+	.hw_free	= hsw_pcm_hw_free,
+	.trigger	= hsw_pcm_trigger,
+	.pointer	= hsw_pcm_pointer,
+	.page		= snd_pcm_sgbuf_ops_page,
+};
+
+static int hsw_pcm_create_modules(struct hsw_priv_data *pdata)
+{
+	struct sst_hsw *hsw = pdata->hsw;
+	struct hsw_pcm_data *pcm_data;
+	int i;
+
+	for (i = 0; i < ARRAY_SIZE(mod_map); i++) {
+		pcm_data = &pdata->pcm[mod_map[i].dai_id][mod_map[i].stream];
+
+		/* create new runtime module, use same offset if recreated */
+		pcm_data->runtime = sst_hsw_runtime_module_create(hsw,
+			mod_map[i].mod_id, pcm_data->persistent_offset);
+		if (pcm_data->runtime == NULL)
+			goto err;
+		pcm_data->persistent_offset =
+			pcm_data->runtime->persistent_offset;
+	}
+
+	/* create runtime blocks for module waves */
+	if (sst_hsw_is_module_loaded(hsw, SST_HSW_MODULE_WAVES)) {
+		pdata->runtime_waves = sst_hsw_runtime_module_create(hsw,
+			SST_HSW_MODULE_WAVES, 0);
+		if (pdata->runtime_waves == NULL)
+			goto err;
+	}
+
+	return 0;
+
+err:
+	for (--i; i >= 0; i--) {
+		pcm_data = &pdata->pcm[mod_map[i].dai_id][mod_map[i].stream];
+		sst_hsw_runtime_module_free(pcm_data->runtime);
+	}
+
+	return -ENODEV;
+}
+
+static void hsw_pcm_free_modules(struct hsw_priv_data *pdata)
+{
+	struct sst_hsw *hsw = pdata->hsw;
+	struct hsw_pcm_data *pcm_data;
+	int i;
+
+	for (i = 0; i < ARRAY_SIZE(mod_map); i++) {
+		pcm_data = &pdata->pcm[mod_map[i].dai_id][mod_map[i].stream];
+		if (pcm_data->runtime){
+			sst_hsw_runtime_module_free(pcm_data->runtime);
+			pcm_data->runtime = NULL;
+		}
+	}
+	if (sst_hsw_is_module_loaded(hsw, SST_HSW_MODULE_WAVES) &&
+				pdata->runtime_waves) {
+		sst_hsw_runtime_module_free(pdata->runtime_waves);
+		pdata->runtime_waves = NULL;
+	}
+}
+
+static int hsw_pcm_new(struct snd_soc_pcm_runtime *rtd)
+{
+	struct snd_pcm *pcm = rtd->pcm;
+	struct snd_soc_platform *platform = rtd->platform;
+	struct sst_pdata *pdata = dev_get_platdata(platform->dev);
+	struct hsw_priv_data *priv_data = dev_get_drvdata(platform->dev);
+	struct device *dev = pdata->dma_dev;
+	int ret = 0;
+
+	if (pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream ||
+			pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream) {
+		ret = snd_pcm_lib_preallocate_pages_for_all(pcm,
+			SNDRV_DMA_TYPE_DEV_SG,
+			dev,
+			hsw_pcm_hardware.buffer_bytes_max,
+			hsw_pcm_hardware.buffer_bytes_max);
+		if (ret) {
+			dev_err(rtd->dev, "dma buffer allocation failed %d\n",
+				ret);
+			return ret;
+		}
+	}
+	if (pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream)
+		priv_data->pcm[rtd->cpu_dai->id][SNDRV_PCM_STREAM_PLAYBACK].hsw_pcm = pcm;
+	if (pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream)
+		priv_data->pcm[rtd->cpu_dai->id][SNDRV_PCM_STREAM_CAPTURE].hsw_pcm = pcm;
+
+	return ret;
+}
+
+#define HSW_FORMATS \
+	(SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S16_LE)
+
+// TODO: this will come from topology
+static struct snd_soc_dai_driver hsw_dais[] = {
+	{
+		.name  = "PCM0 Pin",
+		.id = HSW_PCM_DAI_ID_SYSTEM,
+		.playback = {
+			.stream_name = "System Playback",
+			.channels_min = 2,
+			.channels_max = 2,
+			.rates = SNDRV_PCM_RATE_48000,
+			.formats = SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S16_LE,
+		},
+		.capture = {
+			.stream_name = "Analog Capture",
+			.channels_min = 2,
+			.channels_max = 4,
+			.rates = SNDRV_PCM_RATE_48000,
+			.formats = SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S16_LE,
+		},
+	},
+	{
+		/* PCM */
+		.name  = "PCM1 Pin",
+		.id = HSW_PCM_DAI_ID_OFFLOAD0,
+		.playback = {
+			.stream_name = "PCM1 Playback",
+			.channels_min = 2,
+			.channels_max = 2,
+			.rates = SNDRV_PCM_RATE_48000,
+			.formats = SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S16_LE,
+		},
+	},
+};
+
+// TODO: this will come from topology
+static const struct snd_soc_dapm_widget widgets[] = {
+
+	/* Backend DAIs  */
+	SND_SOC_DAPM_AIF_IN("SSP CODEC IN", NULL, 0, SND_SOC_NOPM, 0, 0),
+	SND_SOC_DAPM_AIF_OUT("SSP CODEC OUT", NULL, 0, SND_SOC_NOPM, 0, 0),
+
+	/* mixers */
+	SND_SOC_DAPM_MIXER("Playback Mixer", SND_SOC_NOPM, 0, 0, NULL, 0),
+
+	/* Volumes */
+	SND_SOC_DAPM_PGA("Playback Mixer Volume", SND_SOC_NOPM, 0, 0, NULL, 0),
+	SND_SOC_DAPM_PGA("Capture Volume", SND_SOC_NOPM, 0, 0, NULL, 0),
+	SND_SOC_DAPM_PGA("PCM0 Playback Volume", SND_SOC_NOPM, 0, 0, NULL, 0),
+	SND_SOC_DAPM_PGA("PCM1 Playback Volume", SND_SOC_NOPM, 0, 0, NULL, 0),
+};
+
+// TODO: this will come from topology
+static const struct snd_soc_dapm_route graph[] = {
+	/* Playback Mixer */
+	{"PCM0 Playback Volume", NULL, "System Playback"},
+	{"PCM1 Playback Volume", NULL, "PCM1 Playback"},
+	{"Playback Mixer", NULL, "PCM0 Playback Volume"},
+	{"Playback Mixer", NULL, "PCM1 Playback Volume"},
+	{"Playback Mixer Volume", NULL, "Playback Mixer"},
+	{"SSP CODEC OUT", NULL, "Playback Mixer Volume"},
+
+	{"Capture Volume", NULL, "SSP CODEC IN"},
+	{"Analog Capture", NULL, "Capture Volume"},
+};
+
+static int hsw_pcm_probe(struct snd_soc_platform *platform)
+{
+	struct hsw_priv_data *priv_data = snd_soc_platform_get_drvdata(platform);
+	struct sst_pdata *pdata = dev_get_platdata(platform->dev);
+	struct device *dma_dev, *dev;
+	int i, ret = 0;
+
+	if (!pdata)
+		return -ENODEV;
+
+	dev = platform->dev;
+	dma_dev = pdata->dma_dev;
+
+	priv_data->hsw = pdata->dsp;
+	priv_data->dev = platform->dev;
+	priv_data->pm_state = HSW_PM_STATE_D0;
+	priv_data->soc_card = platform->component.card;
+
+	/* allocate DSP buffer page tables */
+	for (i = 0; i < ARRAY_SIZE(hsw_dais); i++) {
+
+		/* playback */
+		if (hsw_dais[i].playback.channels_min) {
+			mutex_init(&priv_data->pcm[i][SNDRV_PCM_STREAM_PLAYBACK].mutex);
+			ret = snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, dma_dev,
+				PAGE_SIZE, &priv_data->dmab[i][0]);
+			if (ret < 0)
+				goto err;
+		}
+
+		/* capture */
+		if (hsw_dais[i].capture.channels_min) {
+			mutex_init(&priv_data->pcm[i][SNDRV_PCM_STREAM_CAPTURE].mutex);
+			ret = snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, dma_dev,
+				PAGE_SIZE, &priv_data->dmab[i][1]);
+			if (ret < 0)
+				goto err;
+		}
+	}
+
+	/* allocate runtime modules */
+	ret = hsw_pcm_create_modules(priv_data);
+	if (ret < 0)
+		goto err;
+
+	/* not all DSPs support RTD3 */
+	if (pdata->have_rtd3 == false)
+		return 0;
+
+	/* enable runtime PM with auto suspend */
+	pm_runtime_set_autosuspend_delay(platform->dev,
+		SST_RUNTIME_SUSPEND_DELAY);
+	pm_runtime_use_autosuspend(platform->dev);
+	pm_runtime_enable(platform->dev);
+	pm_runtime_idle(platform->dev);
+
+	return 0;
+
+err:
+	for (--i; i >= 0; i--) {
+		if (hsw_dais[i].playback.channels_min)
+			snd_dma_free_pages(&priv_data->dmab[i][0]);
+		if (hsw_dais[i].capture.channels_min)
+			snd_dma_free_pages(&priv_data->dmab[i][1]);
+	}
+	return ret;
+}
+
+static int hsw_pcm_remove(struct snd_soc_platform *platform)
+{
+	struct hsw_priv_data *priv_data =
+		snd_soc_platform_get_drvdata(platform);
+	int i;
+
+	pm_runtime_disable(platform->dev);
+	hsw_pcm_free_modules(priv_data);
+
+	for (i = 0; i < ARRAY_SIZE(hsw_dais); i++) {
+		if (hsw_dais[i].playback.channels_min)
+			snd_dma_free_pages(&priv_data->dmab[i][0]);
+		if (hsw_dais[i].capture.channels_min)
+			snd_dma_free_pages(&priv_data->dmab[i][1]);
+	}
+
+	return 0;
+}
+
+static struct snd_soc_platform_driver hsw_soc_platform = {
+	.probe		= hsw_pcm_probe,
+	.remove		= hsw_pcm_remove,
+	.ops		= &hsw_pcm_ops,
+	.pcm_new	= hsw_pcm_new,
+};
+
+static const struct snd_soc_component_driver hsw_dai_component = {
+	.name = "haswell-dai",
+	.controls = hsw_volume_controls,
+	.num_controls = ARRAY_SIZE(hsw_volume_controls),
+	.dapm_widgets = widgets,
+	.num_dapm_widgets = ARRAY_SIZE(widgets),
+	.dapm_routes = graph,
+	.num_dapm_routes = ARRAY_SIZE(graph),
+};
+
+static int hsw_pcm_dev_probe(struct platform_device *pdev)
+{
+	struct sst_pdata *sst_pdata = dev_get_platdata(&pdev->dev);
+	struct hsw_priv_data *priv_data;
+	int ret;
+
+	if (!sst_pdata)
+		return -EINVAL;
+
+	priv_data = devm_kzalloc(&pdev->dev, sizeof(*priv_data), GFP_KERNEL);
+	if (!priv_data)
+		return -ENOMEM;
+
+	ret = sst_hsw_dsp_init(&pdev->dev, sst_pdata);
+	if (ret < 0)
+		return -ENODEV;
+
+	priv_data->hsw = sst_pdata->dsp;
+	platform_set_drvdata(pdev, priv_data);
+
+	ret = snd_soc_register_platform(&pdev->dev, &hsw_soc_platform);
+	if (ret < 0)
+		goto err_plat;
+
+	ret = snd_soc_register_component(&pdev->dev, &hsw_dai_component,
+		hsw_dais, ARRAY_SIZE(hsw_dais));
+	if (ret < 0)
+		goto err_comp;
+
+	return 0;
+
+err_comp:
+	snd_soc_unregister_platform(&pdev->dev);
+err_plat:
+	sst_hsw_dsp_free(&pdev->dev, sst_pdata);
+	return 0;
+}
+
+static int hsw_pcm_dev_remove(struct platform_device *pdev)
+{
+	struct sst_pdata *sst_pdata = dev_get_platdata(&pdev->dev);
+
+	snd_soc_unregister_platform(&pdev->dev);
+	snd_soc_unregister_component(&pdev->dev);
+	sst_hsw_dsp_free(&pdev->dev, sst_pdata);
+
+	return 0;
+}
+
+#ifdef CONFIG_PM
+
+static int hsw_pcm_runtime_idle(struct device *dev)
+{
+	return 0;
+}
+
+static int hsw_pcm_suspend(struct device *dev)
+{
+	struct hsw_priv_data *pdata = dev_get_drvdata(dev);
+	struct sst_hsw *hsw = pdata->hsw;
+
+	/* enter D3 state and stall */
+	sst_hsw_dsp_runtime_suspend(hsw);
+	/* free all runtime modules */
+	hsw_pcm_free_modules(pdata);
+	/* put the DSP to sleep, fw unloaded after runtime modules freed */
+	sst_hsw_dsp_runtime_sleep(hsw);
+	return 0;
+}
+
+static int hsw_pcm_runtime_suspend(struct device *dev)
+{
+	struct hsw_priv_data *pdata = dev_get_drvdata(dev);
+	struct sst_hsw *hsw = pdata->hsw;
+	int ret;
+
+	if (pdata->pm_state >= HSW_PM_STATE_RTD3)
+		return 0;
+
+	hsw_pcm_suspend(dev);
+	pdata->pm_state = HSW_PM_STATE_RTD3;
+
+	return 0;
+}
+
+static int hsw_pcm_runtime_resume(struct device *dev)
+{
+	struct hsw_priv_data *pdata = dev_get_drvdata(dev);
+	struct sst_hsw *hsw = pdata->hsw;
+	int ret;
+
+	if (pdata->pm_state != HSW_PM_STATE_RTD3)
+		return 0;
+
+	ret = sst_hsw_dsp_load(hsw);
+	if (ret < 0) {
+		dev_err(dev, "failed to reload %d\n", ret);
+		return ret;
+	}
+
+	ret = hsw_pcm_create_modules(pdata);
+	if (ret < 0) {
+		dev_err(dev, "failed to create modules %d\n", ret);
+		return ret;
+	}
+
+	ret = sst_hsw_dsp_runtime_resume(hsw);
+	if (ret < 0)
+		return ret;
+	else if (ret == 1) /* no action required */
+		return 0;
+
+	/* check flag when resume */
+	if (sst_hsw_is_module_enabled_rtd3(hsw, SST_HSW_MODULE_WAVES)) {
+		ret = sst_hsw_module_enable(hsw, SST_HSW_MODULE_WAVES, 0);
+		if (ret < 0)
+			return ret;
+		/* put parameters from buffer to dsp */
+		ret = sst_hsw_launch_param_buf(hsw);
+		if (ret < 0)
+			return ret;
+		/* unset flag */
+		sst_hsw_set_module_disabled_rtd3(hsw, SST_HSW_MODULE_WAVES);
+	}
+
+	pdata->pm_state = HSW_PM_STATE_D0;
+	return ret;
+}
+
+#else
+#define hsw_pcm_runtime_idle		NULL
+#define hsw_pcm_runtime_suspend		NULL
+#define hsw_pcm_runtime_resume		NULL
+#endif
+
+#ifdef CONFIG_PM
+
+static void hsw_pcm_complete(struct device *dev)
+{
+	struct hsw_priv_data *pdata = dev_get_drvdata(dev);
+	struct sst_hsw *hsw = pdata->hsw;
+	struct hsw_pcm_data *pcm_data;
+	int i, err;
+
+	if (pdata->pm_state != HSW_PM_STATE_D3)
+		return;
+
+	err = sst_hsw_dsp_load(hsw);
+	if (err < 0) {
+		dev_err(dev, "failed to reload %d\n", err);
+		return;
+	}
+
+	err = hsw_pcm_create_modules(pdata);
+	if (err < 0) {
+		dev_err(dev, "failed to create modules %d\n", err);
+		return;
+	}
+
+	for (i = 0; i < ARRAY_SIZE(mod_map); i++) {
+		pcm_data = &pdata->pcm[mod_map[i].dai_id][mod_map[i].stream];
+
+		if (!pcm_data->substream)
+			continue;
+
+		err = sst_module_runtime_restore(pcm_data->runtime,
+			&pcm_data->context);
+		if (err < 0)
+			dev_err(dev, "failed to restore context for PCM %d\n", i);
+	}
+
+	snd_soc_resume(pdata->soc_card->dev);
+
+	err = sst_hsw_dsp_runtime_resume(hsw);
+	if (err < 0)
+		return;
+	else if (err == 1) /* no action required */
+		return;
+
+	pdata->pm_state = HSW_PM_STATE_D0;
+	return;
+}
+
+static int hsw_pcm_prepare(struct device *dev)
+{
+	struct hsw_priv_data *pdata = dev_get_drvdata(dev);
+	struct hsw_pcm_data *pcm_data;
+	int i, err;
+
+	if (pdata->pm_state == HSW_PM_STATE_D3)
+		return 0;
+	else if (pdata->pm_state == HSW_PM_STATE_D0) {
+		/* suspend all active streams */
+		for (i = 0; i < ARRAY_SIZE(mod_map); i++) {
+			pcm_data = &pdata->pcm[mod_map[i].dai_id][mod_map[i].stream];
+
+			if (!pcm_data->substream)
+				continue;
+			dev_dbg(dev, "suspending pcm %d\n", i);
+			snd_pcm_suspend_all(pcm_data->hsw_pcm);
+
+			/* We need to wait until the DSP FW stops the streams */
+			msleep(2);
+		}
+
+		/* preserve persistent memory */
+		for (i = 0; i < ARRAY_SIZE(mod_map); i++) {
+			pcm_data = &pdata->pcm[mod_map[i].dai_id][mod_map[i].stream];
+
+			if (!pcm_data->substream)
+				continue;
+
+			dev_dbg(dev, "saving context pcm %d\n", i);
+			err = sst_module_runtime_save(pcm_data->runtime,
+				&pcm_data->context);
+			if (err < 0)
+				dev_err(dev, "failed to save context for PCM %d\n", i);
+		}
+		hsw_pcm_suspend(dev);
+	}
+
+	snd_soc_suspend(pdata->soc_card->dev);
+	snd_soc_poweroff(pdata->soc_card->dev);
+
+	pdata->pm_state = HSW_PM_STATE_D3;
+
+	return 0;
+}
+
+#else
+#define hsw_pcm_prepare		NULL
+#define hsw_pcm_complete	NULL
+#endif
+
+static const struct dev_pm_ops hsw_pcm_pm = {
+	.runtime_idle = hsw_pcm_runtime_idle,
+	.runtime_suspend = hsw_pcm_runtime_suspend,
+	.runtime_resume = hsw_pcm_runtime_resume,
+	.prepare = hsw_pcm_prepare,
+	.complete = hsw_pcm_complete,
+};
+
+static struct platform_driver hsw_pcm_driver = {
+	.driver = {
+		.name = "haswell-pcm-audio",
+		.pm = &hsw_pcm_pm,
+	},
+
+	.probe = hsw_pcm_dev_probe,
+	.remove = hsw_pcm_dev_remove,
+};
+module_platform_driver(hsw_pcm_driver);
+
+MODULE_AUTHOR("Liam Girdwood, Xingchao Wang");
+MODULE_DESCRIPTION("Haswell/Lynxpoint + Broadwell/Wildcatpoint PCM");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:haswell-pcm-audio");
diff --git a/sound/soc/sof/sof-ipc.h b/sound/soc/sof/sof-ipc.h
new file mode 100644
index 0000000..5477bee
--- /dev/null
+++ b/sound/soc/sof/sof-ipc.h
@@ -0,0 +1,417 @@
+/*
+ * Sound Open Firmware IPC Support
+ *
+ * Copyright (C) 2017, 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 __SND_SOF_IPC_H
+#define __SND_SOF_IPC_H
+
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/platform_device.h>
+#include <sound/asound.h>
+
+#define SND_SOF_MAX_CHANNELS		8
+#define SND_SOF_BUILD_HASH_LENGTH	40
+#define SND_SOF_INFO_LENGTH		40
+
+
+/*
+ * IPC Doorbell
+ */
+#define SND_SOF_IPC_CMD_SHIFT		24
+
+/* stream "PCM" ops */
+#define SND_SOF_IPC_CMD_HW_PARAMS	(0x1 << SND_SOF_IPC_CMD_SHIFT)
+#define SND_SOF_IPC_CMD_HW_FREE		(0x2 << SND_SOF_IPC_CMD_SHIFT)
+#define SND_SOF_IPC_CMD_TRIGGER		(0x3 << SND_SOF_IPC_CMD_SHIFT)
+
+/* "kcontrol" ops */
+#define SND_SOF_IPC_CMD_VOLUME		(0x10 << SND_SOF_IPC_CMD_SHIFT)
+#define SND_SOF_IPC_CMD_MIXER		(0x11 << SND_SOF_IPC_CMD_SHIFT)
+#define SND_SOF_IPC_CMD_CTRL		(0x12 << SND_SOF_IPC_CMD_SHIFT)
+
+/* DAI configuration */
+#define SND_SOF_IPC_CMD_DAI		(0x20 << SND_SOF_IPC_CMD_SHIFT)
+
+/* Topology */
+#define SND_SOF_IPC_CMD_COMP_NEW	(0x30 << SND_SOF_IPC_CMD_SHIFT)
+#define SND_SOF_IPC_CMD_COMP_CON	(0x31 << SND_SOF_IPC_CMD_SHIFT)
+#define SND_SOF_IPC_CMD_COMP_FREE	(0x32 << SND_SOF_IPC_CMD_SHIFT)
+#define SND_SOF_IPC_CMD_PIPE_NEW	(0x33 << SND_SOF_IPC_CMD_SHIFT)
+#define SND_SOF_IPC_CMD_PIPE_CON	(0x34 << SND_SOF_IPC_CMD_SHIFT)
+#define SND_SOF_IPC_CMD_PIPE_FREE	(0x35 << SND_SOF_IPC_CMD_SHIFT)
+#define SND_SOF_IPC_CMD_PIPE_READY	(0x36 << SND_SOF_IPC_CMD_SHIFT)
+
+/* tracing */
+#define SND_SOF_IPC_CMD_TRACE_CONF	(0x60 << SND_SOF_IPC_CMD_SHIFT)
+#define SND_SOF_IPC_CMD_TRACE_CTRL	(0x61 << SND_SOF_IPC_CMD_SHIFT)
+
+/* PM */
+#define SND_SOF_IPC_CMD_PM_CTRL		(0x71 << SND_SOF_IPC_CMD_SHIFT)
+
+/* compund command */
+#define SND_SOF_IPC_CMD_COMPOUND	(0xff << SND_SOF_IPC_CMD_SHIFT)
+
+/*
+ * Coumpund Message.
+ */
+struct snd_sof_ipc_compound_elem {
+	u32 ipc_cmd;
+	u32 offset;
+} __attribute__((packed));
+
+struct snd_sof_ipc_compound {
+	u32 num_elems;
+	struct snd_sof_ipc_compound_elem elem[0];
+	/* command structures here */
+} __attribute__((packed));
+
+
+/*
+ * Firmware Information.
+ */
+
+/* Firmware Ready and FW version */
+struct snd_sof_ipc_fw_version {
+	u32 inbox_offset;
+	u32 outbox_offset;
+	u32 inbox_size;
+	u32 outbox_size;
+	u32 fw_info_size;
+	u32 build;
+	u32 minor;
+	u32 major;
+	u32 ipc_version;
+	u8 fw_info[SND_SOF_INFO_LENGTH];
+	u8 fw_build_hash[SND_SOF_BUILD_HASH_LENGTH];	
+} __attribute__((packed));
+
+/*
+ * Stream configuration.
+ */
+
+/* channel positions */
+enum snd_sof_chmap {
+	SND_SOF_CHMAP_UNKNOWN = 0,
+	SND_SOF_CHMAP_NA,		/* N/A, silent */
+	SND_SOF_CHMAP_MONO,	/* mono stream */
+	/* this follows the alsa-lib mixer channel value + 3 */
+	SND_SOF_CHMAP_FL,		/* front left */
+	SND_SOF_CHMAP_FR,		/* front right */
+	SND_SOF_CHMAP_RL,		/* rear left */
+	SND_SOF_CHMAP_RR,		/* rear right */
+	SND_SOF_CHMAP_FC,		/* front center */
+	SND_SOF_CHMAP_LFE,	/* LFE */
+	SND_SOF_CHMAP_SL,		/* side left */
+	SND_SOF_CHMAP_SR,		/* side right */
+	SND_SOF_CHMAP_RC,		/* rear center */
+	/* new definitions */
+	SND_SOF_CHMAP_FLC,	/* front left center */
+	SND_SOF_CHMAP_FRC,	/* front right center */
+	SND_SOF_CHMAP_RLC,	/* rear left center */
+	SND_SOF_CHMAP_RRC,	/* rear right center */
+	SND_SOF_CHMAP_FLW,	/* front left wide */
+	SND_SOF_CHMAP_FRW,	/* front right wide */
+	SND_SOF_CHMAP_FLH,	/* front left high */
+	SND_SOF_CHMAP_FCH,	/* front center high */
+	SND_SOF_CHMAP_FRH,	/* front right high */
+	SND_SOF_CHMAP_TC,		/* top center */
+	SND_SOF_CHMAP_TFL,	/* top front left */
+	SND_SOF_CHMAP_TFR,	/* top front right */
+	SND_SOF_CHMAP_TFC,	/* top front center */
+	SND_SOF_CHMAP_TRL,	/* top rear left */
+	SND_SOF_CHMAP_TRR,	/* top rear right */
+	SND_SOF_CHMAP_TRC,	/* top rear center */
+	/* new definitions for UAC2 */
+	SND_SOF_CHMAP_TFLC,	/* top front left center */
+	SND_SOF_CHMAP_TFRC,	/* top front right center */
+	SND_SOF_CHMAP_TSL,	/* top side left */
+	SND_SOF_CHMAP_TSR,	/* top side right */
+	SND_SOF_CHMAP_LLFE,	/* left LFE */
+	SND_SOF_CHMAP_RLFE,	/* right LFE */
+	SND_SOF_CHMAP_BC,		/* bottom center */
+	SND_SOF_CHMAP_BLC,	/* bottom left center */
+	SND_SOF_CHMAP_BRC,	/* bottom right center */
+	SND_SOF_CHMAP_LAST = SND_SOF_CHMAP_BRC,
+};
+
+enum snd_soc_stream_type {
+	SND_SOF_STREAM_PCM = 0,
+	SND_SOF_STREAM_VORBIS = 1,
+};
+
+/* Stream buffer info */
+struct snd_sof_ipc_stream_buffer {
+	u64 phy_addr;	/* PHY base address of page tables */
+	u32 num_pages;	
+	u32 size;	/* can be less than num_pages * PAGE_SIZE */
+	u32 offset;
+} __attribute__((packed));
+
+/* pcm format configuration */
+struct snd_sof_ipc_pcm_format {
+	u32 rate;	/* sample rate */
+	u32 format;	/* sample format */
+	enum snd_sof_chmap[SND_SOF_MAX_CHANNELS];	/* channel map */
+} __attribute__((packed));
+
+/* Stream HW Params Request */
+struct snd_sof_ipc_stream_hw_params_req {
+	u32 stream_id;		/* PCM number */
+	enum snd_soc_stream_type type;
+	struct snd_sof_ipc_stream_buffer buffer;
+	union {
+		struct snd_sof_ipc_pcm_format pcm;
+	};
+} __attribute__((packed));
+
+/* Stream runtime information */
+struct snd_sof_stream_info {
+	u32 dma_posn;		/* dma position in frames */
+	u32 dai_posn;		/* dai position readback offset */
+	u64 audio_tstamp;	/* audio timestamp */
+	u64 system_tstamp;	/* system timestamp */
+};
+
+/* HW Params Reply */
+struct snd_sof_ipc_stream_hw_params_reply {
+	u32 stream_id;
+	u32 stream_info_offset;	/* offset to struct snd_sof_stream_info */
+	
+} __attribute__((packed));
+
+/* Stream Free Request */
+struct snd_sof_ipc_stream_hw_free_req {
+	u32 stream_id;
+} __attribute__((packed));
+
+/* Stream set position */
+struct snd_sof_ipc_stream_trigger {
+	u32 stream_id;
+	u32 trigger_cmd;
+	u32 position;  /* TDOD should this be in frames */
+} __attribute__((packed));
+
+/*
+ * DAI configuration.
+ */
+
+enum snd_sof_dai_type {
+	SND_SOF_DAI_SSP = 0,
+	SND_SOF_DAI_DMIC = 1,
+	SND_SOF_DAI_HDA = 2,
+};
+
+struct snd_sof_ipc_ssp_config {
+	u32 mclk;
+	u32 bclk;
+	u32 format;
+	u32 clock_source;
+	u32 mode;
+	u32 num_slots;
+	u32 slot_size;
+} __attribute__((packed));
+
+struct snd_sof_ipc_dmic_config {
+	u32 clk;
+} __attribute__((packed));
+
+struct snd_sof_ipc_hda_config {
+	u32 clk;
+} __attribute__((packed));
+
+/* Device Configuration Request */
+struct snd_sof_ipc_dai_config {
+	u32 dai_id;
+	enum snd_sof_dai_type type;
+	union {
+		struct snd_sof_ipc_ssp_config ssp;
+		struct snd_sof_ipc_dmic_config dmic;
+		struct snd_sof_ipc_hda_config hda;
+	};
+} __attribute__((packed));
+
+
+/*
+ * Volume controls.
+ */
+
+/* Volume Curve Type*/
+enum snd_sof_volume_curve {
+	SND_SOF_VOLUME_CURVE_NONE = 0,
+	SND_SOF_VOLUME_CURVE_FADE = 1
+};
+
+/* Set Volume */
+struct snd_sof_ipc_volume_set {
+	u32 volume_id;
+	u32 channel_mask;
+	u32 target_volume;
+	u32 curve_duration;
+	enum snd_sof_volume_curve curve_type;
+} __attribute__((packed));
+
+/* Get Volume - can also be read by mailbox IO */
+struct snd_sof_ipc_volume_get {
+	u32 volume_id;
+	u32 volume[SND_SOF_MAX_CHANNELS];
+} __attribute__((packed));
+
+
+/*
+ * Mixer configuration.
+ */
+
+/* Set/Get Mixer */
+struct snd_sof_ipc_mixer {
+	u32 mixer_id;
+	u32 channel_mask;
+} __attribute__((packed));
+
+
+/*
+ * Component control - component control that does not fit into regular IPC.
+ */
+struct snd_sof_ipc_control {
+	u32 comp_id;
+	u32 ctrl_id;
+	u32 channel_mask;
+	u32 size;
+	u8 data[0];
+} __attribute__((packed));
+
+/*
+ * Power Management.
+ */
+
+/* DX Power State */
+enum snd_sof_pm_state {
+	SND_SOF_PM_D0	= 0,
+	SND_SOF_PM_D1	= 1,
+	SND_SOF_PM_D3	= 3,
+};
+
+/* DX State Request */
+struct snd_sof_ipc_pm_req {
+	u32 state;
+} __attribute__((packed));
+
+/* DX State Reply Memory Info Item */
+struct snd_sof_ipc_pm_elem {
+	u32 offset;
+	u32 size;
+	u32 source;
+} __attribute__((packed));
+
+/* DX State Reply */
+struct snd_sof_ipc_pm_reply {
+	u32 elems;
+	struct snd_sof_ipc_pm_elem elem[0];
+} __attribute__((packed));
+
+
+/*
+ * Component
+ */
+
+/* types of component */
+enum snd_sof_comp_type {
+	SND_SOF_COMP_HOST = 0,
+	SND_SOF_COMP_DAI,
+	SND_SOF_COMP_SG_HOST,	/* scatter gather varient */
+	SND_SOF_COMP_SG_DAI,	/* scatter gather varient */
+	SND_SOF_COMP_VOLUME,
+	SND_SOF_COMP_MIXER,
+	SND_SOF_COMP_SRC,
+};
+
+/* generic PCM component data */
+struct snd_sof_ipc_pcm_comp {
+	u32 format;	/* data format */
+	u32 frames;	/* number of frames to process */
+	u32 channels;	/* number of channels */
+	enum snd_sof_chmap[0];	/* channel map */
+} __attribute__((packed));
+
+/* generic host componeent */
+struct snd_sof_ipc_comp_host {
+	struct snd_sof_ipc_pcm_comp pcm;
+	uint32_t num_periods;
+	uint32_t preload_period_count;
+	uint32_t dmac_id;
+	uint32_t dmac_chan;
+	uint32_t dmac_config; /* DMA engine specific */
+}  __attribute__((packed));
+
+/* generic DAI componeent */
+struct snd_sof_ipc_comp_dai {
+	struct snd_sof_ipc_pcm_comp pcm;
+	uint32_t num_periods;
+	uint32_t dmac_id;
+	uint32_t dmac_chan;
+	uint32_t dmac_config; /* DMA engine specific */	
+}  __attribute__((packed));
+
+/* generic mixer componeent */
+struct snd_sof_ipc_comp_mixer {
+	struct snd_sof_ipc_pcm_comp pcm;
+}  __attribute__((packed));
+
+/* generic volume componeent */
+struct snd_sof_ipc_comp_volume {
+	struct snd_sof_ipc_pcm_comp pcm;
+	int32_t min_value;
+	int32_t max_value;
+}  __attribute__((packed));
+
+/* generic new component */
+struct snd_sof_ipc_comp_new {
+	u32 comp_id;
+	u32 pipline_id;
+	enum snd_sof_comp_type type;
+	union {
+		struct snd_sof_ipc_comp_volume volume;
+		struct snd_sof_ipc_comp_host host;
+		struct snd_sof_ipc_comp_dai dai;
+		struct snd_sof_ipc_comp_mixer mixer;
+		struct snd_sof_ipc_comp_src src;
+	};
+} __attribute__((packed));
+
+struct snd_sof_ipc_comp_free {
+	u32 comp_id;
+} __attribute__((packed));
+
+/*
+ * Pipeline
+ */
+
+/* new pipeline */
+struct snd_sof_ipc_pipe_new {
+	u32 pipeline_id;
+	u32 core;		/* core we run on */
+	u32 schedule_us;	/* schedule evey us */
+	u32 priority;		/* priority level 0 (low) to 10 (max)*/
+}  __attribute__((packed));
+
+struct snd_sof_ipc_pipe_ready {
+	u32 pipeline_id;
+}  __attribute__((packed));
+
+struct snd_sof_ipc_pipe_free {
+	u32 pipeline_id;
+}  __attribute__((packed));
+
+#endif
diff --git a/sound/soc/sof/sof.h b/sound/soc/sof/sof.h
new file mode 100644
index 0000000..61956eb
--- /dev/null
+++ b/sound/soc/sof/sof.h
@@ -0,0 +1,84 @@
+/*
+ * Intel Smart Sound Technology (SST) Core
+ *
+ * Copyright (C) 2013, 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 __SOUND_SOC_SOF_H
+#define __SOUND_SOC_SOF_H
+
+#include <linux/kernel.h>
+#include <linux/types.h>
+#include <linux/interrupt.h>
+#include <linux/device.h>
+
+#define SOF_NAME_SIZE			16
+#define SOF_IRAM			1
+#define SOF_DRAM			2
+#define SOF_REGS			3
+
+struct snd_soc_sof_fw_block {
+	__le32 ssize;		/* size of this structure */
+	__le32 type;		/* destination type e.g IRA, DRAM etc */
+	__le32 size;		/* block size */
+	__le32 offset;		/* offset from DSP base */
+} __attribute__((packed));
+
+struct snd_soc_sof_fw_hdr {
+	__le32 magic;
+	__le32 ssize;		/* size of this structure */
+	char name[SOF_NAME_SIZE]; /* FW name */
+	__le32 file_size;	/* size of fw minus this header */
+	__le32 abi;		/* ABI version */
+	__le32 modules;		/* # of modules */
+} __attribute__((packed));
+
+struct snd_soc_sof_fw_module {
+	__le32 ssize;		/* size of this structure */
+	char name[SOF_NAME_SIZE]; /* module name */
+	__le32 size;		/* size of module */
+	__le32 blocks;		/* # of blocks */
+} __attribute__((packed));
+
+
+struct snd_sof_dev {
+	struct device *dev;
+	struct mutex mutex;
+
+};
+
+/*
+ * Stream APIs.
+ */
+struct snd_sof_stream *snd_sof_stream_new(struct snd_sof_dev *sof_dev, 
+	struct snd_pcm_substream *substream, int stream_id,
+	u32 (*notify_position)(struct snd_sof_stream *stream, void *data),
+	void *data);
+
+int snd_sof_stream_free(struct snd_sof_stream *stream, 
+	struct snd_pcm_substream *substream);
+
+int snd_sof_stream_hw_params(struct snd_sof_stream *stream,
+	struct snd_pcm_substream *substream,
+	struct snd_pcm_hw_params *params);
+
+int snd_sof_stream_hw_free(struct snd_sof_stream *stream,
+	struct snd_pcm_substream *substream);
+
+int snd_sof_stream_prepare(struct snd_sof_stream *stream,
+	struct snd_pcm_substream *substream);
+
+int snd_sof_stream_trigger(struct snd_sof_stream *stream,
+	struct snd_pcm_substream *substream, int cmd);
+
+#endif
diff --git a/sound/soc/sof/stream.c b/sound/soc/sof/stream.c
new file mode 100644
index 0000000..f268afa
--- /dev/null
+++ b/sound/soc/sof/stream.c
@@ -0,0 +1,247 @@
+/*
+ * Sound Open Firmware - Stream Core
+ *
+ * Copyright (c) 2017, Intel Corporation.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope 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/delay.h>
+#include <linux/fs.h>
+#include <linux/slab.h>
+#include <linux/device.h>
+#include <linux/interrupt.h>
+#include <linux/module.h>
+#include <linux/dma-mapping.h>
+#include <linux/platform_device.h>
+#include <linux/firmware.h>
+#include <uapi/sound/sof-ipc.h>
+#include "sof.h"
+
+static int stream_hw_params_ipc(struct snd_sof_stream *stream)
+{
+	struct snd_sof_dev_ipc_stream_alloc_req *str_req = &stream->request;
+	struct snd_sof_dev_ipc_stream_alloc_reply *reply = &stream->reply;
+	u32 header;
+	int ret;
+
+	header = IPC_GLB_TYPE(IPC_GLB_ALLOCATE_STREAM);
+
+	ret = sst_ipc_tx_message_wait(&sof_dev->ipc, header, str_req,
+		sizeof(*str_req), reply, sizeof(*reply));
+	if (ret < 0) {
+		dev_err(sof_dev->dev, "error: stream commit failed\n");
+		return ret;
+	}
+
+	return 0;
+}
+
+/*
+ * 
+ */
+struct snd_sof_stream *snd_sof_stream_new(struct snd_sof_dev *sof_dev, 
+	struct snd_pcm_substream *substream, int stream_id,
+	u32 (*notify_position)(struct snd_sof_stream *stream, void *data),
+	void *data)
+{
+	struct snd_sof_stream *stream;
+	struct sst_dsp *sst = sof_dev->dsp;
+	unsigned long flags;
+
+	stream = kzalloc(sizeof(*stream), GFP_KERNEL);
+	if (stream == NULL)
+		return NULL;
+
+	spin_lock_irqsave(&sst->spinlock, flags);
+	stream->reply.stream_hw_id = INVALID_STREAM_HW_ID;
+	list_add(&stream->node, &sof_dev->stream_list);
+	stream->notify_position = notify_position;
+	stream->pdata = data;
+	stream->sof_dev = sof_dev;
+	stream->host_id = id;
+
+	/* work to process notification messages */
+	INIT_WORK(&stream->notify_work, sof_dev_notification_work);
+	spin_unlock_irqrestore(&sst->spinlock, flags);
+
+	return stream;
+}
+
+int snd_sof_stream_free(struct snd_sof_stream *stream, 
+	struct snd_pcm_substream *substream)
+{
+	u32 header;
+	int ret = 0;
+	struct sst_dsp *sst = sof_dev->dsp;
+	unsigned long flags;
+
+	/* dont free DSP streams that are not commited */
+	if (!stream->commited)
+		goto out;
+
+	trace_ipc_request("stream free", stream->host_id);
+
+	stream->free_req.stream_id = stream->reply.stream_hw_id;
+	header = IPC_GLB_TYPE(IPC_GLB_FREE_STREAM);
+
+	ret = sst_ipc_tx_message_wait(&sof_dev->ipc, header, &stream->free_req,
+		sizeof(stream->free_req), NULL, 0);
+	if (ret < 0) {
+		dev_err(sof_dev->dev, "error: free stream %d failed\n",
+			stream->free_req.stream_id);
+		return -EAGAIN;
+	}
+
+	trace_sof_dev_stream_free_req(stream, &stream->free_req);
+
+out:
+	cancel_work_sync(&stream->notify_work);
+	spin_lock_irqsave(&sst->spinlock, flags);
+	list_del(&stream->node);
+	kfree(stream);
+	spin_unlock_irqrestore(&sst->spinlock, flags);
+
+	return ret;
+}
+
+int snd_sof_stream_hw_params(struct snd_sof_stream *stream,
+	struct snd_pcm_substream *substream,
+	struct snd_pcm_hw_params *params)
+{
+
+}
+
+int snd_sof_stream_hw_free(struct snd_sof_stream *stream,
+	struct snd_pcm_substream *substream)
+{
+
+}
+
+int snd_sof_stream_prepare(struct snd_sof_stream *stream,
+	struct snd_pcm_substream *substream)
+{
+
+}
+
+int snd_sof_stream_trigger(struct snd_sof_stream *stream,
+	struct snd_pcm_substream *substream, int cmd)
+{
+
+}
+
+snd_pcm_uframes_t snd_sof_stream_get_old_position(struct snd_sof_dev *sof_dev,
+	struct snd_sof_stream *stream)
+{
+	return stream->old_position;
+}
+
+void snd_sof_stream_set_old_position(struct snd_sof_dev *sof_dev,
+	struct snd_sof_stream *stream, snd_pcm_uframes_t val)
+{
+	stream->old_position = val;
+}
+
+bool snd_sof_stream_get_silence_start(struct snd_sof_dev *sof_dev,
+	struct snd_sof_stream *stream)
+{
+	return stream->play_silence;
+}
+
+void snd_sof_stream_set_silence_start(struct snd_sof_dev *sof_dev,
+	struct snd_sof_stream *stream, bool val)
+{
+	stream->play_silence = val;
+}
+
+/* Send stream command */
+static int snd_sof_stream_operations(struct snd_sof_dev *sof_dev, int type,
+	int stream_id, int wait)
+{
+	u32 header;
+
+	header = IPC_GLB_TYPE(IPC_GLB_STREAM_MESSAGE) | IPC_STR_TYPE(type);
+	header |= (stream_id << IPC_STR_ID_SHIFT);
+
+	if (wait)
+		return sst_ipc_tx_message_wait(&sof_dev->ipc, header,
+			NULL, 0, NULL, 0);
+	else
+		return sst_ipc_tx_message_nowait(&sof_dev->ipc, header, NULL, 0);
+}
+
+/* Stream ALSA trigger operations */
+int snd_sof_stream_pause(struct snd_sof_dev *sof_dev, struct snd_sof_stream *stream,
+	int wait)
+{
+	int ret;
+
+	if (!stream) {
+		dev_warn(sof_dev->dev, "warning: stream is NULL, no stream to pause, ignore it.\n");
+		return 0;
+	}
+
+	trace_ipc_request("stream pause", stream->reply.stream_hw_id);
+
+	ret = snd_sof_stream_operations(sof_dev, IPC_STR_PAUSE,
+		stream->reply.stream_hw_id, wait);
+	if (ret < 0)
+		dev_err(sof_dev->dev, "error: failed to pause stream %d\n",
+			stream->reply.stream_hw_id);
+
+	return ret;
+}
+
+int snd_sof_stream_resume(struct snd_sof_dev *sof_dev, struct snd_sof_stream *stream,
+	int wait)
+{
+	int ret;
+
+	if (!stream) {
+		dev_warn(sof_dev->dev, "warning: stream is NULL, no stream to resume, ignore it.\n");
+		return 0;
+	}
+
+	trace_ipc_request("stream resume", stream->reply.stream_hw_id);
+
+	ret = snd_sof_stream_operations(sof_dev, IPC_STR_RESUME,
+		stream->reply.stream_hw_id, wait);
+	if (ret < 0)
+		dev_err(sof_dev->dev, "error: failed to resume stream %d\n",
+			stream->reply.stream_hw_id);
+
+	return ret;
+}
+
+
+/* Stream pointer positions */
+u32 snd_sof_dev_get_dsp_position(struct snd_sof_dev *sof_dev,
+	struct snd_sof_stream *stream)
+{
+	u32 rpos;
+
+	sst_dsp_read(sof_dev->dsp, &rpos,
+		stream->reply.read_position_register_address, sizeof(rpos));
+
+	return rpos;
+}
+
+/* Stream presentation (monotonic) positions */
+u64 snd_sof_dev_get_dsp_presentation_position(struct snd_sof_dev *sof_dev,
+	struct snd_sof_stream *stream)
+{
+	u64 ppos;
+
+	sst_dsp_read(sof_dev->dsp, &ppos,
+		stream->reply.presentation_position_register_address,
+		sizeof(ppos));
+
+	return ppos;
+}
diff --git a/sound/soc/sof/topology.c b/sound/soc/sof/topology.c
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/sound/soc/sof/topology.c
diff --git a/sound/soc/sof/trace.c b/sound/soc/sof/trace.c
new file mode 100644
index 0000000..e27e136
--- /dev/null
+++ b/sound/soc/sof/trace.c
@@ -0,0 +1,119 @@
+struct dma_trace_buffer {
+	u32 size; /* total sg elems size */
+	int read_offset; /* the read ptr in rcurrent */
+	int write_offset; /* the write ptr in wcurrent */
+	int pending_size; /* the data size pending to copy to userspace */
+	struct list_head elem_list;
+	struct list_head *rcurrent;
+	struct list_head *wcurrent;
+	u32 current_end;
+};
+
+static int sst_dma_trace_open(struct inode *inode, struct file *file)
+{
+	struct sst_hsw *hsw = inode->i_private;
+	struct sst_hsw_ipc_debug_log_enable_req *dtrace_req =
+		&hsw->dt_enable_request;
+	struct sst_hsw_ipc_debug_log_reply reply;
+	struct dma_trace_buffer *hbuf = &hsw->host_buffer;
+	u32 header;
+	int ret;
+
+	file->private_data = hsw;
+
+	reply.log_buffer_size = 0;
+	hbuf->read_offset = hbuf->write_offset = 0;
+	hbuf->pending_size = 0;
+	hbuf->rcurrent = hbuf->wcurrent = hbuf->elem_list.next;
+
+	header = IPC_GLB_TYPE(IPC_GLB_DEBUG_LOG_MESSAGE);
+
+	dtrace_req->config[0] = IPC_DEBUG_ENABLE_LOG;
+	dtrace_req->config[1] = PAGE_SIZE/2;
+	ret = sst_ipc_tx_message_wait(&hsw->ipc, header, dtrace_req,
+			  sizeof(*dtrace_req), &reply, sizeof(reply));
+	if (ret < 0) {
+		dev_err(hsw->dev, "error: stream comint failed\n");
+		return ret;
+	}
+
+	return 0;
+}
+
+static int sst_dma_trace_release(struct inode *inode, struct file *file)
+{
+	struct sst_hsw *hsw = inode->i_private;
+	struct sst_hsw_ipc_debug_log_enable_req *dtrace_req =
+		&hsw->dt_enable_request;
+	struct sst_hsw_ipc_debug_log_reply reply;
+	u32 header;
+	int ret;
+
+	header = IPC_GLB_TYPE(IPC_GLB_DEBUG_LOG_MESSAGE);
+
+	dtrace_req->config[0] = IPC_DEBUG_DISABLE_LOG;
+	dtrace_req->config[1] = PAGE_SIZE/2;
+	ret = sst_ipc_tx_message_wait(&hsw->ipc, header, dtrace_req,
+			  sizeof(*dtrace_req), &reply, sizeof(reply));
+	if (ret < 0) {
+		dev_err(hsw->dev, "error: stream comint failed\n");
+		return ret;
+	}
+
+	return 0;
+}
+
+static inline struct list_head *next_dma_trace_sg(struct sst_hsw *hsw,
+						  struct list_head *list)
+{
+	if (list_is_last(list, &hsw->host_buffer.elem_list))
+		return hsw->host_buffer.elem_list.next;
+	else
+		return list->next;
+}
+
+static ssize_t sst_dma_trace_read(struct file *file, char __user *buffer,
+					  size_t count, loff_t *ppos)
+{
+	struct sst_hsw *hsw = file->private_data;
+	struct dma_trace_buffer *hbuf = &hsw->host_buffer;
+	struct dma_trace_sg *sg_elem;
+	int sg_size, msg_size, cnt = 0, ret;
+
+	memset(buffer, 0, count);
+	while (hbuf->pending_size > 0) {
+		sg_elem = list_entry(hbuf->rcurrent, struct dma_trace_sg, list);
+		sg_size = sg_elem->size;
+		/* the dma trace buffer is a ring sg buffer
+		 * msg_size should be:
+		 * 1. if rcurrent != wcurrent, msg_size is sg_size - read_offset
+		 * 2. if rcurrent == wcurrent
+		 *  1) if pending_size > (sg_size - read_offset),
+		 *     the ring is rolled over
+		 *     (sg_size - read_offset) data should be transferred
+		 *  2) if pending_size <= (sg_size - read_offset),
+		 *     transfer all the pending_size data
+		 */
+		msg_size = min(hbuf->pending_size, sg_size - hbuf->read_offset);
+		ret = copy_to_user(buffer, sg_elem->buf + hbuf->read_offset,
+				   msg_size);
+		ret = msg_size - ret;
+		cnt += ret;
+		hbuf->read_offset += ret;
+		if (hbuf->read_offset == sg_size) {
+			hbuf->rcurrent = next_dma_trace_sg(hsw, hbuf->rcurrent);
+			hbuf->read_offset = 0;
+		}
+		hbuf->pending_size -= ret;
+	}
+
+	msleep(100);
+	return cnt + 1;
+}
+
+static const struct file_operations sst_dma_trace_fops = {
+	.open = sst_dma_trace_open,
+	.read = sst_dma_trace_read,
+	.llseek = default_llseek,
+	.release = sst_dma_trace_release,
+};