Skip to content

Commit

Permalink
drivers: dma: intel_adsp_hda: Fix L1 exit condition
Browse files Browse the repository at this point in the history
Transition to a low power DMI L1 state should be allowed only after all
pending DMA channels transfers have started.

Signed-off-by: Serhiy Katsyuba <[email protected]>
  • Loading branch information
serhiy-katsyuba-intel committed Mar 13, 2024
1 parent 1b76f82 commit d9ce924
Show file tree
Hide file tree
Showing 2 changed files with 40 additions and 7 deletions.
34 changes: 27 additions & 7 deletions drivers/dma/dma_intel_adsp_hda.c
Original file line number Diff line number Diff line change
Expand Up @@ -438,8 +438,9 @@ void intel_adsp_hda_dma_isr(void)
#if CONFIG_DMA_INTEL_ADSP_HDA_TIMING_L1_EXIT
struct dma_context *dma_ctx;
const struct intel_adsp_hda_dma_cfg *cfg;
bool clear_l1_exit = false;
bool triggered_interrupts = false;
int i, j;
int expected_interrupts = 0;
const struct device *host_dev[] = {
#if CONFIG_DMA_INTEL_ADSP_HDA_HOST_OUT
DT_FOREACH_STATUS_OKAY(intel_adsp_hda_host_out, DEVICE_DT_GET_AND_COMMA)
Expand All @@ -449,25 +450,44 @@ void intel_adsp_hda_dma_isr(void)
#endif
};

/*
* To initiate transfer, DSP must be in L0 state. Once the transfer is started, DSP can go
* to the low power L1 state, and the transfer will be able to continue and finish in L1
* state. Interrupts are configured to trigger after the first 32 bytes of data arrive.
* Once such an interrupt arrives, the transfer has already started. If all expected
* transfers have started, it is safe to allow the low power L1 state.
*/

for (i = 0; i < ARRAY_SIZE(host_dev); i++) {
dma_ctx = (struct dma_context *)host_dev[i]->data;
cfg = host_dev[i]->config;

for (j = 0; j < dma_ctx->dma_channels; j++) {
if (atomic_test_bit(dma_ctx->atomic, j)) {
clear_l1_exit |=
intel_adsp_hda_check_buffer_interrupt(cfg->base,
cfg->regblock_size,
j);
if (!atomic_test_bit(dma_ctx->atomic, j))
continue;

if (!intel_adsp_hda_is_buffer_interrupt_enabled(cfg->base,
cfg->regblock_size, j))
continue;

if (intel_adsp_hda_check_buffer_interrupt(cfg->base,
cfg->regblock_size, j)) {
triggered_interrupts = true;
intel_adsp_hda_disable_buffer_interrupt(cfg->base,
cfg->regblock_size, j);
intel_adsp_hda_clear_buffer_interrupt(cfg->base,
cfg->regblock_size, j);
} else {
expected_interrupts++;
}
}
}

if (clear_l1_exit) {
/*
* Allow entering low power L1 state only after all enabled interrupts arrived, i.e.,
* transfers started on all channels.
*/
if (triggered_interrupts && expected_interrupts == 0) {
intel_adsp_allow_dmi_l1_state();
}
#endif
Expand Down
13 changes: 13 additions & 0 deletions soc/intel/intel_adsp/common/include/intel_adsp_hda.h
Original file line number Diff line number Diff line change
Expand Up @@ -444,6 +444,19 @@ static inline void intel_adsp_hda_disable_buffer_interrupt(uint32_t base, uint32
*DGCS(base, regblock_size, sid) &= ~DGCS_BSCIE;
}

/**
* @brief Check if BSC interrupt enabled
*
* @param base Base address of the IP register block
* @param regblock_size Register block size
* @param sid Stream ID
*/
static inline bool intel_adsp_hda_is_buffer_interrupt_enabled(uint32_t base,
uint32_t regblock_size, uint32_t sid)
{
return (*DGCS(base, regblock_size, sid) & DGCS_BSCIE) == DGCS_BSCIE;
}

static inline void intel_adsp_force_dmi_l0_state(void)
{
#ifdef CONFIG_SOC_SERIES_INTEL_ADSP_ACE
Expand Down

0 comments on commit d9ce924

Please sign in to comment.