[ALSA] ASoC: Add support for BCLK based on (Rate * Chn * Word Size)
authorLiam Girdwood <lg@opensource.wolfsonmicro.com>
Thu, 19 Oct 2006 18:35:56 +0000 (20:35 +0200)
committerJaroslav Kysela <perex@suse.cz>
Fri, 9 Feb 2007 08:01:07 +0000 (09:01 +0100)
This patch adds support for the DAI BCLK to be generated by multiplying
Rate * Channels * Word Size (RCW).
This now gives 3 options for BCLK clocking and synchronisation :-
 1. BCLK = Rate * x
 2. BCLK = MCLK / x
 3. BCLK = Rate * Chn * Word Size.  (New)
Changes:-
 o Add support for RCW generation of BCLK
 o Update Documentation to include RCW.
 o Update DAI documentation for label = value DAI modes.
 o Add RCW support to wm8731, wm8750 and pxa2xx-i2s drivers.

Signed-off-by: Liam Girdwood <lg@opensource.wolfsonmicro.com>
Signed-off-by: Takashi Iwai <tiwai@suse.de>
Signed-off-by: Jaroslav Kysela <perex@suse.cz>
Documentation/sound/alsa/soc/DAI.txt
Documentation/sound/alsa/soc/clocking.txt
include/sound/soc.h
sound/soc/codecs/wm8731.c
sound/soc/codecs/wm8750.c
sound/soc/pxa/pxa2xx-i2s.c
sound/soc/soc-core.c

index 919de76bab8d329acf8dd551bc9fd15f1f80414c..251545a8869354e5204dfef1a6918aa503dc6eea 100644 (file)
@@ -12,7 +12,8 @@ The bit clock (BCLK) is always driven by the CODEC (usually 12.288MHz) and the
 frame (FRAME) (usually 48kHz) is always driven by the controller. Each AC97
 frame is 21uS long and is divided into 13 time slots.
 
-The AC97 specification can be found at http://intel.com/
+The AC97 specification can be found at :-
+http://www.intel.com/design/chipsets/audio/ac97_r23.pdf
 
 
 I2S
@@ -77,16 +78,16 @@ sample rates first and then test your interface.
 struct snd_soc_dai_mode is defined (in soc.h) as:-
 
 /* SoC DAI mode */
-struct snd_soc_hw_mode {
-       unsigned int fmt:16;            /* SND_SOC_DAIFMT_* */
-       unsigned int tdm:16;            /* SND_SOC_DAITDM_* */
-       unsigned int pcmfmt:6;          /* SNDRV_PCM_FORMAT_* */
-       unsigned int pcmrate:16;        /* SND_SOC_DAIRATE_* */
-       unsigned int pcmdir:2;          /* SND_SOC_DAIDIR_* */
-       unsigned int flags:8;           /* hw flags */
-       unsigned int fs:32;                     /* mclk to rate dividers */
-       unsigned int bfs:16;            /* mclk to bclk dividers */
-       unsigned long priv;             /* private mode data */
+struct snd_soc_dai_mode {
+       u16 fmt;                /* SND_SOC_DAIFMT_* */
+       u16 tdm;                /* SND_SOC_HWTDM_* */
+       u64 pcmfmt;     /* SNDRV_PCM_FMTBIT_* */
+       u16 pcmrate;    /* SND_SOC_HWRATE_* */
+       u16 pcmdir:2;   /* SND_SOC_HWDIR_* */
+       u16 flags:8;    /* hw flags */
+       u16 fs;                 /* mclk to rate divider */
+       u64 bfs;                /* mclk to bclk dividers */
+       unsigned long priv;             /* private mode data */
 };
 
 fmt:
@@ -140,14 +141,14 @@ pcmfmt:
 The hardware PCM format. This describes the PCM formats supported by the DAI
 mode e.g.
 
- .hwpcmfmt = SNDRV_PCM_FORMAT_S16_LE | SNDRV_PCM_FORMAT_S20_3LE | \
+ .pcmfmt = SNDRV_PCM_FORMAT_S16_LE | SNDRV_PCM_FORMAT_S20_3LE | \
        SNDRV_PCM_FORMAT_S24_3LE
 
 pcmrate:
 ----------
 The PCM sample rates supported by the DAI mode. e.g.
 
- .hwpcmrate = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_11025 | SNDRV_PCM_RATE_16000 | \
+ .pcmrate = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_11025 | SNDRV_PCM_RATE_16000 | \
        SNDRV_PCM_RATE_22050 | SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 | \
        SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_88200 | SNDRV_PCM_RATE_96000
 
@@ -161,9 +162,14 @@ flags:
 --------
 The DAI hardware flags supported by the mode.
 
-SND_SOC_DAI_BFS_DIV
-This flag states that bit clock is generated by dividing MCLK in this mode, if
-this flag is absent the bitclock generated by mulitiplying sample rate.
+/* use bfs mclk divider mode (BCLK = MCLK / x) */
+#define SND_SOC_DAI_BFS_DIV            0x1
+/* use bfs rate mulitplier  (BCLK = RATE * x)*/
+#define SND_SOC_DAI_BFS_RATE   0x2
+/* use bfs rcw multiplier (BCLK = RATE * CHN * WORD SIZE) */
+#define SND_SOC_DAI_BFS_RCW            0x4
+/* capture and playback can use different clocks */
+#define SND_SOC_DAI_ASYNC              0x8
 
 NOTE: Bitclock division and mulitiplication modes can be safely matched by the
 core logic.
@@ -181,7 +187,7 @@ depends on the codec or CPU DAI).
 
 The BFS supported by the DAI mode. This can either be the ratio between the
 bitclock (BCLK) and the sample rate OR the ratio between the system clock and
-the sample rate. Depends on the SND_SOC_DAI_BFS_DIV flag above.
+the sample rate. Depends on the flags above.
 
 priv:
 -----
@@ -207,10 +213,15 @@ Simple codec that only runs at 8k & 48k @ 256FS in master mode, can generate a
 BCLK of either MCLK/2 or MCLK/4.
 
        /* codec master */
-       {SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_CBM_CFM, SND_SOC_DAITDM_LRDW(0,0),
-       SNDRV_PCM_FORMAT_S16_LE, SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_48000,
-       SND_SOC_DAIDIR_PLAYBACK | SND_SOC_DAIDIR_CAPTURE, SND_SOC_DAI_BFS_DIV,
-       256, SND_SOC_FSBD(2) | SND_SOC_FSBD(4)},
+       {
+               .fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_CBM_CFM,
+               .pcmfmt = SNDRV_PCM_FORMAT_S16_LE,
+               .pcmrate = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_48000,
+               .pcmdir = SND_SOC_DAIDIR_PLAYBACK | SND_SOC_DAIDIR_CAPTURE,
+               .flags = SND_SOC_DAI_BFS_DIV,
+               .fs = 256,
+               .bfs = SND_SOC_FSBD(2) | SND_SOC_FSBD(4),
+       }
 
 
 Example 2
@@ -219,32 +230,95 @@ Simple codec that only runs at 8k & 48k @ 256FS in master mode, can generate a
 BCLK of either Rate * 32 or Rate * 64.
 
        /* codec master */
-       {SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_CBM_CFM, SND_SOC_DAITDM_LRDW(0,0),
-       SNDRV_PCM_FORMAT_S16_LE, SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_48000,
-       SND_SOC_DAIDIR_PLAYBACK | SND_SOC_DAIDIR_CAPTURE, 0,
-       256, SND_SOC_FSB(32) | SND_SOC_FSB(64)},
+       {
+               .fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_CBM_CFM,
+               .pcmfmt = SNDRV_PCM_FORMAT_S16_LE,
+               .pcmrate = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_48000,
+               .pcmdir = SND_SOC_DAIDIR_PLAYBACK | SND_SOC_DAIDIR_CAPTURE,
+               .flags = SND_SOC_DAI_BFS_RATE,
+               .fs = 256,
+               .bfs = 32,
+       },
+       {
+               .fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_CBM_CFM,
+               .pcmfmt = SNDRV_PCM_FORMAT_S16_LE,
+               .pcmrate = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_48000,
+               .pcmdir = SND_SOC_DAIDIR_PLAYBACK | SND_SOC_DAIDIR_CAPTURE,
+               .flags = SND_SOC_DAI_BFS_RATE,
+               .fs = 256,
+               .bfs = 64,
+       },
 
 
 Example 3
 ---------
+Codec that runs at 8k & 48k @ 256FS in master mode, can generate a BCLK that
+is a multiple of Rate * channels * word size. (RCW) i.e.
+
+       BCLK = 8000 * 2 * 16 (8k, stereo, 16bit)
+            = 256kHz
+
+This codecs supports a RCW multiple of 1,2
+
+       {
+               .fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_CBM_CFM,
+               .pcmfmt = SNDRV_PCM_FORMAT_S16_LE,
+               .pcmrate = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_48000,
+               .pcmdir = SND_SOC_DAIDIR_PLAYBACK | SND_SOC_DAIDIR_CAPTURE,
+               .flags = SND_SOC_DAI_BFS_RCW,
+               .fs = 256,
+               .bfs = SND_SOC_FSBW(1) | SND_SOC_FSBW(2),
+       }
+
+
+Example 4
+---------
 Codec that only runs at 8k & 48k @ 256FS in master mode, can generate a
 BCLK of either Rate * 32 or Rate * 64. Codec can also run in slave mode as long
 as BCLK is rate * 32 or rate * 64.
 
        /* codec master */
-       {SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_CBM_CFM, SND_SOC_DAITDM_LRDW(0,0),
-       SNDRV_PCM_FORMAT_S16_LE, SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_48000,
-       SND_SOC_DAIDIR_PLAYBACK | SND_SOC_DAIDIR_CAPTURE, 0,
-       256, SND_SOC_FSB(32) | SND_SOC_FSB(64)},
+       {
+               .fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_CBM_CFM,
+               .pcmfmt = SNDRV_PCM_FORMAT_S16_LE,
+               .pcmrate = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_48000,
+               .pcmdir = SND_SOC_DAIDIR_PLAYBACK | SND_SOC_DAIDIR_CAPTURE,
+               .flags = SND_SOC_DAI_BFS_RATE,
+               .fs = 256,
+               .bfs = 32,
+       },
+       {
+               .fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_CBM_CFM,
+               .pcmfmt = SNDRV_PCM_FORMAT_S16_LE,
+               .pcmrate = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_48000,
+               .pcmdir = SND_SOC_DAIDIR_PLAYBACK | SND_SOC_DAIDIR_CAPTURE,
+               .flags = SND_SOC_DAI_BFS_RATE,
+               .fs = 256,
+               .bfs = 64,
+       },
 
        /* codec slave */
-       {SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_CBS_CFS, SND_SOC_DAITDM_LRDW(0,0),
-       SNDRV_PCM_FORMAT_S16_LE, SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_48000,
-       SND_SOC_DAIDIR_PLAYBACK | SND_SOC_DAIDIR_CAPTURE, 0,
-       SND_SOC_FS_ALL, SND_SOC_FSB(32) | SND_SOC_FSB(64)},
+       {
+               .fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_CBS_CFS,
+               .pcmfmt = SNDRV_PCM_FORMAT_S16_LE,
+               .pcmdir = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_48000,
+               .pcmdir = SND_SOC_DAIDIR_PLAYBACK | SND_SOC_DAIDIR_CAPTURE,
+               .flags = SND_SOC_DAI_BFS_RATE,
+               .fs = SND_SOC_FS_ALL,
+               .bfs = 32,
+       },
+       {
+               .fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_CBS_CFS,
+               .pcmfmt = SNDRV_PCM_FORMAT_S16_LE,
+               .pcmdir = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_48000,
+               .pcmdir = SND_SOC_DAIDIR_PLAYBACK | SND_SOC_DAIDIR_CAPTURE,
+               .flags = SND_SOC_DAI_BFS_RATE,
+               .fs = SND_SOC_FS_ALL,
+               .bfs = 64,
+       },
 
 
-Example 4
+Example 5
 ---------
 Codec that only runs at 8k, 16k, 32k, 48k, 96k @ 128FS, 192FS & 256FS in master
 mode and can generate a BCLK of MCLK / (1,2,4,8,16). Codec can also run in slave
@@ -259,29 +333,48 @@ mode as and does not care about FS or BCLK (as long as there is enough bandwidth
         SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_96000)
 
        /* codec master @ 128, 192 & 256 FS */
-       {SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_CBM_CFM, SND_SOC_DAITDM_LRDW(0,0),
-       SNDRV_PCM_FORMAT_S16_LE, CODEC_RATES,
-       SND_SOC_DAIDIR_PLAYBACK | SND_SOC_DAIDIR_CAPTURE, SND_SOC_DAI_BFS_DIV,
-       128, CODEC_FSB},
-
-       {SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_CBM_CFM, SND_SOC_DAITDM_LRDW(0,0),
-       SNDRV_PCM_FORMAT_S16_LE, CODEC_RATES,
-       SND_SOC_DAIDIR_PLAYBACK | SND_SOC_DAIDIR_CAPTURE, SND_SOC_DAI_BFS_DIV,
-       192, CODEC_FSB},
-
-       {SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_CBM_CFM, SND_SOC_DAITDM_LRDW(0,0),
-       SNDRV_PCM_FORMAT_S16_LE, CODEC_RATES,
-       SND_SOC_DAIDIR_PLAYBACK | SND_SOC_DAIDIR_CAPTURE, SND_SOC_DAI_BFS_DIV,
-       256, CODEC_FSB},
+       {
+               .fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_CBM_CFM,
+               .pcmfmt = SNDRV_PCM_FORMAT_S16_LE,
+               .pcmrate = CODEC_RATES,
+               .pcmdir = SND_SOC_DAIDIR_PLAYBACK | SND_SOC_DAIDIR_CAPTURE,
+               .flags = SND_SOC_DAI_BFS_DIV,
+               .fs = 128,
+               .bfs = CODEC_FSB,
+       },
+
+       {
+               .fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_CBM_CFM,
+               .pcmfmt = SNDRV_PCM_FORMAT_S16_LE,
+               .pcmrate = CODEC_RATES,
+               .pcmdir = SND_SOC_DAIDIR_PLAYBACK | SND_SOC_DAIDIR_CAPTURE,
+               .flags = SND_SOC_DAI_BFS_DIV,
+               .fs = 192,
+               .bfs = CODEC_FSB
+       },
+
+       {
+               .fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_CBM_CFM,
+               .pcmfmt = SNDRV_PCM_FORMAT_S16_LE,
+               .pcmrate = CODEC_RATES,
+               .pcmdir = SND_SOC_DAIDIR_PLAYBACK | SND_SOC_DAIDIR_CAPTURE,
+               .flags = SND_SOC_DAI_BFS_DIV,
+               .fs = 256,
+               .bfs = CODEC_FSB,
+       },
 
        /* codec slave */
-       {SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_CBS_CFS, SND_SOC_DAITDM_LRDW(0,0),
-       SNDRV_PCM_FORMAT_S16_LE, CODEC_RATES,
-       SND_SOC_DAIDIR_PLAYBACK | SND_SOC_DAIDIR_CAPTURE, 0,
-       SND_SOC_FS_ALL, SND_SOC_FSB_ALL},
+       {
+               .fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_CBS_CFS,
+               .pcmfmt = SNDRV_PCM_FORMAT_S16_LE,
+               .pcmrate = CODEC_RATES,
+               .pcmdir = SND_SOC_DAIDIR_PLAYBACK | SND_SOC_DAIDIR_CAPTURE,
+               .fs = SND_SOC_FS_ALL,
+               .bfs = SND_SOC_FSB_ALL,
+       },
 
 
-Example 5
+Example 6
 ---------
 Codec that only runs at 8k, 44.1k, 48k @ different FS in master mode (for use
 with a fixed MCLK) and can generate a BCLK of MCLK / (1,2,4,8,16).
@@ -298,45 +391,66 @@ sizes.
        SNDRV_PCM_FORMAT_S24_3LE | SNDRV_PCM_FORMAT_S24_LE | SNDRV_PCM_FORMAT_S32_LE)
 
        /* codec master */
-       {SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_CBM_CFM, SND_SOC_DAITDM_LRDW(0,0),
-       SNDRV_PCM_FORMAT_S16_LE, SNDRV_PCM_RATE_8000,
-       SND_SOC_DAIDIR_PLAYBACK | SND_SOC_DAIDIR_CAPTURE, SND_SOC_DAI_BFS_DIV,
-       1536, CODEC_FSB},
-
-       {SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_CBM_CFM, SND_SOC_DAITDM_LRDW(0,0),
-       SNDRV_PCM_FORMAT_S16_LE, SNDRV_PCM_RATE_44100,
-       SND_SOC_DAIDIR_PLAYBACK | SND_SOC_DAIDIR_CAPTURE, SND_SOC_DAI_BFS_DIV,
-       272, CODEC_FSB},
-
-       {SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_CBM_CFM, SND_SOC_DAITDM_LRDW(0,0),
-       SNDRV_PCM_FORMAT_S16_LE, SNDRV_PCM_RATE_48000,
-       SND_SOC_DAIDIR_PLAYBACK | SND_SOC_DAIDIR_CAPTURE, SND_SOC_DAI_BFS_DIV,
-       256, CODEC_FSB},
+       {
+               .fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_CBM_CFM,
+               .pcmfmt = SNDRV_PCM_FORMAT_S16_LE,
+               .pcmrate = SNDRV_PCM_RATE_8000,
+               .pcmdir = SND_SOC_DAIDIR_PLAYBACK | SND_SOC_DAIDIR_CAPTURE,
+               .flags = SND_SOC_DAI_BFS_DIV,
+               .fs = 1536,
+               .bfs = CODEC_FSB,
+       },
+
+       {
+               .fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_CBM_CFM,
+               .pcmfmt = SNDRV_PCM_FORMAT_S16_LE,
+               .pcmrate = SNDRV_PCM_RATE_44100,
+               .pcmdir = SND_SOC_DAIDIR_PLAYBACK | SND_SOC_DAIDIR_CAPTURE,
+               .flags = SND_SOC_DAI_BFS_DIV,
+               .fs = 272,
+               .bfs = CODEC_FSB,
+       },
+
+       {
+               .fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_CBM_CFM,
+               .pcmfmt = SNDRV_PCM_FORMAT_S16_LE,
+               .pcmrate = SNDRV_PCM_RATE_48000,
+               .pcmdir = SND_SOC_DAIDIR_PLAYBACK | SND_SOC_DAIDIR_CAPTURE,
+               .flags = SND_SOC_DAI_BFS_DIV,
+               .fs = 256,
+               .bfs = CODEC_FSB,
+       },
 
        /* codec slave */
-       {SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_CBS_CFS, SND_SOC_DAITDM_LRDW(0,0),
-       SNDRV_PCM_FORMAT_S16_LE, CODEC_RATES,
-       SND_SOC_DAIDIR_PLAYBACK | SND_SOC_DAIDIR_CAPTURE, 0,
-       SND_SOC_FS_ALL, SND_SOC_FSB_ALL},
+       {
+               .fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_CBS_CFS,
+               .pcmfmt = SNDRV_PCM_FORMAT_S16_LE,
+               .pcmrate = CODEC_RATES,
+               .pcmdir = SND_SOC_DAIDIR_PLAYBACK | SND_SOC_DAIDIR_CAPTURE,
+               .fs = SND_SOC_FS_ALL,
+               .bfs = SND_SOC_FSB_ALL,
+       },
 
 
-Example 6
+Example 7
 ---------
 AC97 Codec that does not support VRA (i.e only runs at 48k).
 
        #define AC97_DIR \
        (SND_SOC_DAIDIR_PLAYBACK | SND_SOC_DAIDIR_CAPTURE)
 
-
        #define AC97_PCM_FORMATS \
        (SNDRV_PCM_FORMAT_S16_LE | SNDRV_PCM_FORMAT_S18_3LE | \
        SNDRV_PCM_FORMAT_S20_3LE)
 
        /* AC97 with no VRA */
-       {0, 0,  AC97_PCM_FORMATS,       SNDRV_PCM_RATE_48000},
+       {
+               .pcmfmt = AC97_PCM_FORMATS,
+               .pcmrate = SNDRV_PCM_RATE_48000,
+       }
 
 
-Example 7
+Example 8
 ---------
 
 CPU DAI that supports 8k - 48k @ 256FS and BCLK = MCLK / 4 in master mode.
@@ -354,27 +468,79 @@ BCLK = 64 * rate. (Intel XScale I2S controller).
        SNDRV_PCM_RATE_22050 | SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 | \
        SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_88200 | SNDRV_PCM_RATE_96000)
 
+       /* priv is divider */
+       static struct snd_soc_dai_mode pxa2xx_i2s_modes[] = {
        /* pxa2xx I2S frame and clock master modes */
-       {PXA_I2S_DAIFMT | SND_SOC_DAIFMT_CBS_CFS, SND_SOC_DAITDM_LRDW(0,0), SNDRV_PCM_FORMAT_S16_LE,
-               SNDRV_PCM_RATE_8000, PXA_I2S_DIR, SND_SOC_DAI_BFS_DIV, 256,
-               SND_SOC_FSBD(4), 0x48},
-       {PXA_I2S_DAIFMT | SND_SOC_DAIFMT_CBS_CFS, SND_SOC_DAITDM_LRDW(0,0), SNDRV_PCM_FORMAT_S16_LE,
-               SNDRV_PCM_RATE_11025, PXA_I2S_DIR, SND_SOC_DAI_BFS_DIV, 256,
-               SND_SOC_FSBD(4), 0x34},
-       {PXA_I2S_DAIFMT | SND_SOC_DAIFMT_CBS_CFS, SND_SOC_DAITDM_LRDW(0,0), SNDRV_PCM_FORMAT_S16_LE,
-               SNDRV_PCM_RATE_16000, PXA_I2S_DIR, SND_SOC_DAI_BFS_DIV, 256,
-               SND_SOC_FSBD(4), 0x24},
-       {PXA_I2S_DAIFMT | SND_SOC_DAIFMT_CBS_CFS, SND_SOC_DAITDM_LRDW(0,0), SNDRV_PCM_FORMAT_S16_LE,
-               SNDRV_PCM_RATE_22050, PXA_I2S_DIR, SND_SOC_DAI_BFS_DIV, 256,
-               SND_SOC_FSBD(4), 0x1a},
-       {PXA_I2S_DAIFMT | SND_SOC_DAIFMT_CBS_CFS, SND_SOC_DAITDM_LRDW(0,0), SNDRV_PCM_FORMAT_S16_LE,
-               SNDRV_PCM_RATE_44100, PXA_I2S_DIR, SND_SOC_DAI_BFS_DIV, 256,
-               SND_SOC_FSBD(4), 0xd},
-       {PXA_I2S_DAIFMT | SND_SOC_DAIFMT_CBS_CFS, SND_SOC_DAITDM_LRDW(0,0), SNDRV_PCM_FORMAT_S16_LE,
-               SNDRV_PCM_RATE_48000, PXA_I2S_DIR, SND_SOC_DAI_BFS_DIV, 256,
-               SND_SOC_FSBD(4), 0xc},
+       {
+               .fmt = PXA_I2S_DAIFMT | SND_SOC_DAIFMT_CBS_CFS,
+               .pcmfmt = SNDRV_PCM_FMTBIT_S16_LE,
+               .pcmrate = SNDRV_PCM_RATE_8000,
+               .pcmdir = PXA_I2S_DIR,
+               .flags = SND_SOC_DAI_BFS_DIV,
+               .fs = 256,
+               .bfs = SND_SOC_FSBD(4),
+               .priv = 0x48,
+       },
+       {
+               .fmt = PXA_I2S_DAIFMT | SND_SOC_DAIFMT_CBS_CFS,
+               .pcmfmt = SNDRV_PCM_FMTBIT_S16_LE,
+               .pcmrate = SNDRV_PCM_RATE_11025,
+               .pcmdir = PXA_I2S_DIR,
+               .flags = SND_SOC_DAI_BFS_DIV,
+               .fs = 256,
+               .bfs = SND_SOC_FSBD(4),
+               .priv = 0x34,
+       },
+       {
+               .fmt = PXA_I2S_DAIFMT | SND_SOC_DAIFMT_CBS_CFS,
+               .pcmfmt = SNDRV_PCM_FMTBIT_S16_LE,
+               .pcmrate = SNDRV_PCM_RATE_16000,
+               .pcmdir = PXA_I2S_DIR,
+               .flags = SND_SOC_DAI_BFS_DIV,
+               .fs = 256,
+               .bfs = SND_SOC_FSBD(4),
+               .priv = 0x24,
+       },
+       {
+               .fmt = PXA_I2S_DAIFMT | SND_SOC_DAIFMT_CBS_CFS,
+               .pcmfmt = SNDRV_PCM_FMTBIT_S16_LE,
+               .pcmrate = SNDRV_PCM_RATE_22050,
+               .pcmdir = PXA_I2S_DIR,
+               .flags = SND_SOC_DAI_BFS_DIV,
+               .fs = 256,
+               .bfs = SND_SOC_FSBD(4),
+               .priv = 0x1a,
+       },
+       {
+               .fmt = PXA_I2S_DAIFMT | SND_SOC_DAIFMT_CBS_CFS,
+               .pcmfmt = SNDRV_PCM_FMTBIT_S16_LE,
+               .pcmrate = SNDRV_PCM_RATE_44100,
+               .pcmdir = PXA_I2S_DIR,
+               .flags = SND_SOC_DAI_BFS_DIV,
+               .fs = 256,
+               .bfs = SND_SOC_FSBD(4),
+               .priv = 0xd,
+       },
+       {
+               .fmt = PXA_I2S_DAIFMT | SND_SOC_DAIFMT_CBS_CFS,
+               .pcmfmt = SNDRV_PCM_FMTBIT_S16_LE,
+               .pcmrate = SNDRV_PCM_RATE_48000,
+               .pcmdir = PXA_I2S_DIR,
+               .flags = SND_SOC_DAI_BFS_DIV,
+               .fs = 256,
+               .bfs = SND_SOC_FSBD(4),
+               .priv = 0xc,
+       },
 
        /* pxa2xx I2S frame master and clock slave mode */
-       {PXA_I2S_DAIFMT | SND_SOC_DAIFMT_CBM_CFS, SND_SOC_DAITDM_LRDW(0,0), SNDRV_PCM_FORMAT_S16_LE,
-               PXA_I2S_RATES, PXA_I2S_DIR, 0, SND_SOC_FS_ALL, SND_SOC_FSB(64)},
-
+       {
+               .fmt = PXA_I2S_DAIFMT | SND_SOC_DAIFMT_CBM_CFS,
+               .pcmfmt = SNDRV_PCM_FMTBIT_S16_LE,
+               .pcmrate = PXA_I2S_RATES,
+               .pcmdir = PXA_I2S_DIR,
+               .fs = SND_SOC_FS_ALL,
+               .flags = SND_SOC_DAI_BFS_RATE,
+               .bfs = 64,
+               .priv = 0x48,
+       },
+};
index 88a16c9e1979fc3b2596a7806f9d2cb4ec2f38bf..1f55fd8cb1172d5baf1cfbfd71688ebc606675ee 100644 (file)
@@ -26,9 +26,9 @@ between the codec and CPU.
 
 The DAI also has a frame clock to signal the start of each audio frame. This
 clock is sometimes referred to as LRC (left right clock) or FRAME. This clock
-runs at exactly the sample rate.
+runs at exactly the sample rate (LRC = Rate).
 
-Bit Clock is usually always a ratio of MCLK or a multiple of LRC. i.e.
+Bit Clock can be generated as follows:-
 
 BCLK = MCLK / x
 
@@ -36,9 +36,14 @@ BCLK = MCLK / x
 
 BCLK = LRC * x
 
+ or
+
+BCLK = LRC * Channels * Word Size
+
 This relationship depends on the codec or SoC CPU in particular. ASoC can quite
-easily match a codec that generates BCLK by division (FSBD) with a CPU that
-generates BCLK by multiplication (FSB).
+easily match BCLK generated by division (SND_SOC_DAI_BFS_DIV) with BCLK by
+multiplication (SND_SOC_DAI_BFS_RATE) or BCLK generated  by
+Rate * Channels * Word size (RCW or SND_SOC_DAI_BFS_RCW).
 
 
 ASoC Clocking
index ecdd1fac94b67d7b44d9790e28541c8b8bff53d1..3dfe052e078807f5a8eae81ff115c13a4cd20c2a 100644 (file)
@@ -21,7 +21,7 @@
 #include <sound/control.h>
 #include <sound/ac97_codec.h>
 
-#define SND_SOC_VERSION "0.11.8"
+#define SND_SOC_VERSION "0.12"
 
 /*
  * Convenience kcontrol builders
 /* bit clock dividers */
 #define SND_SOC_FSBD(x)                        (1 << (x - 1))  /* ratio mclk:bclk */
 #define SND_SOC_FSBD_REAL(x)   (ffs(x))
-#define SND_SOC_FSBD_ALL               0xffff /* all bit clock dividers supported */
 
-/* bit clock ratio to sample rate */
-#define SND_SOC_FSB(x)                 (1 << ((x - 16) / 16))
-#define SND_SOC_FSB_REAL(x)            (((ffs(x) - 1) * 16) + 16)
+/* bit clock ratio to (sample rate * channels * word size) */
+#define SND_SOC_FSBW(x)                        (1 << (x - 1))
+#define SND_SOC_FSBW_REAL(x)           (ffs(x))
 /* all bclk ratios supported */
-#define SND_SOC_FSB_ALL                        SND_SOC_FSBD_ALL
+#define SND_SOC_FSB_ALL                        ~0ULL
 
 /*
  * DAI hardware flags
  */
-/* use bfs mclk divider mode, else sample rate ratio */
-#define SND_SOC_DAI_BFS_DIV    0x1
+/* use bfs mclk divider mode (BCLK = MCLK / x) */
+#define SND_SOC_DAI_BFS_DIV            0x1
+/* use bfs rate mulitplier  (BCLK = RATE * x)*/
+#define SND_SOC_DAI_BFS_RATE   0x2
+/* use bfs rcw multiplier (BCLK = RATE * CHN * WORD SIZE) */
+#define SND_SOC_DAI_BFS_RCW            0x4
+/* capture and playback can use different clocks */
+#define SND_SOC_DAI_ASYNC              0x8
 
 /*
  * AC97 codec ID's bitmask
@@ -264,7 +269,7 @@ struct snd_soc_dai_mode {
        u16 pcmdir:2;   /* SND_SOC_HWDIR_* */
        u16 flags:8;    /* hw flags */
        u16 fs;                 /* mclk to rate divider */
-       u32 bfs;                /* mclk to bclk dividers */
+       u64 bfs;                /* mclk to bclk dividers */
        unsigned long priv;             /* private mode data */
 };
 
index 9adbd2d401c4e9834932a6804c4c279c6cc7d033..412291241ece6e334150d06ecfe624fa1de633c1 100644 (file)
@@ -90,32 +90,36 @@ static struct snd_soc_dai_mode wm8731_modes[] = {
                .pcmfmt = WM8731_HIFI_BITS,
                .pcmrate = SNDRV_PCM_RATE_8000,
                .pcmdir = WM8731_DIR,
+               .flags = SND_SOC_DAI_BFS_RATE,
                .fs = 1536,
-               .bfs = SND_SOC_FSB(64),
+               .bfs = 64,
        },
        {
                .fmt = WM8731_DAIFMT | SND_SOC_DAIFMT_CBM_CFM,
                .pcmfmt = WM8731_HIFI_BITS,
                .pcmrate = SNDRV_PCM_RATE_8000,
                .pcmdir = WM8731_DIR,
+               .flags = SND_SOC_DAI_BFS_RATE,
                .fs = 2304,
-               .bfs = SND_SOC_FSB(64),
+               .bfs = 64,
        },
        {
                .fmt = WM8731_DAIFMT | SND_SOC_DAIFMT_CBM_CFM,
                .pcmfmt = WM8731_HIFI_BITS,
                .pcmrate = SNDRV_PCM_RATE_8000,
                .pcmdir = WM8731_DIR,
+               .flags = SND_SOC_DAI_BFS_RATE,
                .fs = 1408,
-               .bfs = SND_SOC_FSB(64),
+               .bfs = 64,
        },
        {
                .fmt = WM8731_DAIFMT | SND_SOC_DAIFMT_CBM_CFM,
                .pcmfmt = WM8731_HIFI_BITS,
                .pcmrate = SNDRV_PCM_RATE_8000,
                .pcmdir = WM8731_DIR,
+               .flags = SND_SOC_DAI_BFS_RATE,
                .fs = 2112,
-               .bfs = SND_SOC_FSB(64),
+               .bfs = 64,
        },
 
        /* 32k */
@@ -124,16 +128,18 @@ static struct snd_soc_dai_mode wm8731_modes[] = {
                .pcmfmt = WM8731_HIFI_BITS,
                .pcmrate = SNDRV_PCM_RATE_32000,
                .pcmdir = WM8731_DIR,
+               .flags = SND_SOC_DAI_BFS_RATE,
                .fs = 384,
-               .bfs = SND_SOC_FSB(64),
+               .bfs = 64,
        },
        {
                .fmt = WM8731_DAIFMT | SND_SOC_DAIFMT_CBM_CFM,
                .pcmfmt = WM8731_HIFI_BITS,
                .pcmrate = SNDRV_PCM_RATE_32000,
                .pcmdir = WM8731_DIR,
+               .flags = SND_SOC_DAI_BFS_RATE,
                .fs = 576,
-               .bfs = SND_SOC_FSB(64),
+               .bfs = 64,
        },
 
        /* 44.1k & 48k */
@@ -142,16 +148,18 @@ static struct snd_soc_dai_mode wm8731_modes[] = {
                .pcmfmt = WM8731_HIFI_BITS,
                .pcmrate = SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000,
                .pcmdir = WM8731_DIR,
+               .flags = SND_SOC_DAI_BFS_RATE,
                .fs = 256,
-               .bfs = SND_SOC_FSB(64),
+               .bfs = 64,
        },
        {
                .fmt = WM8731_DAIFMT | SND_SOC_DAIFMT_CBM_CFM,
                .pcmfmt = WM8731_HIFI_BITS,
                .pcmrate = SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000,
                .pcmdir = WM8731_DIR,
+               .flags = SND_SOC_DAI_BFS_RATE,
                .fs = 384,
-               .bfs = SND_SOC_FSB(64),
+               .bfs = 64,
        },
 
        /* 88.2 & 96k */
@@ -160,17 +168,18 @@ static struct snd_soc_dai_mode wm8731_modes[] = {
                .pcmfmt = WM8731_HIFI_BITS,
                .pcmrate = SNDRV_PCM_RATE_88200 | SNDRV_PCM_RATE_96000,
                .pcmdir = WM8731_DIR,
+               .flags = SND_SOC_DAI_BFS_RATE,
                .fs = 128,
-               .bfs = SND_SOC_FSB(64),
-               
+               .bfs = 64,
        },
        {
                .fmt = WM8731_DAIFMT | SND_SOC_DAIFMT_CBM_CFM,
                .pcmfmt = WM8731_HIFI_BITS,
                .pcmrate = SNDRV_PCM_RATE_88200 | SNDRV_PCM_RATE_96000,
                .pcmdir = WM8731_DIR,
+               .flags = SND_SOC_DAI_BFS_RATE,
                .fs = 192,
-               .bfs = SND_SOC_FSB(64),
+               .bfs = 64,
        },
 
        /* USB codec frame and clock master modes */
@@ -237,7 +246,7 @@ static struct snd_soc_dai_mode wm8731_modes[] = {
                .pcmdir = WM8731_DIR,
                .flags = SND_SOC_DAI_BFS_DIV,
                .fs = SND_SOC_FS_ALL,
-               .bfs = SND_SOC_FSBD_ALL,
+               .bfs = SND_SOC_FSB_ALL,
        },
 };
 
index 243da712d9c14b53b0c4c17c043c9f8903c43f77..c5d13a9454d9eb147623f6b74e9e76b2f4199088 100644 (file)
@@ -343,7 +343,7 @@ static struct snd_soc_dai_mode wm8750_modes[] = {
                .pcmdir = WM8750_DIR,
                .flags = SND_SOC_DAI_BFS_DIV,
                .fs = SND_SOC_FS_ALL,
-               .bfs = SND_SOC_FSBD_ALL,
+               .bfs = SND_SOC_FSB_ALL,
        },
 };
 
@@ -829,6 +829,9 @@ static inline int get_coeff(int mclk, int rate)
                if (coeff_div[i].rate == rate && coeff_div[i].mclk == mclk)
                        return i;
        }
+
+       printk(KERN_ERR "wm8750: could not get coeff for mclk %d @ rate %d\n",
+               mclk, rate);
        return -EINVAL;
 }
 
@@ -836,13 +839,7 @@ static inline int get_coeff(int mclk, int rate)
 static unsigned int wm8750_config_sysclk(struct snd_soc_codec_dai *dai,
        struct snd_soc_clock_info *info, unsigned int clk)
 {
-       dai->mclk = 0;
-
-       /* check that the calculated FS and rate actually match a clock from
-        * the machine driver */
-       if (info->fs * info->rate == clk)
-               dai->mclk = clk;
-
+       dai->mclk = clk;
        return dai->mclk;
 }
 
@@ -859,7 +856,7 @@ static int wm8750_pcm_prepare(struct snd_pcm_substream *substream)
        if (i < 0)
                return i;
 
-       bfs = SND_SOC_FSB_REAL(rtd->codec_dai->dai_runtime.bfs);
+       bfs = SND_SOC_FSBD_REAL(rtd->codec_dai->dai_runtime.bfs);
 
        /* set master/slave audio interface */
        switch (rtd->codec_dai->dai_runtime.fmt & SND_SOC_DAIFMT_CLOCK_MASK) {
index 99f1da32744bb61cc330746388ed191385b98568..98b167fe68e541b068dd84356363b5629c7f3e37 100644 (file)
@@ -126,7 +126,8 @@ static struct snd_soc_dai_mode pxa2xx_i2s_modes[] = {
                .pcmrate = PXA_I2S_RATES,
                .pcmdir = PXA_I2S_DIR,
                .fs = SND_SOC_FS_ALL,
-               .bfs = SND_SOC_FSB(64),
+               .flags = SND_SOC_DAI_BFS_RATE,
+               .bfs = 64,
                .priv = 0x48,
        },
 };
index 2ce0c8251dc3d41dd70eb9cd3d642e5b06546f02..6da1616bf776d0abb21760ba69d142d426578fe5 100644 (file)
@@ -51,6 +51,8 @@
 #define dbgc(format, arg...)
 #endif
 
+#define CODEC_CPU(codec, cpu)  ((codec << 4) | cpu)
+
 static DEFINE_MUTEX(pcm_mutex);
 static DEFINE_MUTEX(io_mutex);
 static struct workqueue_struct *soc_workq;
@@ -150,11 +152,11 @@ static unsigned inline int soc_get_mclk(struct snd_soc_pcm_runtime *rtd,
 }
 
 /* changes a bitclk multiplier mask to a divider mask */
-static u16 soc_bfs_mult_to_div(u16 bfs, int rate, unsigned int mclk,
+static u64 soc_bfs_rcw_to_div(u64 bfs, int rate, unsigned int mclk,
        unsigned int pcmfmt, unsigned int chn)
 {
        int i, j;
-       u16 bfs_ = 0;
+       u64 bfs_ = 0;
        int size = snd_pcm_format_physical_width(pcmfmt), min = 0;
 
        if (size <= 0)
@@ -162,17 +164,14 @@ static u16 soc_bfs_mult_to_div(u16 bfs, int rate, unsigned int mclk,
 
        /* the minimum bit clock that has enough bandwidth */
        min = size * rate * chn;
-       dbgc("mult --> div min bclk %d with mclk %d\n", min, mclk);
+       dbgc("rcw --> div min bclk %d with mclk %d\n", min, mclk);
 
-       for (i = 0; i < 16; i++) {
+       for (i = 0; i < 64; i++) {
                if ((bfs >> i) & 0x1) {
-                       j = rate * SND_SOC_FSB_REAL(1<<i);
-
-                       if (j >= min) {
-                               bfs_ |= SND_SOC_FSBD(mclk/j);
-                               dbgc("mult --> div support mult %d\n",
-                                       SND_SOC_FSB_REAL(1<<i));
-                       }
+                       j = min * (i + 1);
+                       bfs_ |= SND_SOC_FSBD(mclk/j);
+                       dbgc("rcw --> div support mult %d\n",
+                               SND_SOC_FSBD_REAL(1<<i));
                }
        }
 
@@ -180,11 +179,11 @@ static u16 soc_bfs_mult_to_div(u16 bfs, int rate, unsigned int mclk,
 }
 
 /* changes a bitclk divider mask to a multiplier mask */
-static u16 soc_bfs_div_to_mult(u16 bfs, int rate, unsigned int mclk,
+static u64 soc_bfs_div_to_rcw(u64 bfs, int rate, unsigned int mclk,
        unsigned int pcmfmt, unsigned int chn)
 {
        int i, j;
-       u16 bfs_ = 0;
+       u64 bfs_ = 0;
 
        int size = snd_pcm_format_physical_width(pcmfmt), min = 0;
 
@@ -193,15 +192,15 @@ static u16 soc_bfs_div_to_mult(u16 bfs, int rate, unsigned int mclk,
 
        /* the minimum bit clock that has enough bandwidth */
        min = size * rate * chn;
-       dbgc("div to mult min bclk %d with mclk %d\n", min, mclk);
+       dbgc("div to rcw min bclk %d with mclk %d\n", min, mclk);
 
-       for (i = 0; i < 16; i++) {
+       for (i = 0; i < 64; i++) {
                if ((bfs >> i) & 0x1) {
-                       j = mclk / (SND_SOC_FSBD_REAL(1<<i));
+                       j = mclk / (i + 1);
                        if (j >= min) {
-                               bfs_ |= SND_SOC_FSB(j/rate);
-                               dbgc("div --> mult support div %d\n",
-                                       SND_SOC_FSBD_REAL(1<<i));
+                               bfs_ |= SND_SOC_FSBW(j/min);
+                               dbgc("div --> rcw support div %d\n",
+                                       SND_SOC_FSBW_REAL(1<<i));
                        }
                }
        }
@@ -209,6 +208,52 @@ static u16 soc_bfs_div_to_mult(u16 bfs, int rate, unsigned int mclk,
        return bfs_;
 }
 
+/* changes a constant bitclk to a multiplier mask */
+static u64 soc_bfs_rate_to_rcw(u64 bfs, int rate, unsigned int mclk,
+       unsigned int pcmfmt, unsigned int chn)
+{
+       unsigned int bfs_ = rate * bfs;
+       int size = snd_pcm_format_physical_width(pcmfmt), min = 0;
+
+       if (size <= 0)
+               return 0;
+
+       /* the minimum bit clock that has enough bandwidth */
+       min = size * rate * chn;
+       dbgc("rate --> rcw min bclk %d with mclk %d\n", min, mclk);
+
+       if (bfs_ < min)
+               return 0;
+       else {
+               bfs_ = SND_SOC_FSBW(bfs_/min);
+               dbgc("rate --> rcw support div %d\n", SND_SOC_FSBW_REAL(bfs_));
+               return bfs_;
+       }
+}
+
+/* changes a bitclk multiplier mask to a divider mask */
+static u64 soc_bfs_rate_to_div(u64 bfs, int rate, unsigned int mclk,
+       unsigned int pcmfmt, unsigned int chn)
+{
+       unsigned int bfs_ = rate * bfs;
+       int size = snd_pcm_format_physical_width(pcmfmt), min = 0;
+
+       if (size <= 0)
+               return 0;
+
+       /* the minimum bit clock that has enough bandwidth */
+       min = size * rate * chn;
+       dbgc("rate --> div min bclk %d with mclk %d\n", min, mclk);
+
+       if (bfs_ < min)
+               return 0;
+       else {
+               bfs_ = SND_SOC_FSBW(mclk/bfs_);
+               dbgc("rate --> div support div %d\n", SND_SOC_FSBD_REAL(bfs_));
+               return bfs_;
+       }
+}
+
 /* Matches codec DAI and SoC CPU DAI hardware parameters */
 static int soc_hw_match_params(struct snd_pcm_substream *substream,
        struct snd_pcm_hw_params *params)
@@ -217,9 +262,10 @@ static int soc_hw_match_params(struct snd_pcm_substream *substream,
        struct snd_soc_dai_mode *codec_dai_mode = NULL;
        struct snd_soc_dai_mode *cpu_dai_mode = NULL;
        struct snd_soc_clock_info clk_info;
-       unsigned int fs, mclk, codec_bfs, cpu_bfs, rate = params_rate(params),
+       unsigned int fs, mclk, rate = params_rate(params),
                chn, j, k, cpu_bclk, codec_bclk, pcmrate;
        u16 fmt = 0;
+       u64 codec_bfs, cpu_bfs;
 
        dbg("asoc: match version %s\n", SND_SOC_VERSION);
        clk_info.rate = rate;
@@ -309,44 +355,98 @@ static int soc_hw_match_params(struct snd_pcm_substream *substream,
                         * used in the codec and cpu DAI modes. We always choose the
                         * lowest possible clocks to reduce power.
                         */
-                       if (codec_dai_mode->flags & cpu_dai_mode->flags &
-                               SND_SOC_DAI_BFS_DIV) {
+                       switch (CODEC_CPU(codec_dai_mode->flags, cpu_dai_mode->flags)) {
+                       case CODEC_CPU(SND_SOC_DAI_BFS_DIV, SND_SOC_DAI_BFS_DIV):
                                /* cpu & codec bfs dividers */
                                rtd->cpu_dai->dai_runtime.bfs =
                                        rtd->codec_dai->dai_runtime.bfs =
                                        1 << (fls(codec_dai_mode->bfs & cpu_dai_mode->bfs) - 1);
-                       } else if (codec_dai_mode->flags & SND_SOC_DAI_BFS_DIV) {
-                               /* normalise bfs codec divider & cpu mult */
-                               codec_bfs = soc_bfs_div_to_mult(codec_dai_mode->bfs, rate,
+                               break;
+                       case CODEC_CPU(SND_SOC_DAI_BFS_DIV, SND_SOC_DAI_BFS_RCW):
+                               /* normalise bfs codec divider & cpu rcw mult */
+                               codec_bfs = soc_bfs_div_to_rcw(codec_dai_mode->bfs, rate,
                                        mclk, rtd->codec_dai->dai_runtime.pcmfmt, chn);
                                rtd->cpu_dai->dai_runtime.bfs =
                                        1 << (ffs(codec_bfs & cpu_dai_mode->bfs) - 1);
-                               cpu_bfs = soc_bfs_mult_to_div(cpu_dai_mode->bfs, rate, mclk,
+                               cpu_bfs = soc_bfs_rcw_to_div(cpu_dai_mode->bfs, rate, mclk,
                                                rtd->codec_dai->dai_runtime.pcmfmt, chn);
                                rtd->codec_dai->dai_runtime.bfs =
                                        1 << (fls(codec_dai_mode->bfs & cpu_bfs) - 1);
-                       } else if (cpu_dai_mode->flags & SND_SOC_DAI_BFS_DIV) {
-                               /* normalise bfs codec mult & cpu divider */
-                               codec_bfs = soc_bfs_mult_to_div(codec_dai_mode->bfs, rate,
+                               break;
+                       case CODEC_CPU(SND_SOC_DAI_BFS_RCW, SND_SOC_DAI_BFS_DIV):
+                               /* normalise bfs codec rcw mult & cpu divider */
+                               codec_bfs = soc_bfs_rcw_to_div(codec_dai_mode->bfs, rate,
                                        mclk, rtd->codec_dai->dai_runtime.pcmfmt, chn);
                                rtd->cpu_dai->dai_runtime.bfs =
                                        1 << (fls(codec_bfs & cpu_dai_mode->bfs) -1);
-                               cpu_bfs = soc_bfs_div_to_mult(cpu_dai_mode->bfs, rate, mclk,
+                               cpu_bfs = soc_bfs_div_to_rcw(cpu_dai_mode->bfs, rate, mclk,
                                                rtd->codec_dai->dai_runtime.pcmfmt, chn);
                                rtd->codec_dai->dai_runtime.bfs =
                                        1 << (ffs(codec_dai_mode->bfs & cpu_bfs) -1);
-                       } else {
-                               /* codec & cpu bfs rate multipliers */
+                               break;
+                       case CODEC_CPU(SND_SOC_DAI_BFS_RCW, SND_SOC_DAI_BFS_RCW):
+                               /* codec & cpu bfs rate rcw multipliers */
                                rtd->cpu_dai->dai_runtime.bfs =
                                        rtd->codec_dai->dai_runtime.bfs =
                                        1 << (ffs(codec_dai_mode->bfs & cpu_dai_mode->bfs) -1);
+                               break;
+                       case CODEC_CPU(SND_SOC_DAI_BFS_DIV, SND_SOC_DAI_BFS_RATE):
+                               /* normalise cpu bfs rate const multiplier & codec div */
+                               cpu_bfs = soc_bfs_rate_to_div(cpu_dai_mode->bfs, rate,
+                                       mclk, rtd->codec_dai->dai_runtime.pcmfmt, chn);
+                               if(codec_dai_mode->bfs & cpu_bfs) {
+                                       rtd->codec_dai->dai_runtime.bfs = cpu_bfs;
+                                       rtd->cpu_dai->dai_runtime.bfs = cpu_dai_mode->bfs;
+                               } else
+                                       rtd->cpu_dai->dai_runtime.bfs = 0;
+                               break;
+                       case CODEC_CPU(SND_SOC_DAI_BFS_RCW, SND_SOC_DAI_BFS_RATE):
+                               /* normalise cpu bfs rate const multiplier & codec rcw mult */
+                               cpu_bfs = soc_bfs_rate_to_rcw(cpu_dai_mode->bfs, rate,
+                                       mclk, rtd->codec_dai->dai_runtime.pcmfmt, chn);
+                               if(codec_dai_mode->bfs & cpu_bfs) {
+                                       rtd->codec_dai->dai_runtime.bfs = cpu_bfs;
+                                       rtd->cpu_dai->dai_runtime.bfs = cpu_dai_mode->bfs;
+                               } else
+                                       rtd->cpu_dai->dai_runtime.bfs = 0;
+                               break;
+                       case CODEC_CPU(SND_SOC_DAI_BFS_RATE, SND_SOC_DAI_BFS_RCW):
+                               /* normalise cpu bfs rate rcw multiplier & codec const mult */
+                               codec_bfs = soc_bfs_rate_to_rcw(codec_dai_mode->bfs, rate,
+                                       mclk, rtd->codec_dai->dai_runtime.pcmfmt, chn);
+                               if(cpu_dai_mode->bfs & codec_bfs) {
+                                       rtd->cpu_dai->dai_runtime.bfs = codec_bfs;
+                                       rtd->codec_dai->dai_runtime.bfs = codec_dai_mode->bfs;
+                               } else
+                                       rtd->cpu_dai->dai_runtime.bfs = 0;
+                               break;
+                       case CODEC_CPU(SND_SOC_DAI_BFS_RATE, SND_SOC_DAI_BFS_DIV):
+                               /* normalise cpu bfs div & codec const mult */
+                               codec_bfs = soc_bfs_rate_to_div(codec_dai_mode->bfs, rate,
+                                       mclk, rtd->codec_dai->dai_runtime.pcmfmt, chn);
+                               if(codec_dai_mode->bfs & codec_bfs) {
+                                       rtd->cpu_dai->dai_runtime.bfs = codec_bfs;
+                                       rtd->codec_dai->dai_runtime.bfs = codec_dai_mode->bfs;
+                               } else
+                                       rtd->cpu_dai->dai_runtime.bfs = 0;
+                               break;
+                       case CODEC_CPU(SND_SOC_DAI_BFS_RATE, SND_SOC_DAI_BFS_RATE):
+                               /* cpu & codec constant mult */
+                               if(codec_dai_mode->bfs == cpu_dai_mode->bfs)
+                                       rtd->cpu_dai->dai_runtime.bfs =
+                                               rtd->codec_dai->dai_runtime.bfs =
+                                               codec_dai_mode->bfs;
+                               else
+                                       rtd->cpu_dai->dai_runtime.bfs =
+                                               rtd->codec_dai->dai_runtime.bfs = 0;
+                               break;
                        }
 
                        /* make sure the bit clock speed is acceptable */
                        if (!rtd->cpu_dai->dai_runtime.bfs ||
                                !rtd->codec_dai->dai_runtime.bfs) {
                                dbgc("asoc: DAI[%d:%d] failed to match BFS\n", j, k);
-                               dbgc("asoc: cpu_dai %x codec %x\n",
+                               dbgc("asoc: cpu_dai %llu codec %llu\n",
                                        rtd->cpu_dai->dai_runtime.bfs,
                                        rtd->codec_dai->dai_runtime.bfs);
                                dbgc("asoc: mclk %d hwfmt %x\n", mclk, fmt);
@@ -378,26 +478,41 @@ found:
                dbg("asoc: codec fs %d mclk %d bfs div %d bclk %d\n",
                        rtd->codec_dai->dai_runtime.fs, mclk,
                        SND_SOC_FSBD_REAL(rtd->codec_dai->dai_runtime.bfs),     codec_bclk);
-       } else {
-               codec_bclk = params_rate(params) *
-                       SND_SOC_FSB_REAL(rtd->codec_dai->dai_runtime.bfs);
-               dbg("asoc: codec fs %d mclk %d bfs mult %d bclk %d\n",
+       } else if(rtd->codec_dai->dai_runtime.flags == SND_SOC_DAI_BFS_RATE) {
+               codec_bclk = params_rate(params) * rtd->codec_dai->dai_runtime.bfs;
+               dbg("asoc: codec fs %d mclk %d bfs rate mult %llu bclk %d\n",
                        rtd->codec_dai->dai_runtime.fs, mclk,
-                       SND_SOC_FSB_REAL(rtd->codec_dai->dai_runtime.bfs), codec_bclk);
-       }
+                       rtd->codec_dai->dai_runtime.bfs, codec_bclk);
+       } else if (rtd->cpu_dai->dai_runtime.flags == SND_SOC_DAI_BFS_RCW) {
+               codec_bclk = params_rate(params) * params_channels(params) *
+                       snd_pcm_format_physical_width(rtd->codec_dai->dai_runtime.pcmfmt) *
+                       SND_SOC_FSBW_REAL(rtd->codec_dai->dai_runtime.bfs);
+               dbg("asoc: codec fs %d mclk %d bfs rcw mult %d bclk %d\n",
+                       rtd->codec_dai->dai_runtime.fs, mclk,
+                       SND_SOC_FSBW_REAL(rtd->codec_dai->dai_runtime.bfs), codec_bclk);
+       } else
+               codec_bclk = 0;
+
        if (rtd->cpu_dai->dai_runtime.flags == SND_SOC_DAI_BFS_DIV) {
                cpu_bclk = (rtd->cpu_dai->dai_runtime.fs * params_rate(params)) /
                        SND_SOC_FSBD_REAL(rtd->cpu_dai->dai_runtime.bfs);
                dbg("asoc: cpu fs %d mclk %d bfs div %d bclk %d\n",
                        rtd->cpu_dai->dai_runtime.fs, mclk,
                        SND_SOC_FSBD_REAL(rtd->cpu_dai->dai_runtime.bfs), cpu_bclk);
-       } else {
-               cpu_bclk = params_rate(params) *
-                       SND_SOC_FSB_REAL(rtd->cpu_dai->dai_runtime.bfs);
-               dbg("asoc: cpu fs %d mclk %d bfs mult %d bclk %d\n",
+       } else if (rtd->cpu_dai->dai_runtime.flags == SND_SOC_DAI_BFS_RATE) {
+               cpu_bclk = params_rate(params) * rtd->cpu_dai->dai_runtime.bfs;
+               dbg("asoc: cpu fs %d mclk %d bfs rate mult %llu bclk %d\n",
                        rtd->cpu_dai->dai_runtime.fs, mclk,
-                       SND_SOC_FSB_REAL(rtd->cpu_dai->dai_runtime.bfs), cpu_bclk);
-       }
+                       rtd->cpu_dai->dai_runtime.bfs, cpu_bclk);
+       } else if (rtd->cpu_dai->dai_runtime.flags == SND_SOC_DAI_BFS_RCW) {
+               cpu_bclk = params_rate(params) * params_channels(params) *
+                       snd_pcm_format_physical_width(rtd->cpu_dai->dai_runtime.pcmfmt) *
+                       SND_SOC_FSBW_REAL(rtd->cpu_dai->dai_runtime.bfs);
+               dbg("asoc: cpu fs %d mclk %d bfs mult rcw %d bclk %d\n",
+                       rtd->cpu_dai->dai_runtime.fs, mclk,
+                       SND_SOC_FSBW_REAL(rtd->cpu_dai->dai_runtime.bfs), cpu_bclk);
+       } else
+               cpu_bclk = 0;
 
        /*
         * Check we have matching bitclocks. If we don't then it means the
@@ -405,7 +520,7 @@ found:
         * machine sysclock function) is wrong compared with the supported DAI
         * modes for the codec or cpu DAI.
         */
-       if (cpu_bclk != codec_bclk){
+       if (cpu_bclk != codec_bclk && cpu_bclk){
                printk(KERN_ERR
                        "asoc: codec and cpu bitclocks differ, audio may be wrong speed\n"
                        );
@@ -723,14 +838,18 @@ static int soc_pcm_prepare(struct snd_pcm_substream *substream)
        mutex_lock(&pcm_mutex);
        if (platform->pcm_ops->prepare) {
                ret = platform->pcm_ops->prepare(substream);
-               if (ret < 0)
+               if (ret < 0) {
+                       printk(KERN_ERR "asoc: platform prepare error\n");
                        goto out;
+               }
        }
 
        if (rtd->codec_dai->ops.prepare) {
                ret = rtd->codec_dai->ops.prepare(substream);
-               if (ret < 0)
+               if (ret < 0) {
+                       printk(KERN_ERR "asoc: codec DAI prepare error\n");
                        goto out;
+               }
        }
 
        if (rtd->cpu_dai->ops.prepare)