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