18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause) 28c2ecf20Sopenharmony_ci// 38c2ecf20Sopenharmony_ci// This file is provided under a dual BSD/GPLv2 license. When using or 48c2ecf20Sopenharmony_ci// redistributing this file, you may do so under either license. 58c2ecf20Sopenharmony_ci// 68c2ecf20Sopenharmony_ci// Copyright(c) 2018 Intel Corporation. All rights reserved. 78c2ecf20Sopenharmony_ci// 88c2ecf20Sopenharmony_ci// Author: Liam Girdwood <liam.r.girdwood@linux.intel.com> 98c2ecf20Sopenharmony_ci// 108c2ecf20Sopenharmony_ci// Generic firmware loader. 118c2ecf20Sopenharmony_ci// 128c2ecf20Sopenharmony_ci 138c2ecf20Sopenharmony_ci#include <linux/firmware.h> 148c2ecf20Sopenharmony_ci#include <sound/sof.h> 158c2ecf20Sopenharmony_ci#include <sound/sof/ext_manifest.h> 168c2ecf20Sopenharmony_ci#include "ops.h" 178c2ecf20Sopenharmony_ci 188c2ecf20Sopenharmony_cistatic int get_ext_windows(struct snd_sof_dev *sdev, 198c2ecf20Sopenharmony_ci const struct sof_ipc_ext_data_hdr *ext_hdr) 208c2ecf20Sopenharmony_ci{ 218c2ecf20Sopenharmony_ci const struct sof_ipc_window *w = 228c2ecf20Sopenharmony_ci container_of(ext_hdr, struct sof_ipc_window, ext_hdr); 238c2ecf20Sopenharmony_ci 248c2ecf20Sopenharmony_ci if (w->num_windows == 0 || w->num_windows > SOF_IPC_MAX_ELEMS) 258c2ecf20Sopenharmony_ci return -EINVAL; 268c2ecf20Sopenharmony_ci 278c2ecf20Sopenharmony_ci if (sdev->info_window) { 288c2ecf20Sopenharmony_ci if (memcmp(sdev->info_window, w, ext_hdr->hdr.size)) { 298c2ecf20Sopenharmony_ci dev_err(sdev->dev, "error: mismatch between window descriptor from extended manifest and mailbox"); 308c2ecf20Sopenharmony_ci return -EINVAL; 318c2ecf20Sopenharmony_ci } 328c2ecf20Sopenharmony_ci return 0; 338c2ecf20Sopenharmony_ci } 348c2ecf20Sopenharmony_ci 358c2ecf20Sopenharmony_ci /* keep a local copy of the data */ 368c2ecf20Sopenharmony_ci sdev->info_window = devm_kmemdup(sdev->dev, w, ext_hdr->hdr.size, 378c2ecf20Sopenharmony_ci GFP_KERNEL); 388c2ecf20Sopenharmony_ci if (!sdev->info_window) 398c2ecf20Sopenharmony_ci return -ENOMEM; 408c2ecf20Sopenharmony_ci 418c2ecf20Sopenharmony_ci return 0; 428c2ecf20Sopenharmony_ci} 438c2ecf20Sopenharmony_ci 448c2ecf20Sopenharmony_cistatic int get_cc_info(struct snd_sof_dev *sdev, 458c2ecf20Sopenharmony_ci const struct sof_ipc_ext_data_hdr *ext_hdr) 468c2ecf20Sopenharmony_ci{ 478c2ecf20Sopenharmony_ci int ret; 488c2ecf20Sopenharmony_ci 498c2ecf20Sopenharmony_ci const struct sof_ipc_cc_version *cc = 508c2ecf20Sopenharmony_ci container_of(ext_hdr, struct sof_ipc_cc_version, ext_hdr); 518c2ecf20Sopenharmony_ci 528c2ecf20Sopenharmony_ci if (sdev->cc_version) { 538c2ecf20Sopenharmony_ci if (memcmp(sdev->cc_version, cc, cc->ext_hdr.hdr.size)) { 548c2ecf20Sopenharmony_ci dev_err(sdev->dev, "error: receive diverged cc_version descriptions"); 558c2ecf20Sopenharmony_ci return -EINVAL; 568c2ecf20Sopenharmony_ci } 578c2ecf20Sopenharmony_ci return 0; 588c2ecf20Sopenharmony_ci } 598c2ecf20Sopenharmony_ci 608c2ecf20Sopenharmony_ci dev_dbg(sdev->dev, "Firmware info: used compiler %s %d:%d:%d%s used optimization flags %s\n", 618c2ecf20Sopenharmony_ci cc->name, cc->major, cc->minor, cc->micro, cc->desc, 628c2ecf20Sopenharmony_ci cc->optim); 638c2ecf20Sopenharmony_ci 648c2ecf20Sopenharmony_ci /* create read-only cc_version debugfs to store compiler version info */ 658c2ecf20Sopenharmony_ci /* use local copy of the cc_version to prevent data corruption */ 668c2ecf20Sopenharmony_ci if (sdev->first_boot) { 678c2ecf20Sopenharmony_ci sdev->cc_version = devm_kmalloc(sdev->dev, cc->ext_hdr.hdr.size, 688c2ecf20Sopenharmony_ci GFP_KERNEL); 698c2ecf20Sopenharmony_ci 708c2ecf20Sopenharmony_ci if (!sdev->cc_version) 718c2ecf20Sopenharmony_ci return -ENOMEM; 728c2ecf20Sopenharmony_ci 738c2ecf20Sopenharmony_ci memcpy(sdev->cc_version, cc, cc->ext_hdr.hdr.size); 748c2ecf20Sopenharmony_ci ret = snd_sof_debugfs_buf_item(sdev, sdev->cc_version, 758c2ecf20Sopenharmony_ci cc->ext_hdr.hdr.size, 768c2ecf20Sopenharmony_ci "cc_version", 0444); 778c2ecf20Sopenharmony_ci 788c2ecf20Sopenharmony_ci /* errors are only due to memory allocation, not debugfs */ 798c2ecf20Sopenharmony_ci if (ret < 0) { 808c2ecf20Sopenharmony_ci dev_err(sdev->dev, "error: snd_sof_debugfs_buf_item failed\n"); 818c2ecf20Sopenharmony_ci return ret; 828c2ecf20Sopenharmony_ci } 838c2ecf20Sopenharmony_ci } 848c2ecf20Sopenharmony_ci 858c2ecf20Sopenharmony_ci return 0; 868c2ecf20Sopenharmony_ci} 878c2ecf20Sopenharmony_ci 888c2ecf20Sopenharmony_ci/* parse the extended FW boot data structures from FW boot message */ 898c2ecf20Sopenharmony_ciint snd_sof_fw_parse_ext_data(struct snd_sof_dev *sdev, u32 bar, u32 offset) 908c2ecf20Sopenharmony_ci{ 918c2ecf20Sopenharmony_ci struct sof_ipc_ext_data_hdr *ext_hdr; 928c2ecf20Sopenharmony_ci void *ext_data; 938c2ecf20Sopenharmony_ci int ret = 0; 948c2ecf20Sopenharmony_ci 958c2ecf20Sopenharmony_ci ext_data = kzalloc(PAGE_SIZE, GFP_KERNEL); 968c2ecf20Sopenharmony_ci if (!ext_data) 978c2ecf20Sopenharmony_ci return -ENOMEM; 988c2ecf20Sopenharmony_ci 998c2ecf20Sopenharmony_ci /* get first header */ 1008c2ecf20Sopenharmony_ci snd_sof_dsp_block_read(sdev, bar, offset, ext_data, 1018c2ecf20Sopenharmony_ci sizeof(*ext_hdr)); 1028c2ecf20Sopenharmony_ci ext_hdr = ext_data; 1038c2ecf20Sopenharmony_ci 1048c2ecf20Sopenharmony_ci while (ext_hdr->hdr.cmd == SOF_IPC_FW_READY) { 1058c2ecf20Sopenharmony_ci /* read in ext structure */ 1068c2ecf20Sopenharmony_ci snd_sof_dsp_block_read(sdev, bar, offset + sizeof(*ext_hdr), 1078c2ecf20Sopenharmony_ci (void *)((u8 *)ext_data + sizeof(*ext_hdr)), 1088c2ecf20Sopenharmony_ci ext_hdr->hdr.size - sizeof(*ext_hdr)); 1098c2ecf20Sopenharmony_ci 1108c2ecf20Sopenharmony_ci dev_dbg(sdev->dev, "found ext header type %d size 0x%x\n", 1118c2ecf20Sopenharmony_ci ext_hdr->type, ext_hdr->hdr.size); 1128c2ecf20Sopenharmony_ci 1138c2ecf20Sopenharmony_ci /* process structure data */ 1148c2ecf20Sopenharmony_ci switch (ext_hdr->type) { 1158c2ecf20Sopenharmony_ci case SOF_IPC_EXT_WINDOW: 1168c2ecf20Sopenharmony_ci ret = get_ext_windows(sdev, ext_hdr); 1178c2ecf20Sopenharmony_ci break; 1188c2ecf20Sopenharmony_ci case SOF_IPC_EXT_CC_INFO: 1198c2ecf20Sopenharmony_ci ret = get_cc_info(sdev, ext_hdr); 1208c2ecf20Sopenharmony_ci break; 1218c2ecf20Sopenharmony_ci case SOF_IPC_EXT_UNUSED: 1228c2ecf20Sopenharmony_ci case SOF_IPC_EXT_PROBE_INFO: 1238c2ecf20Sopenharmony_ci case SOF_IPC_EXT_USER_ABI_INFO: 1248c2ecf20Sopenharmony_ci /* They are supported but we don't do anything here */ 1258c2ecf20Sopenharmony_ci break; 1268c2ecf20Sopenharmony_ci default: 1278c2ecf20Sopenharmony_ci dev_warn(sdev->dev, "warning: unknown ext header type %d size 0x%x\n", 1288c2ecf20Sopenharmony_ci ext_hdr->type, ext_hdr->hdr.size); 1298c2ecf20Sopenharmony_ci ret = 0; 1308c2ecf20Sopenharmony_ci break; 1318c2ecf20Sopenharmony_ci } 1328c2ecf20Sopenharmony_ci 1338c2ecf20Sopenharmony_ci if (ret < 0) { 1348c2ecf20Sopenharmony_ci dev_err(sdev->dev, "error: failed to parse ext data type %d\n", 1358c2ecf20Sopenharmony_ci ext_hdr->type); 1368c2ecf20Sopenharmony_ci break; 1378c2ecf20Sopenharmony_ci } 1388c2ecf20Sopenharmony_ci 1398c2ecf20Sopenharmony_ci /* move to next header */ 1408c2ecf20Sopenharmony_ci offset += ext_hdr->hdr.size; 1418c2ecf20Sopenharmony_ci snd_sof_dsp_block_read(sdev, bar, offset, ext_data, 1428c2ecf20Sopenharmony_ci sizeof(*ext_hdr)); 1438c2ecf20Sopenharmony_ci ext_hdr = ext_data; 1448c2ecf20Sopenharmony_ci } 1458c2ecf20Sopenharmony_ci 1468c2ecf20Sopenharmony_ci kfree(ext_data); 1478c2ecf20Sopenharmony_ci return ret; 1488c2ecf20Sopenharmony_ci} 1498c2ecf20Sopenharmony_ciEXPORT_SYMBOL(snd_sof_fw_parse_ext_data); 1508c2ecf20Sopenharmony_ci 1518c2ecf20Sopenharmony_cistatic int ext_man_get_fw_version(struct snd_sof_dev *sdev, 1528c2ecf20Sopenharmony_ci const struct sof_ext_man_elem_header *hdr) 1538c2ecf20Sopenharmony_ci{ 1548c2ecf20Sopenharmony_ci const struct sof_ext_man_fw_version *v = 1558c2ecf20Sopenharmony_ci container_of(hdr, struct sof_ext_man_fw_version, hdr); 1568c2ecf20Sopenharmony_ci 1578c2ecf20Sopenharmony_ci memcpy(&sdev->fw_ready.version, &v->version, sizeof(v->version)); 1588c2ecf20Sopenharmony_ci sdev->fw_ready.flags = v->flags; 1598c2ecf20Sopenharmony_ci 1608c2ecf20Sopenharmony_ci /* log ABI versions and check FW compatibility */ 1618c2ecf20Sopenharmony_ci return snd_sof_ipc_valid(sdev); 1628c2ecf20Sopenharmony_ci} 1638c2ecf20Sopenharmony_ci 1648c2ecf20Sopenharmony_cistatic int ext_man_get_windows(struct snd_sof_dev *sdev, 1658c2ecf20Sopenharmony_ci const struct sof_ext_man_elem_header *hdr) 1668c2ecf20Sopenharmony_ci{ 1678c2ecf20Sopenharmony_ci const struct sof_ext_man_window *w; 1688c2ecf20Sopenharmony_ci 1698c2ecf20Sopenharmony_ci w = container_of(hdr, struct sof_ext_man_window, hdr); 1708c2ecf20Sopenharmony_ci 1718c2ecf20Sopenharmony_ci return get_ext_windows(sdev, &w->ipc_window.ext_hdr); 1728c2ecf20Sopenharmony_ci} 1738c2ecf20Sopenharmony_ci 1748c2ecf20Sopenharmony_cistatic int ext_man_get_cc_info(struct snd_sof_dev *sdev, 1758c2ecf20Sopenharmony_ci const struct sof_ext_man_elem_header *hdr) 1768c2ecf20Sopenharmony_ci{ 1778c2ecf20Sopenharmony_ci const struct sof_ext_man_cc_version *cc; 1788c2ecf20Sopenharmony_ci 1798c2ecf20Sopenharmony_ci cc = container_of(hdr, struct sof_ext_man_cc_version, hdr); 1808c2ecf20Sopenharmony_ci 1818c2ecf20Sopenharmony_ci return get_cc_info(sdev, &cc->cc_version.ext_hdr); 1828c2ecf20Sopenharmony_ci} 1838c2ecf20Sopenharmony_ci 1848c2ecf20Sopenharmony_cistatic int ext_man_get_dbg_abi_info(struct snd_sof_dev *sdev, 1858c2ecf20Sopenharmony_ci const struct sof_ext_man_elem_header *hdr) 1868c2ecf20Sopenharmony_ci{ 1878c2ecf20Sopenharmony_ci const struct ext_man_dbg_abi *dbg_abi = 1888c2ecf20Sopenharmony_ci container_of(hdr, struct ext_man_dbg_abi, hdr); 1898c2ecf20Sopenharmony_ci 1908c2ecf20Sopenharmony_ci if (sdev->first_boot) 1918c2ecf20Sopenharmony_ci dev_dbg(sdev->dev, 1928c2ecf20Sopenharmony_ci "Firmware: DBG_ABI %d:%d:%d\n", 1938c2ecf20Sopenharmony_ci SOF_ABI_VERSION_MAJOR(dbg_abi->dbg_abi.abi_dbg_version), 1948c2ecf20Sopenharmony_ci SOF_ABI_VERSION_MINOR(dbg_abi->dbg_abi.abi_dbg_version), 1958c2ecf20Sopenharmony_ci SOF_ABI_VERSION_PATCH(dbg_abi->dbg_abi.abi_dbg_version)); 1968c2ecf20Sopenharmony_ci 1978c2ecf20Sopenharmony_ci return 0; 1988c2ecf20Sopenharmony_ci} 1998c2ecf20Sopenharmony_ci 2008c2ecf20Sopenharmony_cistatic ssize_t snd_sof_ext_man_size(const struct firmware *fw) 2018c2ecf20Sopenharmony_ci{ 2028c2ecf20Sopenharmony_ci const struct sof_ext_man_header *head; 2038c2ecf20Sopenharmony_ci 2048c2ecf20Sopenharmony_ci head = (struct sof_ext_man_header *)fw->data; 2058c2ecf20Sopenharmony_ci 2068c2ecf20Sopenharmony_ci /* 2078c2ecf20Sopenharmony_ci * assert fw size is big enough to contain extended manifest header, 2088c2ecf20Sopenharmony_ci * it prevents from reading unallocated memory from `head` in following 2098c2ecf20Sopenharmony_ci * step. 2108c2ecf20Sopenharmony_ci */ 2118c2ecf20Sopenharmony_ci if (fw->size < sizeof(*head)) 2128c2ecf20Sopenharmony_ci return -EINVAL; 2138c2ecf20Sopenharmony_ci 2148c2ecf20Sopenharmony_ci /* 2158c2ecf20Sopenharmony_ci * When fw points to extended manifest, 2168c2ecf20Sopenharmony_ci * then first u32 must be equal SOF_EXT_MAN_MAGIC_NUMBER. 2178c2ecf20Sopenharmony_ci */ 2188c2ecf20Sopenharmony_ci if (head->magic == SOF_EXT_MAN_MAGIC_NUMBER) 2198c2ecf20Sopenharmony_ci return head->full_size; 2208c2ecf20Sopenharmony_ci 2218c2ecf20Sopenharmony_ci /* otherwise given fw don't have an extended manifest */ 2228c2ecf20Sopenharmony_ci return 0; 2238c2ecf20Sopenharmony_ci} 2248c2ecf20Sopenharmony_ci 2258c2ecf20Sopenharmony_ci/* parse extended FW manifest data structures */ 2268c2ecf20Sopenharmony_cistatic int snd_sof_fw_ext_man_parse(struct snd_sof_dev *sdev, 2278c2ecf20Sopenharmony_ci const struct firmware *fw) 2288c2ecf20Sopenharmony_ci{ 2298c2ecf20Sopenharmony_ci const struct sof_ext_man_elem_header *elem_hdr; 2308c2ecf20Sopenharmony_ci const struct sof_ext_man_header *head; 2318c2ecf20Sopenharmony_ci ssize_t ext_man_size; 2328c2ecf20Sopenharmony_ci ssize_t remaining; 2338c2ecf20Sopenharmony_ci uintptr_t iptr; 2348c2ecf20Sopenharmony_ci int ret = 0; 2358c2ecf20Sopenharmony_ci 2368c2ecf20Sopenharmony_ci head = (struct sof_ext_man_header *)fw->data; 2378c2ecf20Sopenharmony_ci remaining = head->full_size - head->header_size; 2388c2ecf20Sopenharmony_ci ext_man_size = snd_sof_ext_man_size(fw); 2398c2ecf20Sopenharmony_ci 2408c2ecf20Sopenharmony_ci /* Assert firmware starts with extended manifest */ 2418c2ecf20Sopenharmony_ci if (ext_man_size <= 0) 2428c2ecf20Sopenharmony_ci return ext_man_size; 2438c2ecf20Sopenharmony_ci 2448c2ecf20Sopenharmony_ci /* incompatible version */ 2458c2ecf20Sopenharmony_ci if (SOF_EXT_MAN_VERSION_INCOMPATIBLE(SOF_EXT_MAN_VERSION, 2468c2ecf20Sopenharmony_ci head->header_version)) { 2478c2ecf20Sopenharmony_ci dev_err(sdev->dev, "error: extended manifest version 0x%X differ from used 0x%X\n", 2488c2ecf20Sopenharmony_ci head->header_version, SOF_EXT_MAN_VERSION); 2498c2ecf20Sopenharmony_ci return -EINVAL; 2508c2ecf20Sopenharmony_ci } 2518c2ecf20Sopenharmony_ci 2528c2ecf20Sopenharmony_ci /* get first extended manifest element header */ 2538c2ecf20Sopenharmony_ci iptr = (uintptr_t)fw->data + head->header_size; 2548c2ecf20Sopenharmony_ci 2558c2ecf20Sopenharmony_ci while (remaining > sizeof(*elem_hdr)) { 2568c2ecf20Sopenharmony_ci elem_hdr = (struct sof_ext_man_elem_header *)iptr; 2578c2ecf20Sopenharmony_ci 2588c2ecf20Sopenharmony_ci dev_dbg(sdev->dev, "found sof_ext_man header type %d size 0x%X\n", 2598c2ecf20Sopenharmony_ci elem_hdr->type, elem_hdr->size); 2608c2ecf20Sopenharmony_ci 2618c2ecf20Sopenharmony_ci if (elem_hdr->size < sizeof(*elem_hdr) || 2628c2ecf20Sopenharmony_ci elem_hdr->size > remaining) { 2638c2ecf20Sopenharmony_ci dev_err(sdev->dev, "error: invalid sof_ext_man header size, type %d size 0x%X\n", 2648c2ecf20Sopenharmony_ci elem_hdr->type, elem_hdr->size); 2658c2ecf20Sopenharmony_ci return -EINVAL; 2668c2ecf20Sopenharmony_ci } 2678c2ecf20Sopenharmony_ci 2688c2ecf20Sopenharmony_ci /* process structure data */ 2698c2ecf20Sopenharmony_ci switch (elem_hdr->type) { 2708c2ecf20Sopenharmony_ci case SOF_EXT_MAN_ELEM_FW_VERSION: 2718c2ecf20Sopenharmony_ci ret = ext_man_get_fw_version(sdev, elem_hdr); 2728c2ecf20Sopenharmony_ci break; 2738c2ecf20Sopenharmony_ci case SOF_EXT_MAN_ELEM_WINDOW: 2748c2ecf20Sopenharmony_ci ret = ext_man_get_windows(sdev, elem_hdr); 2758c2ecf20Sopenharmony_ci break; 2768c2ecf20Sopenharmony_ci case SOF_EXT_MAN_ELEM_CC_VERSION: 2778c2ecf20Sopenharmony_ci ret = ext_man_get_cc_info(sdev, elem_hdr); 2788c2ecf20Sopenharmony_ci break; 2798c2ecf20Sopenharmony_ci case SOF_EXT_MAN_ELEM_DBG_ABI: 2808c2ecf20Sopenharmony_ci ret = ext_man_get_dbg_abi_info(sdev, elem_hdr); 2818c2ecf20Sopenharmony_ci break; 2828c2ecf20Sopenharmony_ci default: 2838c2ecf20Sopenharmony_ci dev_warn(sdev->dev, "warning: unknown sof_ext_man header type %d size 0x%X\n", 2848c2ecf20Sopenharmony_ci elem_hdr->type, elem_hdr->size); 2858c2ecf20Sopenharmony_ci break; 2868c2ecf20Sopenharmony_ci } 2878c2ecf20Sopenharmony_ci 2888c2ecf20Sopenharmony_ci if (ret < 0) { 2898c2ecf20Sopenharmony_ci dev_err(sdev->dev, "error: failed to parse sof_ext_man header type %d size 0x%X\n", 2908c2ecf20Sopenharmony_ci elem_hdr->type, elem_hdr->size); 2918c2ecf20Sopenharmony_ci return ret; 2928c2ecf20Sopenharmony_ci } 2938c2ecf20Sopenharmony_ci 2948c2ecf20Sopenharmony_ci remaining -= elem_hdr->size; 2958c2ecf20Sopenharmony_ci iptr += elem_hdr->size; 2968c2ecf20Sopenharmony_ci } 2978c2ecf20Sopenharmony_ci 2988c2ecf20Sopenharmony_ci if (remaining) { 2998c2ecf20Sopenharmony_ci dev_err(sdev->dev, "error: sof_ext_man header is inconsistent\n"); 3008c2ecf20Sopenharmony_ci return -EINVAL; 3018c2ecf20Sopenharmony_ci } 3028c2ecf20Sopenharmony_ci 3038c2ecf20Sopenharmony_ci return ext_man_size; 3048c2ecf20Sopenharmony_ci} 3058c2ecf20Sopenharmony_ci 3068c2ecf20Sopenharmony_ci/* 3078c2ecf20Sopenharmony_ci * IPC Firmware ready. 3088c2ecf20Sopenharmony_ci */ 3098c2ecf20Sopenharmony_cistatic void sof_get_windows(struct snd_sof_dev *sdev) 3108c2ecf20Sopenharmony_ci{ 3118c2ecf20Sopenharmony_ci struct sof_ipc_window_elem *elem; 3128c2ecf20Sopenharmony_ci u32 outbox_offset = 0; 3138c2ecf20Sopenharmony_ci u32 stream_offset = 0; 3148c2ecf20Sopenharmony_ci u32 inbox_offset = 0; 3158c2ecf20Sopenharmony_ci u32 outbox_size = 0; 3168c2ecf20Sopenharmony_ci u32 stream_size = 0; 3178c2ecf20Sopenharmony_ci u32 inbox_size = 0; 3188c2ecf20Sopenharmony_ci u32 debug_size = 0; 3198c2ecf20Sopenharmony_ci u32 debug_offset = 0; 3208c2ecf20Sopenharmony_ci int window_offset; 3218c2ecf20Sopenharmony_ci int bar; 3228c2ecf20Sopenharmony_ci int i; 3238c2ecf20Sopenharmony_ci 3248c2ecf20Sopenharmony_ci if (!sdev->info_window) { 3258c2ecf20Sopenharmony_ci dev_err(sdev->dev, "error: have no window info\n"); 3268c2ecf20Sopenharmony_ci return; 3278c2ecf20Sopenharmony_ci } 3288c2ecf20Sopenharmony_ci 3298c2ecf20Sopenharmony_ci bar = snd_sof_dsp_get_bar_index(sdev, SOF_FW_BLK_TYPE_SRAM); 3308c2ecf20Sopenharmony_ci if (bar < 0) { 3318c2ecf20Sopenharmony_ci dev_err(sdev->dev, "error: have no bar mapping\n"); 3328c2ecf20Sopenharmony_ci return; 3338c2ecf20Sopenharmony_ci } 3348c2ecf20Sopenharmony_ci 3358c2ecf20Sopenharmony_ci for (i = 0; i < sdev->info_window->num_windows; i++) { 3368c2ecf20Sopenharmony_ci elem = &sdev->info_window->window[i]; 3378c2ecf20Sopenharmony_ci 3388c2ecf20Sopenharmony_ci window_offset = snd_sof_dsp_get_window_offset(sdev, elem->id); 3398c2ecf20Sopenharmony_ci if (window_offset < 0) { 3408c2ecf20Sopenharmony_ci dev_warn(sdev->dev, "warn: no offset for window %d\n", 3418c2ecf20Sopenharmony_ci elem->id); 3428c2ecf20Sopenharmony_ci continue; 3438c2ecf20Sopenharmony_ci } 3448c2ecf20Sopenharmony_ci 3458c2ecf20Sopenharmony_ci switch (elem->type) { 3468c2ecf20Sopenharmony_ci case SOF_IPC_REGION_UPBOX: 3478c2ecf20Sopenharmony_ci inbox_offset = window_offset + elem->offset; 3488c2ecf20Sopenharmony_ci inbox_size = elem->size; 3498c2ecf20Sopenharmony_ci snd_sof_debugfs_io_item(sdev, 3508c2ecf20Sopenharmony_ci sdev->bar[bar] + 3518c2ecf20Sopenharmony_ci inbox_offset, 3528c2ecf20Sopenharmony_ci elem->size, "inbox", 3538c2ecf20Sopenharmony_ci SOF_DEBUGFS_ACCESS_D0_ONLY); 3548c2ecf20Sopenharmony_ci break; 3558c2ecf20Sopenharmony_ci case SOF_IPC_REGION_DOWNBOX: 3568c2ecf20Sopenharmony_ci outbox_offset = window_offset + elem->offset; 3578c2ecf20Sopenharmony_ci outbox_size = elem->size; 3588c2ecf20Sopenharmony_ci snd_sof_debugfs_io_item(sdev, 3598c2ecf20Sopenharmony_ci sdev->bar[bar] + 3608c2ecf20Sopenharmony_ci outbox_offset, 3618c2ecf20Sopenharmony_ci elem->size, "outbox", 3628c2ecf20Sopenharmony_ci SOF_DEBUGFS_ACCESS_D0_ONLY); 3638c2ecf20Sopenharmony_ci break; 3648c2ecf20Sopenharmony_ci case SOF_IPC_REGION_TRACE: 3658c2ecf20Sopenharmony_ci snd_sof_debugfs_io_item(sdev, 3668c2ecf20Sopenharmony_ci sdev->bar[bar] + 3678c2ecf20Sopenharmony_ci window_offset + 3688c2ecf20Sopenharmony_ci elem->offset, 3698c2ecf20Sopenharmony_ci elem->size, "etrace", 3708c2ecf20Sopenharmony_ci SOF_DEBUGFS_ACCESS_D0_ONLY); 3718c2ecf20Sopenharmony_ci break; 3728c2ecf20Sopenharmony_ci case SOF_IPC_REGION_DEBUG: 3738c2ecf20Sopenharmony_ci debug_offset = window_offset + elem->offset; 3748c2ecf20Sopenharmony_ci debug_size = elem->size; 3758c2ecf20Sopenharmony_ci snd_sof_debugfs_io_item(sdev, 3768c2ecf20Sopenharmony_ci sdev->bar[bar] + 3778c2ecf20Sopenharmony_ci window_offset + 3788c2ecf20Sopenharmony_ci elem->offset, 3798c2ecf20Sopenharmony_ci elem->size, "debug", 3808c2ecf20Sopenharmony_ci SOF_DEBUGFS_ACCESS_D0_ONLY); 3818c2ecf20Sopenharmony_ci break; 3828c2ecf20Sopenharmony_ci case SOF_IPC_REGION_STREAM: 3838c2ecf20Sopenharmony_ci stream_offset = window_offset + elem->offset; 3848c2ecf20Sopenharmony_ci stream_size = elem->size; 3858c2ecf20Sopenharmony_ci snd_sof_debugfs_io_item(sdev, 3868c2ecf20Sopenharmony_ci sdev->bar[bar] + 3878c2ecf20Sopenharmony_ci stream_offset, 3888c2ecf20Sopenharmony_ci elem->size, "stream", 3898c2ecf20Sopenharmony_ci SOF_DEBUGFS_ACCESS_D0_ONLY); 3908c2ecf20Sopenharmony_ci break; 3918c2ecf20Sopenharmony_ci case SOF_IPC_REGION_REGS: 3928c2ecf20Sopenharmony_ci snd_sof_debugfs_io_item(sdev, 3938c2ecf20Sopenharmony_ci sdev->bar[bar] + 3948c2ecf20Sopenharmony_ci window_offset + 3958c2ecf20Sopenharmony_ci elem->offset, 3968c2ecf20Sopenharmony_ci elem->size, "regs", 3978c2ecf20Sopenharmony_ci SOF_DEBUGFS_ACCESS_D0_ONLY); 3988c2ecf20Sopenharmony_ci break; 3998c2ecf20Sopenharmony_ci case SOF_IPC_REGION_EXCEPTION: 4008c2ecf20Sopenharmony_ci sdev->dsp_oops_offset = window_offset + elem->offset; 4018c2ecf20Sopenharmony_ci snd_sof_debugfs_io_item(sdev, 4028c2ecf20Sopenharmony_ci sdev->bar[bar] + 4038c2ecf20Sopenharmony_ci window_offset + 4048c2ecf20Sopenharmony_ci elem->offset, 4058c2ecf20Sopenharmony_ci elem->size, "exception", 4068c2ecf20Sopenharmony_ci SOF_DEBUGFS_ACCESS_D0_ONLY); 4078c2ecf20Sopenharmony_ci break; 4088c2ecf20Sopenharmony_ci default: 4098c2ecf20Sopenharmony_ci dev_err(sdev->dev, "error: get illegal window info\n"); 4108c2ecf20Sopenharmony_ci return; 4118c2ecf20Sopenharmony_ci } 4128c2ecf20Sopenharmony_ci } 4138c2ecf20Sopenharmony_ci 4148c2ecf20Sopenharmony_ci if (outbox_size == 0 || inbox_size == 0) { 4158c2ecf20Sopenharmony_ci dev_err(sdev->dev, "error: get illegal mailbox window\n"); 4168c2ecf20Sopenharmony_ci return; 4178c2ecf20Sopenharmony_ci } 4188c2ecf20Sopenharmony_ci 4198c2ecf20Sopenharmony_ci snd_sof_dsp_mailbox_init(sdev, inbox_offset, inbox_size, 4208c2ecf20Sopenharmony_ci outbox_offset, outbox_size); 4218c2ecf20Sopenharmony_ci sdev->stream_box.offset = stream_offset; 4228c2ecf20Sopenharmony_ci sdev->stream_box.size = stream_size; 4238c2ecf20Sopenharmony_ci 4248c2ecf20Sopenharmony_ci sdev->debug_box.offset = debug_offset; 4258c2ecf20Sopenharmony_ci sdev->debug_box.size = debug_size; 4268c2ecf20Sopenharmony_ci 4278c2ecf20Sopenharmony_ci dev_dbg(sdev->dev, " mailbox upstream 0x%x - size 0x%x\n", 4288c2ecf20Sopenharmony_ci inbox_offset, inbox_size); 4298c2ecf20Sopenharmony_ci dev_dbg(sdev->dev, " mailbox downstream 0x%x - size 0x%x\n", 4308c2ecf20Sopenharmony_ci outbox_offset, outbox_size); 4318c2ecf20Sopenharmony_ci dev_dbg(sdev->dev, " stream region 0x%x - size 0x%x\n", 4328c2ecf20Sopenharmony_ci stream_offset, stream_size); 4338c2ecf20Sopenharmony_ci dev_dbg(sdev->dev, " debug region 0x%x - size 0x%x\n", 4348c2ecf20Sopenharmony_ci debug_offset, debug_size); 4358c2ecf20Sopenharmony_ci} 4368c2ecf20Sopenharmony_ci 4378c2ecf20Sopenharmony_ci/* check for ABI compatibility and create memory windows on first boot */ 4388c2ecf20Sopenharmony_ciint sof_fw_ready(struct snd_sof_dev *sdev, u32 msg_id) 4398c2ecf20Sopenharmony_ci{ 4408c2ecf20Sopenharmony_ci struct sof_ipc_fw_ready *fw_ready = &sdev->fw_ready; 4418c2ecf20Sopenharmony_ci int offset; 4428c2ecf20Sopenharmony_ci int bar; 4438c2ecf20Sopenharmony_ci int ret; 4448c2ecf20Sopenharmony_ci 4458c2ecf20Sopenharmony_ci /* mailbox must be on 4k boundary */ 4468c2ecf20Sopenharmony_ci offset = snd_sof_dsp_get_mailbox_offset(sdev); 4478c2ecf20Sopenharmony_ci if (offset < 0) { 4488c2ecf20Sopenharmony_ci dev_err(sdev->dev, "error: have no mailbox offset\n"); 4498c2ecf20Sopenharmony_ci return offset; 4508c2ecf20Sopenharmony_ci } 4518c2ecf20Sopenharmony_ci 4528c2ecf20Sopenharmony_ci bar = snd_sof_dsp_get_bar_index(sdev, SOF_FW_BLK_TYPE_SRAM); 4538c2ecf20Sopenharmony_ci if (bar < 0) { 4548c2ecf20Sopenharmony_ci dev_err(sdev->dev, "error: have no bar mapping\n"); 4558c2ecf20Sopenharmony_ci return -EINVAL; 4568c2ecf20Sopenharmony_ci } 4578c2ecf20Sopenharmony_ci 4588c2ecf20Sopenharmony_ci dev_dbg(sdev->dev, "ipc: DSP is ready 0x%8.8x offset 0x%x\n", 4598c2ecf20Sopenharmony_ci msg_id, offset); 4608c2ecf20Sopenharmony_ci 4618c2ecf20Sopenharmony_ci /* no need to re-check version/ABI for subsequent boots */ 4628c2ecf20Sopenharmony_ci if (!sdev->first_boot) 4638c2ecf20Sopenharmony_ci return 0; 4648c2ecf20Sopenharmony_ci 4658c2ecf20Sopenharmony_ci /* copy data from the DSP FW ready offset */ 4668c2ecf20Sopenharmony_ci sof_block_read(sdev, bar, offset, fw_ready, sizeof(*fw_ready)); 4678c2ecf20Sopenharmony_ci 4688c2ecf20Sopenharmony_ci /* make sure ABI version is compatible */ 4698c2ecf20Sopenharmony_ci ret = snd_sof_ipc_valid(sdev); 4708c2ecf20Sopenharmony_ci if (ret < 0) 4718c2ecf20Sopenharmony_ci return ret; 4728c2ecf20Sopenharmony_ci 4738c2ecf20Sopenharmony_ci /* now check for extended data */ 4748c2ecf20Sopenharmony_ci snd_sof_fw_parse_ext_data(sdev, bar, offset + 4758c2ecf20Sopenharmony_ci sizeof(struct sof_ipc_fw_ready)); 4768c2ecf20Sopenharmony_ci 4778c2ecf20Sopenharmony_ci sof_get_windows(sdev); 4788c2ecf20Sopenharmony_ci 4798c2ecf20Sopenharmony_ci return 0; 4808c2ecf20Sopenharmony_ci} 4818c2ecf20Sopenharmony_ciEXPORT_SYMBOL(sof_fw_ready); 4828c2ecf20Sopenharmony_ci 4838c2ecf20Sopenharmony_ci/* generic module parser for mmaped DSPs */ 4848c2ecf20Sopenharmony_ciint snd_sof_parse_module_memcpy(struct snd_sof_dev *sdev, 4858c2ecf20Sopenharmony_ci struct snd_sof_mod_hdr *module) 4868c2ecf20Sopenharmony_ci{ 4878c2ecf20Sopenharmony_ci struct snd_sof_blk_hdr *block; 4888c2ecf20Sopenharmony_ci int count, bar; 4898c2ecf20Sopenharmony_ci u32 offset; 4908c2ecf20Sopenharmony_ci size_t remaining; 4918c2ecf20Sopenharmony_ci 4928c2ecf20Sopenharmony_ci dev_dbg(sdev->dev, "new module size 0x%x blocks 0x%x type 0x%x\n", 4938c2ecf20Sopenharmony_ci module->size, module->num_blocks, module->type); 4948c2ecf20Sopenharmony_ci 4958c2ecf20Sopenharmony_ci block = (struct snd_sof_blk_hdr *)((u8 *)module + sizeof(*module)); 4968c2ecf20Sopenharmony_ci 4978c2ecf20Sopenharmony_ci /* module->size doesn't include header size */ 4988c2ecf20Sopenharmony_ci remaining = module->size; 4998c2ecf20Sopenharmony_ci for (count = 0; count < module->num_blocks; count++) { 5008c2ecf20Sopenharmony_ci /* check for wrap */ 5018c2ecf20Sopenharmony_ci if (remaining < sizeof(*block)) { 5028c2ecf20Sopenharmony_ci dev_err(sdev->dev, "error: not enough data remaining\n"); 5038c2ecf20Sopenharmony_ci return -EINVAL; 5048c2ecf20Sopenharmony_ci } 5058c2ecf20Sopenharmony_ci 5068c2ecf20Sopenharmony_ci /* minus header size of block */ 5078c2ecf20Sopenharmony_ci remaining -= sizeof(*block); 5088c2ecf20Sopenharmony_ci 5098c2ecf20Sopenharmony_ci if (block->size == 0) { 5108c2ecf20Sopenharmony_ci dev_warn(sdev->dev, 5118c2ecf20Sopenharmony_ci "warning: block %d size zero\n", count); 5128c2ecf20Sopenharmony_ci dev_warn(sdev->dev, " type 0x%x offset 0x%x\n", 5138c2ecf20Sopenharmony_ci block->type, block->offset); 5148c2ecf20Sopenharmony_ci continue; 5158c2ecf20Sopenharmony_ci } 5168c2ecf20Sopenharmony_ci 5178c2ecf20Sopenharmony_ci switch (block->type) { 5188c2ecf20Sopenharmony_ci case SOF_FW_BLK_TYPE_RSRVD0: 5198c2ecf20Sopenharmony_ci case SOF_FW_BLK_TYPE_ROM...SOF_FW_BLK_TYPE_RSRVD14: 5208c2ecf20Sopenharmony_ci continue; /* not handled atm */ 5218c2ecf20Sopenharmony_ci case SOF_FW_BLK_TYPE_IRAM: 5228c2ecf20Sopenharmony_ci case SOF_FW_BLK_TYPE_DRAM: 5238c2ecf20Sopenharmony_ci case SOF_FW_BLK_TYPE_SRAM: 5248c2ecf20Sopenharmony_ci offset = block->offset; 5258c2ecf20Sopenharmony_ci bar = snd_sof_dsp_get_bar_index(sdev, block->type); 5268c2ecf20Sopenharmony_ci if (bar < 0) { 5278c2ecf20Sopenharmony_ci dev_err(sdev->dev, 5288c2ecf20Sopenharmony_ci "error: no BAR mapping for block type 0x%x\n", 5298c2ecf20Sopenharmony_ci block->type); 5308c2ecf20Sopenharmony_ci return bar; 5318c2ecf20Sopenharmony_ci } 5328c2ecf20Sopenharmony_ci break; 5338c2ecf20Sopenharmony_ci default: 5348c2ecf20Sopenharmony_ci dev_err(sdev->dev, "error: bad type 0x%x for block 0x%x\n", 5358c2ecf20Sopenharmony_ci block->type, count); 5368c2ecf20Sopenharmony_ci return -EINVAL; 5378c2ecf20Sopenharmony_ci } 5388c2ecf20Sopenharmony_ci 5398c2ecf20Sopenharmony_ci dev_dbg(sdev->dev, 5408c2ecf20Sopenharmony_ci "block %d type 0x%x size 0x%x ==> offset 0x%x\n", 5418c2ecf20Sopenharmony_ci count, block->type, block->size, offset); 5428c2ecf20Sopenharmony_ci 5438c2ecf20Sopenharmony_ci /* checking block->size to avoid unaligned access */ 5448c2ecf20Sopenharmony_ci if (block->size % sizeof(u32)) { 5458c2ecf20Sopenharmony_ci dev_err(sdev->dev, "error: invalid block size 0x%x\n", 5468c2ecf20Sopenharmony_ci block->size); 5478c2ecf20Sopenharmony_ci return -EINVAL; 5488c2ecf20Sopenharmony_ci } 5498c2ecf20Sopenharmony_ci snd_sof_dsp_block_write(sdev, bar, offset, 5508c2ecf20Sopenharmony_ci block + 1, block->size); 5518c2ecf20Sopenharmony_ci 5528c2ecf20Sopenharmony_ci if (remaining < block->size) { 5538c2ecf20Sopenharmony_ci dev_err(sdev->dev, "error: not enough data remaining\n"); 5548c2ecf20Sopenharmony_ci return -EINVAL; 5558c2ecf20Sopenharmony_ci } 5568c2ecf20Sopenharmony_ci 5578c2ecf20Sopenharmony_ci /* minus body size of block */ 5588c2ecf20Sopenharmony_ci remaining -= block->size; 5598c2ecf20Sopenharmony_ci /* next block */ 5608c2ecf20Sopenharmony_ci block = (struct snd_sof_blk_hdr *)((u8 *)block + sizeof(*block) 5618c2ecf20Sopenharmony_ci + block->size); 5628c2ecf20Sopenharmony_ci } 5638c2ecf20Sopenharmony_ci 5648c2ecf20Sopenharmony_ci return 0; 5658c2ecf20Sopenharmony_ci} 5668c2ecf20Sopenharmony_ciEXPORT_SYMBOL(snd_sof_parse_module_memcpy); 5678c2ecf20Sopenharmony_ci 5688c2ecf20Sopenharmony_cistatic int check_header(struct snd_sof_dev *sdev, const struct firmware *fw, 5698c2ecf20Sopenharmony_ci size_t fw_offset) 5708c2ecf20Sopenharmony_ci{ 5718c2ecf20Sopenharmony_ci struct snd_sof_fw_header *header; 5728c2ecf20Sopenharmony_ci size_t fw_size = fw->size - fw_offset; 5738c2ecf20Sopenharmony_ci 5748c2ecf20Sopenharmony_ci if (fw->size <= fw_offset) { 5758c2ecf20Sopenharmony_ci dev_err(sdev->dev, "error: firmware size must be greater than firmware offset\n"); 5768c2ecf20Sopenharmony_ci return -EINVAL; 5778c2ecf20Sopenharmony_ci } 5788c2ecf20Sopenharmony_ci 5798c2ecf20Sopenharmony_ci /* Read the header information from the data pointer */ 5808c2ecf20Sopenharmony_ci header = (struct snd_sof_fw_header *)(fw->data + fw_offset); 5818c2ecf20Sopenharmony_ci 5828c2ecf20Sopenharmony_ci /* verify FW sig */ 5838c2ecf20Sopenharmony_ci if (strncmp(header->sig, SND_SOF_FW_SIG, SND_SOF_FW_SIG_SIZE) != 0) { 5848c2ecf20Sopenharmony_ci dev_err(sdev->dev, "error: invalid firmware signature\n"); 5858c2ecf20Sopenharmony_ci return -EINVAL; 5868c2ecf20Sopenharmony_ci } 5878c2ecf20Sopenharmony_ci 5888c2ecf20Sopenharmony_ci /* check size is valid */ 5898c2ecf20Sopenharmony_ci if (fw_size != header->file_size + sizeof(*header)) { 5908c2ecf20Sopenharmony_ci dev_err(sdev->dev, "error: invalid filesize mismatch got 0x%zx expected 0x%zx\n", 5918c2ecf20Sopenharmony_ci fw_size, header->file_size + sizeof(*header)); 5928c2ecf20Sopenharmony_ci return -EINVAL; 5938c2ecf20Sopenharmony_ci } 5948c2ecf20Sopenharmony_ci 5958c2ecf20Sopenharmony_ci dev_dbg(sdev->dev, "header size=0x%x modules=0x%x abi=0x%x size=%zu\n", 5968c2ecf20Sopenharmony_ci header->file_size, header->num_modules, 5978c2ecf20Sopenharmony_ci header->abi, sizeof(*header)); 5988c2ecf20Sopenharmony_ci 5998c2ecf20Sopenharmony_ci return 0; 6008c2ecf20Sopenharmony_ci} 6018c2ecf20Sopenharmony_ci 6028c2ecf20Sopenharmony_cistatic int load_modules(struct snd_sof_dev *sdev, const struct firmware *fw, 6038c2ecf20Sopenharmony_ci size_t fw_offset) 6048c2ecf20Sopenharmony_ci{ 6058c2ecf20Sopenharmony_ci struct snd_sof_fw_header *header; 6068c2ecf20Sopenharmony_ci struct snd_sof_mod_hdr *module; 6078c2ecf20Sopenharmony_ci int (*load_module)(struct snd_sof_dev *sof_dev, 6088c2ecf20Sopenharmony_ci struct snd_sof_mod_hdr *hdr); 6098c2ecf20Sopenharmony_ci int ret, count; 6108c2ecf20Sopenharmony_ci size_t remaining; 6118c2ecf20Sopenharmony_ci 6128c2ecf20Sopenharmony_ci header = (struct snd_sof_fw_header *)(fw->data + fw_offset); 6138c2ecf20Sopenharmony_ci load_module = sof_ops(sdev)->load_module; 6148c2ecf20Sopenharmony_ci if (!load_module) 6158c2ecf20Sopenharmony_ci return -EINVAL; 6168c2ecf20Sopenharmony_ci 6178c2ecf20Sopenharmony_ci /* parse each module */ 6188c2ecf20Sopenharmony_ci module = (struct snd_sof_mod_hdr *)(fw->data + fw_offset + 6198c2ecf20Sopenharmony_ci sizeof(*header)); 6208c2ecf20Sopenharmony_ci remaining = fw->size - sizeof(*header) - fw_offset; 6218c2ecf20Sopenharmony_ci /* check for wrap */ 6228c2ecf20Sopenharmony_ci if (remaining > fw->size) { 6238c2ecf20Sopenharmony_ci dev_err(sdev->dev, "error: fw size smaller than header size\n"); 6248c2ecf20Sopenharmony_ci return -EINVAL; 6258c2ecf20Sopenharmony_ci } 6268c2ecf20Sopenharmony_ci 6278c2ecf20Sopenharmony_ci for (count = 0; count < header->num_modules; count++) { 6288c2ecf20Sopenharmony_ci /* check for wrap */ 6298c2ecf20Sopenharmony_ci if (remaining < sizeof(*module)) { 6308c2ecf20Sopenharmony_ci dev_err(sdev->dev, "error: not enough data remaining\n"); 6318c2ecf20Sopenharmony_ci return -EINVAL; 6328c2ecf20Sopenharmony_ci } 6338c2ecf20Sopenharmony_ci 6348c2ecf20Sopenharmony_ci /* minus header size of module */ 6358c2ecf20Sopenharmony_ci remaining -= sizeof(*module); 6368c2ecf20Sopenharmony_ci 6378c2ecf20Sopenharmony_ci /* module */ 6388c2ecf20Sopenharmony_ci ret = load_module(sdev, module); 6398c2ecf20Sopenharmony_ci if (ret < 0) { 6408c2ecf20Sopenharmony_ci dev_err(sdev->dev, "error: invalid module %d\n", count); 6418c2ecf20Sopenharmony_ci return ret; 6428c2ecf20Sopenharmony_ci } 6438c2ecf20Sopenharmony_ci 6448c2ecf20Sopenharmony_ci if (remaining < module->size) { 6458c2ecf20Sopenharmony_ci dev_err(sdev->dev, "error: not enough data remaining\n"); 6468c2ecf20Sopenharmony_ci return -EINVAL; 6478c2ecf20Sopenharmony_ci } 6488c2ecf20Sopenharmony_ci 6498c2ecf20Sopenharmony_ci /* minus body size of module */ 6508c2ecf20Sopenharmony_ci remaining -= module->size; 6518c2ecf20Sopenharmony_ci module = (struct snd_sof_mod_hdr *)((u8 *)module 6528c2ecf20Sopenharmony_ci + sizeof(*module) + module->size); 6538c2ecf20Sopenharmony_ci } 6548c2ecf20Sopenharmony_ci 6558c2ecf20Sopenharmony_ci return 0; 6568c2ecf20Sopenharmony_ci} 6578c2ecf20Sopenharmony_ci 6588c2ecf20Sopenharmony_ciint snd_sof_load_firmware_raw(struct snd_sof_dev *sdev) 6598c2ecf20Sopenharmony_ci{ 6608c2ecf20Sopenharmony_ci struct snd_sof_pdata *plat_data = sdev->pdata; 6618c2ecf20Sopenharmony_ci const char *fw_filename; 6628c2ecf20Sopenharmony_ci ssize_t ext_man_size; 6638c2ecf20Sopenharmony_ci int ret; 6648c2ecf20Sopenharmony_ci 6658c2ecf20Sopenharmony_ci /* Don't request firmware again if firmware is already requested */ 6668c2ecf20Sopenharmony_ci if (plat_data->fw) 6678c2ecf20Sopenharmony_ci return 0; 6688c2ecf20Sopenharmony_ci 6698c2ecf20Sopenharmony_ci fw_filename = kasprintf(GFP_KERNEL, "%s/%s", 6708c2ecf20Sopenharmony_ci plat_data->fw_filename_prefix, 6718c2ecf20Sopenharmony_ci plat_data->fw_filename); 6728c2ecf20Sopenharmony_ci if (!fw_filename) 6738c2ecf20Sopenharmony_ci return -ENOMEM; 6748c2ecf20Sopenharmony_ci 6758c2ecf20Sopenharmony_ci ret = request_firmware(&plat_data->fw, fw_filename, sdev->dev); 6768c2ecf20Sopenharmony_ci 6778c2ecf20Sopenharmony_ci if (ret < 0) { 6788c2ecf20Sopenharmony_ci dev_err(sdev->dev, "error: request firmware %s failed err: %d\n", 6798c2ecf20Sopenharmony_ci fw_filename, ret); 6808c2ecf20Sopenharmony_ci goto err; 6818c2ecf20Sopenharmony_ci } else { 6828c2ecf20Sopenharmony_ci dev_dbg(sdev->dev, "request_firmware %s successful\n", 6838c2ecf20Sopenharmony_ci fw_filename); 6848c2ecf20Sopenharmony_ci } 6858c2ecf20Sopenharmony_ci 6868c2ecf20Sopenharmony_ci /* check for extended manifest */ 6878c2ecf20Sopenharmony_ci ext_man_size = snd_sof_fw_ext_man_parse(sdev, plat_data->fw); 6888c2ecf20Sopenharmony_ci if (ext_man_size > 0) { 6898c2ecf20Sopenharmony_ci /* when no error occurred, drop extended manifest */ 6908c2ecf20Sopenharmony_ci plat_data->fw_offset = ext_man_size; 6918c2ecf20Sopenharmony_ci } else if (!ext_man_size) { 6928c2ecf20Sopenharmony_ci /* No extended manifest, so nothing to skip during FW load */ 6938c2ecf20Sopenharmony_ci dev_dbg(sdev->dev, "firmware doesn't contain extended manifest\n"); 6948c2ecf20Sopenharmony_ci } else { 6958c2ecf20Sopenharmony_ci ret = ext_man_size; 6968c2ecf20Sopenharmony_ci dev_err(sdev->dev, "error: firmware %s contains unsupported or invalid extended manifest: %d\n", 6978c2ecf20Sopenharmony_ci fw_filename, ret); 6988c2ecf20Sopenharmony_ci } 6998c2ecf20Sopenharmony_ci 7008c2ecf20Sopenharmony_cierr: 7018c2ecf20Sopenharmony_ci kfree(fw_filename); 7028c2ecf20Sopenharmony_ci 7038c2ecf20Sopenharmony_ci return ret; 7048c2ecf20Sopenharmony_ci} 7058c2ecf20Sopenharmony_ciEXPORT_SYMBOL(snd_sof_load_firmware_raw); 7068c2ecf20Sopenharmony_ci 7078c2ecf20Sopenharmony_ciint snd_sof_load_firmware_memcpy(struct snd_sof_dev *sdev) 7088c2ecf20Sopenharmony_ci{ 7098c2ecf20Sopenharmony_ci struct snd_sof_pdata *plat_data = sdev->pdata; 7108c2ecf20Sopenharmony_ci int ret; 7118c2ecf20Sopenharmony_ci 7128c2ecf20Sopenharmony_ci ret = snd_sof_load_firmware_raw(sdev); 7138c2ecf20Sopenharmony_ci if (ret < 0) 7148c2ecf20Sopenharmony_ci return ret; 7158c2ecf20Sopenharmony_ci 7168c2ecf20Sopenharmony_ci /* make sure the FW header and file is valid */ 7178c2ecf20Sopenharmony_ci ret = check_header(sdev, plat_data->fw, plat_data->fw_offset); 7188c2ecf20Sopenharmony_ci if (ret < 0) { 7198c2ecf20Sopenharmony_ci dev_err(sdev->dev, "error: invalid FW header\n"); 7208c2ecf20Sopenharmony_ci goto error; 7218c2ecf20Sopenharmony_ci } 7228c2ecf20Sopenharmony_ci 7238c2ecf20Sopenharmony_ci /* prepare the DSP for FW loading */ 7248c2ecf20Sopenharmony_ci ret = snd_sof_dsp_reset(sdev); 7258c2ecf20Sopenharmony_ci if (ret < 0) { 7268c2ecf20Sopenharmony_ci dev_err(sdev->dev, "error: failed to reset DSP\n"); 7278c2ecf20Sopenharmony_ci goto error; 7288c2ecf20Sopenharmony_ci } 7298c2ecf20Sopenharmony_ci 7308c2ecf20Sopenharmony_ci /* parse and load firmware modules to DSP */ 7318c2ecf20Sopenharmony_ci ret = load_modules(sdev, plat_data->fw, plat_data->fw_offset); 7328c2ecf20Sopenharmony_ci if (ret < 0) { 7338c2ecf20Sopenharmony_ci dev_err(sdev->dev, "error: invalid FW modules\n"); 7348c2ecf20Sopenharmony_ci goto error; 7358c2ecf20Sopenharmony_ci } 7368c2ecf20Sopenharmony_ci 7378c2ecf20Sopenharmony_ci return 0; 7388c2ecf20Sopenharmony_ci 7398c2ecf20Sopenharmony_cierror: 7408c2ecf20Sopenharmony_ci release_firmware(plat_data->fw); 7418c2ecf20Sopenharmony_ci plat_data->fw = NULL; 7428c2ecf20Sopenharmony_ci return ret; 7438c2ecf20Sopenharmony_ci 7448c2ecf20Sopenharmony_ci} 7458c2ecf20Sopenharmony_ciEXPORT_SYMBOL(snd_sof_load_firmware_memcpy); 7468c2ecf20Sopenharmony_ci 7478c2ecf20Sopenharmony_ciint snd_sof_load_firmware(struct snd_sof_dev *sdev) 7488c2ecf20Sopenharmony_ci{ 7498c2ecf20Sopenharmony_ci dev_dbg(sdev->dev, "loading firmware\n"); 7508c2ecf20Sopenharmony_ci 7518c2ecf20Sopenharmony_ci if (sof_ops(sdev)->load_firmware) 7528c2ecf20Sopenharmony_ci return sof_ops(sdev)->load_firmware(sdev); 7538c2ecf20Sopenharmony_ci return 0; 7548c2ecf20Sopenharmony_ci} 7558c2ecf20Sopenharmony_ciEXPORT_SYMBOL(snd_sof_load_firmware); 7568c2ecf20Sopenharmony_ci 7578c2ecf20Sopenharmony_ciint snd_sof_run_firmware(struct snd_sof_dev *sdev) 7588c2ecf20Sopenharmony_ci{ 7598c2ecf20Sopenharmony_ci int ret; 7608c2ecf20Sopenharmony_ci int init_core_mask; 7618c2ecf20Sopenharmony_ci 7628c2ecf20Sopenharmony_ci init_waitqueue_head(&sdev->boot_wait); 7638c2ecf20Sopenharmony_ci 7648c2ecf20Sopenharmony_ci /* create read-only fw_version debugfs to store boot version info */ 7658c2ecf20Sopenharmony_ci if (sdev->first_boot) { 7668c2ecf20Sopenharmony_ci ret = snd_sof_debugfs_buf_item(sdev, &sdev->fw_version, 7678c2ecf20Sopenharmony_ci sizeof(sdev->fw_version), 7688c2ecf20Sopenharmony_ci "fw_version", 0444); 7698c2ecf20Sopenharmony_ci /* errors are only due to memory allocation, not debugfs */ 7708c2ecf20Sopenharmony_ci if (ret < 0) { 7718c2ecf20Sopenharmony_ci dev_err(sdev->dev, "error: snd_sof_debugfs_buf_item failed\n"); 7728c2ecf20Sopenharmony_ci return ret; 7738c2ecf20Sopenharmony_ci } 7748c2ecf20Sopenharmony_ci } 7758c2ecf20Sopenharmony_ci 7768c2ecf20Sopenharmony_ci /* perform pre fw run operations */ 7778c2ecf20Sopenharmony_ci ret = snd_sof_dsp_pre_fw_run(sdev); 7788c2ecf20Sopenharmony_ci if (ret < 0) { 7798c2ecf20Sopenharmony_ci dev_err(sdev->dev, "error: failed pre fw run op\n"); 7808c2ecf20Sopenharmony_ci return ret; 7818c2ecf20Sopenharmony_ci } 7828c2ecf20Sopenharmony_ci 7838c2ecf20Sopenharmony_ci dev_dbg(sdev->dev, "booting DSP firmware\n"); 7848c2ecf20Sopenharmony_ci 7858c2ecf20Sopenharmony_ci /* boot the firmware on the DSP */ 7868c2ecf20Sopenharmony_ci ret = snd_sof_dsp_run(sdev); 7878c2ecf20Sopenharmony_ci if (ret < 0) { 7888c2ecf20Sopenharmony_ci dev_err(sdev->dev, "error: failed to reset DSP\n"); 7898c2ecf20Sopenharmony_ci return ret; 7908c2ecf20Sopenharmony_ci } 7918c2ecf20Sopenharmony_ci 7928c2ecf20Sopenharmony_ci init_core_mask = ret; 7938c2ecf20Sopenharmony_ci 7948c2ecf20Sopenharmony_ci /* 7958c2ecf20Sopenharmony_ci * now wait for the DSP to boot. There are 3 possible outcomes: 7968c2ecf20Sopenharmony_ci * 1. Boot wait times out indicating FW boot failure. 7978c2ecf20Sopenharmony_ci * 2. FW boots successfully and fw_ready op succeeds. 7988c2ecf20Sopenharmony_ci * 3. FW boots but fw_ready op fails. 7998c2ecf20Sopenharmony_ci */ 8008c2ecf20Sopenharmony_ci ret = wait_event_timeout(sdev->boot_wait, 8018c2ecf20Sopenharmony_ci sdev->fw_state > SOF_FW_BOOT_IN_PROGRESS, 8028c2ecf20Sopenharmony_ci msecs_to_jiffies(sdev->boot_timeout)); 8038c2ecf20Sopenharmony_ci if (ret == 0) { 8048c2ecf20Sopenharmony_ci dev_err(sdev->dev, "error: firmware boot failure\n"); 8058c2ecf20Sopenharmony_ci snd_sof_dsp_dbg_dump(sdev, SOF_DBG_REGS | SOF_DBG_MBOX | 8068c2ecf20Sopenharmony_ci SOF_DBG_TEXT | SOF_DBG_PCI); 8078c2ecf20Sopenharmony_ci sdev->fw_state = SOF_FW_BOOT_FAILED; 8088c2ecf20Sopenharmony_ci return -EIO; 8098c2ecf20Sopenharmony_ci } 8108c2ecf20Sopenharmony_ci 8118c2ecf20Sopenharmony_ci if (sdev->fw_state == SOF_FW_BOOT_COMPLETE) 8128c2ecf20Sopenharmony_ci dev_dbg(sdev->dev, "firmware boot complete\n"); 8138c2ecf20Sopenharmony_ci else 8148c2ecf20Sopenharmony_ci return -EIO; /* FW boots but fw_ready op failed */ 8158c2ecf20Sopenharmony_ci 8168c2ecf20Sopenharmony_ci /* perform post fw run operations */ 8178c2ecf20Sopenharmony_ci ret = snd_sof_dsp_post_fw_run(sdev); 8188c2ecf20Sopenharmony_ci if (ret < 0) { 8198c2ecf20Sopenharmony_ci dev_err(sdev->dev, "error: failed post fw run op\n"); 8208c2ecf20Sopenharmony_ci return ret; 8218c2ecf20Sopenharmony_ci } 8228c2ecf20Sopenharmony_ci 8238c2ecf20Sopenharmony_ci /* fw boot is complete. Update the active cores mask */ 8248c2ecf20Sopenharmony_ci sdev->enabled_cores_mask = init_core_mask; 8258c2ecf20Sopenharmony_ci 8268c2ecf20Sopenharmony_ci return 0; 8278c2ecf20Sopenharmony_ci} 8288c2ecf20Sopenharmony_ciEXPORT_SYMBOL(snd_sof_run_firmware); 8298c2ecf20Sopenharmony_ci 8308c2ecf20Sopenharmony_civoid snd_sof_fw_unload(struct snd_sof_dev *sdev) 8318c2ecf20Sopenharmony_ci{ 8328c2ecf20Sopenharmony_ci /* TODO: support module unloading at runtime */ 8338c2ecf20Sopenharmony_ci release_firmware(sdev->pdata->fw); 8348c2ecf20Sopenharmony_ci sdev->pdata->fw = NULL; 8358c2ecf20Sopenharmony_ci} 8368c2ecf20Sopenharmony_ciEXPORT_SYMBOL(snd_sof_fw_unload); 837