diff options
Diffstat (limited to 'sound/soc/fsl/fsl_ssi.c')
-rw-r--r-- | sound/soc/fsl/fsl_ssi.c | 98 |
1 files changed, 63 insertions, 35 deletions
diff --git a/sound/soc/fsl/fsl_ssi.c b/sound/soc/fsl/fsl_ssi.c index c6d6eb71dc1d..169bca295b78 100644 --- a/sound/soc/fsl/fsl_ssi.c +++ b/sound/soc/fsl/fsl_ssi.c | |||
@@ -72,6 +72,7 @@ | |||
72 | * @dev: struct device pointer | 72 | * @dev: struct device pointer |
73 | * @playback: the number of playback streams opened | 73 | * @playback: the number of playback streams opened |
74 | * @capture: the number of capture streams opened | 74 | * @capture: the number of capture streams opened |
75 | * @asynchronous: 0=synchronous mode, 1=asynchronous mode | ||
75 | * @cpu_dai: the CPU DAI for this device | 76 | * @cpu_dai: the CPU DAI for this device |
76 | * @dev_attr: the sysfs device attribute structure | 77 | * @dev_attr: the sysfs device attribute structure |
77 | * @stats: SSI statistics | 78 | * @stats: SSI statistics |
@@ -86,6 +87,7 @@ struct fsl_ssi_private { | |||
86 | struct device *dev; | 87 | struct device *dev; |
87 | unsigned int playback; | 88 | unsigned int playback; |
88 | unsigned int capture; | 89 | unsigned int capture; |
90 | int asynchronous; | ||
89 | struct snd_soc_dai cpu_dai; | 91 | struct snd_soc_dai cpu_dai; |
90 | struct device_attribute dev_attr; | 92 | struct device_attribute dev_attr; |
91 | 93 | ||
@@ -301,9 +303,10 @@ static int fsl_ssi_startup(struct snd_pcm_substream *substream, | |||
301 | * | 303 | * |
302 | * FIXME: Little-endian samples require a different shift dir | 304 | * FIXME: Little-endian samples require a different shift dir |
303 | */ | 305 | */ |
304 | clrsetbits_be32(&ssi->scr, CCSR_SSI_SCR_I2S_MODE_MASK, | 306 | clrsetbits_be32(&ssi->scr, |
305 | CCSR_SSI_SCR_TFR_CLK_DIS | | 307 | CCSR_SSI_SCR_I2S_MODE_MASK | CCSR_SSI_SCR_SYN, |
306 | CCSR_SSI_SCR_I2S_MODE_SLAVE | CCSR_SSI_SCR_SYN); | 308 | CCSR_SSI_SCR_TFR_CLK_DIS | CCSR_SSI_SCR_I2S_MODE_SLAVE |
309 | | (ssi_private->asynchronous ? 0 : CCSR_SSI_SCR_SYN)); | ||
307 | 310 | ||
308 | out_be32(&ssi->stcr, | 311 | out_be32(&ssi->stcr, |
309 | CCSR_SSI_STCR_TXBIT0 | CCSR_SSI_STCR_TFEN0 | | 312 | CCSR_SSI_STCR_TXBIT0 | CCSR_SSI_STCR_TFEN0 | |
@@ -382,10 +385,15 @@ static int fsl_ssi_startup(struct snd_pcm_substream *substream, | |||
382 | SNDRV_PCM_HW_PARAM_RATE, | 385 | SNDRV_PCM_HW_PARAM_RATE, |
383 | first_runtime->rate, first_runtime->rate); | 386 | first_runtime->rate, first_runtime->rate); |
384 | 387 | ||
385 | snd_pcm_hw_constraint_minmax(substream->runtime, | 388 | /* If we're in synchronous mode, then we need to constrain |
386 | SNDRV_PCM_HW_PARAM_SAMPLE_BITS, | 389 | * the sample size as well. We don't support independent sample |
387 | first_runtime->sample_bits, | 390 | * rates in asynchronous mode. |
388 | first_runtime->sample_bits); | 391 | */ |
392 | if (!ssi_private->asynchronous) | ||
393 | snd_pcm_hw_constraint_minmax(substream->runtime, | ||
394 | SNDRV_PCM_HW_PARAM_SAMPLE_BITS, | ||
395 | first_runtime->sample_bits, | ||
396 | first_runtime->sample_bits); | ||
389 | 397 | ||
390 | ssi_private->second_stream = substream; | 398 | ssi_private->second_stream = substream; |
391 | } | 399 | } |
@@ -400,7 +408,7 @@ static int fsl_ssi_startup(struct snd_pcm_substream *substream, | |||
400 | } | 408 | } |
401 | 409 | ||
402 | /** | 410 | /** |
403 | * fsl_ssi_prepare: prepare the SSI. | 411 | * fsl_ssi_hw_params - program the sample size |
404 | * | 412 | * |
405 | * Most of the SSI registers have been programmed in the startup function, | 413 | * Most of the SSI registers have been programmed in the startup function, |
406 | * but the word length must be programmed here. Unfortunately, programming | 414 | * but the word length must be programmed here. Unfortunately, programming |
@@ -412,23 +420,27 @@ static int fsl_ssi_startup(struct snd_pcm_substream *substream, | |||
412 | * Note: The SxCCR.DC and SxCCR.PM bits are only used if the SSI is the | 420 | * Note: The SxCCR.DC and SxCCR.PM bits are only used if the SSI is the |
413 | * clock master. | 421 | * clock master. |
414 | */ | 422 | */ |
415 | static int fsl_ssi_prepare(struct snd_pcm_substream *substream, | 423 | static int fsl_ssi_hw_params(struct snd_pcm_substream *substream, |
416 | struct snd_soc_dai *dai) | 424 | struct snd_pcm_hw_params *hw_params, struct snd_soc_dai *cpu_dai) |
417 | { | 425 | { |
418 | struct snd_pcm_runtime *runtime = substream->runtime; | 426 | struct fsl_ssi_private *ssi_private = cpu_dai->private_data; |
419 | struct snd_soc_pcm_runtime *rtd = substream->private_data; | ||
420 | struct fsl_ssi_private *ssi_private = rtd->dai->cpu_dai->private_data; | ||
421 | |||
422 | struct ccsr_ssi __iomem *ssi = ssi_private->ssi; | ||
423 | 427 | ||
424 | if (substream == ssi_private->first_stream) { | 428 | if (substream == ssi_private->first_stream) { |
425 | u32 wl; | 429 | struct ccsr_ssi __iomem *ssi = ssi_private->ssi; |
430 | unsigned int sample_size = | ||
431 | snd_pcm_format_width(params_format(hw_params)); | ||
432 | u32 wl = CCSR_SSI_SxCCR_WL(sample_size); | ||
426 | 433 | ||
427 | /* The SSI should always be disabled at this points (SSIEN=0) */ | 434 | /* The SSI should always be disabled at this points (SSIEN=0) */ |
428 | wl = CCSR_SSI_SxCCR_WL(snd_pcm_format_width(runtime->format)); | ||
429 | 435 | ||
430 | /* In synchronous mode, the SSI uses STCCR for capture */ | 436 | /* In synchronous mode, the SSI uses STCCR for capture */ |
431 | clrsetbits_be32(&ssi->stccr, CCSR_SSI_SxCCR_WL_MASK, wl); | 437 | if ((substream->stream == SNDRV_PCM_STREAM_PLAYBACK) || |
438 | !ssi_private->asynchronous) | ||
439 | clrsetbits_be32(&ssi->stccr, | ||
440 | CCSR_SSI_SxCCR_WL_MASK, wl); | ||
441 | else | ||
442 | clrsetbits_be32(&ssi->srccr, | ||
443 | CCSR_SSI_SxCCR_WL_MASK, wl); | ||
432 | } | 444 | } |
433 | 445 | ||
434 | return 0; | 446 | return 0; |
@@ -452,28 +464,33 @@ static int fsl_ssi_trigger(struct snd_pcm_substream *substream, int cmd, | |||
452 | 464 | ||
453 | switch (cmd) { | 465 | switch (cmd) { |
454 | case SNDRV_PCM_TRIGGER_START: | 466 | case SNDRV_PCM_TRIGGER_START: |
455 | case SNDRV_PCM_TRIGGER_RESUME: | 467 | clrbits32(&ssi->scr, CCSR_SSI_SCR_SSIEN); |
456 | case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: | 468 | case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: |
457 | if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { | 469 | if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { |
458 | clrbits32(&ssi->scr, CCSR_SSI_SCR_SSIEN); | ||
459 | setbits32(&ssi->scr, | 470 | setbits32(&ssi->scr, |
460 | CCSR_SSI_SCR_SSIEN | CCSR_SSI_SCR_TE); | 471 | CCSR_SSI_SCR_SSIEN | CCSR_SSI_SCR_TE); |
461 | } else { | 472 | } else { |
462 | clrbits32(&ssi->scr, CCSR_SSI_SCR_SSIEN); | 473 | long timeout = jiffies + 10; |
474 | |||
463 | setbits32(&ssi->scr, | 475 | setbits32(&ssi->scr, |
464 | CCSR_SSI_SCR_SSIEN | CCSR_SSI_SCR_RE); | 476 | CCSR_SSI_SCR_SSIEN | CCSR_SSI_SCR_RE); |
465 | 477 | ||
466 | /* | 478 | /* Wait until the SSI has filled its FIFO. Without this |
467 | * I think we need this delay to allow time for the SSI | 479 | * delay, ALSA complains about overruns. When the FIFO |
468 | * to put data into its FIFO. Without it, ALSA starts | 480 | * is full, the DMA controller initiates its first |
469 | * to complain about overruns. | 481 | * transfer. Until then, however, the DMA's DAR |
482 | * register is zero, which translates to an | ||
483 | * out-of-bounds pointer. This makes ALSA think an | ||
484 | * overrun has occurred. | ||
470 | */ | 485 | */ |
471 | mdelay(1); | 486 | while (!(in_be32(&ssi->sisr) & CCSR_SSI_SISR_RFF0) && |
487 | (jiffies < timeout)); | ||
488 | if (!(in_be32(&ssi->sisr) & CCSR_SSI_SISR_RFF0)) | ||
489 | return -EIO; | ||
472 | } | 490 | } |
473 | break; | 491 | break; |
474 | 492 | ||
475 | case SNDRV_PCM_TRIGGER_STOP: | 493 | case SNDRV_PCM_TRIGGER_STOP: |
476 | case SNDRV_PCM_TRIGGER_SUSPEND: | ||
477 | case SNDRV_PCM_TRIGGER_PAUSE_PUSH: | 494 | case SNDRV_PCM_TRIGGER_PAUSE_PUSH: |
478 | if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) | 495 | if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) |
479 | clrbits32(&ssi->scr, CCSR_SSI_SCR_TE); | 496 | clrbits32(&ssi->scr, CCSR_SSI_SCR_TE); |
@@ -563,6 +580,15 @@ static int fsl_ssi_set_fmt(struct snd_soc_dai *cpu_dai, unsigned int format) | |||
563 | /** | 580 | /** |
564 | * fsl_ssi_dai_template: template CPU DAI for the SSI | 581 | * fsl_ssi_dai_template: template CPU DAI for the SSI |
565 | */ | 582 | */ |
583 | static struct snd_soc_dai_ops fsl_ssi_dai_ops = { | ||
584 | .startup = fsl_ssi_startup, | ||
585 | .hw_params = fsl_ssi_hw_params, | ||
586 | .shutdown = fsl_ssi_shutdown, | ||
587 | .trigger = fsl_ssi_trigger, | ||
588 | .set_sysclk = fsl_ssi_set_sysclk, | ||
589 | .set_fmt = fsl_ssi_set_fmt, | ||
590 | }; | ||
591 | |||
566 | static struct snd_soc_dai fsl_ssi_dai_template = { | 592 | static struct snd_soc_dai fsl_ssi_dai_template = { |
567 | .playback = { | 593 | .playback = { |
568 | /* The SSI does not support monaural audio. */ | 594 | /* The SSI does not support monaural audio. */ |
@@ -577,14 +603,7 @@ static struct snd_soc_dai fsl_ssi_dai_template = { | |||
577 | .rates = FSLSSI_I2S_RATES, | 603 | .rates = FSLSSI_I2S_RATES, |
578 | .formats = FSLSSI_I2S_FORMATS, | 604 | .formats = FSLSSI_I2S_FORMATS, |
579 | }, | 605 | }, |
580 | .ops = { | 606 | .ops = &fsl_ssi_dai_ops, |
581 | .startup = fsl_ssi_startup, | ||
582 | .prepare = fsl_ssi_prepare, | ||
583 | .shutdown = fsl_ssi_shutdown, | ||
584 | .trigger = fsl_ssi_trigger, | ||
585 | .set_sysclk = fsl_ssi_set_sysclk, | ||
586 | .set_fmt = fsl_ssi_set_fmt, | ||
587 | }, | ||
588 | }; | 607 | }; |
589 | 608 | ||
590 | /** | 609 | /** |
@@ -654,6 +673,7 @@ struct snd_soc_dai *fsl_ssi_create_dai(struct fsl_ssi_info *ssi_info) | |||
654 | ssi_private->ssi_phys = ssi_info->ssi_phys; | 673 | ssi_private->ssi_phys = ssi_info->ssi_phys; |
655 | ssi_private->irq = ssi_info->irq; | 674 | ssi_private->irq = ssi_info->irq; |
656 | ssi_private->dev = ssi_info->dev; | 675 | ssi_private->dev = ssi_info->dev; |
676 | ssi_private->asynchronous = ssi_info->asynchronous; | ||
657 | 677 | ||
658 | ssi_private->dev->driver_data = fsl_ssi_dai; | 678 | ssi_private->dev->driver_data = fsl_ssi_dai; |
659 | 679 | ||
@@ -704,6 +724,14 @@ void fsl_ssi_destroy_dai(struct snd_soc_dai *fsl_ssi_dai) | |||
704 | } | 724 | } |
705 | EXPORT_SYMBOL_GPL(fsl_ssi_destroy_dai); | 725 | EXPORT_SYMBOL_GPL(fsl_ssi_destroy_dai); |
706 | 726 | ||
727 | static int __init fsl_ssi_init(void) | ||
728 | { | ||
729 | printk(KERN_INFO "Freescale Synchronous Serial Interface (SSI) ASoC Driver\n"); | ||
730 | |||
731 | return 0; | ||
732 | } | ||
733 | module_init(fsl_ssi_init); | ||
734 | |||
707 | MODULE_AUTHOR("Timur Tabi <timur@freescale.com>"); | 735 | MODULE_AUTHOR("Timur Tabi <timur@freescale.com>"); |
708 | MODULE_DESCRIPTION("Freescale Synchronous Serial Interface (SSI) ASoC Driver"); | 736 | MODULE_DESCRIPTION("Freescale Synchronous Serial Interface (SSI) ASoC Driver"); |
709 | MODULE_LICENSE("GPL"); | 737 | MODULE_LICENSE("GPL"); |