lib: dma: simplify DMAC search and fix users to max channels

Currently the dai user only increments the DMA channel usage count on
channel start and decrements on stop. This doesn't align with the current
DMAC allocator so ensure that the DMAC allocator cannot over subscribe
a DMAC with more users than channels. i.e. DAI config will try and allocate
a channel and fail if over subscribed.

This needs to be revisisted with changes to DAI to allow over subscription.

Signed-off-by: Liam Girdwood <liam.r.girdwood@linux.intel.com>
This commit is contained in:
Liam Girdwood 2018-12-07 20:53:57 +00:00 committed by Liam Girdwood
parent 24f84d5844
commit ba4054bc3c
1 changed files with 49 additions and 29 deletions

View File

@ -56,9 +56,11 @@ struct dma *dma_get(uint32_t dir, uint32_t cap, uint32_t dev, uint32_t flags)
struct dma *d = NULL, *dmin = NULL;
if (!lib_dma.num_dmas) {
trace_error(TRACE_CLASS_DMA, "dma_get(): No DMAs installed");
trace_error(TRACE_CLASS_DMA, "dma_get(): No DMACs installed");
return NULL;
}
/* find DMAC with free channels that matches request */
for (d = lib_dma.dma_array; d < lib_dma.dma_array + lib_dma.num_dmas;
d++) {
/* skip if this DMAC does not support the requested dir */
@ -73,6 +75,11 @@ struct dma *dma_get(uint32_t dir, uint32_t cap, uint32_t dev, uint32_t flags)
if (dev && (d->plat_data.devs & dev) == 0)
continue;
/* skip if this DMAC has 1 user per avail channel */
/* TODO: this should be fixed in dai.c to allow more users */
if (d->sref >= d->plat_data.channels)
continue;
/* if exclusive access is requested */
if (flags & DMA_ACCESS_EXCLUSIVE) {
@ -92,37 +99,50 @@ struct dma *dma_get(uint32_t dir, uint32_t cap, uint32_t dev, uint32_t flags)
}
}
/* return DMAC */
if (dmin) {
tracev_event(TRACE_CLASS_DMA, "dma_get(), dma-probe id = %d",
dmin->plat_data.id);
/* Shared DMA controllers with multiple channels
* may be requested many times, let the probe()
* do on-first-use initialization.
*/
spin_lock(&dmin->lock);
ret = 0;
if (dmin->sref == 0) {
ret = dma_probe(dmin);
if (ret < 0) {
trace_error(TRACE_CLASS_DMA,
"dma_get() error: dma-probe failed"
" id = %d, ret = %d",
dmin->plat_data.id, ret);
}
if (!dmin) {
trace_error(TRACE_CLASS_DMA, "No DMAC dir %d caps 0x%x dev 0x%x flags 0x%x",
dir, cap, dev, flags);
for (d = lib_dma.dma_array; d < lib_dma.dma_array + lib_dma.num_dmas;
d++) {
trace_error(TRACE_CLASS_DMA, " DMAC ID %d users %d busy channels %d",
d->plat_data.id, d->sref,
atomic_read(&d->num_channels_busy));
trace_error(TRACE_CLASS_DMA, " caps 0x%x dev 0x%x",
d->plat_data.caps, d->plat_data.devs);
}
if (!ret)
dmin->sref++;
trace_event(TRACE_CLASS_DMA, "dma_get(), dmin = %p, sref = %d",
(uintptr_t)dmin, dmin->sref);
spin_unlock(&dmin->lock);
return !ret ? dmin : NULL;
return NULL;
}
trace_error(TRACE_CLASS_DMA,
"dma_get() error: dir = 0x%x, cap = 0x%x, dev = 0x%x, "
"flags = 0x%x not found", dir, cap, dev, flags);
return NULL;
/* return DMAC */
tracev_event(TRACE_CLASS_DMA, "dma_get(), dma-probe id = %d",
dmin->plat_data.id);
/* Shared DMA controllers with multiple channels
* may be requested many times, let the probe()
* do on-first-use initialization.
*/
spin_lock(&dmin->lock);
ret = 0;
if (dmin->sref == 0) {
ret = dma_probe(dmin);
if (ret < 0) {
trace_error(TRACE_CLASS_DMA,
"dma_get() error: dma-probe failed"
" id = %d, ret = %d",
dmin->plat_data.id, ret);
}
}
if (!ret)
dmin->sref++;
trace_event(TRACE_CLASS_DMA, "dma_get() ID %d sref = %d busy channels %d",
dmin->plat_data.id, dmin->sref,
atomic_read(&dmin->num_channels_busy));
spin_unlock(&dmin->lock);
return !ret ? dmin : NULL;
}
void dma_put(struct dma *dma)