[dev][intel-hda][dsp] introduce ipc workqueue
Change-Id: Ia1aeefd92fae121341a0974b005b0167d91da154
diff --git a/system/dev/audio/intel-hda/dsp/intel-audio-dsp.cpp b/system/dev/audio/intel-hda/dsp/intel-audio-dsp.cpp
index 0c26213..3add636 100644
--- a/system/dev/audio/intel-hda/dsp/intel-audio-dsp.cpp
+++ b/system/dev/audio/intel-hda/dsp/intel-audio-dsp.cpp
@@ -281,6 +281,9 @@
ResetCore(ADSP_REG_ADSPCS_CORE0_MASK);
PowerDownCore(ADSP_REG_ADSPCS_CORE0_MASK);
+ // Fail all pending IPCs
+ ipc_.Shutdown();
+
state_ = State::SHUT_DOWN;
}
diff --git a/system/dev/audio/intel-hda/dsp/intel-dsp-ipc.cpp b/system/dev/audio/intel-hda/dsp/intel-dsp-ipc.cpp
index d05b7c4..e4cb370 100644
--- a/system/dev/audio/intel-hda/dsp/intel-dsp-ipc.cpp
+++ b/system/dev/audio/intel-hda/dsp/intel-dsp-ipc.cpp
@@ -15,6 +15,11 @@
namespace audio {
namespace intel_hda {
+namespace {
+constexpr zx_signals_t IPC_RECEIVED = ZX_EVENT_SIGNALED;
+constexpr zx_signals_t IPC_SHUTDOWN = ZX_USER_SIGNAL_0;
+} // anon namespace
+
IntelDspIpc::IntelDspIpc(IntelAudioDsp& dsp) : dsp_(dsp) {
snprintf(log_prefix_, sizeof(log_prefix_), "IHDA DSP IPC (unknown BDF)");
}
@@ -23,32 +28,34 @@
snprintf(log_prefix_, sizeof(log_prefix_), "%s IPC", new_prefix);
}
-zx_status_t IntelDspIpc::SendIpc(Txn* txn) {
- {
- fbl::AutoLock ipc_lock(&ipc_lock_);
- // 1 at a time
- ZX_DEBUG_ASSERT(pending_txn_ == nullptr);
- if (pending_txn_ != nullptr) {
- return ZX_ERR_BAD_STATE;
- }
- pending_txn_ = txn;
+void IntelDspIpc::Shutdown() {
+ fbl::AutoLock ipc_lock(&ipc_lock_);
+ // Fail all pending IPCs
+ for (auto iter = ipc_queue_.begin(); iter != ipc_queue_.end(); iter++) {
+ completion_signal(&iter->completion);
}
+}
+void IntelDspIpc::SendIpc(const Txn& txn) {
// Copy tx data to outbox
- if (txn->tx_size > 0) {
- dsp_.IpcMailboxWrite(txn->tx_data, txn->tx_size);
+ if (txn.tx_size > 0) {
+ dsp_.IpcMailboxWrite(txn.tx_data, txn.tx_size);
}
- dsp_.SendIpcMessage(txn->request);
- return ZX_OK;
+ dsp_.SendIpcMessage(txn.request);
}
zx_status_t IntelDspIpc::SendIpcWait(Txn* txn) {
- zx_status_t res = SendIpc(txn);
- if (res != ZX_OK) {
- return res;
+ {
+ // Add to the pending queue and start the ipc if necessary
+ fbl::AutoLock ipc_lock(&ipc_lock_);
+ bool needs_start = ipc_queue_.is_empty();
+ ipc_queue_.push_back(txn);
+ if (needs_start) {
+ SendIpc(ipc_queue_.front());
+ }
}
// Wait for completion
- res = completion_wait(&txn->completion, ZX_MSEC(300));
+ zx_status_t res = completion_wait(&txn->completion, ZX_MSEC(300));
if (res != ZX_OK) {
dsp_.DeviceShutdown();
}
@@ -212,37 +219,45 @@
void IntelDspIpc::ProcessIpcReply(const IpcMessage& reply) {
fbl::AutoLock ipc_lock(&ipc_lock_);
- if (pending_txn_ == nullptr) {
+ if (ipc_queue_.is_empty()) {
LOG(INFO, "got spurious reply message\n");
return;
}
+ Txn& pending = ipc_queue_.front();
// Check if the reply matches the pending request.
- IpcMessage* pending = &pending_txn_->request;
- if ((pending->msg_tgt() != reply.msg_tgt()) || (pending->type() != reply.type())) {
+ IpcMessage* req = &pending.request;
+ if ((req->msg_tgt() != reply.msg_tgt()) || (req->type() != reply.type())) {
LOG(INFO, "reply msg mismatch, got pri 0x%08x ext 0x%08x, expect pri 0x%08x ext 0x%08x\n",
- reply.primary, reply.extension, pending->primary, pending->extension);
+ reply.primary, reply.extension, req->primary, req->extension);
return;
}
+ // The pending txn is done
+ ipc_queue_.pop_front();
+ pending.reply = reply;
+ pending.done = true;
+
LOG(INFO, "got reply (status %u) for pending msg, pri 0x%08x ext 0x%08x\n",
to_underlying(reply.status()), reply.primary, reply.extension);
- pending_txn_->reply = reply;
-
if (reply.msg_tgt() == MsgTarget::MODULE_MSG) {
ModuleMsgType type = static_cast<ModuleMsgType>(reply.type());
switch (type) {
case ModuleMsgType::LARGE_CONFIG_GET:
- ProcessLargeConfigGetReply(pending_txn_);
+ ProcessLargeConfigGetReply(&pending);
break;
default:
break;
}
}
- completion_signal(&pending_txn_->completion);
- pending_txn_ = nullptr;
+ completion_signal(&pending.completion);
+
+ // Send the next ipc in the queue
+ if (!ipc_queue_.is_empty()) {
+ SendIpc(ipc_queue_.front());
+ }
}
void IntelDspIpc::ProcessLargeConfigGetReply(Txn* txn) {
diff --git a/system/dev/audio/intel-hda/dsp/intel-dsp-ipc.h b/system/dev/audio/intel-hda/dsp/intel-dsp-ipc.h
index bd122fa..1b23f1d 100644
--- a/system/dev/audio/intel-hda/dsp/intel-dsp-ipc.h
+++ b/system/dev/audio/intel-hda/dsp/intel-dsp-ipc.h
@@ -4,6 +4,7 @@
#pragma once
+#include <fbl/intrusive_double_list.h>
#include <sync/completion.h>
#include <zircon/thread_annotations.h>
@@ -22,19 +23,24 @@
const char* log_prefix() const { return log_prefix_; }
- struct Txn {
+ class Txn : public fbl::DoublyLinkedListable<Txn*> {
+ public:
Txn(const void* tx, size_t txs, void* rx, size_t rxs)
: tx_data(tx), tx_size(txs), rx_data(rx), rx_size(rxs) { }
Txn(uint32_t pri, uint32_t ext, const void* tx, size_t txs, void* rx, size_t rxs)
: request(pri, ext), tx_data(tx), tx_size(txs), rx_data(rx), rx_size(rxs) { }
+ DISALLOW_NEW;
+
bool success() {
- return reply.status() == MsgStatus::IPC_SUCCESS;
+ return done && reply.status() == MsgStatus::IPC_SUCCESS;
}
IpcMessage request;
IpcMessage reply;
+ bool done = false;
+
const void* tx_data = nullptr;
size_t tx_size = 0;
void* rx_data = nullptr;
@@ -46,6 +52,8 @@
void SetLogPrefix(const char* new_prefix);
+ void Shutdown();
+
// Library & Module Management IPC
zx_status_t InitInstance(uint16_t module_id, uint8_t instance_id, ProcDomain proc_domain,
uint8_t core_id, uint8_t ppl_instance_id, uint16_t param_block_size,
@@ -67,12 +75,11 @@
void ProcessLargeConfigGetReply(Txn* txn);
private:
- // Send an IPC message
- zx_status_t SendIpc(Txn* txn);
-
// Send an IPC message and wait for response
zx_status_t SendIpcWait(Txn* txn);
+ void SendIpc(const Txn& txn);
+
zx_status_t dsp_to_zx_status(MsgStatus status) {
return (status == MsgStatus::IPC_SUCCESS) ? ZX_OK : ZX_ERR_INTERNAL;
}
@@ -82,7 +89,11 @@
// Pending IPC
fbl::Mutex ipc_lock_;
- Txn* pending_txn_ TA_GUARDED(ipc_lock_) = nullptr;
+ fbl::DoublyLinkedList<Txn*> ipc_queue_ TA_GUARDED(ipc_lock_);
+
+ // Work queue
+ thrd_t work_thread_;
+ zx::event work_event_;
// A reference to the owning DSP
IntelAudioDsp& dsp_;