diff options
author | Lars-Peter Clausen <lars@metafoo.de> | 2013-05-28 13:22:14 -0400 |
---|---|---|
committer | Mark Brown <broonie@opensource.wolfsonmicro.com> | 2013-05-30 07:33:40 -0400 |
commit | 8b5e2e396b589119bcc9c6a382a999e0202bae18 (patch) | |
tree | bc5057b3426563cace2ae075bfc2b5fa5ba37c11 /sound/soc/blackfin/bf5xx-i2s-pcm.c | |
parent | a3935a29f68c261d31b41c896f95c9333b615abf (diff) |
ASoC: blackfin: bf5xx-i2s: Add support for TDM mode
The bf5xx-i2s{,-pcm} and bf5xx-tdm{-pcm} drivers are nearly identical. Both are
for the same hardware each supporting a slight different subset of the hardware.
The bf5xx-i2s driver supports 2 channel I2S mode while the bf5xx-tdm driver
supports TDM mode and channel remapping. This patch adds support for TDM mode
and channel remapping to the bf5xx-i2s driver so that we'll eventually be able
to retire the bf5xx-tdm driver. Unfortunately the hardware is fixed to using 8
channels in TDM mode. The bf5xx-tdm driver jumps through a few hoops to make it
work well with other channel counts as well:
* Don't support mmap
* Translate between internal frame size (which is always 8 * sample_size)
and ALSA frame size (which depends on the channel count)
* Have special copy and silence callbacks which are aware of the mismatch
between internal and ALSA frame size
* Reduce the maximum buffer size to ensure that there is enough headroom for
dummy data.
The bf5xx-i2s driver is going to use the same mechanisms when being used int
TDM mode.
Signed-off-by: Lars-Peter Clausen <lars@metafoo.de>
Signed-off-by: Mark Brown <broonie@opensource.wolfsonmicro.com>
Diffstat (limited to 'sound/soc/blackfin/bf5xx-i2s-pcm.c')
-rw-r--r-- | sound/soc/blackfin/bf5xx-i2s-pcm.c | 118 |
1 files changed, 116 insertions, 2 deletions
diff --git a/sound/soc/blackfin/bf5xx-i2s-pcm.c b/sound/soc/blackfin/bf5xx-i2s-pcm.c index 9931a18c962e..9cb4a80df98e 100644 --- a/sound/soc/blackfin/bf5xx-i2s-pcm.c +++ b/sound/soc/blackfin/bf5xx-i2s-pcm.c | |||
@@ -40,6 +40,7 @@ | |||
40 | #include <asm/dma.h> | 40 | #include <asm/dma.h> |
41 | 41 | ||
42 | #include "bf5xx-sport.h" | 42 | #include "bf5xx-sport.h" |
43 | #include "bf5xx-i2s-pcm.h" | ||
43 | 44 | ||
44 | static void bf5xx_dma_irq(void *data) | 45 | static void bf5xx_dma_irq(void *data) |
45 | { | 46 | { |
@@ -49,7 +50,6 @@ static void bf5xx_dma_irq(void *data) | |||
49 | 50 | ||
50 | static const struct snd_pcm_hardware bf5xx_pcm_hardware = { | 51 | static const struct snd_pcm_hardware bf5xx_pcm_hardware = { |
51 | .info = SNDRV_PCM_INFO_INTERLEAVED | | 52 | .info = SNDRV_PCM_INFO_INTERLEAVED | |
52 | SNDRV_PCM_INFO_MMAP | | ||
53 | SNDRV_PCM_INFO_MMAP_VALID | | 53 | SNDRV_PCM_INFO_MMAP_VALID | |
54 | SNDRV_PCM_INFO_BLOCK_TRANSFER, | 54 | SNDRV_PCM_INFO_BLOCK_TRANSFER, |
55 | .formats = SNDRV_PCM_FMTBIT_S16_LE | | 55 | .formats = SNDRV_PCM_FMTBIT_S16_LE | |
@@ -66,7 +66,16 @@ static const struct snd_pcm_hardware bf5xx_pcm_hardware = { | |||
66 | static int bf5xx_pcm_hw_params(struct snd_pcm_substream *substream, | 66 | static int bf5xx_pcm_hw_params(struct snd_pcm_substream *substream, |
67 | struct snd_pcm_hw_params *params) | 67 | struct snd_pcm_hw_params *params) |
68 | { | 68 | { |
69 | return snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(params)); | 69 | struct snd_soc_pcm_runtime *rtd = substream->private_data; |
70 | unsigned int buffer_size = params_buffer_bytes(params); | ||
71 | struct bf5xx_i2s_pcm_data *dma_data; | ||
72 | |||
73 | dma_data = snd_soc_dai_get_dma_data(rtd->cpu_dai, substream); | ||
74 | |||
75 | if (dma_data->tdm_mode) | ||
76 | buffer_size = buffer_size / params_channels(params) * 8; | ||
77 | |||
78 | return snd_pcm_lib_malloc_pages(substream, buffer_size); | ||
70 | } | 79 | } |
71 | 80 | ||
72 | static int bf5xx_pcm_hw_free(struct snd_pcm_substream *substream) | 81 | static int bf5xx_pcm_hw_free(struct snd_pcm_substream *substream) |
@@ -78,9 +87,16 @@ static int bf5xx_pcm_hw_free(struct snd_pcm_substream *substream) | |||
78 | 87 | ||
79 | static int bf5xx_pcm_prepare(struct snd_pcm_substream *substream) | 88 | static int bf5xx_pcm_prepare(struct snd_pcm_substream *substream) |
80 | { | 89 | { |
90 | struct snd_soc_pcm_runtime *rtd = substream->private_data; | ||
81 | struct snd_pcm_runtime *runtime = substream->runtime; | 91 | struct snd_pcm_runtime *runtime = substream->runtime; |
82 | struct sport_device *sport = runtime->private_data; | 92 | struct sport_device *sport = runtime->private_data; |
83 | int period_bytes = frames_to_bytes(runtime, runtime->period_size); | 93 | int period_bytes = frames_to_bytes(runtime, runtime->period_size); |
94 | struct bf5xx_i2s_pcm_data *dma_data; | ||
95 | |||
96 | dma_data = snd_soc_dai_get_dma_data(rtd->cpu_dai, substream); | ||
97 | |||
98 | if (dma_data->tdm_mode) | ||
99 | period_bytes = period_bytes / runtime->channels * 8; | ||
84 | 100 | ||
85 | pr_debug("%s enter\n", __func__); | 101 | pr_debug("%s enter\n", __func__); |
86 | if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { | 102 | if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { |
@@ -127,10 +143,15 @@ static int bf5xx_pcm_trigger(struct snd_pcm_substream *substream, int cmd) | |||
127 | 143 | ||
128 | static snd_pcm_uframes_t bf5xx_pcm_pointer(struct snd_pcm_substream *substream) | 144 | static snd_pcm_uframes_t bf5xx_pcm_pointer(struct snd_pcm_substream *substream) |
129 | { | 145 | { |
146 | struct snd_soc_pcm_runtime *rtd = substream->private_data; | ||
130 | struct snd_pcm_runtime *runtime = substream->runtime; | 147 | struct snd_pcm_runtime *runtime = substream->runtime; |
131 | struct sport_device *sport = runtime->private_data; | 148 | struct sport_device *sport = runtime->private_data; |
132 | unsigned int diff; | 149 | unsigned int diff; |
133 | snd_pcm_uframes_t frames; | 150 | snd_pcm_uframes_t frames; |
151 | struct bf5xx_i2s_pcm_data *dma_data; | ||
152 | |||
153 | dma_data = snd_soc_dai_get_dma_data(rtd->cpu_dai, substream); | ||
154 | |||
134 | pr_debug("%s enter\n", __func__); | 155 | pr_debug("%s enter\n", __func__); |
135 | if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { | 156 | if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { |
136 | diff = sport_curr_offset_tx(sport); | 157 | diff = sport_curr_offset_tx(sport); |
@@ -147,6 +168,8 @@ static snd_pcm_uframes_t bf5xx_pcm_pointer(struct snd_pcm_substream *substream) | |||
147 | diff = 0; | 168 | diff = 0; |
148 | 169 | ||
149 | frames = bytes_to_frames(substream->runtime, diff); | 170 | frames = bytes_to_frames(substream->runtime, diff); |
171 | if (dma_data->tdm_mode) | ||
172 | frames = frames * runtime->channels / 8; | ||
150 | 173 | ||
151 | return frames; | 174 | return frames; |
152 | } | 175 | } |
@@ -158,11 +181,18 @@ static int bf5xx_pcm_open(struct snd_pcm_substream *substream) | |||
158 | struct sport_device *sport_handle = snd_soc_dai_get_drvdata(cpu_dai); | 181 | struct sport_device *sport_handle = snd_soc_dai_get_drvdata(cpu_dai); |
159 | struct snd_pcm_runtime *runtime = substream->runtime; | 182 | struct snd_pcm_runtime *runtime = substream->runtime; |
160 | struct snd_dma_buffer *buf = &substream->dma_buffer; | 183 | struct snd_dma_buffer *buf = &substream->dma_buffer; |
184 | struct bf5xx_i2s_pcm_data *dma_data; | ||
161 | int ret; | 185 | int ret; |
162 | 186 | ||
187 | dma_data = snd_soc_dai_get_dma_data(rtd->cpu_dai, substream); | ||
188 | |||
163 | pr_debug("%s enter\n", __func__); | 189 | pr_debug("%s enter\n", __func__); |
164 | 190 | ||
165 | snd_soc_set_runtime_hwparams(substream, &bf5xx_pcm_hardware); | 191 | snd_soc_set_runtime_hwparams(substream, &bf5xx_pcm_hardware); |
192 | if (dma_data->tdm_mode) | ||
193 | runtime->hw.buffer_bytes_max /= 4; | ||
194 | else | ||
195 | runtime->hw.info |= SNDRV_PCM_INFO_MMAP; | ||
166 | 196 | ||
167 | ret = snd_pcm_hw_constraint_integer(runtime, | 197 | ret = snd_pcm_hw_constraint_integer(runtime, |
168 | SNDRV_PCM_HW_PARAM_PERIODS); | 198 | SNDRV_PCM_HW_PARAM_PERIODS); |
@@ -198,6 +228,88 @@ static int bf5xx_pcm_mmap(struct snd_pcm_substream *substream, | |||
198 | return 0 ; | 228 | return 0 ; |
199 | } | 229 | } |
200 | 230 | ||
231 | static int bf5xx_pcm_copy(struct snd_pcm_substream *substream, int channel, | ||
232 | snd_pcm_uframes_t pos, void *buf, snd_pcm_uframes_t count) | ||
233 | { | ||
234 | struct snd_soc_pcm_runtime *rtd = substream->private_data; | ||
235 | struct snd_pcm_runtime *runtime = substream->runtime; | ||
236 | unsigned int sample_size = runtime->sample_bits / 8; | ||
237 | struct bf5xx_i2s_pcm_data *dma_data; | ||
238 | unsigned int i; | ||
239 | void *src, *dst; | ||
240 | |||
241 | dma_data = snd_soc_dai_get_dma_data(rtd->cpu_dai, substream); | ||
242 | |||
243 | if (dma_data->tdm_mode) { | ||
244 | if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { | ||
245 | src = buf; | ||
246 | dst = runtime->dma_area; | ||
247 | dst += pos * sample_size * 8; | ||
248 | |||
249 | while (count--) { | ||
250 | for (i = 0; i < runtime->channels; i++) { | ||
251 | memcpy(dst + dma_data->map[i] * | ||
252 | sample_size, src, sample_size); | ||
253 | src += sample_size; | ||
254 | } | ||
255 | dst += 8 * sample_size; | ||
256 | } | ||
257 | } else { | ||
258 | src = runtime->dma_area; | ||
259 | src += pos * sample_size * 8; | ||
260 | dst = buf; | ||
261 | |||
262 | while (count--) { | ||
263 | for (i = 0; i < runtime->channels; i++) { | ||
264 | memcpy(dst, src + dma_data->map[i] * | ||
265 | sample_size, sample_size); | ||
266 | dst += sample_size; | ||
267 | } | ||
268 | src += 8 * sample_size; | ||
269 | } | ||
270 | } | ||
271 | } else { | ||
272 | if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { | ||
273 | src = buf; | ||
274 | dst = runtime->dma_area; | ||
275 | dst += frames_to_bytes(runtime, pos); | ||
276 | } else { | ||
277 | src = runtime->dma_area; | ||
278 | src += frames_to_bytes(runtime, pos); | ||
279 | dst = buf; | ||
280 | } | ||
281 | |||
282 | memcpy(dst, src, frames_to_bytes(runtime, count)); | ||
283 | } | ||
284 | |||
285 | return 0; | ||
286 | } | ||
287 | |||
288 | static int bf5xx_pcm_silence(struct snd_pcm_substream *substream, | ||
289 | int channel, snd_pcm_uframes_t pos, snd_pcm_uframes_t count) | ||
290 | { | ||
291 | struct snd_soc_pcm_runtime *rtd = substream->private_data; | ||
292 | struct snd_pcm_runtime *runtime = substream->runtime; | ||
293 | unsigned int sample_size = runtime->sample_bits / 8; | ||
294 | void *buf = runtime->dma_area; | ||
295 | struct bf5xx_i2s_pcm_data *dma_data; | ||
296 | unsigned int offset, size; | ||
297 | |||
298 | dma_data = snd_soc_dai_get_dma_data(rtd->cpu_dai, substream); | ||
299 | |||
300 | if (dma_data->tdm_mode) { | ||
301 | offset = pos * 8 * sample_size; | ||
302 | size = count * 8 * sample_size; | ||
303 | } else { | ||
304 | offset = frames_to_bytes(runtime, pos); | ||
305 | size = frames_to_bytes(runtime, count); | ||
306 | } | ||
307 | |||
308 | snd_pcm_format_set_silence(runtime->format, buf + offset, size); | ||
309 | |||
310 | return 0; | ||
311 | } | ||
312 | |||
201 | static struct snd_pcm_ops bf5xx_pcm_i2s_ops = { | 313 | static struct snd_pcm_ops bf5xx_pcm_i2s_ops = { |
202 | .open = bf5xx_pcm_open, | 314 | .open = bf5xx_pcm_open, |
203 | .ioctl = snd_pcm_lib_ioctl, | 315 | .ioctl = snd_pcm_lib_ioctl, |
@@ -207,6 +319,8 @@ static struct snd_pcm_ops bf5xx_pcm_i2s_ops = { | |||
207 | .trigger = bf5xx_pcm_trigger, | 319 | .trigger = bf5xx_pcm_trigger, |
208 | .pointer = bf5xx_pcm_pointer, | 320 | .pointer = bf5xx_pcm_pointer, |
209 | .mmap = bf5xx_pcm_mmap, | 321 | .mmap = bf5xx_pcm_mmap, |
322 | .copy = bf5xx_pcm_copy, | ||
323 | .silence = bf5xx_pcm_silence, | ||
210 | }; | 324 | }; |
211 | 325 | ||
212 | static u64 bf5xx_pcm_dmamask = DMA_BIT_MASK(32); | 326 | static u64 bf5xx_pcm_dmamask = DMA_BIT_MASK(32); |