summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Documentation/devicetree/bindings/dma/fsl-imx-sdma.txt1
-rw-r--r--drivers/dma/imx-sdma.c107
-rw-r--r--include/linux/platform_data/dma-imx-sdma.h1
-rw-r--r--include/linux/platform_data/dma-imx.h2
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)