xref: /openbmc/linux/sound/soc/sof/loader.c (revision 9a29f5fc)
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. All rights reserved.
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 
17 int 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 (plat_data->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(&plat_data->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 		plat_data->fw_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 
61 err:
62 	kfree(fw_filename);
63 
64 	return ret;
65 }
66 EXPORT_SYMBOL(snd_sof_load_firmware_raw);
67 
68 int snd_sof_load_firmware_memcpy(struct snd_sof_dev *sdev)
69 {
70 	struct snd_sof_pdata *plat_data = sdev->pdata;
71 	int ret;
72 
73 	ret = snd_sof_load_firmware_raw(sdev);
74 	if (ret < 0)
75 		return ret;
76 
77 	/* make sure the FW header and file is valid */
78 	ret = sdev->ipc->ops->fw_loader->validate(sdev);
79 	if (ret < 0) {
80 		dev_err(sdev->dev, "error: invalid FW header\n");
81 		goto error;
82 	}
83 
84 	/* prepare the DSP for FW loading */
85 	ret = snd_sof_dsp_reset(sdev);
86 	if (ret < 0) {
87 		dev_err(sdev->dev, "error: failed to reset DSP\n");
88 		goto error;
89 	}
90 
91 	/* parse and load firmware modules to DSP */
92 	if (sdev->ipc->ops->fw_loader->load_fw_to_dsp) {
93 		ret = sdev->ipc->ops->fw_loader->load_fw_to_dsp(sdev);
94 		if (ret < 0) {
95 			dev_err(sdev->dev, "Firmware loading failed\n");
96 			goto error;
97 		}
98 	}
99 
100 	return 0;
101 
102 error:
103 	release_firmware(plat_data->fw);
104 	plat_data->fw = NULL;
105 	return ret;
106 
107 }
108 EXPORT_SYMBOL(snd_sof_load_firmware_memcpy);
109 
110 int snd_sof_run_firmware(struct snd_sof_dev *sdev)
111 {
112 	int ret;
113 
114 	init_waitqueue_head(&sdev->boot_wait);
115 
116 	/* (re-)enable dsp dump */
117 	sdev->dbg_dump_printed = false;
118 	sdev->ipc_dump_printed = false;
119 
120 	/* create read-only fw_version debugfs to store boot version info */
121 	if (sdev->first_boot) {
122 		ret = snd_sof_debugfs_buf_item(sdev, &sdev->fw_version,
123 					       sizeof(sdev->fw_version),
124 					       "fw_version", 0444);
125 		/* errors are only due to memory allocation, not debugfs */
126 		if (ret < 0) {
127 			dev_err(sdev->dev, "error: snd_sof_debugfs_buf_item failed\n");
128 			return ret;
129 		}
130 	}
131 
132 	/* perform pre fw run operations */
133 	ret = snd_sof_dsp_pre_fw_run(sdev);
134 	if (ret < 0) {
135 		dev_err(sdev->dev, "error: failed pre fw run op\n");
136 		return ret;
137 	}
138 
139 	dev_dbg(sdev->dev, "booting DSP firmware\n");
140 
141 	/* boot the firmware on the DSP */
142 	ret = snd_sof_dsp_run(sdev);
143 	if (ret < 0) {
144 		snd_sof_dsp_dbg_dump(sdev, "Failed to start DSP",
145 				     SOF_DBG_DUMP_MBOX | SOF_DBG_DUMP_PCI);
146 		return ret;
147 	}
148 
149 	/*
150 	 * now wait for the DSP to boot. There are 3 possible outcomes:
151 	 * 1. Boot wait times out indicating FW boot failure.
152 	 * 2. FW boots successfully and fw_ready op succeeds.
153 	 * 3. FW boots but fw_ready op fails.
154 	 */
155 	ret = wait_event_timeout(sdev->boot_wait,
156 				 sdev->fw_state > SOF_FW_BOOT_IN_PROGRESS,
157 				 msecs_to_jiffies(sdev->boot_timeout));
158 	if (ret == 0) {
159 		snd_sof_dsp_dbg_dump(sdev, "Firmware boot failure due to timeout",
160 				     SOF_DBG_DUMP_REGS | SOF_DBG_DUMP_MBOX |
161 				     SOF_DBG_DUMP_TEXT | SOF_DBG_DUMP_PCI);
162 		return -EIO;
163 	}
164 
165 	if (sdev->fw_state == SOF_FW_BOOT_READY_FAILED)
166 		return -EIO; /* FW boots but fw_ready op failed */
167 
168 	/* perform post fw run operations */
169 	ret = snd_sof_dsp_post_fw_run(sdev);
170 	if (ret < 0) {
171 		dev_err(sdev->dev, "error: failed post fw run op\n");
172 		return ret;
173 	}
174 
175 	dev_dbg(sdev->dev, "firmware boot complete\n");
176 	sof_set_fw_state(sdev, SOF_FW_BOOT_COMPLETE);
177 
178 	if (sdev->first_boot && sdev->ipc->ops->fw_loader->query_fw_configuration)
179 		return sdev->ipc->ops->fw_loader->query_fw_configuration(sdev);
180 
181 	return 0;
182 }
183 EXPORT_SYMBOL(snd_sof_run_firmware);
184 
185 void snd_sof_fw_unload(struct snd_sof_dev *sdev)
186 {
187 	/* TODO: support module unloading at runtime */
188 	release_firmware(sdev->pdata->fw);
189 	sdev->pdata->fw = NULL;
190 }
191 EXPORT_SYMBOL(snd_sof_fw_unload);
192