diff options
Diffstat (limited to 'sound')
-rw-r--r-- | sound/soc/pxa/pxa-ssp.c | 82 |
1 files changed, 62 insertions, 20 deletions
diff --git a/sound/soc/pxa/pxa-ssp.c b/sound/soc/pxa/pxa-ssp.c index 0291c7cb64eb..ffddcf117eb8 100644 --- a/sound/soc/pxa/pxa-ssp.c +++ b/sound/soc/pxa/pxa-ssp.c @@ -43,7 +43,8 @@ struct ssp_priv { struct ssp_device *ssp; unsigned int sysclk; - int dai_fmt; + unsigned int dai_fmt; + unsigned int configured_dai_fmt; #ifdef CONFIG_PM uint32_t cr0; uint32_t cr1; @@ -433,36 +434,72 @@ static int pxa_ssp_set_dai_tristate(struct snd_soc_dai *cpu_dai, return 0; } +static int pxa_ssp_set_dai_fmt(struct snd_soc_dai *cpu_dai, + unsigned int fmt) +{ + struct ssp_priv *priv = snd_soc_dai_get_drvdata(cpu_dai); + + switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { + case SND_SOC_DAIFMT_CBM_CFM: + case SND_SOC_DAIFMT_CBM_CFS: + case SND_SOC_DAIFMT_CBS_CFS: + break; + default: + return -EINVAL; + } + + switch (fmt & SND_SOC_DAIFMT_INV_MASK) { + case SND_SOC_DAIFMT_NB_NF: + case SND_SOC_DAIFMT_NB_IF: + case SND_SOC_DAIFMT_IB_IF: + case SND_SOC_DAIFMT_IB_NF: + break; + default: + return -EINVAL; + } + + switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { + case SND_SOC_DAIFMT_I2S: + case SND_SOC_DAIFMT_DSP_A: + case SND_SOC_DAIFMT_DSP_B: + break; + + default: + return -EINVAL; + } + + /* Settings will be applied in hw_params() */ + priv->dai_fmt = fmt; + + return 0; +} + /* * Set up the SSP DAI format. * The SSP Port must be inactive before calling this function as the * physical interface format is changed. */ -static int pxa_ssp_set_dai_fmt(struct snd_soc_dai *cpu_dai, - unsigned int fmt) +static int pxa_ssp_configure_dai_fmt(struct ssp_priv *priv) { - struct ssp_priv *priv = snd_soc_dai_get_drvdata(cpu_dai); struct ssp_device *ssp = priv->ssp; u32 sscr0, sscr1, sspsp, scfr; /* check if we need to change anything at all */ - if (priv->dai_fmt == fmt) + if (priv->configured_dai_fmt == priv->dai_fmt) return 0; - /* we can only change the settings if the port is not in use */ - if (pxa_ssp_read_reg(ssp, SSCR0) & SSCR0_SSE) { - dev_err(&ssp->pdev->dev, - "can't change hardware dai format: stream is in use"); - return -EINVAL; - } - /* reset port settings */ sscr0 = pxa_ssp_read_reg(ssp, SSCR0) & - ~(SSCR0_ECS | SSCR0_NCS | SSCR0_MOD | SSCR0_ACS); - sscr1 = SSCR1_RxTresh(8) | SSCR1_TxTresh(7); - sspsp = 0; + ~(SSCR0_PSP | SSCR0_MOD); + sscr1 = pxa_ssp_read_reg(ssp, SSCR1) & + ~(SSCR1_SCLKDIR | SSCR1_SFRMDIR | SSCR1_SCFR | + SSCR1_RWOT | SSCR1_TRAIL | SSCR1_TFT | SSCR1_RFT); + sspsp = pxa_ssp_read_reg(ssp, SSPSP) & + ~(SSPSP_SFRMP | SSPSP_SCMODE(3)); - switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { + sscr1 |= SSCR1_RxTresh(8) | SSCR1_TxTresh(7); + + switch (priv->dai_fmt & SND_SOC_DAIFMT_MASTER_MASK) { case SND_SOC_DAIFMT_CBM_CFM: sscr1 |= SSCR1_SCLKDIR | SSCR1_SFRMDIR | SSCR1_SCFR; break; @@ -475,7 +512,7 @@ static int pxa_ssp_set_dai_fmt(struct snd_soc_dai *cpu_dai, return -EINVAL; } - switch (fmt & SND_SOC_DAIFMT_INV_MASK) { + switch (priv->dai_fmt & SND_SOC_DAIFMT_INV_MASK) { case SND_SOC_DAIFMT_NB_NF: sspsp |= SSPSP_SFRMP; break; @@ -491,7 +528,7 @@ static int pxa_ssp_set_dai_fmt(struct snd_soc_dai *cpu_dai, return -EINVAL; } - switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { + switch (priv->dai_fmt & SND_SOC_DAIFMT_FORMAT_MASK) { case SND_SOC_DAIFMT_I2S: sscr0 |= SSCR0_PSP; sscr1 |= SSCR1_RWOT | SSCR1_TRAIL; @@ -513,7 +550,7 @@ static int pxa_ssp_set_dai_fmt(struct snd_soc_dai *cpu_dai, pxa_ssp_write_reg(ssp, SSCR1, sscr1); pxa_ssp_write_reg(ssp, SSPSP, sspsp); - switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { + switch (priv->dai_fmt & SND_SOC_DAIFMT_MASTER_MASK) { case SND_SOC_DAIFMT_CBM_CFM: case SND_SOC_DAIFMT_CBM_CFS: scfr = pxa_ssp_read_reg(ssp, SSCR1) | SSCR1_SCFR; @@ -530,7 +567,7 @@ static int pxa_ssp_set_dai_fmt(struct snd_soc_dai *cpu_dai, * we have to defer some things until hw_params() where we * know parameters like the sample size. */ - priv->dai_fmt = fmt; + priv->configured_dai_fmt = priv->dai_fmt; return 0; } @@ -551,6 +588,7 @@ static int pxa_ssp_hw_params(struct snd_pcm_substream *substream, int width = snd_pcm_format_physical_width(params_format(params)); int ttsa = pxa_ssp_read_reg(ssp, SSTSA) & 0xf; struct snd_dmaengine_dai_dma_data *dma_data; + int ret; dma_data = snd_soc_dai_get_dma_data(cpu_dai, substream); @@ -566,6 +604,10 @@ static int pxa_ssp_hw_params(struct snd_pcm_substream *substream, if (pxa_ssp_read_reg(ssp, SSCR0) & SSCR0_SSE) return 0; + ret = pxa_ssp_configure_dai_fmt(priv); + if (ret < 0) + return ret; + /* clear selected SSP bits */ sscr0 = pxa_ssp_read_reg(ssp, SSCR0) & ~(SSCR0_DSS | SSCR0_EDSS); |