[alsa-devel] [RFC 0/4] ASoC: OMAP3: Extended threshold mode
Hello,
The following series extends the current threshold implementation for OMAP3 class of devices.
The original threshold implementation has limitation on the size of the period, which can not be bigger than the McBSP port's FIFO size. This limitation makes it hard to use any power saving on McBSP ports other than McBSP2 (McBSP2 has 1280 word buffer, other ports have only 128 word long buffer), since the maximum period size limit is really small.
After the series the limitation on the period size can be lifted, so user space can ask for any period size on any of the McBSP ports.
In order to achieve this, I introduce the following changes, when McBSP is in threshold mode:
if (period_words <= max_threshold) Configure the McBSP/sDMA as it used to be: McBSP threshold = sDMA frame size = period size sDMA packet size = 0
Otherwise (period_words > max_threshold) McBSP threshold = sDMA packet size sDMA frame size = period size
In this case the code will look for the biggest possible threshold value, which can evenly divide the period size, and use that for threshold/packet size.
The first two patch is for preparation.
The series has been generated against: git://git.kernel.org/pub/scm/linux/kernel/git/tiwai/sound-2.6.git:topic/asoc
What do you think?
--- Peter Ujfalusi (4): ASoC: omap-mcbsp: Code cleanup in omap_mcbsp_dai_hw_params ASoC: omap-mcbsp: Restructure the code within omap_mcbsp_dai_hw_params ASoC: omap-mcbsp: Support for sDMA packet mode ASoC: omap-mcbsp: Remove period size constraint in THRESHOLD mode
sound/soc/omap/omap-mcbsp.c | 137 ++++++++++++++++++++++++------------------- 1 files changed, 76 insertions(+), 61 deletions(-)
-- 1.7.2
To make the code a bit more readable, change the indexed references to the omap_mcbsp_dai_dma_params elements with pointer.
Signed-off-by: Peter Ujfalusi peter.ujfalusi@nokia.com --- sound/soc/omap/omap-mcbsp.c | 25 +++++++++++-------------- 1 files changed, 11 insertions(+), 14 deletions(-)
diff --git a/sound/soc/omap/omap-mcbsp.c b/sound/soc/omap/omap-mcbsp.c index aebd3af..88ca71c 100644 --- a/sound/soc/omap/omap-mcbsp.c +++ b/sound/soc/omap/omap-mcbsp.c @@ -348,11 +348,13 @@ static int omap_mcbsp_dai_hw_params(struct snd_pcm_substream *substream, struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai; struct omap_mcbsp_data *mcbsp_data = to_mcbsp(cpu_dai->private_data); struct omap_mcbsp_reg_cfg *regs = &mcbsp_data->regs; - int dma, bus_id = mcbsp_data->bus_id, id = cpu_dai->id; + struct omap_pcm_dma_data *dma_data; + int dma, bus_id = mcbsp_data->bus_id; int wlen, channels, wpf, sync_mode = OMAP_DMA_SYNC_ELEMENT; unsigned long port; unsigned int format, div, framesize, master;
+ dma_data = &omap_mcbsp_dai_dma_params[cpu_dai->id][substream->stream]; if (cpu_class_is_omap1()) { dma = omap1_dma_reqs[bus_id][substream->stream]; port = omap1_mcbsp_port[bus_id][substream->stream]; @@ -365,8 +367,7 @@ static int omap_mcbsp_dai_hw_params(struct snd_pcm_substream *substream, } else if (cpu_is_omap343x()) { dma = omap24xx_dma_reqs[bus_id][substream->stream]; port = omap34xx_mcbsp_port[bus_id][substream->stream]; - omap_mcbsp_dai_dma_params[id][substream->stream].set_threshold = - omap_mcbsp_set_threshold; + dma_data->set_threshold = omap_mcbsp_set_threshold; /* TODO: Currently, MODE_ELEMENT == MODE_FRAME */ if (omap_mcbsp_get_dma_op_mode(bus_id) == MCBSP_DMA_MODE_THRESHOLD) @@ -374,26 +375,22 @@ static int omap_mcbsp_dai_hw_params(struct snd_pcm_substream *substream, } else { return -ENODEV; } - omap_mcbsp_dai_dma_params[id][substream->stream].name = - substream->stream ? "Audio Capture" : "Audio Playback"; - omap_mcbsp_dai_dma_params[id][substream->stream].dma_req = dma; - omap_mcbsp_dai_dma_params[id][substream->stream].port_addr = port; - omap_mcbsp_dai_dma_params[id][substream->stream].sync_mode = sync_mode; + dma_data->name = substream->stream ? "Audio Capture" : "Audio Playback"; + dma_data->dma_req = dma; + dma_data->port_addr = port; + dma_data->sync_mode = sync_mode; switch (params_format(params)) { case SNDRV_PCM_FORMAT_S16_LE: - omap_mcbsp_dai_dma_params[id][substream->stream].data_type = - OMAP_DMA_DATA_TYPE_S16; + dma_data->data_type = OMAP_DMA_DATA_TYPE_S16; break; case SNDRV_PCM_FORMAT_S32_LE: - omap_mcbsp_dai_dma_params[id][substream->stream].data_type = - OMAP_DMA_DATA_TYPE_S32; + dma_data->data_type = OMAP_DMA_DATA_TYPE_S32; break; default: return -EINVAL; }
- snd_soc_dai_set_dma_data(cpu_dai, substream, - &omap_mcbsp_dai_dma_params[id][substream->stream]); + snd_soc_dai_set_dma_data(cpu_dai, substream, dma_data);
if (mcbsp_data->configured) { /* McBSP already configured by another stream */
In preparation for the extended threshold mode (sDMA packet mode support), the code need to be restructured.
Signed-off-by: Peter Ujfalusi peter.ujfalusi@nokia.com --- sound/soc/omap/omap-mcbsp.c | 21 ++++++++++++--------- 1 files changed, 12 insertions(+), 9 deletions(-)
diff --git a/sound/soc/omap/omap-mcbsp.c b/sound/soc/omap/omap-mcbsp.c index 88ca71c..4ac8a08 100644 --- a/sound/soc/omap/omap-mcbsp.c +++ b/sound/soc/omap/omap-mcbsp.c @@ -367,18 +367,9 @@ static int omap_mcbsp_dai_hw_params(struct snd_pcm_substream *substream, } else if (cpu_is_omap343x()) { dma = omap24xx_dma_reqs[bus_id][substream->stream]; port = omap34xx_mcbsp_port[bus_id][substream->stream]; - dma_data->set_threshold = omap_mcbsp_set_threshold; - /* TODO: Currently, MODE_ELEMENT == MODE_FRAME */ - if (omap_mcbsp_get_dma_op_mode(bus_id) == - MCBSP_DMA_MODE_THRESHOLD) - sync_mode = OMAP_DMA_SYNC_FRAME; } else { return -ENODEV; } - dma_data->name = substream->stream ? "Audio Capture" : "Audio Playback"; - dma_data->dma_req = dma; - dma_data->port_addr = port; - dma_data->sync_mode = sync_mode; switch (params_format(params)) { case SNDRV_PCM_FORMAT_S16_LE: dma_data->data_type = OMAP_DMA_DATA_TYPE_S16; @@ -389,6 +380,18 @@ static int omap_mcbsp_dai_hw_params(struct snd_pcm_substream *substream, default: return -EINVAL; } + if (cpu_is_omap343x()) { + dma_data->set_threshold = omap_mcbsp_set_threshold; + /* TODO: Currently, MODE_ELEMENT == MODE_FRAME */ + if (omap_mcbsp_get_dma_op_mode(bus_id) == + MCBSP_DMA_MODE_THRESHOLD) + sync_mode = OMAP_DMA_SYNC_FRAME; + } + + dma_data->name = substream->stream ? "Audio Capture" : "Audio Playback"; + dma_data->dma_req = dma; + dma_data->port_addr = port; + dma_data->sync_mode = sync_mode;
snd_soc_dai_set_dma_data(cpu_dai, substream, dma_data);
Utilize the sDMA controller's packet syncronization mode, when the McBSP FIFO is in use (by extending the THRESHOLD mode). When the sDMA is configured for packet mode, the sDMA frame size does not need to match with the McBSP threshold configuration. Uppon DMA request the sDMA will transfer packet size number of words, and still trigger interrupt on frame boundary.
The patch extends the original THRESHOLD mode by doing the following:
if (period_words <= max_threshold) Current THRESHOLD mode configuration
Otherwise (period_words > max_threshold) McBSP threshold = sDMA packet size sDMA frame size = period size
With the extended THRESHOLD mode we can remove the constraint for the maximum period size, since if the period size is bigger than the maximum allowed threshold, than the driver will switch to packet mode, and picks the best (biggest) threshold value, which can divide evenly the period size.
Signed-off-by: Peter Ujfalusi peter.ujfalusi@nokia.com --- sound/soc/omap/omap-mcbsp.c | 62 ++++++++++++++++++++++++++++++++++++++---- 1 files changed, 56 insertions(+), 6 deletions(-)
diff --git a/sound/soc/omap/omap-mcbsp.c b/sound/soc/omap/omap-mcbsp.c index 4ac8a08..9fd00b0 100644 --- a/sound/soc/omap/omap-mcbsp.c +++ b/sound/soc/omap/omap-mcbsp.c @@ -155,13 +155,23 @@ static void omap_mcbsp_set_threshold(struct snd_pcm_substream *substream) struct snd_soc_pcm_runtime *rtd = substream->private_data; struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai; struct omap_mcbsp_data *mcbsp_data = to_mcbsp(cpu_dai->private_data); + struct omap_pcm_dma_data *dma_data; int dma_op_mode = omap_mcbsp_get_dma_op_mode(mcbsp_data->bus_id); int words;
+ dma_data = snd_soc_dai_get_dma_data(rtd->dai->cpu_dai, substream); + /* TODO: Currently, MODE_ELEMENT == MODE_FRAME */ if (dma_op_mode == MCBSP_DMA_MODE_THRESHOLD) - /* The FIFO size depends on the McBSP word configuration */ - words = snd_pcm_lib_period_bytes(substream) / + /* + * Configure McBSP threshold based on either: + * packet_size, when the sDMA is in packet mode, or + * based on the period size. + */ + if (dma_data->packet_size) + words = dma_data->packet_size; + else + words = snd_pcm_lib_period_bytes(substream) / (mcbsp_data->wlen / 8); else words = 1; @@ -351,6 +361,7 @@ static int omap_mcbsp_dai_hw_params(struct snd_pcm_substream *substream, struct omap_pcm_dma_data *dma_data; int dma, bus_id = mcbsp_data->bus_id; int wlen, channels, wpf, sync_mode = OMAP_DMA_SYNC_ELEMENT; + int pkt_size = 0; unsigned long port; unsigned int format, div, framesize, master;
@@ -373,9 +384,11 @@ static int omap_mcbsp_dai_hw_params(struct snd_pcm_substream *substream, switch (params_format(params)) { case SNDRV_PCM_FORMAT_S16_LE: dma_data->data_type = OMAP_DMA_DATA_TYPE_S16; + wlen = 16; break; case SNDRV_PCM_FORMAT_S32_LE: dma_data->data_type = OMAP_DMA_DATA_TYPE_S32; + wlen = 32; break; default: return -EINVAL; @@ -384,14 +397,53 @@ static int omap_mcbsp_dai_hw_params(struct snd_pcm_substream *substream, dma_data->set_threshold = omap_mcbsp_set_threshold; /* TODO: Currently, MODE_ELEMENT == MODE_FRAME */ if (omap_mcbsp_get_dma_op_mode(bus_id) == - MCBSP_DMA_MODE_THRESHOLD) - sync_mode = OMAP_DMA_SYNC_FRAME; + MCBSP_DMA_MODE_THRESHOLD) { + int period_words, max_thrsh; + + period_words = params_period_bytes(params) / (wlen / 8); + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + max_thrsh = omap_mcbsp_get_max_tx_threshold( + mcbsp_data->bus_id); + else + max_thrsh = omap_mcbsp_get_max_rx_threshold( + mcbsp_data->bus_id); + /* + * If the period contains less or equal number of words, + * we are using the original threshold mode setup: + * McBSP threshold = sDMA frame size = period_size + * Otherwise we switch to sDMA packet mode: + * McBSP threshold = sDMA packet size + * sDMA frame size = period size + */ + if (period_words > max_thrsh) { + int divider = 0; + + /* + * Look for the biggest threshold value, which + * divides the period size evenly. + */ + divider = period_words / max_thrsh; + if (period_words % max_thrsh) + divider++; + while (period_words % divider && + divider < period_words) + divider++; + if (divider == period_words) + return -EINVAL; + + pkt_size = period_words / divider; + sync_mode = OMAP_DMA_SYNC_PACKET; + } else { + sync_mode = OMAP_DMA_SYNC_FRAME; + } + } }
dma_data->name = substream->stream ? "Audio Capture" : "Audio Playback"; dma_data->dma_req = dma; dma_data->port_addr = port; dma_data->sync_mode = sync_mode; + dma_data->packet_size = pkt_size;
snd_soc_dai_set_dma_data(cpu_dai, substream, dma_data);
@@ -419,7 +471,6 @@ static int omap_mcbsp_dai_hw_params(struct snd_pcm_substream *substream, switch (params_format(params)) { case SNDRV_PCM_FORMAT_S16_LE: /* Set word lengths */ - wlen = 16; regs->rcr2 |= RWDLEN2(OMAP_MCBSP_WORD_16); regs->rcr1 |= RWDLEN1(OMAP_MCBSP_WORD_16); regs->xcr2 |= XWDLEN2(OMAP_MCBSP_WORD_16); @@ -427,7 +478,6 @@ static int omap_mcbsp_dai_hw_params(struct snd_pcm_substream *substream, break; case SNDRV_PCM_FORMAT_S32_LE: /* Set word lengths */ - wlen = 32; regs->rcr2 |= RWDLEN2(OMAP_MCBSP_WORD_32); regs->rcr1 |= RWDLEN1(OMAP_MCBSP_WORD_32); regs->xcr2 |= XWDLEN2(OMAP_MCBSP_WORD_32);
The use of sDMA packet mode in THRESHOLD mode removes the restriction on the period size. With the extended THRESHOLD mode user space can ask for any period size it wishes, and the driver will configure the sDMA and McBSP FIFO accordingly.
Replace the hw_rule for the period size with static constraint, which will make sure that the period size will be always even (to avoid prime period size, which could be possible in mono stream)
Signed-off-by: Peter Ujfalusi peter.ujfalusi@nokia.com --- sound/soc/omap/omap-mcbsp.c | 43 ++++--------------------------------------- 1 files changed, 4 insertions(+), 39 deletions(-)
diff --git a/sound/soc/omap/omap-mcbsp.c b/sound/soc/omap/omap-mcbsp.c index 9fd00b0..86f2139 100644 --- a/sound/soc/omap/omap-mcbsp.c +++ b/sound/soc/omap/omap-mcbsp.c @@ -202,31 +202,6 @@ static int omap_mcbsp_hwrule_min_buffersize(struct snd_pcm_hw_params *params, return snd_interval_refine(buffer_size, &frames); }
-static int omap_mcbsp_hwrule_max_periodsize(struct snd_pcm_hw_params *params, - struct snd_pcm_hw_rule *rule) -{ - struct snd_interval *period_size = hw_param_interval(params, - SNDRV_PCM_HW_PARAM_PERIOD_SIZE); - struct snd_interval *channels = hw_param_interval(params, - SNDRV_PCM_HW_PARAM_CHANNELS); - struct snd_pcm_substream *substream = rule->private; - struct snd_soc_pcm_runtime *rtd = substream->private_data; - struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai; - struct omap_mcbsp_data *mcbsp_data = to_mcbsp(cpu_dai->private_data); - struct snd_interval frames; - int size; - - snd_interval_any(&frames); - if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) - size = omap_mcbsp_get_max_tx_threshold(mcbsp_data->bus_id); - else - size = omap_mcbsp_get_max_rx_threshold(mcbsp_data->bus_id); - - frames.max = size / channels->min; - frames.integer = 1; - return snd_interval_refine(period_size, &frames); -} - static int omap_mcbsp_dai_startup(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) { @@ -255,10 +230,8 @@ static int omap_mcbsp_dai_startup(struct snd_pcm_substream *substream, * 4 channels: size is 128 / 4 = 32 frames (4 * 32 words) */ if (cpu_is_omap343x()) { - int dma_op_mode = omap_mcbsp_get_dma_op_mode(bus_id); - /* - * The first rule is for the buffer size, we should not allow + * Rule for the buffer size. We should not allow * smaller buffer than the FIFO size to avoid underruns */ snd_pcm_hw_rule_add(substream->runtime, 0, @@ -267,17 +240,9 @@ static int omap_mcbsp_dai_startup(struct snd_pcm_substream *substream, mcbsp_data, SNDRV_PCM_HW_PARAM_BUFFER_SIZE, -1);
- /* - * In case of threshold mode, the rule will ensure, that the - * period size is not bigger than the maximum allowed threshold - * value. - */ - if (dma_op_mode == MCBSP_DMA_MODE_THRESHOLD) - snd_pcm_hw_rule_add(substream->runtime, 0, - SNDRV_PCM_HW_PARAM_CHANNELS, - omap_mcbsp_hwrule_max_periodsize, - substream, - SNDRV_PCM_HW_PARAM_PERIOD_SIZE, -1); + /* Make sure, that the period size is always even */ + snd_pcm_hw_constraint_step(substream->runtime, 0, + SNDRV_PCM_HW_PARAM_PERIOD_SIZE, 2); }
return err;
On Thu, 29 Jul 2010 09:51:24 +0300 Peter Ujfalusi peter.ujfalusi@nokia.com wrote:
Hello,
The following series extends the current threshold implementation for OMAP3 class of devices.
The original threshold implementation has limitation on the size of the period, which can not be bigger than the McBSP port's FIFO size. This limitation makes it hard to use any power saving on McBSP ports other than McBSP2 (McBSP2 has 1280 word buffer, other ports have only 128 word long buffer), since the maximum period size limit is really small.
...
What do you think?
Well, cleanups are always good as well as making the code more uniform, and if the same set also improves power savings, what else I can do :-)
Acked-by: Jarkko Nikula jhnikula@gmail.com
(this DMA packet transfer is used also with McPDM on OMAP4, trial showed that it can work with McBSP on OMAP3 and as we can see, Peter now implemented it)
On Thu, Jul 29, 2010 at 09:51:24AM +0300, Peter Ujfalusi wrote:
After the series the limitation on the period size can be lifted, so user space can ask for any period size on any of the McBSP ports.
All looks OK from a generic ASoC point of view
Acked-by: Mark Brown broonie@opensource.wolfsonmicro.com
On Thu, 2010-07-29 at 10:22 -0700, Mark Brown wrote:
On Thu, Jul 29, 2010 at 09:51:24AM +0300, Peter Ujfalusi wrote:
After the series the limitation on the period size can be lifted, so user space can ask for any period size on any of the McBSP ports.
All looks OK from a generic ASoC point of view
Acked-by: Mark Brown broonie@opensource.wolfsonmicro.com
All applied.
Liam
participants (4)
-
Jarkko Nikula
-
Liam Girdwood
-
Mark Brown
-
Peter Ujfalusi