amic: implement sound out DMA IRQ.

This commit is contained in:
Maxim Poliakovski 2023-11-03 10:45:05 +01:00
parent 4d5062c601
commit 7708f987d5
2 changed files with 71 additions and 18 deletions

View File

@ -74,6 +74,7 @@ AMIC::AMIC() : MMIODevice()
// initialize sound HW
this->snd_out_dma = std::unique_ptr<AmicSndOutDma> (new AmicSndOutDma());
this->snd_out_dma->init_interrupts(this, 2 << 8);
this->awacs = std::unique_ptr<AwacDevicePdm> (new AwacDevicePdm());
this->awacs->set_dma_out(this->snd_out_dma.get());
@ -176,6 +177,10 @@ uint32_t AMIC::read(uint32_t rgn_start, uint32_t offset, int size)
return this->mon_id;
case AMICReg::Int_Ctrl:
return (this->int_ctrl & 0xC0) | (this->dev_irq_lines & 0x3F);
case AMICReg::DMA_IFR_0:
return this->dma_ifr0;
case AMICReg::DMA_IFR_1:
return this->dma_ifr1;
case AMICReg::Diag_Reg:
return 0xFE | this->emmo_pin;
case AMICReg::DMA_Base_Addr_0:
@ -245,14 +250,14 @@ void AMIC::write(uint32_t rgn_start, uint32_t offset, uint32_t value, int size)
LOG_F(9, "AMIC Sound Out Ctrl updated, val=%x", value);
if ((value & 1) != (this->snd_out_ctrl & 1)) {
if (value & 1) {
LOG_F(9, "AMIC Sound Out DMA enabled!");
LOG_F(INFO, "AMIC Sound Out DMA enabled!");
this->snd_out_dma->init(this->dma_base & ~0x3FFFF,
this->snd_buf_size);
this->snd_out_dma->enable();
this->awacs->set_sample_rate((this->snd_out_ctrl >> 1) & 3);
this->awacs->dma_out_start();
} else {
LOG_F(9, "AMIC Sound Out DMA disabled!");
LOG_F(INFO, "AMIC Sound Out DMA disabled!");
this->snd_out_dma->disable();
}
}
@ -505,7 +510,24 @@ void AMIC::ack_cpu_int(uint32_t irq_id, uint8_t irq_line_state) {
}
void AMIC::ack_dma_int(uint32_t irq_id, uint8_t irq_line_state) {
ABORT_F("AMIC: ack_dma_int() not implemented");
if (irq_id >= 0x100) { // DMA Interrupt Flags 1
irq_id = (irq_id >> 8) & 0xFFU;
if (irq_line_state)
this->dma_ifr1 |= irq_id;
else
this->dma_ifr1 &= ~irq_id;
} else { // DMA Interrupt Flags 0
irq_id &= 0xFFU;
if (irq_line_state)
this->dma_ifr0 |= irq_id;
else
this->dma_ifr0 &= ~irq_id;
}
uint8_t new_irq = (this->dma_ifr0 || this->dma_ifr1) ? 1 : 0;
if (new_irq != this->dma_irq) {
this->dma_irq = new_irq;
this->ack_cpu_int(CPU_INT_ALL_DMA, new_irq);
}
}
// ============================ Sound DMA stuff ================================
@ -524,6 +546,7 @@ void AmicSndOutDma::init(uint32_t buf_base, uint32_t buf_samples)
this->snd_buf_num = 0;
this->cur_buf_pos = 0;
this->irq_level = 0;
}
uint8_t AmicSndOutDma::read_stat()
@ -531,12 +554,25 @@ uint8_t AmicSndOutDma::read_stat()
return this->dma_out_ctrl;
}
void AmicSndOutDma::update_irq() {
uint8_t new_level = !!((this->dma_out_ctrl >> 4) & this->dma_out_ctrl);
if (new_level != this->irq_level) {
this->irq_level = new_level;
TimerManager::get_instance()->add_immediate_timer([this] {
this->int_ctrl->ack_dma_int(this->irq_id, this->irq_level);
});
}
}
void AmicSndOutDma::write_dma_out_ctrl(uint8_t value)
{
// clear interrupt flags
value &= ~PDM_DMA_INTS_MASK;
this->dma_out_ctrl = value;
LOG_F(9, "AMIC: Sound out DMA control set to 0x%X", value);
if (value & PDM_DMA_INTS_MASK) {
this->dma_out_ctrl &= ~(value & PDM_DMA_INTS_MASK);
this->update_irq();
}
// enable sound output DMA interrupts
this->dma_out_ctrl |= value & 0x0FU;
}
DmaPullResult AmicSndOutDma::pull_data(uint32_t req_len, uint32_t *avail_len,
@ -549,13 +585,15 @@ DmaPullResult AmicSndOutDma::pull_data(uint32_t req_len, uint32_t *avail_len,
if (!this->snd_buf_num) {
// signal buffer 0 drained
this->dma_out_ctrl |= PDM_DMA_IF0;
// TODO: generate IE0 interrupt if enabled
} else {
// signal buffer 1 drained
this->dma_out_ctrl |= PDM_DMA_IF1;
// TODO: generate IE1 interrupt if enabled
}
// generate sound out DMA interrupt if (IF0 & IE0)
// or (IF1 & IE1) or (ERR_IF & ERR_IE)
this->update_irq();
// check DMA enable flag after buffer 1 was processed
// if it's false stop delivering sound data
// this will effectively stop audio playback

View File

@ -96,20 +96,30 @@ public:
void enable() { this->enabled = true; };
void disable() { this->enabled = false; };
uint8_t read_stat();
void update_irq();
void write_dma_out_ctrl(uint8_t value);
uint32_t get_cur_buf_pos() { return this->cur_buf_pos; };
DmaPullResult pull_data(uint32_t req_len, uint32_t *avail_len,
uint8_t **p_data);
private:
bool enabled;
uint8_t dma_out_ctrl;
void init_interrupts(InterruptCtrl *int_ctrl, uint32_t irq_id) {
this->int_ctrl = int_ctrl;
this->irq_id = irq_id;
};
uint32_t out_buf0;
uint32_t out_buf1;
uint32_t out_buf_len;
uint32_t snd_buf_num;
uint32_t cur_buf_pos;
private:
bool enabled = false;
uint8_t dma_out_ctrl;
uint32_t out_buf0;
uint32_t out_buf1;
uint32_t out_buf_len;
uint32_t snd_buf_num;
uint32_t cur_buf_pos;
InterruptCtrl *int_ctrl = nullptr;
uint32_t irq_id = 0;
uint8_t irq_level = 0;
};
/** AMIC-specific floppy DMA implementation. */
@ -203,9 +213,9 @@ enum AMICReg : uint32_t {
// Interrupt registers
Int_Ctrl = 0x2A000,
DMA_Int_0 = 0x2A008,
DMA_IFR_0 = 0x2A008,
Bus_Err_Int_0 = 0x2A009,
DMA_Int_1 = 0x2A00A,
DMA_IFR_1 = 0x2A00A,
Bus_Err_Int_1 = 0x2A00B,
// Undocumented diagnostics register
@ -293,6 +303,11 @@ private:
uint8_t int_ctrl = 0;
uint8_t dev_irq_lines = 0; // state of the IRQ lines
// DMA IRQ flag registers
uint8_t dma_ifr0 = 0;
uint8_t dma_ifr1 = 0;
uint8_t dma_irq = 0;
// pseudo VIA2 state
uint8_t via2_ier = 0;
uint8_t via2_ifr = 0;