Commit cf80e158 authored by Peter Ujfalusi's avatar Peter Ujfalusi Committed by Liam Girdwood

ASoC: omap-mcbsp: Support for sDMA packet mode

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: default avatarPeter Ujfalusi <peter.ujfalusi@nokia.com>
Acked-by: default avatarJarkko Nikula <jhnikula@gmail.com>
Acked-by: default avatarMark Brown <broonie@opensource.wolfsonmicro.com>
Signed-off-by: default avatarLiam Girdwood <lrg@slimlogic.co.uk>
parent 15d01430
...@@ -155,13 +155,23 @@ static void omap_mcbsp_set_threshold(struct snd_pcm_substream *substream) ...@@ -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_pcm_runtime *rtd = substream->private_data;
struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai; 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_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 dma_op_mode = omap_mcbsp_get_dma_op_mode(mcbsp_data->bus_id);
int words; int words;
dma_data = snd_soc_dai_get_dma_data(rtd->dai->cpu_dai, substream);
/* TODO: Currently, MODE_ELEMENT == MODE_FRAME */ /* TODO: Currently, MODE_ELEMENT == MODE_FRAME */
if (dma_op_mode == MCBSP_DMA_MODE_THRESHOLD) 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); (mcbsp_data->wlen / 8);
else else
words = 1; words = 1;
...@@ -351,6 +361,7 @@ static int omap_mcbsp_dai_hw_params(struct snd_pcm_substream *substream, ...@@ -351,6 +361,7 @@ static int omap_mcbsp_dai_hw_params(struct snd_pcm_substream *substream,
struct omap_pcm_dma_data *dma_data; struct omap_pcm_dma_data *dma_data;
int dma, bus_id = mcbsp_data->bus_id; int dma, bus_id = mcbsp_data->bus_id;
int wlen, channels, wpf, sync_mode = OMAP_DMA_SYNC_ELEMENT; int wlen, channels, wpf, sync_mode = OMAP_DMA_SYNC_ELEMENT;
int pkt_size = 0;
unsigned long port; unsigned long port;
unsigned int format, div, framesize, master; unsigned int format, div, framesize, master;
...@@ -373,9 +384,11 @@ static int omap_mcbsp_dai_hw_params(struct snd_pcm_substream *substream, ...@@ -373,9 +384,11 @@ static int omap_mcbsp_dai_hw_params(struct snd_pcm_substream *substream,
switch (params_format(params)) { switch (params_format(params)) {
case SNDRV_PCM_FORMAT_S16_LE: case SNDRV_PCM_FORMAT_S16_LE:
dma_data->data_type = OMAP_DMA_DATA_TYPE_S16; dma_data->data_type = OMAP_DMA_DATA_TYPE_S16;
wlen = 16;
break; break;
case SNDRV_PCM_FORMAT_S32_LE: case SNDRV_PCM_FORMAT_S32_LE:
dma_data->data_type = OMAP_DMA_DATA_TYPE_S32; dma_data->data_type = OMAP_DMA_DATA_TYPE_S32;
wlen = 32;
break; break;
default: default:
return -EINVAL; return -EINVAL;
...@@ -384,14 +397,53 @@ static int omap_mcbsp_dai_hw_params(struct snd_pcm_substream *substream, ...@@ -384,14 +397,53 @@ static int omap_mcbsp_dai_hw_params(struct snd_pcm_substream *substream,
dma_data->set_threshold = omap_mcbsp_set_threshold; dma_data->set_threshold = omap_mcbsp_set_threshold;
/* TODO: Currently, MODE_ELEMENT == MODE_FRAME */ /* TODO: Currently, MODE_ELEMENT == MODE_FRAME */
if (omap_mcbsp_get_dma_op_mode(bus_id) == if (omap_mcbsp_get_dma_op_mode(bus_id) ==
MCBSP_DMA_MODE_THRESHOLD) MCBSP_DMA_MODE_THRESHOLD) {
sync_mode = OMAP_DMA_SYNC_FRAME; 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->name = substream->stream ? "Audio Capture" : "Audio Playback";
dma_data->dma_req = dma; dma_data->dma_req = dma;
dma_data->port_addr = port; dma_data->port_addr = port;
dma_data->sync_mode = sync_mode; dma_data->sync_mode = sync_mode;
dma_data->packet_size = pkt_size;
snd_soc_dai_set_dma_data(cpu_dai, substream, dma_data); 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, ...@@ -419,7 +471,6 @@ static int omap_mcbsp_dai_hw_params(struct snd_pcm_substream *substream,
switch (params_format(params)) { switch (params_format(params)) {
case SNDRV_PCM_FORMAT_S16_LE: case SNDRV_PCM_FORMAT_S16_LE:
/* Set word lengths */ /* Set word lengths */
wlen = 16;
regs->rcr2 |= RWDLEN2(OMAP_MCBSP_WORD_16); regs->rcr2 |= RWDLEN2(OMAP_MCBSP_WORD_16);
regs->rcr1 |= RWDLEN1(OMAP_MCBSP_WORD_16); regs->rcr1 |= RWDLEN1(OMAP_MCBSP_WORD_16);
regs->xcr2 |= XWDLEN2(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, ...@@ -427,7 +478,6 @@ static int omap_mcbsp_dai_hw_params(struct snd_pcm_substream *substream,
break; break;
case SNDRV_PCM_FORMAT_S32_LE: case SNDRV_PCM_FORMAT_S32_LE:
/* Set word lengths */ /* Set word lengths */
wlen = 32;
regs->rcr2 |= RWDLEN2(OMAP_MCBSP_WORD_32); regs->rcr2 |= RWDLEN2(OMAP_MCBSP_WORD_32);
regs->rcr1 |= RWDLEN1(OMAP_MCBSP_WORD_32); regs->rcr1 |= RWDLEN1(OMAP_MCBSP_WORD_32);
regs->xcr2 |= XWDLEN2(OMAP_MCBSP_WORD_32); regs->xcr2 |= XWDLEN2(OMAP_MCBSP_WORD_32);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment