From 9b19b77abb9b9a8d6e4badfc381c215053b30c3d Mon Sep 17 00:00:00 2001 From: Liam Girdwood Date: Fri, 7 Dec 2018 20:53:57 +0000 Subject: [PATCH] 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 --- src/lib/dma.c | 76 ++++++++++++++++++++++++++++++++------------------- 1 file changed, 48 insertions(+), 28 deletions(-) diff --git a/src/lib/dma.c b/src/lib/dma.c index 245ff4ee2b41..0fbad8e7c520 100644 --- a/src/lib/dma.c +++ b/src/lib/dma.c @@ -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) } } + 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); + } + return NULL; + } + /* 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); - } + 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(), dmin = %p, sref = %d", - (uintptr_t)dmin, dmin->sref); - spin_unlock(&dmin->lock); - return !ret ? dmin : NULL; } + 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)); - 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; + spin_unlock(&dmin->lock); + return !ret ? dmin : NULL; } void dma_put(struct dma *dma)