ipc4.c 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681
  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) 2022 Intel Corporation. All rights reserved.
  7. //
  8. // Authors: Rander Wang <[email protected]>
  9. // Peter Ujfalusi <[email protected]>
  10. //
  11. #include <sound/sof/header.h>
  12. #include <sound/sof/ipc4/header.h>
  13. #include "sof-priv.h"
  14. #include "sof-audio.h"
  15. #include "ipc4-priv.h"
  16. #include "ops.h"
  17. #ifdef DEBUG_VERBOSE
  18. #define sof_ipc4_dump_payload(sdev, ipc_data, size) \
  19. print_hex_dump_debug("Message payload: ", \
  20. DUMP_PREFIX_OFFSET, \
  21. 16, 4, ipc_data, size, false)
  22. #else
  23. #define sof_ipc4_dump_payload(sdev, ipc_data, size) do { } while (0)
  24. #endif
  25. static const struct sof_ipc4_fw_status {
  26. int status;
  27. char *msg;
  28. } ipc4_status[] = {
  29. {0, "The operation was successful"},
  30. {1, "Invalid parameter specified"},
  31. {2, "Unknown message type specified"},
  32. {3, "Not enough space in the IPC reply buffer to complete the request"},
  33. {4, "The system or resource is busy"},
  34. {5, "Replaced ADSP IPC PENDING (unused)"},
  35. {6, "Unknown error while processing the request"},
  36. {7, "Unsupported operation requested"},
  37. {8, "Reserved (ADSP_STAGE_UNINITIALIZED removed)"},
  38. {9, "Specified resource not found"},
  39. {10, "A resource's ID requested to be created is already assigned"},
  40. {11, "Reserved (ADSP_IPC_OUT_OF_MIPS removed)"},
  41. {12, "Required resource is in invalid state"},
  42. {13, "Requested power transition failed to complete"},
  43. {14, "Manifest of the library being loaded is invalid"},
  44. {15, "Requested service or data is unavailable on the target platform"},
  45. {42, "Library target address is out of storage memory range"},
  46. {43, "Reserved"},
  47. {44, "Image verification by CSE failed"},
  48. {100, "General module management error"},
  49. {101, "Module loading failed"},
  50. {102, "Integrity check of the loaded module content failed"},
  51. {103, "Attempt to unload code of the module in use"},
  52. {104, "Other failure of module instance initialization request"},
  53. {105, "Reserved (ADSP_IPC_OUT_OF_MIPS removed)"},
  54. {106, "Reserved (ADSP_IPC_CONFIG_GET_ERROR removed)"},
  55. {107, "Reserved (ADSP_IPC_CONFIG_SET_ERROR removed)"},
  56. {108, "Reserved (ADSP_IPC_LARGE_CONFIG_GET_ERROR removed)"},
  57. {109, "Reserved (ADSP_IPC_LARGE_CONFIG_SET_ERROR removed)"},
  58. {110, "Invalid (out of range) module ID provided"},
  59. {111, "Invalid module instance ID provided"},
  60. {112, "Invalid queue (pin) ID provided"},
  61. {113, "Invalid destination queue (pin) ID provided"},
  62. {114, "Reserved (ADSP_IPC_BIND_UNBIND_DST_SINK_UNSUPPORTED removed)"},
  63. {115, "Reserved (ADSP_IPC_UNLOAD_INST_EXISTS removed)"},
  64. {116, "Invalid target code ID provided"},
  65. {117, "Injection DMA buffer is too small for probing the input pin"},
  66. {118, "Extraction DMA buffer is too small for probing the output pin"},
  67. {120, "Invalid ID of configuration item provided in TLV list"},
  68. {121, "Invalid length of configuration item provided in TLV list"},
  69. {122, "Invalid structure of configuration item provided"},
  70. {140, "Initialization of DMA Gateway failed"},
  71. {141, "Invalid ID of gateway provided"},
  72. {142, "Setting state of DMA Gateway failed"},
  73. {143, "DMA_CONTROL message targeting gateway not allocated yet"},
  74. {150, "Attempt to configure SCLK while I2S port is running"},
  75. {151, "Attempt to configure MCLK while I2S port is running"},
  76. {152, "Attempt to stop SCLK that is not running"},
  77. {153, "Attempt to stop MCLK that is not running"},
  78. {160, "Reserved (ADSP_IPC_PIPELINE_NOT_INITIALIZED removed)"},
  79. {161, "Reserved (ADSP_IPC_PIPELINE_NOT_EXIST removed)"},
  80. {162, "Reserved (ADSP_IPC_PIPELINE_SAVE_FAILED removed)"},
  81. {163, "Reserved (ADSP_IPC_PIPELINE_RESTORE_FAILED removed)"},
  82. {165, "Reserved (ADSP_IPC_PIPELINE_ALREADY_EXISTS removed)"},
  83. };
  84. static int sof_ipc4_check_reply_status(struct snd_sof_dev *sdev, u32 status)
  85. {
  86. int i, ret;
  87. status &= SOF_IPC4_REPLY_STATUS;
  88. if (!status)
  89. return 0;
  90. for (i = 0; i < ARRAY_SIZE(ipc4_status); i++) {
  91. if (ipc4_status[i].status == status) {
  92. dev_err(sdev->dev, "FW reported error: %u - %s\n",
  93. status, ipc4_status[i].msg);
  94. goto to_errno;
  95. }
  96. }
  97. if (i == ARRAY_SIZE(ipc4_status))
  98. dev_err(sdev->dev, "FW reported error: %u - Unknown\n", status);
  99. to_errno:
  100. switch (status) {
  101. case 8:
  102. case 11:
  103. case 105 ... 109:
  104. case 114 ... 115:
  105. case 160 ... 163:
  106. case 165:
  107. ret = -ENOENT;
  108. break;
  109. case 4:
  110. case 150:
  111. case 151:
  112. ret = -EBUSY;
  113. break;
  114. default:
  115. ret = -EINVAL;
  116. break;
  117. }
  118. return ret;
  119. }
  120. #if IS_ENABLED(CONFIG_SND_SOC_SOF_DEBUG_VERBOSE_IPC)
  121. #define DBG_IPC4_MSG_TYPE_ENTRY(type) [SOF_IPC4_##type] = #type
  122. static const char * const ipc4_dbg_mod_msg_type[] = {
  123. DBG_IPC4_MSG_TYPE_ENTRY(MOD_INIT_INSTANCE),
  124. DBG_IPC4_MSG_TYPE_ENTRY(MOD_CONFIG_GET),
  125. DBG_IPC4_MSG_TYPE_ENTRY(MOD_CONFIG_SET),
  126. DBG_IPC4_MSG_TYPE_ENTRY(MOD_LARGE_CONFIG_GET),
  127. DBG_IPC4_MSG_TYPE_ENTRY(MOD_LARGE_CONFIG_SET),
  128. DBG_IPC4_MSG_TYPE_ENTRY(MOD_BIND),
  129. DBG_IPC4_MSG_TYPE_ENTRY(MOD_UNBIND),
  130. DBG_IPC4_MSG_TYPE_ENTRY(MOD_SET_DX),
  131. DBG_IPC4_MSG_TYPE_ENTRY(MOD_SET_D0IX),
  132. DBG_IPC4_MSG_TYPE_ENTRY(MOD_ENTER_MODULE_RESTORE),
  133. DBG_IPC4_MSG_TYPE_ENTRY(MOD_EXIT_MODULE_RESTORE),
  134. DBG_IPC4_MSG_TYPE_ENTRY(MOD_DELETE_INSTANCE),
  135. };
  136. static const char * const ipc4_dbg_glb_msg_type[] = {
  137. DBG_IPC4_MSG_TYPE_ENTRY(GLB_BOOT_CONFIG),
  138. DBG_IPC4_MSG_TYPE_ENTRY(GLB_ROM_CONTROL),
  139. DBG_IPC4_MSG_TYPE_ENTRY(GLB_IPCGATEWAY_CMD),
  140. DBG_IPC4_MSG_TYPE_ENTRY(GLB_PERF_MEASUREMENTS_CMD),
  141. DBG_IPC4_MSG_TYPE_ENTRY(GLB_CHAIN_DMA),
  142. DBG_IPC4_MSG_TYPE_ENTRY(GLB_LOAD_MULTIPLE_MODULES),
  143. DBG_IPC4_MSG_TYPE_ENTRY(GLB_UNLOAD_MULTIPLE_MODULES),
  144. DBG_IPC4_MSG_TYPE_ENTRY(GLB_CREATE_PIPELINE),
  145. DBG_IPC4_MSG_TYPE_ENTRY(GLB_DELETE_PIPELINE),
  146. DBG_IPC4_MSG_TYPE_ENTRY(GLB_SET_PIPELINE_STATE),
  147. DBG_IPC4_MSG_TYPE_ENTRY(GLB_GET_PIPELINE_STATE),
  148. DBG_IPC4_MSG_TYPE_ENTRY(GLB_GET_PIPELINE_CONTEXT_SIZE),
  149. DBG_IPC4_MSG_TYPE_ENTRY(GLB_SAVE_PIPELINE),
  150. DBG_IPC4_MSG_TYPE_ENTRY(GLB_RESTORE_PIPELINE),
  151. DBG_IPC4_MSG_TYPE_ENTRY(GLB_LOAD_LIBRARY),
  152. DBG_IPC4_MSG_TYPE_ENTRY(GLB_INTERNAL_MESSAGE),
  153. DBG_IPC4_MSG_TYPE_ENTRY(GLB_NOTIFICATION),
  154. };
  155. #define DBG_IPC4_NOTIFICATION_TYPE_ENTRY(type) [SOF_IPC4_NOTIFY_##type] = #type
  156. static const char * const ipc4_dbg_notification_type[] = {
  157. DBG_IPC4_NOTIFICATION_TYPE_ENTRY(PHRASE_DETECTED),
  158. DBG_IPC4_NOTIFICATION_TYPE_ENTRY(RESOURCE_EVENT),
  159. DBG_IPC4_NOTIFICATION_TYPE_ENTRY(LOG_BUFFER_STATUS),
  160. DBG_IPC4_NOTIFICATION_TYPE_ENTRY(TIMESTAMP_CAPTURED),
  161. DBG_IPC4_NOTIFICATION_TYPE_ENTRY(FW_READY),
  162. DBG_IPC4_NOTIFICATION_TYPE_ENTRY(FW_AUD_CLASS_RESULT),
  163. DBG_IPC4_NOTIFICATION_TYPE_ENTRY(EXCEPTION_CAUGHT),
  164. DBG_IPC4_NOTIFICATION_TYPE_ENTRY(MODULE_NOTIFICATION),
  165. DBG_IPC4_NOTIFICATION_TYPE_ENTRY(PROBE_DATA_AVAILABLE),
  166. DBG_IPC4_NOTIFICATION_TYPE_ENTRY(ASYNC_MSG_SRVC_MESSAGE),
  167. };
  168. static void sof_ipc4_log_header(struct device *dev, u8 *text, struct sof_ipc4_msg *msg,
  169. bool data_size_valid)
  170. {
  171. u32 val, type;
  172. const u8 *str2 = NULL;
  173. const u8 *str = NULL;
  174. val = msg->primary & SOF_IPC4_MSG_TARGET_MASK;
  175. type = SOF_IPC4_MSG_TYPE_GET(msg->primary);
  176. if (val == SOF_IPC4_MSG_TARGET(SOF_IPC4_MODULE_MSG)) {
  177. /* Module message */
  178. if (type < SOF_IPC4_MOD_TYPE_LAST)
  179. str = ipc4_dbg_mod_msg_type[type];
  180. if (!str)
  181. str = "Unknown Module message type";
  182. } else {
  183. /* Global FW message */
  184. if (type < SOF_IPC4_GLB_TYPE_LAST)
  185. str = ipc4_dbg_glb_msg_type[type];
  186. if (!str)
  187. str = "Unknown Global message type";
  188. if (type == SOF_IPC4_GLB_NOTIFICATION) {
  189. /* Notification message */
  190. u32 notif = SOF_IPC4_NOTIFICATION_TYPE_GET(msg->primary);
  191. /* Do not print log buffer notification if not desired */
  192. if (notif == SOF_IPC4_NOTIFY_LOG_BUFFER_STATUS &&
  193. !sof_debug_check_flag(SOF_DBG_PRINT_DMA_POSITION_UPDATE_LOGS))
  194. return;
  195. if (notif < SOF_IPC4_NOTIFY_TYPE_LAST)
  196. str2 = ipc4_dbg_notification_type[notif];
  197. if (!str2)
  198. str2 = "Unknown Global notification";
  199. }
  200. }
  201. if (str2) {
  202. if (data_size_valid && msg->data_size)
  203. dev_dbg(dev, "%s: %#x|%#x: %s|%s [data size: %zu]\n",
  204. text, msg->primary, msg->extension, str, str2,
  205. msg->data_size);
  206. else
  207. dev_dbg(dev, "%s: %#x|%#x: %s|%s\n", text, msg->primary,
  208. msg->extension, str, str2);
  209. } else {
  210. if (data_size_valid && msg->data_size)
  211. dev_dbg(dev, "%s: %#x|%#x: %s [data size: %zu]\n",
  212. text, msg->primary, msg->extension, str,
  213. msg->data_size);
  214. else
  215. dev_dbg(dev, "%s: %#x|%#x: %s\n", text, msg->primary,
  216. msg->extension, str);
  217. }
  218. }
  219. #else /* CONFIG_SND_SOC_SOF_DEBUG_VERBOSE_IPC */
  220. static void sof_ipc4_log_header(struct device *dev, u8 *text, struct sof_ipc4_msg *msg,
  221. bool data_size_valid)
  222. {
  223. /* Do not print log buffer notification if not desired */
  224. if (!sof_debug_check_flag(SOF_DBG_PRINT_DMA_POSITION_UPDATE_LOGS) &&
  225. !SOF_IPC4_MSG_IS_MODULE_MSG(msg->primary) &&
  226. SOF_IPC4_MSG_TYPE_GET(msg->primary) == SOF_IPC4_GLB_NOTIFICATION &&
  227. SOF_IPC4_NOTIFICATION_TYPE_GET(msg->primary) == SOF_IPC4_NOTIFY_LOG_BUFFER_STATUS)
  228. return;
  229. if (data_size_valid && msg->data_size)
  230. dev_dbg(dev, "%s: %#x|%#x [data size: %zu]\n", text,
  231. msg->primary, msg->extension, msg->data_size);
  232. else
  233. dev_dbg(dev, "%s: %#x|%#x\n", text, msg->primary, msg->extension);
  234. }
  235. #endif
  236. static int sof_ipc4_get_reply(struct snd_sof_dev *sdev)
  237. {
  238. struct snd_sof_ipc_msg *msg = sdev->msg;
  239. struct sof_ipc4_msg *ipc4_reply;
  240. int ret;
  241. /* get the generic reply */
  242. ipc4_reply = msg->reply_data;
  243. sof_ipc4_log_header(sdev->dev, "ipc tx reply", ipc4_reply, false);
  244. ret = sof_ipc4_check_reply_status(sdev, ipc4_reply->primary);
  245. if (ret)
  246. return ret;
  247. /* No other information is expected for non large config get replies */
  248. if (!msg->reply_size || !SOF_IPC4_MSG_IS_MODULE_MSG(ipc4_reply->primary) ||
  249. (SOF_IPC4_MSG_TYPE_GET(ipc4_reply->primary) != SOF_IPC4_MOD_LARGE_CONFIG_GET))
  250. return 0;
  251. /* Read the requested payload */
  252. snd_sof_dsp_mailbox_read(sdev, sdev->dsp_box.offset, ipc4_reply->data_ptr,
  253. msg->reply_size);
  254. return 0;
  255. }
  256. /* wait for IPC message reply */
  257. static int ipc4_wait_tx_done(struct snd_sof_ipc *ipc, void *reply_data)
  258. {
  259. struct snd_sof_ipc_msg *msg = &ipc->msg;
  260. struct sof_ipc4_msg *ipc4_msg = msg->msg_data;
  261. struct snd_sof_dev *sdev = ipc->sdev;
  262. int ret;
  263. /* wait for DSP IPC completion */
  264. ret = wait_event_timeout(msg->waitq, msg->ipc_complete,
  265. msecs_to_jiffies(sdev->ipc_timeout));
  266. if (ret == 0) {
  267. dev_err(sdev->dev, "ipc timed out for %#x|%#x\n",
  268. ipc4_msg->primary, ipc4_msg->extension);
  269. snd_sof_handle_fw_exception(ipc->sdev, "IPC timeout");
  270. return -ETIMEDOUT;
  271. }
  272. if (msg->reply_error) {
  273. dev_err(sdev->dev, "ipc error for msg %#x|%#x\n",
  274. ipc4_msg->primary, ipc4_msg->extension);
  275. ret = msg->reply_error;
  276. } else {
  277. if (reply_data) {
  278. struct sof_ipc4_msg *ipc4_reply = msg->reply_data;
  279. struct sof_ipc4_msg *ipc4_reply_data = reply_data;
  280. /* Copy the header */
  281. ipc4_reply_data->header_u64 = ipc4_reply->header_u64;
  282. if (msg->reply_size && ipc4_reply_data->data_ptr) {
  283. /* copy the payload returned from DSP */
  284. memcpy(ipc4_reply_data->data_ptr, ipc4_reply->data_ptr,
  285. msg->reply_size);
  286. ipc4_reply_data->data_size = msg->reply_size;
  287. }
  288. }
  289. ret = 0;
  290. sof_ipc4_log_header(sdev->dev, "ipc tx done ", ipc4_msg, true);
  291. }
  292. /* re-enable dumps after successful IPC tx */
  293. if (sdev->ipc_dump_printed) {
  294. sdev->dbg_dump_printed = false;
  295. sdev->ipc_dump_printed = false;
  296. }
  297. return ret;
  298. }
  299. static int ipc4_tx_msg_unlocked(struct snd_sof_ipc *ipc,
  300. void *msg_data, size_t msg_bytes,
  301. void *reply_data, size_t reply_bytes)
  302. {
  303. struct sof_ipc4_msg *ipc4_msg = msg_data;
  304. struct snd_sof_dev *sdev = ipc->sdev;
  305. int ret;
  306. if (msg_bytes > ipc->max_payload_size || reply_bytes > ipc->max_payload_size)
  307. return -EINVAL;
  308. ret = sof_ipc_send_msg(sdev, msg_data, msg_bytes, reply_bytes);
  309. if (ret) {
  310. dev_err_ratelimited(sdev->dev,
  311. "%s: ipc message send for %#x|%#x failed: %d\n",
  312. __func__, ipc4_msg->primary, ipc4_msg->extension, ret);
  313. return ret;
  314. }
  315. sof_ipc4_log_header(sdev->dev, "ipc tx ", msg_data, true);
  316. /* now wait for completion */
  317. return ipc4_wait_tx_done(ipc, reply_data);
  318. }
  319. static int sof_ipc4_tx_msg(struct snd_sof_dev *sdev, void *msg_data, size_t msg_bytes,
  320. void *reply_data, size_t reply_bytes, bool no_pm)
  321. {
  322. struct snd_sof_ipc *ipc = sdev->ipc;
  323. #ifdef DEBUG_VERBOSE
  324. struct sof_ipc4_msg *msg = NULL;
  325. #endif
  326. int ret;
  327. if (!msg_data)
  328. return -EINVAL;
  329. /* Serialise IPC TX */
  330. mutex_lock(&ipc->tx_mutex);
  331. ret = ipc4_tx_msg_unlocked(ipc, msg_data, msg_bytes, reply_data, reply_bytes);
  332. mutex_unlock(&ipc->tx_mutex);
  333. #ifdef DEBUG_VERBOSE
  334. /* payload is indicated by non zero msg/reply_bytes */
  335. if (msg_bytes)
  336. msg = msg_data;
  337. else if (reply_bytes)
  338. msg = reply_data;
  339. if (msg)
  340. sof_ipc4_dump_payload(sdev, msg->data_ptr, msg->data_size);
  341. #endif
  342. return ret;
  343. }
  344. static int sof_ipc4_set_get_data(struct snd_sof_dev *sdev, void *data,
  345. size_t payload_bytes, bool set)
  346. {
  347. const struct sof_dsp_power_state target_state = {
  348. .state = SOF_DSP_PM_D0,
  349. };
  350. size_t payload_limit = sdev->ipc->max_payload_size;
  351. struct sof_ipc4_msg *ipc4_msg = data;
  352. struct sof_ipc4_msg tx = {{ 0 }};
  353. struct sof_ipc4_msg rx = {{ 0 }};
  354. size_t remaining = payload_bytes;
  355. size_t offset = 0;
  356. size_t chunk_size;
  357. int ret;
  358. if (!data)
  359. return -EINVAL;
  360. if ((ipc4_msg->primary & SOF_IPC4_MSG_TARGET_MASK) !=
  361. SOF_IPC4_MSG_TARGET(SOF_IPC4_MODULE_MSG))
  362. return -EINVAL;
  363. ipc4_msg->primary &= ~SOF_IPC4_MSG_TYPE_MASK;
  364. tx.primary = ipc4_msg->primary;
  365. tx.extension = ipc4_msg->extension;
  366. if (set)
  367. tx.primary |= SOF_IPC4_MSG_TYPE_SET(SOF_IPC4_MOD_LARGE_CONFIG_SET);
  368. else
  369. tx.primary |= SOF_IPC4_MSG_TYPE_SET(SOF_IPC4_MOD_LARGE_CONFIG_GET);
  370. tx.extension &= ~SOF_IPC4_MOD_EXT_MSG_SIZE_MASK;
  371. tx.extension |= SOF_IPC4_MOD_EXT_MSG_SIZE(payload_bytes);
  372. tx.extension |= SOF_IPC4_MOD_EXT_MSG_FIRST_BLOCK(1);
  373. /* ensure the DSP is in D0i0 before sending IPC */
  374. ret = snd_sof_dsp_set_power_state(sdev, &target_state);
  375. if (ret < 0)
  376. return ret;
  377. /* Serialise IPC TX */
  378. mutex_lock(&sdev->ipc->tx_mutex);
  379. do {
  380. size_t tx_size, rx_size;
  381. if (remaining > payload_limit) {
  382. chunk_size = payload_limit;
  383. } else {
  384. chunk_size = remaining;
  385. if (set)
  386. tx.extension |= SOF_IPC4_MOD_EXT_MSG_LAST_BLOCK(1);
  387. }
  388. if (offset) {
  389. tx.extension &= ~SOF_IPC4_MOD_EXT_MSG_FIRST_BLOCK_MASK;
  390. tx.extension &= ~SOF_IPC4_MOD_EXT_MSG_SIZE_MASK;
  391. tx.extension |= SOF_IPC4_MOD_EXT_MSG_SIZE(offset);
  392. }
  393. if (set) {
  394. tx.data_size = chunk_size;
  395. tx.data_ptr = ipc4_msg->data_ptr + offset;
  396. tx_size = chunk_size;
  397. rx_size = 0;
  398. } else {
  399. rx.primary = 0;
  400. rx.extension = 0;
  401. rx.data_size = chunk_size;
  402. rx.data_ptr = ipc4_msg->data_ptr + offset;
  403. tx_size = 0;
  404. rx_size = chunk_size;
  405. }
  406. /* Send the message for the current chunk */
  407. ret = ipc4_tx_msg_unlocked(sdev->ipc, &tx, tx_size, &rx, rx_size);
  408. if (ret < 0) {
  409. dev_err(sdev->dev,
  410. "%s: large config %s failed at offset %zu: %d\n",
  411. __func__, set ? "set" : "get", offset, ret);
  412. goto out;
  413. }
  414. if (!set && rx.extension & SOF_IPC4_MOD_EXT_MSG_FIRST_BLOCK_MASK) {
  415. /* Verify the firmware reported total payload size */
  416. rx_size = rx.extension & SOF_IPC4_MOD_EXT_MSG_SIZE_MASK;
  417. if (rx_size > payload_bytes) {
  418. dev_err(sdev->dev,
  419. "%s: Receive buffer (%zu) is too small for %zu\n",
  420. __func__, payload_bytes, rx_size);
  421. ret = -ENOMEM;
  422. goto out;
  423. }
  424. if (rx_size < chunk_size) {
  425. chunk_size = rx_size;
  426. remaining = rx_size;
  427. } else if (rx_size < payload_bytes) {
  428. remaining = rx_size;
  429. }
  430. }
  431. offset += chunk_size;
  432. remaining -= chunk_size;
  433. } while (remaining);
  434. /* Adjust the received data size if needed */
  435. if (!set && payload_bytes != offset)
  436. ipc4_msg->data_size = offset;
  437. sof_ipc4_dump_payload(sdev, ipc4_msg->data_ptr, ipc4_msg->data_size);
  438. out:
  439. mutex_unlock(&sdev->ipc->tx_mutex);
  440. return ret;
  441. }
  442. static int sof_ipc4_init_msg_memory(struct snd_sof_dev *sdev)
  443. {
  444. struct sof_ipc4_msg *ipc4_msg;
  445. struct snd_sof_ipc_msg *msg = &sdev->ipc->msg;
  446. /* TODO: get max_payload_size from firmware */
  447. sdev->ipc->max_payload_size = SOF_IPC4_MSG_MAX_SIZE;
  448. /* Allocate memory for the ipc4 container and the maximum payload */
  449. msg->reply_data = devm_kzalloc(sdev->dev, sdev->ipc->max_payload_size +
  450. sizeof(struct sof_ipc4_msg), GFP_KERNEL);
  451. if (!msg->reply_data)
  452. return -ENOMEM;
  453. ipc4_msg = msg->reply_data;
  454. ipc4_msg->data_ptr = msg->reply_data + sizeof(struct sof_ipc4_msg);
  455. return 0;
  456. }
  457. static int ipc4_fw_ready(struct snd_sof_dev *sdev, struct sof_ipc4_msg *ipc4_msg)
  458. {
  459. int inbox_offset, inbox_size, outbox_offset, outbox_size;
  460. /* no need to re-check version/ABI for subsequent boots */
  461. if (!sdev->first_boot)
  462. return 0;
  463. /* Set up the windows for IPC communication */
  464. inbox_offset = snd_sof_dsp_get_mailbox_offset(sdev);
  465. if (inbox_offset < 0) {
  466. dev_err(sdev->dev, "%s: No mailbox offset\n", __func__);
  467. return inbox_offset;
  468. }
  469. inbox_size = SOF_IPC4_MSG_MAX_SIZE;
  470. outbox_offset = snd_sof_dsp_get_window_offset(sdev, SOF_IPC4_OUTBOX_WINDOW_IDX);
  471. outbox_size = SOF_IPC4_MSG_MAX_SIZE;
  472. sdev->dsp_box.offset = inbox_offset;
  473. sdev->dsp_box.size = inbox_size;
  474. sdev->host_box.offset = outbox_offset;
  475. sdev->host_box.size = outbox_size;
  476. sdev->debug_box.offset = snd_sof_dsp_get_window_offset(sdev,
  477. SOF_IPC4_DEBUG_WINDOW_IDX);
  478. dev_dbg(sdev->dev, "mailbox upstream 0x%x - size 0x%x\n",
  479. inbox_offset, inbox_size);
  480. dev_dbg(sdev->dev, "mailbox downstream 0x%x - size 0x%x\n",
  481. outbox_offset, outbox_size);
  482. dev_dbg(sdev->dev, "debug box 0x%x\n", sdev->debug_box.offset);
  483. return sof_ipc4_init_msg_memory(sdev);
  484. }
  485. static void sof_ipc4_rx_msg(struct snd_sof_dev *sdev)
  486. {
  487. struct sof_ipc4_msg *ipc4_msg = sdev->ipc->msg.rx_data;
  488. size_t data_size = 0;
  489. int err;
  490. if (!ipc4_msg || !SOF_IPC4_MSG_IS_NOTIFICATION(ipc4_msg->primary))
  491. return;
  492. ipc4_msg->data_ptr = NULL;
  493. ipc4_msg->data_size = 0;
  494. sof_ipc4_log_header(sdev->dev, "ipc rx ", ipc4_msg, false);
  495. switch (SOF_IPC4_NOTIFICATION_TYPE_GET(ipc4_msg->primary)) {
  496. case SOF_IPC4_NOTIFY_FW_READY:
  497. /* check for FW boot completion */
  498. if (sdev->fw_state == SOF_FW_BOOT_IN_PROGRESS) {
  499. err = ipc4_fw_ready(sdev, ipc4_msg);
  500. if (err < 0)
  501. sof_set_fw_state(sdev, SOF_FW_BOOT_READY_FAILED);
  502. else
  503. sof_set_fw_state(sdev, SOF_FW_BOOT_READY_OK);
  504. /* wake up firmware loader */
  505. wake_up(&sdev->boot_wait);
  506. }
  507. break;
  508. case SOF_IPC4_NOTIFY_RESOURCE_EVENT:
  509. data_size = sizeof(struct sof_ipc4_notify_resource_data);
  510. break;
  511. case SOF_IPC4_NOTIFY_LOG_BUFFER_STATUS:
  512. sof_ipc4_mtrace_update_pos(sdev, SOF_IPC4_LOG_CORE_GET(ipc4_msg->primary));
  513. break;
  514. case SOF_IPC4_NOTIFY_EXCEPTION_CAUGHT:
  515. snd_sof_dsp_panic(sdev, 0, true);
  516. break;
  517. default:
  518. dev_dbg(sdev->dev, "Unhandled DSP message: %#x|%#x\n",
  519. ipc4_msg->primary, ipc4_msg->extension);
  520. break;
  521. }
  522. if (data_size) {
  523. ipc4_msg->data_ptr = kmalloc(data_size, GFP_KERNEL);
  524. if (!ipc4_msg->data_ptr)
  525. return;
  526. ipc4_msg->data_size = data_size;
  527. snd_sof_ipc_msg_data(sdev, NULL, ipc4_msg->data_ptr, ipc4_msg->data_size);
  528. }
  529. sof_ipc4_log_header(sdev->dev, "ipc rx done ", ipc4_msg, true);
  530. if (data_size) {
  531. kfree(ipc4_msg->data_ptr);
  532. ipc4_msg->data_ptr = NULL;
  533. ipc4_msg->data_size = 0;
  534. }
  535. }
  536. static int sof_ipc4_set_core_state(struct snd_sof_dev *sdev, int core_idx, bool on)
  537. {
  538. struct sof_ipc4_dx_state_info dx_state;
  539. struct sof_ipc4_msg msg;
  540. dx_state.core_mask = BIT(core_idx);
  541. if (on)
  542. dx_state.dx_mask = BIT(core_idx);
  543. else
  544. dx_state.dx_mask = 0;
  545. msg.primary = SOF_IPC4_MSG_TYPE_SET(SOF_IPC4_MOD_SET_DX);
  546. msg.primary |= SOF_IPC4_MSG_DIR(SOF_IPC4_MSG_REQUEST);
  547. msg.primary |= SOF_IPC4_MSG_TARGET(SOF_IPC4_MODULE_MSG);
  548. msg.extension = 0;
  549. msg.data_ptr = &dx_state;
  550. msg.data_size = sizeof(dx_state);
  551. return sof_ipc4_tx_msg(sdev, &msg, msg.data_size, NULL, 0, false);
  552. }
  553. /*
  554. * The context save callback is used to send a message to the firmware notifying
  555. * it that the primary core is going to be turned off, which is used as an
  556. * indication to prepare for a full power down, thus preparing for IMR boot
  557. * (when supported)
  558. *
  559. * Note: in IPC4 there is no message used to restore context, thus no context
  560. * restore callback is implemented
  561. */
  562. static int sof_ipc4_ctx_save(struct snd_sof_dev *sdev)
  563. {
  564. return sof_ipc4_set_core_state(sdev, SOF_DSP_PRIMARY_CORE, false);
  565. }
  566. static const struct sof_ipc_pm_ops ipc4_pm_ops = {
  567. .ctx_save = sof_ipc4_ctx_save,
  568. .set_core_state = sof_ipc4_set_core_state,
  569. };
  570. const struct sof_ipc_ops ipc4_ops = {
  571. .tx_msg = sof_ipc4_tx_msg,
  572. .rx_msg = sof_ipc4_rx_msg,
  573. .set_get_data = sof_ipc4_set_get_data,
  574. .get_reply = sof_ipc4_get_reply,
  575. .pm = &ipc4_pm_ops,
  576. .fw_loader = &ipc4_loader_ops,
  577. .tplg = &ipc4_tplg_ops,
  578. .pcm = &ipc4_pcm_ops,
  579. .fw_tracing = &ipc4_mtrace_ops,
  580. };