alsa-devel.alsa-project.org archive mirror
 help / color / mirror / Atom feed
From: Liam Girdwood <liam.r.girdwood@linux.intel.com>
To: alsa-devel@alsa-project.org
Cc: Liam Girdwood <liam.r.girdwood@linux.intel.com>,
	Mark Brown <broonie@kernel.org>
Subject: [PATCH 09/11] ASoC: SOF: Add firmware loader support
Date: Thu, 19 Jul 2018 19:53:33 +0100	[thread overview]
Message-ID: <20180719185335.30912-9-liam.r.girdwood@linux.intel.com> (raw)
In-Reply-To: <20180719185335.30912-1-liam.r.girdwood@linux.intel.com>

The firmware loader exports APIs that can be called by core to load and
process multiple different file formats.

Signed-off-by: Liam Girdwood <liam.r.girdwood@linux.intel.com>
---
 sound/soc/sof/loader.c | 287 +++++++++++++++++++++++++++++++++++++++++
 1 file changed, 287 insertions(+)
 create mode 100644 sound/soc/sof/loader.c

diff --git a/sound/soc/sof/loader.c b/sound/soc/sof/loader.c
new file mode 100644
index 000000000000..4bc7d7ac8ff5
--- /dev/null
+++ b/sound/soc/sof/loader.c
@@ -0,0 +1,287 @@
+// SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause)
+/*
+ * This file is provided under a dual BSD/GPLv2 license.  When using or
+ * redistributing this file, you may do so under either license.
+ *
+ * Copyright(c) 2017 Intel Corporation. All rights reserved.
+ *
+ * Author: Liam Girdwood <liam.r.girdwood@linux.intel.com>
+ *
+ * Generic firmware loader.
+ */
+
+#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-fw.h>
+#include "sof-priv.h"
+#include "ops.h"
+
+static int get_ext_windows(struct snd_sof_dev *sdev,
+			   struct sof_ipc_ext_data_hdr *ext_hdr)
+{
+	struct sof_ipc_window *w = (struct sof_ipc_window *)ext_hdr;
+
+	int ret = 0;
+	size_t size;
+
+	if (w->num_windows == 0 || w->num_windows > SOF_IPC_MAX_ELEMS)
+		return -EINVAL;
+
+	size = sizeof(*w) + sizeof(struct sof_ipc_window_elem) * w->num_windows;
+
+	/* keep a local copy of the data */
+	sdev->info_window = kmemdup(w, size, GFP_KERNEL);
+	if (!sdev->info_window)
+		return -ENOMEM;
+
+	return ret;
+}
+
+/* parse the extended FW boot data structures from FW boot message */
+int snd_sof_fw_parse_ext_data(struct snd_sof_dev *sdev, u32 offset)
+{
+	struct sof_ipc_ext_data_hdr *ext_hdr;
+	void *ext_data;
+	int ret = 0;
+
+	ext_data = kzalloc(PAGE_SIZE, GFP_KERNEL);
+	if (!ext_data)
+		return -ENOMEM;
+
+	/* get first header */
+	snd_sof_dsp_block_read(sdev, offset, ext_data, sizeof(*ext_hdr));
+	ext_hdr = (struct sof_ipc_ext_data_hdr *)ext_data;
+
+	while (ext_hdr->hdr.cmd == SOF_IPC_FW_READY) {
+		/* read in ext structure */
+		offset += sizeof(*ext_hdr);
+		snd_sof_dsp_block_read(sdev, offset,
+				       ext_data + sizeof(*ext_hdr),
+				       ext_hdr->hdr.size - sizeof(*ext_hdr));
+
+		dev_dbg(sdev->dev, "found ext header type %d size 0x%x\n",
+			ext_hdr->type, ext_hdr->hdr.size);
+
+		/* process structure data */
+		switch (ext_hdr->type) {
+		case SOF_IPC_EXT_DMA_BUFFER:
+			break;
+		case SOF_IPC_EXT_WINDOW:
+			ret = get_ext_windows(sdev, ext_hdr);
+			break;
+		default:
+			break;
+		}
+
+		if (ret < 0) {
+			dev_err(sdev->dev, "error: failed to parse ext data type %d\n",
+				ext_hdr->type);
+		}
+
+		/* move to next header */
+		offset += ext_hdr->hdr.size;
+		snd_sof_dsp_block_read(sdev, offset, ext_data,
+				       sizeof(*ext_hdr));
+		ext_hdr = (struct sof_ipc_ext_data_hdr *)ext_data;
+	}
+
+	kfree(ext_data);
+	return ret;
+}
+EXPORT_SYMBOL(snd_sof_fw_parse_ext_data);
+
+/* generic module parser for mmaped DSPs */
+int snd_sof_parse_module_memcpy(struct snd_sof_dev *sdev,
+				struct snd_sof_mod_hdr *module)
+{
+	struct snd_sof_blk_hdr *block;
+	int count;
+	u32 offset;
+
+	dev_dbg(sdev->dev, "new module size 0x%x blocks 0x%x type 0x%x\n",
+		module->size, module->num_blocks, module->type);
+
+	block = (void *)module + sizeof(*module);
+
+	for (count = 0; count < module->num_blocks; count++) {
+		if (block->size == 0) {
+			dev_warn(sdev->dev,
+				 "warning: block %d size zero\n", count);
+			dev_warn(sdev->dev, " type 0x%x offset 0x%x\n",
+				 block->type, block->offset);
+			continue;
+		}
+
+		switch (block->type) {
+		case SOF_BLK_IMAGE:
+		case SOF_BLK_CACHE:
+		case SOF_BLK_REGS:
+		case SOF_BLK_SIG:
+		case SOF_BLK_ROM:
+			continue;	/* not handled atm */
+		case SOF_BLK_TEXT:
+		case SOF_BLK_DATA:
+			offset = block->offset;
+			break;
+		default:
+			dev_err(sdev->dev, "error: bad type 0x%x for block 0x%x\n",
+				block->type, count);
+			return -EINVAL;
+		}
+
+		dev_dbg(sdev->dev,
+			"block %d type 0x%x size 0x%x ==>  offset 0x%x\n",
+			count, block->type, block->size, offset);
+
+		snd_sof_dsp_block_write(sdev, offset,
+					(void *)block + sizeof(*block),
+					block->size);
+
+		/* next block */
+		block = (void *)block + sizeof(*block) + block->size;
+	}
+
+	return 0;
+}
+EXPORT_SYMBOL(snd_sof_parse_module_memcpy);
+
+static int check_header(struct snd_sof_dev *sdev, const struct firmware *fw)
+{
+	struct snd_sof_fw_header *header;
+
+	/* Read the header information from the data pointer */
+	header = (struct snd_sof_fw_header *)fw->data;
+
+	/* verify FW sig */
+	if (strncmp(header->sig, SND_SOF_FW_SIG, SND_SOF_FW_SIG_SIZE) != 0) {
+		dev_err(sdev->dev, "error: invalid firmware signature\n");
+		return -EINVAL;
+	}
+
+	/* check size is valid */
+	if (fw->size != header->file_size + sizeof(*header)) {
+		dev_err(sdev->dev, "error: invalid filesize mismatch got 0x%zx expected 0x%zx\n",
+			fw->size, header->file_size + sizeof(*header));
+		return -EINVAL;
+	}
+
+	dev_dbg(sdev->dev, "header size=0x%x modules=0x%x abi=0x%x size=%zu\n",
+		header->file_size, header->num_modules,
+		header->abi, sizeof(*header));
+
+	return 0;
+}
+
+static int load_modules(struct snd_sof_dev *sdev, const struct firmware *fw)
+{
+	struct snd_sof_fw_header *header;
+	struct snd_sof_mod_hdr *module;
+	int (*load_module)(struct snd_sof_dev *sof_dev,
+			   struct snd_sof_mod_hdr *hdr);
+	int ret, count;
+
+	header = (struct snd_sof_fw_header *)fw->data;
+	load_module = sdev->ops->load_module;
+	if (!load_module)
+		return -EINVAL;
+
+	/* parse each module */
+	module = (void *)fw->data + sizeof(*header);
+	for (count = 0; count < header->num_modules; count++) {
+		/* module */
+		ret = load_module(sdev, module);
+		if (ret < 0) {
+			dev_err(sdev->dev, "error: invalid module %d\n", count);
+			return ret;
+		}
+		module = (void *)module + sizeof(*module) + module->size;
+	}
+
+	return 0;
+}
+
+int snd_sof_load_firmware_memcpy(struct snd_sof_dev *sdev,
+				 const struct firmware *fw)
+{
+	int ret;
+
+	/* make sure the FW header and file is valid */
+	ret = check_header(sdev, fw);
+	if (ret < 0) {
+		dev_err(sdev->dev, "error: invalid FW header\n");
+		return ret;
+	}
+
+	/* prepare the DSP for FW loading */
+	ret = snd_sof_dsp_reset(sdev);
+	if (ret < 0) {
+		dev_err(sdev->dev, "error: failed to reset DSP\n");
+		return ret;
+	}
+
+	/* parse and load firmware modules to DSP */
+	ret = load_modules(sdev, fw);
+	if (ret < 0) {
+		dev_err(sdev->dev, "error: invalid FW modules\n");
+		return ret;
+	}
+
+	return ret;
+}
+EXPORT_SYMBOL(snd_sof_load_firmware_memcpy);
+
+int snd_sof_load_firmware(struct snd_sof_dev *sdev,
+			  const struct firmware *fw)
+{
+	dev_dbg(sdev->dev, "loading firmware\n");
+
+	if (sdev->ops->load_firmware)
+		return sdev->ops->load_firmware(sdev, fw);
+	return 0;
+}
+EXPORT_SYMBOL(snd_sof_load_firmware);
+
+int snd_sof_run_firmware(struct snd_sof_dev *sdev)
+{
+	int ret;
+
+	init_waitqueue_head(&sdev->boot_wait);
+	sdev->boot_complete = false;
+
+	dev_dbg(sdev->dev, "booting DSP firmware\n");
+
+	/* boot the firmware on the DSP */
+	ret = snd_sof_dsp_run(sdev);
+	if (ret < 0) {
+		dev_err(sdev->dev, "error: failed to reset DSP\n");
+		return ret;
+	}
+
+	/* now wait for the DSP to boot */
+	ret = wait_event_timeout(sdev->boot_wait, sdev->boot_complete,
+				 msecs_to_jiffies(sdev->boot_timeout));
+	if (ret == 0) {
+		dev_err(sdev->dev, "error: firmware boot timeout\n");
+		snd_sof_dsp_dbg_dump(sdev, SOF_DBG_REGS | SOF_DBG_MBOX |
+			SOF_DBG_TEXT | SOF_DBG_PCI);
+		return -EIO;
+	}
+
+	dev_info(sdev->dev, "firmware boot complete\n");
+
+	return 0;
+}
+EXPORT_SYMBOL(snd_sof_run_firmware);
+
+void snd_sof_fw_unload(struct snd_sof_dev *sdev)
+{
+	/* TODO: support module unloading at runtime */
+}
+EXPORT_SYMBOL(snd_sof_fw_unload);
-- 
2.17.1

  parent reply	other threads:[~2018-07-19 18:54 UTC|newest]

Thread overview: 23+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2018-07-19 18:53 [PATCH 01/11] ASoC: SOF: Add Sound Open Firmware driver core Liam Girdwood
2018-07-19 18:53 ` [PATCH 02/11] ASoC: SOF: Add Sound Open Firmware KControl support Liam Girdwood
2018-07-23 18:58   ` Mark Brown
2018-07-19 18:53 ` [PATCH 03/11] ASoC: SOF: Add driver debug support Liam Girdwood
2018-07-23 19:03   ` Mark Brown
2018-07-19 18:53 ` [PATCH 04/11] ASoC: SOF: Add support for IPC IO between DSP and Host Liam Girdwood
2018-07-19 18:53 ` [PATCH 05/11] ASoC: SOF: Add PCM operations support Liam Girdwood
2018-07-24 15:07   ` Mark Brown
2018-07-19 18:53 ` [PATCH 06/11] ASoC: SOF: Add support for loading topologies Liam Girdwood
2018-07-19 18:53 ` [PATCH 07/11] ASoC: SOF: Add DSP firmware trace event support Liam Girdwood
2018-07-19 18:53 ` [PATCH 08/11] ASoC: SOF: Add DSP HW abstraction operations Liam Girdwood
2018-07-30 15:21   ` Mark Brown
2018-07-19 18:53 ` Liam Girdwood [this message]
2018-07-19 18:53 ` [PATCH 10/11] ASoC: SOF: Add userspace ABI support Liam Girdwood
2018-07-19 18:53 ` [PATCH 11/11] ASoC: SOF: Add Build support for SOF core Liam Girdwood
2018-07-20 14:11   ` Pierre-Louis Bossart
2018-07-19 18:58 ` [PATCH 01/11] ASoC: SOF: Add Sound Open Firmware driver core Takashi Iwai
2018-07-20 11:05   ` Liam Girdwood
2018-07-23 18:56 ` Mark Brown
2018-08-22 16:26   ` Liam Girdwood
2018-07-24 15:57 ` Pierre-Louis Bossart
2018-07-25  7:36   ` Takashi Iwai
2018-07-25 13:57     ` Pierre-Louis Bossart

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20180719185335.30912-9-liam.r.girdwood@linux.intel.com \
    --to=liam.r.girdwood@linux.intel.com \
    --cc=alsa-devel@alsa-project.org \
    --cc=broonie@kernel.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).