diff options
Diffstat (limited to 'sound/soc/fsl')
-rw-r--r-- | sound/soc/fsl/fsl_dma.c | 3 | ||||
-rw-r--r-- | sound/soc/fsl/fsl_ssi.c | 65 | ||||
-rw-r--r-- | sound/soc/fsl/fsl_ssi.h | 2 | ||||
-rw-r--r-- | sound/soc/fsl/mpc8610_hpcd.c | 5 |
4 files changed, 55 insertions, 20 deletions
diff --git a/sound/soc/fsl/fsl_dma.c b/sound/soc/fsl/fsl_dma.c index 58a3fa49750..b3eb8570cd7 100644 --- a/sound/soc/fsl/fsl_dma.c +++ b/sound/soc/fsl/fsl_dma.c | |||
@@ -142,7 +142,8 @@ static const struct snd_pcm_hardware fsl_dma_hardware = { | |||
142 | .info = SNDRV_PCM_INFO_INTERLEAVED | | 142 | .info = SNDRV_PCM_INFO_INTERLEAVED | |
143 | SNDRV_PCM_INFO_MMAP | | 143 | SNDRV_PCM_INFO_MMAP | |
144 | SNDRV_PCM_INFO_MMAP_VALID | | 144 | SNDRV_PCM_INFO_MMAP_VALID | |
145 | SNDRV_PCM_INFO_JOINT_DUPLEX, | 145 | SNDRV_PCM_INFO_JOINT_DUPLEX | |
146 | SNDRV_PCM_INFO_PAUSE, | ||
146 | .formats = FSLDMA_PCM_FORMATS, | 147 | .formats = FSLDMA_PCM_FORMATS, |
147 | .rates = FSLDMA_PCM_RATES, | 148 | .rates = FSLDMA_PCM_RATES, |
148 | .rate_min = 5512, | 149 | .rate_min = 5512, |
diff --git a/sound/soc/fsl/fsl_ssi.c b/sound/soc/fsl/fsl_ssi.c index 0fddd437a7c..169bca295b7 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 | } |
@@ -421,13 +429,18 @@ static int fsl_ssi_hw_params(struct snd_pcm_substream *substream, | |||
421 | struct ccsr_ssi __iomem *ssi = ssi_private->ssi; | 429 | struct ccsr_ssi __iomem *ssi = ssi_private->ssi; |
422 | unsigned int sample_size = | 430 | unsigned int sample_size = |
423 | snd_pcm_format_width(params_format(hw_params)); | 431 | snd_pcm_format_width(params_format(hw_params)); |
424 | u32 wl; | 432 | u32 wl = CCSR_SSI_SxCCR_WL(sample_size); |
425 | 433 | ||
426 | /* The SSI should always be disabled at this points (SSIEN=0) */ | 434 | /* The SSI should always be disabled at this points (SSIEN=0) */ |
427 | wl = CCSR_SSI_SxCCR_WL(sample_size); | ||
428 | 435 | ||
429 | /* In synchronous mode, the SSI uses STCCR for capture */ | 436 | /* In synchronous mode, the SSI uses STCCR for capture */ |
430 | 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); | ||
431 | } | 444 | } |
432 | 445 | ||
433 | return 0; | 446 | return 0; |
@@ -451,28 +464,33 @@ static int fsl_ssi_trigger(struct snd_pcm_substream *substream, int cmd, | |||
451 | 464 | ||
452 | switch (cmd) { | 465 | switch (cmd) { |
453 | case SNDRV_PCM_TRIGGER_START: | 466 | case SNDRV_PCM_TRIGGER_START: |
454 | case SNDRV_PCM_TRIGGER_RESUME: | 467 | clrbits32(&ssi->scr, CCSR_SSI_SCR_SSIEN); |
455 | case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: | 468 | case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: |
456 | if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { | 469 | if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { |
457 | clrbits32(&ssi->scr, CCSR_SSI_SCR_SSIEN); | ||
458 | setbits32(&ssi->scr, | 470 | setbits32(&ssi->scr, |
459 | CCSR_SSI_SCR_SSIEN | CCSR_SSI_SCR_TE); | 471 | CCSR_SSI_SCR_SSIEN | CCSR_SSI_SCR_TE); |
460 | } else { | 472 | } else { |
461 | clrbits32(&ssi->scr, CCSR_SSI_SCR_SSIEN); | 473 | long timeout = jiffies + 10; |
474 | |||
462 | setbits32(&ssi->scr, | 475 | setbits32(&ssi->scr, |
463 | CCSR_SSI_SCR_SSIEN | CCSR_SSI_SCR_RE); | 476 | CCSR_SSI_SCR_SSIEN | CCSR_SSI_SCR_RE); |
464 | 477 | ||
465 | /* | 478 | /* Wait until the SSI has filled its FIFO. Without this |
466 | * I think we need this delay to allow time for the SSI | 479 | * delay, ALSA complains about overruns. When the FIFO |
467 | * to put data into its FIFO. Without it, ALSA starts | 480 | * is full, the DMA controller initiates its first |
468 | * 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. | ||
469 | */ | 485 | */ |
470 | 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; | ||
471 | } | 490 | } |
472 | break; | 491 | break; |
473 | 492 | ||
474 | case SNDRV_PCM_TRIGGER_STOP: | 493 | case SNDRV_PCM_TRIGGER_STOP: |
475 | case SNDRV_PCM_TRIGGER_SUSPEND: | ||
476 | case SNDRV_PCM_TRIGGER_PAUSE_PUSH: | 494 | case SNDRV_PCM_TRIGGER_PAUSE_PUSH: |
477 | if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) | 495 | if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) |
478 | clrbits32(&ssi->scr, CCSR_SSI_SCR_TE); | 496 | clrbits32(&ssi->scr, CCSR_SSI_SCR_TE); |
@@ -655,6 +673,7 @@ struct snd_soc_dai *fsl_ssi_create_dai(struct fsl_ssi_info *ssi_info) | |||
655 | ssi_private->ssi_phys = ssi_info->ssi_phys; | 673 | ssi_private->ssi_phys = ssi_info->ssi_phys; |
656 | ssi_private->irq = ssi_info->irq; | 674 | ssi_private->irq = ssi_info->irq; |
657 | ssi_private->dev = ssi_info->dev; | 675 | ssi_private->dev = ssi_info->dev; |
676 | ssi_private->asynchronous = ssi_info->asynchronous; | ||
658 | 677 | ||
659 | ssi_private->dev->driver_data = fsl_ssi_dai; | 678 | ssi_private->dev->driver_data = fsl_ssi_dai; |
660 | 679 | ||
@@ -705,6 +724,14 @@ void fsl_ssi_destroy_dai(struct snd_soc_dai *fsl_ssi_dai) | |||
705 | } | 724 | } |
706 | EXPORT_SYMBOL_GPL(fsl_ssi_destroy_dai); | 725 | EXPORT_SYMBOL_GPL(fsl_ssi_destroy_dai); |
707 | 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 | |||
708 | MODULE_AUTHOR("Timur Tabi <timur@freescale.com>"); | 735 | MODULE_AUTHOR("Timur Tabi <timur@freescale.com>"); |
709 | MODULE_DESCRIPTION("Freescale Synchronous Serial Interface (SSI) ASoC Driver"); | 736 | MODULE_DESCRIPTION("Freescale Synchronous Serial Interface (SSI) ASoC Driver"); |
710 | MODULE_LICENSE("GPL"); | 737 | MODULE_LICENSE("GPL"); |
diff --git a/sound/soc/fsl/fsl_ssi.h b/sound/soc/fsl/fsl_ssi.h index 83b44d700e3..eade01feaab 100644 --- a/sound/soc/fsl/fsl_ssi.h +++ b/sound/soc/fsl/fsl_ssi.h | |||
@@ -208,6 +208,7 @@ struct ccsr_ssi { | |||
208 | * ssi_phys: physical address of the SSI registers | 208 | * ssi_phys: physical address of the SSI registers |
209 | * irq: IRQ of this SSI | 209 | * irq: IRQ of this SSI |
210 | * dev: struct device, used to create the sysfs statistics file | 210 | * dev: struct device, used to create the sysfs statistics file |
211 | * asynchronous: 0=synchronous mode, 1=asynchronous mode | ||
211 | */ | 212 | */ |
212 | struct fsl_ssi_info { | 213 | struct fsl_ssi_info { |
213 | unsigned int id; | 214 | unsigned int id; |
@@ -215,6 +216,7 @@ struct fsl_ssi_info { | |||
215 | dma_addr_t ssi_phys; | 216 | dma_addr_t ssi_phys; |
216 | unsigned int irq; | 217 | unsigned int irq; |
217 | struct device *dev; | 218 | struct device *dev; |
219 | int asynchronous; | ||
218 | }; | 220 | }; |
219 | 221 | ||
220 | struct snd_soc_dai *fsl_ssi_create_dai(struct fsl_ssi_info *ssi_info); | 222 | struct snd_soc_dai *fsl_ssi_create_dai(struct fsl_ssi_info *ssi_info); |
diff --git a/sound/soc/fsl/mpc8610_hpcd.c b/sound/soc/fsl/mpc8610_hpcd.c index acf39a646b2..ef67d1cdffe 100644 --- a/sound/soc/fsl/mpc8610_hpcd.c +++ b/sound/soc/fsl/mpc8610_hpcd.c | |||
@@ -353,6 +353,11 @@ static int mpc8610_hpcd_probe(struct of_device *ofdev, | |||
353 | } | 353 | } |
354 | ssi_info.irq = machine_data->ssi_irq; | 354 | ssi_info.irq = machine_data->ssi_irq; |
355 | 355 | ||
356 | /* Do we want to use asynchronous mode? */ | ||
357 | ssi_info.asynchronous = | ||
358 | of_find_property(np, "fsl,ssi-asynchronous", NULL) ? 1 : 0; | ||
359 | if (ssi_info.asynchronous) | ||
360 | dev_info(&ofdev->dev, "using asynchronous mode\n"); | ||
356 | 361 | ||
357 | /* Map the global utilities registers. */ | 362 | /* Map the global utilities registers. */ |
358 | guts_np = of_find_compatible_node(NULL, NULL, "fsl,mpc8610-guts"); | 363 | guts_np = of_find_compatible_node(NULL, NULL, "fsl,mpc8610-guts"); |