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
......@@ -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,
},
};
......@@ -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
......
......@@ -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
......@@ -141,19 +141,24 @@
/* 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 */
};
......
......@@ -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,
},
};
......
......@@ -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) {
......
......@@ -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,
},
};
......
......@@ -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 @@ static int soc_hw_match_params(struct snd_pcm_substream *substream,
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 @@ static int soc_hw_match_params(struct snd_pcm_substream *substream,
* 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)
......
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