diff options
Diffstat (limited to 'sound/soc/omap/omap-mcbsp.c')
-rw-r--r-- | sound/soc/omap/omap-mcbsp.c | 175 |
1 files changed, 125 insertions, 50 deletions
diff --git a/sound/soc/omap/omap-mcbsp.c b/sound/soc/omap/omap-mcbsp.c index 6f44cb4d30b8..86f213905e2c 100644 --- a/sound/soc/omap/omap-mcbsp.c +++ b/sound/soc/omap/omap-mcbsp.c | |||
@@ -59,6 +59,7 @@ struct omap_mcbsp_data { | |||
59 | int configured; | 59 | int configured; |
60 | unsigned int in_freq; | 60 | unsigned int in_freq; |
61 | int clk_div; | 61 | int clk_div; |
62 | int wlen; | ||
62 | }; | 63 | }; |
63 | 64 | ||
64 | #define to_mcbsp(priv) container_of((priv), struct omap_mcbsp_data, bus_id) | 65 | #define to_mcbsp(priv) container_of((priv), struct omap_mcbsp_data, bus_id) |
@@ -154,20 +155,51 @@ static void omap_mcbsp_set_threshold(struct snd_pcm_substream *substream) | |||
154 | struct snd_soc_pcm_runtime *rtd = substream->private_data; | 155 | struct snd_soc_pcm_runtime *rtd = substream->private_data; |
155 | struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai; | 156 | struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai; |
156 | struct omap_mcbsp_data *mcbsp_data = to_mcbsp(cpu_dai->private_data); | 157 | struct omap_mcbsp_data *mcbsp_data = to_mcbsp(cpu_dai->private_data); |
158 | struct omap_pcm_dma_data *dma_data; | ||
157 | int dma_op_mode = omap_mcbsp_get_dma_op_mode(mcbsp_data->bus_id); | 159 | int dma_op_mode = omap_mcbsp_get_dma_op_mode(mcbsp_data->bus_id); |
158 | int samples; | 160 | int words; |
161 | |||
162 | dma_data = snd_soc_dai_get_dma_data(rtd->dai->cpu_dai, substream); | ||
159 | 163 | ||
160 | /* TODO: Currently, MODE_ELEMENT == MODE_FRAME */ | 164 | /* TODO: Currently, MODE_ELEMENT == MODE_FRAME */ |
161 | if (dma_op_mode == MCBSP_DMA_MODE_THRESHOLD) | 165 | if (dma_op_mode == MCBSP_DMA_MODE_THRESHOLD) |
162 | samples = snd_pcm_lib_period_bytes(substream) >> 1; | 166 | /* |
167 | * Configure McBSP threshold based on either: | ||
168 | * packet_size, when the sDMA is in packet mode, or | ||
169 | * based on the period size. | ||
170 | */ | ||
171 | if (dma_data->packet_size) | ||
172 | words = dma_data->packet_size; | ||
173 | else | ||
174 | words = snd_pcm_lib_period_bytes(substream) / | ||
175 | (mcbsp_data->wlen / 8); | ||
163 | else | 176 | else |
164 | samples = 1; | 177 | words = 1; |
165 | 178 | ||
166 | /* Configure McBSP internal buffer usage */ | 179 | /* Configure McBSP internal buffer usage */ |
167 | if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) | 180 | if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) |
168 | omap_mcbsp_set_tx_threshold(mcbsp_data->bus_id, samples - 1); | 181 | omap_mcbsp_set_tx_threshold(mcbsp_data->bus_id, words); |
169 | else | 182 | else |
170 | omap_mcbsp_set_rx_threshold(mcbsp_data->bus_id, samples - 1); | 183 | omap_mcbsp_set_rx_threshold(mcbsp_data->bus_id, words); |
184 | } | ||
185 | |||
186 | static int omap_mcbsp_hwrule_min_buffersize(struct snd_pcm_hw_params *params, | ||
187 | struct snd_pcm_hw_rule *rule) | ||
188 | { | ||
189 | struct snd_interval *buffer_size = hw_param_interval(params, | ||
190 | SNDRV_PCM_HW_PARAM_BUFFER_SIZE); | ||
191 | struct snd_interval *channels = hw_param_interval(params, | ||
192 | SNDRV_PCM_HW_PARAM_CHANNELS); | ||
193 | struct omap_mcbsp_data *mcbsp_data = rule->private; | ||
194 | struct snd_interval frames; | ||
195 | int size; | ||
196 | |||
197 | snd_interval_any(&frames); | ||
198 | size = omap_mcbsp_get_fifo_size(mcbsp_data->bus_id); | ||
199 | |||
200 | frames.min = size / channels->min; | ||
201 | frames.integer = 1; | ||
202 | return snd_interval_refine(buffer_size, &frames); | ||
171 | } | 203 | } |
172 | 204 | ||
173 | static int omap_mcbsp_dai_startup(struct snd_pcm_substream *substream, | 205 | static int omap_mcbsp_dai_startup(struct snd_pcm_substream *substream, |
@@ -182,33 +214,35 @@ static int omap_mcbsp_dai_startup(struct snd_pcm_substream *substream, | |||
182 | if (!cpu_dai->active) | 214 | if (!cpu_dai->active) |
183 | err = omap_mcbsp_request(bus_id); | 215 | err = omap_mcbsp_request(bus_id); |
184 | 216 | ||
217 | /* | ||
218 | * OMAP3 McBSP FIFO is word structured. | ||
219 | * McBSP2 has 1024 + 256 = 1280 word long buffer, | ||
220 | * McBSP1,3,4,5 has 128 word long buffer | ||
221 | * This means that the size of the FIFO depends on the sample format. | ||
222 | * For example on McBSP3: | ||
223 | * 16bit samples: size is 128 * 2 = 256 bytes | ||
224 | * 32bit samples: size is 128 * 4 = 512 bytes | ||
225 | * It is simpler to place constraint for buffer and period based on | ||
226 | * channels. | ||
227 | * McBSP3 as example again (16 or 32 bit samples): | ||
228 | * 1 channel (mono): size is 128 frames (128 words) | ||
229 | * 2 channels (stereo): size is 128 / 2 = 64 frames (2 * 64 words) | ||
230 | * 4 channels: size is 128 / 4 = 32 frames (4 * 32 words) | ||
231 | */ | ||
185 | if (cpu_is_omap343x()) { | 232 | if (cpu_is_omap343x()) { |
186 | int dma_op_mode = omap_mcbsp_get_dma_op_mode(bus_id); | ||
187 | int max_period; | ||
188 | |||
189 | /* | 233 | /* |
190 | * McBSP2 in OMAP3 has 1024 * 32-bit internal audio buffer. | 234 | * Rule for the buffer size. We should not allow |
191 | * Set constraint for minimum buffer size to the same than FIFO | 235 | * smaller buffer than the FIFO size to avoid underruns |
192 | * size in order to avoid underruns in playback startup because | 236 | */ |
193 | * HW is keeping the DMA request active until FIFO is filled. | 237 | snd_pcm_hw_rule_add(substream->runtime, 0, |
194 | */ | 238 | SNDRV_PCM_HW_PARAM_CHANNELS, |
195 | if (bus_id == 1) | 239 | omap_mcbsp_hwrule_min_buffersize, |
196 | snd_pcm_hw_constraint_minmax(substream->runtime, | 240 | mcbsp_data, |
197 | SNDRV_PCM_HW_PARAM_BUFFER_BYTES, | 241 | SNDRV_PCM_HW_PARAM_BUFFER_SIZE, -1); |
198 | 4096, UINT_MAX); | 242 | |
199 | 243 | /* Make sure, that the period size is always even */ | |
200 | if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) | 244 | snd_pcm_hw_constraint_step(substream->runtime, 0, |
201 | max_period = omap_mcbsp_get_max_tx_threshold(bus_id); | 245 | SNDRV_PCM_HW_PARAM_PERIOD_SIZE, 2); |
202 | else | ||
203 | max_period = omap_mcbsp_get_max_rx_threshold(bus_id); | ||
204 | |||
205 | max_period++; | ||
206 | max_period <<= 1; | ||
207 | |||
208 | if (dma_op_mode == MCBSP_DMA_MODE_THRESHOLD) | ||
209 | snd_pcm_hw_constraint_minmax(substream->runtime, | ||
210 | SNDRV_PCM_HW_PARAM_PERIOD_BYTES, | ||
211 | 32, max_period); | ||
212 | } | 246 | } |
213 | 247 | ||
214 | return err; | 248 | return err; |
@@ -289,11 +323,14 @@ static int omap_mcbsp_dai_hw_params(struct snd_pcm_substream *substream, | |||
289 | struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai; | 323 | struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai; |
290 | struct omap_mcbsp_data *mcbsp_data = to_mcbsp(cpu_dai->private_data); | 324 | struct omap_mcbsp_data *mcbsp_data = to_mcbsp(cpu_dai->private_data); |
291 | struct omap_mcbsp_reg_cfg *regs = &mcbsp_data->regs; | 325 | struct omap_mcbsp_reg_cfg *regs = &mcbsp_data->regs; |
292 | int dma, bus_id = mcbsp_data->bus_id, id = cpu_dai->id; | 326 | struct omap_pcm_dma_data *dma_data; |
327 | int dma, bus_id = mcbsp_data->bus_id; | ||
293 | int wlen, channels, wpf, sync_mode = OMAP_DMA_SYNC_ELEMENT; | 328 | int wlen, channels, wpf, sync_mode = OMAP_DMA_SYNC_ELEMENT; |
329 | int pkt_size = 0; | ||
294 | unsigned long port; | 330 | unsigned long port; |
295 | unsigned int format, div, framesize, master; | 331 | unsigned int format, div, framesize, master; |
296 | 332 | ||
333 | dma_data = &omap_mcbsp_dai_dma_params[cpu_dai->id][substream->stream]; | ||
297 | if (cpu_class_is_omap1()) { | 334 | if (cpu_class_is_omap1()) { |
298 | dma = omap1_dma_reqs[bus_id][substream->stream]; | 335 | dma = omap1_dma_reqs[bus_id][substream->stream]; |
299 | port = omap1_mcbsp_port[bus_id][substream->stream]; | 336 | port = omap1_mcbsp_port[bus_id][substream->stream]; |
@@ -306,35 +343,74 @@ static int omap_mcbsp_dai_hw_params(struct snd_pcm_substream *substream, | |||
306 | } else if (cpu_is_omap343x()) { | 343 | } else if (cpu_is_omap343x()) { |
307 | dma = omap24xx_dma_reqs[bus_id][substream->stream]; | 344 | dma = omap24xx_dma_reqs[bus_id][substream->stream]; |
308 | port = omap34xx_mcbsp_port[bus_id][substream->stream]; | 345 | port = omap34xx_mcbsp_port[bus_id][substream->stream]; |
309 | omap_mcbsp_dai_dma_params[id][substream->stream].set_threshold = | ||
310 | omap_mcbsp_set_threshold; | ||
311 | /* TODO: Currently, MODE_ELEMENT == MODE_FRAME */ | ||
312 | if (omap_mcbsp_get_dma_op_mode(bus_id) == | ||
313 | MCBSP_DMA_MODE_THRESHOLD) | ||
314 | sync_mode = OMAP_DMA_SYNC_FRAME; | ||
315 | } else { | 346 | } else { |
316 | return -ENODEV; | 347 | return -ENODEV; |
317 | } | 348 | } |
318 | omap_mcbsp_dai_dma_params[id][substream->stream].name = | ||
319 | substream->stream ? "Audio Capture" : "Audio Playback"; | ||
320 | omap_mcbsp_dai_dma_params[id][substream->stream].dma_req = dma; | ||
321 | omap_mcbsp_dai_dma_params[id][substream->stream].port_addr = port; | ||
322 | omap_mcbsp_dai_dma_params[id][substream->stream].sync_mode = sync_mode; | ||
323 | switch (params_format(params)) { | 349 | switch (params_format(params)) { |
324 | case SNDRV_PCM_FORMAT_S16_LE: | 350 | case SNDRV_PCM_FORMAT_S16_LE: |
325 | omap_mcbsp_dai_dma_params[id][substream->stream].data_type = | 351 | dma_data->data_type = OMAP_DMA_DATA_TYPE_S16; |
326 | OMAP_DMA_DATA_TYPE_S16; | 352 | wlen = 16; |
327 | break; | 353 | break; |
328 | case SNDRV_PCM_FORMAT_S32_LE: | 354 | case SNDRV_PCM_FORMAT_S32_LE: |
329 | omap_mcbsp_dai_dma_params[id][substream->stream].data_type = | 355 | dma_data->data_type = OMAP_DMA_DATA_TYPE_S32; |
330 | OMAP_DMA_DATA_TYPE_S32; | 356 | wlen = 32; |
331 | break; | 357 | break; |
332 | default: | 358 | default: |
333 | return -EINVAL; | 359 | return -EINVAL; |
334 | } | 360 | } |
361 | if (cpu_is_omap343x()) { | ||
362 | dma_data->set_threshold = omap_mcbsp_set_threshold; | ||
363 | /* TODO: Currently, MODE_ELEMENT == MODE_FRAME */ | ||
364 | if (omap_mcbsp_get_dma_op_mode(bus_id) == | ||
365 | MCBSP_DMA_MODE_THRESHOLD) { | ||
366 | int period_words, max_thrsh; | ||
367 | |||
368 | period_words = params_period_bytes(params) / (wlen / 8); | ||
369 | if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) | ||
370 | max_thrsh = omap_mcbsp_get_max_tx_threshold( | ||
371 | mcbsp_data->bus_id); | ||
372 | else | ||
373 | max_thrsh = omap_mcbsp_get_max_rx_threshold( | ||
374 | mcbsp_data->bus_id); | ||
375 | /* | ||
376 | * If the period contains less or equal number of words, | ||
377 | * we are using the original threshold mode setup: | ||
378 | * McBSP threshold = sDMA frame size = period_size | ||
379 | * Otherwise we switch to sDMA packet mode: | ||
380 | * McBSP threshold = sDMA packet size | ||
381 | * sDMA frame size = period size | ||
382 | */ | ||
383 | if (period_words > max_thrsh) { | ||
384 | int divider = 0; | ||
385 | |||
386 | /* | ||
387 | * Look for the biggest threshold value, which | ||
388 | * divides the period size evenly. | ||
389 | */ | ||
390 | divider = period_words / max_thrsh; | ||
391 | if (period_words % max_thrsh) | ||
392 | divider++; | ||
393 | while (period_words % divider && | ||
394 | divider < period_words) | ||
395 | divider++; | ||
396 | if (divider == period_words) | ||
397 | return -EINVAL; | ||
398 | |||
399 | pkt_size = period_words / divider; | ||
400 | sync_mode = OMAP_DMA_SYNC_PACKET; | ||
401 | } else { | ||
402 | sync_mode = OMAP_DMA_SYNC_FRAME; | ||
403 | } | ||
404 | } | ||
405 | } | ||
335 | 406 | ||
336 | snd_soc_dai_set_dma_data(cpu_dai, substream, | 407 | dma_data->name = substream->stream ? "Audio Capture" : "Audio Playback"; |
337 | &omap_mcbsp_dai_dma_params[id][substream->stream]); | 408 | dma_data->dma_req = dma; |
409 | dma_data->port_addr = port; | ||
410 | dma_data->sync_mode = sync_mode; | ||
411 | dma_data->packet_size = pkt_size; | ||
412 | |||
413 | snd_soc_dai_set_dma_data(cpu_dai, substream, dma_data); | ||
338 | 414 | ||
339 | if (mcbsp_data->configured) { | 415 | if (mcbsp_data->configured) { |
340 | /* McBSP already configured by another stream */ | 416 | /* McBSP already configured by another stream */ |
@@ -360,7 +436,6 @@ static int omap_mcbsp_dai_hw_params(struct snd_pcm_substream *substream, | |||
360 | switch (params_format(params)) { | 436 | switch (params_format(params)) { |
361 | case SNDRV_PCM_FORMAT_S16_LE: | 437 | case SNDRV_PCM_FORMAT_S16_LE: |
362 | /* Set word lengths */ | 438 | /* Set word lengths */ |
363 | wlen = 16; | ||
364 | regs->rcr2 |= RWDLEN2(OMAP_MCBSP_WORD_16); | 439 | regs->rcr2 |= RWDLEN2(OMAP_MCBSP_WORD_16); |
365 | regs->rcr1 |= RWDLEN1(OMAP_MCBSP_WORD_16); | 440 | regs->rcr1 |= RWDLEN1(OMAP_MCBSP_WORD_16); |
366 | regs->xcr2 |= XWDLEN2(OMAP_MCBSP_WORD_16); | 441 | regs->xcr2 |= XWDLEN2(OMAP_MCBSP_WORD_16); |
@@ -368,7 +443,6 @@ static int omap_mcbsp_dai_hw_params(struct snd_pcm_substream *substream, | |||
368 | break; | 443 | break; |
369 | case SNDRV_PCM_FORMAT_S32_LE: | 444 | case SNDRV_PCM_FORMAT_S32_LE: |
370 | /* Set word lengths */ | 445 | /* Set word lengths */ |
371 | wlen = 32; | ||
372 | regs->rcr2 |= RWDLEN2(OMAP_MCBSP_WORD_32); | 446 | regs->rcr2 |= RWDLEN2(OMAP_MCBSP_WORD_32); |
373 | regs->rcr1 |= RWDLEN1(OMAP_MCBSP_WORD_32); | 447 | regs->rcr1 |= RWDLEN1(OMAP_MCBSP_WORD_32); |
374 | regs->xcr2 |= XWDLEN2(OMAP_MCBSP_WORD_32); | 448 | regs->xcr2 |= XWDLEN2(OMAP_MCBSP_WORD_32); |
@@ -409,6 +483,7 @@ static int omap_mcbsp_dai_hw_params(struct snd_pcm_substream *substream, | |||
409 | } | 483 | } |
410 | 484 | ||
411 | omap_mcbsp_config(bus_id, &mcbsp_data->regs); | 485 | omap_mcbsp_config(bus_id, &mcbsp_data->regs); |
486 | mcbsp_data->wlen = wlen; | ||
412 | mcbsp_data->configured = 1; | 487 | mcbsp_data->configured = 1; |
413 | 488 | ||
414 | return 0; | 489 | return 0; |