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