diff mbox series

[2/5] ASoC: atmel_ssc_dai: rework DAI format configuration

Message ID ff244c1b626fc9c63b4a1fa4e09eeaa579e683d9.1563819483.git.mirq-linux@rere.qmqm.pl (mailing list archive)
State New, archived
Headers show
Series ASoC: atmel: extend SSC support | expand

Commit Message

Michał Mirosław July 22, 2019, 6:27 p.m. UTC
Rework DAI format calculation in preparation for adding more formats
later.

Note: this changes FSEDGE to POSITIVE for I2S CBM_CFS mode as the TXSYN
interrupt is not used anyway.

Signed-off-by: Michał Mirosław <mirq-linux@rere.qmqm.pl>
---
 sound/soc/atmel/atmel_ssc_dai.c | 283 +++++++++-----------------------
 1 file changed, 79 insertions(+), 204 deletions(-)

Comments

Codrin Ciubotariu July 24, 2019, 10:35 a.m. UTC | #1
On 22.07.2019 21:27, Michał Mirosław wrote:
> Rework DAI format calculation in preparation for adding more formats
> later.
> 
> Note: this changes FSEDGE to POSITIVE for I2S CBM_CFS mode as the TXSYN
> interrupt is not used anyway.
> 
> Signed-off-by: Michał Mirosław <mirq-linux@rere.qmqm.pl>
> ---
>   sound/soc/atmel/atmel_ssc_dai.c | 283 +++++++++-----------------------
>   1 file changed, 79 insertions(+), 204 deletions(-)
> 
> diff --git a/sound/soc/atmel/atmel_ssc_dai.c b/sound/soc/atmel/atmel_ssc_dai.c
> index 6f89483ac88c..b2992496e52f 100644
> --- a/sound/soc/atmel/atmel_ssc_dai.c
> +++ b/sound/soc/atmel/atmel_ssc_dai.c
> @@ -471,7 +471,7 @@ static int atmel_ssc_hw_params(struct snd_pcm_substream *substream,
>   	int dir, channels, bits;
>   	u32 tfmr, rfmr, tcmr, rcmr;
>   	int ret;
> -	int fslen, fslen_ext;
> +	int fslen, fslen_ext, fs_osync;
>   	u32 cmr_div;
>   	u32 tcmr_period;
>   	u32 rcmr_period;
> @@ -558,226 +558,40 @@ static int atmel_ssc_hw_params(struct snd_pcm_substream *substream,
>   	/*
>   	 * Compute SSC register settings.
>   	 */
> -	switch (ssc_p->daifmt
> -		& (SND_SOC_DAIFMT_FORMAT_MASK | SND_SOC_DAIFMT_MASTER_MASK)) {
>   
> -	case SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_CBS_CFS:
> -		/*
> -		 * I2S format, SSC provides BCLK and LRC clocks.
> -		 *
> -		 * The SSC transmit and receive clocks are generated
> -		 * from the MCK divider, and the BCLK signal
> -		 * is output on the SSC TK line.
> -		 */
> -
> -		if (bits > 16 && !ssc->pdata->has_fslen_ext) {
> -			dev_err(dai->dev,
> -				"sample size %d is too large for SSC device\n",
> -				bits);
> -			return -EINVAL;
> -		}
> -
> -		fslen_ext = (bits - 1) / 16;
> -		fslen = (bits - 1) % 16;
> -
> -		rcmr =	  SSC_BF(RCMR_PERIOD, rcmr_period)
> -			| SSC_BF(RCMR_STTDLY, START_DELAY)
> -			| SSC_BF(RCMR_START, SSC_START_FALLING_RF)
> -			| SSC_BF(RCMR_CKI, SSC_CKI_RISING)
> -			| SSC_BF(RCMR_CKO, SSC_CKO_NONE)
> -			| SSC_BF(RCMR_CKS, SSC_CKS_DIV);
> -
> -		rfmr =    SSC_BF(RFMR_FSLEN_EXT, fslen_ext)
> -			| SSC_BF(RFMR_FSEDGE, SSC_FSEDGE_POSITIVE)
> -			| SSC_BF(RFMR_FSOS, SSC_FSOS_NEGATIVE)
> -			| SSC_BF(RFMR_FSLEN, fslen)
> -			| SSC_BF(RFMR_DATNB, (channels - 1))
> -			| SSC_BIT(RFMR_MSBF)
> -			| SSC_BF(RFMR_LOOP, 0)
> -			| SSC_BF(RFMR_DATLEN, (bits - 1));
> -
> -		tcmr =	  SSC_BF(TCMR_PERIOD, tcmr_period)
> -			| SSC_BF(TCMR_STTDLY, START_DELAY)
> -			| SSC_BF(TCMR_START, SSC_START_FALLING_RF)
> -			| SSC_BF(TCMR_CKI, SSC_CKI_FALLING)
> -			| SSC_BF(TCMR_CKO, SSC_CKO_CONTINUOUS)
> -			| SSC_BF(TCMR_CKS, SSC_CKS_DIV);
> -
> -		tfmr =    SSC_BF(TFMR_FSLEN_EXT, fslen_ext)
> -			| SSC_BF(TFMR_FSEDGE, SSC_FSEDGE_POSITIVE)
> -			| SSC_BF(TFMR_FSDEN, 0)
> -			| SSC_BF(TFMR_FSOS, SSC_FSOS_NEGATIVE)
> -			| SSC_BF(TFMR_FSLEN, fslen)
> -			| SSC_BF(TFMR_DATNB, (channels - 1))
> -			| SSC_BIT(TFMR_MSBF)
> -			| SSC_BF(TFMR_DATDEF, 0)
> -			| SSC_BF(TFMR_DATLEN, (bits - 1));
> -		break;
> -
> -	case SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_CBM_CFM:
> -		/* I2S format, CODEC supplies BCLK and LRC clocks. */
> -		rcmr =	  SSC_BF(RCMR_PERIOD, 0)
> -			| SSC_BF(RCMR_STTDLY, START_DELAY)
> -			| SSC_BF(RCMR_START, SSC_START_FALLING_RF)
> -			| SSC_BF(RCMR_CKI, SSC_CKI_RISING)
> -			| SSC_BF(RCMR_CKO, SSC_CKO_NONE)
> -			| SSC_BF(RCMR_CKS, ssc->clk_from_rk_pin ?
> -					   SSC_CKS_PIN : SSC_CKS_CLOCK);
> -
> -		rfmr =	  SSC_BF(RFMR_FSEDGE, SSC_FSEDGE_POSITIVE)
> -			| SSC_BF(RFMR_FSOS, SSC_FSOS_NONE)
> -			| SSC_BF(RFMR_FSLEN, 0)
> -			| SSC_BF(RFMR_DATNB, (channels - 1))
> -			| SSC_BIT(RFMR_MSBF)
> -			| SSC_BF(RFMR_LOOP, 0)
> -			| SSC_BF(RFMR_DATLEN, (bits - 1));
> +	fslen_ext = (bits - 1) / 16;
> +	fslen = (bits - 1) % 16;
>   
> -		tcmr =	  SSC_BF(TCMR_PERIOD, 0)
> -			| SSC_BF(TCMR_STTDLY, START_DELAY)
> -			| SSC_BF(TCMR_START, SSC_START_FALLING_RF)
> -			| SSC_BF(TCMR_CKI, SSC_CKI_FALLING)
> -			| SSC_BF(TCMR_CKO, SSC_CKO_NONE)
> -			| SSC_BF(TCMR_CKS, ssc->clk_from_rk_pin ?
> -					   SSC_CKS_CLOCK : SSC_CKS_PIN);
> +	switch (ssc_p->daifmt & SND_SOC_DAIFMT_FORMAT_MASK) {
>   
> -		tfmr =	  SSC_BF(TFMR_FSEDGE, SSC_FSEDGE_POSITIVE)
> -			| SSC_BF(TFMR_FSDEN, 0)
> -			| SSC_BF(TFMR_FSOS, SSC_FSOS_NONE)
> -			| SSC_BF(TFMR_FSLEN, 0)
> -			| SSC_BF(TFMR_DATNB, (channels - 1))
> -			| SSC_BIT(TFMR_MSBF)
> -			| SSC_BF(TFMR_DATDEF, 0)
> -			| SSC_BF(TFMR_DATLEN, (bits - 1));
> -		break;
> -
> -	case SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_CBM_CFS:
> -		/* I2S format, CODEC supplies BCLK, SSC supplies LRCLK. */
> -		if (bits > 16 && !ssc->pdata->has_fslen_ext) {
> -			dev_err(dai->dev,
> -				"sample size %d is too large for SSC device\n",
> -				bits);
> -			return -EINVAL;
> -		}
> -
> -		fslen_ext = (bits - 1) / 16;
> -		fslen = (bits - 1) % 16;
> -
> -		rcmr =	  SSC_BF(RCMR_PERIOD, rcmr_period)
> -			| SSC_BF(RCMR_STTDLY, START_DELAY)
> -			| SSC_BF(RCMR_START, SSC_START_FALLING_RF)
> -			| SSC_BF(RCMR_CKI, SSC_CKI_RISING)
> -			| SSC_BF(RCMR_CKO, SSC_CKO_NONE)
> -			| SSC_BF(RCMR_CKS, ssc->clk_from_rk_pin ?
> -					   SSC_CKS_PIN : SSC_CKS_CLOCK);
> -
> -		rfmr =    SSC_BF(RFMR_FSLEN_EXT, fslen_ext)
> -			| SSC_BF(RFMR_FSEDGE, SSC_FSEDGE_POSITIVE)
> -			| SSC_BF(RFMR_FSOS, SSC_FSOS_NEGATIVE)
> -			| SSC_BF(RFMR_FSLEN, fslen)
> -			| SSC_BF(RFMR_DATNB, (channels - 1))
> -			| SSC_BIT(RFMR_MSBF)
> -			| SSC_BF(RFMR_LOOP, 0)
> -			| SSC_BF(RFMR_DATLEN, (bits - 1));
> -
> -		tcmr =	  SSC_BF(TCMR_PERIOD, tcmr_period)
> -			| SSC_BF(TCMR_STTDLY, START_DELAY)
> -			| SSC_BF(TCMR_START, SSC_START_FALLING_RF)
> -			| SSC_BF(TCMR_CKI, SSC_CKI_FALLING)
> -			| SSC_BF(TCMR_CKO, SSC_CKO_NONE)
> -			| SSC_BF(TCMR_CKS, ssc->clk_from_rk_pin ?
> -					   SSC_CKS_CLOCK : SSC_CKS_PIN);
> -
> -		tfmr =    SSC_BF(TFMR_FSLEN_EXT, fslen_ext)
> -			| SSC_BF(TFMR_FSEDGE, SSC_FSEDGE_NEGATIVE)
> -			| SSC_BF(TFMR_FSDEN, 0)
> -			| SSC_BF(TFMR_FSOS, SSC_FSOS_NEGATIVE)
> -			| SSC_BF(TFMR_FSLEN, fslen)
> -			| SSC_BF(TFMR_DATNB, (channels - 1))
> -			| SSC_BIT(TFMR_MSBF)
> -			| SSC_BF(TFMR_DATDEF, 0)
> -			| SSC_BF(TFMR_DATLEN, (bits - 1));
> -		break;
> -
> -	case SND_SOC_DAIFMT_DSP_A | SND_SOC_DAIFMT_CBS_CFS:
> -		/*
> -		 * DSP/PCM Mode A format, SSC provides BCLK and LRC clocks.
> -		 *
> -		 * The SSC transmit and receive clocks are generated from the
> -		 * MCK divider, and the BCLK signal is output
> -		 * on the SSC TK line.
> -		 */
> -		rcmr =	  SSC_BF(RCMR_PERIOD, rcmr_period)
> -			| SSC_BF(RCMR_STTDLY, 1)
> -			| SSC_BF(RCMR_START, SSC_START_RISING_RF)
> -			| SSC_BF(RCMR_CKI, SSC_CKI_RISING)
> -			| SSC_BF(RCMR_CKO, SSC_CKO_NONE)
> -			| SSC_BF(RCMR_CKS, SSC_CKS_DIV);
> +	case SND_SOC_DAIFMT_I2S:
> +		fs_osync = SSC_FSOS_NEGATIVE;
>   
> -		rfmr =	  SSC_BF(RFMR_FSEDGE, SSC_FSEDGE_POSITIVE)
> -			| SSC_BF(RFMR_FSOS, SSC_FSOS_POSITIVE)
> -			| SSC_BF(RFMR_FSLEN, 0)
> -			| SSC_BF(RFMR_DATNB, (channels - 1))
> -			| SSC_BIT(RFMR_MSBF)
> -			| SSC_BF(RFMR_LOOP, 0)
> -			| SSC_BF(RFMR_DATLEN, (bits - 1));
> +		rcmr =	  SSC_BF(RCMR_STTDLY, 1)
> +			| SSC_BF(RCMR_START, SSC_START_FALLING_RF);
>   
> -		tcmr =	  SSC_BF(TCMR_PERIOD, tcmr_period)
> -			| SSC_BF(TCMR_STTDLY, 1)
> -			| SSC_BF(TCMR_START, SSC_START_RISING_RF)
> -			| SSC_BF(TCMR_CKI, SSC_CKI_FALLING)
> -			| SSC_BF(TCMR_CKO, SSC_CKO_CONTINUOUS)
> -			| SSC_BF(TCMR_CKS, SSC_CKS_DIV);
> +		tcmr =	  SSC_BF(TCMR_STTDLY, 1)
> +			| SSC_BF(TCMR_START, SSC_START_FALLING_RF);
>   
> -		tfmr =	  SSC_BF(TFMR_FSEDGE, SSC_FSEDGE_POSITIVE)
> -			| SSC_BF(TFMR_FSDEN, 0)
> -			| SSC_BF(TFMR_FSOS, SSC_FSOS_POSITIVE)
> -			| SSC_BF(TFMR_FSLEN, 0)
> -			| SSC_BF(TFMR_DATNB, (channels - 1))
> -			| SSC_BIT(TFMR_MSBF)
> -			| SSC_BF(TFMR_DATDEF, 0)
> -			| SSC_BF(TFMR_DATLEN, (bits - 1));
>   		break;
>   
> -	case SND_SOC_DAIFMT_DSP_A | SND_SOC_DAIFMT_CBM_CFM:
> +	case SND_SOC_DAIFMT_DSP_A:
>   		/*
> -		 * DSP/PCM Mode A format, CODEC supplies BCLK and LRC clocks.
> +		 * DSP/PCM Mode A format
>   		 *
>   		 * Data is transferred on first BCLK after LRC pulse rising
>   		 * edge.If stereo, the right channel data is contiguous with
>   		 * the left channel data.
>   		 */
> -		rcmr =	  SSC_BF(RCMR_PERIOD, 0)
> -			| SSC_BF(RCMR_STTDLY, START_DELAY)
> -			| SSC_BF(RCMR_START, SSC_START_RISING_RF)
> -			| SSC_BF(RCMR_CKI, SSC_CKI_RISING)
> -			| SSC_BF(RCMR_CKO, SSC_CKO_NONE)
> -			| SSC_BF(RCMR_CKS, ssc->clk_from_rk_pin ?
> -					   SSC_CKS_PIN : SSC_CKS_CLOCK);
> +		fs_osync = SSC_FSOS_POSITIVE;
> +		fslen = fslen_ext = 0;
>   
> -		rfmr =	  SSC_BF(RFMR_FSEDGE, SSC_FSEDGE_POSITIVE)
> -			| SSC_BF(RFMR_FSOS, SSC_FSOS_NONE)
> -			| SSC_BF(RFMR_FSLEN, 0)
> -			| SSC_BF(RFMR_DATNB, (channels - 1))
> -			| SSC_BIT(RFMR_MSBF)
> -			| SSC_BF(RFMR_LOOP, 0)
> -			| SSC_BF(RFMR_DATLEN, (bits - 1));
> +		rcmr =	  SSC_BF(RCMR_STTDLY, 1)
> +			| SSC_BF(RCMR_START, SSC_START_RISING_RF);
>   
> -		tcmr =	  SSC_BF(TCMR_PERIOD, 0)
> -			| SSC_BF(TCMR_STTDLY, START_DELAY)
> -			| SSC_BF(TCMR_START, SSC_START_RISING_RF)
> -			| SSC_BF(TCMR_CKI, SSC_CKI_FALLING)
> -			| SSC_BF(TCMR_CKO, SSC_CKO_NONE)
> -			| SSC_BF(RCMR_CKS, ssc->clk_from_rk_pin ?
> -					   SSC_CKS_CLOCK : SSC_CKS_PIN);
> +		tcmr =	  SSC_BF(TCMR_STTDLY, 1)
> +			| SSC_BF(TCMR_START, SSC_START_RISING_RF);
>   
> -		tfmr =	  SSC_BF(TFMR_FSEDGE, SSC_FSEDGE_POSITIVE)
> -			| SSC_BF(TFMR_FSDEN, 0)
> -			| SSC_BF(TFMR_FSOS, SSC_FSOS_NONE)
> -			| SSC_BF(TFMR_FSLEN, 0)
> -			| SSC_BF(TFMR_DATNB, (channels - 1))
> -			| SSC_BIT(TFMR_MSBF)
> -			| SSC_BF(TFMR_DATDEF, 0)
> -			| SSC_BF(TFMR_DATLEN, (bits - 1));
>   		break;
>   
>   	default:
> @@ -785,6 +599,67 @@ static int atmel_ssc_hw_params(struct snd_pcm_substream *substream,
>   			ssc_p->daifmt);
>   		return -EINVAL;
>   	}
> +
> +	if (!atmel_ssc_cfs(ssc_p)) {
> +		fslen = fslen_ext = 0;
> +		rcmr_period = tcmr_period = 0;
> +		fs_osync = SSC_FSOS_NONE;
> +	}
> +
> +	if (atmel_ssc_cbs(ssc_p)) {
> +		/*
> +		 * SSC provides BCLK
> +		 *
> +		 * The SSC transmit and receive clocks are generated from the
> +		 * MCK divider, and the BCLK signal is output
> +		 * on the SSC TK line.
> +		 */
> +		rcmr |=	  SSC_BF(RCMR_CKS, SSC_CKS_DIV)
> +			| SSC_BF(RCMR_CKO, SSC_CKO_NONE);
> +
> +		tcmr |=	  SSC_BF(TCMR_CKS, SSC_CKS_DIV)
> +			| SSC_BF(TCMR_CKO, SSC_CKO_CONTINUOUS);
> +	} else {
> +		rcmr |=	  SSC_BF(RCMR_CKS, ssc->clk_from_rk_pin ?
> +					SSC_CKS_PIN : SSC_CKS_CLOCK)
> +			| SSC_BF(RCMR_CKO, SSC_CKO_NONE);
> +
> +		tcmr |=	  SSC_BF(TCMR_CKS, ssc->clk_from_rk_pin ?
> +					SSC_CKS_CLOCK : SSC_CKS_PIN)
> +			| SSC_BF(TCMR_CKO, SSC_CKO_NONE);
> +	}
> +
> +	rcmr |=	  SSC_BF(RCMR_PERIOD, rcmr_period)
> +		| SSC_BF(RCMR_CKI, SSC_CKI_RISING);

You can also add here SSC_BF(RCMR_CKO, SSC_CKO_NONE) and remove it from 
the if-else above;

> +
> +	tcmr |=   SSC_BF(TCMR_PERIOD, tcmr_period)
> +		| SSC_BF(TCMR_CKI, SSC_CKI_FALLING);
> +
> +	rfmr =    SSC_BF(RFMR_FSLEN_EXT, fslen_ext)
> +		| SSC_BF(RFMR_FSEDGE, SSC_FSEDGE_POSITIVE)
> +		| SSC_BF(RFMR_FSOS, fs_osync)
> +		| SSC_BF(RFMR_FSLEN, fslen)
> +		| SSC_BF(RFMR_DATNB, (channels - 1))
> +		| SSC_BIT(RFMR_MSBF)
> +		| SSC_BF(RFMR_LOOP, 0)
> +		| SSC_BF(RFMR_DATLEN, (bits - 1));
> +
> +	tfmr =    SSC_BF(TFMR_FSLEN_EXT, fslen_ext)
> +		| SSC_BF(TFMR_FSEDGE, SSC_FSEDGE_POSITIVE)
> +		| SSC_BF(TFMR_FSDEN, 0)
> +		| SSC_BF(TFMR_FSOS, fs_osync)
> +		| SSC_BF(TFMR_FSLEN, fslen)
> +		| SSC_BF(TFMR_DATNB, (channels - 1))
> +		| SSC_BIT(TFMR_MSBF)
> +		| SSC_BF(TFMR_DATDEF, 0)
> +		| SSC_BF(TFMR_DATLEN, (bits - 1));
> +
> +	if (fslen_ext && !ssc->pdata->has_fslen_ext) {
> +		dev_err(dai->dev, "sample size %d is too large for SSC device\n",
> +			bits);
> +		return -EINVAL;
> +	}
> +
>   	pr_debug("atmel_ssc_hw_params: "
>   			"RCMR=%08x RFMR=%08x TCMR=%08x TFMR=%08x\n",
>   			rcmr, rfmr, tcmr, tfmr);
> 

You are adding support for SND_SOC_DAIFMT_DSP_A | 
SND_SOC_DAIFMT_CBM_CFS. If this is intended, please make a separate 
patch. If not, then:

printk(KERN_WARNING "atmel_ssc_dai: unsupported DAI format 0x%x\n",
			ssc_p->daifmt);
return -EINVAL;

The rest of the patch looks great.

Thanks and best regards,
Codrin
Michał Mirosław July 24, 2019, 11:15 a.m. UTC | #2
On Wed, Jul 24, 2019 at 10:35:29AM +0000, Codrin.Ciubotariu@microchip.com wrote:
> On 22.07.2019 21:27, Michał Mirosław wrote:
> > Rework DAI format calculation in preparation for adding more formats
> > later.
> > 
> > Note: this changes FSEDGE to POSITIVE for I2S CBM_CFS mode as the TXSYN
> > interrupt is not used anyway.
> > 
> > Signed-off-by: Michał Mirosław <mirq-linux@rere.qmqm.pl>
> > ---
> >   sound/soc/atmel/atmel_ssc_dai.c | 283 +++++++++-----------------------
> >   1 file changed, 79 insertions(+), 204 deletions(-)
> > 
> > diff --git a/sound/soc/atmel/atmel_ssc_dai.c b/sound/soc/atmel/atmel_ssc_dai.c
> > index 6f89483ac88c..b2992496e52f 100644
> > --- a/sound/soc/atmel/atmel_ssc_dai.c
> > +++ b/sound/soc/atmel/atmel_ssc_dai.c
[...]
> > +	if (atmel_ssc_cbs(ssc_p)) {
> > +		/*
> > +		 * SSC provides BCLK
> > +		 *
> > +		 * The SSC transmit and receive clocks are generated from the
> > +		 * MCK divider, and the BCLK signal is output
> > +		 * on the SSC TK line.
> > +		 */
> > +		rcmr |=	  SSC_BF(RCMR_CKS, SSC_CKS_DIV)
> > +			| SSC_BF(RCMR_CKO, SSC_CKO_NONE);
> > +
> > +		tcmr |=	  SSC_BF(TCMR_CKS, SSC_CKS_DIV)
> > +			| SSC_BF(TCMR_CKO, SSC_CKO_CONTINUOUS);
> > +	} else {
> > +		rcmr |=	  SSC_BF(RCMR_CKS, ssc->clk_from_rk_pin ?
> > +					SSC_CKS_PIN : SSC_CKS_CLOCK)
> > +			| SSC_BF(RCMR_CKO, SSC_CKO_NONE);
> > +
> > +		tcmr |=	  SSC_BF(TCMR_CKS, ssc->clk_from_rk_pin ?
> > +					SSC_CKS_CLOCK : SSC_CKS_PIN)
> > +			| SSC_BF(TCMR_CKO, SSC_CKO_NONE);
> > +	}
> > +
> > +	rcmr |=	  SSC_BF(RCMR_PERIOD, rcmr_period)
> > +		| SSC_BF(RCMR_CKI, SSC_CKI_RISING);
> 
> You can also add here SSC_BF(RCMR_CKO, SSC_CKO_NONE) and remove it from 
> the if-else above;

I left it to keep symmetry between TX and RX code. I can pull it here if
you prefer that way.

> > +
> > +	tcmr |=   SSC_BF(TCMR_PERIOD, tcmr_period)
> > +		| SSC_BF(TCMR_CKI, SSC_CKI_FALLING);
> > +
> > +	rfmr =    SSC_BF(RFMR_FSLEN_EXT, fslen_ext)
> > +		| SSC_BF(RFMR_FSEDGE, SSC_FSEDGE_POSITIVE)
> > +		| SSC_BF(RFMR_FSOS, fs_osync)
> > +		| SSC_BF(RFMR_FSLEN, fslen)
> > +		| SSC_BF(RFMR_DATNB, (channels - 1))
> > +		| SSC_BIT(RFMR_MSBF)
> > +		| SSC_BF(RFMR_LOOP, 0)
> > +		| SSC_BF(RFMR_DATLEN, (bits - 1));
> > +
> > +	tfmr =    SSC_BF(TFMR_FSLEN_EXT, fslen_ext)
> > +		| SSC_BF(TFMR_FSEDGE, SSC_FSEDGE_POSITIVE)
> > +		| SSC_BF(TFMR_FSDEN, 0)
> > +		| SSC_BF(TFMR_FSOS, fs_osync)
> > +		| SSC_BF(TFMR_FSLEN, fslen)
> > +		| SSC_BF(TFMR_DATNB, (channels - 1))
> > +		| SSC_BIT(TFMR_MSBF)
> > +		| SSC_BF(TFMR_DATDEF, 0)
> > +		| SSC_BF(TFMR_DATLEN, (bits - 1));
> > +
> > +	if (fslen_ext && !ssc->pdata->has_fslen_ext) {
> > +		dev_err(dai->dev, "sample size %d is too large for SSC device\n",
> > +			bits);
> > +		return -EINVAL;
> > +	}
> > +
> >   	pr_debug("atmel_ssc_hw_params: "
> >   			"RCMR=%08x RFMR=%08x TCMR=%08x TFMR=%08x\n",
> >   			rcmr, rfmr, tcmr, tfmr);
> > 
> 
> You are adding support for SND_SOC_DAIFMT_DSP_A | 
> SND_SOC_DAIFMT_CBM_CFS. If this is intended, please make a separate 
> patch. If not, then:
> 
> printk(KERN_WARNING "atmel_ssc_dai: unsupported DAI format 0x%x\n",
> 			ssc_p->daifmt);
> return -EINVAL;

Hmm. I guess this is actually a good side effect. I can't see a way to
contain this change that doesn't involve adding code that's immediately
removed in next patch. So would you agree to just mentioning this in
commit message?

Best Regards,
Michał Mirosław
Codrin Ciubotariu July 24, 2019, 12:54 p.m. UTC | #3
On 24.07.2019 14:15, mirq-linux@rere.qmqm.pl wrote:
> External E-Mail
> 
> 
> On Wed, Jul 24, 2019 at 10:35:29AM +0000, Codrin.Ciubotariu@microchip.com wrote:
>> On 22.07.2019 21:27, Michał Mirosław wrote:
>>> Rework DAI format calculation in preparation for adding more formats
>>> later.
>>>
>>> Note: this changes FSEDGE to POSITIVE for I2S CBM_CFS mode as the TXSYN
>>> interrupt is not used anyway.
>>>
>>> Signed-off-by: Michał Mirosław <mirq-linux@rere.qmqm.pl>
>>> ---
>>>    sound/soc/atmel/atmel_ssc_dai.c | 283 +++++++++-----------------------
>>>    1 file changed, 79 insertions(+), 204 deletions(-)
>>>
>>> diff --git a/sound/soc/atmel/atmel_ssc_dai.c b/sound/soc/atmel/atmel_ssc_dai.c
>>> index 6f89483ac88c..b2992496e52f 100644
>>> --- a/sound/soc/atmel/atmel_ssc_dai.c
>>> +++ b/sound/soc/atmel/atmel_ssc_dai.c
> [...]
>>> +	if (atmel_ssc_cbs(ssc_p)) {
>>> +		/*
>>> +		 * SSC provides BCLK
>>> +		 *
>>> +		 * The SSC transmit and receive clocks are generated from the
>>> +		 * MCK divider, and the BCLK signal is output
>>> +		 * on the SSC TK line.
>>> +		 */
>>> +		rcmr |=	  SSC_BF(RCMR_CKS, SSC_CKS_DIV)
>>> +			| SSC_BF(RCMR_CKO, SSC_CKO_NONE);
>>> +
>>> +		tcmr |=	  SSC_BF(TCMR_CKS, SSC_CKS_DIV)
>>> +			| SSC_BF(TCMR_CKO, SSC_CKO_CONTINUOUS);
>>> +	} else {
>>> +		rcmr |=	  SSC_BF(RCMR_CKS, ssc->clk_from_rk_pin ?
>>> +					SSC_CKS_PIN : SSC_CKS_CLOCK)
>>> +			| SSC_BF(RCMR_CKO, SSC_CKO_NONE);
>>> +
>>> +		tcmr |=	  SSC_BF(TCMR_CKS, ssc->clk_from_rk_pin ?
>>> +					SSC_CKS_CLOCK : SSC_CKS_PIN)
>>> +			| SSC_BF(TCMR_CKO, SSC_CKO_NONE);
>>> +	}
>>> +
>>> +	rcmr |=	  SSC_BF(RCMR_PERIOD, rcmr_period)
>>> +		| SSC_BF(RCMR_CKI, SSC_CKI_RISING);
>>
>> You can also add here SSC_BF(RCMR_CKO, SSC_CKO_NONE) and remove it from
>> the if-else above;
> 
> I left it to keep symmetry between TX and RX code. I can pull it here if
> you prefer that way.

Right, you can leave it then.

> 
>>> +
>>> +	tcmr |=   SSC_BF(TCMR_PERIOD, tcmr_period)
>>> +		| SSC_BF(TCMR_CKI, SSC_CKI_FALLING);
>>> +
>>> +	rfmr =    SSC_BF(RFMR_FSLEN_EXT, fslen_ext)
>>> +		| SSC_BF(RFMR_FSEDGE, SSC_FSEDGE_POSITIVE)
>>> +		| SSC_BF(RFMR_FSOS, fs_osync)
>>> +		| SSC_BF(RFMR_FSLEN, fslen)
>>> +		| SSC_BF(RFMR_DATNB, (channels - 1))
>>> +		| SSC_BIT(RFMR_MSBF)
>>> +		| SSC_BF(RFMR_LOOP, 0)
>>> +		| SSC_BF(RFMR_DATLEN, (bits - 1));
>>> +
>>> +	tfmr =    SSC_BF(TFMR_FSLEN_EXT, fslen_ext)
>>> +		| SSC_BF(TFMR_FSEDGE, SSC_FSEDGE_POSITIVE)
>>> +		| SSC_BF(TFMR_FSDEN, 0)
>>> +		| SSC_BF(TFMR_FSOS, fs_osync)
>>> +		| SSC_BF(TFMR_FSLEN, fslen)
>>> +		| SSC_BF(TFMR_DATNB, (channels - 1))
>>> +		| SSC_BIT(TFMR_MSBF)
>>> +		| SSC_BF(TFMR_DATDEF, 0)
>>> +		| SSC_BF(TFMR_DATLEN, (bits - 1));
>>> +
>>> +	if (fslen_ext && !ssc->pdata->has_fslen_ext) {
>>> +		dev_err(dai->dev, "sample size %d is too large for SSC device\n",
>>> +			bits);
>>> +		return -EINVAL;
>>> +	}
>>> +
>>>    	pr_debug("atmel_ssc_hw_params: "
>>>    			"RCMR=%08x RFMR=%08x TCMR=%08x TFMR=%08x\n",
>>>    			rcmr, rfmr, tcmr, tfmr);
>>>
>>
>> You are adding support for SND_SOC_DAIFMT_DSP_A |
>> SND_SOC_DAIFMT_CBM_CFS. If this is intended, please make a separate
>> patch. If not, then:
>>
>> printk(KERN_WARNING "atmel_ssc_dai: unsupported DAI format 0x%x\n",
>> 			ssc_p->daifmt);
>> return -EINVAL;
> 
> Hmm. I guess this is actually a good side effect. I can't see a way to
> contain this change that doesn't involve adding code that's immediately
> removed in next patch. So would you agree to just mentioning this in
> commit message?

I prefer a separate patch, for clarity mostly, but I don't have a strong 
opinion on this. Later, it might prove trickier to investigate a bug 
this case and review this patch. Also, we should test and see that this 
format works indeed...

Best regards,
Codrin
diff mbox series

Patch

diff --git a/sound/soc/atmel/atmel_ssc_dai.c b/sound/soc/atmel/atmel_ssc_dai.c
index 6f89483ac88c..b2992496e52f 100644
--- a/sound/soc/atmel/atmel_ssc_dai.c
+++ b/sound/soc/atmel/atmel_ssc_dai.c
@@ -471,7 +471,7 @@  static int atmel_ssc_hw_params(struct snd_pcm_substream *substream,
 	int dir, channels, bits;
 	u32 tfmr, rfmr, tcmr, rcmr;
 	int ret;
-	int fslen, fslen_ext;
+	int fslen, fslen_ext, fs_osync;
 	u32 cmr_div;
 	u32 tcmr_period;
 	u32 rcmr_period;
@@ -558,226 +558,40 @@  static int atmel_ssc_hw_params(struct snd_pcm_substream *substream,
 	/*
 	 * Compute SSC register settings.
 	 */
-	switch (ssc_p->daifmt
-		& (SND_SOC_DAIFMT_FORMAT_MASK | SND_SOC_DAIFMT_MASTER_MASK)) {
 
-	case SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_CBS_CFS:
-		/*
-		 * I2S format, SSC provides BCLK and LRC clocks.
-		 *
-		 * The SSC transmit and receive clocks are generated
-		 * from the MCK divider, and the BCLK signal
-		 * is output on the SSC TK line.
-		 */
-
-		if (bits > 16 && !ssc->pdata->has_fslen_ext) {
-			dev_err(dai->dev,
-				"sample size %d is too large for SSC device\n",
-				bits);
-			return -EINVAL;
-		}
-
-		fslen_ext = (bits - 1) / 16;
-		fslen = (bits - 1) % 16;
-
-		rcmr =	  SSC_BF(RCMR_PERIOD, rcmr_period)
-			| SSC_BF(RCMR_STTDLY, START_DELAY)
-			| SSC_BF(RCMR_START, SSC_START_FALLING_RF)
-			| SSC_BF(RCMR_CKI, SSC_CKI_RISING)
-			| SSC_BF(RCMR_CKO, SSC_CKO_NONE)
-			| SSC_BF(RCMR_CKS, SSC_CKS_DIV);
-
-		rfmr =    SSC_BF(RFMR_FSLEN_EXT, fslen_ext)
-			| SSC_BF(RFMR_FSEDGE, SSC_FSEDGE_POSITIVE)
-			| SSC_BF(RFMR_FSOS, SSC_FSOS_NEGATIVE)
-			| SSC_BF(RFMR_FSLEN, fslen)
-			| SSC_BF(RFMR_DATNB, (channels - 1))
-			| SSC_BIT(RFMR_MSBF)
-			| SSC_BF(RFMR_LOOP, 0)
-			| SSC_BF(RFMR_DATLEN, (bits - 1));
-
-		tcmr =	  SSC_BF(TCMR_PERIOD, tcmr_period)
-			| SSC_BF(TCMR_STTDLY, START_DELAY)
-			| SSC_BF(TCMR_START, SSC_START_FALLING_RF)
-			| SSC_BF(TCMR_CKI, SSC_CKI_FALLING)
-			| SSC_BF(TCMR_CKO, SSC_CKO_CONTINUOUS)
-			| SSC_BF(TCMR_CKS, SSC_CKS_DIV);
-
-		tfmr =    SSC_BF(TFMR_FSLEN_EXT, fslen_ext)
-			| SSC_BF(TFMR_FSEDGE, SSC_FSEDGE_POSITIVE)
-			| SSC_BF(TFMR_FSDEN, 0)
-			| SSC_BF(TFMR_FSOS, SSC_FSOS_NEGATIVE)
-			| SSC_BF(TFMR_FSLEN, fslen)
-			| SSC_BF(TFMR_DATNB, (channels - 1))
-			| SSC_BIT(TFMR_MSBF)
-			| SSC_BF(TFMR_DATDEF, 0)
-			| SSC_BF(TFMR_DATLEN, (bits - 1));
-		break;
-
-	case SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_CBM_CFM:
-		/* I2S format, CODEC supplies BCLK and LRC clocks. */
-		rcmr =	  SSC_BF(RCMR_PERIOD, 0)
-			| SSC_BF(RCMR_STTDLY, START_DELAY)
-			| SSC_BF(RCMR_START, SSC_START_FALLING_RF)
-			| SSC_BF(RCMR_CKI, SSC_CKI_RISING)
-			| SSC_BF(RCMR_CKO, SSC_CKO_NONE)
-			| SSC_BF(RCMR_CKS, ssc->clk_from_rk_pin ?
-					   SSC_CKS_PIN : SSC_CKS_CLOCK);
-
-		rfmr =	  SSC_BF(RFMR_FSEDGE, SSC_FSEDGE_POSITIVE)
-			| SSC_BF(RFMR_FSOS, SSC_FSOS_NONE)
-			| SSC_BF(RFMR_FSLEN, 0)
-			| SSC_BF(RFMR_DATNB, (channels - 1))
-			| SSC_BIT(RFMR_MSBF)
-			| SSC_BF(RFMR_LOOP, 0)
-			| SSC_BF(RFMR_DATLEN, (bits - 1));
+	fslen_ext = (bits - 1) / 16;
+	fslen = (bits - 1) % 16;
 
-		tcmr =	  SSC_BF(TCMR_PERIOD, 0)
-			| SSC_BF(TCMR_STTDLY, START_DELAY)
-			| SSC_BF(TCMR_START, SSC_START_FALLING_RF)
-			| SSC_BF(TCMR_CKI, SSC_CKI_FALLING)
-			| SSC_BF(TCMR_CKO, SSC_CKO_NONE)
-			| SSC_BF(TCMR_CKS, ssc->clk_from_rk_pin ?
-					   SSC_CKS_CLOCK : SSC_CKS_PIN);
+	switch (ssc_p->daifmt & SND_SOC_DAIFMT_FORMAT_MASK) {
 
-		tfmr =	  SSC_BF(TFMR_FSEDGE, SSC_FSEDGE_POSITIVE)
-			| SSC_BF(TFMR_FSDEN, 0)
-			| SSC_BF(TFMR_FSOS, SSC_FSOS_NONE)
-			| SSC_BF(TFMR_FSLEN, 0)
-			| SSC_BF(TFMR_DATNB, (channels - 1))
-			| SSC_BIT(TFMR_MSBF)
-			| SSC_BF(TFMR_DATDEF, 0)
-			| SSC_BF(TFMR_DATLEN, (bits - 1));
-		break;
-
-	case SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_CBM_CFS:
-		/* I2S format, CODEC supplies BCLK, SSC supplies LRCLK. */
-		if (bits > 16 && !ssc->pdata->has_fslen_ext) {
-			dev_err(dai->dev,
-				"sample size %d is too large for SSC device\n",
-				bits);
-			return -EINVAL;
-		}
-
-		fslen_ext = (bits - 1) / 16;
-		fslen = (bits - 1) % 16;
-
-		rcmr =	  SSC_BF(RCMR_PERIOD, rcmr_period)
-			| SSC_BF(RCMR_STTDLY, START_DELAY)
-			| SSC_BF(RCMR_START, SSC_START_FALLING_RF)
-			| SSC_BF(RCMR_CKI, SSC_CKI_RISING)
-			| SSC_BF(RCMR_CKO, SSC_CKO_NONE)
-			| SSC_BF(RCMR_CKS, ssc->clk_from_rk_pin ?
-					   SSC_CKS_PIN : SSC_CKS_CLOCK);
-
-		rfmr =    SSC_BF(RFMR_FSLEN_EXT, fslen_ext)
-			| SSC_BF(RFMR_FSEDGE, SSC_FSEDGE_POSITIVE)
-			| SSC_BF(RFMR_FSOS, SSC_FSOS_NEGATIVE)
-			| SSC_BF(RFMR_FSLEN, fslen)
-			| SSC_BF(RFMR_DATNB, (channels - 1))
-			| SSC_BIT(RFMR_MSBF)
-			| SSC_BF(RFMR_LOOP, 0)
-			| SSC_BF(RFMR_DATLEN, (bits - 1));
-
-		tcmr =	  SSC_BF(TCMR_PERIOD, tcmr_period)
-			| SSC_BF(TCMR_STTDLY, START_DELAY)
-			| SSC_BF(TCMR_START, SSC_START_FALLING_RF)
-			| SSC_BF(TCMR_CKI, SSC_CKI_FALLING)
-			| SSC_BF(TCMR_CKO, SSC_CKO_NONE)
-			| SSC_BF(TCMR_CKS, ssc->clk_from_rk_pin ?
-					   SSC_CKS_CLOCK : SSC_CKS_PIN);
-
-		tfmr =    SSC_BF(TFMR_FSLEN_EXT, fslen_ext)
-			| SSC_BF(TFMR_FSEDGE, SSC_FSEDGE_NEGATIVE)
-			| SSC_BF(TFMR_FSDEN, 0)
-			| SSC_BF(TFMR_FSOS, SSC_FSOS_NEGATIVE)
-			| SSC_BF(TFMR_FSLEN, fslen)
-			| SSC_BF(TFMR_DATNB, (channels - 1))
-			| SSC_BIT(TFMR_MSBF)
-			| SSC_BF(TFMR_DATDEF, 0)
-			| SSC_BF(TFMR_DATLEN, (bits - 1));
-		break;
-
-	case SND_SOC_DAIFMT_DSP_A | SND_SOC_DAIFMT_CBS_CFS:
-		/*
-		 * DSP/PCM Mode A format, SSC provides BCLK and LRC clocks.
-		 *
-		 * The SSC transmit and receive clocks are generated from the
-		 * MCK divider, and the BCLK signal is output
-		 * on the SSC TK line.
-		 */
-		rcmr =	  SSC_BF(RCMR_PERIOD, rcmr_period)
-			| SSC_BF(RCMR_STTDLY, 1)
-			| SSC_BF(RCMR_START, SSC_START_RISING_RF)
-			| SSC_BF(RCMR_CKI, SSC_CKI_RISING)
-			| SSC_BF(RCMR_CKO, SSC_CKO_NONE)
-			| SSC_BF(RCMR_CKS, SSC_CKS_DIV);
+	case SND_SOC_DAIFMT_I2S:
+		fs_osync = SSC_FSOS_NEGATIVE;
 
-		rfmr =	  SSC_BF(RFMR_FSEDGE, SSC_FSEDGE_POSITIVE)
-			| SSC_BF(RFMR_FSOS, SSC_FSOS_POSITIVE)
-			| SSC_BF(RFMR_FSLEN, 0)
-			| SSC_BF(RFMR_DATNB, (channels - 1))
-			| SSC_BIT(RFMR_MSBF)
-			| SSC_BF(RFMR_LOOP, 0)
-			| SSC_BF(RFMR_DATLEN, (bits - 1));
+		rcmr =	  SSC_BF(RCMR_STTDLY, 1)
+			| SSC_BF(RCMR_START, SSC_START_FALLING_RF);
 
-		tcmr =	  SSC_BF(TCMR_PERIOD, tcmr_period)
-			| SSC_BF(TCMR_STTDLY, 1)
-			| SSC_BF(TCMR_START, SSC_START_RISING_RF)
-			| SSC_BF(TCMR_CKI, SSC_CKI_FALLING)
-			| SSC_BF(TCMR_CKO, SSC_CKO_CONTINUOUS)
-			| SSC_BF(TCMR_CKS, SSC_CKS_DIV);
+		tcmr =	  SSC_BF(TCMR_STTDLY, 1)
+			| SSC_BF(TCMR_START, SSC_START_FALLING_RF);
 
-		tfmr =	  SSC_BF(TFMR_FSEDGE, SSC_FSEDGE_POSITIVE)
-			| SSC_BF(TFMR_FSDEN, 0)
-			| SSC_BF(TFMR_FSOS, SSC_FSOS_POSITIVE)
-			| SSC_BF(TFMR_FSLEN, 0)
-			| SSC_BF(TFMR_DATNB, (channels - 1))
-			| SSC_BIT(TFMR_MSBF)
-			| SSC_BF(TFMR_DATDEF, 0)
-			| SSC_BF(TFMR_DATLEN, (bits - 1));
 		break;
 
-	case SND_SOC_DAIFMT_DSP_A | SND_SOC_DAIFMT_CBM_CFM:
+	case SND_SOC_DAIFMT_DSP_A:
 		/*
-		 * DSP/PCM Mode A format, CODEC supplies BCLK and LRC clocks.
+		 * DSP/PCM Mode A format
 		 *
 		 * Data is transferred on first BCLK after LRC pulse rising
 		 * edge.If stereo, the right channel data is contiguous with
 		 * the left channel data.
 		 */
-		rcmr =	  SSC_BF(RCMR_PERIOD, 0)
-			| SSC_BF(RCMR_STTDLY, START_DELAY)
-			| SSC_BF(RCMR_START, SSC_START_RISING_RF)
-			| SSC_BF(RCMR_CKI, SSC_CKI_RISING)
-			| SSC_BF(RCMR_CKO, SSC_CKO_NONE)
-			| SSC_BF(RCMR_CKS, ssc->clk_from_rk_pin ?
-					   SSC_CKS_PIN : SSC_CKS_CLOCK);
+		fs_osync = SSC_FSOS_POSITIVE;
+		fslen = fslen_ext = 0;
 
-		rfmr =	  SSC_BF(RFMR_FSEDGE, SSC_FSEDGE_POSITIVE)
-			| SSC_BF(RFMR_FSOS, SSC_FSOS_NONE)
-			| SSC_BF(RFMR_FSLEN, 0)
-			| SSC_BF(RFMR_DATNB, (channels - 1))
-			| SSC_BIT(RFMR_MSBF)
-			| SSC_BF(RFMR_LOOP, 0)
-			| SSC_BF(RFMR_DATLEN, (bits - 1));
+		rcmr =	  SSC_BF(RCMR_STTDLY, 1)
+			| SSC_BF(RCMR_START, SSC_START_RISING_RF);
 
-		tcmr =	  SSC_BF(TCMR_PERIOD, 0)
-			| SSC_BF(TCMR_STTDLY, START_DELAY)
-			| SSC_BF(TCMR_START, SSC_START_RISING_RF)
-			| SSC_BF(TCMR_CKI, SSC_CKI_FALLING)
-			| SSC_BF(TCMR_CKO, SSC_CKO_NONE)
-			| SSC_BF(RCMR_CKS, ssc->clk_from_rk_pin ?
-					   SSC_CKS_CLOCK : SSC_CKS_PIN);
+		tcmr =	  SSC_BF(TCMR_STTDLY, 1)
+			| SSC_BF(TCMR_START, SSC_START_RISING_RF);
 
-		tfmr =	  SSC_BF(TFMR_FSEDGE, SSC_FSEDGE_POSITIVE)
-			| SSC_BF(TFMR_FSDEN, 0)
-			| SSC_BF(TFMR_FSOS, SSC_FSOS_NONE)
-			| SSC_BF(TFMR_FSLEN, 0)
-			| SSC_BF(TFMR_DATNB, (channels - 1))
-			| SSC_BIT(TFMR_MSBF)
-			| SSC_BF(TFMR_DATDEF, 0)
-			| SSC_BF(TFMR_DATLEN, (bits - 1));
 		break;
 
 	default:
@@ -785,6 +599,67 @@  static int atmel_ssc_hw_params(struct snd_pcm_substream *substream,
 			ssc_p->daifmt);
 		return -EINVAL;
 	}
+
+	if (!atmel_ssc_cfs(ssc_p)) {
+		fslen = fslen_ext = 0;
+		rcmr_period = tcmr_period = 0;
+		fs_osync = SSC_FSOS_NONE;
+	}
+
+	if (atmel_ssc_cbs(ssc_p)) {
+		/*
+		 * SSC provides BCLK
+		 *
+		 * The SSC transmit and receive clocks are generated from the
+		 * MCK divider, and the BCLK signal is output
+		 * on the SSC TK line.
+		 */
+		rcmr |=	  SSC_BF(RCMR_CKS, SSC_CKS_DIV)
+			| SSC_BF(RCMR_CKO, SSC_CKO_NONE);
+
+		tcmr |=	  SSC_BF(TCMR_CKS, SSC_CKS_DIV)
+			| SSC_BF(TCMR_CKO, SSC_CKO_CONTINUOUS);
+	} else {
+		rcmr |=	  SSC_BF(RCMR_CKS, ssc->clk_from_rk_pin ?
+					SSC_CKS_PIN : SSC_CKS_CLOCK)
+			| SSC_BF(RCMR_CKO, SSC_CKO_NONE);
+
+		tcmr |=	  SSC_BF(TCMR_CKS, ssc->clk_from_rk_pin ?
+					SSC_CKS_CLOCK : SSC_CKS_PIN)
+			| SSC_BF(TCMR_CKO, SSC_CKO_NONE);
+	}
+
+	rcmr |=	  SSC_BF(RCMR_PERIOD, rcmr_period)
+		| SSC_BF(RCMR_CKI, SSC_CKI_RISING);
+
+	tcmr |=   SSC_BF(TCMR_PERIOD, tcmr_period)
+		| SSC_BF(TCMR_CKI, SSC_CKI_FALLING);
+
+	rfmr =    SSC_BF(RFMR_FSLEN_EXT, fslen_ext)
+		| SSC_BF(RFMR_FSEDGE, SSC_FSEDGE_POSITIVE)
+		| SSC_BF(RFMR_FSOS, fs_osync)
+		| SSC_BF(RFMR_FSLEN, fslen)
+		| SSC_BF(RFMR_DATNB, (channels - 1))
+		| SSC_BIT(RFMR_MSBF)
+		| SSC_BF(RFMR_LOOP, 0)
+		| SSC_BF(RFMR_DATLEN, (bits - 1));
+
+	tfmr =    SSC_BF(TFMR_FSLEN_EXT, fslen_ext)
+		| SSC_BF(TFMR_FSEDGE, SSC_FSEDGE_POSITIVE)
+		| SSC_BF(TFMR_FSDEN, 0)
+		| SSC_BF(TFMR_FSOS, fs_osync)
+		| SSC_BF(TFMR_FSLEN, fslen)
+		| SSC_BF(TFMR_DATNB, (channels - 1))
+		| SSC_BIT(TFMR_MSBF)
+		| SSC_BF(TFMR_DATDEF, 0)
+		| SSC_BF(TFMR_DATLEN, (bits - 1));
+
+	if (fslen_ext && !ssc->pdata->has_fslen_ext) {
+		dev_err(dai->dev, "sample size %d is too large for SSC device\n",
+			bits);
+		return -EINVAL;
+	}
+
 	pr_debug("atmel_ssc_hw_params: "
 			"RCMR=%08x RFMR=%08x TCMR=%08x TFMR=%08x\n",
 			rcmr, rfmr, tcmr, tfmr);