[alsa-devel] [PATCHv3] dmaengine: Add support for BCM2835

Russell King - ARM Linux linux at arm.linux.org.uk
Tue Nov 12 20:16:33 CET 2013


On Mon, Nov 11, 2013 at 11:05:21PM +0100, Florian Meier wrote:
> Add support for DMA controller of BCM2835 as used in the Raspberry Pi.
> Currently it only supports cyclic DMA.
> 
> Signed-off-by: Florian Meier <florian.meier at koalo.de>
> ---
> 
> Thank you for your comments!
> I hope I have now removed all leftovers of the sg struct.
> Regarding the endian-ness: I have not found any hint about that in the
> datasheet. Therefore, I chose uint32_t. If you think fixed le32 is more
> likely I will change it.

I guess it's easy to change later if needed; there's likely a large
number of drivers which fall into this same category.

> The PAD fields do not seem to be used, but the datasheet states they
> should be set to 0.

Ok.  This now looks a lot better, and is smaller too!  There's a few
issues which need to be resolved still...

> +struct bcm2835_desc {
> +	struct virt_dma_desc vd;
> +	enum dma_transfer_direction dir;
> +	dma_addr_t dev_addr;

I don't think you need dev_addr here anymore - it seems to only be used
within bcm2835_dma_prep_dma_cyclic().

> +static int bcm2835_dma_alloc_chan_resources(struct dma_chan *chan)
> +{
> +	struct bcm2835_chan *c = to_bcm2835_dma_chan(chan);
> +	int ret;
> +	struct bcm2835_dmadev *d = to_bcm2835_dma_dev(chan->device);
> +	uint32_t chans = d->chans_available;

Probably just uint32_t chans; here is sufficient.  Also, as you'll be
touching this area again, a minor comment to order the variable
declarations in a more tidy way here.

> +	int chanID = 0;

Is a channel ID of zero a legal channel number?

> +	unsigned long flags;
> +
> +	spin_lock_irqsave(&d->lock, flags);
> +
> +	chans = d->chans_available;
> +
> +	dev_dbg(c->vc.chan.device->dev,
> +			"allocating channel for %u\n", c->dma_sig);
> +
> +	/* do not use the FIQ and BULK channels */
> +	chans &= ~0xD;
> +
> +	if (!chans) {
> +		spin_unlock_irqrestore(&d->lock, flags);
> +		return -ENOMEM;
> +	}
> +
> +	/* return the ordinal of the first channel in the bitmap */
> +	chanID = __ffs(chans);
> +
> +	/* claim the channel */
> +	d->chans_available &= ~(1 << chanID);
> +
> +	c->dma_chan_base = BCM2835_DMA_CHANIO(d->dma_base, chanID);
> +	c->dma_irq_number = d->dma_irq_numbers[chanID];
> +	c->dma_ch = chanID;
> +
> +	ret = request_irq(c->dma_irq_number,
> +			bcm2835_dma_callback, 0, "DMA IRQ", c);
> +
> +	spin_unlock_irqrestore(&d->lock, flags);

Calling request_irq() from within a spinlocked region is not a nice thing
to do.  May I suggest an alternative coding for this function?

	int chanID = -1;

	dev_dbg(c->vc.chan.device->dev,
		"allocating channel for %u\n", c->dma_sig);

	spin_lock_irqsave(&d->lock, flags);

	chans = d->chans_available;
	if (chans) {
		/* return the ordinal of the first channel in the bitmap */
		chanID = __ffs(chans);

		d->chans_available &= ~(1 << chanID);
	}

	spin_unlock_irqrestore(&d->lock, flags);

	if (chanID == -1)
		return -ENOMEM;

	c->dma_chan_base = BCM2835_DMA_CHANIO(d->dma_base, chanID);
	c->dma_irq_number = d->dma_irq_numbers[chanID];
	c->dma_ch = chanID;

	ret = request_irq(c->dma_irq_number,
			bcm2835_dma_callback, 0, "DMA IRQ", c);

Now, don't forget to clean up if request_irq() fails...

	if (ret < 0) {
		spin_lock_irqsave(&d->lock, flags);
		d->chans_available |= 1 << chanID;
		spin_unlock_irqrestore(&d->lock, flags);
	}

	return ret;

How does that look?

> +
> +	if (ret < 0)
> +		return ret;
> +
> +	return 0;
> +}
> +
> +static void bcm2835_dma_free_chan_resources(struct dma_chan *chan)
> +{
> +	struct bcm2835_chan *c = to_bcm2835_dma_chan(chan);
> +	struct bcm2835_dmadev *d = to_bcm2835_dma_dev(chan->device);
> +	unsigned long flags;
> +
> +	spin_lock_irqsave(&d->lock, flags);
> +	vchan_free_chan_resources(&c->vc);
> +	d->chans_available |= 1 << c->dma_ch;
> +	free_irq(c->dma_irq_number, c);
> +	spin_unlock_irqrestore(&d->lock, flags);

A better ordering here would be:

	vchan_free_chan_resources(&c->vc);
	free_irq(c->dma_irq_number, c);

	spin_lock_irqsave(&d->lock, flags);
	d->chans_available |= 1 << c->dma_ch;
	spin_unlock_irqrestore(&d->lock, flags);

You don't need to call the first two under the spinlock - all you need to
protect is the read-modify-write of d->chans_available here and also in
the above function.

...
> +		/*
> +		 * Next block is the next frame.
> +		 * This DMA engine driver currently only supports cyclic DMA.
> +		 * Therefore, wrap around at number of frames.
> +		 */
> +		control_block->next = d->control_block_base_phys +
> +			sizeof(struct bcm2835_dma_cb) * ((frame + 1) % (d->frames));

Minor comment here - the parens around d->frames isn't required, and
wrapping this a little better would be nice.  I'd suggest moving
((frame + 1) % d->frames) onto the next line.

Other than those comments, it's looking really quite good!  Well done.


More information about the Alsa-devel mailing list