Pierre-Louis Bossart | e149ca2 | 2020-05-01 09:58:50 -0500 | [diff] [blame] | 1 | // SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause) |
Liam Girdwood | 70cd525 | 2019-04-12 11:05:14 -0500 | [diff] [blame] | 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 <sound/sof.h> |
Karol Trzcinski | a80cf19 | 2020-05-20 19:59:08 +0300 | [diff] [blame] | 15 | #include <sound/sof/ext_manifest.h> |
Rander Wang | febf5da | 2021-10-08 12:38:36 +0300 | [diff] [blame] | 16 | #include "sof-priv.h" |
Liam Girdwood | 70cd525 | 2019-04-12 11:05:14 -0500 | [diff] [blame] | 17 | #include "ops.h" |
| 18 | |
| 19 | static int get_ext_windows(struct snd_sof_dev *sdev, |
Karol Trzcinski | 0730c09 | 2020-04-15 15:27:54 -0500 | [diff] [blame] | 20 | const struct sof_ipc_ext_data_hdr *ext_hdr) |
Liam Girdwood | 70cd525 | 2019-04-12 11:05:14 -0500 | [diff] [blame] | 21 | { |
Karol Trzcinski | 0730c09 | 2020-04-15 15:27:54 -0500 | [diff] [blame] | 22 | const struct sof_ipc_window *w = |
Liam Girdwood | 70cd525 | 2019-04-12 11:05:14 -0500 | [diff] [blame] | 23 | container_of(ext_hdr, struct sof_ipc_window, ext_hdr); |
Liam Girdwood | 70cd525 | 2019-04-12 11:05:14 -0500 | [diff] [blame] | 24 | |
| 25 | if (w->num_windows == 0 || w->num_windows > SOF_IPC_MAX_ELEMS) |
| 26 | return -EINVAL; |
| 27 | |
Karol Trzcinski | 8d809c1 | 2020-05-20 19:59:10 +0300 | [diff] [blame] | 28 | if (sdev->info_window) { |
Karol Trzcinski | 76ab546 | 2020-08-25 16:58:51 -0700 | [diff] [blame] | 29 | if (memcmp(sdev->info_window, w, ext_hdr->hdr.size)) { |
Karol Trzcinski | 8d809c1 | 2020-05-20 19:59:10 +0300 | [diff] [blame] | 30 | dev_err(sdev->dev, "error: mismatch between window descriptor from extended manifest and mailbox"); |
| 31 | return -EINVAL; |
| 32 | } |
| 33 | return 0; |
| 34 | } |
| 35 | |
Liam Girdwood | 70cd525 | 2019-04-12 11:05:14 -0500 | [diff] [blame] | 36 | /* keep a local copy of the data */ |
Pierre-Louis Bossart | e9157a4 | 2020-08-25 16:58:52 -0700 | [diff] [blame] | 37 | sdev->info_window = devm_kmemdup(sdev->dev, w, ext_hdr->hdr.size, |
| 38 | GFP_KERNEL); |
Liam Girdwood | 70cd525 | 2019-04-12 11:05:14 -0500 | [diff] [blame] | 39 | if (!sdev->info_window) |
| 40 | return -ENOMEM; |
| 41 | |
| 42 | return 0; |
| 43 | } |
| 44 | |
Karol Trzcinski | 5928395 | 2019-12-17 18:26:11 -0600 | [diff] [blame] | 45 | static int get_cc_info(struct snd_sof_dev *sdev, |
Karol Trzcinski | 0730c09 | 2020-04-15 15:27:54 -0500 | [diff] [blame] | 46 | const struct sof_ipc_ext_data_hdr *ext_hdr) |
Karol Trzcinski | 5928395 | 2019-12-17 18:26:11 -0600 | [diff] [blame] | 47 | { |
| 48 | int ret; |
| 49 | |
Karol Trzcinski | 0730c09 | 2020-04-15 15:27:54 -0500 | [diff] [blame] | 50 | const struct sof_ipc_cc_version *cc = |
Karol Trzcinski | 5928395 | 2019-12-17 18:26:11 -0600 | [diff] [blame] | 51 | container_of(ext_hdr, struct sof_ipc_cc_version, ext_hdr); |
| 52 | |
Karol Trzcinski | 4c4a975 | 2020-05-20 19:59:11 +0300 | [diff] [blame] | 53 | if (sdev->cc_version) { |
| 54 | if (memcmp(sdev->cc_version, cc, cc->ext_hdr.hdr.size)) { |
| 55 | dev_err(sdev->dev, "error: receive diverged cc_version descriptions"); |
| 56 | return -EINVAL; |
| 57 | } |
| 58 | return 0; |
| 59 | } |
| 60 | |
Karol Trzcinski | 5928395 | 2019-12-17 18:26:11 -0600 | [diff] [blame] | 61 | dev_dbg(sdev->dev, "Firmware info: used compiler %s %d:%d:%d%s used optimization flags %s\n", |
| 62 | cc->name, cc->major, cc->minor, cc->micro, cc->desc, |
| 63 | cc->optim); |
| 64 | |
| 65 | /* create read-only cc_version debugfs to store compiler version info */ |
| 66 | /* use local copy of the cc_version to prevent data corruption */ |
| 67 | if (sdev->first_boot) { |
| 68 | sdev->cc_version = devm_kmalloc(sdev->dev, cc->ext_hdr.hdr.size, |
| 69 | GFP_KERNEL); |
| 70 | |
| 71 | if (!sdev->cc_version) |
| 72 | return -ENOMEM; |
| 73 | |
| 74 | memcpy(sdev->cc_version, cc, cc->ext_hdr.hdr.size); |
| 75 | ret = snd_sof_debugfs_buf_item(sdev, sdev->cc_version, |
| 76 | cc->ext_hdr.hdr.size, |
| 77 | "cc_version", 0444); |
| 78 | |
| 79 | /* errors are only due to memory allocation, not debugfs */ |
| 80 | if (ret < 0) { |
| 81 | dev_err(sdev->dev, "error: snd_sof_debugfs_buf_item failed\n"); |
| 82 | return ret; |
| 83 | } |
| 84 | } |
| 85 | |
| 86 | return 0; |
| 87 | } |
| 88 | |
Liam Girdwood | 70cd525 | 2019-04-12 11:05:14 -0500 | [diff] [blame] | 89 | /* parse the extended FW boot data structures from FW boot message */ |
Peter Ujfalusi | 4624bb2 | 2021-09-15 15:21:11 +0300 | [diff] [blame] | 90 | static int snd_sof_fw_parse_ext_data(struct snd_sof_dev *sdev, u32 offset) |
Liam Girdwood | 70cd525 | 2019-04-12 11:05:14 -0500 | [diff] [blame] | 91 | { |
| 92 | struct sof_ipc_ext_data_hdr *ext_hdr; |
| 93 | void *ext_data; |
| 94 | int ret = 0; |
| 95 | |
| 96 | ext_data = kzalloc(PAGE_SIZE, GFP_KERNEL); |
| 97 | if (!ext_data) |
| 98 | return -ENOMEM; |
| 99 | |
| 100 | /* get first header */ |
Peter Ujfalusi | 4624bb2 | 2021-09-15 15:21:11 +0300 | [diff] [blame] | 101 | snd_sof_dsp_block_read(sdev, SOF_FW_BLK_TYPE_SRAM, offset, ext_data, |
Liam Girdwood | 70cd525 | 2019-04-12 11:05:14 -0500 | [diff] [blame] | 102 | sizeof(*ext_hdr)); |
| 103 | ext_hdr = ext_data; |
| 104 | |
| 105 | while (ext_hdr->hdr.cmd == SOF_IPC_FW_READY) { |
| 106 | /* read in ext structure */ |
Peter Ujfalusi | 4624bb2 | 2021-09-15 15:21:11 +0300 | [diff] [blame] | 107 | snd_sof_dsp_block_read(sdev, SOF_FW_BLK_TYPE_SRAM, |
| 108 | offset + sizeof(*ext_hdr), |
| 109 | (void *)((u8 *)ext_data + sizeof(*ext_hdr)), |
| 110 | ext_hdr->hdr.size - sizeof(*ext_hdr)); |
Liam Girdwood | 70cd525 | 2019-04-12 11:05:14 -0500 | [diff] [blame] | 111 | |
| 112 | dev_dbg(sdev->dev, "found ext header type %d size 0x%x\n", |
| 113 | ext_hdr->type, ext_hdr->hdr.size); |
| 114 | |
| 115 | /* process structure data */ |
| 116 | switch (ext_hdr->type) { |
Liam Girdwood | 70cd525 | 2019-04-12 11:05:14 -0500 | [diff] [blame] | 117 | case SOF_IPC_EXT_WINDOW: |
| 118 | ret = get_ext_windows(sdev, ext_hdr); |
| 119 | break; |
Karol Trzcinski | 5928395 | 2019-12-17 18:26:11 -0600 | [diff] [blame] | 120 | case SOF_IPC_EXT_CC_INFO: |
| 121 | ret = get_cc_info(sdev, ext_hdr); |
| 122 | break; |
Bard Liao | 6e5329c | 2020-10-21 21:24:19 +0300 | [diff] [blame] | 123 | case SOF_IPC_EXT_UNUSED: |
| 124 | case SOF_IPC_EXT_PROBE_INFO: |
| 125 | case SOF_IPC_EXT_USER_ABI_INFO: |
| 126 | /* They are supported but we don't do anything here */ |
| 127 | break; |
Liam Girdwood | 70cd525 | 2019-04-12 11:05:14 -0500 | [diff] [blame] | 128 | default: |
Kai Vehmanen | 0c7f946 | 2020-11-11 19:33:21 +0200 | [diff] [blame] | 129 | dev_info(sdev->dev, "unknown ext header type %d size 0x%x\n", |
Karol Trzcinski | 8edc956 | 2019-12-09 18:48:48 -0600 | [diff] [blame] | 130 | ext_hdr->type, ext_hdr->hdr.size); |
Karol Trzcinski | 6bb03c2 | 2019-12-09 18:48:49 -0600 | [diff] [blame] | 131 | ret = 0; |
Liam Girdwood | 70cd525 | 2019-04-12 11:05:14 -0500 | [diff] [blame] | 132 | break; |
| 133 | } |
| 134 | |
| 135 | if (ret < 0) { |
| 136 | dev_err(sdev->dev, "error: failed to parse ext data type %d\n", |
| 137 | ext_hdr->type); |
| 138 | break; |
| 139 | } |
| 140 | |
| 141 | /* move to next header */ |
| 142 | offset += ext_hdr->hdr.size; |
Peter Ujfalusi | 4624bb2 | 2021-09-15 15:21:11 +0300 | [diff] [blame] | 143 | snd_sof_dsp_block_read(sdev, SOF_FW_BLK_TYPE_SRAM, offset, ext_data, |
Liam Girdwood | 70cd525 | 2019-04-12 11:05:14 -0500 | [diff] [blame] | 144 | sizeof(*ext_hdr)); |
| 145 | ext_hdr = ext_data; |
| 146 | } |
| 147 | |
| 148 | kfree(ext_data); |
| 149 | return ret; |
| 150 | } |
Liam Girdwood | 70cd525 | 2019-04-12 11:05:14 -0500 | [diff] [blame] | 151 | |
Karol Trzcinski | 3e2a89d | 2020-05-20 19:59:09 +0300 | [diff] [blame] | 152 | static int ext_man_get_fw_version(struct snd_sof_dev *sdev, |
| 153 | const struct sof_ext_man_elem_header *hdr) |
| 154 | { |
| 155 | const struct sof_ext_man_fw_version *v = |
| 156 | container_of(hdr, struct sof_ext_man_fw_version, hdr); |
| 157 | |
| 158 | memcpy(&sdev->fw_ready.version, &v->version, sizeof(v->version)); |
| 159 | sdev->fw_ready.flags = v->flags; |
| 160 | |
| 161 | /* log ABI versions and check FW compatibility */ |
| 162 | return snd_sof_ipc_valid(sdev); |
| 163 | } |
| 164 | |
Karol Trzcinski | 8d809c1 | 2020-05-20 19:59:10 +0300 | [diff] [blame] | 165 | static int ext_man_get_windows(struct snd_sof_dev *sdev, |
| 166 | const struct sof_ext_man_elem_header *hdr) |
| 167 | { |
| 168 | const struct sof_ext_man_window *w; |
| 169 | |
| 170 | w = container_of(hdr, struct sof_ext_man_window, hdr); |
| 171 | |
| 172 | return get_ext_windows(sdev, &w->ipc_window.ext_hdr); |
| 173 | } |
| 174 | |
Karol Trzcinski | 4c4a975 | 2020-05-20 19:59:11 +0300 | [diff] [blame] | 175 | static int ext_man_get_cc_info(struct snd_sof_dev *sdev, |
| 176 | const struct sof_ext_man_elem_header *hdr) |
| 177 | { |
| 178 | const struct sof_ext_man_cc_version *cc; |
| 179 | |
| 180 | cc = container_of(hdr, struct sof_ext_man_cc_version, hdr); |
| 181 | |
| 182 | return get_cc_info(sdev, &cc->cc_version.ext_hdr); |
| 183 | } |
| 184 | |
Karol Trzcinski | 60b7c1b | 2020-08-25 16:58:53 -0700 | [diff] [blame] | 185 | static int ext_man_get_dbg_abi_info(struct snd_sof_dev *sdev, |
| 186 | const struct sof_ext_man_elem_header *hdr) |
| 187 | { |
| 188 | const struct ext_man_dbg_abi *dbg_abi = |
| 189 | container_of(hdr, struct ext_man_dbg_abi, hdr); |
| 190 | |
| 191 | if (sdev->first_boot) |
| 192 | dev_dbg(sdev->dev, |
| 193 | "Firmware: DBG_ABI %d:%d:%d\n", |
| 194 | SOF_ABI_VERSION_MAJOR(dbg_abi->dbg_abi.abi_dbg_version), |
| 195 | SOF_ABI_VERSION_MINOR(dbg_abi->dbg_abi.abi_dbg_version), |
| 196 | SOF_ABI_VERSION_PATCH(dbg_abi->dbg_abi.abi_dbg_version)); |
| 197 | |
| 198 | return 0; |
| 199 | } |
| 200 | |
Karol Trzcinski | 7f09f79 | 2020-11-24 20:00:14 +0200 | [diff] [blame] | 201 | static int ext_man_get_config_data(struct snd_sof_dev *sdev, |
| 202 | const struct sof_ext_man_elem_header *hdr) |
| 203 | { |
| 204 | const struct sof_ext_man_config_data *config = |
| 205 | container_of(hdr, struct sof_ext_man_config_data, hdr); |
| 206 | const struct sof_config_elem *elem; |
| 207 | int elems_counter; |
| 208 | int elems_size; |
Karol Trzcinski | 5b10b62 | 2020-11-24 20:00:17 +0200 | [diff] [blame] | 209 | int ret = 0; |
Karol Trzcinski | 7f09f79 | 2020-11-24 20:00:14 +0200 | [diff] [blame] | 210 | int i; |
| 211 | |
| 212 | /* calculate elements counter */ |
| 213 | elems_size = config->hdr.size - sizeof(struct sof_ext_man_elem_header); |
| 214 | elems_counter = elems_size / sizeof(struct sof_config_elem); |
| 215 | |
| 216 | dev_dbg(sdev->dev, "%s can hold up to %d config elements\n", |
| 217 | __func__, elems_counter); |
| 218 | |
| 219 | for (i = 0; i < elems_counter; ++i) { |
| 220 | elem = &config->elems[i]; |
| 221 | dev_dbg(sdev->dev, "%s get index %d token %d val %d\n", |
| 222 | __func__, i, elem->token, elem->value); |
| 223 | switch (elem->token) { |
| 224 | case SOF_EXT_MAN_CONFIG_EMPTY: |
| 225 | /* unused memory space is zero filled - mapped to EMPTY elements */ |
| 226 | break; |
| 227 | case SOF_EXT_MAN_CONFIG_IPC_MSG_SIZE: |
| 228 | /* TODO: use ipc msg size from config data */ |
| 229 | break; |
Karol Trzcinski | 5b10b62 | 2020-11-24 20:00:17 +0200 | [diff] [blame] | 230 | case SOF_EXT_MAN_CONFIG_MEMORY_USAGE_SCAN: |
| 231 | if (sdev->first_boot && elem->value) |
| 232 | ret = snd_sof_dbg_memory_info_init(sdev); |
| 233 | break; |
Karol Trzcinski | 7f09f79 | 2020-11-24 20:00:14 +0200 | [diff] [blame] | 234 | default: |
| 235 | dev_info(sdev->dev, "Unknown firmware configuration token %d value %d", |
| 236 | elem->token, elem->value); |
| 237 | break; |
| 238 | } |
Karol Trzcinski | 5b10b62 | 2020-11-24 20:00:17 +0200 | [diff] [blame] | 239 | if (ret < 0) { |
| 240 | dev_err(sdev->dev, "error: processing sof_ext_man_config_data failed for token %d value 0x%x, %d\n", |
| 241 | elem->token, elem->value, ret); |
| 242 | return ret; |
| 243 | } |
Karol Trzcinski | 7f09f79 | 2020-11-24 20:00:14 +0200 | [diff] [blame] | 244 | } |
| 245 | |
| 246 | return 0; |
| 247 | } |
| 248 | |
Karol Trzcinski | a80cf19 | 2020-05-20 19:59:08 +0300 | [diff] [blame] | 249 | static ssize_t snd_sof_ext_man_size(const struct firmware *fw) |
| 250 | { |
| 251 | const struct sof_ext_man_header *head; |
| 252 | |
| 253 | head = (struct sof_ext_man_header *)fw->data; |
| 254 | |
| 255 | /* |
| 256 | * assert fw size is big enough to contain extended manifest header, |
| 257 | * it prevents from reading unallocated memory from `head` in following |
| 258 | * step. |
| 259 | */ |
| 260 | if (fw->size < sizeof(*head)) |
| 261 | return -EINVAL; |
| 262 | |
| 263 | /* |
| 264 | * When fw points to extended manifest, |
| 265 | * then first u32 must be equal SOF_EXT_MAN_MAGIC_NUMBER. |
| 266 | */ |
| 267 | if (head->magic == SOF_EXT_MAN_MAGIC_NUMBER) |
| 268 | return head->full_size; |
| 269 | |
| 270 | /* otherwise given fw don't have an extended manifest */ |
| 271 | return 0; |
| 272 | } |
| 273 | |
| 274 | /* parse extended FW manifest data structures */ |
| 275 | static int snd_sof_fw_ext_man_parse(struct snd_sof_dev *sdev, |
| 276 | const struct firmware *fw) |
| 277 | { |
| 278 | const struct sof_ext_man_elem_header *elem_hdr; |
| 279 | const struct sof_ext_man_header *head; |
| 280 | ssize_t ext_man_size; |
| 281 | ssize_t remaining; |
| 282 | uintptr_t iptr; |
| 283 | int ret = 0; |
| 284 | |
| 285 | head = (struct sof_ext_man_header *)fw->data; |
| 286 | remaining = head->full_size - head->header_size; |
| 287 | ext_man_size = snd_sof_ext_man_size(fw); |
| 288 | |
| 289 | /* Assert firmware starts with extended manifest */ |
| 290 | if (ext_man_size <= 0) |
| 291 | return ext_man_size; |
| 292 | |
| 293 | /* incompatible version */ |
| 294 | if (SOF_EXT_MAN_VERSION_INCOMPATIBLE(SOF_EXT_MAN_VERSION, |
| 295 | head->header_version)) { |
| 296 | dev_err(sdev->dev, "error: extended manifest version 0x%X differ from used 0x%X\n", |
| 297 | head->header_version, SOF_EXT_MAN_VERSION); |
| 298 | return -EINVAL; |
| 299 | } |
| 300 | |
| 301 | /* get first extended manifest element header */ |
| 302 | iptr = (uintptr_t)fw->data + head->header_size; |
| 303 | |
| 304 | while (remaining > sizeof(*elem_hdr)) { |
| 305 | elem_hdr = (struct sof_ext_man_elem_header *)iptr; |
| 306 | |
| 307 | dev_dbg(sdev->dev, "found sof_ext_man header type %d size 0x%X\n", |
| 308 | elem_hdr->type, elem_hdr->size); |
| 309 | |
| 310 | if (elem_hdr->size < sizeof(*elem_hdr) || |
| 311 | elem_hdr->size > remaining) { |
| 312 | dev_err(sdev->dev, "error: invalid sof_ext_man header size, type %d size 0x%X\n", |
| 313 | elem_hdr->type, elem_hdr->size); |
| 314 | return -EINVAL; |
| 315 | } |
| 316 | |
| 317 | /* process structure data */ |
| 318 | switch (elem_hdr->type) { |
Karol Trzcinski | 3e2a89d | 2020-05-20 19:59:09 +0300 | [diff] [blame] | 319 | case SOF_EXT_MAN_ELEM_FW_VERSION: |
| 320 | ret = ext_man_get_fw_version(sdev, elem_hdr); |
| 321 | break; |
Karol Trzcinski | 8d809c1 | 2020-05-20 19:59:10 +0300 | [diff] [blame] | 322 | case SOF_EXT_MAN_ELEM_WINDOW: |
| 323 | ret = ext_man_get_windows(sdev, elem_hdr); |
| 324 | break; |
Karol Trzcinski | 4c4a975 | 2020-05-20 19:59:11 +0300 | [diff] [blame] | 325 | case SOF_EXT_MAN_ELEM_CC_VERSION: |
| 326 | ret = ext_man_get_cc_info(sdev, elem_hdr); |
| 327 | break; |
Karol Trzcinski | 60b7c1b | 2020-08-25 16:58:53 -0700 | [diff] [blame] | 328 | case SOF_EXT_MAN_ELEM_DBG_ABI: |
| 329 | ret = ext_man_get_dbg_abi_info(sdev, elem_hdr); |
| 330 | break; |
Karol Trzcinski | 7f09f79 | 2020-11-24 20:00:14 +0200 | [diff] [blame] | 331 | case SOF_EXT_MAN_ELEM_CONFIG_DATA: |
| 332 | ret = ext_man_get_config_data(sdev, elem_hdr); |
| 333 | break; |
Fred Oh | e984f3ef | 2020-11-27 18:40:18 +0200 | [diff] [blame] | 334 | case SOF_EXT_MAN_ELEM_PLATFORM_CONFIG_DATA: |
| 335 | ret = snd_sof_dsp_parse_platform_ext_manifest(sdev, elem_hdr); |
| 336 | break; |
Karol Trzcinski | a80cf19 | 2020-05-20 19:59:08 +0300 | [diff] [blame] | 337 | default: |
Kai Vehmanen | 0c7f946 | 2020-11-11 19:33:21 +0200 | [diff] [blame] | 338 | dev_info(sdev->dev, "unknown sof_ext_man header type %d size 0x%X\n", |
Karol Trzcinski | a80cf19 | 2020-05-20 19:59:08 +0300 | [diff] [blame] | 339 | elem_hdr->type, elem_hdr->size); |
| 340 | break; |
| 341 | } |
| 342 | |
| 343 | if (ret < 0) { |
| 344 | dev_err(sdev->dev, "error: failed to parse sof_ext_man header type %d size 0x%X\n", |
| 345 | elem_hdr->type, elem_hdr->size); |
| 346 | return ret; |
| 347 | } |
| 348 | |
| 349 | remaining -= elem_hdr->size; |
| 350 | iptr += elem_hdr->size; |
| 351 | } |
| 352 | |
| 353 | if (remaining) { |
| 354 | dev_err(sdev->dev, "error: sof_ext_man header is inconsistent\n"); |
| 355 | return -EINVAL; |
| 356 | } |
| 357 | |
| 358 | return ext_man_size; |
| 359 | } |
| 360 | |
Daniel Baluta | 83ee7ab | 2019-08-07 10:02:01 -0500 | [diff] [blame] | 361 | /* |
| 362 | * IPC Firmware ready. |
| 363 | */ |
| 364 | static void sof_get_windows(struct snd_sof_dev *sdev) |
| 365 | { |
| 366 | struct sof_ipc_window_elem *elem; |
| 367 | u32 outbox_offset = 0; |
| 368 | u32 stream_offset = 0; |
| 369 | u32 inbox_offset = 0; |
| 370 | u32 outbox_size = 0; |
| 371 | u32 stream_size = 0; |
| 372 | u32 inbox_size = 0; |
Iulian Olaru | e17b738 | 2020-08-25 16:58:54 -0700 | [diff] [blame] | 373 | u32 debug_size = 0; |
| 374 | u32 debug_offset = 0; |
Daniel Baluta | 83ee7ab | 2019-08-07 10:02:01 -0500 | [diff] [blame] | 375 | int window_offset; |
Daniel Baluta | 83ee7ab | 2019-08-07 10:02:01 -0500 | [diff] [blame] | 376 | int i; |
| 377 | |
| 378 | if (!sdev->info_window) { |
| 379 | dev_err(sdev->dev, "error: have no window info\n"); |
| 380 | return; |
| 381 | } |
| 382 | |
Daniel Baluta | 83ee7ab | 2019-08-07 10:02:01 -0500 | [diff] [blame] | 383 | for (i = 0; i < sdev->info_window->num_windows; i++) { |
| 384 | elem = &sdev->info_window->window[i]; |
| 385 | |
| 386 | window_offset = snd_sof_dsp_get_window_offset(sdev, elem->id); |
| 387 | if (window_offset < 0) { |
| 388 | dev_warn(sdev->dev, "warn: no offset for window %d\n", |
| 389 | elem->id); |
| 390 | continue; |
| 391 | } |
| 392 | |
| 393 | switch (elem->type) { |
| 394 | case SOF_IPC_REGION_UPBOX: |
| 395 | inbox_offset = window_offset + elem->offset; |
| 396 | inbox_size = elem->size; |
Peter Ujfalusi | 55dfc2a | 2021-09-15 15:21:15 +0300 | [diff] [blame] | 397 | snd_sof_debugfs_add_region_item(sdev, SOF_FW_BLK_TYPE_SRAM, |
| 398 | inbox_offset, |
| 399 | elem->size, "inbox", |
| 400 | SOF_DEBUGFS_ACCESS_D0_ONLY); |
Daniel Baluta | 83ee7ab | 2019-08-07 10:02:01 -0500 | [diff] [blame] | 401 | break; |
| 402 | case SOF_IPC_REGION_DOWNBOX: |
| 403 | outbox_offset = window_offset + elem->offset; |
| 404 | outbox_size = elem->size; |
Peter Ujfalusi | 55dfc2a | 2021-09-15 15:21:15 +0300 | [diff] [blame] | 405 | snd_sof_debugfs_add_region_item(sdev, SOF_FW_BLK_TYPE_SRAM, |
| 406 | outbox_offset, |
| 407 | elem->size, "outbox", |
| 408 | SOF_DEBUGFS_ACCESS_D0_ONLY); |
Daniel Baluta | 83ee7ab | 2019-08-07 10:02:01 -0500 | [diff] [blame] | 409 | break; |
| 410 | case SOF_IPC_REGION_TRACE: |
Peter Ujfalusi | 55dfc2a | 2021-09-15 15:21:15 +0300 | [diff] [blame] | 411 | snd_sof_debugfs_add_region_item(sdev, SOF_FW_BLK_TYPE_SRAM, |
| 412 | window_offset + elem->offset, |
| 413 | elem->size, "etrace", |
| 414 | SOF_DEBUGFS_ACCESS_D0_ONLY); |
Daniel Baluta | 83ee7ab | 2019-08-07 10:02:01 -0500 | [diff] [blame] | 415 | break; |
| 416 | case SOF_IPC_REGION_DEBUG: |
Iulian Olaru | e17b738 | 2020-08-25 16:58:54 -0700 | [diff] [blame] | 417 | debug_offset = window_offset + elem->offset; |
| 418 | debug_size = elem->size; |
Peter Ujfalusi | 55dfc2a | 2021-09-15 15:21:15 +0300 | [diff] [blame] | 419 | snd_sof_debugfs_add_region_item(sdev, SOF_FW_BLK_TYPE_SRAM, |
| 420 | window_offset + elem->offset, |
| 421 | elem->size, "debug", |
| 422 | SOF_DEBUGFS_ACCESS_D0_ONLY); |
Daniel Baluta | 83ee7ab | 2019-08-07 10:02:01 -0500 | [diff] [blame] | 423 | break; |
| 424 | case SOF_IPC_REGION_STREAM: |
| 425 | stream_offset = window_offset + elem->offset; |
| 426 | stream_size = elem->size; |
Peter Ujfalusi | 55dfc2a | 2021-09-15 15:21:15 +0300 | [diff] [blame] | 427 | snd_sof_debugfs_add_region_item(sdev, SOF_FW_BLK_TYPE_SRAM, |
| 428 | stream_offset, |
| 429 | elem->size, "stream", |
| 430 | SOF_DEBUGFS_ACCESS_D0_ONLY); |
Daniel Baluta | 83ee7ab | 2019-08-07 10:02:01 -0500 | [diff] [blame] | 431 | break; |
| 432 | case SOF_IPC_REGION_REGS: |
Peter Ujfalusi | 55dfc2a | 2021-09-15 15:21:15 +0300 | [diff] [blame] | 433 | snd_sof_debugfs_add_region_item(sdev, SOF_FW_BLK_TYPE_SRAM, |
| 434 | window_offset + elem->offset, |
| 435 | elem->size, "regs", |
| 436 | SOF_DEBUGFS_ACCESS_D0_ONLY); |
Daniel Baluta | 83ee7ab | 2019-08-07 10:02:01 -0500 | [diff] [blame] | 437 | break; |
| 438 | case SOF_IPC_REGION_EXCEPTION: |
| 439 | sdev->dsp_oops_offset = window_offset + elem->offset; |
Peter Ujfalusi | 55dfc2a | 2021-09-15 15:21:15 +0300 | [diff] [blame] | 440 | snd_sof_debugfs_add_region_item(sdev, SOF_FW_BLK_TYPE_SRAM, |
| 441 | window_offset + elem->offset, |
| 442 | elem->size, "exception", |
| 443 | SOF_DEBUGFS_ACCESS_D0_ONLY); |
Daniel Baluta | 83ee7ab | 2019-08-07 10:02:01 -0500 | [diff] [blame] | 444 | break; |
| 445 | default: |
| 446 | dev_err(sdev->dev, "error: get illegal window info\n"); |
| 447 | return; |
| 448 | } |
| 449 | } |
| 450 | |
| 451 | if (outbox_size == 0 || inbox_size == 0) { |
| 452 | dev_err(sdev->dev, "error: get illegal mailbox window\n"); |
| 453 | return; |
| 454 | } |
| 455 | |
Peter Ujfalusi | b295818 | 2021-09-15 15:21:06 +0300 | [diff] [blame] | 456 | sdev->dsp_box.offset = inbox_offset; |
| 457 | sdev->dsp_box.size = inbox_size; |
| 458 | |
| 459 | sdev->host_box.offset = outbox_offset; |
| 460 | sdev->host_box.size = outbox_size; |
| 461 | |
Daniel Baluta | 83ee7ab | 2019-08-07 10:02:01 -0500 | [diff] [blame] | 462 | sdev->stream_box.offset = stream_offset; |
| 463 | sdev->stream_box.size = stream_size; |
| 464 | |
Iulian Olaru | e17b738 | 2020-08-25 16:58:54 -0700 | [diff] [blame] | 465 | sdev->debug_box.offset = debug_offset; |
| 466 | sdev->debug_box.size = debug_size; |
| 467 | |
Daniel Baluta | 83ee7ab | 2019-08-07 10:02:01 -0500 | [diff] [blame] | 468 | dev_dbg(sdev->dev, " mailbox upstream 0x%x - size 0x%x\n", |
| 469 | inbox_offset, inbox_size); |
| 470 | dev_dbg(sdev->dev, " mailbox downstream 0x%x - size 0x%x\n", |
| 471 | outbox_offset, outbox_size); |
| 472 | dev_dbg(sdev->dev, " stream region 0x%x - size 0x%x\n", |
| 473 | stream_offset, stream_size); |
Iulian Olaru | e17b738 | 2020-08-25 16:58:54 -0700 | [diff] [blame] | 474 | dev_dbg(sdev->dev, " debug region 0x%x - size 0x%x\n", |
| 475 | debug_offset, debug_size); |
Daniel Baluta | 83ee7ab | 2019-08-07 10:02:01 -0500 | [diff] [blame] | 476 | } |
| 477 | |
| 478 | /* check for ABI compatibility and create memory windows on first boot */ |
| 479 | int sof_fw_ready(struct snd_sof_dev *sdev, u32 msg_id) |
| 480 | { |
| 481 | struct sof_ipc_fw_ready *fw_ready = &sdev->fw_ready; |
| 482 | int offset; |
Daniel Baluta | 83ee7ab | 2019-08-07 10:02:01 -0500 | [diff] [blame] | 483 | int ret; |
| 484 | |
| 485 | /* mailbox must be on 4k boundary */ |
| 486 | offset = snd_sof_dsp_get_mailbox_offset(sdev); |
| 487 | if (offset < 0) { |
| 488 | dev_err(sdev->dev, "error: have no mailbox offset\n"); |
| 489 | return offset; |
| 490 | } |
| 491 | |
Daniel Baluta | 83ee7ab | 2019-08-07 10:02:01 -0500 | [diff] [blame] | 492 | dev_dbg(sdev->dev, "ipc: DSP is ready 0x%8.8x offset 0x%x\n", |
| 493 | msg_id, offset); |
| 494 | |
| 495 | /* no need to re-check version/ABI for subsequent boots */ |
| 496 | if (!sdev->first_boot) |
| 497 | return 0; |
| 498 | |
Peter Ujfalusi | 4624bb2 | 2021-09-15 15:21:11 +0300 | [diff] [blame] | 499 | /* |
| 500 | * copy data from the DSP FW ready offset |
| 501 | * Subsequent error handling is not needed for BLK_TYPE_SRAM |
| 502 | */ |
| 503 | ret = snd_sof_dsp_block_read(sdev, SOF_FW_BLK_TYPE_SRAM, offset, fw_ready, |
| 504 | sizeof(*fw_ready)); |
| 505 | if (ret) { |
| 506 | dev_err(sdev->dev, |
| 507 | "error: unable to read fw_ready, read from TYPE_SRAM failed\n"); |
| 508 | return ret; |
| 509 | } |
Daniel Baluta | 83ee7ab | 2019-08-07 10:02:01 -0500 | [diff] [blame] | 510 | |
| 511 | /* make sure ABI version is compatible */ |
| 512 | ret = snd_sof_ipc_valid(sdev); |
| 513 | if (ret < 0) |
| 514 | return ret; |
| 515 | |
| 516 | /* now check for extended data */ |
Peter Ujfalusi | 4624bb2 | 2021-09-15 15:21:11 +0300 | [diff] [blame] | 517 | snd_sof_fw_parse_ext_data(sdev, offset + sizeof(struct sof_ipc_fw_ready)); |
Daniel Baluta | 83ee7ab | 2019-08-07 10:02:01 -0500 | [diff] [blame] | 518 | |
| 519 | sof_get_windows(sdev); |
| 520 | |
Rander Wang | febf5da | 2021-10-08 12:38:36 +0300 | [diff] [blame] | 521 | return sof_ipc_init_msg_memory(sdev); |
Daniel Baluta | 83ee7ab | 2019-08-07 10:02:01 -0500 | [diff] [blame] | 522 | } |
| 523 | EXPORT_SYMBOL(sof_fw_ready); |
| 524 | |
Liam Girdwood | 70cd525 | 2019-04-12 11:05:14 -0500 | [diff] [blame] | 525 | /* generic module parser for mmaped DSPs */ |
| 526 | int snd_sof_parse_module_memcpy(struct snd_sof_dev *sdev, |
| 527 | struct snd_sof_mod_hdr *module) |
| 528 | { |
| 529 | struct snd_sof_blk_hdr *block; |
Peter Ujfalusi | 4624bb2 | 2021-09-15 15:21:11 +0300 | [diff] [blame] | 530 | int count, ret; |
Liam Girdwood | 70cd525 | 2019-04-12 11:05:14 -0500 | [diff] [blame] | 531 | u32 offset; |
| 532 | size_t remaining; |
| 533 | |
| 534 | dev_dbg(sdev->dev, "new module size 0x%x blocks 0x%x type 0x%x\n", |
| 535 | module->size, module->num_blocks, module->type); |
| 536 | |
| 537 | block = (struct snd_sof_blk_hdr *)((u8 *)module + sizeof(*module)); |
| 538 | |
| 539 | /* module->size doesn't include header size */ |
| 540 | remaining = module->size; |
| 541 | for (count = 0; count < module->num_blocks; count++) { |
| 542 | /* check for wrap */ |
| 543 | if (remaining < sizeof(*block)) { |
| 544 | dev_err(sdev->dev, "error: not enough data remaining\n"); |
| 545 | return -EINVAL; |
| 546 | } |
| 547 | |
| 548 | /* minus header size of block */ |
| 549 | remaining -= sizeof(*block); |
| 550 | |
| 551 | if (block->size == 0) { |
| 552 | dev_warn(sdev->dev, |
| 553 | "warning: block %d size zero\n", count); |
| 554 | dev_warn(sdev->dev, " type 0x%x offset 0x%x\n", |
| 555 | block->type, block->offset); |
| 556 | continue; |
| 557 | } |
| 558 | |
| 559 | switch (block->type) { |
| 560 | case SOF_FW_BLK_TYPE_RSRVD0: |
Daniel Baluta | 441c58c | 2019-07-22 09:13:49 -0500 | [diff] [blame] | 561 | case SOF_FW_BLK_TYPE_ROM...SOF_FW_BLK_TYPE_RSRVD14: |
Liam Girdwood | 70cd525 | 2019-04-12 11:05:14 -0500 | [diff] [blame] | 562 | continue; /* not handled atm */ |
| 563 | case SOF_FW_BLK_TYPE_IRAM: |
| 564 | case SOF_FW_BLK_TYPE_DRAM: |
Daniel Baluta | 441c58c | 2019-07-22 09:13:49 -0500 | [diff] [blame] | 565 | case SOF_FW_BLK_TYPE_SRAM: |
Liam Girdwood | 70cd525 | 2019-04-12 11:05:14 -0500 | [diff] [blame] | 566 | offset = block->offset; |
| 567 | break; |
| 568 | default: |
| 569 | dev_err(sdev->dev, "error: bad type 0x%x for block 0x%x\n", |
| 570 | block->type, count); |
| 571 | return -EINVAL; |
| 572 | } |
| 573 | |
| 574 | dev_dbg(sdev->dev, |
| 575 | "block %d type 0x%x size 0x%x ==> offset 0x%x\n", |
| 576 | count, block->type, block->size, offset); |
| 577 | |
| 578 | /* checking block->size to avoid unaligned access */ |
| 579 | if (block->size % sizeof(u32)) { |
| 580 | dev_err(sdev->dev, "error: invalid block size 0x%x\n", |
| 581 | block->size); |
| 582 | return -EINVAL; |
| 583 | } |
Peter Ujfalusi | 4624bb2 | 2021-09-15 15:21:11 +0300 | [diff] [blame] | 584 | ret = snd_sof_dsp_block_write(sdev, block->type, offset, |
| 585 | block + 1, block->size); |
| 586 | if (ret < 0) { |
| 587 | dev_err(sdev->dev, "error: write to block type 0x%x failed\n", |
| 588 | block->type); |
| 589 | return ret; |
| 590 | } |
Liam Girdwood | 70cd525 | 2019-04-12 11:05:14 -0500 | [diff] [blame] | 591 | |
| 592 | if (remaining < block->size) { |
| 593 | dev_err(sdev->dev, "error: not enough data remaining\n"); |
| 594 | return -EINVAL; |
| 595 | } |
| 596 | |
| 597 | /* minus body size of block */ |
| 598 | remaining -= block->size; |
| 599 | /* next block */ |
| 600 | block = (struct snd_sof_blk_hdr *)((u8 *)block + sizeof(*block) |
| 601 | + block->size); |
| 602 | } |
| 603 | |
| 604 | return 0; |
| 605 | } |
| 606 | EXPORT_SYMBOL(snd_sof_parse_module_memcpy); |
| 607 | |
Karol Trzcinski | 92be17a | 2020-04-15 15:27:55 -0500 | [diff] [blame] | 608 | static int check_header(struct snd_sof_dev *sdev, const struct firmware *fw, |
| 609 | size_t fw_offset) |
Liam Girdwood | 70cd525 | 2019-04-12 11:05:14 -0500 | [diff] [blame] | 610 | { |
| 611 | struct snd_sof_fw_header *header; |
Karol Trzcinski | 92be17a | 2020-04-15 15:27:55 -0500 | [diff] [blame] | 612 | size_t fw_size = fw->size - fw_offset; |
| 613 | |
Karol Trzcinski | 523773b | 2020-05-20 19:59:07 +0300 | [diff] [blame] | 614 | if (fw->size <= fw_offset) { |
Karol Trzcinski | 92be17a | 2020-04-15 15:27:55 -0500 | [diff] [blame] | 615 | dev_err(sdev->dev, "error: firmware size must be greater than firmware offset\n"); |
| 616 | return -EINVAL; |
| 617 | } |
Liam Girdwood | 70cd525 | 2019-04-12 11:05:14 -0500 | [diff] [blame] | 618 | |
| 619 | /* Read the header information from the data pointer */ |
Karol Trzcinski | 92be17a | 2020-04-15 15:27:55 -0500 | [diff] [blame] | 620 | header = (struct snd_sof_fw_header *)(fw->data + fw_offset); |
Liam Girdwood | 70cd525 | 2019-04-12 11:05:14 -0500 | [diff] [blame] | 621 | |
| 622 | /* verify FW sig */ |
| 623 | if (strncmp(header->sig, SND_SOF_FW_SIG, SND_SOF_FW_SIG_SIZE) != 0) { |
| 624 | dev_err(sdev->dev, "error: invalid firmware signature\n"); |
| 625 | return -EINVAL; |
| 626 | } |
| 627 | |
| 628 | /* check size is valid */ |
Karol Trzcinski | 92be17a | 2020-04-15 15:27:55 -0500 | [diff] [blame] | 629 | if (fw_size != header->file_size + sizeof(*header)) { |
Liam Girdwood | 70cd525 | 2019-04-12 11:05:14 -0500 | [diff] [blame] | 630 | dev_err(sdev->dev, "error: invalid filesize mismatch got 0x%zx expected 0x%zx\n", |
Karol Trzcinski | 92be17a | 2020-04-15 15:27:55 -0500 | [diff] [blame] | 631 | fw_size, header->file_size + sizeof(*header)); |
Liam Girdwood | 70cd525 | 2019-04-12 11:05:14 -0500 | [diff] [blame] | 632 | return -EINVAL; |
| 633 | } |
| 634 | |
| 635 | dev_dbg(sdev->dev, "header size=0x%x modules=0x%x abi=0x%x size=%zu\n", |
| 636 | header->file_size, header->num_modules, |
| 637 | header->abi, sizeof(*header)); |
| 638 | |
| 639 | return 0; |
| 640 | } |
| 641 | |
Karol Trzcinski | 92be17a | 2020-04-15 15:27:55 -0500 | [diff] [blame] | 642 | static int load_modules(struct snd_sof_dev *sdev, const struct firmware *fw, |
| 643 | size_t fw_offset) |
Liam Girdwood | 70cd525 | 2019-04-12 11:05:14 -0500 | [diff] [blame] | 644 | { |
| 645 | struct snd_sof_fw_header *header; |
| 646 | struct snd_sof_mod_hdr *module; |
| 647 | int (*load_module)(struct snd_sof_dev *sof_dev, |
| 648 | struct snd_sof_mod_hdr *hdr); |
| 649 | int ret, count; |
| 650 | size_t remaining; |
| 651 | |
Karol Trzcinski | 92be17a | 2020-04-15 15:27:55 -0500 | [diff] [blame] | 652 | header = (struct snd_sof_fw_header *)(fw->data + fw_offset); |
Liam Girdwood | 70cd525 | 2019-04-12 11:05:14 -0500 | [diff] [blame] | 653 | load_module = sof_ops(sdev)->load_module; |
| 654 | if (!load_module) |
| 655 | return -EINVAL; |
| 656 | |
| 657 | /* parse each module */ |
Karol Trzcinski | 92be17a | 2020-04-15 15:27:55 -0500 | [diff] [blame] | 658 | module = (struct snd_sof_mod_hdr *)(fw->data + fw_offset + |
| 659 | sizeof(*header)); |
| 660 | remaining = fw->size - sizeof(*header) - fw_offset; |
Liam Girdwood | 70cd525 | 2019-04-12 11:05:14 -0500 | [diff] [blame] | 661 | /* check for wrap */ |
| 662 | if (remaining > fw->size) { |
| 663 | dev_err(sdev->dev, "error: fw size smaller than header size\n"); |
| 664 | return -EINVAL; |
| 665 | } |
| 666 | |
| 667 | for (count = 0; count < header->num_modules; count++) { |
| 668 | /* check for wrap */ |
| 669 | if (remaining < sizeof(*module)) { |
| 670 | dev_err(sdev->dev, "error: not enough data remaining\n"); |
| 671 | return -EINVAL; |
| 672 | } |
| 673 | |
| 674 | /* minus header size of module */ |
| 675 | remaining -= sizeof(*module); |
| 676 | |
| 677 | /* module */ |
| 678 | ret = load_module(sdev, module); |
| 679 | if (ret < 0) { |
| 680 | dev_err(sdev->dev, "error: invalid module %d\n", count); |
| 681 | return ret; |
| 682 | } |
| 683 | |
| 684 | if (remaining < module->size) { |
| 685 | dev_err(sdev->dev, "error: not enough data remaining\n"); |
| 686 | return -EINVAL; |
| 687 | } |
| 688 | |
| 689 | /* minus body size of module */ |
| 690 | remaining -= module->size; |
| 691 | module = (struct snd_sof_mod_hdr *)((u8 *)module |
| 692 | + sizeof(*module) + module->size); |
| 693 | } |
| 694 | |
| 695 | return 0; |
| 696 | } |
| 697 | |
| 698 | int snd_sof_load_firmware_raw(struct snd_sof_dev *sdev) |
| 699 | { |
| 700 | struct snd_sof_pdata *plat_data = sdev->pdata; |
| 701 | const char *fw_filename; |
Karol Trzcinski | a80cf19 | 2020-05-20 19:59:08 +0300 | [diff] [blame] | 702 | ssize_t ext_man_size; |
Liam Girdwood | 70cd525 | 2019-04-12 11:05:14 -0500 | [diff] [blame] | 703 | int ret; |
| 704 | |
Liam Girdwood | 70cd525 | 2019-04-12 11:05:14 -0500 | [diff] [blame] | 705 | /* Don't request firmware again if firmware is already requested */ |
| 706 | if (plat_data->fw) |
| 707 | return 0; |
| 708 | |
| 709 | fw_filename = kasprintf(GFP_KERNEL, "%s/%s", |
| 710 | plat_data->fw_filename_prefix, |
| 711 | plat_data->fw_filename); |
| 712 | if (!fw_filename) |
| 713 | return -ENOMEM; |
| 714 | |
| 715 | ret = request_firmware(&plat_data->fw, fw_filename, sdev->dev); |
| 716 | |
| 717 | if (ret < 0) { |
Kai Vehmanen | 89e641a | 2021-01-27 14:23:58 +0200 | [diff] [blame] | 718 | dev_err(sdev->dev, |
Peter Ujfalusi | 25766ee | 2021-09-16 11:53:42 +0300 | [diff] [blame] | 719 | "error: sof firmware file is missing, you might need to\n"); |
| 720 | dev_err(sdev->dev, |
| 721 | " download it from https://github.com/thesofproject/sof-bin/\n"); |
Karol Trzcinski | a80cf19 | 2020-05-20 19:59:08 +0300 | [diff] [blame] | 722 | goto err; |
Pierre-Louis Bossart | 490a625 | 2020-01-07 10:08:40 -0600 | [diff] [blame] | 723 | } else { |
| 724 | dev_dbg(sdev->dev, "request_firmware %s successful\n", |
| 725 | fw_filename); |
Liam Girdwood | 70cd525 | 2019-04-12 11:05:14 -0500 | [diff] [blame] | 726 | } |
| 727 | |
Karol Trzcinski | a80cf19 | 2020-05-20 19:59:08 +0300 | [diff] [blame] | 728 | /* check for extended manifest */ |
| 729 | ext_man_size = snd_sof_fw_ext_man_parse(sdev, plat_data->fw); |
| 730 | if (ext_man_size > 0) { |
| 731 | /* when no error occurred, drop extended manifest */ |
| 732 | plat_data->fw_offset = ext_man_size; |
| 733 | } else if (!ext_man_size) { |
| 734 | /* No extended manifest, so nothing to skip during FW load */ |
| 735 | dev_dbg(sdev->dev, "firmware doesn't contain extended manifest\n"); |
| 736 | } else { |
| 737 | ret = ext_man_size; |
| 738 | dev_err(sdev->dev, "error: firmware %s contains unsupported or invalid extended manifest: %d\n", |
| 739 | fw_filename, ret); |
| 740 | } |
| 741 | |
| 742 | err: |
Liam Girdwood | 70cd525 | 2019-04-12 11:05:14 -0500 | [diff] [blame] | 743 | kfree(fw_filename); |
| 744 | |
| 745 | return ret; |
| 746 | } |
| 747 | EXPORT_SYMBOL(snd_sof_load_firmware_raw); |
| 748 | |
| 749 | int snd_sof_load_firmware_memcpy(struct snd_sof_dev *sdev) |
| 750 | { |
| 751 | struct snd_sof_pdata *plat_data = sdev->pdata; |
| 752 | int ret; |
| 753 | |
| 754 | ret = snd_sof_load_firmware_raw(sdev); |
| 755 | if (ret < 0) |
| 756 | return ret; |
| 757 | |
| 758 | /* make sure the FW header and file is valid */ |
Karol Trzcinski | 92be17a | 2020-04-15 15:27:55 -0500 | [diff] [blame] | 759 | ret = check_header(sdev, plat_data->fw, plat_data->fw_offset); |
Liam Girdwood | 70cd525 | 2019-04-12 11:05:14 -0500 | [diff] [blame] | 760 | if (ret < 0) { |
| 761 | dev_err(sdev->dev, "error: invalid FW header\n"); |
| 762 | goto error; |
| 763 | } |
| 764 | |
| 765 | /* prepare the DSP for FW loading */ |
| 766 | ret = snd_sof_dsp_reset(sdev); |
| 767 | if (ret < 0) { |
| 768 | dev_err(sdev->dev, "error: failed to reset DSP\n"); |
| 769 | goto error; |
| 770 | } |
| 771 | |
| 772 | /* parse and load firmware modules to DSP */ |
Karol Trzcinski | 92be17a | 2020-04-15 15:27:55 -0500 | [diff] [blame] | 773 | ret = load_modules(sdev, plat_data->fw, plat_data->fw_offset); |
Liam Girdwood | 70cd525 | 2019-04-12 11:05:14 -0500 | [diff] [blame] | 774 | if (ret < 0) { |
| 775 | dev_err(sdev->dev, "error: invalid FW modules\n"); |
| 776 | goto error; |
| 777 | } |
| 778 | |
| 779 | return 0; |
| 780 | |
| 781 | error: |
| 782 | release_firmware(plat_data->fw); |
| 783 | plat_data->fw = NULL; |
| 784 | return ret; |
| 785 | |
| 786 | } |
| 787 | EXPORT_SYMBOL(snd_sof_load_firmware_memcpy); |
| 788 | |
Liam Girdwood | 70cd525 | 2019-04-12 11:05:14 -0500 | [diff] [blame] | 789 | int snd_sof_run_firmware(struct snd_sof_dev *sdev) |
| 790 | { |
| 791 | int ret; |
Liam Girdwood | 70cd525 | 2019-04-12 11:05:14 -0500 | [diff] [blame] | 792 | |
| 793 | init_waitqueue_head(&sdev->boot_wait); |
Liam Girdwood | 70cd525 | 2019-04-12 11:05:14 -0500 | [diff] [blame] | 794 | |
Peter Ujfalusi | 9ff9085 | 2021-10-06 14:06:30 +0300 | [diff] [blame] | 795 | /* (re-)enable dsp dump */ |
| 796 | sdev->dbg_dump_printed = false; |
| 797 | sdev->ipc_dump_printed = false; |
| 798 | |
Ranjani Sridharan | 5c9714f | 2019-06-03 11:18:18 -0500 | [diff] [blame] | 799 | /* create read-only fw_version debugfs to store boot version info */ |
Liam Girdwood | 70cd525 | 2019-04-12 11:05:14 -0500 | [diff] [blame] | 800 | if (sdev->first_boot) { |
| 801 | ret = snd_sof_debugfs_buf_item(sdev, &sdev->fw_version, |
| 802 | sizeof(sdev->fw_version), |
Ranjani Sridharan | 5c9714f | 2019-06-03 11:18:18 -0500 | [diff] [blame] | 803 | "fw_version", 0444); |
Liam Girdwood | 70cd525 | 2019-04-12 11:05:14 -0500 | [diff] [blame] | 804 | /* errors are only due to memory allocation, not debugfs */ |
| 805 | if (ret < 0) { |
| 806 | dev_err(sdev->dev, "error: snd_sof_debugfs_buf_item failed\n"); |
| 807 | return ret; |
| 808 | } |
| 809 | } |
| 810 | |
| 811 | /* perform pre fw run operations */ |
| 812 | ret = snd_sof_dsp_pre_fw_run(sdev); |
| 813 | if (ret < 0) { |
| 814 | dev_err(sdev->dev, "error: failed pre fw run op\n"); |
| 815 | return ret; |
| 816 | } |
| 817 | |
| 818 | dev_dbg(sdev->dev, "booting DSP firmware\n"); |
| 819 | |
| 820 | /* boot the firmware on the DSP */ |
| 821 | ret = snd_sof_dsp_run(sdev); |
| 822 | if (ret < 0) { |
Peter Ujfalusi | 247ac64 | 2021-10-06 14:06:31 +0300 | [diff] [blame] | 823 | dev_err(sdev->dev, "error: failed to start DSP\n"); |
Peter Ujfalusi | f8c3ec4 | 2021-10-06 14:06:43 +0300 | [diff] [blame] | 824 | snd_sof_dsp_dbg_dump(sdev, SOF_DBG_DUMP_MBOX | SOF_DBG_DUMP_PCI); |
Liam Girdwood | 70cd525 | 2019-04-12 11:05:14 -0500 | [diff] [blame] | 825 | return ret; |
| 826 | } |
| 827 | |
Ranjani Sridharan | 6ca5cec | 2019-12-17 18:26:09 -0600 | [diff] [blame] | 828 | /* |
| 829 | * now wait for the DSP to boot. There are 3 possible outcomes: |
| 830 | * 1. Boot wait times out indicating FW boot failure. |
| 831 | * 2. FW boots successfully and fw_ready op succeeds. |
| 832 | * 3. FW boots but fw_ready op fails. |
| 833 | */ |
| 834 | ret = wait_event_timeout(sdev->boot_wait, |
| 835 | sdev->fw_state > SOF_FW_BOOT_IN_PROGRESS, |
Liam Girdwood | 70cd525 | 2019-04-12 11:05:14 -0500 | [diff] [blame] | 836 | msecs_to_jiffies(sdev->boot_timeout)); |
| 837 | if (ret == 0) { |
| 838 | dev_err(sdev->dev, "error: firmware boot failure\n"); |
Ranjani Sridharan | fbfa22e | 2020-12-11 12:07:42 +0200 | [diff] [blame] | 839 | snd_sof_dsp_dbg_dump(sdev, SOF_DBG_DUMP_REGS | SOF_DBG_DUMP_MBOX | |
Peter Ujfalusi | 2301333 | 2021-10-06 14:06:36 +0300 | [diff] [blame] | 840 | SOF_DBG_DUMP_TEXT | SOF_DBG_DUMP_PCI); |
Peter Ujfalusi | 58a5c9a | 2021-10-06 14:06:40 +0300 | [diff] [blame] | 841 | sof_set_fw_state(sdev, SOF_FW_BOOT_FAILED); |
Liam Girdwood | 70cd525 | 2019-04-12 11:05:14 -0500 | [diff] [blame] | 842 | return -EIO; |
| 843 | } |
| 844 | |
Ranjani Sridharan | 6ca5cec | 2019-12-17 18:26:09 -0600 | [diff] [blame] | 845 | if (sdev->fw_state == SOF_FW_BOOT_COMPLETE) |
Hans de Goede | 904f353 | 2020-04-02 20:49:48 +0200 | [diff] [blame] | 846 | dev_dbg(sdev->dev, "firmware boot complete\n"); |
Ranjani Sridharan | 6ca5cec | 2019-12-17 18:26:09 -0600 | [diff] [blame] | 847 | else |
| 848 | return -EIO; /* FW boots but fw_ready op failed */ |
Liam Girdwood | 70cd525 | 2019-04-12 11:05:14 -0500 | [diff] [blame] | 849 | |
| 850 | /* perform post fw run operations */ |
| 851 | ret = snd_sof_dsp_post_fw_run(sdev); |
| 852 | if (ret < 0) { |
| 853 | dev_err(sdev->dev, "error: failed post fw run op\n"); |
| 854 | return ret; |
| 855 | } |
| 856 | |
Liam Girdwood | 70cd525 | 2019-04-12 11:05:14 -0500 | [diff] [blame] | 857 | return 0; |
| 858 | } |
| 859 | EXPORT_SYMBOL(snd_sof_run_firmware); |
| 860 | |
| 861 | void snd_sof_fw_unload(struct snd_sof_dev *sdev) |
| 862 | { |
| 863 | /* TODO: support module unloading at runtime */ |
Marc Herbert | 8a8e181 | 2021-09-16 11:50:08 +0300 | [diff] [blame] | 864 | release_firmware(sdev->pdata->fw); |
| 865 | sdev->pdata->fw = NULL; |
Liam Girdwood | 70cd525 | 2019-04-12 11:05:14 -0500 | [diff] [blame] | 866 | } |
| 867 | EXPORT_SYMBOL(snd_sof_fw_unload); |