aboutsummaryrefslogtreecommitdiffstats
path: root/sound/soc/omap/omap-mcbsp.c
diff options
context:
space:
mode:
Diffstat (limited to 'sound/soc/omap/omap-mcbsp.c')
-rw-r--r--sound/soc/omap/omap-mcbsp.c137
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
195static 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
220static int omap_mcbsp_dai_startup(struct snd_pcm_substream *substream, 205static 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);