[alsa-devel] [PATCH 00/18] edma/ASoC: dmaengine PCM for AM335x and AM447x
Hi,
With this series AM335x and AM447x will use the dmaengine PCM for audio. The daVinci devices will keep using the davinci-pcm for now since I do not have means to test them but the code is written in a way that they can be switched to use the edma-pcm easily (DA850/OMAP-L138 has been tested and audio was working fine).
To be able to use the dmaengine PCM I needed to make changes to the edma code in arch/arm/common/ and in drivers/dma/ : Adding support for DMA pause/resume Possibility to select non default event queue/TC for cyclic (audio) dma channels: all devices using the eDMA via dmaengine was assigned to the default EQ/TC (mmc, i2c, spi, etc, and audio). This is not optimal from system performance point of view since sharing the same EQ/TC can cause latency spikes for cyclic channels (long DMA transfers for MMC for example).
While debugging the edma to get things sorted out I noticed that the debug was too verbose and the important information was hidden even when the we did not asked for verbose dmaengine debug. I have included some debug cleanups for the edma dmaengine driver also.
On the ASoC side: the series has been tested on AM335x and AM447x. I have report that DA850/OMAP-L138 works with this series (which boots w/o DT).
Regards, Peter --- Peter Ujfalusi (18): platform_data: edma: Be precise with the paRAM struct dma: edma: Add support for DMA_PAUSE/RESUME operation dma: edma: Set DMA_CYCLIC capability flag arm: common: edma: Select event queue 1 as default when booted with DT arm: common: edma: Save the number of event queues/TCs arm: common: edma: API to request non default queue for a channel DMA: edma: Use different eventq for cyclic channels dma: edma: Implement device_slave_caps callback dma: edma: Simplify direction configuration in edma_config_pset() dma: edma: Reduce debug print verbosity for non verbose debugging dma: edma: Prefix debug prints where the text were identical in prep callbacks dma: edma: Add channel number to debug prints dma: edma: Print the direction value as well when it is not supported ASoC: davinci: Add edma dmaengine platform driver ASoC: davinci-mcasp: Use dmaengine based platform driver for AM335x/447x ASoC: davinci-mcasp: Provide correct filter_data for dmaengine for non-DT boot ASoC: davinci-mcasp: Assign the dma_data earlier in dai_probe callback ASoC: davinci-mcasp: Place constraint on the period size based on FIFO config
arch/arm/common/edma.c | 34 ++++++++++++- drivers/dma/edma.c | 92 ++++++++++++++++++++++++++-------- include/linux/platform_data/edma.h | 20 ++++---- sound/soc/davinci/Kconfig | 1 + sound/soc/davinci/Makefile | 2 +- sound/soc/davinci/davinci-mcasp.c | 100 ++++++++++++++++++++++++++++++------- sound/soc/davinci/edma-pcm.c | 77 ++++++++++++++++++++++++++++ sound/soc/davinci/edma-pcm.h | 26 ++++++++++ 8 files changed, 303 insertions(+), 49 deletions(-) create mode 100644 sound/soc/davinci/edma-pcm.c create mode 100644 sound/soc/davinci/edma-pcm.h
The edmacc_param struct should follow the layout of the paRAM area in the HW. Be explicit on the size of the fields (u32) and also mark the struct as packed to avoid any padding on non 32bit architectures.
Signed-off-by: Peter Ujfalusi peter.ujfalusi@ti.com --- include/linux/platform_data/edma.h | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-)
diff --git a/include/linux/platform_data/edma.h b/include/linux/platform_data/edma.h index f50821cb64be..923f8a3e4ce0 100644 --- a/include/linux/platform_data/edma.h +++ b/include/linux/platform_data/edma.h @@ -43,15 +43,15 @@
/* PaRAM slots are laid out like this */ struct edmacc_param { - unsigned int opt; - unsigned int src; - unsigned int a_b_cnt; - unsigned int dst; - unsigned int src_dst_bidx; - unsigned int link_bcntrld; - unsigned int src_dst_cidx; - unsigned int ccnt; -}; + u32 opt; + u32 src; + u32 a_b_cnt; + u32 dst; + u32 src_dst_bidx; + u32 link_bcntrld; + u32 src_dst_cidx; + u32 ccnt; +} __packed;
/* fields in edmacc_param.opt */ #define SAM BIT(0)
Pause/Resume can be used by the audio stack when the stream is paused/resumed The edma platform code has support for this and the legacy audio stack used this.
Signed-off-by: Peter Ujfalusi peter.ujfalusi@ti.com --- drivers/dma/edma.c | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+)
diff --git a/drivers/dma/edma.c b/drivers/dma/edma.c index cd04eb7b182e..b54cd2dedde0 100644 --- a/drivers/dma/edma.c +++ b/drivers/dma/edma.c @@ -240,6 +240,26 @@ static int edma_slave_config(struct edma_chan *echan, return 0; }
+static int edma_dma_pause(struct edma_chan *echan) +{ + /* Pause/Resume only allowed with cyclic mode */ + if (!echan->edesc->cyclic) + return -EINVAL; + + edma_pause(echan->ch_num); + return 0; +} + +static int edma_dma_resume(struct edma_chan *echan) +{ + /* Pause/Resume only allowed with cyclic mode */ + if (!echan->edesc->cyclic) + return -EINVAL; + + edma_resume(echan->ch_num); + return 0; +} + static int edma_control(struct dma_chan *chan, enum dma_ctrl_cmd cmd, unsigned long arg) { @@ -255,6 +275,14 @@ static int edma_control(struct dma_chan *chan, enum dma_ctrl_cmd cmd, config = (struct dma_slave_config *)arg; ret = edma_slave_config(echan, config); break; + case DMA_PAUSE: + ret = edma_dma_pause(echan); + break; + + case DMA_RESUME: + ret = edma_dma_resume(echan); + break; + default: ret = -ENOSYS; }
Indicate that the edma dmaengine driver has support for cyclic mode.
Signed-off-by: Peter Ujfalusi peter.ujfalusi@ti.com --- arch/arm/common/edma.c | 1 + drivers/dma/edma.c | 1 + 2 files changed, 2 insertions(+)
diff --git a/arch/arm/common/edma.c b/arch/arm/common/edma.c index 41bca32409fc..86a8b263278f 100644 --- a/arch/arm/common/edma.c +++ b/arch/arm/common/edma.c @@ -1574,6 +1574,7 @@ static struct edma_soc_info *edma_setup_info_from_dt(struct device *dev, return ERR_PTR(ret);
dma_cap_set(DMA_SLAVE, edma_filter_info.dma_cap); + dma_cap_set(DMA_CYCLIC, edma_filter_info.dma_cap); of_dma_controller_register(dev->of_node, of_dma_simple_xlate, &edma_filter_info);
diff --git a/drivers/dma/edma.c b/drivers/dma/edma.c index b54cd2dedde0..86d6a3fb0d92 100644 --- a/drivers/dma/edma.c +++ b/drivers/dma/edma.c @@ -887,6 +887,7 @@ static int edma_probe(struct platform_device *pdev)
dma_cap_zero(ecc->dma_slave.cap_mask); dma_cap_set(DMA_SLAVE, ecc->dma_slave.cap_mask); + dma_cap_set(DMA_CYCLIC, ecc->dma_slave.cap_mask);
edma_dma_init(ecc, &ecc->dma_slave, &pdev->dev);
Use the EVENTQ_1 for default and leave the EVENTQ_0 to be used by high priority channels, like audio.
Signed-off-by: Peter Ujfalusi peter.ujfalusi@ti.com --- arch/arm/common/edma.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/arch/arm/common/edma.c b/arch/arm/common/edma.c index 86a8b263278f..19520e2519d9 100644 --- a/arch/arm/common/edma.c +++ b/arch/arm/common/edma.c @@ -1546,7 +1546,8 @@ static int edma_of_parse_dt(struct device *dev,
pdata->queue_priority_mapping = queue_priority_map;
- pdata->default_queue = 0; + /* select queue 1 as default */ + pdata->default_queue = EVENTQ_1;
prop = of_find_property(node, "ti,edma-xbar-event-map", &sz); if (prop)
For later use save the number of queues available for the CC.
Signed-off-by: Peter Ujfalusi peter.ujfalusi@ti.com --- arch/arm/common/edma.c | 3 +++ 1 file changed, 3 insertions(+)
diff --git a/arch/arm/common/edma.c b/arch/arm/common/edma.c index 19520e2519d9..be267b2080be 100644 --- a/arch/arm/common/edma.c +++ b/arch/arm/common/edma.c @@ -1770,6 +1770,9 @@ static int edma_probe(struct platform_device *pdev) map_queue_tc(j, queue_tc_mapping[i][0], queue_tc_mapping[i][1]);
+ /* Save the number of TCs */ + edma_cc[j]->num_tc = i; + /* Event queue priority mapping */ for (i = 0; queue_priority_mapping[i][0] != -1; i++) assign_priority_to_queue(j,
When using eDMA3 via dmaengine all dma channels will use the default queue. Since during request time we do not have means to change this it need to be done later, before the DMA has been started. With the added function it is possible to move the channel to a non default queue if it is possible, otherwise (when only one EQ/TC is available for the CC) the default queue is going to be used. For example: For optimal system performance the audio (cyclic) DMA should be placed to a separate queue which is different than what the rest of the system is using.
Signed-off-by: Peter Ujfalusi peter.ujfalusi@ti.com --- arch/arm/common/edma.c | 27 +++++++++++++++++++++++++++ include/linux/platform_data/edma.h | 2 ++ 2 files changed, 29 insertions(+)
diff --git a/arch/arm/common/edma.c b/arch/arm/common/edma.c index be267b2080be..eaf6dd19f082 100644 --- a/arch/arm/common/edma.c +++ b/arch/arm/common/edma.c @@ -712,6 +712,33 @@ int edma_alloc_channel(int channel, } EXPORT_SYMBOL(edma_alloc_channel);
+/** + * edma_request_non_default_queue - try to map the channel to non default queue + * @channel: dma channel returned from edma_alloc_channel() + * + * For certain type of applications like audio it is preferred to not use the + * default event queue/tc to avoid eDMA caused latency. + * + * This function will iterate through the event queues available for the CC and + * picks the first EQ/TC which is not set as the default for the CC + */ +void edma_request_non_default_queue(int channel) +{ + unsigned ctlr = EDMA_CTLR(channel); + enum dma_event_q eventq_no = EVENTQ_DEFAULT; + int i; + + for (i = 0; i < edma_cc[ctlr]->num_tc; i++) { + if (i != edma_cc[ctlr]->default_queue) { + eventq_no = i; + break; + } + } + + channel = EDMA_CHAN_SLOT(channel); + map_dmach_queue(ctlr, channel, eventq_no); +} +EXPORT_SYMBOL(edma_request_non_default_queue);
/** * edma_free_channel - deallocate DMA channel diff --git a/include/linux/platform_data/edma.h b/include/linux/platform_data/edma.h index 923f8a3e4ce0..5d0a1b98f205 100644 --- a/include/linux/platform_data/edma.h +++ b/include/linux/platform_data/edma.h @@ -117,6 +117,8 @@ int edma_alloc_channel(int channel, void *data, enum dma_event_q); void edma_free_channel(unsigned channel);
+void edma_request_non_default_queue(int channel); + /* alloc/free parameter RAM slots */ int edma_alloc_slot(unsigned ctlr, int slot); void edma_free_slot(unsigned slot);
To improve latency with cyclic DMA operation it is preferred to use different eventq/tc than the default which is used by all other drivers (mmc, spi, i2c, etc). When preparing the cyclic dma ask for non default queue for the channel which is going to be used with cyclic mode.
Signed-off-by: Peter Ujfalusi peter.ujfalusi@ti.com --- drivers/dma/edma.c | 3 +++ 1 file changed, 3 insertions(+)
diff --git a/drivers/dma/edma.c b/drivers/dma/edma.c index 86d6a3fb0d92..604c7c94c731 100644 --- a/drivers/dma/edma.c +++ b/drivers/dma/edma.c @@ -624,6 +624,9 @@ static struct dma_async_tx_descriptor *edma_prep_dma_cyclic( edesc->pset[i].opt |= TCINTEN; }
+ /* Use different eventq/tc for cyclic DMA to reduce latency */ + edma_request_non_default_queue(echan->ch_num); + return vchan_tx_prep(&echan->vchan, &edesc->vdesc, tx_flags); }
With the callback implemented omap-dma can provide information to client drivers regarding to supported address widths, directions, residue granularity, etc.
Signed-off-by: Peter Ujfalusi peter.ujfalusi@ti.com --- drivers/dma/edma.c | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+)
diff --git a/drivers/dma/edma.c b/drivers/dma/edma.c index 604c7c94c731..1877319379fc 100644 --- a/drivers/dma/edma.c +++ b/drivers/dma/edma.c @@ -851,6 +851,23 @@ static void __init edma_chan_init(struct edma_cc *ecc, } }
+#define EDMA_DMA_BUSWIDTHS (BIT(DMA_SLAVE_BUSWIDTH_1_BYTE) | \ + BIT(DMA_SLAVE_BUSWIDTH_2_BYTES) | \ + BIT(DMA_SLAVE_BUSWIDTH_4_BYTES)) + +static int edma_dma_device_slave_caps(struct dma_chan *dchan, + struct dma_slave_caps *caps) +{ + caps->src_addr_widths = EDMA_DMA_BUSWIDTHS; + caps->dstn_addr_widths = EDMA_DMA_BUSWIDTHS; + caps->directions = BIT(DMA_DEV_TO_MEM) | BIT(DMA_MEM_TO_DEV); + caps->cmd_pause = true; + caps->cmd_terminate = true; + caps->residue_granularity = DMA_RESIDUE_GRANULARITY_DESCRIPTOR; + + return 0; +} + static void edma_dma_init(struct edma_cc *ecc, struct dma_device *dma, struct device *dev) { @@ -861,6 +878,7 @@ static void edma_dma_init(struct edma_cc *ecc, struct dma_device *dma, dma->device_issue_pending = edma_issue_pending; dma->device_tx_status = edma_tx_status; dma->device_control = edma_control; + dma->device_slave_caps = edma_dma_device_slave_caps; dma->dev = dev;
INIT_LIST_HEAD(&dma->channels);
We only support DEV_TO_MEM or MEM_TO_DEV directions with edma driver and the check for the direction has been already done in the function calling edma_config_pset(). The error reporting is redundant and also the "else if ()" can be removed.
Signed-off-by: Peter Ujfalusi peter.ujfalusi@ti.com --- drivers/dma/edma.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-)
diff --git a/drivers/dma/edma.c b/drivers/dma/edma.c index 1877319379fc..e4f4a0cef58c 100644 --- a/drivers/dma/edma.c +++ b/drivers/dma/edma.c @@ -368,14 +368,12 @@ static int edma_config_pset(struct dma_chan *chan, struct edmacc_param *pset, src_cidx = cidx; dst_bidx = 0; dst_cidx = 0; - } else if (direction == DMA_DEV_TO_MEM) { + } else { + /* DMA_DEV_TO_MEM */ src_bidx = 0; src_cidx = 0; dst_bidx = acnt; dst_cidx = cidx; - } else { - dev_err(dev, "%s: direction not implemented yet\n", __func__); - return -EINVAL; }
pset->opt = EDMA_TCC(EDMA_CHAN_SLOT(echan->ch_num));
Do not print the paRAM information when verbose debugging is not asked and also reduce the number of lines printed in edma_prep_dma_cyclic()
Signed-off-by: Peter Ujfalusi peter.ujfalusi@ti.com --- drivers/dma/edma.c | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-)
diff --git a/drivers/dma/edma.c b/drivers/dma/edma.c index e4f4a0cef58c..e2aa42b8342f 100644 --- a/drivers/dma/edma.c +++ b/drivers/dma/edma.c @@ -141,7 +141,7 @@ static void edma_execute(struct edma_chan *echan) for (i = 0; i < nslots; i++) { j = i + edesc->processed; edma_write_slot(echan->slot[i], &edesc->pset[j]); - dev_dbg(echan->vchan.chan.device->dev, + dev_vdbg(echan->vchan.chan.device->dev, "\n pset[%d]:\n" " chnum\t%d\n" " slot\t%d\n" @@ -554,9 +554,8 @@ static struct dma_async_tx_descriptor *edma_prep_dma_cyclic( edesc->cyclic = 1; edesc->pset_nr = nslots;
- dev_dbg(dev, "%s: nslots=%d\n", __func__, nslots); - dev_dbg(dev, "%s: period_len=%d\n", __func__, period_len); - dev_dbg(dev, "%s: buf_len=%d\n", __func__, buf_len); + dev_dbg(dev, "%s: channel=%d nslots=%d period_len=%d buf_len=%d\n", + __func__, echan->ch_num, nslots, period_len, buf_len);
for (i = 0; i < nslots; i++) { /* Allocate a PaRAM slot, if needed */ @@ -590,8 +589,8 @@ static struct dma_async_tx_descriptor *edma_prep_dma_cyclic( else src_addr += period_len;
- dev_dbg(dev, "%s: Configure period %d of buf:\n", __func__, i); - dev_dbg(dev, + dev_vdbg(dev, "%s: Configure period %d of buf:\n", __func__, i); + dev_vdbg(dev, "\n pset[%d]:\n" " chnum\t%d\n" " slot\t%d\n"
On Thu, 2014-03-13 at 11:18 +0200, Peter Ujfalusi wrote:
Do not print the paRAM information when verbose debugging is not asked and also reduce the number of lines printed in edma_prep_dma_cyclic()
Signed-off-by: Peter Ujfalusi peter.ujfalusi@ti.com
drivers/dma/edma.c | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-)
diff --git a/drivers/dma/edma.c b/drivers/dma/edma.c index e4f4a0cef58c..e2aa42b8342f 100644 --- a/drivers/dma/edma.c +++ b/drivers/dma/edma.c @@ -141,7 +141,7 @@ static void edma_execute(struct edma_chan *echan) for (i = 0; i < nslots; i++) { j = i + edesc->processed; edma_write_slot(echan->slot[i], &edesc->pset[j]);
dev_dbg(echan->vchan.chan.device->dev,
dev_vdbg(echan->vchan.chan.device->dev, "\n pset[%d]:\n" " chnum\t%d\n" " slot\t%d\n"
I believe you may move this code to separate function and reuse it later. Moreover %d is not good specifier for unsigned types, maybe %u?
@@ -554,9 +554,8 @@ static struct dma_async_tx_descriptor *edma_prep_dma_cyclic( edesc->cyclic = 1; edesc->pset_nr = nslots;
- dev_dbg(dev, "%s: nslots=%d\n", __func__, nslots);
- dev_dbg(dev, "%s: period_len=%d\n", __func__, period_len);
- dev_dbg(dev, "%s: buf_len=%d\n", __func__, buf_len);
- dev_dbg(dev, "%s: channel=%d nslots=%d period_len=%d buf_len=%d\n",
__func__, echan->ch_num, nslots, period_len, buf_len);
Consider to use proper specifiers for size_t types, namely %zd.
for (i = 0; i < nslots; i++) { /* Allocate a PaRAM slot, if needed */ @@ -590,8 +589,8 @@ static struct dma_async_tx_descriptor *edma_prep_dma_cyclic( else src_addr += period_len;
dev_dbg(dev, "%s: Configure period %d of buf:\n", __func__, i);
dev_dbg(dev,
dev_vdbg(dev, "%s: Configure period %d of buf:\n", __func__, i);
dev_vdbg(dev,
See the first comment.
"\n pset[%d]:\n" " chnum\t%d\n" " slot\t%d\n"
On 03/13/2014 02:53 PM, Shevchenko, Andriy wrote:
On Thu, 2014-03-13 at 11:18 +0200, Peter Ujfalusi wrote:
Do not print the paRAM information when verbose debugging is not asked and also reduce the number of lines printed in edma_prep_dma_cyclic()
Signed-off-by: Peter Ujfalusi peter.ujfalusi@ti.com
drivers/dma/edma.c | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-)
diff --git a/drivers/dma/edma.c b/drivers/dma/edma.c index e4f4a0cef58c..e2aa42b8342f 100644 --- a/drivers/dma/edma.c +++ b/drivers/dma/edma.c @@ -141,7 +141,7 @@ static void edma_execute(struct edma_chan *echan) for (i = 0; i < nslots; i++) { j = i + edesc->processed; edma_write_slot(echan->slot[i], &edesc->pset[j]);
dev_dbg(echan->vchan.chan.device->dev,
dev_vdbg(echan->vchan.chan.device->dev, "\n pset[%d]:\n" " chnum\t%d\n" " slot\t%d\n"
I believe you may move this code to separate function and reuse it later.
As the per patch description, I only changed the debug level in this patch.
Moreover %d is not good specifier for unsigned types, maybe %u?
You are right for unsigned type %u is the correct. This is why we have %d since j, echan->ch_num and echan->slot[i] are integer.
@@ -554,9 +554,8 @@ static struct dma_async_tx_descriptor *edma_prep_dma_cyclic( edesc->cyclic = 1; edesc->pset_nr = nslots;
- dev_dbg(dev, "%s: nslots=%d\n", __func__, nslots);
- dev_dbg(dev, "%s: period_len=%d\n", __func__, period_len);
- dev_dbg(dev, "%s: buf_len=%d\n", __func__, buf_len);
- dev_dbg(dev, "%s: channel=%d nslots=%d period_len=%d buf_len=%d\n",
__func__, echan->ch_num, nslots, period_len, buf_len);
Consider to use proper specifiers for size_t types, namely %zd.
I just collapsed the three line of dev_dbg into one and have not really checked the formats. For size_t the correct format should be %zu. I'll fix this up for the next version.
for (i = 0; i < nslots; i++) { /* Allocate a PaRAM slot, if needed */ @@ -590,8 +589,8 @@ static struct dma_async_tx_descriptor *edma_prep_dma_cyclic( else src_addr += period_len;
dev_dbg(dev, "%s: Configure period %d of buf:\n", __func__, i);
dev_dbg(dev,
dev_vdbg(dev, "%s: Configure period %d of buf:\n", __func__, i);
dev_vdbg(dev,
See the first comment.
As the per patch description, I only changed the debug level in this patch. This can be done as a separate patch later IMO as part of a bigger debug cleanup.
prep_slave_sg and prep_dma_cyclic callbacks have mostly same failure cases with the same texts printed in case we hit them. It helps when debugging if we know exactly which callback generated the errors. At the same time change the debug level for descriptor allocation failure from dbg to err since all other error cases are dev_err and this failure is similarly fatal as the other ones.
Signed-off-by: Peter Ujfalusi peter.ujfalusi@ti.com --- drivers/dma/edma.c | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-)
diff --git a/drivers/dma/edma.c b/drivers/dma/edma.c index e2aa42b8342f..07ac5f4eeb56 100644 --- a/drivers/dma/edma.c +++ b/drivers/dma/edma.c @@ -430,14 +430,14 @@ static struct dma_async_tx_descriptor *edma_prep_slave_sg( }
if (dev_width == DMA_SLAVE_BUSWIDTH_UNDEFINED) { - dev_err(dev, "Undefined slave buswidth\n"); + dev_err(dev, "%s: Undefined slave buswidth\n", __func__); return NULL; }
edesc = kzalloc(sizeof(*edesc) + sg_len * sizeof(edesc->pset[0]), GFP_ATOMIC); if (!edesc) { - dev_dbg(dev, "Failed to allocate a descriptor\n"); + dev_err(dev, "%s: Failed to allocate a descriptor\n", __func__); return NULL; }
@@ -453,7 +453,8 @@ static struct dma_async_tx_descriptor *edma_prep_slave_sg( EDMA_SLOT_ANY); if (echan->slot[i] < 0) { kfree(edesc); - dev_err(dev, "Failed to allocate slot\n"); + dev_err(dev, "%s: Failed to allocate slot\n", + __func__); return NULL; } } @@ -522,7 +523,7 @@ static struct dma_async_tx_descriptor *edma_prep_dma_cyclic( }
if (dev_width == DMA_SLAVE_BUSWIDTH_UNDEFINED) { - dev_err(dev, "Undefined slave buswidth\n"); + dev_err(dev, "%s: Undefined slave buswidth\n", __func__); return NULL; }
@@ -547,7 +548,7 @@ static struct dma_async_tx_descriptor *edma_prep_dma_cyclic( edesc = kzalloc(sizeof(*edesc) + nslots * sizeof(edesc->pset[0]), GFP_ATOMIC); if (!edesc) { - dev_dbg(dev, "Failed to allocate a descriptor\n"); + dev_err(dev, "%s: Failed to allocate a descriptor\n", __func__); return NULL; }
@@ -565,7 +566,8 @@ static struct dma_async_tx_descriptor *edma_prep_dma_cyclic( EDMA_SLOT_ANY); if (echan->slot[i] < 0) { kfree(edesc); - dev_err(dev, "Failed to allocate slot\n"); + dev_err(dev, "%s: Failed to allocate slot\n", + __func__); return NULL; } }
It helps to identify issues if we have some information regarding to the channel which the event is associated.
Signed-off-by: Peter Ujfalusi peter.ujfalusi@ti.com --- drivers/dma/edma.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-)
diff --git a/drivers/dma/edma.c b/drivers/dma/edma.c index 07ac5f4eeb56..47abaac7eb18 100644 --- a/drivers/dma/edma.c +++ b/drivers/dma/edma.c @@ -185,7 +185,8 @@ static void edma_execute(struct edma_chan *echan) edma_resume(echan->ch_num);
if (edesc->processed <= MAX_NR_SG) { - dev_dbg(dev, "first transfer starting %d\n", echan->ch_num); + dev_dbg(dev, "first transfer starting on channel %d\n", + echan->ch_num); edma_start(echan->ch_num); }
@@ -195,7 +196,7 @@ static void edma_execute(struct edma_chan *echan) * MAX_NR_SG */ if (echan->missed) { - dev_dbg(dev, "missed event in execute detected\n"); + dev_dbg(dev, "missed event on channel %d\n", echan->ch_num); edma_clean_channel(echan->ch_num); edma_stop(echan->ch_num); edma_start(echan->ch_num); @@ -732,7 +733,7 @@ static int edma_alloc_chan_resources(struct dma_chan *chan) echan->alloced = true; echan->slot[0] = echan->ch_num;
- dev_dbg(dev, "allocated channel for %u:%u\n", + dev_dbg(dev, "allocated channel %d for %u:%u\n", echan->ch_num, EDMA_CTLR(echan->ch_num), EDMA_CHAN_SLOT(echan->ch_num));
return 0;
In case of not supported direction it is better to print the direction also. It is unlikely, but in such an event it helps with the debugging.
Signed-off-by: Peter Ujfalusi peter.ujfalusi@ti.com --- drivers/dma/edma.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/drivers/dma/edma.c b/drivers/dma/edma.c index 47abaac7eb18..69dd4e3de67e 100644 --- a/drivers/dma/edma.c +++ b/drivers/dma/edma.c @@ -426,7 +426,7 @@ static struct dma_async_tx_descriptor *edma_prep_slave_sg( dev_width = echan->cfg.dst_addr_width; burst = echan->cfg.dst_maxburst; } else { - dev_err(dev, "%s: bad direction?\n", __func__); + dev_err(dev, "%s: bad direction: %d\n", __func__, direction); return NULL; }
@@ -519,7 +519,7 @@ static struct dma_async_tx_descriptor *edma_prep_dma_cyclic( dev_width = echan->cfg.dst_addr_width; burst = echan->cfg.dst_maxburst; } else { - dev_err(dev, "%s: bad direction?\n", __func__); + dev_err(dev, "%s: bad direction: %d\n", __func__, direction); return NULL; }
On Thu, 2014-03-13 at 11:18 +0200, Peter Ujfalusi wrote:
In case of not supported direction it is better to print the direction also. It is unlikely, but in such an event it helps with the debugging.
Signed-off-by: Peter Ujfalusi peter.ujfalusi@ti.com
drivers/dma/edma.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/drivers/dma/edma.c b/drivers/dma/edma.c index 47abaac7eb18..69dd4e3de67e 100644 --- a/drivers/dma/edma.c +++ b/drivers/dma/edma.c @@ -426,7 +426,7 @@ static struct dma_async_tx_descriptor *edma_prep_slave_sg( dev_width = echan->cfg.dst_addr_width; burst = echan->cfg.dst_maxburst; } else {
dev_err(dev, "%s: bad direction?\n", __func__);
dev_err(dev, "%s: bad direction: %d\n", __func__, direction);
Is it possible to have direction less than zero? Maybe %u?
return NULL;
}
@@ -519,7 +519,7 @@ static struct dma_async_tx_descriptor *edma_prep_dma_cyclic( dev_width = echan->cfg.dst_addr_width; burst = echan->cfg.dst_maxburst; } else {
dev_err(dev, "%s: bad direction?\n", __func__);
dev_err(dev, "%s: bad direction: %d\n", __func__, direction);
Ditto.
return NULL;
}
On 03/13/2014 03:03 PM, Shevchenko, Andriy wrote:
On Thu, 2014-03-13 at 11:18 +0200, Peter Ujfalusi wrote:
In case of not supported direction it is better to print the direction also. It is unlikely, but in such an event it helps with the debugging.
Signed-off-by: Peter Ujfalusi peter.ujfalusi@ti.com
drivers/dma/edma.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/drivers/dma/edma.c b/drivers/dma/edma.c index 47abaac7eb18..69dd4e3de67e 100644 --- a/drivers/dma/edma.c +++ b/drivers/dma/edma.c @@ -426,7 +426,7 @@ static struct dma_async_tx_descriptor *edma_prep_slave_sg( dev_width = echan->cfg.dst_addr_width; burst = echan->cfg.dst_maxburst; } else {
dev_err(dev, "%s: bad direction?\n", __func__);
dev_err(dev, "%s: bad direction: %d\n", __func__, direction);
Is it possible to have direction less than zero? Maybe %u?
It is 'enum dma_transfer_direction direction'. %d should be fine.
return NULL;
}
@@ -519,7 +519,7 @@ static struct dma_async_tx_descriptor *edma_prep_dma_cyclic( dev_width = echan->cfg.dst_addr_width; burst = echan->cfg.dst_maxburst; } else {
dev_err(dev, "%s: bad direction?\n", __func__);
dev_err(dev, "%s: bad direction: %d\n", __func__, direction);
Ditto.
return NULL;
}
Platform driver glue for SoC using eDMA3 to use dmaengine PCM. The maximum number of periods need to be limited to 19 since the edma dmaengine driver limits the paRAM slot use for audio at in cyclic mode.
Signed-off-by: Peter Ujfalusi peter.ujfalusi@ti.com --- sound/soc/davinci/Kconfig | 1 + sound/soc/davinci/Makefile | 2 +- sound/soc/davinci/edma-pcm.c | 77 ++++++++++++++++++++++++++++++++++++++++++++ sound/soc/davinci/edma-pcm.h | 26 +++++++++++++++ 4 files changed, 105 insertions(+), 1 deletion(-) create mode 100644 sound/soc/davinci/edma-pcm.c create mode 100644 sound/soc/davinci/edma-pcm.h
diff --git a/sound/soc/davinci/Kconfig b/sound/soc/davinci/Kconfig index a8ec1fc3e4d0..f9d9f748e743 100644 --- a/sound/soc/davinci/Kconfig +++ b/sound/soc/davinci/Kconfig @@ -1,6 +1,7 @@ config SND_DAVINCI_SOC tristate "SoC Audio for TI DAVINCI or AM33XX/AM43XX chips" depends on ARCH_DAVINCI || SOC_AM33XX || SOC_AM43XX + select SND_SOC_GENERIC_DMAENGINE_PCM
config SND_DAVINCI_SOC_I2S tristate diff --git a/sound/soc/davinci/Makefile b/sound/soc/davinci/Makefile index 744d4d9a0184..97d05a2f1723 100644 --- a/sound/soc/davinci/Makefile +++ b/sound/soc/davinci/Makefile @@ -1,5 +1,5 @@ # DAVINCI Platform Support -snd-soc-davinci-objs := davinci-pcm.o +snd-soc-davinci-objs := davinci-pcm.o edma-pcm.o snd-soc-davinci-i2s-objs := davinci-i2s.o snd-soc-davinci-mcasp-objs:= davinci-mcasp.o snd-soc-davinci-vcif-objs:= davinci-vcif.o diff --git a/sound/soc/davinci/edma-pcm.c b/sound/soc/davinci/edma-pcm.c new file mode 100644 index 000000000000..eeed927a2657 --- /dev/null +++ b/sound/soc/davinci/edma-pcm.c @@ -0,0 +1,77 @@ +/* + * edma-pcm.c - eDMA PCM driver using dmaengine for AM3xxx, AM4xxx + * + * Copyright (C) 2014 Texas Instruments, Inc. + * + * Author: Peter Ujfalusi peter.ujfalusi@ti.com + * + * Based on: sound/soc/tegra/tegra_pcm.c + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + */ + +#include <linux/module.h> +#include <sound/core.h> +#include <sound/pcm.h> +#include <sound/pcm_params.h> +#include <sound/soc.h> +#include <sound/dmaengine_pcm.h> +#include <linux/edma.h> + +static const struct snd_pcm_hardware edma_pcm_hardware = { + .info = SNDRV_PCM_INFO_MMAP | + SNDRV_PCM_INFO_MMAP_VALID | + SNDRV_PCM_INFO_BATCH | + SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_RESUME | + SNDRV_PCM_INFO_INTERLEAVED, + .buffer_bytes_max = 128 * 1024, + .period_bytes_min = 32, + .period_bytes_max = 64 * 1024, + .periods_min = 2, + .periods_max = 19, /* Limit by edma dmaengine driver */ +}; + +static const struct snd_dmaengine_pcm_config edma_dmaengine_pcm_config = { + .pcm_hardware = &edma_pcm_hardware, + .prepare_slave_config = snd_dmaengine_pcm_prepare_slave_config, + .prealloc_buffer_size = 128 * 1024, +}; + +static const struct snd_dmaengine_pcm_config edma_compat_dmaengine_pcm_config = { + .pcm_hardware = &edma_pcm_hardware, + .prepare_slave_config = snd_dmaengine_pcm_prepare_slave_config, + .compat_filter_fn = edma_filter_fn, + .prealloc_buffer_size = 128 * 1024, +}; + +int edma_pcm_platform_register(struct device *dev) +{ + if (dev->of_node) + return snd_dmaengine_pcm_register(dev, + &edma_dmaengine_pcm_config, + SND_DMAENGINE_PCM_FLAG_NO_RESIDUE); + else + return snd_dmaengine_pcm_register(dev, + &edma_compat_dmaengine_pcm_config, + SND_DMAENGINE_PCM_FLAG_NO_RESIDUE | + SND_DMAENGINE_PCM_FLAG_NO_DT | + SND_DMAENGINE_PCM_FLAG_COMPAT); +} +EXPORT_SYMBOL_GPL(edma_pcm_platform_register); + +void edma_pcm_platform_unregister(struct device *dev) +{ + snd_dmaengine_pcm_unregister(dev); +} +EXPORT_SYMBOL_GPL(edma_pcm_platform_unregister); + +MODULE_AUTHOR("Peter Ujfalusi peter.ujfalusi@ti.com"); +MODULE_DESCRIPTION("eDMA PCM ASoC platform driver"); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/davinci/edma-pcm.h b/sound/soc/davinci/edma-pcm.h new file mode 100644 index 000000000000..25dc8c7ff093 --- /dev/null +++ b/sound/soc/davinci/edma-pcm.h @@ -0,0 +1,26 @@ +/* + * edma-pcm.h - eDMA PCM driver using dmaengine for AM3xxx, AM4xxx + * + * Copyright (C) 2014 Texas Instruments, Inc. + * + * Author: Peter Ujfalusi peter.ujfalusi@ti.com + * + * Based on: sound/soc/tegra/tegra_pcm.h + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + */ + +#ifndef __EDMA_PCM_H__ +#define __EDMA_PCM_H__ + +int edma_pcm_platform_register(struct device *dev); +void edma_pcm_platform_unregister(struct device *dev); + +#endif /* __EDMA_PCM_H__ */
On 03/13/2014 10:18 AM, Peter Ujfalusi wrote: [...]
+static const struct snd_pcm_hardware edma_pcm_hardware = {
- .info = SNDRV_PCM_INFO_MMAP |
SNDRV_PCM_INFO_MMAP_VALID |
SNDRV_PCM_INFO_BATCH |
SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_RESUME |
SNDRV_PCM_INFO_INTERLEAVED,
- .buffer_bytes_max = 128 * 1024,
- .period_bytes_min = 32,
- .period_bytes_max = 64 * 1024,
- .periods_min = 2,
- .periods_max = 19, /* Limit by edma dmaengine driver */
+};
The idea is that we can auto-discover all the things using the dma_slave_caps API. Too bad we removed the possibility to specify the maximum number of segments from the API. Maybe we need to add it back. Is the 19 a hard-limit or could it be worked around by software in the dmaengine driver?
+static const struct snd_dmaengine_pcm_config edma_dmaengine_pcm_config = {
- .pcm_hardware = &edma_pcm_hardware,
- .prepare_slave_config = snd_dmaengine_pcm_prepare_slave_config,
- .prealloc_buffer_size = 128 * 1024,
Unless there is a very good reason for exactly this size, just leave it 0 and let the generic dmaengine driver use the default.
+};
+static const struct snd_dmaengine_pcm_config edma_compat_dmaengine_pcm_config = {
- .pcm_hardware = &edma_pcm_hardware,
- .prepare_slave_config = snd_dmaengine_pcm_prepare_slave_config,
- .compat_filter_fn = edma_filter_fn,
- .prealloc_buffer_size = 128 * 1024,
+};
There is no need for different configs for DT and non-DT.
+int edma_pcm_platform_register(struct device *dev) +{
- if (dev->of_node)
return snd_dmaengine_pcm_register(dev,
&edma_dmaengine_pcm_config,
SND_DMAENGINE_PCM_FLAG_NO_RESIDUE);
Since the edma dmaengine driver implements the slave cap API there is no need to manually specify SND_DMAENGINE_PCM_FLAG_NO_RESIDUE manually. But since the edma driver sets the granularity to DMA_RESIDUE_GRANULARITY_DESCRIPTOR in this case the generic dmaengine will not set SND_DMAENGINE_PCM_FLAG_NO_RESIDUE automatically since it assumes that the dmaengine driver is capable of properly reporting the DMA position.
- else
return snd_dmaengine_pcm_register(dev,
&edma_compat_dmaengine_pcm_config,
SND_DMAENGINE_PCM_FLAG_NO_RESIDUE |
SND_DMAENGINE_PCM_FLAG_NO_DT |
SND_DMAENGINE_PCM_FLAG_COMPAT);
If you set the flags to just SND_DMAENGINE_PCM_FLAG_COMPAT it will do the right thing in the generic dmaengine driver depending on whether dev->of_node is set or not.
There is also a devm_ version of snd_dmaengine_pcm_register() it probably makes sense to use it here.
On 03/13/2014 12:28 PM, Lars-Peter Clausen wrote:
On 03/13/2014 10:18 AM, Peter Ujfalusi wrote: [...]
+static const struct snd_pcm_hardware edma_pcm_hardware = { + .info = SNDRV_PCM_INFO_MMAP | + SNDRV_PCM_INFO_MMAP_VALID | + SNDRV_PCM_INFO_BATCH | + SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_RESUME | + SNDRV_PCM_INFO_INTERLEAVED, + .buffer_bytes_max = 128 * 1024, + .period_bytes_min = 32, + .period_bytes_max = 64 * 1024, + .periods_min = 2, + .periods_max = 19, /* Limit by edma dmaengine driver */ +};
The idea is that we can auto-discover all the things using the dma_slave_caps API. Too bad we removed the possibility to specify the maximum number of segments from the API. Maybe we need to add it back. Is the 19 a hard-limit or could it be worked around by software in the dmaengine driver?
The edma dmaengine driver will refuse to configure more than 20 paRAM slots (1 for the channel + 19 for the periods). Depending on the SoC we might have different number of paRAM entries available. The intention with the limit was to prevent cases when we run out of paRAM entires for other devices because audio took most of them.
- +static const struct snd_dmaengine_pcm_config edma_dmaengine_pcm_config
= { + .pcm_hardware = &edma_pcm_hardware, + .prepare_slave_config = snd_dmaengine_pcm_prepare_slave_config, + .prealloc_buffer_size = 128
- 1024,
Unless there is a very good reason for exactly this size, just leave it 0 and let the generic dmaengine driver use the default.
I'd rather keep this here. Since I have the .pcm_hardware the core will ignore the snd_pcm_hardware.buffer_bytes_max when preallocating and it is in fact going to go for: prealloc_buffer_size = 512 * 1024; max_buffer_size = SIZE_MAX;
While I have 128 * 1024 for the snd_pcm_hardware.buffer_bytes_max.
I think as long as I have the .pcm_hardware provided from the edma-pcm driver I will have the .prealloc_buffer_size as well.
+}; + +static const struct snd_dmaengine_pcm_config edma_compat_dmaengine_pcm_config = { + .pcm_hardware = &edma_pcm_hardware, + .prepare_slave_config = snd_dmaengine_pcm_prepare_slave_config, + .compat_filter_fn = edma_filter_fn, + .prealloc_buffer_size = 128 * 1024, +};
There is no need for different configs for DT and non-DT.
- +int edma_pcm_platform_register(struct device *dev) +{ + if
(dev->of_node) + return snd_dmaengine_pcm_register(dev, + &edma_dmaengine_pcm_config, + SND_DMAENGINE_PCM_FLAG_NO_RESIDUE);
Since the edma dmaengine driver implements the slave cap API there is no need to manually specify SND_DMAENGINE_PCM_FLAG_NO_RESIDUE manually. But since the edma driver sets the granularity to DMA_RESIDUE_GRANULARITY_DESCRIPTOR in this case the generic dmaengine will not set SND_DMAENGINE_PCM_FLAG_NO_RESIDUE automatically since it assumes that the dmaengine driver is capable of properly reporting the DMA position.
- else + return snd_dmaengine_pcm_register(dev, +
&edma_compat_dmaengine_pcm_config, + SND_DMAENGINE_PCM_FLAG_NO_RESIDUE | + SND_DMAENGINE_PCM_FLAG_NO_DT | + SND_DMAENGINE_PCM_FLAG_COMPAT);
If you set the flags to just SND_DMAENGINE_PCM_FLAG_COMPAT it will do the right thing in the generic dmaengine driver depending on whether dev->of_node is set or not.
I have missed these in the core. Makes the code simpler for sure.
There is also a devm_ version of snd_dmaengine_pcm_register() it probably makes sense to use it here.
I forgot about the devm_ version. True, I'll use that instead.
On 03/13/2014 12:56 PM, Peter Ujfalusi wrote:
On 03/13/2014 12:28 PM, Lars-Peter Clausen wrote:
On 03/13/2014 10:18 AM, Peter Ujfalusi wrote: [...]
+static const struct snd_pcm_hardware edma_pcm_hardware = { + .info = SNDRV_PCM_INFO_MMAP | + SNDRV_PCM_INFO_MMAP_VALID | + SNDRV_PCM_INFO_BATCH | + SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_RESUME | + SNDRV_PCM_INFO_INTERLEAVED, + .buffer_bytes_max = 128 * 1024, + .period_bytes_min = 32, + .period_bytes_max = 64 * 1024, + .periods_min = 2, + .periods_max = 19, /* Limit by edma dmaengine driver */ +};
The idea is that we can auto-discover all the things using the dma_slave_caps API. Too bad we removed the possibility to specify the maximum number of segments from the API. Maybe we need to add it back. Is the 19 a hard-limit or could it be worked around by software in the dmaengine driver?
The edma dmaengine driver will refuse to configure more than 20 paRAM slots (1 for the channel + 19 for the periods). Depending on the SoC we might have different number of paRAM entries available. The intention with the limit was to prevent cases when we run out of paRAM entires for other devices because audio took most of them.
The reason why we removed the max_segments field from the slave_caps struct was because we though it should be possible to, even when the hardware only supports a fixed amount of segments, emulate support for more in software. If this is not the case with the edma, we need to bring this field back.
On 03/13/2014 12:28 PM, Lars-Peter Clausen wrote:
+int edma_pcm_platform_register(struct device *dev) +{
- if (dev->of_node)
return snd_dmaengine_pcm_register(dev,
&edma_dmaengine_pcm_config,
SND_DMAENGINE_PCM_FLAG_NO_RESIDUE);
Since the edma dmaengine driver implements the slave cap API there is no need to manually specify SND_DMAENGINE_PCM_FLAG_NO_RESIDUE manually. But since the edma driver sets the granularity to DMA_RESIDUE_GRANULARITY_DESCRIPTOR in this case the generic dmaengine will not set SND_DMAENGINE_PCM_FLAG_NO_RESIDUE automatically since it assumes that the dmaengine driver is capable of properly reporting the DMA position.
Hrm, I see. For eDMA I think we can support DMA_RESIDUE_GRANULARITY_SEGMENT granularity. Since according to the documentation the _SEGMENT means that the DMA position will be updated per periods, which is basically the same thing what we are doing at the moment when the granularity is DMA_RESIDUE_GRANULARITY_DESCRIPTOR.
From ALSA point of view at least they are the same: neither of them can report
exact position, the DMA pointer jumps from period to period.
IMHO in the generic dmaengine PCM we should set the SNDRV_PCM_INFO_BATCH for both cases.
On 03/13/2014 02:03 PM, Peter Ujfalusi wrote:
On 03/13/2014 12:28 PM, Lars-Peter Clausen wrote:
+int edma_pcm_platform_register(struct device *dev) +{
- if (dev->of_node)
return snd_dmaengine_pcm_register(dev,
&edma_dmaengine_pcm_config,
SND_DMAENGINE_PCM_FLAG_NO_RESIDUE);
Since the edma dmaengine driver implements the slave cap API there is no need to manually specify SND_DMAENGINE_PCM_FLAG_NO_RESIDUE manually. But since the edma driver sets the granularity to DMA_RESIDUE_GRANULARITY_DESCRIPTOR in this case the generic dmaengine will not set SND_DMAENGINE_PCM_FLAG_NO_RESIDUE automatically since it assumes that the dmaengine driver is capable of properly reporting the DMA position.
Hrm, I see. For eDMA I think we can support DMA_RESIDUE_GRANULARITY_SEGMENT granularity. Since according to the documentation the _SEGMENT means that the DMA position will be updated per periods, which is basically the same thing what we are doing at the moment when the granularity is DMA_RESIDUE_GRANULARITY_DESCRIPTOR. From ALSA point of view at least they are the same: neither of them can report exact position, the DMA pointer jumps from period to period.
IMHO in the generic dmaengine PCM we should set the SNDRV_PCM_INFO_BATCH for both cases.
Ups, sorry mixed up DMA_RESIDUE_GRANULARITY_SEGMENT and DMA_RESIDUE_GRANULARITY_DESCRIPTOR. You can just remove the SND_DMAENGINE_PCM_FLAG_NO_RESIDUE when registering the dmaengine PCM driver and everything will still work as expected.
Use the edma-pcm with AM335x and AM447x SoCs. Keep using the davinci-pcm for DaVinci devices, they can be switched to use the dmaengine based driver later when they are verified to work correctly.
Signed-off-by: Peter Ujfalusi peter.ujfalusi@ti.com --- sound/soc/davinci/davinci-mcasp.c | 46 +++++++++++++++++++++++++++++++++------ 1 file changed, 39 insertions(+), 7 deletions(-)
diff --git a/sound/soc/davinci/davinci-mcasp.c b/sound/soc/davinci/davinci-mcasp.c index b0ae0677f023..604aa982f5c4 100644 --- a/sound/soc/davinci/davinci-mcasp.c +++ b/sound/soc/davinci/davinci-mcasp.c @@ -36,6 +36,7 @@
#include "davinci-pcm.h" #include "davinci-mcasp.h" +#include "edma-pcm.h"
struct davinci_mcasp_context { u32 txfmtctl; @@ -721,10 +722,12 @@ static int davinci_mcasp_startup(struct snd_pcm_substream *substream, { struct davinci_mcasp *mcasp = snd_soc_dai_get_drvdata(dai);
- if (mcasp->version == MCASP_VERSION_4) + if (mcasp->version >= MCASP_VERSION_3) + /* Using dmaengine PCM */ snd_soc_dai_set_dma_data(dai, substream, &mcasp->dma_data[substream->stream]); else + /* Using davinci-pcm */ snd_soc_dai_set_dma_data(dai, substream, mcasp->dma_params);
return 0; @@ -1154,12 +1157,28 @@ static int davinci_mcasp_probe(struct platform_device *pdev) if (ret != 0) goto err_release_clk;
- if (mcasp->version != MCASP_VERSION_4) { + /* Based on the McASP version use different platform driver */ + switch (mcasp->version) { + case MCASP_VERSION_1: + case MCASP_VERSION_2: ret = davinci_soc_platform_register(&pdev->dev); - if (ret) { - dev_err(&pdev->dev, "register PCM failed: %d\n", ret); - goto err_unregister_component; - } + break; + case MCASP_VERSION_3: + ret = edma_pcm_platform_register(&pdev->dev); + break; + case MCASP_VERSION_4: + /* Using omap-pcm as platform driver */ + break; + default: + dev_err(&pdev->dev, "Invalid McASP version: %d\n", + mcasp->version); + ret = -EINVAL; + break; + } + + if (ret) { + dev_err(&pdev->dev, "register PCM failed: %d\n", ret); + goto err_unregister_component; }
return 0; @@ -1177,8 +1196,21 @@ static int davinci_mcasp_remove(struct platform_device *pdev) struct davinci_mcasp *mcasp = dev_get_drvdata(&pdev->dev);
snd_soc_unregister_component(&pdev->dev); - if (mcasp->version != MCASP_VERSION_4) + + switch (mcasp->version) { + case MCASP_VERSION_1: + case MCASP_VERSION_2: davinci_soc_platform_unregister(&pdev->dev); + break; + case MCASP_VERSION_3: + edma_pcm_platform_unregister(&pdev->dev); + break; + case MCASP_VERSION_4: + /* Using omap-pcm as platform driver */ + break; + default: + break; + }
pm_runtime_put_sync(&pdev->dev); pm_runtime_disable(&pdev->dev);
When we boot with non-DT mode the damengine will need the channel number and a filter function in order to get the channel. The filter_data is filled in the DAI driver while the filter_function will be provided by the edma-pcm driver.
Signed-off-by: Peter Ujfalusi peter.ujfalusi@ti.com --- sound/soc/davinci/davinci-mcasp.c | 21 ++++++++++++++++----- 1 file changed, 16 insertions(+), 5 deletions(-)
diff --git a/sound/soc/davinci/davinci-mcasp.c b/sound/soc/davinci/davinci-mcasp.c index 604aa982f5c4..5428615413e2 100644 --- a/sound/soc/davinci/davinci-mcasp.c +++ b/sound/soc/davinci/davinci-mcasp.c @@ -1029,6 +1029,7 @@ nodata: static int davinci_mcasp_probe(struct platform_device *pdev) { struct davinci_pcm_dma_params *dma_params; + struct snd_dmaengine_dai_dma_data *dma_data; struct resource *mem, *ioarea, *res, *dat; struct davinci_mcasp_pdata *pdata; struct davinci_mcasp *mcasp; @@ -1098,6 +1099,7 @@ static int davinci_mcasp_probe(struct platform_device *pdev) mcasp->dat_port = true;
dma_params = &mcasp->dma_params[SNDRV_PCM_STREAM_PLAYBACK]; + dma_data = &mcasp->dma_data[SNDRV_PCM_STREAM_PLAYBACK]; dma_params->asp_chan_q = pdata->asp_chan_q; dma_params->ram_chan_q = pdata->ram_chan_q; dma_params->sram_pool = pdata->sram_pool; @@ -1108,7 +1110,7 @@ static int davinci_mcasp_probe(struct platform_device *pdev) dma_params->dma_addr = mem->start + pdata->tx_dma_offset;
/* Unconditional dmaengine stuff */ - mcasp->dma_data[SNDRV_PCM_STREAM_PLAYBACK].addr = dma_params->dma_addr; + dma_data->addr = dma_params->dma_addr;
res = platform_get_resource(pdev, IORESOURCE_DMA, 0); if (res) @@ -1116,7 +1118,14 @@ static int davinci_mcasp_probe(struct platform_device *pdev) else dma_params->channel = pdata->tx_dma_channel;
+ /* dmaengine filter data for DT and non-DT boot */ + if (pdev->dev.of_node) + dma_data->filter_data = "tx"; + else + dma_data->filter_data = &dma_params->channel; + dma_params = &mcasp->dma_params[SNDRV_PCM_STREAM_CAPTURE]; + dma_data = &mcasp->dma_data[SNDRV_PCM_STREAM_CAPTURE]; dma_params->asp_chan_q = pdata->asp_chan_q; dma_params->ram_chan_q = pdata->ram_chan_q; dma_params->sram_pool = pdata->sram_pool; @@ -1127,7 +1136,7 @@ static int davinci_mcasp_probe(struct platform_device *pdev) dma_params->dma_addr = mem->start + pdata->rx_dma_offset;
/* Unconditional dmaengine stuff */ - mcasp->dma_data[SNDRV_PCM_STREAM_CAPTURE].addr = dma_params->dma_addr; + dma_data->addr = dma_params->dma_addr;
if (mcasp->version < MCASP_VERSION_3) { mcasp->fifo_base = DAVINCI_MCASP_V2_AFIFO_BASE; @@ -1143,9 +1152,11 @@ static int davinci_mcasp_probe(struct platform_device *pdev) else dma_params->channel = pdata->rx_dma_channel;
- /* Unconditional dmaengine stuff */ - mcasp->dma_data[SNDRV_PCM_STREAM_PLAYBACK].filter_data = "tx"; - mcasp->dma_data[SNDRV_PCM_STREAM_CAPTURE].filter_data = "rx"; + /* dmaengine filter data for DT and non-DT boot */ + if (pdev->dev.of_node) + dma_data->filter_data = "rx"; + else + dma_data->filter_data = &dma_params->channel;
dev_set_drvdata(&pdev->dev, mcasp);
Remove the dai startup callback and do the dma_data setup for dmaengine in the dai_probe function.
Signed-off-by: Peter Ujfalusi peter.ujfalusi@ti.com --- sound/soc/davinci/davinci-mcasp.c | 35 +++++++++++++++++++---------------- 1 file changed, 19 insertions(+), 16 deletions(-)
diff --git a/sound/soc/davinci/davinci-mcasp.c b/sound/soc/davinci/davinci-mcasp.c index 5428615413e2..958933614ca4 100644 --- a/sound/soc/davinci/davinci-mcasp.c +++ b/sound/soc/davinci/davinci-mcasp.c @@ -717,31 +717,33 @@ static int davinci_mcasp_trigger(struct snd_pcm_substream *substream, return ret; }
-static int davinci_mcasp_startup(struct snd_pcm_substream *substream, - struct snd_soc_dai *dai) +static const struct snd_soc_dai_ops davinci_mcasp_dai_ops = { + .trigger = davinci_mcasp_trigger, + .hw_params = davinci_mcasp_hw_params, + .set_fmt = davinci_mcasp_set_dai_fmt, + .set_clkdiv = davinci_mcasp_set_clkdiv, + .set_sysclk = davinci_mcasp_set_sysclk, +}; + +static int davinci_mcasp_dai_probe(struct snd_soc_dai *dai) { struct davinci_mcasp *mcasp = snd_soc_dai_get_drvdata(dai);
- if (mcasp->version >= MCASP_VERSION_3) + if (mcasp->version >= MCASP_VERSION_3) { /* Using dmaengine PCM */ - snd_soc_dai_set_dma_data(dai, substream, - &mcasp->dma_data[substream->stream]); - else + dai->playback_dma_data = + &mcasp->dma_data[SNDRV_PCM_STREAM_PLAYBACK]; + dai->capture_dma_data = + &mcasp->dma_data[SNDRV_PCM_STREAM_CAPTURE]; + } else { /* Using davinci-pcm */ - snd_soc_dai_set_dma_data(dai, substream, mcasp->dma_params); + dai->playback_dma_data = mcasp->dma_params; + dai->capture_dma_data = mcasp->dma_params; + }
return 0; }
-static const struct snd_soc_dai_ops davinci_mcasp_dai_ops = { - .startup = davinci_mcasp_startup, - .trigger = davinci_mcasp_trigger, - .hw_params = davinci_mcasp_hw_params, - .set_fmt = davinci_mcasp_set_dai_fmt, - .set_clkdiv = davinci_mcasp_set_clkdiv, - .set_sysclk = davinci_mcasp_set_sysclk, -}; - #ifdef CONFIG_PM_SLEEP static int davinci_mcasp_suspend(struct snd_soc_dai *dai) { @@ -795,6 +797,7 @@ static int davinci_mcasp_resume(struct snd_soc_dai *dai) static struct snd_soc_dai_driver davinci_mcasp_dai[] = { { .name = "davinci-mcasp.0", + .probe = davinci_mcasp_dai_probe, .suspend = davinci_mcasp_suspend, .resume = davinci_mcasp_resume, .playback = {
We need to place constraint on the period size (and indirectly to buffer size) if the read or write AFIFO is enabled and it is configured for more than one word otherwise the DMA will fail in buffer configuration where the sizes are not aligned with the requested FIFO configuration.
Signed-off-by: Peter Ujfalusi peter.ujfalusi@ti.com --- sound/soc/davinci/davinci-mcasp.c | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+)
diff --git a/sound/soc/davinci/davinci-mcasp.c b/sound/soc/davinci/davinci-mcasp.c index 958933614ca4..17d1112bce24 100644 --- a/sound/soc/davinci/davinci-mcasp.c +++ b/sound/soc/davinci/davinci-mcasp.c @@ -717,7 +717,27 @@ static int davinci_mcasp_trigger(struct snd_pcm_substream *substream, return ret; }
+static int davinci_mcasp_startup(struct snd_pcm_substream *substream, + struct snd_soc_dai *cpu_dai) +{ + struct davinci_mcasp *mcasp = snd_soc_dai_get_drvdata(cpu_dai); + int afifo_numevt; + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + afifo_numevt = mcasp->txnumevt; + else + afifo_numevt = mcasp->rxnumevt; + + if (afifo_numevt > 1) + snd_pcm_hw_constraint_step(substream->runtime, 0, + SNDRV_PCM_HW_PARAM_PERIOD_SIZE, + afifo_numevt); + + return 0; +} + static const struct snd_soc_dai_ops davinci_mcasp_dai_ops = { + .startup = davinci_mcasp_startup, .trigger = davinci_mcasp_trigger, .hw_params = davinci_mcasp_hw_params, .set_fmt = davinci_mcasp_set_dai_fmt,
On Thu, Mar 13, 2014 at 11:18:22AM +0200, Peter Ujfalusi wrote:
With this series AM335x and AM447x will use the dmaengine PCM for audio. The daVinci devices will keep using the davinci-pcm for now since I do not have means to test them but the code is written in a way that they can be switched to use the edma-pcm easily (DA850/OMAP-L138 has been tested and audio was working fine).
Can you please not resend the ASoC portions of this series until the EDMA portion is stable (and ideally committed)? I'm not quite sure why but every EDMA patch series seems to go through lots of iterations which gets rather noisy - my initial reaction to seeing EDMA in a subject is to just delete the thread.
On 03/13/2014 03:46 PM, Mark Brown wrote:
On Thu, Mar 13, 2014 at 11:18:22AM +0200, Peter Ujfalusi wrote:
With this series AM335x and AM447x will use the dmaengine PCM for audio. The daVinci devices will keep using the davinci-pcm for now since I do not have means to test them but the code is written in a way that they can be switched to use the edma-pcm easily (DA850/OMAP-L138 has been tested and audio was working fine).
Can you please not resend the ASoC portions of this series until the EDMA portion is stable (and ideally committed)? I'm not quite sure why but every EDMA patch series seems to go through lots of iterations which gets rather noisy - my initial reaction to seeing EDMA in a subject is to just delete the thread.
Sure, I'm not planning to spam alsa-devel however the switch to dmaengine PCM for AM335x/AM447x needs the edma patches, otherwise it is not going to work. AFAIK I have not sent patch to edma as of now so I have no idea how long it takes to get stuff in (the good thing is that the edma part does not touch DT, which is alone can reduce the number of iterations). Anyway, what I can - and already did is: Reorder the ASoC patches and I can leave out the switch to dmaengine PCM patch for davinci-mcasp for now. The edma-pcm and the other patches for McASP can go in right away (I will not add the edma-pcm to the Makefile/Kconfig to avoid issues).
I will detach the edma stuff and follow it up outside of alsa-devel.
The only thing I'm afraid off is that it is going to take two release cycle to get this in: first cycle edma part, next cycle for the ASoC to switch to use the edma-pcm.
On Fri, Mar 14, 2014 at 11:38:58AM +0200, Peter Ujfalusi wrote:
The only thing I'm afraid off is that it is going to take two release cycle to get this in: first cycle edma part, next cycle for the ASoC to switch to use the edma-pcm.
We can do a cross tree merge, or the EDMA code can be applied to ASoC directly if that's what people want.
participants (4)
-
Lars-Peter Clausen
-
Mark Brown
-
Peter Ujfalusi
-
Shevchenko, Andriy