Commit a71a468a authored by Liam Girdwood's avatar Liam Girdwood Committed by Jaroslav Kysela

[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: default avatarLiam Girdwood <lg@opensource.wolfsonmicro.com>
Signed-off-by: default avatarTakashi Iwai <tiwai@suse.de>
Signed-off-by: default avatarJaroslav Kysela <perex@suse.cz>
parent 543a0fbe
This diff is collapsed.
...@@ -26,9 +26,9 @@ between the codec and CPU. ...@@ -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 ...@@ -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
......
...@@ -21,7 +21,7 @@ ...@@ -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 @@ ...@@ -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 { ...@@ -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 */
}; };
......
...@@ -90,32 +90,36 @@ static struct snd_soc_dai_mode wm8731_modes[] = { ...@@ -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[] = { ...@@ -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[] = { ...@@ -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[] = { ...@@ -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[] = { ...@@ -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,
}, },
}; };
......
...@@ -343,7 +343,7 @@ static struct snd_soc_dai_mode wm8750_modes[] = { ...@@ -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) ...@@ -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) ...@@ -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) ...@@ -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) {
......
...@@ -126,7 +126,8 @@ static struct snd_soc_dai_mode pxa2xx_i2s_modes[] = { ...@@ -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,
}, },
}; };
......
This diff is collapsed.
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment