[PATCH 2/2] dmaengine: s3c24xx-dma: Add cyclic transfer support
Heiko Stübner
heiko at sntech.de
Sun May 18 16:03:17 PDT 2014
Am Sonntag, 18. Mai 2014, 23:44:36 schrieb Vasily Khoruzhick:
> Many audio interface drivers require support of cyclic transfers to work
> correctly, for example Samsung ASoC DMA driver. This patch adds support
> for cyclic transfers to the s3c24xx-dma driver
>
> Signed-off-by: Vasily Khoruzhick <anarsoul at gmail.com>
Really nice addition :-) .
I'm just not sure if the chunk of copy'n'pasted setup code is big enough to
warant a move to shared function or not.
Nevertheless,
Acked-by: Heiko Stuebner <heiko at sntech.de>
> ---
> drivers/dma/s3c24xx-dma.c | 111
> +++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 110
> insertions(+), 1 deletion(-)
>
> diff --git a/drivers/dma/s3c24xx-dma.c b/drivers/dma/s3c24xx-dma.c
> index 2167608..1e419fb 100644
> --- a/drivers/dma/s3c24xx-dma.c
> +++ b/drivers/dma/s3c24xx-dma.c
> @@ -164,6 +164,7 @@ struct s3c24xx_sg {
> * @disrcc: value for source control register
> * @didstc: value for destination control register
> * @dcon: base value for dcon register
> + * @cyclic: indicate cyclic transfer
> */
> struct s3c24xx_txd {
> struct virt_dma_desc vd;
> @@ -173,6 +174,7 @@ struct s3c24xx_txd {
> u32 disrcc;
> u32 didstc;
> u32 dcon;
> + bool cyclic;
> };
>
> struct s3c24xx_dma_chan;
> @@ -669,8 +671,10 @@ static irqreturn_t s3c24xx_dma_irq(int irq, void *data)
> /* when more sg's are in this txd, start the next one */
> if (!list_is_last(txd->at, &txd->dsg_list)) {
> txd->at = txd->at->next;
> + if (txd->cyclic)
> + vchan_cyclic_callback(&txd->vd);
> s3c24xx_dma_start_next_sg(s3cchan, txd);
> - } else {
> + } else if (!txd->cyclic) {
> s3cchan->at = NULL;
> vchan_cookie_complete(&txd->vd);
>
> @@ -682,6 +686,12 @@ static irqreturn_t s3c24xx_dma_irq(int irq, void *data)
> s3c24xx_dma_start_next_txd(s3cchan);
> else
> s3c24xx_dma_phy_free(s3cchan);
> + } else {
> + vchan_cyclic_callback(&txd->vd);
> +
> + /* Cyclic: reset at beginning */
> + txd->at = txd->dsg_list.next;
> + s3c24xx_dma_start_next_sg(s3cchan, txd);
> }
> }
> spin_unlock(&s3cchan->vc.lock);
> @@ -877,6 +887,103 @@ static struct dma_async_tx_descriptor
> *s3c24xx_dma_prep_memcpy( return vchan_tx_prep(&s3cchan->vc, &txd->vd,
> flags);
> }
>
> +static struct dma_async_tx_descriptor *s3c24xx_dma_prep_dma_cyclic(
> + struct dma_chan *chan, dma_addr_t addr, size_t size, size_t period,
> + enum dma_transfer_direction direction, unsigned long flags,
> + void *context)
> +{
> + struct s3c24xx_dma_chan *s3cchan = to_s3c24xx_dma_chan(chan);
> + struct s3c24xx_dma_engine *s3cdma = s3cchan->host;
> + const struct s3c24xx_dma_platdata *pdata = s3cdma->pdata;
> + struct s3c24xx_dma_channel *cdata = &pdata->channels[s3cchan->id];
> + struct s3c24xx_txd *txd;
> + struct s3c24xx_sg *dsg;
> + unsigned sg_len;
> + dma_addr_t slave_addr;
> + u32 hwcfg = 0;
> + int i;
> +
> + dev_dbg(&s3cdma->pdev->dev,
> + "prepare cyclic transaction of %d bytes with period %d from %s\n",
> + size, period, s3cchan->name);
> +
> + txd = s3c24xx_dma_get_txd();
> + if (!txd)
> + return NULL;
> +
> + txd->cyclic = 1;
> +
> + if (cdata->handshake)
> + txd->dcon |= S3C24XX_DCON_HANDSHAKE;
> +
> + switch (cdata->bus) {
> + case S3C24XX_DMA_APB:
> + txd->dcon |= S3C24XX_DCON_SYNC_PCLK;
> + hwcfg |= S3C24XX_DISRCC_LOC_APB;
> + break;
> + case S3C24XX_DMA_AHB:
> + txd->dcon |= S3C24XX_DCON_SYNC_HCLK;
> + hwcfg |= S3C24XX_DISRCC_LOC_AHB;
> + break;
> + }
> +
> + /*
> + * Always assume our peripheral desintation is a fixed
> + * address in memory.
> + */
> + hwcfg |= S3C24XX_DISRCC_INC_FIXED;
> +
> + /*
> + * Individual dma operations are requested by the slave,
> + * so serve only single atomic operations (S3C24XX_DCON_SERV_SINGLE).
> + */
> + txd->dcon |= S3C24XX_DCON_SERV_SINGLE;
> +
> + if (direction == DMA_MEM_TO_DEV) {
> + txd->disrcc = S3C24XX_DISRCC_LOC_AHB |
> + S3C24XX_DISRCC_INC_INCREMENT;
> + txd->didstc = hwcfg;
> + slave_addr = s3cchan->cfg.dst_addr;
> + txd->width = s3cchan->cfg.dst_addr_width;
> + } else if (direction == DMA_DEV_TO_MEM) {
> + txd->disrcc = hwcfg;
> + txd->didstc = S3C24XX_DIDSTC_LOC_AHB |
> + S3C24XX_DIDSTC_INC_INCREMENT;
> + slave_addr = s3cchan->cfg.src_addr;
> + txd->width = s3cchan->cfg.src_addr_width;
> + } else {
> + s3c24xx_dma_free_txd(txd);
> + dev_err(&s3cdma->pdev->dev,
> + "direction %d unsupported\n", direction);
> + return NULL;
> + }
> +
> + sg_len = size / period;
> +
> + for (i = 0; i < sg_len; i++) {
> + dsg = kzalloc(sizeof(*dsg), GFP_NOWAIT);
> + if (!dsg) {
> + s3c24xx_dma_free_txd(txd);
> + return NULL;
> + }
> + list_add_tail(&dsg->node, &txd->dsg_list);
> +
> + dsg->len = period;
> + /* Check last period length */
> + if (i == (sg_len - 1))
> + dsg->len = size - (period * i);
> + if (direction == DMA_MEM_TO_DEV) {
> + dsg->src_addr = addr + (period * i);
> + dsg->dst_addr = slave_addr;
> + } else { /* DMA_DEV_TO_MEM */
> + dsg->src_addr = slave_addr;
> + dsg->dst_addr = addr + (period * i);
> + }
> + }
> +
> + return vchan_tx_prep(&s3cchan->vc, &txd->vd, flags);
> +}
> +
> static struct dma_async_tx_descriptor *s3c24xx_dma_prep_slave_sg(
> struct dma_chan *chan, struct scatterlist *sgl,
> unsigned int sg_len, enum dma_transfer_direction direction,
> @@ -1197,6 +1304,7 @@ static int s3c24xx_dma_probe(struct platform_device
> *pdev)
>
> /* Initialize slave engine for SoC internal dedicated peripherals */
> dma_cap_set(DMA_SLAVE, s3cdma->slave.cap_mask);
> + dma_cap_set(DMA_CYCLIC, s3cdma->slave.cap_mask);
> dma_cap_set(DMA_PRIVATE, s3cdma->slave.cap_mask);
> s3cdma->slave.dev = &pdev->dev;
> s3cdma->slave.device_alloc_chan_resources =
> @@ -1206,6 +1314,7 @@ static int s3c24xx_dma_probe(struct platform_device
> *pdev) s3cdma->slave.device_tx_status = s3c24xx_dma_tx_status;
> s3cdma->slave.device_issue_pending = s3c24xx_dma_issue_pending;
> s3cdma->slave.device_prep_slave_sg = s3c24xx_dma_prep_slave_sg;
> + s3cdma->slave.device_prep_dma_cyclic = s3c24xx_dma_prep_dma_cyclic;
> s3cdma->slave.device_control = s3c24xx_dma_control;
>
> /* Register as many memcpy channels as there are physical channels */
More information about the linux-arm-kernel
mailing list