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