Skip to content

Commit 3b24c20

Browse files
Alban BedelVinod Koul
authored andcommitted
dmaengine: PL08x: Add cyclic transfer support
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 amba-pl08x driver. Signed-off-by: Alban Bedel <[email protected]> [tfiga: Rebase and slightly beautify the original patch.] Signed-off-by: Tomasz Figa <[email protected]> Acked-by: Linus Walleij <[email protected]> Signed-off-by: Vinod Koul <[email protected]>
1 parent f3287a5 commit 3b24c20

File tree

1 file changed

+118
-29
lines changed

1 file changed

+118
-29
lines changed

drivers/dma/amba-pl08x.c

Lines changed: 118 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -173,6 +173,7 @@ struct pl08x_sg {
173173
* @ccfg: config reg values for current txd
174174
* @done: this marks completed descriptors, which should not have their
175175
* mux released.
176+
* @cyclic: indicate cyclic transfers
176177
*/
177178
struct pl08x_txd {
178179
struct virt_dma_desc vd;
@@ -187,6 +188,7 @@ struct pl08x_txd {
187188
*/
188189
u32 ccfg;
189190
bool done;
191+
bool cyclic;
190192
};
191193

192194
/**
@@ -574,9 +576,9 @@ static u32 pl08x_getbytes_chan(struct pl08x_dma_chan *plchan)
574576
bytes += get_bytes_in_cctl(llis_va[PL080_LLI_CCTL]);
575577

576578
/*
577-
* A LLI pointer of 0 terminates the LLI list
579+
* A LLI pointer going backward terminates the LLI list
578580
*/
579-
if (!llis_va[PL080_LLI_LLI])
581+
if (llis_va[PL080_LLI_LLI] <= clli)
580582
break;
581583
}
582584

@@ -1125,10 +1127,16 @@ static int pl08x_fill_llis_for_desc(struct pl08x_driver_data *pl08x,
11251127

11261128
llis_va = txd->llis_va;
11271129
last_lli = llis_va + (num_llis - 1) * pl08x->lli_words;
1128-
/* The final LLI terminates the LLI. */
1129-
last_lli[PL080_LLI_LLI] = 0;
1130-
/* The final LLI element shall also fire an interrupt. */
1131-
last_lli[PL080_LLI_CCTL] |= PL080_CONTROL_TC_IRQ_EN;
1130+
1131+
if (txd->cyclic) {
1132+
/* Link back to the first LLI. */
1133+
last_lli[PL080_LLI_LLI] = txd->llis_bus | bd.lli_bus;
1134+
} else {
1135+
/* The final LLI terminates the LLI. */
1136+
last_lli[PL080_LLI_LLI] = 0;
1137+
/* The final LLI element shall also fire an interrupt. */
1138+
last_lli[PL080_LLI_CCTL] |= PL080_CONTROL_TC_IRQ_EN;
1139+
}
11321140

11331141
pl08x_dump_lli(pl08x, llis_va, num_llis);
11341142

@@ -1513,25 +1521,19 @@ static struct dma_async_tx_descriptor *pl08x_prep_dma_memcpy(
15131521
return vchan_tx_prep(&plchan->vc, &txd->vd, flags);
15141522
}
15151523

1516-
static struct dma_async_tx_descriptor *pl08x_prep_slave_sg(
1517-
struct dma_chan *chan, struct scatterlist *sgl,
1518-
unsigned int sg_len, enum dma_transfer_direction direction,
1519-
unsigned long flags, void *context)
1524+
static struct pl08x_txd *pl08x_init_txd(
1525+
struct dma_chan *chan,
1526+
enum dma_transfer_direction direction,
1527+
dma_addr_t *slave_addr)
15201528
{
15211529
struct pl08x_dma_chan *plchan = to_pl08x_chan(chan);
15221530
struct pl08x_driver_data *pl08x = plchan->host;
15231531
struct pl08x_txd *txd;
1524-
struct pl08x_sg *dsg;
1525-
struct scatterlist *sg;
15261532
enum dma_slave_buswidth addr_width;
1527-
dma_addr_t slave_addr;
15281533
int ret, tmp;
15291534
u8 src_buses, dst_buses;
15301535
u32 maxburst, cctl;
15311536

1532-
dev_dbg(&pl08x->adev->dev, "%s prepare transaction of %d bytes from %s\n",
1533-
__func__, sg_dma_len(sgl), plchan->name);
1534-
15351537
txd = pl08x_get_txd(plchan);
15361538
if (!txd) {
15371539
dev_err(&pl08x->adev->dev, "%s no txd\n", __func__);
@@ -1545,14 +1547,14 @@ static struct dma_async_tx_descriptor *pl08x_prep_slave_sg(
15451547
*/
15461548
if (direction == DMA_MEM_TO_DEV) {
15471549
cctl = PL080_CONTROL_SRC_INCR;
1548-
slave_addr = plchan->cfg.dst_addr;
1550+
*slave_addr = plchan->cfg.dst_addr;
15491551
addr_width = plchan->cfg.dst_addr_width;
15501552
maxburst = plchan->cfg.dst_maxburst;
15511553
src_buses = pl08x->mem_buses;
15521554
dst_buses = plchan->cd->periph_buses;
15531555
} else if (direction == DMA_DEV_TO_MEM) {
15541556
cctl = PL080_CONTROL_DST_INCR;
1555-
slave_addr = plchan->cfg.src_addr;
1557+
*slave_addr = plchan->cfg.src_addr;
15561558
addr_width = plchan->cfg.src_addr_width;
15571559
maxburst = plchan->cfg.src_maxburst;
15581560
src_buses = plchan->cd->periph_buses;
@@ -1601,24 +1603,107 @@ static struct dma_async_tx_descriptor *pl08x_prep_slave_sg(
16011603
else
16021604
txd->ccfg |= plchan->signal << PL080_CONFIG_SRC_SEL_SHIFT;
16031605

1606+
return txd;
1607+
}
1608+
1609+
static int pl08x_tx_add_sg(struct pl08x_txd *txd,
1610+
enum dma_transfer_direction direction,
1611+
dma_addr_t slave_addr,
1612+
dma_addr_t buf_addr,
1613+
unsigned int len)
1614+
{
1615+
struct pl08x_sg *dsg;
1616+
1617+
dsg = kzalloc(sizeof(struct pl08x_sg), GFP_NOWAIT);
1618+
if (!dsg)
1619+
return -ENOMEM;
1620+
1621+
list_add_tail(&dsg->node, &txd->dsg_list);
1622+
1623+
dsg->len = len;
1624+
if (direction == DMA_MEM_TO_DEV) {
1625+
dsg->src_addr = buf_addr;
1626+
dsg->dst_addr = slave_addr;
1627+
} else {
1628+
dsg->src_addr = slave_addr;
1629+
dsg->dst_addr = buf_addr;
1630+
}
1631+
1632+
return 0;
1633+
}
1634+
1635+
static struct dma_async_tx_descriptor *pl08x_prep_slave_sg(
1636+
struct dma_chan *chan, struct scatterlist *sgl,
1637+
unsigned int sg_len, enum dma_transfer_direction direction,
1638+
unsigned long flags, void *context)
1639+
{
1640+
struct pl08x_dma_chan *plchan = to_pl08x_chan(chan);
1641+
struct pl08x_driver_data *pl08x = plchan->host;
1642+
struct pl08x_txd *txd;
1643+
struct scatterlist *sg;
1644+
int ret, tmp;
1645+
dma_addr_t slave_addr;
1646+
1647+
dev_dbg(&pl08x->adev->dev, "%s prepare transaction of %d bytes from %s\n",
1648+
__func__, sg_dma_len(sgl), plchan->name);
1649+
1650+
txd = pl08x_init_txd(chan, direction, &slave_addr);
1651+
if (!txd)
1652+
return NULL;
1653+
16041654
for_each_sg(sgl, sg, sg_len, tmp) {
1605-
dsg = kzalloc(sizeof(struct pl08x_sg), GFP_NOWAIT);
1606-
if (!dsg) {
1655+
ret = pl08x_tx_add_sg(txd, direction, slave_addr,
1656+
sg_dma_address(sg),
1657+
sg_dma_len(sg));
1658+
if (ret) {
16071659
pl08x_release_mux(plchan);
16081660
pl08x_free_txd(pl08x, txd);
16091661
dev_err(&pl08x->adev->dev, "%s no mem for pl080 sg\n",
16101662
__func__);
16111663
return NULL;
16121664
}
1613-
list_add_tail(&dsg->node, &txd->dsg_list);
1665+
}
16141666

1615-
dsg->len = sg_dma_len(sg);
1616-
if (direction == DMA_MEM_TO_DEV) {
1617-
dsg->src_addr = sg_dma_address(sg);
1618-
dsg->dst_addr = slave_addr;
1619-
} else {
1620-
dsg->src_addr = slave_addr;
1621-
dsg->dst_addr = sg_dma_address(sg);
1667+
ret = pl08x_fill_llis_for_desc(plchan->host, txd);
1668+
if (!ret) {
1669+
pl08x_release_mux(plchan);
1670+
pl08x_free_txd(pl08x, txd);
1671+
return NULL;
1672+
}
1673+
1674+
return vchan_tx_prep(&plchan->vc, &txd->vd, flags);
1675+
}
1676+
1677+
static struct dma_async_tx_descriptor *pl08x_prep_dma_cyclic(
1678+
struct dma_chan *chan, dma_addr_t buf_addr, size_t buf_len,
1679+
size_t period_len, enum dma_transfer_direction direction,
1680+
unsigned long flags, void *context)
1681+
{
1682+
struct pl08x_dma_chan *plchan = to_pl08x_chan(chan);
1683+
struct pl08x_driver_data *pl08x = plchan->host;
1684+
struct pl08x_txd *txd;
1685+
int ret, tmp;
1686+
dma_addr_t slave_addr;
1687+
1688+
dev_dbg(&pl08x->adev->dev,
1689+
"%s prepare cyclic transaction of %d/%d bytes %s %s\n",
1690+
__func__, period_len, buf_len,
1691+
direction == DMA_MEM_TO_DEV ? "to" : "from",
1692+
plchan->name);
1693+
1694+
txd = pl08x_init_txd(chan, direction, &slave_addr);
1695+
if (!txd)
1696+
return NULL;
1697+
1698+
txd->cyclic = true;
1699+
txd->cctl |= PL080_CONTROL_TC_IRQ_EN;
1700+
for (tmp = 0; tmp < buf_len; tmp += period_len) {
1701+
ret = pl08x_tx_add_sg(txd, direction, slave_addr,
1702+
buf_addr + tmp, period_len);
1703+
if (ret) {
1704+
pl08x_release_mux(plchan);
1705+
pl08x_free_txd(pl08x, txd);
1706+
return NULL;
16221707
}
16231708
}
16241709

@@ -1761,7 +1846,9 @@ static irqreturn_t pl08x_irq(int irq, void *dev)
17611846

17621847
spin_lock(&plchan->vc.lock);
17631848
tx = plchan->at;
1764-
if (tx) {
1849+
if (tx && tx->cyclic) {
1850+
vchan_cyclic_callback(&tx->vd);
1851+
} else if (tx) {
17651852
plchan->at = NULL;
17661853
/*
17671854
* This descriptor is done, release its mux
@@ -1983,13 +2070,15 @@ static int pl08x_probe(struct amba_device *adev, const struct amba_id *id)
19832070

19842071
/* Initialize slave engine */
19852072
dma_cap_set(DMA_SLAVE, pl08x->slave.cap_mask);
2073+
dma_cap_set(DMA_CYCLIC, pl08x->slave.cap_mask);
19862074
pl08x->slave.dev = &adev->dev;
19872075
pl08x->slave.device_alloc_chan_resources = pl08x_alloc_chan_resources;
19882076
pl08x->slave.device_free_chan_resources = pl08x_free_chan_resources;
19892077
pl08x->slave.device_prep_dma_interrupt = pl08x_prep_dma_interrupt;
19902078
pl08x->slave.device_tx_status = pl08x_dma_tx_status;
19912079
pl08x->slave.device_issue_pending = pl08x_issue_pending;
19922080
pl08x->slave.device_prep_slave_sg = pl08x_prep_slave_sg;
2081+
pl08x->slave.device_prep_dma_cyclic = pl08x_prep_dma_cyclic;
19932082
pl08x->slave.device_control = pl08x_control;
19942083

19952084
/* Get the platform data */

0 commit comments

Comments
 (0)