Merge tag 'dmaengine-4.9-rc1' of git://git.infradead.org/users/vkoul/slave-dma
[cascardo/linux.git] / drivers / dma / imx-sdma.c
index 03ec76f..b9629b2 100644 (file)
 struct sdma_mode_count {
        u32 count   : 16; /* size of the buffer pointed by this BD */
        u32 status  :  8; /* E,R,I,C,W,D status bits stored here */
-       u32 command :  8; /* command mostlky used for channel 0 */
+       u32 command :  8; /* command mostly used for channel 0 */
 };
 
 /*
@@ -479,6 +479,24 @@ static struct sdma_driver_data sdma_imx6q = {
        .script_addrs = &sdma_script_imx6q,
 };
 
+static struct sdma_script_start_addrs sdma_script_imx7d = {
+       .ap_2_ap_addr = 644,
+       .uart_2_mcu_addr = 819,
+       .mcu_2_app_addr = 749,
+       .uartsh_2_mcu_addr = 1034,
+       .mcu_2_shp_addr = 962,
+       .app_2_mcu_addr = 685,
+       .shp_2_mcu_addr = 893,
+       .spdif_2_mcu_addr = 1102,
+       .mcu_2_spdif_addr = 1136,
+};
+
+static struct sdma_driver_data sdma_imx7d = {
+       .chnenbl0 = SDMA_CHNENBL0_IMX35,
+       .num_events = 48,
+       .script_addrs = &sdma_script_imx7d,
+};
+
 static const struct platform_device_id sdma_devtypes[] = {
        {
                .name = "imx25-sdma",
@@ -498,6 +516,9 @@ static const struct platform_device_id sdma_devtypes[] = {
        }, {
                .name = "imx6q-sdma",
                .driver_data = (unsigned long)&sdma_imx6q,
+       }, {
+               .name = "imx7d-sdma",
+               .driver_data = (unsigned long)&sdma_imx7d,
        }, {
                /* sentinel */
        }
@@ -511,6 +532,7 @@ static const struct of_device_id sdma_dt_ids[] = {
        { .compatible = "fsl,imx35-sdma", .data = &sdma_imx35, },
        { .compatible = "fsl,imx31-sdma", .data = &sdma_imx31, },
        { .compatible = "fsl,imx25-sdma", .data = &sdma_imx25, },
+       { .compatible = "fsl,imx7d-sdma", .data = &sdma_imx7d, },
        { /* sentinel */ }
 };
 MODULE_DEVICE_TABLE(of, sdma_dt_ids);
@@ -648,15 +670,11 @@ static void sdma_event_disable(struct sdma_channel *sdmac, unsigned int event)
        writel_relaxed(val, sdma->regs + chnenbl);
 }
 
-static void sdma_handle_channel_loop(struct sdma_channel *sdmac)
-{
-       if (sdmac->desc.callback)
-               sdmac->desc.callback(sdmac->desc.callback_param);
-}
-
 static void sdma_update_channel_loop(struct sdma_channel *sdmac)
 {
        struct sdma_buffer_descriptor *bd;
+       int error = 0;
+       enum dma_status old_status = sdmac->status;
 
        /*
         * loop mode. Iterate over descriptors, re-setup them and
@@ -668,17 +686,41 @@ static void sdma_update_channel_loop(struct sdma_channel *sdmac)
                if (bd->mode.status & BD_DONE)
                        break;
 
-               if (bd->mode.status & BD_RROR)
+               if (bd->mode.status & BD_RROR) {
+                       bd->mode.status &= ~BD_RROR;
                        sdmac->status = DMA_ERROR;
+                       error = -EIO;
+               }
 
+              /*
+               * We use bd->mode.count to calculate the residue, since contains
+               * the number of bytes present in the current buffer descriptor.
+               */
+
+               sdmac->chn_real_count = bd->mode.count;
                bd->mode.status |= BD_DONE;
+               bd->mode.count = sdmac->period_len;
+
+               /*
+                * The callback is called from the interrupt context in order
+                * to reduce latency and to avoid the risk of altering the
+                * SDMA transaction status by the time the client tasklet is
+                * executed.
+                */
+
+               dmaengine_desc_get_callback_invoke(&sdmac->desc, NULL);
+
                sdmac->buf_tail++;
                sdmac->buf_tail %= sdmac->num_bd;
+
+               if (error)
+                       sdmac->status = old_status;
        }
 }
 
-static void mxc_sdma_handle_channel_normal(struct sdma_channel *sdmac)
+static void mxc_sdma_handle_channel_normal(unsigned long data)
 {
+       struct sdma_channel *sdmac = (struct sdma_channel *) data;
        struct sdma_buffer_descriptor *bd;
        int i, error = 0;
 
@@ -701,18 +743,8 @@ static void mxc_sdma_handle_channel_normal(struct sdma_channel *sdmac)
                sdmac->status = DMA_COMPLETE;
 
        dma_cookie_complete(&sdmac->desc);
-       if (sdmac->desc.callback)
-               sdmac->desc.callback(sdmac->desc.callback_param);
-}
-
-static void sdma_tasklet(unsigned long data)
-{
-       struct sdma_channel *sdmac = (struct sdma_channel *) data;
 
-       if (sdmac->flags & IMX_DMA_SG_LOOP)
-               sdma_handle_channel_loop(sdmac);
-       else
-               mxc_sdma_handle_channel_normal(sdmac);
+       dmaengine_desc_get_callback_invoke(&sdmac->desc, NULL);
 }
 
 static irqreturn_t sdma_int_handler(int irq, void *dev_id)
@@ -731,8 +763,8 @@ static irqreturn_t sdma_int_handler(int irq, void *dev_id)
 
                if (sdmac->flags & IMX_DMA_SG_LOOP)
                        sdma_update_channel_loop(sdmac);
-
-               tasklet_schedule(&sdmac->tasklet);
+               else
+                       tasklet_schedule(&sdmac->tasklet);
 
                __clear_bit(channel, &stat);
        }
@@ -1353,7 +1385,8 @@ static enum dma_status sdma_tx_status(struct dma_chan *chan,
        u32 residue;
 
        if (sdmac->flags & IMX_DMA_SG_LOOP)
-               residue = (sdmac->num_bd - sdmac->buf_tail) * sdmac->period_len;
+               residue = (sdmac->num_bd - sdmac->buf_tail) *
+                          sdmac->period_len - sdmac->chn_real_count;
        else
                residue = sdmac->chn_count - sdmac->chn_real_count;
 
@@ -1375,6 +1408,7 @@ static void sdma_issue_pending(struct dma_chan *chan)
 #define SDMA_SCRIPT_ADDRS_ARRAY_SIZE_V1        34
 #define SDMA_SCRIPT_ADDRS_ARRAY_SIZE_V2        38
 #define SDMA_SCRIPT_ADDRS_ARRAY_SIZE_V3        41
+#define SDMA_SCRIPT_ADDRS_ARRAY_SIZE_V4        42
 
 static void sdma_add_scripts(struct sdma_engine *sdma,
                const struct sdma_script_start_addrs *addr)
@@ -1424,6 +1458,9 @@ static void sdma_load_firmware(const struct firmware *fw, void *context)
        case 3:
                sdma->script_number = SDMA_SCRIPT_ADDRS_ARRAY_SIZE_V3;
                break;
+       case 4:
+               sdma->script_number = SDMA_SCRIPT_ADDRS_ARRAY_SIZE_V4;
+               break;
        default:
                dev_err(sdma->dev, "unknown firmware version\n");
                goto err_firmware;
@@ -1732,7 +1769,7 @@ static int sdma_probe(struct platform_device *pdev)
                dma_cookie_init(&sdmac->chan);
                sdmac->channel = i;
 
-               tasklet_init(&sdmac->tasklet, sdma_tasklet,
+               tasklet_init(&sdmac->tasklet, mxc_sdma_handle_channel_normal,
                             (unsigned long) sdmac);
                /*
                 * Add the channel to the DMAC list. Do not add channel 0 though