Skip to content

schedule: implement DMA scheduling domain - #1850

Merged
tlauda merged 27 commits into
thesofproject:masterfrom
tlauda:topic/dma-irq-interface
Oct 1, 2019
Merged

tlauda merged 27 commits into
thesofproject:masterfrom
tlauda:topic/dma-irq-interface

Conversation

@tlauda

@tlauda tlauda commented Sep 20, 2019

Copy link
Copy Markdown
Contributor

Implements DMA scheduling domain. It allows to initialize
another low latency scheduler and schedule on any DMA channel.
The functionality stays the same as the one already done in
DMA drivers e.g. DW-DMA.

Signed-off-by: Tomasz Lauda tomasz.lauda@linux.intel.com

@paulstelian97

paulstelian97 commented Sep 20, 2019 •

Copy link
Copy Markdown
Collaborator

I need to understand these changes more. So a couple questions:

  1. Where would the DMA drivers register an IRQ for the channel?
  2. Given the removal of DMA_CB_STATUS_{IGNORE,SPLIT}, what was their meaning and why is it no longer relevant?
  3. I presume the interrupt() callback only controls whether the interrupt is registered/enabled/disabled, but where is the callback itself? (might have missed it from the PR being large)

I also need to understand them because build currently fails on i.MX. I suppose a dummy function which I should afterwards fill in may be needed.

@tlauda
tlauda force-pushed the topic/dma-irq-interface branch from 420597f to 72603a5 Compare September 20, 2019 10:56
@tlauda

tlauda commented Sep 20, 2019

Copy link
Copy Markdown
Contributor Author

@paulstelian97

  1. It's automatically done by creating dma_multi_chan_domain in platform.c. You just need to initialize it with the DMA you want to schedule your pipelines on. Function schedule_ll_tasks_run from ll_schedule.c will be called every irq trigger and it will run appropriate tasks.
  2. DMA_CB_STATUS_SPLIT was used only to perform split transactions between DSP and host and it was used only be the DW-DMA. Since those transfers are very fast I have changed the implementation to use blocking copy instead in host.c. There is no point in generating additional interrupts if it can be done much easier. DMA_CB_STATUS_IGNORE was used to handle both timer driven pipelines and DMA interrupt driven pipelines. Now the flow has changed and for every case we are going only through dma_copy (not that we are no longer register for DMA_CB_IRQ).
  3. Function schedule_ll_tasks_run from ll_schedule.c will be called every irq trigger and it will run appropriate tasks.

@paulstelian97

paulstelian97 commented Sep 20, 2019 •

Copy link
Copy Markdown
Collaborator
  1. It's automatically done by creating dma_multi_chan_domain in platform.c. You just need to initialize it with the DMA you want to schedule your pipelines on. Function schedule_ll_tasks_run from ll_schedule.c will be called every irq trigger and it will run appropriate tasks.

So my driver should just have an entry in there and have the .interrupt callback so that the channel itself can register the correct interrupt number? (On the i.MX the EDMA controllers may use different interrupt numbers for different channels)

  1. DMA_CB_STATUS_SPLIT was used only to perform split transactions between DSP and host and it was used only be the DW-DMA. Since those transfers are very fast I have changed the implementation to use blocking copy instead in host.c. There is no point in generating additional interrupts if it can be done much easier. DMA_CB_STATUS_IGNORE was used to handle both timer driven pipelines and DMA interrupt driven pipelines. Now the flow has changed and for every case we are going only through dma_copy (not that we are no longer register for DMA_CB_IRQ).

So now there's only reload and end. Reload for getting the next elem and end would automatically trigger a stop?

  1. Function schedule_ll_tasks_run from ll_schedule.c will be called every irq trigger and it will run appropriate tasks.

So this will, for example, run pipeline_copy(). Ok, this one makes sense.

@tlauda

tlauda commented Sep 20, 2019

Copy link
Copy Markdown
Contributor Author

SOFCI TEST

@tlauda

tlauda commented Sep 20, 2019 •

Copy link
Copy Markdown
Contributor Author
  1. It's automatically done by creating dma_multi_chan_domain in platform.c. You just need to initialize it with the DMA you want to schedule your pipelines on. Function schedule_ll_tasks_run from ll_schedule.c will be called every irq trigger and it will run appropriate tasks.

So my driver should just have an entry in there and have the .interrupt callback so that the channel itself can register the correct interrupt number? (On the i.MX the EDMA controllers may use different interrupt numbers for different channels)

Just implement .interrupt() API and it should work automatically. If you need to e.g. load next element in DMA do it in dma_copy.

  1. DMA_CB_STATUS_SPLIT was used only to perform split transactions between DSP and host and it was used only be the DW-DMA. Since those transfers are very fast I have changed the implementation to use blocking copy instead in host.c. There is no point in generating additional interrupts if it can be done much easier. DMA_CB_STATUS_IGNORE was used to handle both timer driven pipelines and DMA interrupt driven pipelines. Now the flow has changed and for every case we are going only through dma_copy (not that we are no longer register for DMA_CB_IRQ).

So now there's only reload and end. Reload for getting the next elem and end would automatically trigger a stop?

Yes, basically RELOAD means that we should proceed normally and END means that we should not proceed with the transfer e.g. do not reload next elem. Pipeline should automatically handle stopping the DMA.

  1. Function schedule_ll_tasks_run from ll_schedule.c will be called every irq trigger and it will run appropriate tasks.

So this will, for example, run pipeline_copy(). Ok, this one makes sense.

@paulstelian97

Copy link
Copy Markdown
Collaborator
  1. DMA_CB_STATUS_SPLIT was used only to perform split transactions between DSP and host and it was used only be the DW-DMA. Since those transfers are very fast I have changed the implementation to use blocking copy instead in host.c. There is no point in generating additional interrupts if it can be done much easier. DMA_CB_STATUS_IGNORE was used to handle both timer driven pipelines and DMA interrupt driven pipelines. Now the flow has changed and for every case we are going only through dma_copy (not that we are no longer register for DMA_CB_IRQ).

So now there's only reload and end. Reload for getting the next elem and end would automatically trigger a stop?

Yes, basically RELOAD means that we should proceed normally and END means that we should not proceed with the transfer e.g. do not reload next elem. Pipeline should automatically handle stopping the DMA.

So this means that within my dma_copy() function I should check for what the callback says and, if it says "RELOAD" I will set the next descriptor (essentially set the registers for EDMA and do nothing for Dummy DMA), while for "END" I would skip these actions? My current implementation does the copy first and then runs the callback.

@tlauda

tlauda commented Sep 20, 2019 •

Copy link
Copy Markdown
Contributor Author

@paulstelian97 DW-DMA also copies the data first and then executes action based on RELOAD/END, so this sounds correct. This new implementation should resolve your issue with scheduling task not yet done.

@paulstelian97

Copy link
Copy Markdown
Collaborator

Alright, waiting for this one to be merged so I can update my own. Even Dummy DMA needs to be updated even if it cannot support IRQs (I'd just return some -EINVAL on the .interrupt callback, right?)

@tlauda

tlauda commented Sep 20, 2019

Copy link
Copy Markdown
Contributor Author

Alright, waiting for this one to be merged so I can update my own. Even Dummy DMA needs to be updated even if it cannot support IRQs (I'd just return some -EINVAL on the .interrupt callback, right?)

Yes, but you only register your scheduling DMAs, so it won't change anything anyway.
Right after this change I will also introduce DMA domain that schedules all the tasks on one DMA channel irq, so maybe you could use that one, if your EDMA can support it. It can be used by the DMAs which support asynchronous interrupt handling. What this means is if you don't necessary need to reload data right after every interrupt, but can handle up to 2 ms of continuous transfer, then such scheduling can improve your performance by triggering only 1 interrupt per all the DMA channels instead of 1 per channel.

@tlauda
tlauda force-pushed the topic/dma-irq-interface branch from 72603a5 to c3509f8 Compare September 20, 2019 12:36
@paulstelian97

Copy link
Copy Markdown
Collaborator

Right after this change I will also introduce DMA domain that schedules all the tasks on one DMA channel irq, so maybe you could use that one, if your EDMA can support it. It can be used by the DMAs which support asynchronous interrupt handling. What this means is if you don't necessary need to reload data right after every interrupt, but can handle up to 2 ms of continuous transfer, then such scheduling can improve your performance by triggering only 1 interrupt per all the DMA channels instead of 1 per channel.

I have discussed this one internally and that's actually not a preferable solution (the EDMA controller can also be used by the ARM core, it's not reserved to the DSP). If we used the global interrupt we may get interrupts for other, uninteresting channels. Those interrupts would wake the DSP for no reason so we would need a good reason to switch to global interrupt per controller (each EDMA instance will have separate interrupts anyway).

In our use case, we have two flavors of the i.MX (support for the second flavor will come later), which differ in a few respects (some addresses and the other flavor will have a second ESAI controller which connects to channels in EDMA1 instead of EDMA0). The IRQs for Tx and Rx for both ESAI controllers do match but I don't get how it will handle the different channels like that (Tx happens on one channel, Rx on another, depending on Playback vs Capture).

Also, handling 2ms of continuous transfer... Sounds like a nice idea actually. There are discussions on features that I didn't think could be implemented such as avoiding the double buffering and using the DAI FIFO as the second buffer. Would your future change allow something like that to be done?

Comment thread src/platform/imx8/include/platform/lib/dma.h Outdated
Comment thread src/schedule/dma_multi_chan_domain.c Outdated
@tlauda

tlauda commented Sep 27, 2019

Copy link
Copy Markdown
Contributor Author

SOFCI TEST

Comment thread src/drivers/dw/dma.c Outdated
Comment thread src/audio/pipeline.c Outdated
Comment thread src/audio/dai.c Outdated
Comment thread src/include/sof/lib/dma.h Outdated
Comment thread src/include/sof/schedule/schedule.h Outdated
Comment thread src/schedule/dma_multi_chan_domain.c Outdated
Comment thread src/platform/baytrail/include/platform/drivers/dw-dma.h Outdated
Comment thread src/audio/pipeline.c Outdated
tlauda added 20 commits October 1, 2019 13:13
Adds comp_is_scheduling_source function, which checks whether
component is the one used as a scheduling source for the pipeline.

Signed-off-by: Tomasz Lauda <tomasz.lauda@linux.intel.com>
Adds scheduling_source field and dma_is_scheduling_source
function. It allows for easy identification whether particular
DMA channel is the source of pipeline scheduling.

Signed-off-by: Tomasz Lauda <tomasz.lauda@linux.intel.com>
There can be many potential low latency scheduling domains,
so let be more specific here.

Signed-off-by: Tomasz Lauda <tomasz.lauda@linux.intel.com>
Adds SOF_SCHEDULE_LL_DMA as the new low latency scheduling
domain type.

Signed-off-by: Tomasz Lauda <tomasz.lauda@linux.intel.com>
Moves CAVS_PLATFORM_NUM_DMACS definition to specific platforms
and renames it to PLATFORM_NUM_DMACS. This way we can access
to the number of platform DMAs from generic code.

Signed-off-by: Tomasz Lauda <tomasz.lauda@linux.intel.com>
Removes HDA_DMA_MAX_CHANS definition as it's not needed.
We can directly use the number of allocated channels.

Signed-off-by: Tomasz Lauda <tomasz.lauda@linux.intel.com>
Adds PLATFORM_MAX_DMA_CHAN definition to every platform.
This way generic API can retrieve this information.

Signed-off-by: Tomasz Lauda <tomasz.lauda@linux.intel.com>
Signed-off-by: Tomasz Lauda <tomasz.lauda@linux.intel.com>
Implements DMA scheduling domain. It allows to initialize
another low latency scheduler and schedule on any DMA channel.
The functionality stays the same as the one already done in
DMA drivers e.g. DW-DMA.

Signed-off-by: Tomasz Lauda <tomasz.lauda@linux.intel.com>
Adds PLATFORM_NUM_DW_DMACS definition. This way generic
API can access this information.

Signed-off-by: Tomasz Lauda <tomasz.lauda@linux.intel.com>
Extracts dma array to be accessible from the generic API.

Signed-off-by: Tomasz Lauda <tomasz.lauda@linux.intel.com>
Renames DMA_AGGREGATED_IRQ to DW_DMA_AGGREGATED_IRQ
definition. This is more appropriate as it's not generic
DMA setting.

Signed-off-by: Tomasz Lauda <tomasz.lauda@linux.intel.com>
Adds PLATFORM_DW_DMA_INDEX definition. This allows
to access this value from the generic API.

Signed-off-by: Tomasz Lauda <tomasz.lauda@linux.intel.com>
Signed-off-by: Tomasz Lauda <tomasz.lauda@linux.intel.com>
Registers low latency scheduler with DMA multi channel
domain as scheduling source. This way we can finally get rid
of scheduling code from DW-DMA driver and DAI component.
Functionally it works the same as the previous implementation,
but transfers scheduling layer to the right places.

Signed-off-by: Tomasz Lauda <tomasz.lauda@linux.intel.com>
Removes double channel index assignment in dw_dma_probe.

Signed-off-by: Tomasz Lauda <tomasz.lauda@linux.intel.com>
Cleanups DW-DMA driver and removes any code related
to interrupt handling.

Signed-off-by: Tomasz Lauda <tomasz.lauda@linux.intel.com>
Removes DMA_CB_STATUS_IGNORE, which is no longer used.

Signed-off-by: Tomasz Lauda <tomasz.lauda@linux.intel.com>
Removes unused DMA_CB_STATUS_SPLIT and related code.

Signed-off-by: Tomasz Lauda <tomasz.lauda@linux.intel.com>
Next has this status set by default, so there is no need
to set it explicitly.

Signed-off-by: Tomasz Lauda <tomasz.lauda@linux.intel.com>
@tlauda
tlauda force-pushed the topic/dma-irq-interface branch from 6d40b85 to ebe4749 Compare October 1, 2019 11:15
@tlauda

tlauda commented Oct 1, 2019

Copy link
Copy Markdown
Contributor Author

SOFCI TEST

@tlauda
tlauda merged commit f69f4a8 into thesofproject:master Oct 1, 2019
@paulstelian97

Copy link
Copy Markdown
Collaborator

@tlauda I have rebased my working branch on top of this one and my issue is that, due to having the need to use different IRQs per channel (not having shared IRQ as a real option, due to the DMA controller being also used for other things unrelated to the DSP) the rebase breaks the currently working code. I forgot how I was supposed to set the correct interrupt number per-channel with this change.

Right now I'm registering a custom IRQ handler every time set_config() is called per channel, and unregistering it during channel_put() (the system knows not to keep duplicate registrations so this was fine). The IRQ number I am currently storing in a handshake although I could change it (the thing is it CANNOT be unique to all channels).

Can you remind me how I was supposed to do this? Other than simply doing the hack of using the IRQ number as a controller IRQ number (I will do that right now for the moment to make it work but I will need a better solution)

@dbaluta

dbaluta commented Oct 1, 2019 •

Copy link
Copy Markdown
Collaborator

Right after this change I will also introduce DMA domain that schedules all the tasks on one DMA channel irq, so maybe you could use that one, if your EDMA can support it.

@tlauda Do you have some patches for this?

@tlauda

tlauda commented Oct 1, 2019 •

Copy link
Copy Markdown
Contributor Author

@paulstelian97 I think setting aggregated_irq to false in dma_multi_chan_domain_init should work. We also have different irq per every channel on APL platform.

@dbaluta Just started rebase.

@paulstelian97

Copy link
Copy Markdown
Collaborator

@tlauda Does this mean I would need to have a table for the channel numbers so I can implement dma_chan_irq()? Also what if the channel numbers map to different interrupt controllers? (for various reasons I had to split IRQ_STEER into 8 controllers from the SOF point of view; what if the channel IRQ numbers will eventually be from different such splits? Would I need to either update IRQ_STEER (and the interrupt framework overall) to support one controller with all 512 interrupts? (coming on 8 HW lines, that's why I actually did the split) Would I need to precompute the interrupt numbers manually or in that macro so that the name itself could return as empty?

One previous implementation of the EDMA driver had such a table however it was considered a hardcoding. Would I need to go back to it?

(issue is: interrupt numbers for EDMA channels are not even nearly sequential but they go all over the place and seem to be close to the interrupt for the served device -- for example 441 for ESAI and 442 for the channels which serve the ESAI)

@tlauda

tlauda commented Oct 2, 2019

Copy link
Copy Markdown
Contributor Author

@paulstelian97 Channel numbers from different controllers shouldn't be an issue. Your interrupt.c implementation should handle cascaded interrupts with many children. This DMA schedule domain just enables certain interrupt numbers. Your irq API should take care of the rest.
I still don't fully understand the problem with dma_chan_irq macro. If you're saying you don't have dedicated interrupts for EDMA, then maybe you should think about reserving such irq pool.

@paulstelian97

Copy link
Copy Markdown
Collaborator

@paulstelian97 Channel numbers from different controllers shouldn't be an issue. Your interrupt.c implementation should handle cascaded interrupts with many children. This DMA schedule domain just enables certain interrupt numbers. Your irq API should take care of the rest.
I still don't fully understand the problem with dma_chan_irq macro. If you're saying you don't have dedicated interrupts for EDMA, then maybe you should think about reserving such irq pool.

My issue is that the per-channel interrupts may not map nicely on the actual child controllers in my subsystem (the thing is that I have only one controller but it outputs 8 interrupt lines depending on which shared interrupt is triggered). I have one for 0-63, one for 64-127, one for 128-191 etc until one for 384-447 (which seems to capture the interrupts for now) and one for 448-511. They are separate due to using separate DSP interrupts.

Each channel has its own interrupt number. Channels 6 and 7 in particular which tie to the ESAI do share the same interrupt number 442, SPDIF uses 437/438, the SAI uses several between 429 and 436 and I think I'm also getting from other audio IP interrupts outside the 384-447 range (though the manual is hard to read). They would require different names or hardcoding the SOF interrupt itself (losing out on the SOF support for cascaded interrupts at least for the lookup). The dma_name() (or similar, can't look up the macro name right now) doesn't give a channel as a parameter but the DMAC itself, which means it assumes all channels of a DMAC must have their interrupts under the same interrupt controller (which may break in our case)

@tlauda
tlauda deleted the topic/dma-irq-interface branch November 22, 2019 11:22
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants