diff options
| author | Russell King <rmk+kernel@arm.linux.org.uk> | 2012-05-26 09:42:23 -0400 |
|---|---|---|
| committer | Russell King <rmk+kernel@arm.linux.org.uk> | 2012-07-01 09:16:01 -0400 |
| commit | 18536134abd6b8157d5cb11162606cc264d07232 (patch) | |
| tree | da683ed8dd00138ac23884ebfaae1d4dccfb763d | |
| parent | 879f127bb2d0b604cf49f7682c0431d47f42f8f9 (diff) | |
dmaengine: PL08x: convert to use vchan done list
Convert to use the virtual dma channel done list, tasklet, and
descriptor freeing.
Acked-by: Linus Walleij <linus.walleij@linaro.org>
Tested-by: Linus Walleij <linus.walleij@linaro.org>
Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>
| -rw-r--r-- | drivers/dma/amba-pl08x.c | 135 |
1 files changed, 54 insertions, 81 deletions
diff --git a/drivers/dma/amba-pl08x.c b/drivers/dma/amba-pl08x.c index 5333a91518ed..6a35e37d14bc 100644 --- a/drivers/dma/amba-pl08x.c +++ b/drivers/dma/amba-pl08x.c | |||
| @@ -167,16 +167,16 @@ struct pl08x_sg { | |||
| 167 | /** | 167 | /** |
| 168 | * struct pl08x_txd - wrapper for struct dma_async_tx_descriptor | 168 | * struct pl08x_txd - wrapper for struct dma_async_tx_descriptor |
| 169 | * @vd: virtual DMA descriptor | 169 | * @vd: virtual DMA descriptor |
| 170 | * @node: node for txd list for channels | ||
| 171 | * @dsg_list: list of children sg's | 170 | * @dsg_list: list of children sg's |
| 172 | * @llis_bus: DMA memory address (physical) start for the LLIs | 171 | * @llis_bus: DMA memory address (physical) start for the LLIs |
| 173 | * @llis_va: virtual memory address start for the LLIs | 172 | * @llis_va: virtual memory address start for the LLIs |
| 174 | * @cctl: control reg values for current txd | 173 | * @cctl: control reg values for current txd |
| 175 | * @ccfg: config reg values for current txd | 174 | * @ccfg: config reg values for current txd |
| 175 | * @done: this marks completed descriptors, which should not have their | ||
| 176 | * mux released. | ||
| 176 | */ | 177 | */ |
| 177 | struct pl08x_txd { | 178 | struct pl08x_txd { |
| 178 | struct virt_dma_desc vd; | 179 | struct virt_dma_desc vd; |
| 179 | struct list_head node; | ||
| 180 | struct list_head dsg_list; | 180 | struct list_head dsg_list; |
| 181 | dma_addr_t llis_bus; | 181 | dma_addr_t llis_bus; |
| 182 | struct pl08x_lli *llis_va; | 182 | struct pl08x_lli *llis_va; |
| @@ -187,6 +187,7 @@ struct pl08x_txd { | |||
| 187 | * trigger this txd. Other registers are in llis_va[0]. | 187 | * trigger this txd. Other registers are in llis_va[0]. |
| 188 | */ | 188 | */ |
| 189 | u32 ccfg; | 189 | u32 ccfg; |
| 190 | bool done; | ||
| 190 | }; | 191 | }; |
| 191 | 192 | ||
| 192 | /** | 193 | /** |
| @@ -211,11 +212,9 @@ enum pl08x_dma_chan_state { | |||
| 211 | * struct pl08x_dma_chan - this structure wraps a DMA ENGINE channel | 212 | * struct pl08x_dma_chan - this structure wraps a DMA ENGINE channel |
| 212 | * @vc: wrappped virtual channel | 213 | * @vc: wrappped virtual channel |
| 213 | * @phychan: the physical channel utilized by this channel, if there is one | 214 | * @phychan: the physical channel utilized by this channel, if there is one |
| 214 | * @tasklet: tasklet scheduled by the IRQ to handle actual work etc | ||
| 215 | * @name: name of channel | 215 | * @name: name of channel |
| 216 | * @cd: channel platform data | 216 | * @cd: channel platform data |
| 217 | * @runtime_addr: address for RX/TX according to the runtime config | 217 | * @runtime_addr: address for RX/TX according to the runtime config |
| 218 | * @done_list: list of completed transactions | ||
| 219 | * @at: active transaction on this channel | 218 | * @at: active transaction on this channel |
| 220 | * @lock: a lock for this channel data | 219 | * @lock: a lock for this channel data |
| 221 | * @host: a pointer to the host (internal use) | 220 | * @host: a pointer to the host (internal use) |
| @@ -227,11 +226,9 @@ enum pl08x_dma_chan_state { | |||
| 227 | struct pl08x_dma_chan { | 226 | struct pl08x_dma_chan { |
| 228 | struct virt_dma_chan vc; | 227 | struct virt_dma_chan vc; |
| 229 | struct pl08x_phy_chan *phychan; | 228 | struct pl08x_phy_chan *phychan; |
| 230 | struct tasklet_struct tasklet; | ||
| 231 | const char *name; | 229 | const char *name; |
| 232 | const struct pl08x_channel_data *cd; | 230 | const struct pl08x_channel_data *cd; |
| 233 | struct dma_slave_config cfg; | 231 | struct dma_slave_config cfg; |
| 234 | struct list_head done_list; | ||
| 235 | struct pl08x_txd *at; | 232 | struct pl08x_txd *at; |
| 236 | struct pl08x_driver_data *host; | 233 | struct pl08x_driver_data *host; |
| 237 | enum pl08x_dma_chan_state state; | 234 | enum pl08x_dma_chan_state state; |
| @@ -1084,6 +1081,52 @@ static void pl08x_free_txd(struct pl08x_driver_data *pl08x, | |||
| 1084 | kfree(txd); | 1081 | kfree(txd); |
| 1085 | } | 1082 | } |
| 1086 | 1083 | ||
| 1084 | static void pl08x_unmap_buffers(struct pl08x_txd *txd) | ||
| 1085 | { | ||
| 1086 | struct device *dev = txd->vd.tx.chan->device->dev; | ||
| 1087 | struct pl08x_sg *dsg; | ||
| 1088 | |||
| 1089 | if (!(txd->vd.tx.flags & DMA_COMPL_SKIP_SRC_UNMAP)) { | ||
| 1090 | if (txd->vd.tx.flags & DMA_COMPL_SRC_UNMAP_SINGLE) | ||
| 1091 | list_for_each_entry(dsg, &txd->dsg_list, node) | ||
| 1092 | dma_unmap_single(dev, dsg->src_addr, dsg->len, | ||
| 1093 | DMA_TO_DEVICE); | ||
| 1094 | else { | ||
| 1095 | list_for_each_entry(dsg, &txd->dsg_list, node) | ||
| 1096 | dma_unmap_page(dev, dsg->src_addr, dsg->len, | ||
| 1097 | DMA_TO_DEVICE); | ||
| 1098 | } | ||
| 1099 | } | ||
| 1100 | if (!(txd->vd.tx.flags & DMA_COMPL_SKIP_DEST_UNMAP)) { | ||
| 1101 | if (txd->vd.tx.flags & DMA_COMPL_DEST_UNMAP_SINGLE) | ||
| 1102 | list_for_each_entry(dsg, &txd->dsg_list, node) | ||
| 1103 | dma_unmap_single(dev, dsg->dst_addr, dsg->len, | ||
| 1104 | DMA_FROM_DEVICE); | ||
| 1105 | else | ||
| 1106 | list_for_each_entry(dsg, &txd->dsg_list, node) | ||
| 1107 | dma_unmap_page(dev, dsg->dst_addr, dsg->len, | ||
| 1108 | DMA_FROM_DEVICE); | ||
| 1109 | } | ||
| 1110 | } | ||
| 1111 | |||
| 1112 | static void pl08x_desc_free(struct virt_dma_desc *vd) | ||
| 1113 | { | ||
| 1114 | struct pl08x_txd *txd = to_pl08x_txd(&vd->tx); | ||
| 1115 | struct pl08x_dma_chan *plchan = to_pl08x_chan(vd->tx.chan); | ||
| 1116 | struct pl08x_driver_data *pl08x = plchan->host; | ||
| 1117 | unsigned long flags; | ||
| 1118 | |||
| 1119 | if (!plchan->slave) | ||
| 1120 | pl08x_unmap_buffers(txd); | ||
| 1121 | |||
| 1122 | if (!txd->done) | ||
| 1123 | pl08x_release_mux(plchan); | ||
| 1124 | |||
| 1125 | spin_lock_irqsave(&pl08x->lock, flags); | ||
| 1126 | pl08x_free_txd(plchan->host, txd); | ||
| 1127 | spin_unlock_irqrestore(&pl08x->lock, flags); | ||
| 1128 | } | ||
| 1129 | |||
| 1087 | static void pl08x_free_txd_list(struct pl08x_driver_data *pl08x, | 1130 | static void pl08x_free_txd_list(struct pl08x_driver_data *pl08x, |
| 1088 | struct pl08x_dma_chan *plchan) | 1131 | struct pl08x_dma_chan *plchan) |
| 1089 | { | 1132 | { |
| @@ -1094,9 +1137,8 @@ static void pl08x_free_txd_list(struct pl08x_driver_data *pl08x, | |||
| 1094 | 1137 | ||
| 1095 | while (!list_empty(&head)) { | 1138 | while (!list_empty(&head)) { |
| 1096 | txd = list_first_entry(&head, struct pl08x_txd, vd.node); | 1139 | txd = list_first_entry(&head, struct pl08x_txd, vd.node); |
| 1097 | pl08x_release_mux(plchan); | ||
| 1098 | list_del(&txd->vd.node); | 1140 | list_del(&txd->vd.node); |
| 1099 | pl08x_free_txd(pl08x, txd); | 1141 | pl08x_desc_free(&txd->vd); |
| 1100 | } | 1142 | } |
| 1101 | } | 1143 | } |
| 1102 | 1144 | ||
| @@ -1541,9 +1583,7 @@ static int pl08x_control(struct dma_chan *chan, enum dma_ctrl_cmd cmd, | |||
| 1541 | } | 1583 | } |
| 1542 | /* Dequeue jobs and free LLIs */ | 1584 | /* Dequeue jobs and free LLIs */ |
| 1543 | if (plchan->at) { | 1585 | if (plchan->at) { |
| 1544 | /* Killing this one off, release its mux */ | 1586 | pl08x_desc_free(&plchan->at->vd); |
| 1545 | pl08x_release_mux(plchan); | ||
| 1546 | pl08x_free_txd(pl08x, plchan->at); | ||
| 1547 | plchan->at = NULL; | 1587 | plchan->at = NULL; |
| 1548 | } | 1588 | } |
| 1549 | /* Dequeue jobs not yet fired as well */ | 1589 | /* Dequeue jobs not yet fired as well */ |
| @@ -1600,68 +1640,6 @@ static void pl08x_ensure_on(struct pl08x_driver_data *pl08x) | |||
| 1600 | writel(PL080_CONFIG_ENABLE, pl08x->base + PL080_CONFIG); | 1640 | writel(PL080_CONFIG_ENABLE, pl08x->base + PL080_CONFIG); |
| 1601 | } | 1641 | } |
| 1602 | 1642 | ||
| 1603 | static void pl08x_unmap_buffers(struct pl08x_txd *txd) | ||
| 1604 | { | ||
| 1605 | struct device *dev = txd->vd.tx.chan->device->dev; | ||
| 1606 | struct pl08x_sg *dsg; | ||
| 1607 | |||
| 1608 | if (!(txd->vd.tx.flags & DMA_COMPL_SKIP_SRC_UNMAP)) { | ||
| 1609 | if (txd->vd.tx.flags & DMA_COMPL_SRC_UNMAP_SINGLE) | ||
| 1610 | list_for_each_entry(dsg, &txd->dsg_list, node) | ||
| 1611 | dma_unmap_single(dev, dsg->src_addr, dsg->len, | ||
| 1612 | DMA_TO_DEVICE); | ||
| 1613 | else { | ||
| 1614 | list_for_each_entry(dsg, &txd->dsg_list, node) | ||
| 1615 | dma_unmap_page(dev, dsg->src_addr, dsg->len, | ||
| 1616 | DMA_TO_DEVICE); | ||
| 1617 | } | ||
| 1618 | } | ||
| 1619 | if (!(txd->vd.tx.flags & DMA_COMPL_SKIP_DEST_UNMAP)) { | ||
| 1620 | if (txd->vd.tx.flags & DMA_COMPL_DEST_UNMAP_SINGLE) | ||
| 1621 | list_for_each_entry(dsg, &txd->dsg_list, node) | ||
| 1622 | dma_unmap_single(dev, dsg->dst_addr, dsg->len, | ||
| 1623 | DMA_FROM_DEVICE); | ||
| 1624 | else | ||
| 1625 | list_for_each_entry(dsg, &txd->dsg_list, node) | ||
| 1626 | dma_unmap_page(dev, dsg->dst_addr, dsg->len, | ||
| 1627 | DMA_FROM_DEVICE); | ||
| 1628 | } | ||
| 1629 | } | ||
| 1630 | |||
| 1631 | static void pl08x_tasklet(unsigned long data) | ||
| 1632 | { | ||
| 1633 | struct pl08x_dma_chan *plchan = (struct pl08x_dma_chan *) data; | ||
| 1634 | struct pl08x_driver_data *pl08x = plchan->host; | ||
| 1635 | unsigned long flags; | ||
| 1636 | LIST_HEAD(head); | ||
| 1637 | |||
| 1638 | spin_lock_irqsave(&plchan->vc.lock, flags); | ||
| 1639 | list_splice_tail_init(&plchan->done_list, &head); | ||
| 1640 | spin_unlock_irqrestore(&plchan->vc.lock, flags); | ||
| 1641 | |||
| 1642 | while (!list_empty(&head)) { | ||
| 1643 | struct pl08x_txd *txd = list_first_entry(&head, | ||
| 1644 | struct pl08x_txd, node); | ||
| 1645 | dma_async_tx_callback callback = txd->vd.tx.callback; | ||
| 1646 | void *callback_param = txd->vd.tx.callback_param; | ||
| 1647 | |||
| 1648 | list_del(&txd->node); | ||
| 1649 | |||
| 1650 | /* Don't try to unmap buffers on slave channels */ | ||
| 1651 | if (!plchan->slave) | ||
| 1652 | pl08x_unmap_buffers(txd); | ||
| 1653 | |||
| 1654 | /* Free the descriptor */ | ||
| 1655 | spin_lock_irqsave(&plchan->vc.lock, flags); | ||
| 1656 | pl08x_free_txd(pl08x, txd); | ||
| 1657 | spin_unlock_irqrestore(&plchan->vc.lock, flags); | ||
| 1658 | |||
| 1659 | /* Callback to signal completion */ | ||
| 1660 | if (callback) | ||
| 1661 | callback(callback_param); | ||
| 1662 | } | ||
| 1663 | } | ||
| 1664 | |||
| 1665 | static irqreturn_t pl08x_irq(int irq, void *dev) | 1643 | static irqreturn_t pl08x_irq(int irq, void *dev) |
| 1666 | { | 1644 | { |
| 1667 | struct pl08x_driver_data *pl08x = dev; | 1645 | struct pl08x_driver_data *pl08x = dev; |
| @@ -1704,8 +1682,8 @@ static irqreturn_t pl08x_irq(int irq, void *dev) | |||
| 1704 | * reservation. | 1682 | * reservation. |
| 1705 | */ | 1683 | */ |
| 1706 | pl08x_release_mux(plchan); | 1684 | pl08x_release_mux(plchan); |
| 1707 | dma_cookie_complete(&tx->vd.tx); | 1685 | tx->done = true; |
| 1708 | list_add_tail(&tx->node, &plchan->done_list); | 1686 | vchan_cookie_complete(&tx->vd); |
| 1709 | 1687 | ||
| 1710 | /* | 1688 | /* |
| 1711 | * And start the next descriptor (if any), | 1689 | * And start the next descriptor (if any), |
| @@ -1718,8 +1696,6 @@ static irqreturn_t pl08x_irq(int irq, void *dev) | |||
| 1718 | } | 1696 | } |
| 1719 | spin_unlock(&plchan->vc.lock); | 1697 | spin_unlock(&plchan->vc.lock); |
| 1720 | 1698 | ||
| 1721 | /* Schedule tasklet on this channel */ | ||
| 1722 | tasklet_schedule(&plchan->tasklet); | ||
| 1723 | mask |= (1 << i); | 1699 | mask |= (1 << i); |
| 1724 | } | 1700 | } |
| 1725 | } | 1701 | } |
| @@ -1779,10 +1755,7 @@ static int pl08x_dma_init_virtual_channels(struct pl08x_driver_data *pl08x, | |||
| 1779 | "initialize virtual channel \"%s\"\n", | 1755 | "initialize virtual channel \"%s\"\n", |
| 1780 | chan->name); | 1756 | chan->name); |
| 1781 | 1757 | ||
| 1782 | INIT_LIST_HEAD(&chan->done_list); | 1758 | chan->vc.desc_free = pl08x_desc_free; |
| 1783 | tasklet_init(&chan->tasklet, pl08x_tasklet, | ||
| 1784 | (unsigned long) chan); | ||
| 1785 | |||
| 1786 | vchan_init(&chan->vc, dmadev); | 1759 | vchan_init(&chan->vc, dmadev); |
| 1787 | } | 1760 | } |
| 1788 | dev_info(&pl08x->adev->dev, "initialized %d virtual %s channels\n", | 1761 | dev_info(&pl08x->adev->dev, "initialized %d virtual %s channels\n", |
