Commit a454dad1 authored by Timur Tabi's avatar Timur Tabi Committed by Mark Brown

ASoC: add support for SSI asynchronous mode to the Freescale SSI drivers

Add a new device tree property for the SSI node: "fsl,ssi-asynchronous".  If
defined, the SSI is programmed into asynchronous mode, otherwise it is
programmed into synchronous mode.  In asynchronous mode, pin SRCK must be
connected to the same clock source as STFS, and pin SRFS must be connected to
the same signal as STFS.  Asynchronous mode allows playback and capture to
use different sample sizes.  It also technically allows different sample rates,
but the driver does not support that.
Signed-off-by: default avatarTimur Tabi <timur@freescale.com>
Signed-off-by: default avatarMark Brown <broonie@opensource.wolfsonmicro.com>
parent 499d8f4a
...@@ -72,6 +72,7 @@ ...@@ -72,6 +72,7 @@
* @dev: struct device pointer * @dev: struct device pointer
* @playback: the number of playback streams opened * @playback: the number of playback streams opened
* @capture: the number of capture streams opened * @capture: the number of capture streams opened
* @asynchronous: 0=synchronous mode, 1=asynchronous mode
* @cpu_dai: the CPU DAI for this device * @cpu_dai: the CPU DAI for this device
* @dev_attr: the sysfs device attribute structure * @dev_attr: the sysfs device attribute structure
* @stats: SSI statistics * @stats: SSI statistics
...@@ -86,6 +87,7 @@ struct fsl_ssi_private { ...@@ -86,6 +87,7 @@ struct fsl_ssi_private {
struct device *dev; struct device *dev;
unsigned int playback; unsigned int playback;
unsigned int capture; unsigned int capture;
int asynchronous;
struct snd_soc_dai cpu_dai; struct snd_soc_dai cpu_dai;
struct device_attribute dev_attr; struct device_attribute dev_attr;
...@@ -301,9 +303,10 @@ static int fsl_ssi_startup(struct snd_pcm_substream *substream, ...@@ -301,9 +303,10 @@ static int fsl_ssi_startup(struct snd_pcm_substream *substream,
* *
* FIXME: Little-endian samples require a different shift dir * FIXME: Little-endian samples require a different shift dir
*/ */
clrsetbits_be32(&ssi->scr, CCSR_SSI_SCR_I2S_MODE_MASK, clrsetbits_be32(&ssi->scr,
CCSR_SSI_SCR_TFR_CLK_DIS | CCSR_SSI_SCR_I2S_MODE_MASK | CCSR_SSI_SCR_SYN,
CCSR_SSI_SCR_I2S_MODE_SLAVE | CCSR_SSI_SCR_SYN); CCSR_SSI_SCR_TFR_CLK_DIS | CCSR_SSI_SCR_I2S_MODE_SLAVE
| (ssi_private->asynchronous ? 0 : CCSR_SSI_SCR_SYN));
out_be32(&ssi->stcr, out_be32(&ssi->stcr,
CCSR_SSI_STCR_TXBIT0 | CCSR_SSI_STCR_TFEN0 | CCSR_SSI_STCR_TXBIT0 | CCSR_SSI_STCR_TFEN0 |
...@@ -382,10 +385,15 @@ static int fsl_ssi_startup(struct snd_pcm_substream *substream, ...@@ -382,10 +385,15 @@ static int fsl_ssi_startup(struct snd_pcm_substream *substream,
SNDRV_PCM_HW_PARAM_RATE, SNDRV_PCM_HW_PARAM_RATE,
first_runtime->rate, first_runtime->rate); first_runtime->rate, first_runtime->rate);
snd_pcm_hw_constraint_minmax(substream->runtime, /* If we're in synchronous mode, then we need to constrain
SNDRV_PCM_HW_PARAM_SAMPLE_BITS, * the sample size as well. We don't support independent sample
first_runtime->sample_bits, * rates in asynchronous mode.
first_runtime->sample_bits); */
if (!ssi_private->asynchronous)
snd_pcm_hw_constraint_minmax(substream->runtime,
SNDRV_PCM_HW_PARAM_SAMPLE_BITS,
first_runtime->sample_bits,
first_runtime->sample_bits);
ssi_private->second_stream = substream; ssi_private->second_stream = substream;
} }
...@@ -421,13 +429,18 @@ static int fsl_ssi_hw_params(struct snd_pcm_substream *substream, ...@@ -421,13 +429,18 @@ static int fsl_ssi_hw_params(struct snd_pcm_substream *substream,
struct ccsr_ssi __iomem *ssi = ssi_private->ssi; struct ccsr_ssi __iomem *ssi = ssi_private->ssi;
unsigned int sample_size = unsigned int sample_size =
snd_pcm_format_width(params_format(hw_params)); snd_pcm_format_width(params_format(hw_params));
u32 wl; u32 wl = CCSR_SSI_SxCCR_WL(sample_size);
/* The SSI should always be disabled at this points (SSIEN=0) */ /* The SSI should always be disabled at this points (SSIEN=0) */
wl = CCSR_SSI_SxCCR_WL(sample_size);
/* In synchronous mode, the SSI uses STCCR for capture */ /* In synchronous mode, the SSI uses STCCR for capture */
clrsetbits_be32(&ssi->stccr, CCSR_SSI_SxCCR_WL_MASK, wl); if ((substream->stream == SNDRV_PCM_STREAM_PLAYBACK) ||
!ssi_private->asynchronous)
clrsetbits_be32(&ssi->stccr,
CCSR_SSI_SxCCR_WL_MASK, wl);
else
clrsetbits_be32(&ssi->srccr,
CCSR_SSI_SxCCR_WL_MASK, wl);
} }
return 0; return 0;
...@@ -653,6 +666,7 @@ struct snd_soc_dai *fsl_ssi_create_dai(struct fsl_ssi_info *ssi_info) ...@@ -653,6 +666,7 @@ struct snd_soc_dai *fsl_ssi_create_dai(struct fsl_ssi_info *ssi_info)
ssi_private->ssi_phys = ssi_info->ssi_phys; ssi_private->ssi_phys = ssi_info->ssi_phys;
ssi_private->irq = ssi_info->irq; ssi_private->irq = ssi_info->irq;
ssi_private->dev = ssi_info->dev; ssi_private->dev = ssi_info->dev;
ssi_private->asynchronous = ssi_info->asynchronous;
ssi_private->dev->driver_data = fsl_ssi_dai; ssi_private->dev->driver_data = fsl_ssi_dai;
...@@ -703,6 +717,14 @@ void fsl_ssi_destroy_dai(struct snd_soc_dai *fsl_ssi_dai) ...@@ -703,6 +717,14 @@ void fsl_ssi_destroy_dai(struct snd_soc_dai *fsl_ssi_dai)
} }
EXPORT_SYMBOL_GPL(fsl_ssi_destroy_dai); EXPORT_SYMBOL_GPL(fsl_ssi_destroy_dai);
static int __init fsl_ssi_init(void)
{
printk(KERN_INFO "Freescale Synchronous Serial Interface (SSI) ASoC Driver\n");
return 0;
}
module_init(fsl_ssi_init);
MODULE_AUTHOR("Timur Tabi <timur@freescale.com>"); MODULE_AUTHOR("Timur Tabi <timur@freescale.com>");
MODULE_DESCRIPTION("Freescale Synchronous Serial Interface (SSI) ASoC Driver"); MODULE_DESCRIPTION("Freescale Synchronous Serial Interface (SSI) ASoC Driver");
MODULE_LICENSE("GPL"); MODULE_LICENSE("GPL");
...@@ -208,6 +208,7 @@ struct ccsr_ssi { ...@@ -208,6 +208,7 @@ struct ccsr_ssi {
* ssi_phys: physical address of the SSI registers * ssi_phys: physical address of the SSI registers
* irq: IRQ of this SSI * irq: IRQ of this SSI
* dev: struct device, used to create the sysfs statistics file * dev: struct device, used to create the sysfs statistics file
* asynchronous: 0=synchronous mode, 1=asynchronous mode
*/ */
struct fsl_ssi_info { struct fsl_ssi_info {
unsigned int id; unsigned int id;
...@@ -215,6 +216,7 @@ struct fsl_ssi_info { ...@@ -215,6 +216,7 @@ struct fsl_ssi_info {
dma_addr_t ssi_phys; dma_addr_t ssi_phys;
unsigned int irq; unsigned int irq;
struct device *dev; struct device *dev;
int asynchronous;
}; };
struct snd_soc_dai *fsl_ssi_create_dai(struct fsl_ssi_info *ssi_info); struct snd_soc_dai *fsl_ssi_create_dai(struct fsl_ssi_info *ssi_info);
......
...@@ -353,6 +353,11 @@ static int mpc8610_hpcd_probe(struct of_device *ofdev, ...@@ -353,6 +353,11 @@ static int mpc8610_hpcd_probe(struct of_device *ofdev,
} }
ssi_info.irq = machine_data->ssi_irq; ssi_info.irq = machine_data->ssi_irq;
/* Do we want to use asynchronous mode? */
ssi_info.asynchronous =
of_find_property(np, "fsl,ssi-asynchronous", NULL) ? 1 : 0;
if (ssi_info.asynchronous)
dev_info(&ofdev->dev, "using asynchronous mode\n");
/* Map the global utilities registers. */ /* Map the global utilities registers. */
guts_np = of_find_compatible_node(NULL, NULL, "fsl,mpc8610-guts"); guts_np = of_find_compatible_node(NULL, NULL, "fsl,mpc8610-guts");
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment