dbdma: Add data callbacks.

Allow the dbdma program to initiate reading/writing by adding in and out callbacks.
Support the DBDMA flush command by adding a flush callback. If the transfer completes, then clear the flush flag, otherwise leave it unchanged.
Clear the flush flag after it is copied to the xferStatus of the DBDMA command.
This commit is contained in:
joevt 2024-03-20 08:30:56 -07:00 committed by dingusdev
parent 30afcb6ddc
commit 11d61359c1
3 changed files with 72 additions and 3 deletions

View File

@ -39,6 +39,12 @@ void DMAChannel::set_callbacks(DbdmaCallback start_cb, DbdmaCallback stop_cb) {
this->stop_cb = stop_cb;
}
void DMAChannel::set_data_callbacks(DbdmaCallback in_cb, DbdmaCallback out_cb, DbdmaCallback flush_cb) {
this->in_cb = in_cb;
this->out_cb = out_cb;
this->flush_cb = flush_cb;
}
/* Load DMACmd from physical memory. */
DMACmd* DMAChannel::fetch_cmd(uint32_t cmd_addr, DMACmd* p_cmd, bool *is_writable) {
MapDmaResult res = mmu_map_dma_mem(cmd_addr, 16, false);
@ -88,6 +94,18 @@ uint8_t DMAChannel::interpret_cmd() {
this->queue_data = res.host_va;
this->res_count = 0;
this->cmd_in_progress = true;
switch (this->cur_cmd) {
case DBDMA_Cmd::OUTPUT_MORE:
case DBDMA_Cmd::OUTPUT_LAST:
if (this->out_cb)
this->out_cb();
break;
case DBDMA_Cmd::INPUT_MORE:
case DBDMA_Cmd::INPUT_LAST:
if (this->in_cb)
this->in_cb();
break;
}
} else
this->finish_cmd();
break;
@ -153,6 +171,7 @@ void DMAChannel::finish_cmd() {
if (res.is_writable)
WRITE_WORD_LE_A(&cmd_desc[14], this->ch_stat | CH_STAT_ACTIVE);
this->ch_stat &= ~CH_STAT_FLUSH;
// react to cmd.b (branch) bits
if (cmd_desc[2] & 0xC) {
@ -320,9 +339,13 @@ void DMAChannel::reg_write(uint32_t offset, uint32_t value, int size) {
// That means we need to update memory before channel operation
// is aborted to prevent data loss.
if (new_stat & CH_STAT_FLUSH) {
// NOTE: because this implementation doesn't currently support
// partial memory updates no special action is taken here
new_stat &= ~CH_STAT_FLUSH;
if (this->cur_cmd <= DBDMA_Cmd::INPUT_LAST && this->flush_cb && (new_stat & CH_STAT_ACTIVE) && !(this->ch_stat & CH_STAT_DEAD)) {
this->flush_cb();
} else {
// NOTE: because this implementation doesn't currently support
// partial memory updates no special action is taken here
new_stat &= ~CH_STAT_FLUSH;
}
this->ch_stat = new_stat;
}
@ -446,6 +469,40 @@ int DMAChannel::push_data(const char* src_ptr, int len) {
return 0;
}
void DMAChannel::end_pull_data() {
if (this->ch_stat & CH_STAT_DEAD || !(this->ch_stat & CH_STAT_ACTIVE)) {
// dead or idle channel? -> no more data
LOG_F(WARNING, "%s: Ending Dead/idle channel -> no more data", this->get_name().c_str());
return;
}
if (this->queue_len) {
this->queue_len = 0;
} else {
this->ch_stat &= ~CH_STAT_FLUSH;
}
// proceed with the DBDMA program
this->interpret_cmd();
}
void DMAChannel::end_push_data() {
if (this->ch_stat & CH_STAT_DEAD || !(this->ch_stat & CH_STAT_ACTIVE)) {
LOG_F(WARNING, "%s: Attempt to end push data to dead/idle channel",
this->get_name().c_str());
return;
}
if (this->queue_len) {
this->queue_len = 0;
} else {
this->ch_stat &= ~CH_STAT_FLUSH;
}
// proceed with the DBDMA program
this->interpret_cmd();
}
bool DMAChannel::is_out_active() {
if (this->ch_stat & CH_STAT_DEAD || !(this->ch_stat & CH_STAT_ACTIVE)) {
return false;

View File

@ -109,6 +109,7 @@ public:
~DMAChannel() = default;
void set_callbacks(DbdmaCallback start_cb, DbdmaCallback stop_cb);
void set_data_callbacks(DbdmaCallback in_cb, DbdmaCallback out_cb, DbdmaCallback flush_cb);
uint32_t reg_read(uint32_t offset, int size);
void reg_write(uint32_t offset, uint32_t value, int size);
void set_stat(uint8_t new_stat) { this->ch_stat = (this->ch_stat & 0xff00) | new_stat; }
@ -117,6 +118,10 @@ public:
bool is_in_active();
DmaPullResult pull_data(uint32_t req_len, uint32_t *avail_len, uint8_t **p_data);
int push_data(const char* src_ptr, int len);
int get_pull_data_remaining() { return this->queue_len; }
int get_push_data_remaining() { return this->queue_len; }
void end_pull_data();
void end_push_data();
void register_dma_int(InterruptCtrl* int_ctrl_obj, uint32_t irq_id) {
this->int_ctrl = int_ctrl_obj;
@ -138,6 +143,9 @@ protected:
private:
std::function<void(void)> start_cb = nullptr; // DMA channel start callback
std::function<void(void)> stop_cb = nullptr; // DMA channel stop callback
std::function<void(void)> in_cb = nullptr; // DMA channel in callback
std::function<void(void)> out_cb = nullptr; // DMA channel out callback
std::function<void(void)> flush_cb = nullptr; // DMA channel flush callback
uint16_t ch_stat = 0;
uint32_t cmd_ptr = 0;

View File

@ -39,6 +39,8 @@ public:
virtual bool is_out_active() { return true; };
virtual DmaPullResult pull_data(uint32_t req_len, uint32_t *avail_len,
uint8_t **p_data) = 0;
virtual int get_pull_data_remaining() { return 1; };
virtual void end_pull_data() { };
std::string get_name(void) { return this->name; };
@ -52,6 +54,8 @@ public:
virtual bool is_in_active() { return true; };
virtual int push_data(const char* src_ptr, int len) = 0;
virtual int get_push_data_remaining() { return 1; };
virtual void end_push_data() { };
std::string get_name(void) { return this->name; };