-
Notifications
You must be signed in to change notification settings - Fork 8.5k
ipc: intel_adsp: rework host IPC service backend and add critical send support #100682
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
15159d9
173a46d
cb9fec7
09b4d00
3cfbf17
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -43,94 +43,75 @@ static void intel_adsp_ipc_receive_cb(const void *data, size_t len, void *priv) | |
| struct intel_adsp_ipc_data *devdata = dev->data; | ||
| struct intel_adsp_ipc_ept_priv_data *priv_data = | ||
| (struct intel_adsp_ipc_ept_priv_data *)priv; | ||
| bool done = true; | ||
|
|
||
| if (len == INTEL_ADSP_IPC_CB_MSG) { | ||
| const struct intel_adsp_ipc_msg *msg = (const struct intel_adsp_ipc_msg *)data; | ||
|
|
||
| if (devdata->handle_message != NULL) { | ||
| done = devdata->handle_message(dev, devdata->handler_arg, msg->data, | ||
| msg->ext_data); | ||
| } | ||
|
|
||
| if (done) { | ||
| priv_data->cb_ret = INTEL_ADSP_IPC_CB_RET_OKAY; | ||
| } else { | ||
| priv_data->cb_ret = -EBADMSG; | ||
| } | ||
| } else if (len == INTEL_ADSP_IPC_CB_DONE) { | ||
| bool external_completion = false; | ||
|
|
||
| if (devdata->done_notify != NULL) { | ||
| external_completion = devdata->done_notify(dev, devdata->done_arg); | ||
| } | ||
|
|
||
| if (external_completion) { | ||
| priv_data->cb_ret = INTEL_ADSP_IPC_CB_RET_EXT_COMPLETE; | ||
| } else { | ||
| priv_data->cb_ret = INTEL_ADSP_IPC_CB_RET_OKAY; | ||
| } | ||
|
|
||
| __ASSERT(len == sizeof(uint32_t) * 2, "Unexpected IPC payload length: %zu", len); | ||
| __ASSERT(data != NULL, "IPC payload pointer is NULL"); | ||
|
|
||
| const uint32_t *msg = (const uint32_t *)data; | ||
|
|
||
| if (devdata->handle_message != NULL) { | ||
| priv_data->msg_done = devdata->handle_message(dev, devdata->handler_arg, msg[0], | ||
| msg[1]); | ||
| } | ||
| } | ||
|
|
||
| void intel_adsp_ipc_complete(const struct device *dev) | ||
| { | ||
| int ret; | ||
|
|
||
| ret = ipc_service_send(&intel_adsp_ipc_ept, NULL, INTEL_ADSP_IPC_SEND_DONE); | ||
| ARG_UNUSED(dev); | ||
| int ret = ipc_service_release_rx_buffer(&intel_adsp_ipc_ept, NULL); | ||
|
|
||
| ARG_UNUSED(ret); | ||
| __ASSERT(ret == 0, "ipc_service_release_rx_buffer() failed: %d", ret); | ||
| } | ||
|
|
||
| bool intel_adsp_ipc_is_complete(const struct device *dev) | ||
| { | ||
| int ret; | ||
|
|
||
| ret = ipc_service_send(&intel_adsp_ipc_ept, NULL, INTEL_ADSP_IPC_SEND_IS_COMPLETE); | ||
|
|
||
| return ret == 0; | ||
| ARG_UNUSED(dev); | ||
| return ipc_service_get_tx_buffer_size(&intel_adsp_ipc_ept) > 0; | ||
| } | ||
|
|
||
| int intel_adsp_ipc_send_message(const struct device *dev, uint32_t data, uint32_t ext_data) | ||
| { | ||
| struct intel_adsp_ipc_msg msg = {.data = data, .ext_data = ext_data}; | ||
| int ret; | ||
|
|
||
| ret = ipc_service_send(&intel_adsp_ipc_ept, &msg, INTEL_ADSP_IPC_SEND_MSG); | ||
| ARG_UNUSED(dev); | ||
| uint32_t msg[2] = {data, ext_data}; | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I've been looking at how to bolt my #98862 on top of this, but seems this is doable. So if the full payload is sent using this interface, the msg would be 4x32bit, with inband header and an optional larger payload. I do wonder if the "struct intel_adsp_ipc_msg" should be kept as the payload is somewhat special already with the 2x32bit inband message.
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I haven't seen the changes you're proposing before. I was considering a similar solution, but instead of a structure with a pointer, I saw it more as a buffer that has space for two dwords of message + payload. When SOF sends a message, the IPC backend would know how many bytes to copy to the mailbox based on the passed size.
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @tmleman Given SOF client code is using a scatter gather approach (the IPC payload is a tuple of upto two register words and a variable size payload), I think we need some structure for the payload. We will have variance betweeb different HW implementations (some transfer data inband in the doorbell registers, some don't), and between IPC versions (IPC3 has no concept of "ext_data"). I was thinking a four item tuple "first_word, second_word, payload_ptr, payload_len" would be enough to cover all variation we know about. Given we are mixing data and pointers, I think it would be better to use a struct to describe the object that is passed to IPC service send routine. I think this makes the code more readable both in SOF side, as well as the various Zephyr backends.
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @tmleman Any takes on this? Do you prefer to merge this first, and then follow-up with the payload extension? I'm ok with this as well, but would be good to have a rough consensus of how this can be extended. If this will be forever limited to "two word" payloads, not sure it's worth the effort tot move this to IPC service as we already know now this will only be useful for one type of hardware. And if that's the case, SOF code could just directly use the driver interface.
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @kv2019i Right now I don’t have a concrete design for the payload descriptor that would be passed to the backend via the IPC service. The steps I had in mind are:
Today, the data we pass through IPC service is intentionally backend‑specific, and each vendor can define its own layout. I’d like to hear from other vendors what they would need here. If we see enough commonality for SOF IPC, we can then agree on a shared struct or helper type on top of that.
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @kv2019i here are two PR in SOF to test those changes:
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @tmleman Ack thanks, I think we are aligned then. I'm good to take this in steps. We can use the size parameter to incrementally add features (if message length is more than 2 words, then client is using a newer interface). W.r.t. this:
I think can aim higher already now. If you look at IPC3 drivers in https://github.com/thesofproject/sof/blob/main/src/drivers/ , we have 4 four vendors using exactly the same layout. So I think it's really already aligned in SOF -- we have two layouts currently, IPC3 and IPC4 and the two are never compiled in same binary, so in practise there's no variation and the layout doesn't vary from hardware to another. This has the great benefit that we don't need add another abstract on client (SOF) side, but can directly use Zephyr IPC interface. I mean if any folks from other vendors object, I'm happy to change my mind, but given this already worked once, I'd rather go towards this goal and keep code in SOF clean and take full benefit of the hardware abstraction Zephyr provides. |
||
|
|
||
| if (ret < 0) { | ||
| return ret; | ||
| } | ||
|
|
||
| return 0; | ||
| return ipc_service_send(&intel_adsp_ipc_ept, &msg, sizeof(msg)); | ||
| } | ||
|
|
||
| /* | ||
| * This helper sends an IPC message and then waits synchronously for completion using the backend | ||
| * semaphore. It is currently only used by tests, SOF firmware does not rely on it. | ||
| * | ||
| * The long‑term plan is to either: | ||
| * - remove this helper entirely, | ||
| * - move the synchronous wait logic to the application layer (SOF), or | ||
| * - extend the generic IPC service API with an explicit synchronous send primitive. | ||
| * | ||
| * Until that decision is made, the function is kept here only to support existing test code and | ||
| * should not be used by new callers. | ||
| */ | ||
| int intel_adsp_ipc_send_message_sync(const struct device *dev, uint32_t data, uint32_t ext_data, | ||
| k_timeout_t timeout) | ||
| { | ||
| struct intel_adsp_ipc_msg msg = { | ||
| .data = data, | ||
| .ext_data = ext_data, | ||
| .timeout = timeout, | ||
| }; | ||
| int ret; | ||
| uint32_t msg[2] = {data, ext_data}; | ||
| struct intel_adsp_ipc_data *devdata = dev->data; | ||
|
|
||
| ret = ipc_service_send(&intel_adsp_ipc_ept, &msg, INTEL_ADSP_IPC_SEND_MSG_SYNC); | ||
| int ret = ipc_service_send(&intel_adsp_ipc_ept, &msg, sizeof(msg)); | ||
|
|
||
| if (ret < 0) { | ||
| return ret; | ||
| k_sem_take(&devdata->sem, timeout); | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. oh... so the only reason it can fail is because it's delayed? I see a few more ways how also previously the check was |
||
| } | ||
|
|
||
| return 0; | ||
| return ret; | ||
| } | ||
|
|
||
| void intel_adsp_ipc_send_message_emergency(const struct device *dev, uint32_t data, | ||
| uint32_t ext_data) | ||
| { | ||
| struct intel_adsp_ipc_msg msg = {.data = data, .ext_data = ext_data}; | ||
| int ret; | ||
| uint32_t msg[2] = {data, ext_data}; | ||
|
|
||
| ret = ipc_service_send(&intel_adsp_ipc_ept, &msg, INTEL_ADSP_IPC_SEND_MSG_EMERGENCY); | ||
| ret = ipc_service_send_critical(&intel_adsp_ipc_ept, &msg, sizeof(msg)); | ||
|
|
||
| ARG_UNUSED(ret); | ||
| } | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
looks like the type-cast shouldn't be needed