loader.c 5.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191
  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 <[email protected]>
  9. //
  10. // Generic firmware loader.
  11. //
  12. #include <linux/firmware.h>
  13. #include "sof-priv.h"
  14. #include "ops.h"
  15. int snd_sof_load_firmware_raw(struct snd_sof_dev *sdev)
  16. {
  17. struct snd_sof_pdata *plat_data = sdev->pdata;
  18. const char *fw_filename;
  19. ssize_t ext_man_size;
  20. int ret;
  21. /* Don't request firmware again if firmware is already requested */
  22. if (plat_data->fw)
  23. return 0;
  24. fw_filename = kasprintf(GFP_KERNEL, "%s/%s",
  25. plat_data->fw_filename_prefix,
  26. plat_data->fw_filename);
  27. if (!fw_filename)
  28. return -ENOMEM;
  29. ret = request_firmware(&plat_data->fw, fw_filename, sdev->dev);
  30. if (ret < 0) {
  31. dev_err(sdev->dev,
  32. "error: sof firmware file is missing, you might need to\n");
  33. dev_err(sdev->dev,
  34. " download it from https://github.com/thesofproject/sof-bin/\n");
  35. goto err;
  36. } else {
  37. dev_dbg(sdev->dev, "request_firmware %s successful\n",
  38. fw_filename);
  39. }
  40. /* check for extended manifest */
  41. ext_man_size = sdev->ipc->ops->fw_loader->parse_ext_manifest(sdev);
  42. if (ext_man_size > 0) {
  43. /* when no error occurred, drop extended manifest */
  44. plat_data->fw_offset = ext_man_size;
  45. } else if (!ext_man_size) {
  46. /* No extended manifest, so nothing to skip during FW load */
  47. dev_dbg(sdev->dev, "firmware doesn't contain extended manifest\n");
  48. } else {
  49. ret = ext_man_size;
  50. dev_err(sdev->dev, "error: firmware %s contains unsupported or invalid extended manifest: %d\n",
  51. fw_filename, ret);
  52. }
  53. err:
  54. kfree(fw_filename);
  55. return ret;
  56. }
  57. EXPORT_SYMBOL(snd_sof_load_firmware_raw);
  58. int snd_sof_load_firmware_memcpy(struct snd_sof_dev *sdev)
  59. {
  60. struct snd_sof_pdata *plat_data = sdev->pdata;
  61. int ret;
  62. ret = snd_sof_load_firmware_raw(sdev);
  63. if (ret < 0)
  64. return ret;
  65. /* make sure the FW header and file is valid */
  66. ret = sdev->ipc->ops->fw_loader->validate(sdev);
  67. if (ret < 0) {
  68. dev_err(sdev->dev, "error: invalid FW header\n");
  69. goto error;
  70. }
  71. /* prepare the DSP for FW loading */
  72. ret = snd_sof_dsp_reset(sdev);
  73. if (ret < 0) {
  74. dev_err(sdev->dev, "error: failed to reset DSP\n");
  75. goto error;
  76. }
  77. /* parse and load firmware modules to DSP */
  78. if (sdev->ipc->ops->fw_loader->load_fw_to_dsp) {
  79. ret = sdev->ipc->ops->fw_loader->load_fw_to_dsp(sdev);
  80. if (ret < 0) {
  81. dev_err(sdev->dev, "Firmware loading failed\n");
  82. goto error;
  83. }
  84. }
  85. return 0;
  86. error:
  87. release_firmware(plat_data->fw);
  88. plat_data->fw = NULL;
  89. return ret;
  90. }
  91. EXPORT_SYMBOL(snd_sof_load_firmware_memcpy);
  92. int snd_sof_run_firmware(struct snd_sof_dev *sdev)
  93. {
  94. int ret;
  95. init_waitqueue_head(&sdev->boot_wait);
  96. /* (re-)enable dsp dump */
  97. sdev->dbg_dump_printed = false;
  98. sdev->ipc_dump_printed = false;
  99. /* create read-only fw_version debugfs to store boot version info */
  100. if (sdev->first_boot) {
  101. ret = snd_sof_debugfs_buf_item(sdev, &sdev->fw_version,
  102. sizeof(sdev->fw_version),
  103. "fw_version", 0444);
  104. /* errors are only due to memory allocation, not debugfs */
  105. if (ret < 0) {
  106. dev_err(sdev->dev, "error: snd_sof_debugfs_buf_item failed\n");
  107. return ret;
  108. }
  109. }
  110. /* perform pre fw run operations */
  111. ret = snd_sof_dsp_pre_fw_run(sdev);
  112. if (ret < 0) {
  113. dev_err(sdev->dev, "error: failed pre fw run op\n");
  114. return ret;
  115. }
  116. dev_dbg(sdev->dev, "booting DSP firmware\n");
  117. /* boot the firmware on the DSP */
  118. ret = snd_sof_dsp_run(sdev);
  119. if (ret < 0) {
  120. snd_sof_dsp_dbg_dump(sdev, "Failed to start DSP",
  121. SOF_DBG_DUMP_MBOX | SOF_DBG_DUMP_PCI);
  122. return ret;
  123. }
  124. /*
  125. * now wait for the DSP to boot. There are 3 possible outcomes:
  126. * 1. Boot wait times out indicating FW boot failure.
  127. * 2. FW boots successfully and fw_ready op succeeds.
  128. * 3. FW boots but fw_ready op fails.
  129. */
  130. ret = wait_event_timeout(sdev->boot_wait,
  131. sdev->fw_state > SOF_FW_BOOT_IN_PROGRESS,
  132. msecs_to_jiffies(sdev->boot_timeout));
  133. if (ret == 0) {
  134. snd_sof_dsp_dbg_dump(sdev, "Firmware boot failure due to timeout",
  135. SOF_DBG_DUMP_REGS | SOF_DBG_DUMP_MBOX |
  136. SOF_DBG_DUMP_TEXT | SOF_DBG_DUMP_PCI);
  137. return -EIO;
  138. }
  139. if (sdev->fw_state == SOF_FW_BOOT_READY_FAILED)
  140. return -EIO; /* FW boots but fw_ready op failed */
  141. /* perform post fw run operations */
  142. ret = snd_sof_dsp_post_fw_run(sdev);
  143. if (ret < 0) {
  144. dev_err(sdev->dev, "error: failed post fw run op\n");
  145. return ret;
  146. }
  147. dev_dbg(sdev->dev, "firmware boot complete\n");
  148. sof_set_fw_state(sdev, SOF_FW_BOOT_COMPLETE);
  149. if (sdev->first_boot && sdev->ipc->ops->fw_loader->query_fw_configuration)
  150. return sdev->ipc->ops->fw_loader->query_fw_configuration(sdev);
  151. return 0;
  152. }
  153. EXPORT_SYMBOL(snd_sof_run_firmware);
  154. void snd_sof_fw_unload(struct snd_sof_dev *sdev)
  155. {
  156. /* TODO: support module unloading at runtime */
  157. release_firmware(sdev->pdata->fw);
  158. sdev->pdata->fw = NULL;
  159. }
  160. EXPORT_SYMBOL(snd_sof_fw_unload);