File Index
Files and directories
Sign in
FILE

loader.c

File preview with syntax highlighting.

linux-6.19.14/sound/soc/sof/loader.c C
  1// SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause)
  2//
  3// This file is provided under a dual BSD/GPLv2 license.  When using or
  4// redistributing this file, you may do so under either license.
  5//
  6// Copyright(c) 2018 Intel Corporation
  7//
  8// Author: Liam Girdwood <liam.r.girdwood@linux.intel.com>
  9//
 10// Generic firmware loader.
 11//
 12
 13#include <linux/firmware.h>
 14#include "sof-priv.h"
 15#include "ops.h"
 16
 17int snd_sof_load_firmware_raw(struct snd_sof_dev *sdev)
 18{
 19	struct snd_sof_pdata *plat_data = sdev->pdata;
 20	const char *fw_filename;
 21	ssize_t ext_man_size;
 22	int ret;
 23
 24	/* Don't request firmware again if firmware is already requested */
 25	if (sdev->basefw.fw)
 26		return 0;
 27
 28	fw_filename = kasprintf(GFP_KERNEL, "%s/%s",
 29				plat_data->fw_filename_prefix,
 30				plat_data->fw_filename);
 31	if (!fw_filename)
 32		return -ENOMEM;
 33
 34	ret = request_firmware(&sdev->basefw.fw, fw_filename, sdev->dev);
 35
 36	if (ret < 0) {
 37		dev_err(sdev->dev,
 38			"error: sof firmware file is missing, you might need to\n");
 39		dev_err(sdev->dev,
 40			"       download it from https://github.com/thesofproject/sof-bin/\n");
 41		goto err;
 42	} else {
 43		dev_dbg(sdev->dev, "request_firmware %s successful\n",
 44			fw_filename);
 45	}
 46
 47	/* check for extended manifest */
 48	ext_man_size = sdev->ipc->ops->fw_loader->parse_ext_manifest(sdev);
 49	if (ext_man_size > 0) {
 50		/* when no error occurred, drop extended manifest */
 51		sdev->basefw.payload_offset = ext_man_size;
 52	} else if (!ext_man_size) {
 53		/* No extended manifest, so nothing to skip during FW load */
 54		dev_dbg(sdev->dev, "firmware doesn't contain extended manifest\n");
 55	} else {
 56		ret = ext_man_size;
 57		dev_err(sdev->dev, "error: firmware %s contains unsupported or invalid extended manifest: %d\n",
 58			fw_filename, ret);
 59	}
 60
 61err:
 62	kfree(fw_filename);
 63
 64	return ret;
 65}
 66EXPORT_SYMBOL(snd_sof_load_firmware_raw);
 67
 68int snd_sof_load_firmware_memcpy(struct snd_sof_dev *sdev)
 69{
 70	int ret;
 71
 72	ret = snd_sof_load_firmware_raw(sdev);
 73	if (ret < 0)
 74		return ret;
 75
 76	/* make sure the FW header and file is valid */
 77	ret = sdev->ipc->ops->fw_loader->validate(sdev);
 78	if (ret < 0) {
 79		dev_err(sdev->dev, "error: invalid FW header\n");
 80		goto error;
 81	}
 82
 83	/* prepare the DSP for FW loading */
 84	ret = snd_sof_dsp_reset(sdev);
 85	if (ret < 0) {
 86		dev_err(sdev->dev, "error: failed to reset DSP\n");
 87		goto error;
 88	}
 89
 90	/* parse and load firmware modules to DSP */
 91	if (sdev->ipc->ops->fw_loader->load_fw_to_dsp) {
 92		ret = sdev->ipc->ops->fw_loader->load_fw_to_dsp(sdev);
 93		if (ret < 0) {
 94			dev_err(sdev->dev, "Firmware loading failed\n");
 95			goto error;
 96		}
 97	}
 98
 99	return 0;
100
101error:
102	release_firmware(sdev->basefw.fw);
103	sdev->basefw.fw = NULL;
104	return ret;
105
106}
107EXPORT_SYMBOL(snd_sof_load_firmware_memcpy);
108
109int snd_sof_run_firmware(struct snd_sof_dev *sdev)
110{
111	int ret;
112
113	init_waitqueue_head(&sdev->boot_wait);
114
115	/* (re-)enable dsp dump */
116	sdev->dbg_dump_printed = false;
117	sdev->ipc_dump_printed = false;
118
119	/* create read-only fw_version debugfs to store boot version info */
120	if (sdev->first_boot) {
121		ret = snd_sof_debugfs_buf_item(sdev, &sdev->fw_version,
122					       sizeof(sdev->fw_version),
123					       "fw_version", 0444);
124		/* errors are only due to memory allocation, not debugfs */
125		if (ret < 0) {
126			dev_err(sdev->dev, "snd_sof_debugfs_buf_item failed\n");
127			return ret;
128		}
129	}
130
131	/* perform pre fw run operations */
132	ret = snd_sof_dsp_pre_fw_run(sdev);
133	if (ret < 0) {
134		dev_err(sdev->dev, "failed pre fw run op\n");
135		return ret;
136	}
137
138	dev_dbg(sdev->dev, "booting DSP firmware\n");
139
140	/* boot the firmware on the DSP */
141	ret = snd_sof_dsp_run(sdev);
142	if (ret < 0) {
143		snd_sof_dsp_dbg_dump(sdev, "Failed to start DSP",
144				     SOF_DBG_DUMP_MBOX | SOF_DBG_DUMP_PCI);
145		return ret;
146	}
147
148	/*
149	 * now wait for the DSP to boot. There are 3 possible outcomes:
150	 * 1. Boot wait times out indicating FW boot failure.
151	 * 2. FW boots successfully and fw_ready op succeeds.
152	 * 3. FW boots but fw_ready op fails.
153	 */
154	ret = wait_event_timeout(sdev->boot_wait,
155				 sdev->fw_state > SOF_FW_BOOT_IN_PROGRESS,
156				 msecs_to_jiffies(sdev->boot_timeout));
157	if (ret == 0) {
158		snd_sof_dsp_dbg_dump(sdev, "Firmware boot failure due to timeout",
159				     SOF_DBG_DUMP_REGS | SOF_DBG_DUMP_MBOX |
160				     SOF_DBG_DUMP_TEXT | SOF_DBG_DUMP_PCI);
161		return -EIO;
162	}
163
164	if (sdev->fw_state == SOF_FW_BOOT_READY_FAILED)
165		return -EIO; /* FW boots but fw_ready op failed */
166
167	dev_dbg(sdev->dev, "firmware boot complete\n");
168	sof_set_fw_state(sdev, SOF_FW_BOOT_COMPLETE);
169
170	/* perform post fw run operations */
171	ret = snd_sof_dsp_post_fw_run(sdev);
172	if (ret < 0) {
173		dev_err(sdev->dev, "error: failed post fw run op\n");
174		return ret;
175	}
176
177	if (sdev->ipc->ops->post_fw_boot)
178		return sdev->ipc->ops->post_fw_boot(sdev);
179
180	return 0;
181}
182EXPORT_SYMBOL(snd_sof_run_firmware);
183
184void snd_sof_fw_unload(struct snd_sof_dev *sdev)
185{
186	/* TODO: support module unloading at runtime */
187	release_firmware(sdev->basefw.fw);
188	sdev->basefw.fw = NULL;
189}
190EXPORT_SYMBOL(snd_sof_fw_unload);
~/ Read-only file preview.