On Thu, May 21, 2015 at 01:45:27PM -0300, Emilio López wrote:
> Hi Maxime, Vinod,
>
> El 20/05/15 a las 18:17, Maxime Ripard escibió:
> >>>+static struct dma_async_tx_descriptor *
> >>>+sun4i_dma_prep_dma_cyclic(struct dma_chan *chan, dma_addr_t buf, size_t
> >>>len,
> >>>+ size_t period_len, enum dma_transfer_direction dir,
> >>>+ unsigned long flags)
> >>>+{
> >>>+ struct sun4i_dma_vchan *vchan = to_sun4i_dma_vchan(chan);
> >>>+ struct dma_slave_config *sconfig = &vchan->cfg;
> >>>+ struct sun4i_dma_promise *promise;
> >>>+ struct sun4i_dma_contract *contract;
> >>>+ dma_addr_t src, dest;
> >>>+ u32 endpoints;
> >>>+ int nr_periods, offset, plength, i;
> >>>+
> >>>+ if (!is_slave_direction(dir)) {
> >>>+ dev_err(chan2dev(chan), "Invalid DMA direction\n");
> >>>+ return NULL;
> >>>+ }
> >>>+
> >>>+ if (vchan->is_dedicated) {
> >>>+ /*
> >>>+ * As we are using this just for audio data, we need to use
> >>>+ * normal DMA. There is nothing stopping us from supporting
> >>>+ * dedicated DMA here as well, so if a client comes up and
> >>>+ * requires it, it will be simple to implement it.
> >>>+ */
> >>>+ dev_err(chan2dev(chan),
> >>>+ "Cyclic transfers are only supported on Normal DMA\n");
> >>>+ return NULL;
> >>>+ }
> >>>+
> >>>+ contract = generate_dma_contract();
> >>>+ if (!contract)
> >>>+ return NULL;
> >>>+
> >>>+ contract->is_cyclic = 1;
> >>>+
> >>>+ /* Figure out the endpoints and the address we need */
> >>>+ if (dir == DMA_MEM_TO_DEV) {
> >>>+ src = buf;
> >>>+ dest = sconfig->dst_addr;
> >>>+ endpoints = NDMA_CFG_SRC_DRQ_TYPE(NDMA_DRQ_TYPE_SDRAM) |
> >>>+ NDMA_CFG_DEST_DRQ_TYPE(vchan->endpoint) |
> >>>+ NDMA_CFG_DEST_FIXED_ADDR;
> >>>+ } else {
> >>>+ src = sconfig->src_addr;
> >>>+ dest = buf;
> >>>+ endpoints = NDMA_CFG_SRC_DRQ_TYPE(vchan->endpoint) |
> >>>+ NDMA_CFG_SRC_FIXED_ADDR |
> >>>+ NDMA_CFG_DEST_DRQ_TYPE(NDMA_DRQ_TYPE_SDRAM);
> >>>+ }
> >>>+
> >>>+ /*
> >>>+ * We will be using half done interrupts to make two periods
> >>>+ * out of a promise, so we need to program the DMA engine less
> >>>+ * often
> >>>+ */
> >>>+ nr_periods = DIV_ROUND_UP(len / period_len, 2);
> >>
> >>and why is that.. why don't you use actual period count here?
> >
> >I must admit I don't really know on this one.
> >
> >Emilio?
>
> You mean why is it that I chose to divide "len / period_len" (is
> there some other way to get period count that I'm missing?) by 2 and
> use half done interrupts? The engine can interrupt on half-transfer,
> so we can use this feature to program the engine half as often as if
> we didn't use it (keep in mind the hardware doesn't support linked
> lists).
>
> Say you have a set of periods (| marks the start/end, I for
> interrupt, P for programming the engine to do a new transfer), the
> easy but slow way would be to do
>
> |---|---|---|---| (periods / promises)
> P I,P I,P I,P I
>
> Using half transfer interrupts you can do
>
> |-------|-------| (promises as configured on hw)
> |---|---|---|---| (periods)
> P I I,P I I
>
> Which requires half the engine programming for the same functionality.
>
> Feel free to include these drawings on the comment if you think
> they'll help.
That explains it then, please do add this bit to driver documentation
>
> >>>+static struct dma_async_tx_descriptor *
> >>>+sun4i_dma_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 sun4i_dma_vchan *vchan = to_sun4i_dma_vchan(chan);
> >>>+ struct dma_slave_config *sconfig = &vchan->cfg;
> >>>+ struct sun4i_dma_promise *promise;
> >>>+ struct sun4i_dma_contract *contract;
> >>>+ struct scatterlist *sg;
> >>>+ dma_addr_t srcaddr, dstaddr;
> >>>+ u32 endpoints, para;
> >>>+ int i;
> >>>+
> >>>+ if (!sgl)
> >>>+ return NULL;
> >>>+
> >>>+ if (!is_slave_direction(dir)) {
> >>>+ dev_err(chan2dev(chan), "Invalid DMA direction\n");
> >>>+ return NULL;
> >>>+ }
> >>>+
> >>>+ contract = generate_dma_contract();
> >>>+ if (!contract)
> >>>+ return NULL;
> >>>+
> >>>+ /* Figure out endpoints */
> >>>+ if (vchan->is_dedicated && dir == DMA_MEM_TO_DEV) {
> >>>+ endpoints = DDMA_CFG_SRC_DRQ_TYPE(DDMA_DRQ_TYPE_SDRAM) |
> >>>+ DDMA_CFG_SRC_ADDR_MODE(DDMA_ADDR_MODE_LINEAR) |
> >>>+ DDMA_CFG_DEST_DRQ_TYPE(vchan->endpoint) |
> >>>+ DDMA_CFG_DEST_ADDR_MODE(DDMA_ADDR_MODE_IO);
> >>>+ } else if (!vchan->is_dedicated && dir == DMA_MEM_TO_DEV) {
> >>>+ endpoints = NDMA_CFG_SRC_DRQ_TYPE(NDMA_DRQ_TYPE_SDRAM) |
> >>>+ NDMA_CFG_DEST_DRQ_TYPE(vchan->endpoint) |
> >>>+ NDMA_CFG_DEST_FIXED_ADDR;
> >>>+ } else if (vchan->is_dedicated) {
> >>>+ endpoints = DDMA_CFG_SRC_DRQ_TYPE(vchan->endpoint) |
> >>>+ DDMA_CFG_SRC_ADDR_MODE(DDMA_ADDR_MODE_IO) |
> >>>+ DDMA_CFG_DEST_DRQ_TYPE(DDMA_DRQ_TYPE_SDRAM) |
> >>>+ DDMA_CFG_DEST_ADDR_MODE(DDMA_ADDR_MODE_LINEAR);
> >>>+ } else {
> >>>+ endpoints = NDMA_CFG_SRC_DRQ_TYPE(vchan->endpoint) |
> >>>+ NDMA_CFG_SRC_FIXED_ADDR |
> >>>+ NDMA_CFG_DEST_DRQ_TYPE(NDMA_DRQ_TYPE_SDRAM);
> >>>+ }
> >>
> >>I would like this to be reworked a bit with mapping of each endpoint
> >>configuration to setting like dedicated and dir, that way adding future
> >>value gets simpler by adding one more case rather than dense bring here
> >
> >I don't really follow you on this one. You mean adding a
> >macro/function to generate the endpoints value?
>
> FWIW, these 4 cases are all the cases possible.
for this generation yes, but future ?
to simply this very complex logic, I would rather program each bit based on
its simpler condition. If you think you cant break it then at least at some
explanation of various values being configured and why
>
> >>>+static int sun4i_dma_remove(struct platform_device *pdev)
> >>>+{
> >>>+ struct sun4i_dma_dev *priv = platform_get_drvdata(pdev);
> >>>+
> >>>+ /* Disable IRQ so no more work is scheduled */
> >>>+ disable_irq(priv->irq);
> >>>+
> >>>+ of_dma_controller_free(pdev->dev.of_node);
> >>>+ dma_async_device_unregister(&priv->slave);
> >>>+
> >>>+ clk_disable_unprepare(priv->clk);
> >>
> >>you still have irq enabled here, expilcit call to free_irq helps that as
> >>well
> >>as ensuring all the tasklets are executed before you remove the device
>
> Why is that? Doesn't disable_irq disable the irq and wait for all of
> the handlers to finish executing? That's what I get from the comment
> on http://lxr.free-electrons.com/source/kernel/irq/manage.c#L424
Sorry my bad, i missed the disable_irq() fine in that case
>
> >Which tasklets? We don't use any in our driver.
vchan does
--
~Vinod
--
You received this message because you are subscribed to the Google Groups
"linux-sunxi" group.
To unsubscribe from this group and stop receiving emails from it, send an email
to [email protected].
For more options, visit https://groups.google.com/d/optout.