diff options
Diffstat (limited to 'sound/soc/omap/omap-mcbsp.c')
-rw-r--r-- | sound/soc/omap/omap-mcbsp.c | 137 |
1 files changed, 76 insertions, 61 deletions
diff --git a/sound/soc/omap/omap-mcbsp.c b/sound/soc/omap/omap-mcbsp.c index aebd3af2ab79..86f213905e2c 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) | |||
155 | struct snd_soc_pcm_runtime *rtd = substream->private_data; | 155 | struct snd_soc_pcm_runtime *rtd = substream->private_data; |
156 | struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai; | 156 | struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai; |
157 | 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; | ||
158 | 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); |
159 | int words; | 160 | int words; |
160 | 161 | ||
162 | dma_data = snd_soc_dai_get_dma_data(rtd->dai->cpu_dai, substream); | ||
163 | |||
161 | /* TODO: Currently, MODE_ELEMENT == MODE_FRAME */ | 164 | /* TODO: Currently, MODE_ELEMENT == MODE_FRAME */ |
162 | if (dma_op_mode == MCBSP_DMA_MODE_THRESHOLD) | 165 | if (dma_op_mode == MCBSP_DMA_MODE_THRESHOLD) |
163 | /* The FIFO size depends on the McBSP word configuration */ | 166 | /* |
164 | words = snd_pcm_lib_period_bytes(substream) / | 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) / | ||
165 | (mcbsp_data->wlen / 8); | 175 | (mcbsp_data->wlen / 8); |
166 | else | 176 | else |
167 | words = 1; | 177 | words = 1; |
@@ -192,31 +202,6 @@ static int omap_mcbsp_hwrule_min_buffersize(struct snd_pcm_hw_params *params, | |||
192 | return snd_interval_refine(buffer_size, &frames); | 202 | return snd_interval_refine(buffer_size, &frames); |
193 | } | 203 | } |
194 | 204 | ||
195 | static int omap_mcbsp_hwrule_max_periodsize(struct snd_pcm_hw_params *params, | ||
196 | struct snd_pcm_hw_rule *rule) | ||
197 | { | ||
198 | struct snd_interval *period_size = hw_param_interval(params, | ||
199 | SNDRV_PCM_HW_PARAM_PERIOD_SIZE); | ||
200 | struct snd_interval *channels = hw_param_interval(params, | ||
201 | SNDRV_PCM_HW_PARAM_CHANNELS); | ||
202 | struct snd_pcm_substream *substream = rule->private; | ||
203 | struct snd_soc_pcm_runtime *rtd = substream->private_data; | ||
204 | struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai; | ||
205 | struct omap_mcbsp_data *mcbsp_data = to_mcbsp(cpu_dai->private_data); | ||
206 | struct snd_interval frames; | ||
207 | int size; | ||
208 | |||
209 | snd_interval_any(&frames); | ||
210 | if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) | ||
211 | size = omap_mcbsp_get_max_tx_threshold(mcbsp_data->bus_id); | ||
212 | else | ||
213 | size = omap_mcbsp_get_max_rx_threshold(mcbsp_data->bus_id); | ||
214 | |||
215 | frames.max = size / channels->min; | ||
216 | frames.integer = 1; | ||
217 | return snd_interval_refine(period_size, &frames); | ||
218 | } | ||
219 | |||
220 | static int omap_mcbsp_dai_startup(struct snd_pcm_substream *substream, | 205 | static int omap_mcbsp_dai_startup(struct snd_pcm_substream *substream, |
221 | struct snd_soc_dai *dai) | 206 | struct snd_soc_dai *dai) |
222 | { | 207 | { |
@@ -245,10 +230,8 @@ static int omap_mcbsp_dai_startup(struct snd_pcm_substream *substream, | |||
245 | * 4 channels: size is 128 / 4 = 32 frames (4 * 32 words) | 230 | * 4 channels: size is 128 / 4 = 32 frames (4 * 32 words) |
246 | */ | 231 | */ |
247 | if (cpu_is_omap343x()) { | 232 | if (cpu_is_omap343x()) { |
248 | int dma_op_mode = omap_mcbsp_get_dma_op_mode(bus_id); | ||
249 | |||
250 | /* | 233 | /* |
251 | * The first rule is for the buffer size, we should not allow | 234 | * Rule for the buffer size. We should not allow |
252 | * smaller buffer than the FIFO size to avoid underruns | 235 | * smaller buffer than the FIFO size to avoid underruns |
253 | */ | 236 | */ |
254 | snd_pcm_hw_rule_add(substream->runtime, 0, | 237 | snd_pcm_hw_rule_add(substream->runtime, 0, |
@@ -257,17 +240,9 @@ static int omap_mcbsp_dai_startup(struct snd_pcm_substream *substream, | |||
257 | mcbsp_data, | 240 | mcbsp_data, |
258 | SNDRV_PCM_HW_PARAM_BUFFER_SIZE, -1); | 241 | SNDRV_PCM_HW_PARAM_BUFFER_SIZE, -1); |
259 | 242 | ||
260 | /* | 243 | /* Make sure, that the period size is always even */ |
261 | * In case of threshold mode, the rule will ensure, that the | 244 | snd_pcm_hw_constraint_step(substream->runtime, 0, |
262 | * period size is not bigger than the maximum allowed threshold | 245 | SNDRV_PCM_HW_PARAM_PERIOD_SIZE, 2); |
263 | * value. | ||
264 | */ | ||
265 | if (dma_op_mode == MCBSP_DMA_MODE_THRESHOLD) | ||
266 | snd_pcm_hw_rule_add(substream->runtime, 0, | ||
267 | SNDRV_PCM_HW_PARAM_CHANNELS, | ||
268 | omap_mcbsp_hwrule_max_periodsize, | ||
269 | substream, | ||
270 | SNDRV_PCM_HW_PARAM_PERIOD_SIZE, -1); | ||
271 | } | 246 | } |
272 | 247 | ||
273 | return err; | 248 | return err; |
@@ -348,11 +323,14 @@ static int omap_mcbsp_dai_hw_params(struct snd_pcm_substream *substream, | |||
348 | struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai; | 323 | struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai; |
349 | 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); |
350 | struct omap_mcbsp_reg_cfg *regs = &mcbsp_data->regs; | 325 | struct omap_mcbsp_reg_cfg *regs = &mcbsp_data->regs; |
351 | 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; | ||
352 | 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; | ||
353 | unsigned long port; | 330 | unsigned long port; |
354 | unsigned int format, div, framesize, master; | 331 | unsigned int format, div, framesize, master; |
355 | 332 | ||
333 | dma_data = &omap_mcbsp_dai_dma_params[cpu_dai->id][substream->stream]; | ||
356 | if (cpu_class_is_omap1()) { | 334 | if (cpu_class_is_omap1()) { |
357 | dma = omap1_dma_reqs[bus_id][substream->stream]; | 335 | dma = omap1_dma_reqs[bus_id][substream->stream]; |
358 | port = omap1_mcbsp_port[bus_id][substream->stream]; | 336 | port = omap1_mcbsp_port[bus_id][substream->stream]; |
@@ -365,35 +343,74 @@ static int omap_mcbsp_dai_hw_params(struct snd_pcm_substream *substream, | |||
365 | } else if (cpu_is_omap343x()) { | 343 | } else if (cpu_is_omap343x()) { |
366 | dma = omap24xx_dma_reqs[bus_id][substream->stream]; | 344 | dma = omap24xx_dma_reqs[bus_id][substream->stream]; |
367 | port = omap34xx_mcbsp_port[bus_id][substream->stream]; | 345 | port = omap34xx_mcbsp_port[bus_id][substream->stream]; |
368 | omap_mcbsp_dai_dma_params[id][substream->stream].set_threshold = | ||
369 | omap_mcbsp_set_threshold; | ||
370 | /* TODO: Currently, MODE_ELEMENT == MODE_FRAME */ | ||
371 | if (omap_mcbsp_get_dma_op_mode(bus_id) == | ||
372 | MCBSP_DMA_MODE_THRESHOLD) | ||
373 | sync_mode = OMAP_DMA_SYNC_FRAME; | ||
374 | } else { | 346 | } else { |
375 | return -ENODEV; | 347 | return -ENODEV; |
376 | } | 348 | } |
377 | omap_mcbsp_dai_dma_params[id][substream->stream].name = | ||
378 | substream->stream ? "Audio Capture" : "Audio Playback"; | ||
379 | omap_mcbsp_dai_dma_params[id][substream->stream].dma_req = dma; | ||
380 | omap_mcbsp_dai_dma_params[id][substream->stream].port_addr = port; | ||
381 | omap_mcbsp_dai_dma_params[id][substream->stream].sync_mode = sync_mode; | ||
382 | switch (params_format(params)) { | 349 | switch (params_format(params)) { |
383 | case SNDRV_PCM_FORMAT_S16_LE: | 350 | case SNDRV_PCM_FORMAT_S16_LE: |
384 | omap_mcbsp_dai_dma_params[id][substream->stream].data_type = | 351 | dma_data->data_type = OMAP_DMA_DATA_TYPE_S16; |
385 | OMAP_DMA_DATA_TYPE_S16; | 352 | wlen = 16; |
386 | break; | 353 | break; |
387 | case SNDRV_PCM_FORMAT_S32_LE: | 354 | case SNDRV_PCM_FORMAT_S32_LE: |
388 | omap_mcbsp_dai_dma_params[id][substream->stream].data_type = | 355 | dma_data->data_type = OMAP_DMA_DATA_TYPE_S32; |
389 | OMAP_DMA_DATA_TYPE_S32; | 356 | wlen = 32; |
390 | break; | 357 | break; |
391 | default: | 358 | default: |
392 | return -EINVAL; | 359 | return -EINVAL; |
393 | } | 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 | } | ||
406 | |||
407 | dma_data->name = substream->stream ? "Audio Capture" : "Audio Playback"; | ||
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; | ||
394 | 412 | ||
395 | snd_soc_dai_set_dma_data(cpu_dai, substream, | 413 | snd_soc_dai_set_dma_data(cpu_dai, substream, dma_data); |
396 | &omap_mcbsp_dai_dma_params[id][substream->stream]); | ||
397 | 414 | ||
398 | if (mcbsp_data->configured) { | 415 | if (mcbsp_data->configured) { |
399 | /* McBSP already configured by another stream */ | 416 | /* McBSP already configured by another stream */ |
@@ -419,7 +436,6 @@ static int omap_mcbsp_dai_hw_params(struct snd_pcm_substream *substream, | |||
419 | switch (params_format(params)) { | 436 | switch (params_format(params)) { |
420 | case SNDRV_PCM_FORMAT_S16_LE: | 437 | case SNDRV_PCM_FORMAT_S16_LE: |
421 | /* Set word lengths */ | 438 | /* Set word lengths */ |
422 | wlen = 16; | ||
423 | regs->rcr2 |= RWDLEN2(OMAP_MCBSP_WORD_16); | 439 | regs->rcr2 |= RWDLEN2(OMAP_MCBSP_WORD_16); |
424 | regs->rcr1 |= RWDLEN1(OMAP_MCBSP_WORD_16); | 440 | regs->rcr1 |= RWDLEN1(OMAP_MCBSP_WORD_16); |
425 | regs->xcr2 |= XWDLEN2(OMAP_MCBSP_WORD_16); | 441 | regs->xcr2 |= XWDLEN2(OMAP_MCBSP_WORD_16); |
@@ -427,7 +443,6 @@ static int omap_mcbsp_dai_hw_params(struct snd_pcm_substream *substream, | |||
427 | break; | 443 | break; |
428 | case SNDRV_PCM_FORMAT_S32_LE: | 444 | case SNDRV_PCM_FORMAT_S32_LE: |
429 | /* Set word lengths */ | 445 | /* Set word lengths */ |
430 | wlen = 32; | ||
431 | regs->rcr2 |= RWDLEN2(OMAP_MCBSP_WORD_32); | 446 | regs->rcr2 |= RWDLEN2(OMAP_MCBSP_WORD_32); |
432 | regs->rcr1 |= RWDLEN1(OMAP_MCBSP_WORD_32); | 447 | regs->rcr1 |= RWDLEN1(OMAP_MCBSP_WORD_32); |
433 | regs->xcr2 |= XWDLEN2(OMAP_MCBSP_WORD_32); | 448 | regs->xcr2 |= XWDLEN2(OMAP_MCBSP_WORD_32); |