[PATCH/RFC 1/4] dma: add a function to request a DMA channel for a specific client
Guennadi Liakhovetski
g.liakhovetski at gmx.de
Tue Jul 17 06:53:44 EDT 2012
Add a new channel-request function, that uses a device pointer and a
transfer direction to locate the required client DMA request line. Devices,
that require DMA channels exceeding the traditional Tx / Rx scheme, can use
the optional "name" parameter.
This new function uses a new DMA multiplexer interface. The new DMA request
line routing concept declares, that client DMA request lines are always
statically connected to a DMA provider. If a client is directly connected
to a DMAC, such a statical connection results in a 1-to-1 relationship
between client DMA request lines and DMAC DMA channels. A more flexible
configuration includes a multiplexer between clients and DMAC instances. In
these cases it is the multiplexer, that controls the routing. The initial
DMA multiplexer API consists of only one call-back: .request_chan(), that
finds the next free (physical or virtual) DMA channel, that can be routed
to the requested slave DMA interface. The actual routing, if required,
should be performed as a part of the dmaengine_slave_config() processing.
Signed-off-by: Guennadi Liakhovetski <g.liakhovetski at gmx.de>
---
drivers/dma/dmaengine.c | 73 +++++++++++++++++++++++++++++++++++++++++++++
include/linux/dmaengine.h | 9 +++++
2 files changed, 82 insertions(+), 0 deletions(-)
diff --git a/drivers/dma/dmaengine.c b/drivers/dma/dmaengine.c
index 2397f6f..4fb927c 100644
--- a/drivers/dma/dmaengine.c
+++ b/drivers/dma/dmaengine.c
@@ -542,6 +542,79 @@ struct dma_chan *__dma_request_channel(dma_cap_mask_t *mask, dma_filter_fn fn, v
}
EXPORT_SYMBOL_GPL(__dma_request_channel);
+/**
+ * dma_request_slave_channel() - try to allocate an exclusive channel for device
+ * @dev: client device, for which a channel should be allocated
+ * @direction: transfer direction
+ * @name: optional name of the channel, used, when the direction is not
+ * sufficient to uniquely identify the DMA request
+ */
+struct dma_chan *dma_request_slave_channel(struct device *dev,
+ enum dma_transfer_direction direction, const char *name)
+{
+ struct dma_device *device, *_d;
+ struct dma_chan *chan = NULL;
+ dma_cap_mask_t mask;
+ int err;
+
+ dma_cap_zero(mask);
+ dma_cap_set(DMA_SLAVE, mask);
+
+ /* Find a channel */
+ mutex_lock(&dma_list_mutex);
+ list_for_each_entry_safe(device, _d, &dma_device_list, global_node) {
+ if (!device->mux || !device->mux->request_chan)
+ continue;
+
+ if (!dma_device_satisfies_mask(device, mask)) {
+ pr_info("%s: wrong capabilities\n", __func__);
+ continue;
+ }
+
+ /* ensure that all channels are either private or public. */
+ if (!dma_has_cap(DMA_PRIVATE, device->cap_mask)) {
+ bool public = false;
+ list_for_each_entry(chan, &device->channels, device_node) {
+ /* some channels are already publicly allocated */
+ if (chan->client_count) {
+ public = true;
+ break;
+ }
+ }
+ if (public)
+ continue;
+ }
+
+ chan = device->mux->request_chan(device, dev, direction, name);
+ if (chan) {
+ dma_cap_set(DMA_PRIVATE, device->cap_mask);
+ device->privatecnt++;
+ err = dma_chan_get(chan);
+ if (!err)
+ break;
+ if (err == -ENODEV) {
+ pr_debug("%s: %s module removed\n", __func__,
+ dma_chan_name(chan));
+ list_del_rcu(&device->global_node);
+ } else
+ pr_debug("%s: failed to get %s: (%d)\n",
+ __func__, dma_chan_name(chan), err);
+
+ if (--device->privatecnt == 0)
+ dma_cap_clear(DMA_PRIVATE, device->cap_mask);
+
+ chan = NULL;
+ }
+ }
+ mutex_unlock(&dma_list_mutex);
+
+ pr_debug("%s: %s (%s)\n", __func__, chan ? "success" : "fail",
+ chan ? dma_chan_name(chan) : NULL);
+
+ return chan;
+}
+EXPORT_SYMBOL_GPL(dma_request_slave_channel);
+
void dma_release_channel(struct dma_chan *chan)
{
mutex_lock(&dma_list_mutex);
diff --git a/include/linux/dmaengine.h b/include/linux/dmaengine.h
index ccec62f..19b96d9 100644
--- a/include/linux/dmaengine.h
+++ b/include/linux/dmaengine.h
@@ -482,6 +482,11 @@ static inline struct dma_async_tx_descriptor *txd_next(struct dma_async_tx_descr
}
#endif
+struct dma_multiplexer {
+ struct dma_chan *(*request_chan)(struct dma_device *, struct device *,
+ enum dma_transfer_direction, const char *);
+};
+
/**
* struct dma_tx_state - filled in to report the status of
* a transfer.
@@ -553,6 +558,8 @@ struct dma_device {
int dev_id;
struct device *dev;
+ const struct dma_multiplexer *mux;
+
int (*device_alloc_chan_resources)(struct dma_chan *chan);
void (*device_free_chan_resources)(struct dma_chan *chan);
@@ -994,6 +1001,8 @@ void dma_run_dependencies(struct dma_async_tx_descriptor *tx);
struct dma_chan *dma_find_channel(enum dma_transaction_type tx_type);
struct dma_chan *net_dma_find_channel(void);
#define dma_request_channel(mask, x, y) __dma_request_channel(&(mask), x, y)
+struct dma_chan *dma_request_slave_channel(struct device *dev,
+ enum dma_transfer_direction direction, const char *name);
/* --- Helper iov-locking functions --- */
--
1.7.2.5
More information about the linux-arm-kernel
mailing list