From 37216919c0abc297222ccf617eda7b32de1c10b1 Mon Sep 17 00:00:00 2001 From: Tomasz Lauda Date: Tue, 12 Mar 2019 13:29:23 +0100 Subject: [PATCH 1/2] pipeline: add check for pipeline scheduling mode Adds simple method to check if pipeline is scheduled with timer or not. Signed-off-by: Tomasz Lauda --- src/include/sof/audio/pipeline.h | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/include/sof/audio/pipeline.h b/src/include/sof/audio/pipeline.h index 5c8514a28953..f5916441799e 100644 --- a/src/include/sof/audio/pipeline.h +++ b/src/include/sof/audio/pipeline.h @@ -123,6 +123,12 @@ static inline bool pipeline_is_preload(struct pipeline *p) return p->preload; } +/* checks if pipeline is scheduled with timer */ +static inline bool pipeline_is_timer_driven(struct pipeline *p) +{ + return p->ipc_pipe.timer_delay; +} + /* pipeline creation and destruction */ struct pipeline *pipeline_new(struct sof_ipc_pipe_new *pipe_desc, struct comp_dev *cd); From 1f6e469cb0909ec8a0971b0290c7ed0a3696f573 Mon Sep 17 00:00:00 2001 From: Tomasz Lauda Date: Tue, 12 Mar 2019 13:42:28 +0100 Subject: [PATCH 2/2] dw-dma: enable interrupts based on the scheduling mode Adds new functionality to DW-DMA driver to enable/disable interrupts based on the pipeline scheduling mode. If we are scheduling on timer there is no need to touch any irq related registers. Signed-off-by: Tomasz Lauda --- src/audio/dai.c | 2 + src/audio/host.c | 1 + src/drivers/dw/dma.c | 122 +++++++++++++++++++++++++++++++++--------- src/include/sof/dma.h | 1 + src/ipc/ipc.c | 1 + 5 files changed, 103 insertions(+), 24 deletions(-) diff --git a/src/audio/dai.c b/src/audio/dai.c index 1733d81412d7..abb30efe9e76 100644 --- a/src/audio/dai.c +++ b/src/audio/dai.c @@ -293,6 +293,7 @@ static int dai_playback_params(struct comp_dev *dev) config->src_width = comp_sample_bytes(dev); config->dest_width = comp_sample_bytes(dev); config->cyclic = 1; + config->irq_disabled = pipeline_is_timer_driven(dev->pipeline); config->dest_dev = dd->dai->plat_data.fifo[0].handshake; /* set up local and host DMA elems to reset values */ @@ -343,6 +344,7 @@ static int dai_capture_params(struct comp_dev *dev) /* set up DMA configuration */ config->direction = DMA_DIR_DEV_TO_MEM; config->cyclic = 1; + config->irq_disabled = pipeline_is_timer_driven(dev->pipeline); config->src_dev = dd->dai->plat_data.fifo[1].handshake; /* TODO: Make this code platform-specific or move it driver callback */ diff --git a/src/audio/host.c b/src/audio/host.c index 3b922fad8a1a..585fac1cc649 100644 --- a/src/audio/host.c +++ b/src/audio/host.c @@ -572,6 +572,7 @@ static int host_params(struct comp_dev *dev) config->src_width = comp_sample_bytes(dev); config->dest_width = comp_sample_bytes(dev); config->cyclic = 0; + config->irq_disabled = pipeline_is_timer_driven(dev->pipeline); #if CONFIG_DMA_GW dev->params.stream_tag -= 1; diff --git a/src/drivers/dw/dma.c b/src/drivers/dw/dma.c index f397832bc00b..edc11f8df2df 100644 --- a/src/drivers/dw/dma.c +++ b/src/drivers/dw/dma.c @@ -294,6 +294,7 @@ struct dma_chan_data { uint32_t cfg_lo; uint32_t cfg_hi; struct dma_id id; + bool irq_disabled; /* pointer data */ struct dma_ptr_data ptr_data; @@ -336,6 +337,64 @@ static inline void dw_update_bits(struct dma *dma, uint32_t reg, uint32_t mask, io_reg_update_bits(dma_base(dma) + reg, mask, value); } +static void dw_dma_interrupt_mask(struct dma *dma, int channel) +{ + const struct dma_pdata *p = dma_get_drvdata(dma); + + if (p->chan[channel].irq_disabled) { + tracev_event(TRACE_CLASS_DMA, "dw_dma_interrupt_mask(): " + "dma %d channel %d not working in irq mode", + dma->plat_data.id, channel); + return; + } + + /* mask block, transfer and error interrupts for channel */ + dw_write(dma, DW_MASK_TFR, INT_MASK(channel)); + dw_write(dma, DW_MASK_BLOCK, INT_MASK(channel)); + dw_write(dma, DW_MASK_ERR, INT_MASK(channel)); +} + +static void dw_dma_interrupt_unmask(struct dma *dma, int channel) +{ + const struct dma_pdata *p = dma_get_drvdata(dma); + + if (p->chan[channel].irq_disabled) { + tracev_event(TRACE_CLASS_DMA, "dw_dma_interrupt_mask(): " + "dma %d channel %d not working in irq mode", + dma->plat_data.id, channel); + return; + } + + /* unmask block, transfer and error interrupts for channel */ + dw_write(dma, DW_MASK_TFR, INT_UNMASK(channel)); + dw_write(dma, DW_MASK_BLOCK, INT_UNMASK(channel)); + dw_write(dma, DW_MASK_ERR, INT_UNMASK(channel)); +} + +static void dw_dma_interrupt_clear(struct dma *dma, int channel) +{ + const struct dma_pdata *p = dma_get_drvdata(dma); + + if (p->chan[channel].irq_disabled) { + tracev_event(TRACE_CLASS_DMA, "dw_dma_interrupt_mask(): " + "dma %d channel %d not working in irq mode", + dma->plat_data.id, channel); + return; + } + + /* write interrupt clear registers for the channel: + * ClearTfr, ClearBlock, ClearSrcTran, ClearDstTran, ClearErr + */ + dw_write(dma, DW_CLEAR_TFR, 0x1 << channel); + dw_write(dma, DW_CLEAR_BLOCK, 0x1 << channel); + dw_write(dma, DW_CLEAR_SRC_TRAN, 0x1 << channel); + dw_write(dma, DW_CLEAR_DST_TRAN, 0x1 << channel); + dw_write(dma, DW_CLEAR_ERR, 0x1 << channel); + + /* clear platform interrupt */ + platform_interrupt_clear(dma_irq(dma, cpu_get_id()), 1 << channel); +} + /* allocate next free DMA channel */ static int dw_dma_channel_get(struct dma *dma, int req_chan) { @@ -381,10 +440,7 @@ static void dw_dma_channel_put_unlocked(struct dma *dma, int channel) return; } - /* mask block, transfer and error interrupts for channel */ - dw_write(dma, DW_MASK_TFR, INT_MASK(channel)); - dw_write(dma, DW_MASK_BLOCK, INT_MASK(channel)); - dw_write(dma, DW_MASK_ERR, INT_MASK(channel)); + dw_dma_interrupt_mask(dma, channel); /* free the lli allocated by set_config*/ if (chan->lli) { @@ -455,18 +511,7 @@ static int dw_dma_start(struct dma *dma, int channel) goto out; } - /* write interrupt clear registers for the channel: - * ClearTfr, ClearBlock, ClearSrcTran, ClearDstTran, ClearErr - */ - dw_write(dma, DW_CLEAR_TFR, 0x1 << channel); - dw_write(dma, DW_CLEAR_BLOCK, 0x1 << channel); - dw_write(dma, DW_CLEAR_SRC_TRAN, 0x1 << channel); - dw_write(dma, DW_CLEAR_DST_TRAN, 0x1 << channel); - dw_write(dma, DW_CLEAR_ERR, 0x1 << channel); - - /* clear platform interrupt */ - platform_interrupt_clear(dma_irq(dma, cpu_get_id()), - 1 << channel); + dw_dma_interrupt_clear(dma, channel); #if DW_USE_HW_LLI /* TODO: Revisit: are we using LLP mode or single transfer ? */ @@ -599,7 +644,8 @@ static int dw_dma_stop(struct dma *dma, int channel) trace_dwdma_error("dw-dma: %d channel %d timeout", dma->plat_data.id, channel); - dw_write(dma, DW_CLEAR_BLOCK, 0x1 << channel); + if (!chan->irq_disabled) + dw_write(dma, DW_CLEAR_BLOCK, 0x1 << channel); chan->status = COMP_STATE_PREPARE; @@ -641,7 +687,8 @@ static int dw_dma_stop(struct dma *dma, int channel) sizeof(struct dw_lli2) * chan->desc_count); #endif - dw_write(dma, DW_CLEAR_BLOCK, 0x1 << channel); + if (!chan->irq_disabled) + dw_write(dma, DW_CLEAR_BLOCK, 0x1 << channel); /* disable interrupt */ dw_dma_interrupt_unregister(dma, channel); @@ -739,6 +786,7 @@ static int dw_dma_set_config(struct dma *dma, int channel, /* default channel config */ chan->direction = config->direction; + chan->irq_disabled = config->irq_disabled; chan->cfg_lo = DW_CFG_LOW_DEF; chan->cfg_hi = DW_CFG_HIGH_DEF; @@ -786,12 +834,7 @@ static int dw_dma_set_config(struct dma *dma, int channel, } } - /* unmask block, transfer and error interrupts - * for channel - */ - dw_write(dma, DW_MASK_TFR, INT_UNMASK(channel)); - dw_write(dma, DW_MASK_BLOCK, INT_UNMASK(channel)); - dw_write(dma, DW_MASK_ERR, INT_UNMASK(channel)); + dw_dma_interrupt_unmask(dma, channel); chan->ptr_data.buffer_bytes = 0; @@ -1328,6 +1371,13 @@ static inline int dw_dma_interrupt_register(struct dma *dma, int channel) trace_event(TRACE_CLASS_DMA, "dw_dma_interrupt_register()"); + if (p->chan[channel].irq_disabled) { + tracev_event(TRACE_CLASS_DMA, "dw_dma_interrupt_register(): " + "dma %d channel %d not working in irq mode", + dma->plat_data.id, channel); + return 0; + } + ret = interrupt_register(irq, IRQ_AUTO_UNMASK, dw_dma_irq_handler, &p->chan[channel].id); if (ret < 0) { @@ -1341,9 +1391,17 @@ static inline int dw_dma_interrupt_register(struct dma *dma, int channel) static inline void dw_dma_interrupt_unregister(struct dma *dma, int channel) { + struct dma_pdata *p = dma_get_drvdata(dma); uint32_t irq = dma_irq(dma, cpu_get_id()) + (channel << SOF_IRQ_BIT_SHIFT); + if (p->chan[channel].irq_disabled) { + tracev_event(TRACE_CLASS_DMA, "dw_dma_interrupt_unregister(): " + "dma %d channel %d not working in irq mode", + dma->plat_data.id, channel); + return; + } + interrupt_disable(irq); interrupt_unregister(irq); } @@ -1427,9 +1485,17 @@ static void dw_dma_irq_handler(void *data) static inline int dw_dma_interrupt_register(struct dma *dma, int channel) { + struct dma_pdata *p = dma_get_drvdata(dma); uint32_t irq = dma_irq(dma, cpu_get_id()); int ret; + if (p->chan[channel].irq_disabled) { + tracev_event(TRACE_CLASS_DMA, "dw_dma_interrupt_register(): " + "dma %d channel %d not working in irq mode", + dma->plat_data.id, channel); + return 0; + } + if (!dma->mask_irq_channels) { ret = interrupt_register(irq, IRQ_AUTO_UNMASK, dw_dma_irq_handler, dma); @@ -1448,8 +1514,16 @@ static inline int dw_dma_interrupt_register(struct dma *dma, int channel) static inline void dw_dma_interrupt_unregister(struct dma *dma, int channel) { + struct dma_pdata *p = dma_get_drvdata(dma); uint32_t irq = dma_irq(dma, cpu_get_id()); + if (p->chan[channel].irq_disabled) { + tracev_event(TRACE_CLASS_DMA, "dw_dma_interrupt_unregister(): " + "dma %d channel %d not working in irq mode", + dma->plat_data.id, channel); + return; + } + dma->mask_irq_channels = dma->mask_irq_channels & ~BIT(channel); if (!dma->mask_irq_channels) { diff --git a/src/include/sof/dma.h b/src/include/sof/dma.h index c0f17d8a6d8a..ac293d534e3e 100644 --- a/src/include/sof/dma.h +++ b/src/include/sof/dma.h @@ -127,6 +127,7 @@ struct dma_sg_config { uint32_t cyclic; /* circular buffer */ struct dma_sg_elem_array elem_array; /* array of dma_sg elems */ bool scatter; + bool irq_disabled; }; struct dma_chan_status { diff --git a/src/ipc/ipc.c b/src/ipc/ipc.c index 5291bcd26956..6609d83fc6d1 100644 --- a/src/ipc/ipc.c +++ b/src/ipc/ipc.c @@ -541,6 +541,7 @@ int ipc_get_page_descriptors(struct dma *dmac, uint8_t *page_table, config.src_width = sizeof(uint32_t); config.dest_width = sizeof(uint32_t); config.cyclic = 0; + config.irq_disabled = false; dma_sg_init(&config.elem_array); /* set up DMA descriptor */