ASoC: atmel_ssc_dai: enable fslen extension feature
authorBo Shen <voice.shen@atmel.com>
Wed, 11 Jun 2014 10:14:40 +0000 (18:14 +0800)
committerMark Brown <broonie@linaro.org>
Sat, 21 Jun 2014 10:37:35 +0000 (11:37 +0100)
When SSC work as master, it will generate the frame sync signal.
On old SoCs, it only supports frame sync length less or equal to
16bits, on newer SoCs, it supports frame sync length extension,
which can support frame size larger than 16 bits.
So, add this to make it supports playback 24/32 bits audio clips.

Signed-off-by: Bo Shen <voice.shen@atmel.com>
Acked-by: Nicolas Ferre <nicolas.ferre@atmel.com>
Signed-off-by: Mark Brown <broonie@linaro.org>
include/linux/atmel-ssc.h
sound/soc/atmel/atmel_ssc_dai.c

index e8dd40873d5533ac7fa9199188f3309087e0c100..7c0f6549898bd29cacebaadd08596f6f4d2ab778 100644 (file)
@@ -72,6 +72,12 @@ void ssc_free(struct ssc_device *ssc);
 #define SSC_RFMR_DATNB_OFFSET                   8
 #define SSC_RFMR_FSEDGE_SIZE                    1
 #define SSC_RFMR_FSEDGE_OFFSET                 24
+/*
+ * The FSLEN_EXT exist on at91sam9rl, at91sam9g10,
+ * at91sam9g20, and at91sam9g45 and newer SoCs
+ */
+#define SSC_RFMR_FSLEN_EXT_SIZE                         4
+#define SSC_RFMR_FSLEN_EXT_OFFSET              28
 #define SSC_RFMR_FSLEN_SIZE                     4
 #define SSC_RFMR_FSLEN_OFFSET                  16
 #define SSC_RFMR_FSOS_SIZE                      4
@@ -110,6 +116,12 @@ void ssc_free(struct ssc_device *ssc);
 #define SSC_TFMR_FSDEN_OFFSET                  23
 #define SSC_TFMR_FSEDGE_SIZE                    1
 #define SSC_TFMR_FSEDGE_OFFSET                 24
+/*
+ * The FSLEN_EXT exist on at91sam9rl, at91sam9g10,
+ * at91sam9g20, and at91sam9g45 and newer SoCs
+ */
+#define SSC_TFMR_FSLEN_EXT_SIZE                         4
+#define SSC_TFMR_FSLEN_EXT_OFFSET              28
 #define SSC_TFMR_FSLEN_SIZE                     4
 #define SSC_TFMR_FSLEN_OFFSET                  16
 #define SSC_TFMR_FSOS_SIZE                      3
index de433cfd044ce2c2ce6599766e4d7cedef5798b3..f403f399808afc1431b6ad8a52a377329536707f 100644 (file)
@@ -347,6 +347,7 @@ static int atmel_ssc_hw_params(struct snd_pcm_substream *substream,
        u32 tfmr, rfmr, tcmr, rcmr;
        int start_event;
        int ret;
+       int fslen, fslen_ext;
 
        /*
         * Currently, there is only one set of dma params for
@@ -387,18 +388,6 @@ static int atmel_ssc_hw_params(struct snd_pcm_substream *substream,
                return -EINVAL;
        }
 
-       /*
-        * The SSC only supports up to 16-bit samples in I2S format, due
-        * to the size of the Frame Mode Register FSLEN field.
-        */
-       if ((ssc_p->daifmt & SND_SOC_DAIFMT_FORMAT_MASK) == SND_SOC_DAIFMT_I2S
-               && bits > 16) {
-               printk(KERN_WARNING
-                               "atmel_ssc_dai: sample size %d "
-                               "is too large for I2S\n", bits);
-               return -EINVAL;
-       }
-
        /*
         * Compute SSC register settings.
         */
@@ -413,6 +402,17 @@ static int atmel_ssc_hw_params(struct snd_pcm_substream *substream,
                 * 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, ssc_p->rcmr_period)
                        | SSC_BF(RCMR_STTDLY, START_DELAY)
                        | SSC_BF(RCMR_START, SSC_START_FALLING_RF)
@@ -420,9 +420,10 @@ static int atmel_ssc_hw_params(struct snd_pcm_substream *substream,
                        | SSC_BF(RCMR_CKO, SSC_CKO_NONE)
                        | SSC_BF(RCMR_CKS, SSC_CKS_DIV);
 
-               rfmr =    SSC_BF(RFMR_FSEDGE, SSC_FSEDGE_POSITIVE)
+               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, (bits - 1))
+                       | SSC_BF(RFMR_FSLEN, fslen)
                        | SSC_BF(RFMR_DATNB, (channels - 1))
                        | SSC_BIT(RFMR_MSBF)
                        | SSC_BF(RFMR_LOOP, 0)
@@ -435,10 +436,11 @@ static int atmel_ssc_hw_params(struct snd_pcm_substream *substream,
                        | SSC_BF(TCMR_CKO, SSC_CKO_CONTINUOUS)
                        | SSC_BF(TCMR_CKS, SSC_CKS_DIV);
 
-               tfmr =    SSC_BF(TFMR_FSEDGE, SSC_FSEDGE_POSITIVE)
+               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, (bits - 1))
+                       | SSC_BF(TFMR_FSLEN, fslen)
                        | SSC_BF(TFMR_DATNB, (channels - 1))
                        | SSC_BIT(TFMR_MSBF)
                        | SSC_BF(TFMR_DATDEF, 0)