diff options
-rw-r--r-- | Documentation/devicetree/bindings/dma/fsl-imx-sdma.txt | 1 | ||||
-rw-r--r-- | drivers/dma/imx-sdma.c | 107 | ||||
-rw-r--r-- | include/linux/platform_data/dma-imx-sdma.h | 1 | ||||
-rw-r--r-- | include/linux/platform_data/dma-imx.h | 2 |
4 files changed, 94 insertions, 17 deletions
diff --git a/Documentation/devicetree/bindings/dma/fsl-imx-sdma.txt b/Documentation/devicetree/bindings/dma/fsl-imx-sdma.txt index 68cee4f5539f..7953d422b662 100644 --- a/Documentation/devicetree/bindings/dma/fsl-imx-sdma.txt +++ b/Documentation/devicetree/bindings/dma/fsl-imx-sdma.txt @@ -37,6 +37,7 @@ The full ID of peripheral types can be found below. 19 IPU Memory 20 ASRC 21 ESAI + 22 HDMI Audio The third cell specifies the transfer priority as below. diff --git a/drivers/dma/imx-sdma.c b/drivers/dma/imx-sdma.c index 174c98913b73..705add4e893e 100644 --- a/drivers/dma/imx-sdma.c +++ b/drivers/dma/imx-sdma.c @@ -232,6 +232,14 @@ struct sdma_context_data { struct sdma_engine; +enum sdma_mode { + SDMA_MODE_INVALID = 0, + SDMA_MODE_LOOP, + SDMA_MODE_NORMAL, + SDMA_MODE_P2P, + SDMA_MODE_NO_BD, +}; + /** * struct sdma_channel - housekeeping for a SDMA channel * @@ -261,11 +269,13 @@ struct sdma_channel { dma_addr_t bd_phys; unsigned int pc_from_device, pc_to_device; unsigned int device_to_device; - unsigned long flags; + unsigned int other_script; + enum sdma_mode mode; dma_addr_t per_address, per_address2; unsigned long event_mask[2]; unsigned long watermark_level; u32 shp_addr, per_addr; + u32 data_addr1, data_addr2; struct dma_chan chan; spinlock_t lock; struct dma_async_tx_descriptor desc; @@ -275,8 +285,6 @@ struct sdma_channel { struct tasklet_struct tasklet; }; -#define IMX_DMA_SG_LOOP BIT(0) - #define MAX_DMA_CHANNELS 32 #define MXC_SDMA_DEFAULT_PRIORITY 1 #define MXC_SDMA_MIN_PRIORITY 1 @@ -544,16 +552,33 @@ static void mxc_sdma_handle_channel_normal(struct sdma_channel *sdmac) sdmac->desc.callback(sdmac->desc.callback_param); } +static void sdma_handle_other_intr(struct sdma_channel *sdmac) +{ + 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; + struct sdma_engine *sdma = sdmac->sdma; complete(&sdmac->done); - if (sdmac->flags & IMX_DMA_SG_LOOP) + switch (sdmac->mode) { + case SDMA_MODE_LOOP: sdma_handle_channel_loop(sdmac); - else + break; + case SDMA_MODE_NORMAL: mxc_sdma_handle_channel_normal(sdmac); + break; + case SDMA_MODE_NO_BD: + sdma_handle_other_intr(sdmac); + break; + default: + dev_err(sdma->dev, "invalid SDMA MODE!\n"); + break; + } } static irqreturn_t sdma_int_handler(int irq, void *dev_id) @@ -591,10 +616,12 @@ static void sdma_get_pc(struct sdma_channel *sdmac, * two peripherals or memory-to-memory transfers */ int per_2_per = 0, emi_2_emi = 0; + int other = 0; sdmac->pc_from_device = 0; sdmac->pc_to_device = 0; sdmac->device_to_device = 0; + sdmac->other_script = 0; switch (peripheral_type) { case IMX_DMATYPE_MEMORY: @@ -657,6 +684,9 @@ static void sdma_get_pc(struct sdma_channel *sdmac, case IMX_DMATYPE_IPU_MEMORY: emi_2_per = sdma->script_addrs->ext_mem_2_ipu_addr; break; + case IMX_DMATYPE_HDMI: + other = sdma->script_addrs->hdmi_dma_addr; + break; default: break; } @@ -664,6 +694,7 @@ static void sdma_get_pc(struct sdma_channel *sdmac, sdmac->pc_from_device = per_2_emi; sdmac->pc_to_device = emi_2_per; sdmac->device_to_device = per_2_per; + sdmac->other_script = other; } static int sdma_load_context(struct sdma_channel *sdmac) @@ -680,8 +711,10 @@ static int sdma_load_context(struct sdma_channel *sdmac) load_address = sdmac->pc_from_device; else if (sdmac->direction == DMA_DEV_TO_DEV) load_address = sdmac->device_to_device; - else + else if (sdmac->direction == DMA_MEM_TO_DEV) load_address = sdmac->pc_to_device; + else + load_address = sdmac->other_script; if (load_address < 0) return load_address; @@ -701,11 +734,16 @@ static int sdma_load_context(struct sdma_channel *sdmac) /* Send by context the event mask,base address for peripheral * and watermark level */ - context->gReg[0] = sdmac->event_mask[1]; - context->gReg[1] = sdmac->event_mask[0]; - context->gReg[2] = sdmac->per_addr; - context->gReg[6] = sdmac->shp_addr; - context->gReg[7] = sdmac->watermark_level; + if (sdmac->peripheral_type == IMX_DMATYPE_HDMI) { + context->gReg[4] = sdmac->data_addr1; + context->gReg[6] = sdmac->data_addr2; + } else { + context->gReg[0] = sdmac->event_mask[1]; + context->gReg[1] = sdmac->event_mask[0]; + context->gReg[2] = sdmac->per_addr; + context->gReg[6] = sdmac->shp_addr; + context->gReg[7] = sdmac->watermark_level; + } bd0->mode.command = C0_SETDM; bd0->mode.status = BD_DONE | BD_INTR | BD_WRAP | BD_EXTD; @@ -730,6 +768,7 @@ static void sdma_disable_channel(struct sdma_channel *sdmac) static int sdma_config_channel(struct sdma_channel *sdmac) { + struct imx_dma_data *data = sdmac->chan.private; int ret; sdma_disable_channel(sdmac); @@ -738,6 +777,8 @@ static int sdma_config_channel(struct sdma_channel *sdmac) sdmac->event_mask[1] = 0; sdmac->shp_addr = 0; sdmac->per_addr = 0; + sdmac->data_addr1 = 0; + sdmac->data_addr2 = 0; if (sdmac->event_id0) { if (sdmac->event_id0 >= sdmac->sdma->num_events) @@ -827,6 +868,13 @@ static int sdma_config_channel(struct sdma_channel *sdmac) if (sdmac->direction == DMA_DEV_TO_DEV) { sdmac->shp_addr = sdmac->per_address2; sdmac->per_addr = sdmac->per_address; + } else if (sdmac->direction == DMA_TRANS_NONE) { + if (sdmac->peripheral_type != IMX_DMATYPE_HDMI || + !data->data_addr1 || !data->data_addr2) + return -EINVAL; + sdmac->data_addr1 = *(u32 *)data->data_addr1; + sdmac->data_addr2 = *(u32 *)data->data_addr2; + sdmac->watermark_level = 0; } else { sdmac->shp_addr = sdmac->per_address; } @@ -944,6 +992,9 @@ static int sdma_alloc_chan_resources(struct dma_chan *chan) /* txd.flags will be overwritten in prep funcs */ sdmac->desc.flags = DMA_CTRL_ACK; + /* Set SDMA channel mode to unvalid to avoid misconfig */ + sdmac->mode = SDMA_MODE_INVALID; + return 0; } @@ -985,7 +1036,7 @@ static struct dma_async_tx_descriptor *sdma_prep_slave_sg( return NULL; sdmac->status = DMA_IN_PROGRESS; - sdmac->flags = 0; + sdmac->mode = SDMA_MODE_NORMAL; sdmac->buf_tail = 0; @@ -1078,9 +1129,9 @@ static struct dma_async_tx_descriptor *sdma_prep_dma_cyclic( { struct sdma_channel *sdmac = to_sdma_chan(chan); struct sdma_engine *sdma = sdmac->sdma; - int num_periods = buf_len / period_len; int channel = sdmac->channel; int ret, i = 0, buf = 0; + int num_periods; dev_dbg(sdma->dev, "%s channel: %d\n", __func__, channel); @@ -1091,12 +1142,33 @@ static struct dma_async_tx_descriptor *sdma_prep_dma_cyclic( sdmac->buf_tail = 0; - sdmac->flags |= IMX_DMA_SG_LOOP; sdmac->direction = direction; + + switch (sdmac->direction) { + case DMA_DEV_TO_DEV: + sdmac->mode = SDMA_MODE_P2P; + break; + case DMA_TRANS_NONE: + sdmac->mode = SDMA_MODE_NO_BD; + break; + case DMA_MEM_TO_DEV: + case DMA_DEV_TO_MEM: + sdmac->mode = SDMA_MODE_LOOP; + break; + default: + dev_err(sdma->dev, "invalid SDMA direction %d\n", direction); + return NULL; + } + ret = sdma_load_context(sdmac); if (ret) goto err_out; + if (period_len) + num_periods = buf_len / period_len; + else + return &sdmac->desc; + if (num_periods > NUM_BD) { dev_err(sdma->dev, "SDMA channel %d: maximum number of sg exceeded: %d > %d\n", channel, num_periods, NUM_BD); @@ -1175,7 +1247,7 @@ static int sdma_control(struct dma_chan *chan, enum dma_ctrl_cmd cmd, sdmac->watermark_level = dmaengine_cfg->src_maxburst * dmaengine_cfg->src_addr_width; sdmac->word_size = dmaengine_cfg->src_addr_width; - } else { + } else if (dmaengine_cfg->direction == DMA_MEM_TO_DEV) { sdmac->per_address = dmaengine_cfg->dst_addr; sdmac->watermark_level = dmaengine_cfg->dst_maxburst * dmaengine_cfg->dst_addr_width; @@ -1214,7 +1286,8 @@ static void sdma_issue_pending(struct dma_chan *chan) struct sdma_channel *sdmac = to_sdma_chan(chan); struct sdma_engine *sdma = sdmac->sdma; - if (sdmac->status == DMA_IN_PROGRESS) + /* Only HDMI audio uses other_script, and it does not care status */ + if (sdmac->status == DMA_IN_PROGRESS || sdmac->other_script) sdma_enable_channel(sdma, sdmac->channel); } @@ -1233,7 +1306,7 @@ void sdma_set_event_pending(struct dma_chan *chan) } EXPORT_SYMBOL(sdma_set_event_pending); -#define SDMA_SCRIPT_ADDRS_ARRAY_SIZE_V1 37 +#define SDMA_SCRIPT_ADDRS_ARRAY_SIZE_V1 38 static void sdma_add_scripts(struct sdma_engine *sdma, const struct sdma_script_start_addrs *addr) diff --git a/include/linux/platform_data/dma-imx-sdma.h b/include/linux/platform_data/dma-imx-sdma.h index 19cfa9a1fd16..14ca582adf24 100644 --- a/include/linux/platform_data/dma-imx-sdma.h +++ b/include/linux/platform_data/dma-imx-sdma.h @@ -45,6 +45,7 @@ struct sdma_script_start_addrs { s32 ram_code_start_addr; s32 mcu_2_ssish_addr; s32 ssish_2_mcu_addr; + s32 hdmi_dma_addr; }; /** diff --git a/include/linux/platform_data/dma-imx.h b/include/linux/platform_data/dma-imx.h index ac4e9e958e82..dd50bc70f59c 100644 --- a/include/linux/platform_data/dma-imx.h +++ b/include/linux/platform_data/dma-imx.h @@ -39,6 +39,7 @@ enum sdma_peripheral_type { IMX_DMATYPE_IPU_MEMORY, /* IPU Memory */ IMX_DMATYPE_ASRC, /* ASRC */ IMX_DMATYPE_ESAI, /* ESAI */ + IMX_DMATYPE_HDMI, /* HDMI Audio */ }; enum imx_dma_prio { @@ -52,6 +53,7 @@ struct imx_dma_data { int dma_request1; enum sdma_peripheral_type peripheral_type; int priority; + void *data_addr1, *data_addr2; }; static inline int imx_dma_is_ipu(struct dma_chan *chan) |