diff mbox

[v4,2/7] ASoC: Intel: mrfld - Add DSP load and management

Message ID 1413469819-18775-3-git-send-email-vinod.koul@intel.com (mailing list archive)
State Accepted
Commit 9012c9544eeac485b2193fea721233907f0847fa
Headers show

Commit Message

Vinod Koul Oct. 16, 2014, 2:30 p.m. UTC
This patch contains all dsp controlling functions like firmware download,
setting/resetting dsp cores, etc.

Signed-off-by: Subhransu S. Prusty <subhransu.s.prusty@intel.com>
Signed-off-by: Vinod Koul <vinod.koul@intel.com>
---
 sound/soc/intel/sst/sst_loader.c |  461 ++++++++++++++++++++++++++++++++++++++
 1 files changed, 461 insertions(+), 0 deletions(-)
 create mode 100644 sound/soc/intel/sst/sst_loader.c

Comments

Vinod Koul Oct. 17, 2014, 9:18 a.m. UTC | #1
On Fri, Oct 17, 2014 at 11:44:14AM +0200, Mark Brown wrote:
> On Thu, Oct 16, 2014 at 08:00:14PM +0530, Vinod Koul wrote:
> 
> > +static void memcpy32_toio(void __iomem *dst, const void *src, int count)
> > +{
> > +	int i;
> > +	const u32 *src_32 = src;
> > +	u32 *dst_32 = dst;
> > +
> > +	for (i = 0; i < count/sizeof(u32); i++)
> > +		writel(*src_32++, dst_32++);
> > +}
> 
> We also have a memcpy32_toio in atmel_nand.c which I pointed out before
> and now another in mxc_nand.c.  All of which use different writel
> variants.  This isn't awesome, we should at least try to clean this up
> rather than add another duplicate - by the time we're onto the third
> user there's a clear need for something generic.  Do you have any
> thoughts on dealing with this?
I dont mind moving this and other users to common handler. I can send a
patch to add that. I think Subranshu did send one but that needs to be
improved upon. Also this should be in BYT driver as well.

But creates dependency for us
> 
> Otherwise the patch looks good.
Thanks, since the size is bit on higher size and no other issues, would it
be okay to merge this.

I will work on moving memcpy32_toio to core and move users as well :)


Thanks
Mark Brown Oct. 17, 2014, 9:44 a.m. UTC | #2
On Thu, Oct 16, 2014 at 08:00:14PM +0530, Vinod Koul wrote:

> +static void memcpy32_toio(void __iomem *dst, const void *src, int count)
> +{
> +	int i;
> +	const u32 *src_32 = src;
> +	u32 *dst_32 = dst;
> +
> +	for (i = 0; i < count/sizeof(u32); i++)
> +		writel(*src_32++, dst_32++);
> +}

We also have a memcpy32_toio in atmel_nand.c which I pointed out before
and now another in mxc_nand.c.  All of which use different writel
variants.  This isn't awesome, we should at least try to clean this up
rather than add another duplicate - by the time we're onto the third
user there's a clear need for something generic.  Do you have any
thoughts on dealing with this?

Otherwise the patch looks good.
Takashi Iwai Oct. 17, 2014, 10:04 a.m. UTC | #3
At Fri, 17 Oct 2014 14:48:16 +0530,
Vinod Koul wrote:
> 
> On Fri, Oct 17, 2014 at 11:44:14AM +0200, Mark Brown wrote:
> > On Thu, Oct 16, 2014 at 08:00:14PM +0530, Vinod Koul wrote:
> > 
> > > +static void memcpy32_toio(void __iomem *dst, const void *src, int count)
> > > +{
> > > +	int i;
> > > +	const u32 *src_32 = src;
> > > +	u32 *dst_32 = dst;
> > > +
> > > +	for (i = 0; i < count/sizeof(u32); i++)
> > > +		writel(*src_32++, dst_32++);
> > > +}
> > 
> > We also have a memcpy32_toio in atmel_nand.c which I pointed out before
> > and now another in mxc_nand.c.  All of which use different writel
> > variants.  This isn't awesome, we should at least try to clean this up
> > rather than add another duplicate - by the time we're onto the third
> > user there's a clear need for something generic.  Do you have any
> > thoughts on dealing with this?
> I dont mind moving this and other users to common handler. I can send a
> patch to add that. I think Subranshu did send one but that needs to be
> improved upon. Also this should be in BYT driver as well.
> 
> But creates dependency for us
> > 
> > Otherwise the patch looks good.
> Thanks, since the size is bit on higher size and no other issues, would it
> be okay to merge this.
> 
> I will work on moving memcpy32_toio to core and move users as well :)

We can move them into sound/core/memory.c.  They are pretty small and
useful.


Takashi
Mark Brown Oct. 17, 2014, 12:01 p.m. UTC | #4
On Fri, Oct 17, 2014 at 12:04:56PM +0200, Takashi Iwai wrote:
> Vinod Koul wrote:

> > I will work on moving memcpy32_toio to core and move users as well :)

> We can move them into sound/core/memory.c.  They are pretty small and
> useful.

It's not really anything to do with sound though, the other users are
MTD devices at the minute.
Takashi Iwai Oct. 17, 2014, 12:07 p.m. UTC | #5
At Fri, 17 Oct 2014 14:01:47 +0200,
Mark Brown wrote:
> 
> On Fri, Oct 17, 2014 at 12:04:56PM +0200, Takashi Iwai wrote:
> > Vinod Koul wrote:
> 
> > > I will work on moving memcpy32_toio to core and move users as well :)
> 
> > We can move them into sound/core/memory.c.  They are pretty small and
> > useful.
> 
> It's not really anything to do with sound though, the other users are
> MTD devices at the minute.

Yeah, we can move up again, then :)

I think it's even not bad to have it as a static inline function.


Takashi
Vinod Koul Oct. 20, 2014, 5:41 a.m. UTC | #6
On Fri, Oct 17, 2014 at 02:07:43PM +0200, Takashi Iwai wrote:
> At Fri, 17 Oct 2014 14:01:47 +0200,
> Mark Brown wrote:
> > 
> > On Fri, Oct 17, 2014 at 12:04:56PM +0200, Takashi Iwai wrote:
> > > Vinod Koul wrote:
> > 
> > > > I will work on moving memcpy32_toio to core and move users as well :)

And I did prepare RFC for that and had to trash it :(

Turns out we already have an API for this in kernel.

While adding the new header, though it should be io.h, stumbled upon not
much used __iowrite32_copy() which seems to be the API I need.

I am testing it as we speak, will send conversion patch today.
Mark Brown Oct. 20, 2014, 3:29 p.m. UTC | #7
On Mon, Oct 20, 2014 at 11:11:49AM +0530, Vinod Koul wrote:
> On Fri, Oct 17, 2014 at 02:07:43PM +0200, Takashi Iwai wrote:

> > > > > I will work on moving memcpy32_toio to core and move users as well :)

> And I did prepare RFC for that and had to trash it :(

> Turns out we already have an API for this in kernel.

> While adding the new header, though it should be io.h, stumbled upon not
> much used __iowrite32_copy() which seems to be the API I need.

> I am testing it as we speak, will send conversion patch today.

Probably best to send patches for the MTD devices too while you're at
it.
Vinod Koul Oct. 20, 2014, 4:07 p.m. UTC | #8
On Mon, Oct 20, 2014 at 04:29:00PM +0100, Mark Brown wrote:
> On Mon, Oct 20, 2014 at 11:11:49AM +0530, Vinod Koul wrote:
> > On Fri, Oct 17, 2014 at 02:07:43PM +0200, Takashi Iwai wrote:
> 
> > > > > > I will work on moving memcpy32_toio to core and move users as well :)
> 
> > And I did prepare RFC for that and had to trash it :(
> 
> > Turns out we already have an API for this in kernel.
> 
> > While adding the new header, though it should be io.h, stumbled upon not
> > much used __iowrite32_copy() which seems to be the API I need.
> 
> > I am testing it as we speak, will send conversion patch today.
> 
> Probably best to send patches for the MTD devices too while you're at
> it.
Yup, sent out few moments back.

Thanks
diff mbox

Patch

diff --git a/sound/soc/intel/sst/sst_loader.c b/sound/soc/intel/sst/sst_loader.c
new file mode 100644
index 0000000..b6d27c1
--- /dev/null
+++ b/sound/soc/intel/sst/sst_loader.c
@@ -0,0 +1,461 @@ 
+/*
+ *  sst_dsp.c - Intel SST Driver for audio engine
+ *
+ *  Copyright (C) 2008-14	Intel Corp
+ *  Authors:	Vinod Koul <vinod.koul@intel.com>
+ *		Harsha Priya <priya.harsha@intel.com>
+ *		Dharageswari R <dharageswari.r@intel.com>
+ *		KP Jeeja <jeeja.kp@intel.com>
+ *  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; version 2 of the License.
+ *
+ *  This program is distributed in the hope that it will be useful, but
+ *  WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ *  General Public License for more details.
+ *
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ *
+ *  This file contains all dsp controlling functions like firmware download,
+ * setting/resetting dsp cores, etc
+ */
+#include <linux/pci.h>
+#include <linux/delay.h>
+#include <linux/fs.h>
+#include <linux/sched.h>
+#include <linux/firmware.h>
+#include <linux/dmaengine.h>
+#include <linux/pm_runtime.h>
+#include <linux/pm_qos.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/soc.h>
+#include <sound/compress_driver.h>
+#include <asm/platform_sst_audio.h>
+#include "../sst-mfld-platform.h"
+#include "sst.h"
+#include "../sst-dsp.h"
+
+static void memcpy32_toio(void __iomem *dst, const void *src, int count)
+{
+	int i;
+	const u32 *src_32 = src;
+	u32 *dst_32 = dst;
+
+	for (i = 0; i < count/sizeof(u32); i++)
+		writel(*src_32++, dst_32++);
+}
+
+/**
+ * intel_sst_reset_dsp_mrfld - Resetting SST DSP
+ *
+ * This resets DSP in case of MRFLD platfroms
+ */
+int intel_sst_reset_dsp_mrfld(struct intel_sst_drv *sst_drv_ctx)
+{
+	union config_status_reg_mrfld csr;
+
+	dev_dbg(sst_drv_ctx->dev, "sst: Resetting the DSP in mrfld\n");
+	csr.full = sst_shim_read64(sst_drv_ctx->shim, SST_CSR);
+
+	dev_dbg(sst_drv_ctx->dev, "value:0x%llx\n", csr.full);
+
+	csr.full |= 0x7;
+	sst_shim_write64(sst_drv_ctx->shim, SST_CSR, csr.full);
+	csr.full = sst_shim_read64(sst_drv_ctx->shim, SST_CSR);
+
+	dev_dbg(sst_drv_ctx->dev, "value:0x%llx\n", csr.full);
+
+	csr.full &= ~(0x1);
+	sst_shim_write64(sst_drv_ctx->shim, SST_CSR, csr.full);
+
+	csr.full = sst_shim_read64(sst_drv_ctx->shim, SST_CSR);
+	dev_dbg(sst_drv_ctx->dev, "value:0x%llx\n", csr.full);
+	return 0;
+}
+
+/**
+ * sst_start_merrifield - Start the SST DSP processor
+ *
+ * This starts the DSP in MERRIFIELD platfroms
+ */
+int sst_start_mrfld(struct intel_sst_drv *sst_drv_ctx)
+{
+	union config_status_reg_mrfld csr;
+
+	dev_dbg(sst_drv_ctx->dev, "sst: Starting the DSP in mrfld LALALALA\n");
+	csr.full = sst_shim_read64(sst_drv_ctx->shim, SST_CSR);
+	dev_dbg(sst_drv_ctx->dev, "value:0x%llx\n", csr.full);
+
+	csr.full |= 0x7;
+	sst_shim_write64(sst_drv_ctx->shim, SST_CSR, csr.full);
+
+	csr.full = sst_shim_read64(sst_drv_ctx->shim, SST_CSR);
+	dev_dbg(sst_drv_ctx->dev, "value:0x%llx\n", csr.full);
+
+	csr.part.xt_snoop = 1;
+	csr.full &= ~(0x5);
+	sst_shim_write64(sst_drv_ctx->shim, SST_CSR, csr.full);
+
+	csr.full = sst_shim_read64(sst_drv_ctx->shim, SST_CSR);
+	dev_dbg(sst_drv_ctx->dev, "sst: Starting the DSP_merrifield:%llx\n",
+			csr.full);
+	return 0;
+}
+
+static int sst_validate_fw_image(struct intel_sst_drv *ctx, unsigned long size,
+		struct fw_module_header **module, u32 *num_modules)
+{
+	struct sst_fw_header *header;
+	const void *sst_fw_in_mem = ctx->fw_in_mem;
+
+	dev_dbg(ctx->dev, "Enter\n");
+
+	/* Read the header information from the data pointer */
+	header = (struct sst_fw_header *)sst_fw_in_mem;
+	dev_dbg(ctx->dev,
+		"header sign=%s size=%x modules=%x fmt=%x size=%zx\n",
+		header->signature, header->file_size, header->modules,
+		header->file_format, sizeof(*header));
+
+	/* verify FW */
+	if ((strncmp(header->signature, SST_FW_SIGN, 4) != 0) ||
+		(size != header->file_size + sizeof(*header))) {
+		/* Invalid FW signature */
+		dev_err(ctx->dev, "InvalidFW sign/filesize mismatch\n");
+		return -EINVAL;
+	}
+	*num_modules = header->modules;
+	*module = (void *)sst_fw_in_mem + sizeof(*header);
+
+	return 0;
+}
+
+/*
+ * sst_fill_memcpy_list - Fill the memcpy list
+ *
+ * @memcpy_list: List to be filled
+ * @destn: Destination addr to be filled in the list
+ * @src: Source addr to be filled in the list
+ * @size: Size to be filled in the list
+ *
+ * Adds the node to the list after required fields
+ * are populated in the node
+ */
+static int sst_fill_memcpy_list(struct list_head *memcpy_list,
+			void *destn, const void *src, u32 size, bool is_io)
+{
+	struct sst_memcpy_list *listnode;
+
+	listnode = kzalloc(sizeof(*listnode), GFP_KERNEL);
+	if (listnode == NULL)
+		return -ENOMEM;
+	listnode->dstn = destn;
+	listnode->src = src;
+	listnode->size = size;
+	listnode->is_io = is_io;
+	list_add_tail(&listnode->memcpylist, memcpy_list);
+
+	return 0;
+}
+
+/**
+ * sst_parse_module_memcpy - Parse audio FW modules and populate the memcpy list
+ *
+ * @sst_drv_ctx		: driver context
+ * @module		: FW module header
+ * @memcpy_list	: Pointer to the list to be populated
+ * Create the memcpy list as the number of block to be copied
+ * returns error or 0 if module sizes are proper
+ */
+static int sst_parse_module_memcpy(struct intel_sst_drv *sst_drv_ctx,
+		struct fw_module_header *module, struct list_head *memcpy_list)
+{
+	struct fw_block_info *block;
+	u32 count;
+	int ret_val = 0;
+	void __iomem *ram_iomem;
+
+	dev_dbg(sst_drv_ctx->dev, "module sign %s size %x blocks %x type %x\n",
+			module->signature, module->mod_size,
+			module->blocks, module->type);
+	dev_dbg(sst_drv_ctx->dev, "module entrypoint 0x%x\n", module->entry_point);
+
+	block = (void *)module + sizeof(*module);
+
+	for (count = 0; count < module->blocks; count++) {
+		if (block->size <= 0) {
+			dev_err(sst_drv_ctx->dev, "block size invalid\n");
+			return -EINVAL;
+		}
+		switch (block->type) {
+		case SST_IRAM:
+			ram_iomem = sst_drv_ctx->iram;
+			break;
+		case SST_DRAM:
+			ram_iomem = sst_drv_ctx->dram;
+			break;
+		case SST_DDR:
+			ram_iomem = sst_drv_ctx->ddr;
+			break;
+		case SST_CUSTOM_INFO:
+			block = (void *)block + sizeof(*block) + block->size;
+			continue;
+		default:
+			dev_err(sst_drv_ctx->dev, "wrong ram type0x%x in block0x%x\n",
+					block->type, count);
+			return -EINVAL;
+		}
+
+		ret_val = sst_fill_memcpy_list(memcpy_list,
+				ram_iomem + block->ram_offset,
+				(void *)block + sizeof(*block), block->size, 1);
+		if (ret_val)
+			return ret_val;
+
+		block = (void *)block + sizeof(*block) + block->size;
+	}
+	return 0;
+}
+
+/**
+ * sst_parse_fw_memcpy - parse the firmware image & populate the list for memcpy
+ *
+ * @ctx			: pointer to drv context
+ * @size		: size of the firmware
+ * @fw_list		: pointer to list_head to be populated
+ * This function parses the FW image and saves the parsed image in the list
+ * for memcpy
+ */
+static int sst_parse_fw_memcpy(struct intel_sst_drv *ctx, unsigned long size,
+				struct list_head *fw_list)
+{
+	struct fw_module_header *module;
+	u32 count, num_modules;
+	int ret_val;
+
+	ret_val = sst_validate_fw_image(ctx, size, &module, &num_modules);
+	if (ret_val)
+		return ret_val;
+
+	for (count = 0; count < num_modules; count++) {
+		ret_val = sst_parse_module_memcpy(ctx, module, fw_list);
+		if (ret_val)
+			return ret_val;
+		module = (void *)module + sizeof(*module) + module->mod_size;
+	}
+
+	return 0;
+}
+
+/**
+ * sst_do_memcpy - function initiates the memcpy
+ *
+ * @memcpy_list: Pter to memcpy list on which the memcpy needs to be initiated
+ *
+ * Triggers the memcpy
+ */
+static void sst_do_memcpy(struct list_head *memcpy_list)
+{
+	struct sst_memcpy_list *listnode;
+
+	list_for_each_entry(listnode, memcpy_list, memcpylist) {
+		if (listnode->is_io == true)
+			memcpy32_toio((void __iomem *)listnode->dstn,
+					listnode->src, listnode->size);
+		else
+			memcpy(listnode->dstn, listnode->src, listnode->size);
+	}
+}
+
+void sst_memcpy_free_resources(struct intel_sst_drv *sst_drv_ctx)
+{
+	struct sst_memcpy_list *listnode, *tmplistnode;
+
+	/* Free the list */
+	if (!list_empty(&sst_drv_ctx->memcpy_list)) {
+		list_for_each_entry_safe(listnode, tmplistnode,
+				&sst_drv_ctx->memcpy_list, memcpylist) {
+			list_del(&listnode->memcpylist);
+			kfree(listnode);
+		}
+	}
+}
+
+static int sst_cache_and_parse_fw(struct intel_sst_drv *sst,
+		const struct firmware *fw)
+{
+	int retval = 0;
+
+	sst->fw_in_mem = kzalloc(fw->size, GFP_KERNEL);
+	if (!sst->fw_in_mem) {
+		retval = -ENOMEM;
+		goto end_release;
+	}
+	dev_dbg(sst->dev, "copied fw to %p", sst->fw_in_mem);
+	dev_dbg(sst->dev, "phys: %lx", (unsigned long)virt_to_phys(sst->fw_in_mem));
+	memcpy(sst->fw_in_mem, fw->data, fw->size);
+	retval = sst_parse_fw_memcpy(sst, fw->size, &sst->memcpy_list);
+	if (retval) {
+		dev_err(sst->dev, "Failed to parse fw\n");
+		kfree(sst->fw_in_mem);
+		sst->fw_in_mem = NULL;
+	}
+
+end_release:
+	release_firmware(fw);
+	return retval;
+
+}
+
+void sst_firmware_load_cb(const struct firmware *fw, void *context)
+{
+	struct intel_sst_drv *ctx = context;
+
+	dev_dbg(ctx->dev, "Enter\n");
+
+	if (fw == NULL) {
+		dev_err(ctx->dev, "request fw failed\n");
+		return;
+	}
+
+	mutex_lock(&ctx->sst_lock);
+
+	if (ctx->sst_state != SST_RESET ||
+			ctx->fw_in_mem != NULL) {
+		if (fw != NULL)
+			release_firmware(fw);
+		mutex_unlock(&ctx->sst_lock);
+		return;
+	}
+
+	dev_dbg(ctx->dev, "Request Fw completed\n");
+	sst_cache_and_parse_fw(ctx, fw);
+	mutex_unlock(&ctx->sst_lock);
+}
+
+/*
+ * sst_request_fw - requests audio fw from kernel and saves a copy
+ *
+ * This function requests the SST FW from the kernel, parses it and
+ * saves a copy in the driver context
+ */
+static int sst_request_fw(struct intel_sst_drv *sst)
+{
+	int retval = 0;
+	char name[20];
+	const struct firmware *fw;
+
+	dev_dbg(sst->dev, "Requesting FW %s now...\n", name);
+
+	retval = request_firmware(&fw, name, sst->dev);
+	if (fw == NULL) {
+		dev_err(sst->dev, "fw is returning as null\n");
+		return -EINVAL;
+	}
+	if (retval) {
+		dev_err(sst->dev, "request fw failed %d\n", retval);
+		return retval;
+	}
+	mutex_lock(&sst->sst_lock);
+	retval = sst_cache_and_parse_fw(sst, fw);
+	mutex_unlock(&sst->sst_lock);
+
+	return retval;
+}
+
+/*
+ * Writing the DDR physical base to DCCM offset
+ * so that FW can use it to setup TLB
+ */
+static void sst_dccm_config_write(void __iomem *dram_base,
+		unsigned int ddr_base)
+{
+	void __iomem *addr;
+	u32 bss_reset = 0;
+
+	addr = (void __iomem *)(dram_base + MRFLD_FW_DDR_BASE_OFFSET);
+	memcpy32_toio(addr, (void *)&ddr_base, sizeof(u32));
+	bss_reset |= (1 << MRFLD_FW_BSS_RESET_BIT);
+	addr = (void __iomem *)(dram_base + MRFLD_FW_FEATURE_BASE_OFFSET);
+	memcpy32_toio(addr, &bss_reset, sizeof(u32));
+
+}
+
+void sst_post_download_mrfld(struct intel_sst_drv *ctx)
+{
+	sst_dccm_config_write(ctx->dram, ctx->ddr_base);
+	dev_dbg(ctx->dev, "config written to DCCM\n");
+}
+
+/**
+ * sst_load_fw - function to load FW into DSP
+ * Transfers the FW to DSP using dma/memcpy
+ */
+int sst_load_fw(struct intel_sst_drv *sst_drv_ctx)
+{
+	int ret_val = 0;
+	struct sst_block *block;
+
+	dev_dbg(sst_drv_ctx->dev, "sst_load_fw\n");
+
+	if (sst_drv_ctx->sst_state !=  SST_RESET ||
+			sst_drv_ctx->sst_state == SST_SHUTDOWN)
+		return -EAGAIN;
+
+	if (!sst_drv_ctx->fw_in_mem) {
+		dev_dbg(sst_drv_ctx->dev, "sst: FW not in memory retry to download\n");
+		ret_val = sst_request_fw(sst_drv_ctx);
+		if (ret_val)
+			return ret_val;
+	}
+
+	BUG_ON(!sst_drv_ctx->fw_in_mem);
+	block = sst_create_block(sst_drv_ctx, 0, FW_DWNL_ID);
+	if (block == NULL)
+		return -ENOMEM;
+
+	/* Prevent C-states beyond C6 */
+	pm_qos_update_request(sst_drv_ctx->qos, 0);
+
+	sst_drv_ctx->sst_state = SST_FW_LOADING;
+
+	ret_val = sst_drv_ctx->ops->reset(sst_drv_ctx);
+	if (ret_val)
+		goto restore;
+
+	sst_do_memcpy(&sst_drv_ctx->memcpy_list);
+
+	/* Write the DRAM/DCCM config before enabling FW */
+	if (sst_drv_ctx->ops->post_download)
+		sst_drv_ctx->ops->post_download(sst_drv_ctx);
+
+	/* bring sst out of reset */
+	ret_val = sst_drv_ctx->ops->start(sst_drv_ctx);
+	if (ret_val)
+		goto restore;
+
+	ret_val = sst_wait_timeout(sst_drv_ctx, block);
+	if (ret_val) {
+		dev_err(sst_drv_ctx->dev, "fw download failed %d\n" , ret_val);
+		/* FW download failed due to timeout */
+		ret_val = -EBUSY;
+
+	}
+
+
+restore:
+	/* Re-enable Deeper C-states beyond C6 */
+	pm_qos_update_request(sst_drv_ctx->qos, PM_QOS_DEFAULT_VALUE);
+	sst_free_block(sst_drv_ctx, block);
+	dev_dbg(sst_drv_ctx->dev, "fw load successful!!!\n");
+
+	if (sst_drv_ctx->ops->restore_dsp_context)
+		sst_drv_ctx->ops->restore_dsp_context();
+	sst_drv_ctx->sst_state = SST_FW_RUNNING;
+	return ret_val;
+}
+