[ALSA] ASoC: Add support for BCLK based on (Rate * Chn * Word Size)

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>
This commit is contained in:
Liam Girdwood 2006-10-19 20:35:56 +02:00 committed by Jaroslav Kysela
parent 543a0fbe18
commit a71a468a50
7 changed files with 477 additions and 175 deletions

View 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 (FRAME) (usually 48kHz) is always driven by the controller. Each AC97
frame is 21uS long and is divided into 13 time slots. 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 I2S
@ -77,16 +78,16 @@ sample rates first and then test your interface.
struct snd_soc_dai_mode is defined (in soc.h) as:- struct snd_soc_dai_mode is defined (in soc.h) as:-
/* SoC DAI mode */ /* SoC DAI mode */
struct snd_soc_hw_mode { struct snd_soc_dai_mode {
unsigned int fmt:16; /* SND_SOC_DAIFMT_* */ u16 fmt; /* SND_SOC_DAIFMT_* */
unsigned int tdm:16; /* SND_SOC_DAITDM_* */ u16 tdm; /* SND_SOC_HWTDM_* */
unsigned int pcmfmt:6; /* SNDRV_PCM_FORMAT_* */ u64 pcmfmt; /* SNDRV_PCM_FMTBIT_* */
unsigned int pcmrate:16; /* SND_SOC_DAIRATE_* */ u16 pcmrate; /* SND_SOC_HWRATE_* */
unsigned int pcmdir:2; /* SND_SOC_DAIDIR_* */ u16 pcmdir:2; /* SND_SOC_HWDIR_* */
unsigned int flags:8; /* hw flags */ u16 flags:8; /* hw flags */
unsigned int fs:32; /* mclk to rate dividers */ u16 fs; /* mclk to rate divider */
unsigned int bfs:16; /* mclk to bclk dividers */ u64 bfs; /* mclk to bclk dividers */
unsigned long priv; /* private mode data */ unsigned long priv; /* private mode data */
}; };
fmt: fmt:
@ -140,14 +141,14 @@ pcmfmt:
The hardware PCM format. This describes the PCM formats supported by the DAI The hardware PCM format. This describes the PCM formats supported by the DAI
mode e.g. 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 SNDRV_PCM_FORMAT_S24_3LE
pcmrate: pcmrate:
---------- ----------
The PCM sample rates supported by the DAI mode. e.g. 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_22050 | SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 | \
SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_88200 | SNDRV_PCM_RATE_96000 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. The DAI hardware flags supported by the mode.
SND_SOC_DAI_BFS_DIV /* use bfs mclk divider mode (BCLK = MCLK / x) */
This flag states that bit clock is generated by dividing MCLK in this mode, if #define SND_SOC_DAI_BFS_DIV 0x1
this flag is absent the bitclock generated by mulitiplying sample rate. /* 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 NOTE: Bitclock division and mulitiplication modes can be safely matched by the
core logic. 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 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 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: 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. BCLK of either MCLK/2 or MCLK/4.
/* codec master */ /* 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, .fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_CBM_CFM,
SND_SOC_DAIDIR_PLAYBACK | SND_SOC_DAIDIR_CAPTURE, SND_SOC_DAI_BFS_DIV, .pcmfmt = SNDRV_PCM_FORMAT_S16_LE,
256, SND_SOC_FSBD(2) | SND_SOC_FSBD(4)}, .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 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. BCLK of either Rate * 32 or Rate * 64.
/* codec master */ /* 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, .fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_CBM_CFM,
SND_SOC_DAIDIR_PLAYBACK | SND_SOC_DAIDIR_CAPTURE, 0, .pcmfmt = SNDRV_PCM_FORMAT_S16_LE,
256, SND_SOC_FSB(32) | SND_SOC_FSB(64)}, .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 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 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 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. as BCLK is rate * 32 or rate * 64.
/* codec master */ /* 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, .fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_CBM_CFM,
SND_SOC_DAIDIR_PLAYBACK | SND_SOC_DAIDIR_CAPTURE, 0, .pcmfmt = SNDRV_PCM_FORMAT_S16_LE,
256, SND_SOC_FSB(32) | SND_SOC_FSB(64)}, .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 */ /* 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, .fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_CBS_CFS,
SND_SOC_DAIDIR_PLAYBACK | SND_SOC_DAIDIR_CAPTURE, 0, .pcmfmt = SNDRV_PCM_FORMAT_S16_LE,
SND_SOC_FS_ALL, SND_SOC_FSB(32) | SND_SOC_FSB(64)}, .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 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 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) SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_96000)
/* codec master @ 128, 192 & 256 FS */ /* 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, .fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_CBM_CFM,
SND_SOC_DAIDIR_PLAYBACK | SND_SOC_DAIDIR_CAPTURE, SND_SOC_DAI_BFS_DIV, .pcmfmt = SNDRV_PCM_FORMAT_S16_LE,
128, CODEC_FSB}, .pcmrate = CODEC_RATES,
.pcmdir = SND_SOC_DAIDIR_PLAYBACK | SND_SOC_DAIDIR_CAPTURE,
.flags = SND_SOC_DAI_BFS_DIV,
.fs = 128,
.bfs = CODEC_FSB,
},
{SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_CBM_CFM, SND_SOC_DAITDM_LRDW(0,0), {
SNDRV_PCM_FORMAT_S16_LE, CODEC_RATES, .fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_CBM_CFM,
SND_SOC_DAIDIR_PLAYBACK | SND_SOC_DAIDIR_CAPTURE, SND_SOC_DAI_BFS_DIV, .pcmfmt = SNDRV_PCM_FORMAT_S16_LE,
192, CODEC_FSB}, .pcmrate = CODEC_RATES,
.pcmdir = SND_SOC_DAIDIR_PLAYBACK | SND_SOC_DAIDIR_CAPTURE,
.flags = SND_SOC_DAI_BFS_DIV,
.fs = 192,
.bfs = CODEC_FSB
},
{SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_CBM_CFM, SND_SOC_DAITDM_LRDW(0,0), {
SNDRV_PCM_FORMAT_S16_LE, CODEC_RATES, .fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_CBM_CFM,
SND_SOC_DAIDIR_PLAYBACK | SND_SOC_DAIDIR_CAPTURE, SND_SOC_DAI_BFS_DIV, .pcmfmt = SNDRV_PCM_FORMAT_S16_LE,
256, CODEC_FSB}, .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 */ /* codec slave */
{SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_CBS_CFS, SND_SOC_DAITDM_LRDW(0,0), {
SNDRV_PCM_FORMAT_S16_LE, CODEC_RATES, .fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_CBS_CFS,
SND_SOC_DAIDIR_PLAYBACK | SND_SOC_DAIDIR_CAPTURE, 0, .pcmfmt = SNDRV_PCM_FORMAT_S16_LE,
SND_SOC_FS_ALL, SND_SOC_FSB_ALL}, .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 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). 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) SNDRV_PCM_FORMAT_S24_3LE | SNDRV_PCM_FORMAT_S24_LE | SNDRV_PCM_FORMAT_S32_LE)
/* codec master */ /* 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, .fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_CBM_CFM,
SND_SOC_DAIDIR_PLAYBACK | SND_SOC_DAIDIR_CAPTURE, SND_SOC_DAI_BFS_DIV, .pcmfmt = SNDRV_PCM_FORMAT_S16_LE,
1536, CODEC_FSB}, .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,
},
{SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_CBM_CFM, SND_SOC_DAITDM_LRDW(0,0), {
SNDRV_PCM_FORMAT_S16_LE, SNDRV_PCM_RATE_44100, .fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_CBM_CFM,
SND_SOC_DAIDIR_PLAYBACK | SND_SOC_DAIDIR_CAPTURE, SND_SOC_DAI_BFS_DIV, .pcmfmt = SNDRV_PCM_FORMAT_S16_LE,
272, CODEC_FSB}, .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,
},
{SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_CBM_CFM, SND_SOC_DAITDM_LRDW(0,0), {
SNDRV_PCM_FORMAT_S16_LE, SNDRV_PCM_RATE_48000, .fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_CBM_CFM,
SND_SOC_DAIDIR_PLAYBACK | SND_SOC_DAIDIR_CAPTURE, SND_SOC_DAI_BFS_DIV, .pcmfmt = SNDRV_PCM_FORMAT_S16_LE,
256, CODEC_FSB}, .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 */ /* codec slave */
{SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_CBS_CFS, SND_SOC_DAITDM_LRDW(0,0), {
SNDRV_PCM_FORMAT_S16_LE, CODEC_RATES, .fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_CBS_CFS,
SND_SOC_DAIDIR_PLAYBACK | SND_SOC_DAIDIR_CAPTURE, 0, .pcmfmt = SNDRV_PCM_FORMAT_S16_LE,
SND_SOC_FS_ALL, SND_SOC_FSB_ALL}, .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). AC97 Codec that does not support VRA (i.e only runs at 48k).
#define AC97_DIR \ #define AC97_DIR \
(SND_SOC_DAIDIR_PLAYBACK | SND_SOC_DAIDIR_CAPTURE) (SND_SOC_DAIDIR_PLAYBACK | SND_SOC_DAIDIR_CAPTURE)
#define AC97_PCM_FORMATS \ #define AC97_PCM_FORMATS \
(SNDRV_PCM_FORMAT_S16_LE | SNDRV_PCM_FORMAT_S18_3LE | \ (SNDRV_PCM_FORMAT_S16_LE | SNDRV_PCM_FORMAT_S18_3LE | \
SNDRV_PCM_FORMAT_S20_3LE) SNDRV_PCM_FORMAT_S20_3LE)
/* AC97 with no VRA */ /* 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. 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_22050 | SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 | \
SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_88200 | SNDRV_PCM_RATE_96000) 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 */ /* 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, .fmt = PXA_I2S_DAIFMT | SND_SOC_DAIFMT_CBS_CFS,
SND_SOC_FSBD(4), 0x48}, .pcmfmt = SNDRV_PCM_FMTBIT_S16_LE,
{PXA_I2S_DAIFMT | SND_SOC_DAIFMT_CBS_CFS, SND_SOC_DAITDM_LRDW(0,0), SNDRV_PCM_FORMAT_S16_LE, .pcmrate = SNDRV_PCM_RATE_8000,
SNDRV_PCM_RATE_11025, PXA_I2S_DIR, SND_SOC_DAI_BFS_DIV, 256, .pcmdir = PXA_I2S_DIR,
SND_SOC_FSBD(4), 0x34}, .flags = SND_SOC_DAI_BFS_DIV,
{PXA_I2S_DAIFMT | SND_SOC_DAIFMT_CBS_CFS, SND_SOC_DAITDM_LRDW(0,0), SNDRV_PCM_FORMAT_S16_LE, .fs = 256,
SNDRV_PCM_RATE_16000, PXA_I2S_DIR, SND_SOC_DAI_BFS_DIV, 256, .bfs = SND_SOC_FSBD(4),
SND_SOC_FSBD(4), 0x24}, .priv = 0x48,
{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}, .fmt = PXA_I2S_DAIFMT | SND_SOC_DAIFMT_CBS_CFS,
{PXA_I2S_DAIFMT | SND_SOC_DAIFMT_CBS_CFS, SND_SOC_DAITDM_LRDW(0,0), SNDRV_PCM_FORMAT_S16_LE, .pcmfmt = SNDRV_PCM_FMTBIT_S16_LE,
SNDRV_PCM_RATE_44100, PXA_I2S_DIR, SND_SOC_DAI_BFS_DIV, 256, .pcmrate = SNDRV_PCM_RATE_11025,
SND_SOC_FSBD(4), 0xd}, .pcmdir = PXA_I2S_DIR,
{PXA_I2S_DAIFMT | SND_SOC_DAIFMT_CBS_CFS, SND_SOC_DAITDM_LRDW(0,0), SNDRV_PCM_FORMAT_S16_LE, .flags = SND_SOC_DAI_BFS_DIV,
SNDRV_PCM_RATE_48000, PXA_I2S_DIR, SND_SOC_DAI_BFS_DIV, 256, .fs = 256,
SND_SOC_FSBD(4), 0xc}, .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 */ /* 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,
},
};

View 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 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 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 BCLK = MCLK / x
@ -36,9 +36,14 @@ BCLK = MCLK / x
BCLK = LRC * x BCLK = LRC * x
or
BCLK = LRC * Channels * Word Size
This relationship depends on the codec or SoC CPU in particular. ASoC can quite 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 easily match BCLK generated by division (SND_SOC_DAI_BFS_DIV) with BCLK by
generates BCLK by multiplication (FSB). multiplication (SND_SOC_DAI_BFS_RATE) or BCLK generated by
Rate * Channels * Word size (RCW or SND_SOC_DAI_BFS_RCW).
ASoC Clocking ASoC Clocking

View File

@ -21,7 +21,7 @@
#include <sound/control.h> #include <sound/control.h>
#include <sound/ac97_codec.h> #include <sound/ac97_codec.h>
#define SND_SOC_VERSION "0.11.8" #define SND_SOC_VERSION "0.12"
/* /*
* Convenience kcontrol builders * Convenience kcontrol builders
@ -141,19 +141,24 @@
/* bit clock dividers */ /* bit clock dividers */
#define SND_SOC_FSBD(x) (1 << (x - 1)) /* ratio mclk:bclk */ #define SND_SOC_FSBD(x) (1 << (x - 1)) /* ratio mclk:bclk */
#define SND_SOC_FSBD_REAL(x) (ffs(x)) #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 */ /* bit clock ratio to (sample rate * channels * word size) */
#define SND_SOC_FSB(x) (1 << ((x - 16) / 16)) #define SND_SOC_FSBW(x) (1 << (x - 1))
#define SND_SOC_FSB_REAL(x) (((ffs(x) - 1) * 16) + 16) #define SND_SOC_FSBW_REAL(x) (ffs(x))
/* all bclk ratios supported */ /* all bclk ratios supported */
#define SND_SOC_FSB_ALL SND_SOC_FSBD_ALL #define SND_SOC_FSB_ALL ~0ULL
/* /*
* DAI hardware flags * DAI hardware flags
*/ */
/* use bfs mclk divider mode, else sample rate ratio */ /* use bfs mclk divider mode (BCLK = MCLK / x) */
#define SND_SOC_DAI_BFS_DIV 0x1 #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 * AC97 codec ID's bitmask
@ -264,7 +269,7 @@ struct snd_soc_dai_mode {
u16 pcmdir:2; /* SND_SOC_HWDIR_* */ u16 pcmdir:2; /* SND_SOC_HWDIR_* */
u16 flags:8; /* hw flags */ u16 flags:8; /* hw flags */
u16 fs; /* mclk to rate divider */ u16 fs; /* mclk to rate divider */
u32 bfs; /* mclk to bclk dividers */ u64 bfs; /* mclk to bclk dividers */
unsigned long priv; /* private mode data */ unsigned long priv; /* private mode data */
}; };

View File

@ -90,32 +90,36 @@ static struct snd_soc_dai_mode wm8731_modes[] = {
.pcmfmt = WM8731_HIFI_BITS, .pcmfmt = WM8731_HIFI_BITS,
.pcmrate = SNDRV_PCM_RATE_8000, .pcmrate = SNDRV_PCM_RATE_8000,
.pcmdir = WM8731_DIR, .pcmdir = WM8731_DIR,
.flags = SND_SOC_DAI_BFS_RATE,
.fs = 1536, .fs = 1536,
.bfs = SND_SOC_FSB(64), .bfs = 64,
}, },
{ {
.fmt = WM8731_DAIFMT | SND_SOC_DAIFMT_CBM_CFM, .fmt = WM8731_DAIFMT | SND_SOC_DAIFMT_CBM_CFM,
.pcmfmt = WM8731_HIFI_BITS, .pcmfmt = WM8731_HIFI_BITS,
.pcmrate = SNDRV_PCM_RATE_8000, .pcmrate = SNDRV_PCM_RATE_8000,
.pcmdir = WM8731_DIR, .pcmdir = WM8731_DIR,
.flags = SND_SOC_DAI_BFS_RATE,
.fs = 2304, .fs = 2304,
.bfs = SND_SOC_FSB(64), .bfs = 64,
}, },
{ {
.fmt = WM8731_DAIFMT | SND_SOC_DAIFMT_CBM_CFM, .fmt = WM8731_DAIFMT | SND_SOC_DAIFMT_CBM_CFM,
.pcmfmt = WM8731_HIFI_BITS, .pcmfmt = WM8731_HIFI_BITS,
.pcmrate = SNDRV_PCM_RATE_8000, .pcmrate = SNDRV_PCM_RATE_8000,
.pcmdir = WM8731_DIR, .pcmdir = WM8731_DIR,
.flags = SND_SOC_DAI_BFS_RATE,
.fs = 1408, .fs = 1408,
.bfs = SND_SOC_FSB(64), .bfs = 64,
}, },
{ {
.fmt = WM8731_DAIFMT | SND_SOC_DAIFMT_CBM_CFM, .fmt = WM8731_DAIFMT | SND_SOC_DAIFMT_CBM_CFM,
.pcmfmt = WM8731_HIFI_BITS, .pcmfmt = WM8731_HIFI_BITS,
.pcmrate = SNDRV_PCM_RATE_8000, .pcmrate = SNDRV_PCM_RATE_8000,
.pcmdir = WM8731_DIR, .pcmdir = WM8731_DIR,
.flags = SND_SOC_DAI_BFS_RATE,
.fs = 2112, .fs = 2112,
.bfs = SND_SOC_FSB(64), .bfs = 64,
}, },
/* 32k */ /* 32k */
@ -124,16 +128,18 @@ static struct snd_soc_dai_mode wm8731_modes[] = {
.pcmfmt = WM8731_HIFI_BITS, .pcmfmt = WM8731_HIFI_BITS,
.pcmrate = SNDRV_PCM_RATE_32000, .pcmrate = SNDRV_PCM_RATE_32000,
.pcmdir = WM8731_DIR, .pcmdir = WM8731_DIR,
.flags = SND_SOC_DAI_BFS_RATE,
.fs = 384, .fs = 384,
.bfs = SND_SOC_FSB(64), .bfs = 64,
}, },
{ {
.fmt = WM8731_DAIFMT | SND_SOC_DAIFMT_CBM_CFM, .fmt = WM8731_DAIFMT | SND_SOC_DAIFMT_CBM_CFM,
.pcmfmt = WM8731_HIFI_BITS, .pcmfmt = WM8731_HIFI_BITS,
.pcmrate = SNDRV_PCM_RATE_32000, .pcmrate = SNDRV_PCM_RATE_32000,
.pcmdir = WM8731_DIR, .pcmdir = WM8731_DIR,
.flags = SND_SOC_DAI_BFS_RATE,
.fs = 576, .fs = 576,
.bfs = SND_SOC_FSB(64), .bfs = 64,
}, },
/* 44.1k & 48k */ /* 44.1k & 48k */
@ -142,16 +148,18 @@ static struct snd_soc_dai_mode wm8731_modes[] = {
.pcmfmt = WM8731_HIFI_BITS, .pcmfmt = WM8731_HIFI_BITS,
.pcmrate = SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000, .pcmrate = SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000,
.pcmdir = WM8731_DIR, .pcmdir = WM8731_DIR,
.flags = SND_SOC_DAI_BFS_RATE,
.fs = 256, .fs = 256,
.bfs = SND_SOC_FSB(64), .bfs = 64,
}, },
{ {
.fmt = WM8731_DAIFMT | SND_SOC_DAIFMT_CBM_CFM, .fmt = WM8731_DAIFMT | SND_SOC_DAIFMT_CBM_CFM,
.pcmfmt = WM8731_HIFI_BITS, .pcmfmt = WM8731_HIFI_BITS,
.pcmrate = SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000, .pcmrate = SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000,
.pcmdir = WM8731_DIR, .pcmdir = WM8731_DIR,
.flags = SND_SOC_DAI_BFS_RATE,
.fs = 384, .fs = 384,
.bfs = SND_SOC_FSB(64), .bfs = 64,
}, },
/* 88.2 & 96k */ /* 88.2 & 96k */
@ -160,17 +168,18 @@ static struct snd_soc_dai_mode wm8731_modes[] = {
.pcmfmt = WM8731_HIFI_BITS, .pcmfmt = WM8731_HIFI_BITS,
.pcmrate = SNDRV_PCM_RATE_88200 | SNDRV_PCM_RATE_96000, .pcmrate = SNDRV_PCM_RATE_88200 | SNDRV_PCM_RATE_96000,
.pcmdir = WM8731_DIR, .pcmdir = WM8731_DIR,
.flags = SND_SOC_DAI_BFS_RATE,
.fs = 128, .fs = 128,
.bfs = SND_SOC_FSB(64), .bfs = 64,
}, },
{ {
.fmt = WM8731_DAIFMT | SND_SOC_DAIFMT_CBM_CFM, .fmt = WM8731_DAIFMT | SND_SOC_DAIFMT_CBM_CFM,
.pcmfmt = WM8731_HIFI_BITS, .pcmfmt = WM8731_HIFI_BITS,
.pcmrate = SNDRV_PCM_RATE_88200 | SNDRV_PCM_RATE_96000, .pcmrate = SNDRV_PCM_RATE_88200 | SNDRV_PCM_RATE_96000,
.pcmdir = WM8731_DIR, .pcmdir = WM8731_DIR,
.flags = SND_SOC_DAI_BFS_RATE,
.fs = 192, .fs = 192,
.bfs = SND_SOC_FSB(64), .bfs = 64,
}, },
/* USB codec frame and clock master modes */ /* USB codec frame and clock master modes */
@ -237,7 +246,7 @@ static struct snd_soc_dai_mode wm8731_modes[] = {
.pcmdir = WM8731_DIR, .pcmdir = WM8731_DIR,
.flags = SND_SOC_DAI_BFS_DIV, .flags = SND_SOC_DAI_BFS_DIV,
.fs = SND_SOC_FS_ALL, .fs = SND_SOC_FS_ALL,
.bfs = SND_SOC_FSBD_ALL, .bfs = SND_SOC_FSB_ALL,
}, },
}; };

View File

@ -343,7 +343,7 @@ static struct snd_soc_dai_mode wm8750_modes[] = {
.pcmdir = WM8750_DIR, .pcmdir = WM8750_DIR,
.flags = SND_SOC_DAI_BFS_DIV, .flags = SND_SOC_DAI_BFS_DIV,
.fs = SND_SOC_FS_ALL, .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) if (coeff_div[i].rate == rate && coeff_div[i].mclk == mclk)
return i; return i;
} }
printk(KERN_ERR "wm8750: could not get coeff for mclk %d @ rate %d\n",
mclk, rate);
return -EINVAL; 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, static unsigned int wm8750_config_sysclk(struct snd_soc_codec_dai *dai,
struct snd_soc_clock_info *info, unsigned int clk) struct snd_soc_clock_info *info, unsigned int clk)
{ {
dai->mclk = 0; dai->mclk = clk;
/* check that the calculated FS and rate actually match a clock from
* the machine driver */
if (info->fs * info->rate == clk)
dai->mclk = clk;
return dai->mclk; return dai->mclk;
} }
@ -859,7 +856,7 @@ static int wm8750_pcm_prepare(struct snd_pcm_substream *substream)
if (i < 0) if (i < 0)
return i; 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 */ /* set master/slave audio interface */
switch (rtd->codec_dai->dai_runtime.fmt & SND_SOC_DAIFMT_CLOCK_MASK) { switch (rtd->codec_dai->dai_runtime.fmt & SND_SOC_DAIFMT_CLOCK_MASK) {

View File

@ -126,7 +126,8 @@ static struct snd_soc_dai_mode pxa2xx_i2s_modes[] = {
.pcmrate = PXA_I2S_RATES, .pcmrate = PXA_I2S_RATES,
.pcmdir = PXA_I2S_DIR, .pcmdir = PXA_I2S_DIR,
.fs = SND_SOC_FS_ALL, .fs = SND_SOC_FS_ALL,
.bfs = SND_SOC_FSB(64), .flags = SND_SOC_DAI_BFS_RATE,
.bfs = 64,
.priv = 0x48, .priv = 0x48,
}, },
}; };

View File

@ -51,6 +51,8 @@
#define dbgc(format, arg...) #define dbgc(format, arg...)
#endif #endif
#define CODEC_CPU(codec, cpu) ((codec << 4) | cpu)
static DEFINE_MUTEX(pcm_mutex); static DEFINE_MUTEX(pcm_mutex);
static DEFINE_MUTEX(io_mutex); static DEFINE_MUTEX(io_mutex);
static struct workqueue_struct *soc_workq; 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 */ /* 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) unsigned int pcmfmt, unsigned int chn)
{ {
int i, j; int i, j;
u16 bfs_ = 0; u64 bfs_ = 0;
int size = snd_pcm_format_physical_width(pcmfmt), min = 0; int size = snd_pcm_format_physical_width(pcmfmt), min = 0;
if (size <= 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 */ /* the minimum bit clock that has enough bandwidth */
min = size * rate * chn; 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) { if ((bfs >> i) & 0x1) {
j = rate * SND_SOC_FSB_REAL(1<<i); j = min * (i + 1);
bfs_ |= SND_SOC_FSBD(mclk/j);
if (j >= min) { dbgc("rcw --> div support mult %d\n",
bfs_ |= SND_SOC_FSBD(mclk/j); SND_SOC_FSBD_REAL(1<<i));
dbgc("mult --> div support mult %d\n",
SND_SOC_FSB_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 */ /* 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) unsigned int pcmfmt, unsigned int chn)
{ {
int i, j; int i, j;
u16 bfs_ = 0; u64 bfs_ = 0;
int size = snd_pcm_format_physical_width(pcmfmt), min = 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 */ /* the minimum bit clock that has enough bandwidth */
min = size * rate * chn; 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) { if ((bfs >> i) & 0x1) {
j = mclk / (SND_SOC_FSBD_REAL(1<<i)); j = mclk / (i + 1);
if (j >= min) { if (j >= min) {
bfs_ |= SND_SOC_FSB(j/rate); bfs_ |= SND_SOC_FSBW(j/min);
dbgc("div --> mult support div %d\n", dbgc("div --> rcw support div %d\n",
SND_SOC_FSBD_REAL(1<<i)); 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_; 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 */ /* Matches codec DAI and SoC CPU DAI hardware parameters */
static int soc_hw_match_params(struct snd_pcm_substream *substream, static int soc_hw_match_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params) 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 *codec_dai_mode = NULL;
struct snd_soc_dai_mode *cpu_dai_mode = NULL; struct snd_soc_dai_mode *cpu_dai_mode = NULL;
struct snd_soc_clock_info clk_info; 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; chn, j, k, cpu_bclk, codec_bclk, pcmrate;
u16 fmt = 0; u16 fmt = 0;
u64 codec_bfs, cpu_bfs;
dbg("asoc: match version %s\n", SND_SOC_VERSION); dbg("asoc: match version %s\n", SND_SOC_VERSION);
clk_info.rate = rate; 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 * used in the codec and cpu DAI modes. We always choose the
* lowest possible clocks to reduce power. * lowest possible clocks to reduce power.
*/ */
if (codec_dai_mode->flags & cpu_dai_mode->flags & switch (CODEC_CPU(codec_dai_mode->flags, cpu_dai_mode->flags)) {
SND_SOC_DAI_BFS_DIV) { case CODEC_CPU(SND_SOC_DAI_BFS_DIV, SND_SOC_DAI_BFS_DIV):
/* cpu & codec bfs dividers */ /* cpu & codec bfs dividers */
rtd->cpu_dai->dai_runtime.bfs = rtd->cpu_dai->dai_runtime.bfs =
rtd->codec_dai->dai_runtime.bfs = rtd->codec_dai->dai_runtime.bfs =
1 << (fls(codec_dai_mode->bfs & cpu_dai_mode->bfs) - 1); 1 << (fls(codec_dai_mode->bfs & cpu_dai_mode->bfs) - 1);
} else if (codec_dai_mode->flags & SND_SOC_DAI_BFS_DIV) { break;
/* normalise bfs codec divider & cpu mult */ case CODEC_CPU(SND_SOC_DAI_BFS_DIV, SND_SOC_DAI_BFS_RCW):
codec_bfs = soc_bfs_div_to_mult(codec_dai_mode->bfs, rate, /* 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); mclk, rtd->codec_dai->dai_runtime.pcmfmt, chn);
rtd->cpu_dai->dai_runtime.bfs = rtd->cpu_dai->dai_runtime.bfs =
1 << (ffs(codec_bfs & cpu_dai_mode->bfs) - 1); 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.pcmfmt, chn);
rtd->codec_dai->dai_runtime.bfs = rtd->codec_dai->dai_runtime.bfs =
1 << (fls(codec_dai_mode->bfs & cpu_bfs) - 1); 1 << (fls(codec_dai_mode->bfs & cpu_bfs) - 1);
} else if (cpu_dai_mode->flags & SND_SOC_DAI_BFS_DIV) { break;
/* normalise bfs codec mult & cpu divider */ case CODEC_CPU(SND_SOC_DAI_BFS_RCW, SND_SOC_DAI_BFS_DIV):
codec_bfs = soc_bfs_mult_to_div(codec_dai_mode->bfs, rate, /* 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); mclk, rtd->codec_dai->dai_runtime.pcmfmt, chn);
rtd->cpu_dai->dai_runtime.bfs = rtd->cpu_dai->dai_runtime.bfs =
1 << (fls(codec_bfs & cpu_dai_mode->bfs) -1); 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.pcmfmt, chn);
rtd->codec_dai->dai_runtime.bfs = rtd->codec_dai->dai_runtime.bfs =
1 << (ffs(codec_dai_mode->bfs & cpu_bfs) -1); 1 << (ffs(codec_dai_mode->bfs & cpu_bfs) -1);
} else { break;
/* codec & cpu bfs rate multipliers */ 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->cpu_dai->dai_runtime.bfs =
rtd->codec_dai->dai_runtime.bfs = rtd->codec_dai->dai_runtime.bfs =
1 << (ffs(codec_dai_mode->bfs & cpu_dai_mode->bfs) -1); 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 */ /* make sure the bit clock speed is acceptable */
if (!rtd->cpu_dai->dai_runtime.bfs || if (!rtd->cpu_dai->dai_runtime.bfs ||
!rtd->codec_dai->dai_runtime.bfs) { !rtd->codec_dai->dai_runtime.bfs) {
dbgc("asoc: DAI[%d:%d] failed to match BFS\n", j, k); 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->cpu_dai->dai_runtime.bfs,
rtd->codec_dai->dai_runtime.bfs); rtd->codec_dai->dai_runtime.bfs);
dbgc("asoc: mclk %d hwfmt %x\n", mclk, fmt); 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", dbg("asoc: codec fs %d mclk %d bfs div %d bclk %d\n",
rtd->codec_dai->dai_runtime.fs, mclk, rtd->codec_dai->dai_runtime.fs, mclk,
SND_SOC_FSBD_REAL(rtd->codec_dai->dai_runtime.bfs), codec_bclk); SND_SOC_FSBD_REAL(rtd->codec_dai->dai_runtime.bfs), codec_bclk);
} else { } else if(rtd->codec_dai->dai_runtime.flags == SND_SOC_DAI_BFS_RATE) {
codec_bclk = params_rate(params) * codec_bclk = params_rate(params) * rtd->codec_dai->dai_runtime.bfs;
SND_SOC_FSB_REAL(rtd->codec_dai->dai_runtime.bfs); dbg("asoc: codec fs %d mclk %d bfs rate mult %llu bclk %d\n",
dbg("asoc: codec fs %d mclk %d bfs mult %d bclk %d\n",
rtd->codec_dai->dai_runtime.fs, mclk, 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) { if (rtd->cpu_dai->dai_runtime.flags == SND_SOC_DAI_BFS_DIV) {
cpu_bclk = (rtd->cpu_dai->dai_runtime.fs * params_rate(params)) / cpu_bclk = (rtd->cpu_dai->dai_runtime.fs * params_rate(params)) /
SND_SOC_FSBD_REAL(rtd->cpu_dai->dai_runtime.bfs); SND_SOC_FSBD_REAL(rtd->cpu_dai->dai_runtime.bfs);
dbg("asoc: cpu fs %d mclk %d bfs div %d bclk %d\n", dbg("asoc: cpu fs %d mclk %d bfs div %d bclk %d\n",
rtd->cpu_dai->dai_runtime.fs, mclk, rtd->cpu_dai->dai_runtime.fs, mclk,
SND_SOC_FSBD_REAL(rtd->cpu_dai->dai_runtime.bfs), cpu_bclk); SND_SOC_FSBD_REAL(rtd->cpu_dai->dai_runtime.bfs), cpu_bclk);
} else { } else if (rtd->cpu_dai->dai_runtime.flags == SND_SOC_DAI_BFS_RATE) {
cpu_bclk = params_rate(params) * cpu_bclk = params_rate(params) * rtd->cpu_dai->dai_runtime.bfs;
SND_SOC_FSB_REAL(rtd->cpu_dai->dai_runtime.bfs); dbg("asoc: cpu fs %d mclk %d bfs rate mult %llu bclk %d\n",
dbg("asoc: cpu fs %d mclk %d bfs mult %d bclk %d\n",
rtd->cpu_dai->dai_runtime.fs, mclk, 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 * 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 * machine sysclock function) is wrong compared with the supported DAI
* modes for the codec or cpu DAI. * modes for the codec or cpu DAI.
*/ */
if (cpu_bclk != codec_bclk){ if (cpu_bclk != codec_bclk && cpu_bclk){
printk(KERN_ERR printk(KERN_ERR
"asoc: codec and cpu bitclocks differ, audio may be wrong speed\n" "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); mutex_lock(&pcm_mutex);
if (platform->pcm_ops->prepare) { if (platform->pcm_ops->prepare) {
ret = platform->pcm_ops->prepare(substream); ret = platform->pcm_ops->prepare(substream);
if (ret < 0) if (ret < 0) {
printk(KERN_ERR "asoc: platform prepare error\n");
goto out; goto out;
}
} }
if (rtd->codec_dai->ops.prepare) { if (rtd->codec_dai->ops.prepare) {
ret = rtd->codec_dai->ops.prepare(substream); ret = rtd->codec_dai->ops.prepare(substream);
if (ret < 0) if (ret < 0) {
printk(KERN_ERR "asoc: codec DAI prepare error\n");
goto out; goto out;
}
} }
if (rtd->cpu_dai->ops.prepare) if (rtd->cpu_dai->ops.prepare)