diff options
author | Jarkko Nikula <jhnikula@gmail.com> | 2009-08-07 02:59:47 -0400 |
---|---|---|
committer | Mark Brown <broonie@opensource.wolfsonmicro.com> | 2009-08-07 05:57:42 -0400 |
commit | c12abc012e18b362204345c323536f228d65c4db (patch) | |
tree | 105552bd558076eb3206ce5f4026af60921dc27f /arch/arm | |
parent | 06c71282a90470184a78f7f0ab0f7ce0fc1f69c8 (diff) |
ARM: OMAP: McBSP: Fix ASoC on OMAP1510 by fixing API of omap_mcbsp_start/stop
Simultaneous audio playback and capture on OMAP1510 can cause that second
stream is stalled if there is enough delay between startup of the audio
streams.
Current implementation of the omap_mcbsp_start is starting both transmitter
and receiver at the same time and it is called only for firstly started
audio stream from the OMAP McBSP based ASoC DAI driver.
Since DMA request lines on OMAP1510 are edge sensitive, the DMA request is
missed if there is no DMA transfer set up at that time when the first word
after McBSP startup is transmitted. The problem hasn't noted before since
later OMAPs are using level sensitive DMA request lines.
Fix the problem by changing API of omap_mcbsp_start and omap_mcbsp_stop by
allowing to start and stop individually McBSP transmitter and receiver
logics. Then call those functions individually for both audio playback
and capture streams. This ensures that DMA transfer is setup before
transmitter or receiver is started.
Thanks to Janusz Krzysztofik <jkrzyszt@tis.icnet.pl> for detailed problem
analysis and Peter Ujfalusi <peter.ujfalusi@nokia.com> for info about DMA
request line behavior differences between the OMAP generations.
Reported-and-tested-by: Janusz Krzysztofik <jkrzyszt@tis.icnet.pl>
Signed-off-by: Jarkko Nikula <jhnikula@gmail.com>
Acked-by: Tony Lindgren <tony@atomide.com>
Acked-by: Peter Ujfalusi <peter.ujfalusi@nokia.com>
Signed-off-by: Mark Brown <broonie@opensource.wolfsonmicro.com>
Diffstat (limited to 'arch/arm')
-rw-r--r-- | arch/arm/plat-omap/include/mach/mcbsp.h | 4 | ||||
-rw-r--r-- | arch/arm/plat-omap/mcbsp.c | 50 |
2 files changed, 34 insertions, 20 deletions
diff --git a/arch/arm/plat-omap/include/mach/mcbsp.h b/arch/arm/plat-omap/include/mach/mcbsp.h index bb154ea76769..57249bb1e9bc 100644 --- a/arch/arm/plat-omap/include/mach/mcbsp.h +++ b/arch/arm/plat-omap/include/mach/mcbsp.h | |||
@@ -387,8 +387,8 @@ void omap_mcbsp_register_board_cfg(struct omap_mcbsp_platform_data *config, | |||
387 | void omap_mcbsp_config(unsigned int id, const struct omap_mcbsp_reg_cfg * config); | 387 | void omap_mcbsp_config(unsigned int id, const struct omap_mcbsp_reg_cfg * config); |
388 | int omap_mcbsp_request(unsigned int id); | 388 | int omap_mcbsp_request(unsigned int id); |
389 | void omap_mcbsp_free(unsigned int id); | 389 | void omap_mcbsp_free(unsigned int id); |
390 | void omap_mcbsp_start(unsigned int id); | 390 | void omap_mcbsp_start(unsigned int id, int tx, int rx); |
391 | void omap_mcbsp_stop(unsigned int id); | 391 | void omap_mcbsp_stop(unsigned int id, int tx, int rx); |
392 | void omap_mcbsp_xmit_word(unsigned int id, u32 word); | 392 | void omap_mcbsp_xmit_word(unsigned int id, u32 word); |
393 | u32 omap_mcbsp_recv_word(unsigned int id); | 393 | u32 omap_mcbsp_recv_word(unsigned int id); |
394 | 394 | ||
diff --git a/arch/arm/plat-omap/mcbsp.c b/arch/arm/plat-omap/mcbsp.c index efa0e0111f38..a3d2313460b3 100644 --- a/arch/arm/plat-omap/mcbsp.c +++ b/arch/arm/plat-omap/mcbsp.c | |||
@@ -328,14 +328,15 @@ void omap_mcbsp_free(unsigned int id) | |||
328 | EXPORT_SYMBOL(omap_mcbsp_free); | 328 | EXPORT_SYMBOL(omap_mcbsp_free); |
329 | 329 | ||
330 | /* | 330 | /* |
331 | * Here we start the McBSP, by enabling the sample | 331 | * Here we start the McBSP, by enabling transmitter, receiver or both. |
332 | * generator, both transmitter and receivers, | 332 | * If no transmitter or receiver is active prior calling, then sample-rate |
333 | * and the frame sync. | 333 | * generator and frame sync are started. |
334 | */ | 334 | */ |
335 | void omap_mcbsp_start(unsigned int id) | 335 | void omap_mcbsp_start(unsigned int id, int tx, int rx) |
336 | { | 336 | { |
337 | struct omap_mcbsp *mcbsp; | 337 | struct omap_mcbsp *mcbsp; |
338 | void __iomem *io_base; | 338 | void __iomem *io_base; |
339 | int idle; | ||
339 | u16 w; | 340 | u16 w; |
340 | 341 | ||
341 | if (!omap_mcbsp_check_valid_id(id)) { | 342 | if (!omap_mcbsp_check_valid_id(id)) { |
@@ -348,32 +349,40 @@ void omap_mcbsp_start(unsigned int id) | |||
348 | mcbsp->rx_word_length = (OMAP_MCBSP_READ(io_base, RCR1) >> 5) & 0x7; | 349 | mcbsp->rx_word_length = (OMAP_MCBSP_READ(io_base, RCR1) >> 5) & 0x7; |
349 | mcbsp->tx_word_length = (OMAP_MCBSP_READ(io_base, XCR1) >> 5) & 0x7; | 350 | mcbsp->tx_word_length = (OMAP_MCBSP_READ(io_base, XCR1) >> 5) & 0x7; |
350 | 351 | ||
351 | /* Start the sample generator */ | 352 | idle = !((OMAP_MCBSP_READ(io_base, SPCR2) | |
352 | w = OMAP_MCBSP_READ(io_base, SPCR2); | 353 | OMAP_MCBSP_READ(io_base, SPCR1)) & 1); |
353 | OMAP_MCBSP_WRITE(io_base, SPCR2, w | (1 << 6)); | 354 | |
355 | if (idle) { | ||
356 | /* Start the sample generator */ | ||
357 | w = OMAP_MCBSP_READ(io_base, SPCR2); | ||
358 | OMAP_MCBSP_WRITE(io_base, SPCR2, w | (1 << 6)); | ||
359 | } | ||
354 | 360 | ||
355 | /* Enable transmitter and receiver */ | 361 | /* Enable transmitter and receiver */ |
356 | w = OMAP_MCBSP_READ(io_base, SPCR2); | 362 | w = OMAP_MCBSP_READ(io_base, SPCR2); |
357 | OMAP_MCBSP_WRITE(io_base, SPCR2, w | 1); | 363 | OMAP_MCBSP_WRITE(io_base, SPCR2, w | (tx & 1)); |
358 | 364 | ||
359 | w = OMAP_MCBSP_READ(io_base, SPCR1); | 365 | w = OMAP_MCBSP_READ(io_base, SPCR1); |
360 | OMAP_MCBSP_WRITE(io_base, SPCR1, w | 1); | 366 | OMAP_MCBSP_WRITE(io_base, SPCR1, w | (rx & 1)); |
361 | 367 | ||
362 | udelay(100); | 368 | udelay(100); |
363 | 369 | ||
364 | /* Start frame sync */ | 370 | if (idle) { |
365 | w = OMAP_MCBSP_READ(io_base, SPCR2); | 371 | /* Start frame sync */ |
366 | OMAP_MCBSP_WRITE(io_base, SPCR2, w | (1 << 7)); | 372 | w = OMAP_MCBSP_READ(io_base, SPCR2); |
373 | OMAP_MCBSP_WRITE(io_base, SPCR2, w | (1 << 7)); | ||
374 | } | ||
367 | 375 | ||
368 | /* Dump McBSP Regs */ | 376 | /* Dump McBSP Regs */ |
369 | omap_mcbsp_dump_reg(id); | 377 | omap_mcbsp_dump_reg(id); |
370 | } | 378 | } |
371 | EXPORT_SYMBOL(omap_mcbsp_start); | 379 | EXPORT_SYMBOL(omap_mcbsp_start); |
372 | 380 | ||
373 | void omap_mcbsp_stop(unsigned int id) | 381 | void omap_mcbsp_stop(unsigned int id, int tx, int rx) |
374 | { | 382 | { |
375 | struct omap_mcbsp *mcbsp; | 383 | struct omap_mcbsp *mcbsp; |
376 | void __iomem *io_base; | 384 | void __iomem *io_base; |
385 | int idle; | ||
377 | u16 w; | 386 | u16 w; |
378 | 387 | ||
379 | if (!omap_mcbsp_check_valid_id(id)) { | 388 | if (!omap_mcbsp_check_valid_id(id)) { |
@@ -386,15 +395,20 @@ void omap_mcbsp_stop(unsigned int id) | |||
386 | 395 | ||
387 | /* Reset transmitter */ | 396 | /* Reset transmitter */ |
388 | w = OMAP_MCBSP_READ(io_base, SPCR2); | 397 | w = OMAP_MCBSP_READ(io_base, SPCR2); |
389 | OMAP_MCBSP_WRITE(io_base, SPCR2, w & ~(1)); | 398 | OMAP_MCBSP_WRITE(io_base, SPCR2, w & ~(tx & 1)); |
390 | 399 | ||
391 | /* Reset receiver */ | 400 | /* Reset receiver */ |
392 | w = OMAP_MCBSP_READ(io_base, SPCR1); | 401 | w = OMAP_MCBSP_READ(io_base, SPCR1); |
393 | OMAP_MCBSP_WRITE(io_base, SPCR1, w & ~(1)); | 402 | OMAP_MCBSP_WRITE(io_base, SPCR1, w & ~(rx & 1)); |
394 | 403 | ||
395 | /* Reset the sample rate generator */ | 404 | idle = !((OMAP_MCBSP_READ(io_base, SPCR2) | |
396 | w = OMAP_MCBSP_READ(io_base, SPCR2); | 405 | OMAP_MCBSP_READ(io_base, SPCR1)) & 1); |
397 | OMAP_MCBSP_WRITE(io_base, SPCR2, w & ~(1 << 6)); | 406 | |
407 | if (idle) { | ||
408 | /* Reset the sample rate generator */ | ||
409 | w = OMAP_MCBSP_READ(io_base, SPCR2); | ||
410 | OMAP_MCBSP_WRITE(io_base, SPCR2, w & ~(1 << 6)); | ||
411 | } | ||
398 | } | 412 | } |
399 | EXPORT_SYMBOL(omap_mcbsp_stop); | 413 | EXPORT_SYMBOL(omap_mcbsp_stop); |
400 | 414 | ||