Re: [PATCH RFC 1/2] dmaengine: Introduce scheduled DMA framework

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



On Sat, Mar 21, 2015 at 12:42:06PM -0700, Maxime Ripard wrote:
> This framework aims at easing the development of dmaengine drivers by providing
> generic implementations of the functions usually required by dmaengine, while
> abstracting away most of the logic required.
> 
> It is very relevant for controllers that have more requests than channels,
> where you need to have some scheduling that is usually very bug prone, and
> shouldn't be implemented in each and every driver.
> 
> This introduces a new set of callbacks for drivers to implement the device
> specific behaviour. These new sets of callbacks aims at providing both how to
> handle channel related operations (start the transfer of a new descriptor,
> pause, resume, etc.) and the LLI related operations (iterator and various
> accessors).
> 
> So far, the only transfer types supported are memcpy and slave transfers, but
> eventually should support new transfer types as new drivers get converted.
A good direction indeed, few comments below...

> 
> Signed-off-by: Maxime Ripard <maxime.ripard@xxxxxxxxxxxxxxxxxx>
> ---
>  drivers/dma/Kconfig         |   4 +
>  drivers/dma/Makefile        |   1 +
>  drivers/dma/scheduled-dma.c | 571 ++++++++++++++++++++++++++++++++++++++++++++
>  drivers/dma/scheduled-dma.h | 140 +++++++++++
>  4 files changed, 716 insertions(+)
>  create mode 100644 drivers/dma/scheduled-dma.c
>  create mode 100644 drivers/dma/scheduled-dma.h
> 
> diff --git a/drivers/dma/Kconfig b/drivers/dma/Kconfig
> index a874b6ec6650..032bf5fcd58b 100644
> --- a/drivers/dma/Kconfig
> +++ b/drivers/dma/Kconfig
> @@ -406,6 +406,7 @@ config DMA_SUN6I
>  	depends on RESET_CONTROLLER
>  	select DMA_ENGINE
>  	select DMA_VIRTUAL_CHANNELS
> +	select DMA_SCHEDULED
ummm, selecting without converting driver?
>  	help
>  	  Support for the DMA engine first found in Allwinner A31 SoCs.
>  
> @@ -431,6 +432,9 @@ config DMA_ENGINE
>  config DMA_VIRTUAL_CHANNELS
>  	tristate
>  
> +config DMA_SCHEDULED
> +	bool
it might make sense to do select DMA_VIRTUAL_CHANNELS but this is fine too.
> +
>  config DMA_ACPI
>  	def_bool y
>  	depends on ACPI
> diff --git a/drivers/dma/Makefile b/drivers/dma/Makefile
> index f915f61ec574..1db31814c749 100644
> --- a/drivers/dma/Makefile
> +++ b/drivers/dma/Makefile
> @@ -5,6 +5,7 @@ obj-$(CONFIG_DMA_ENGINE) += dmaengine.o
>  obj-$(CONFIG_DMA_VIRTUAL_CHANNELS) += virt-dma.o
>  obj-$(CONFIG_DMA_ACPI) += acpi-dma.o
>  obj-$(CONFIG_DMA_OF) += of-dma.o
> +obj-$(CONFIG_DMA_SCHEDULED) += scheduled-dma.o
>  
>  obj-$(CONFIG_INTEL_MID_DMAC) += intel_mid_dma.o
>  obj-$(CONFIG_DMATEST) += dmatest.o
> diff --git a/drivers/dma/scheduled-dma.c b/drivers/dma/scheduled-dma.c
> new file mode 100644
> index 000000000000..482d04f2ccbc
> --- /dev/null
> +++ b/drivers/dma/scheduled-dma.c
> @@ -0,0 +1,571 @@
> +/*
> + * Copyright (C) 2015 Maxime Ripard
> + * Maxime Ripard <maxime.ripard@xxxxxxxxxxxxxxxxxx>
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License as published by
> + * the Free Software Foundation; either version 2 of the License, or
> + * (at your option) any later version.
> + */
> +
> +#include <linux/dmaengine.h>
> +#include <linux/dmapool.h>
> +#include <linux/list.h>
> +#include <linux/slab.h>
> +
> +#include "scheduled-dma.h"
> +#include "virt-dma.h"
> +
> +static struct sdma_request *sdma_pop_queued_transfer(struct sdma *sdma,
> +						     struct sdma_channel *schan)
> +{
> +	struct sdma_request *sreq = NULL;
> +	unsigned long flags;
> +
> +	spin_lock_irqsave(&sdma->lock, flags);
> +
> +	/* No requests are awaiting an available channel */
> +	if (list_empty(&sdma->pend_reqs))
> +		goto out;
> +
> +	/*
> +	 * If we don't have any validate_request callback, any request
> +	 * can be dispatched to any channel.
> +	 *
> +	 * Remove the first entry and return it.
> +	 */
> +	if (!sdma->ops->validate_request) {
> +		sreq = list_first_entry(&sdma->pend_reqs,
> +					struct sdma_request, node);
> +		list_del_init(&sreq->node);
> +		goto out;
> +	}
> +
> +	list_for_each_entry(sreq, &sdma->pend_reqs, node) {
> +		/*
> +		 * Ask the driver to validate that the request can
> +		 * happen on the channel.
> +		 */
> +		if (sdma->ops->validate_request(schan, sreq)) {
> +			list_del_init(&sreq->node);
> +			goto out;
> +		}
> +
> +		/* Otherwise, just keep looping */
> +	}
> +
> +out:
> +	spin_unlock_irqrestore(&sdma->lock, flags);
> +
> +	return sreq;
> +}
> +
> +/*
> + * Besoin d'une fonction pour pusher un descriptor vers un pchan
> + *
> + * Flow normal:
> + *   - Election d'un pchan (Framework)
> + *   - Push d'un descripteur vers le pchan (Driver)
> + *   - idle....
> + *   - interrupt (driver)
> + *   - Transfert terminé, notification vers le framework (driver)
> + *   - Nouveau transfert dans la queue?
> + *     + Si oui, on est reparti
> + *     + Si non, on sort de l'interrupt, le pchan est libéré
Sorry cant understand this, this is actually bad for non french speakers

> + */
> +
> +struct sdma_desc *sdma_report(struct sdma *sdma,
> +			      struct sdma_channel *schan,
> +			      enum sdma_report_status status)
> +{
> +	struct sdma_desc *sdesc = NULL;
> +	struct virt_dma_desc *vdesc;
> +	struct sdma_request *sreq;
> +
> +	switch (status) {
> +	case SDMA_REPORT_TRANSFER:
> +		/*
> +		 * We're done with the current transfer that was in this
> +		 * physical channel.
> +		 */
> +		vchan_cookie_complete(&schan->desc->vdesc);
> +
> +		/*
> +		 * Now, try to see if there's any queued transfer
> +		 * awaiting an available channel.
> +		 *
> +		 * If not, just bail out, and mark the pchan as
> +		 * available.
> +		 *
> +		 * If there's such a transfer, validate that the
> +		 * driver can handle it, and ask it to do the
> +		 * transfer.
> +		 */
> +		sreq = sdma_pop_queued_transfer(sdma, schan);
> +		if (!sreq) {
> +			list_add_tail(&schan->node, &sdma->avail_chans);
> +			return NULL;
> +		}
> +
> +		/* Mark the request as assigned to a particular channel */
> +		sreq->chan = schan;
> +
> +		/* Retrieve the next transfer descriptor */
> +		vdesc = vchan_next_desc(&sreq->vchan);
> +		schan->desc = sdesc = to_sdma_desc(&vdesc->tx);
> +
> +		break;
> +
> +	default:
> +		break;
since you have only one case, the switch seems overkill here!
Or the plan is to get chunk support added eventually, but then...

> +	}
> +
> +	return sdesc;
> +}
> +EXPORT_SYMBOL_GPL(sdma_report);
> +
> +static enum dma_status sdma_tx_status(struct dma_chan *chan,
> +				      dma_cookie_t cookie,
> +				      struct dma_tx_state *state)
> +{
> +	struct sdma_request *sreq = to_sdma_request(chan);
> +	struct sdma *sdma = to_sdma(chan->device);
> +	struct sdma_channel *schan = sreq->chan;
> +	struct virt_dma_desc *vd;
> +	struct sdma_desc *desc;
> +	enum dma_status ret;
> +	unsigned long flags;
> +	size_t bytes = 0;
> +	void *lli;
> +
> +	spin_lock_irqsave(&sreq->vchan.lock, flags);
> +
> +	ret = dma_cookie_status(chan, cookie, state);
> +	if (ret == DMA_COMPLETE)
> +		goto out;
> +
> +	vd = vchan_find_desc(&sreq->vchan, cookie);
> +	desc = to_sdma_desc(&vd->tx);
> +
> +	if (vd) {
> +		lli = desc->v_lli;
> +		while (true) {
> +			bytes += sdma->ops->lli_size(lli);
> +
> +			if (!sdma->ops->lli_has_next(lli))
> +				break;
> +
> +			lli = sdma->ops->lli_next(lli);
wouldn't it be nicer to just use a link list here which is embedded in a generic
struct which in turn can be embedded in a driver specific one, so we actually
don't care what driver specific implementation is... that way we cna get rid
of lli_size and lli_has_next and lli_next ones. Further that will allow
drivers/framework to break a txn into multiple smaller txns internally.

> +		}
> +	} else if (chan) {
> +		bytes = sdma->ops->channel_residue(schan);
> +	}
> +
> +	dma_set_residue(state, bytes);
> +
> +out:
> +	spin_unlock_irqrestore(&sreq->vchan.lock, flags);
> +
> +	return ret;
> +};
> +
> +static int sdma_config(struct dma_chan *chan,
> +		       struct dma_slave_config *config)
> +{
> +	struct sdma_request *sreq = to_sdma_request(chan);
> +	unsigned long flags;
> +
> +	spin_lock_irqsave(&sreq->vchan.lock, flags);
> +	memcpy(&sreq->cfg, config, sizeof(*config));
> +	spin_unlock_irqrestore(&sreq->vchan.lock, flags);
> +
> +	return 0;
> +}
> +
> +static int sdma_pause(struct dma_chan *chan)
> +{
> +	struct sdma_request *sreq = to_sdma_request(chan);
> +	struct sdma_channel *schan = sreq->chan;
> +	struct sdma *sdma = to_sdma(chan->device);
> +	unsigned long flags;
> +
> +	spin_lock_irqsave(&sreq->vchan.lock, flags);
> +
> +	/*
> +	 * If the request is currently scheduled on a channel, just
> +	 * pause the channel.
> +	 *
> +	 * If not, remove the request from the pending list.
The current API pauses a channel, not a request, so this part isn't clear to
me, cna you pls explain...

> +	 */
> +	if (schan) {
> +		sdma->ops->channel_pause(schan);
make sure you document this and rest of the calls can be invoked with lock
held!
> +	} else {
> +		spin_lock(&sdma->lock);
> +		list_del_init(&sreq->node);
> +		spin_unlock(&sdma->lock);
> +	}
> +
> +	spin_unlock_irqrestore(&sreq->vchan.lock, flags);
> +
> +	return 0;
> +}
> +
> +static int sdma_resume(struct dma_chan *chan)
> +{
> +	struct sdma_request *sreq = to_sdma_request(chan);
> +	struct sdma_channel *schan = sreq->chan;
> +	struct sdma *sdma = to_sdma(chan->device);
> +	unsigned long flags;
> +
> +	spin_lock_irqsave(&sreq->vchan.lock, flags);
> +
> +	/*
> +	 * If the request is currently scheduled on a channel, just
> +	 * resume the channel.
> +	 *
> +	 * If not, add the request from the pending list.
same here too
> +	 */
> +	if (schan) {
> +		sdma->ops->channel_resume(schan);
> +	} else {
> +		spin_lock(&sdma->lock);
> +		list_add_tail(&sreq->node, &sdma->pend_reqs);
> +		spin_unlock(&sdma->lock);
> +	}
> +
> +	spin_unlock_irqrestore(&sreq->vchan.lock, flags);
> +
> +	return 0;
> +}
> +
> +static int sdma_terminate(struct dma_chan *chan)
> +{
> +	struct sdma_request *sreq = to_sdma_request(chan);
> +	struct sdma_channel *schan = sreq->chan;
> +	struct sdma *sdma = to_sdma(chan->device);
> +	unsigned long flags;
> +
> +	spin_lock_irqsave(&sreq->vchan.lock, flags);
> +
> +	/*
> +	 * If the request is currently scheduled on a channel,
> +	 * terminate the channel.
> +	 *
> +	 * If not, prevent the request from being scheduled.
> +	 */
> +	if (schan) {
> +		sdma->ops->channel_terminate(schan);
> +	} else {
> +		spin_lock(&sdma->lock);
> +		list_del_init(&sreq->node);
> +		spin_unlock(&sdma->lock);
> +	}
> +
> +	spin_unlock_irqrestore(&sreq->vchan.lock, flags);
> +
> +	/*
> +	 * Flush all the pending descriptors from our vchan
> +	 */
> +	vchan_free_chan_resources(&sreq->vchan);
I think cleanup is not right we need more work here.
> +
> +	return 0;
> +}
> +
> +
> +static struct dma_async_tx_descriptor *sdma_prep_memcpy(struct dma_chan *chan,
> +							dma_addr_t dest, dma_addr_t src,
> +							size_t len, unsigned long flags)
> +{
> +	struct sdma_request *req = to_sdma_request(chan);
> +	struct sdma *sdma = to_sdma(chan->device);
> +	struct sdma_desc *desc;
> +	dma_addr_t p_lli;
> +	void *v_lli;
> +
> +	if (!len)
> +		return NULL;
> +
> +	/* Allocate our representation of a descriptor */
> +	desc = kzalloc(sizeof(*desc), GFP_NOWAIT);
> +	if (!desc)
> +		return NULL;
> +
> +	v_lli = dma_pool_alloc(sdma->pool, GFP_NOWAIT, &p_lli);
> +	if (!v_lli)
> +		goto err_desc_free;
> +
> +	/* Ask the driver to initialise its hardware descriptor */
> +	if (sdma->ops->lli_init(v_lli, req->private,
> +				SDMA_TRANSFER_MEMCPY,
> +				DMA_MEM_TO_MEM, src, dest, len,
> +				NULL))
> +		goto err_lli_free;
> +
> +	/* Create our single item LLI */
> +	sdma->ops->lli_queue(NULL, v_lli, p_lli);
> +	desc->p_lli = p_lli;
> +	desc->v_lli = v_lli;
> +
> +	return vchan_tx_prep(&req->vchan, &desc->vdesc, flags);
typically driver would do calculations for descriptor setting up the
registers values which cna be programmed, so a driver callback would be nice
here. That way lesser work in start function and which is good as I am
assuming start should be done from irq context

Also this needs to take into account the dma_slave_config, which is allowed
to me modified for subsequent txns
> +
> +err_lli_free:
> +	dma_pool_free(sdma->pool, v_lli, p_lli);
> +err_desc_free:
> +	kfree(desc);
> +
> +	return NULL;
> +}
> +
> +static struct dma_async_tx_descriptor *sdma_prep_slave_sg(struct dma_chan *chan,
> +							  struct scatterlist *sgl,
> +							  unsigned int sg_len,
> +							  enum dma_transfer_direction dir,
> +							  unsigned long flags, void *context)
> +{
> +	struct sdma_request *req = to_sdma_request(chan);
> +	struct dma_slave_config *config = &req->cfg;
> +	struct sdma *sdma = to_sdma(chan->device);
> +	void *v_lli, *prev_v_lli = NULL;
> +	struct scatterlist *sg;
> +	struct sdma_desc *desc;
> +	dma_addr_t p_lli;
> +	int i;
> +
> +	if (!sgl || !sg_len)
> +		return NULL;
> +
> +	/* Allocate our representation of a descriptor */
> +	desc = kzalloc(sizeof(*desc), GFP_NOWAIT);
> +	if (!desc)
> +		return NULL;
> +
> +	/*
> +	 * For each scatter list entry, build up our representation of
> +	 * the LLI, and ask the driver to create its hardware
> +	 * descriptor.
> +	 */
> +	for_each_sg(sgl, sg, sg_len, i) {
> +		v_lli = dma_pool_alloc(sdma->pool, GFP_NOWAIT, &p_lli);
> +		if (!v_lli)
> +			goto err_lli_free;
> +
> +		/* Ask the driver to initialise its hardware descriptor */
> +		if (sdma->ops->lli_init(v_lli, req->private,
> +					SDMA_TRANSFER_SLAVE,
> +					dir, sg_dma_address(sg),
> +					config->dst_addr, sg_dma_len(sg),
> +					config))
> +			goto err_lli_free;
> +
> +		/*
> +		 * If it's our first item, initialise our descriptor
> +		 * pointers to the lli.
> +		 *
> +		 * Otherwise, queue it to the end of the LLI.
> +		 */
> +		if (!prev_v_lli) {
> +			desc->p_lli = p_lli;
> +			desc->v_lli = v_lli;
> +			prev_v_lli = v_lli;
> +		} else {
> +			/* And to queue it at the end of its hardware LLI */
> +			prev_v_lli = sdma->ops->lli_queue(prev_v_lli, v_lli, p_lli);
> +		}
> +	}
> +
> +	return vchan_tx_prep(&req->vchan, &desc->vdesc, flags);
> +
> +err_lli_free:
> +#warning "Free the LLI"
why a non std preprocessor warning

> +
> +	kfree(desc);
> +	return NULL;
> +}
> +
> +static void sdma_issue_pending(struct dma_chan *chan)
> +{
> +	struct sdma_request *sreq = to_sdma_request(chan);
> +	struct sdma *sdma = to_sdma(chan->device);
> +	struct virt_dma_desc *vdesc;
> +	struct sdma_channel *schan;
> +	struct sdma_desc *sdesc;
> +	unsigned long flags;
> +
> +	spin_lock_irqsave(&sreq->vchan.lock, flags);
> +
> +	/* See if we have anything to do */
> +	if (!vchan_issue_pending(&sreq->vchan))
> +		goto out_chan_unlock;
> +
> +	/* Is some work in progress already? */
> +	if (sreq->chan)
> +		goto out_chan_unlock;
> +
> +	spin_lock(&sdma->lock);
> +
> +	/* Is there an available channel */
> +	if (list_empty(&sdma->avail_chans))
> +		goto out_main_unlock;
> +
> +	/*
> +	 * If there's no validate_request callback, it means that all
> +	 * channels can transfer any request. Pick the first available
> +	 * channel.
> +	 *
> +	 * Otherwise, iterate over all the pending channels and call
> +	 * validate_request.
> +	 */
> +	if (!sdma->ops->validate_request) {
> +		schan = list_first_entry(&sdma->avail_chans,
> +					 struct sdma_channel, node);
> +	} else {
> +		list_for_each_entry(schan, &sdma->avail_chans, node) {
> +			if (sdma->ops->validate_request(schan, sreq)) {
> +				list_del_init(&schan->node);
> +				break;
> +			}
> +		}
> +	}
why not call sdma_pop_queued_transfer() ?
> +
> +	if (!schan)
> +		goto out_main_unlock;
> +
> +	sreq->chan = schan;
> +
> +	/* Retrieve the next transfer descriptor */
> +	vdesc = vchan_next_desc(&sreq->vchan);
> +	schan->desc = sdesc = to_sdma_desc(&vdesc->tx);
> +
> +	sdma->ops->channel_start(schan, sdesc);
> +
> +out_main_unlock:
> +	spin_unlock(&sdma->lock);
> +out_chan_unlock:
> +	spin_unlock_irqrestore(&sreq->vchan.lock, flags);
> +}
> +
> +static void sdma_free_chan_resources(struct dma_chan *chan)
> +{
> +	struct sdma_request *sreq = to_sdma_request(chan);
> +	unsigned long flags;
> +
> +	spin_lock_irqsave(&sreq->vchan.lock, flags);
> +	list_del_init(&sreq->node);
> +	spin_unlock_irqrestore(&sreq->vchan.lock, flags);
> +
> +	vchan_free_chan_resources(&sreq->vchan);
> +}
> +
> +static void sdma_free_desc(struct virt_dma_desc *vdesc)
> +{
> +#warning "Free the descriptors"
??

> +}
> +
> +struct sdma *sdma_alloc(struct device *dev,
> +			unsigned int channels,
> +			unsigned int requests,
> +			ssize_t lli_size,
> +			ssize_t priv_size)
> +{
> +	struct sdma *sdma;
> +	int ret, i;
> +
> +	sdma = devm_kzalloc(dev, sizeof(*sdma) + priv_size, GFP_KERNEL);
> +	if (!sdma) {
> +		ret = -ENOMEM;
> +		goto out;
> +	}
> +	INIT_LIST_HEAD(&sdma->pend_reqs);
> +	INIT_LIST_HEAD(&sdma->avail_chans);
> +	spin_lock_init(&sdma->lock);
> +
> +	sdma->pool = dmam_pool_create(dev_name(dev), dev, lli_size, 4, 0);
> +	if (!sdma->pool) {
> +		ret = -ENOMEM;
> +		goto out;
> +	}
> +
> +	sdma->channels_nr = channels;
> +	sdma->channels = devm_kcalloc(dev, channels, sizeof(*sdma->channels), GFP_KERNEL);
> +	if (!sdma->channels) {
> +		ret = -ENOMEM;
> +		goto out;
> +	}
> +
> +	for (i = 0; i < channels; i++) {
> +		struct sdma_channel *schan = &sdma->channels[i];
> +
> +		list_add_tail(&schan->node, &sdma->avail_chans);
> +		schan->index = i;
> +	}
> +
> +	sdma->requests_nr = requests;
> +	sdma->requests = devm_kcalloc(dev, requests, sizeof(*sdma->requests), GFP_KERNEL);
> +	if (!sdma->channels) {
> +		ret = -ENOMEM;
> +		goto out;
> +	}
> +
> +	INIT_LIST_HEAD(&sdma->ddev.channels);
> +
> +	for (i = 0; i < requests; i++) {
> +		struct sdma_request *sreq = &sdma->requests[i];
> +
> +		INIT_LIST_HEAD(&sreq->node);
> +		sreq->vchan.desc_free = sdma_free_desc;
> +		vchan_init(&sreq->vchan, &sdma->ddev);
> +	}
> +
> +	return sdma;
> +
> +out:
> +	return ERR_PTR(ret);
> +}
> +EXPORT_SYMBOL(sdma_alloc);
> +
> +void sdma_free(struct sdma *sdma)
> +{
> +	return;
> +}
> +EXPORT_SYMBOL(sdma_free);
> +
> +int sdma_register(struct sdma *sdma,
> +		  struct sdma_ops *ops)
> +{
> +	struct dma_device *ddev = &sdma->ddev;
> +
> +	sdma->ops = ops;
> +
> +	ddev->device_config = sdma_config;
> +	ddev->device_tx_status = sdma_tx_status;
> +	ddev->device_issue_pending = sdma_issue_pending;
> +	ddev->device_free_chan_resources = sdma_free_chan_resources;
> +
> +	if (ops->channel_pause)
> +		ddev->device_pause = sdma_pause;
> +
> +	if (ops->channel_resume)
> +		ddev->device_resume = sdma_resume;
> +
> +	if (ops->channel_terminate)
> +		ddev->device_terminate_all = sdma_terminate;
> +
> +	if (dma_has_cap(DMA_SLAVE, ddev->cap_mask))
> +		ddev->device_prep_slave_sg = sdma_prep_slave_sg;
> +
> +	if (dma_has_cap(DMA_MEMCPY, ddev->cap_mask))
> +		ddev->device_prep_dma_memcpy = sdma_prep_memcpy;
> +
> +	dma_async_device_register(ddev);
> +
> +	return 0;
> +}
> +EXPORT_SYMBOL(sdma_register);
> +
> +int sdma_unregister(struct sdma *sdma)
> +{
> +	dma_async_device_unregister(&sdma->ddev);
> +
> +	return 0;
> +}
> +EXPORT_SYMBOL(sdma_unregister);
> diff --git a/drivers/dma/scheduled-dma.h b/drivers/dma/scheduled-dma.h
> new file mode 100644
> index 000000000000..d24c8143b2b6
> --- /dev/null
> +++ b/drivers/dma/scheduled-dma.h
> @@ -0,0 +1,140 @@
> +/*
> + * Copyright (C) 2015 Maxime Ripard
> + * Maxime Ripard <maxime.ripard@xxxxxxxxxxxxxxxxxx>
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License as published by
> + * the Free Software Foundation; either version 2 of the License, or
> + * (at your option) any later version.
> + */
> +
> +#include "virt-dma.h"
> +
> +#ifndef _SCHEDULED_DMA_H_
> +#define _SCHEDULED_DMA_H_
> +
> +enum sdma_transfer_type {
> +	SDMA_TRANSFER_MEMCPY,
> +	SDMA_TRANSFER_SLAVE,
> +};
> +
> +enum sdma_report_status {
> +	SDMA_REPORT_CHUNK,
> +	SDMA_REPORT_TRANSFER,
> +};
can you pls add kernel style documentation for these and below.
> +
> +struct sdma_desc {
> +	struct virt_dma_desc	vdesc;
> +
> +	/* Entry point to our LLI */
> +	dma_addr_t		p_lli;
> +	void			*v_lli;
> +};
> +
> +struct sdma_channel {
> +	struct sdma_desc	*desc;
> +	unsigned int		index;
> +	struct list_head	node;
> +	void			*private;
> +};
> +
> +struct sdma_request {
> +	struct dma_slave_config	cfg;
> +	struct list_head	node;
> +	struct virt_dma_chan	vchan;
> +
> +	struct sdma_channel	*chan;
> +	void			*private;
> +};
> +
> +struct sdma_ops {
> +	/* LLI management operations */
> +	bool			(*lli_has_next)(void *v_lli);
> +	void			*(*lli_next)(void *v_lli);
> +	int			(*lli_init)(void *v_lli, void *sreq_priv,
> +					    enum sdma_transfer_type type,
> +					    enum dma_transfer_direction dir,
> +					    dma_addr_t src,
> +					    dma_addr_t dst, u32 len,
> +					    struct dma_slave_config *config);
> +	void			*(*lli_queue)(void *prev_v_lli, void *v_lli, dma_addr_t p_lli);
> +	size_t			(*lli_size)(void *v_lli);
if we can manage these using the standard list then it would simplify the
code a lot as well

> +
> +	/* Scheduler helper */
> +	struct sdma_request	*(*validate_request)(struct sdma_channel *chan,
> +						     struct sdma_request *req);
> +
> +	/* Transfer Management Functions */
> +	int			(*channel_pause)(struct sdma_channel *chan);
> +	int			(*channel_resume)(struct sdma_channel *chan);
> +	int			(*channel_start)(struct sdma_channel *chan, struct sdma_desc *sdesc);
> +	int			(*channel_terminate)(struct sdma_channel *chan);
> +	size_t			(*channel_residue)(struct sdma_channel *chan);
> +};
> +
> +struct sdma {
> +	struct dma_device	ddev;
> +	struct sdma_ops		*ops;
> +
> +	struct dma_pool		*pool;
> +
> +	struct sdma_channel	*channels;
> +	int			channels_nr;
> +	struct sdma_request	*requests;
> +	int			requests_nr;
> +
> +	struct list_head	avail_chans;
> +	struct list_head	pend_reqs;
> +
> +	spinlock_t		lock;
> +
> +	unsigned long		private[];
> +};
> +
> +static inline struct sdma *to_sdma(struct dma_device *d)
> +{
> +	return container_of(d, struct sdma, ddev);
> +}
> +
> +static inline struct sdma_request *to_sdma_request(struct dma_chan *chan)
> +{
> +	return container_of(chan, struct sdma_request, vchan.chan);
> +}
> +
> +static inline struct sdma_desc *to_sdma_desc(struct dma_async_tx_descriptor *tx)
> +{
> +	return container_of(tx, struct sdma_desc, vdesc.tx);
> +}
> +
> +static inline void *sdma_priv(struct sdma *sdma)
> +{
> +	return (void*)sdma->private;
> +}
> +
> +static inline void sdma_set_chan_private(struct sdma *sdma, void *ptr)
> +{
> +	int i;
> +
> +	for (i = 0; i < sdma->channels_nr; i++) {
> +		struct sdma_channel *schan = &sdma->channels[i];
> +
> +		schan->private = ptr;
> +	}
> +}
> +
> +struct sdma_desc *sdma_report(struct sdma *sdma,
> +			      struct sdma_channel *chan,
> +			      enum sdma_report_status status);
> +
> +struct sdma *sdma_alloc(struct device *dev,
> +			unsigned int channels,
> +			unsigned int requests,
> +			ssize_t lli_size,
> +			ssize_t priv_size);
> +void sdma_free(struct sdma *sdma);
> +
> +int sdma_register(struct sdma *sdma,
> +		  struct sdma_ops *ops);
> +int sdma_unregister(struct sdma *sdma);
> +
> +#endif /* _SCHEDULED_DMA_H_ */
> -- 
> 2.3.3
> 
> --
> To unsubscribe from this list: send the line "unsubscribe dmaengine" in
> the body of a message to majordomo@xxxxxxxxxxxxxxx
> More majordomo info at  http://vger.kernel.org/majordomo-info.html

-- 
--
To unsubscribe from this list: send the line "unsubscribe dmaengine" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html




[Index of Archives]     [Linux Kernel]     [Linux ARM (vger)]     [Linux ARM MSM]     [Linux Omap]     [Linux Arm]     [Linux Tegra]     [Fedora ARM]     [Linux for Samsung SOC]     [eCos]     [Linux PCI]     [Linux Fastboot]     [Gcc Help]     [Git]     [DCCP]     [IETF Announce]     [Security]     [Linux MIPS]     [Yosemite Campsites]

  Powered by Linux