1 // SPDX-License-Identifier: (GPL-2.0 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 11 #include <linux/firmware.h> 12 #include <linux/module.h> 13 #include <sound/soc.h> 14 #include <sound/sof.h> 15 #include "sof-priv.h" 16 #include "ops.h" 17 18 /* see SOF_DBG_ flags */ 19 int sof_core_debug; 20 module_param_named(sof_debug, sof_core_debug, int, 0444); 21 MODULE_PARM_DESC(sof_debug, "SOF core debug options (0x0 all off)"); 22 23 /* SOF defaults if not provided by the platform in ms */ 24 #define TIMEOUT_DEFAULT_IPC_MS 500 25 #define TIMEOUT_DEFAULT_BOOT_MS 2000 26 27 /* 28 * FW Panic/fault handling. 29 */ 30 31 struct sof_panic_msg { 32 u32 id; 33 const char *msg; 34 }; 35 36 /* standard FW panic types */ 37 static const struct sof_panic_msg panic_msg[] = { 38 {SOF_IPC_PANIC_MEM, "out of memory"}, 39 {SOF_IPC_PANIC_WORK, "work subsystem init failed"}, 40 {SOF_IPC_PANIC_IPC, "IPC subsystem init failed"}, 41 {SOF_IPC_PANIC_ARCH, "arch init failed"}, 42 {SOF_IPC_PANIC_PLATFORM, "platform init failed"}, 43 {SOF_IPC_PANIC_TASK, "scheduler init failed"}, 44 {SOF_IPC_PANIC_EXCEPTION, "runtime exception"}, 45 {SOF_IPC_PANIC_DEADLOCK, "deadlock"}, 46 {SOF_IPC_PANIC_STACK, "stack overflow"}, 47 {SOF_IPC_PANIC_IDLE, "can't enter idle"}, 48 {SOF_IPC_PANIC_WFI, "invalid wait state"}, 49 {SOF_IPC_PANIC_ASSERT, "assertion failed"}, 50 }; 51 52 /* 53 * helper to be called from .dbg_dump callbacks. No error code is 54 * provided, it's left as an exercise for the caller of .dbg_dump 55 * (typically IPC or loader) 56 */ 57 void snd_sof_get_status(struct snd_sof_dev *sdev, u32 panic_code, 58 u32 tracep_code, void *oops, 59 struct sof_ipc_panic_info *panic_info, 60 void *stack, size_t stack_words) 61 { 62 u32 code; 63 int i; 64 65 /* is firmware dead ? */ 66 if ((panic_code & SOF_IPC_PANIC_MAGIC_MASK) != SOF_IPC_PANIC_MAGIC) { 67 dev_err(sdev->dev, "error: unexpected fault 0x%8.8x trace 0x%8.8x\n", 68 panic_code, tracep_code); 69 return; /* no fault ? */ 70 } 71 72 code = panic_code & (SOF_IPC_PANIC_MAGIC_MASK | SOF_IPC_PANIC_CODE_MASK); 73 74 for (i = 0; i < ARRAY_SIZE(panic_msg); i++) { 75 if (panic_msg[i].id == code) { 76 dev_err(sdev->dev, "error: %s\n", panic_msg[i].msg); 77 dev_err(sdev->dev, "error: trace point %8.8x\n", 78 tracep_code); 79 goto out; 80 } 81 } 82 83 /* unknown error */ 84 dev_err(sdev->dev, "error: unknown reason %8.8x\n", panic_code); 85 dev_err(sdev->dev, "error: trace point %8.8x\n", tracep_code); 86 87 out: 88 dev_err(sdev->dev, "error: panic at %s:%d\n", 89 panic_info->filename, panic_info->linenum); 90 sof_oops(sdev, oops); 91 sof_stack(sdev, oops, stack, stack_words); 92 } 93 EXPORT_SYMBOL(snd_sof_get_status); 94 95 /* 96 * FW Boot State Transition Diagram 97 * 98 * +-----------------------------------------------------------------------+ 99 * | | 100 * ------------------ ------------------ | 101 * | | | | | 102 * | BOOT_FAILED | | READY_FAILED |-------------------------+ | 103 * | | | | | | 104 * ------------------ ------------------ | | 105 * ^ ^ | | 106 * | | | | 107 * (FW Boot Timeout) (FW_READY FAIL) | | 108 * | | | | 109 * | | | | 110 * ------------------ | ------------------ | | 111 * | | | | | | | 112 * | IN_PROGRESS |---------------+------------->| COMPLETE | | | 113 * | | (FW Boot OK) (FW_READY OK) | | | | 114 * ------------------ ------------------ | | 115 * ^ | | | 116 * | | | | 117 * (FW Loading OK) (System Suspend/Runtime Suspend) 118 * | | | | 119 * | | | | 120 * ------------------ ------------------ | | | 121 * | | | |<-----+ | | 122 * | PREPARE | | NOT_STARTED |<---------------------+ | 123 * | | | |<---------------------------+ 124 * ------------------ ------------------ 125 * | ^ | ^ 126 * | | | | 127 * | +-----------------------+ | 128 * | (DSP Probe OK) | 129 * | | 130 * | | 131 * +------------------------------------+ 132 * (System Suspend/Runtime Suspend) 133 */ 134 135 static int sof_probe_continue(struct snd_sof_dev *sdev) 136 { 137 struct snd_sof_pdata *plat_data = sdev->pdata; 138 int ret; 139 140 /* probe the DSP hardware */ 141 ret = snd_sof_probe(sdev); 142 if (ret < 0) { 143 dev_err(sdev->dev, "error: failed to probe DSP %d\n", ret); 144 return ret; 145 } 146 147 sdev->fw_state = SOF_FW_BOOT_PREPARE; 148 149 /* check machine info */ 150 ret = sof_machine_check(sdev); 151 if (ret < 0) { 152 dev_err(sdev->dev, "error: failed to get machine info %d\n", 153 ret); 154 goto dbg_err; 155 } 156 157 /* set up platform component driver */ 158 snd_sof_new_platform_drv(sdev); 159 160 /* register any debug/trace capabilities */ 161 ret = snd_sof_dbg_init(sdev); 162 if (ret < 0) { 163 /* 164 * debugfs issues are suppressed in snd_sof_dbg_init() since 165 * we cannot rely on debugfs 166 * here we trap errors due to memory allocation only. 167 */ 168 dev_err(sdev->dev, "error: failed to init DSP trace/debug %d\n", 169 ret); 170 goto dbg_err; 171 } 172 173 /* init the IPC */ 174 sdev->ipc = snd_sof_ipc_init(sdev); 175 if (!sdev->ipc) { 176 dev_err(sdev->dev, "error: failed to init DSP IPC %d\n", ret); 177 goto ipc_err; 178 } 179 180 /* load the firmware */ 181 ret = snd_sof_load_firmware(sdev); 182 if (ret < 0) { 183 dev_err(sdev->dev, "error: failed to load DSP firmware %d\n", 184 ret); 185 goto fw_load_err; 186 } 187 188 sdev->fw_state = SOF_FW_BOOT_IN_PROGRESS; 189 190 /* 191 * Boot the firmware. The FW boot status will be modified 192 * in snd_sof_run_firmware() depending on the outcome. 193 */ 194 ret = snd_sof_run_firmware(sdev); 195 if (ret < 0) { 196 dev_err(sdev->dev, "error: failed to boot DSP firmware %d\n", 197 ret); 198 goto fw_run_err; 199 } 200 201 if (IS_ENABLED(CONFIG_SND_SOC_SOF_DEBUG_ENABLE_FIRMWARE_TRACE) || 202 (sof_core_debug & SOF_DBG_ENABLE_TRACE)) { 203 sdev->dtrace_is_supported = true; 204 205 /* init DMA trace */ 206 ret = snd_sof_init_trace(sdev); 207 if (ret < 0) { 208 /* non fatal */ 209 dev_warn(sdev->dev, 210 "warning: failed to initialize trace %d\n", 211 ret); 212 } 213 } else { 214 dev_dbg(sdev->dev, "SOF firmware trace disabled\n"); 215 } 216 217 /* hereafter all FW boot flows are for PM reasons */ 218 sdev->first_boot = false; 219 220 /* now register audio DSP platform driver and dai */ 221 ret = devm_snd_soc_register_component(sdev->dev, &sdev->plat_drv, 222 sof_ops(sdev)->drv, 223 sof_ops(sdev)->num_drv); 224 if (ret < 0) { 225 dev_err(sdev->dev, 226 "error: failed to register DSP DAI driver %d\n", ret); 227 goto fw_trace_err; 228 } 229 230 ret = snd_sof_machine_register(sdev, plat_data); 231 if (ret < 0) 232 goto fw_trace_err; 233 234 /* 235 * Some platforms in SOF, ex: BYT, may not have their platform PM 236 * callbacks set. Increment the usage count so as to 237 * prevent the device from entering runtime suspend. 238 */ 239 if (!sof_ops(sdev)->runtime_suspend || !sof_ops(sdev)->runtime_resume) 240 pm_runtime_get_noresume(sdev->dev); 241 242 if (plat_data->sof_probe_complete) 243 plat_data->sof_probe_complete(sdev->dev); 244 245 return 0; 246 247 fw_trace_err: 248 snd_sof_free_trace(sdev); 249 fw_run_err: 250 snd_sof_fw_unload(sdev); 251 fw_load_err: 252 snd_sof_ipc_free(sdev); 253 ipc_err: 254 snd_sof_free_debug(sdev); 255 dbg_err: 256 snd_sof_remove(sdev); 257 258 /* all resources freed, update state to match */ 259 sdev->fw_state = SOF_FW_BOOT_NOT_STARTED; 260 sdev->first_boot = true; 261 262 return ret; 263 } 264 265 static void sof_probe_work(struct work_struct *work) 266 { 267 struct snd_sof_dev *sdev = 268 container_of(work, struct snd_sof_dev, probe_work); 269 int ret; 270 271 ret = sof_probe_continue(sdev); 272 if (ret < 0) { 273 /* errors cannot be propagated, log */ 274 dev_err(sdev->dev, "error: %s failed err: %d\n", __func__, ret); 275 } 276 } 277 278 int snd_sof_device_probe(struct device *dev, struct snd_sof_pdata *plat_data) 279 { 280 struct snd_sof_dev *sdev; 281 282 sdev = devm_kzalloc(dev, sizeof(*sdev), GFP_KERNEL); 283 if (!sdev) 284 return -ENOMEM; 285 286 /* initialize sof device */ 287 sdev->dev = dev; 288 289 /* initialize default D0 sub-state */ 290 sdev->d0_substate = SOF_DSP_D0I0; 291 292 sdev->pdata = plat_data; 293 sdev->first_boot = true; 294 sdev->fw_state = SOF_FW_BOOT_NOT_STARTED; 295 dev_set_drvdata(dev, sdev); 296 297 /* check all mandatory ops */ 298 if (!sof_ops(sdev) || !sof_ops(sdev)->probe || !sof_ops(sdev)->run || 299 !sof_ops(sdev)->block_read || !sof_ops(sdev)->block_write || 300 !sof_ops(sdev)->send_msg || !sof_ops(sdev)->load_firmware || 301 !sof_ops(sdev)->ipc_msg_data || !sof_ops(sdev)->ipc_pcm_params || 302 !sof_ops(sdev)->fw_ready) 303 return -EINVAL; 304 305 INIT_LIST_HEAD(&sdev->pcm_list); 306 INIT_LIST_HEAD(&sdev->kcontrol_list); 307 INIT_LIST_HEAD(&sdev->widget_list); 308 INIT_LIST_HEAD(&sdev->dai_list); 309 INIT_LIST_HEAD(&sdev->route_list); 310 spin_lock_init(&sdev->ipc_lock); 311 spin_lock_init(&sdev->hw_lock); 312 313 if (IS_ENABLED(CONFIG_SND_SOC_SOF_PROBE_WORK_QUEUE)) 314 INIT_WORK(&sdev->probe_work, sof_probe_work); 315 316 /* set default timeouts if none provided */ 317 if (plat_data->desc->ipc_timeout == 0) 318 sdev->ipc_timeout = TIMEOUT_DEFAULT_IPC_MS; 319 else 320 sdev->ipc_timeout = plat_data->desc->ipc_timeout; 321 if (plat_data->desc->boot_timeout == 0) 322 sdev->boot_timeout = TIMEOUT_DEFAULT_BOOT_MS; 323 else 324 sdev->boot_timeout = plat_data->desc->boot_timeout; 325 326 if (IS_ENABLED(CONFIG_SND_SOC_SOF_PROBE_WORK_QUEUE)) { 327 schedule_work(&sdev->probe_work); 328 return 0; 329 } 330 331 return sof_probe_continue(sdev); 332 } 333 EXPORT_SYMBOL(snd_sof_device_probe); 334 335 int snd_sof_device_remove(struct device *dev) 336 { 337 struct snd_sof_dev *sdev = dev_get_drvdata(dev); 338 struct snd_sof_pdata *pdata = sdev->pdata; 339 340 if (IS_ENABLED(CONFIG_SND_SOC_SOF_PROBE_WORK_QUEUE)) 341 cancel_work_sync(&sdev->probe_work); 342 343 if (sdev->fw_state > SOF_FW_BOOT_NOT_STARTED) { 344 snd_sof_fw_unload(sdev); 345 snd_sof_ipc_free(sdev); 346 snd_sof_free_debug(sdev); 347 snd_sof_free_trace(sdev); 348 } 349 350 /* 351 * Unregister machine driver. This will unbind the snd_card which 352 * will remove the component driver and unload the topology 353 * before freeing the snd_card. 354 */ 355 snd_sof_machine_unregister(sdev, pdata); 356 357 /* 358 * Unregistering the machine driver results in unloading the topology. 359 * Some widgets, ex: scheduler, attempt to power down the core they are 360 * scheduled on, when they are unloaded. Therefore, the DSP must be 361 * removed only after the topology has been unloaded. 362 */ 363 if (sdev->fw_state > SOF_FW_BOOT_NOT_STARTED) 364 snd_sof_remove(sdev); 365 366 /* release firmware */ 367 release_firmware(pdata->fw); 368 pdata->fw = NULL; 369 370 return 0; 371 } 372 EXPORT_SYMBOL(snd_sof_device_remove); 373 374 MODULE_AUTHOR("Liam Girdwood"); 375 MODULE_DESCRIPTION("Sound Open Firmware (SOF) Core"); 376 MODULE_LICENSE("Dual BSD/GPL"); 377 MODULE_ALIAS("platform:sof-audio"); 378