|
@@ -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);
|
|
|
|