mirror of
https://github.com/adulau/aha.git
synced 2024-12-28 19:56:18 +00:00
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: Timur Tabi <timur@freescale.com> Signed-off-by: Mark Brown <broonie@opensource.wolfsonmicro.com>
This commit is contained in:
parent
499d8f4a52
commit
a454dad19e
3 changed files with 39 additions and 10 deletions
|
@ -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 {
|
||||||
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,
|
||||||
*
|
*
|
||||||
* 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,
|
||||||
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,
|
||||||
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)
|
||||||
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)
|
||||||
}
|
}
|
||||||
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 {
|
||||||
* 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 {
|
||||||
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,
|
||||||
}
|
}
|
||||||
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");
|
||||||
|
|
Loading…
Reference in a new issue