2006-10-06 18:34:51 +02:00
|
|
|
Audio Clocking
|
|
|
|
==============
|
|
|
|
|
|
|
|
This text describes the audio clocking terms in ASoC and digital audio in
|
|
|
|
general. Note: Audio clocking can be complex !
|
|
|
|
|
|
|
|
|
|
|
|
Master Clock
|
|
|
|
------------
|
|
|
|
|
|
|
|
Every audio subsystem is driven by a master clock (sometimes refered to as MCLK
|
|
|
|
or SYSCLK). This audio master clock can be derived from a number of sources
|
|
|
|
(e.g. crystal, PLL, CPU clock) and is responsible for producing the correct
|
|
|
|
audio playback and capture sample rates.
|
|
|
|
|
|
|
|
Some master clocks (e.g. PLL's and CPU based clocks) are configuarble in that
|
|
|
|
their speed can be altered by software (depending on the system use and to save
|
|
|
|
power). Other master clocks are fixed at at set frequency (i.e. crystals).
|
|
|
|
|
|
|
|
|
|
|
|
DAI Clocks
|
|
|
|
----------
|
|
|
|
The Digital Audio Interface is usually driven by a Bit Clock (often referred to
|
|
|
|
as BCLK). This clock is used to drive the digital audio data across the link
|
|
|
|
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
|
2006-10-19 20:35:56 +02:00
|
|
|
runs at exactly the sample rate (LRC = Rate).
|
2006-10-06 18:34:51 +02:00
|
|
|
|
2006-10-19 20:35:56 +02:00
|
|
|
Bit Clock can be generated as follows:-
|
2006-10-06 18:34:51 +02:00
|
|
|
|
|
|
|
BCLK = MCLK / x
|
|
|
|
|
|
|
|
or
|
|
|
|
|
|
|
|
BCLK = LRC * x
|
|
|
|
|
2006-10-19 20:35:56 +02:00
|
|
|
or
|
|
|
|
|
|
|
|
BCLK = LRC * Channels * Word Size
|
|
|
|
|
2006-10-06 18:34:51 +02:00
|
|
|
This relationship depends on the codec or SoC CPU in particular. ASoC can quite
|
2006-10-19 20:35:56 +02:00
|
|
|
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).
|
2006-10-06 18:34:51 +02:00
|
|
|
|
|
|
|
|
|
|
|
ASoC Clocking
|
|
|
|
-------------
|
|
|
|
|
|
|
|
The ASoC core determines the clocking for each particular configuration at
|
|
|
|
runtime. This is to allow for dynamic audio clocking wereby the audio clock is
|
|
|
|
variable and depends on the system state or device usage scenario. i.e. a voice
|
|
|
|
call requires slower clocks (and hence less power) than MP3 playback.
|
|
|
|
|
|
|
|
ASoC will call the config_sysclock() function for the target machine during the
|
|
|
|
audio parameters configuration. The function is responsible for then clocking
|
|
|
|
the machine audio subsytem and returning the audio clock speed to the core.
|
|
|
|
This function should also call the codec and cpu DAI clock_config() functions
|
|
|
|
to configure their respective internal clocking if required.
|
|
|
|
|
|
|
|
|
|
|
|
ASoC Clocking Control Flow
|
|
|
|
--------------------------
|
|
|
|
|
|
|
|
The ASoC core will call the machine drivers config_sysclock() when most of the
|
|
|
|
DAI capabilities are known. The machine driver is then responsible for calling
|
|
|
|
the codec and/or CPU DAI drivers with the selected capabilities and the current
|
|
|
|
MCLK. Note that the machine driver is also resonsible for setting the MCLK (and
|
|
|
|
enabling it).
|
|
|
|
|
|
|
|
(1) Match Codec and CPU DAI capabilities. At this point we have
|
|
|
|
matched the majority of the DAI fields and now need to make sure this
|
|
|
|
mode is currently clockable.
|
|
|
|
|
|
|
|
(2) machine->config_sysclk() is now called with the matched DAI FS, sample
|
|
|
|
rate and BCLK master. This function then gets/sets the current audio
|
|
|
|
clock (depening on usage) and calls the codec and CPUI DAI drivers with
|
|
|
|
the FS, rate, BCLK master and MCLK.
|
|
|
|
|
|
|
|
(3) Codec/CPU DAI config_sysclock(). This function checks that the FS, rate,
|
|
|
|
BCLK master and MCLK are acceptable for the codec or CPU DAI. It also
|
|
|
|
sets the DAI internal state to work with said clocks.
|
|
|
|
|
|
|
|
The config_sysclk() functions for CPU, codec and machine should return the MCLK
|
|
|
|
on success and 0 on failure.
|
|
|
|
|
|
|
|
|
|
|
|
Examples (b = BCLK, l = LRC)
|
|
|
|
============================
|
|
|
|
|
|
|
|
Example 1
|
|
|
|
---------
|
|
|
|
|
|
|
|
Simple codec that only runs at 48k @ 256FS in master mode.
|
|
|
|
|
|
|
|
CPU only runs as slave DAI, however it generates a variable MCLK.
|
|
|
|
|
|
|
|
-------- ---------
|
|
|
|
| | <----mclk--- | |
|
|
|
|
| Codec |b -----------> | CPU |
|
|
|
|
| |l -----------> | |
|
|
|
|
| | | |
|
|
|
|
-------- ---------
|
|
|
|
|
|
|
|
The codec driver has the following config_sysclock()
|
|
|
|
|
|
|
|
static unsigned int config_sysclk(struct snd_soc_codec_dai *dai,
|
|
|
|
struct snd_soc_clock_info *info, unsigned int clk)
|
|
|
|
{
|
|
|
|
/* make sure clock is 256 * rate */
|
|
|
|
if(info->rate << 8 == clk) {
|
|
|
|
dai->mclk = clk;
|
|
|
|
return clk;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
The CPU I2S DAI driver has the following config_sysclk()
|
|
|
|
|
|
|
|
static unsigned int config_sysclk(struct snd_soc_codec_dai *dai,
|
|
|
|
struct snd_soc_clock_info *info, unsigned int clk)
|
|
|
|
{
|
|
|
|
/* can we support this clk */
|
|
|
|
if(set_audio_clk(clk) < 0)
|
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
dai->mclk = clk;
|
|
|
|
return dai->clk;
|
|
|
|
}
|
|
|
|
|
|
|
|
The machine driver config_sysclk() in this example is as follows:-
|
|
|
|
|
|
|
|
unsigned int machine_config_sysclk(struct snd_soc_pcm_runtime *rtd,
|
|
|
|
struct snd_soc_clock_info *info)
|
|
|
|
{
|
|
|
|
int clk = info->rate * info->fs;
|
|
|
|
|
|
|
|
/* check that CPU can deliver clock */
|
|
|
|
if(rtd->cpu_dai->config_sysclk(rtd->cpu_dai, info, clk) < 0)
|
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
/* can codec work with this clock */
|
|
|
|
return rtd->codec_dai->config_sysclk(rtd->codec_dai, info, clk);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
Example 2
|
|
|
|
---------
|
|
|
|
|
|
|
|
Codec that can master at 8k and 48k at various FS (and hence supports a fixed
|
|
|
|
set of input MCLK's) and can also be slave at various FS .
|
|
|
|
|
|
|
|
The CPU can master at 8k and 48k @256 FS and can be slave at any FS.
|
|
|
|
|
|
|
|
MCLK is a 12.288MHz crystal on this machine.
|
|
|
|
|
|
|
|
-------- ---------
|
|
|
|
| | <---xtal---> | |
|
|
|
|
| Codec |b <----------> | CPU |
|
|
|
|
| |l <----------> | |
|
|
|
|
| | | |
|
|
|
|
-------- ---------
|
|
|
|
|
|
|
|
|
|
|
|
The codec driver has the following config_sysclock()
|
|
|
|
|
|
|
|
/* supported input clocks */
|
|
|
|
const static int hifi_clks[] = {11289600, 12000000, 12288000,
|
|
|
|
16934400, 18432000};
|
|
|
|
|
|
|
|
static unsigned int config_hsysclk(struct snd_soc_codec_dai *dai,
|
|
|
|
struct snd_soc_clock_info *info, unsigned int clk)
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
|
|
|
|
/* is clk supported */
|
|
|
|
for(i = 0; i < ARRAY_SIZE(hifi_clks); i++) {
|
|
|
|
if(clk == hifi_clks[i]) {
|
|
|
|
dai->mclk = clk;
|
|
|
|
return clk;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* this clk is not supported */
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
The CPU I2S DAI driver has the following config_sysclk()
|
|
|
|
|
|
|
|
static unsigned int config_sysclk(struct snd_soc_codec_dai *dai,
|
|
|
|
struct snd_soc_clock_info *info, unsigned int clk)
|
|
|
|
{
|
|
|
|
/* are we master or slave */
|
|
|
|
if (info->bclk_master &
|
|
|
|
(SND_SOC_DAIFMT_CBM_CFM | SND_SOC_DAIFMT_CBM_CFS)) {
|
|
|
|
|
|
|
|
/* we can only master @ 256FS */
|
|
|
|
if(info->rate << 8 == clk) {
|
|
|
|
dai->mclk = clk;
|
|
|
|
return dai->mclk;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
/* slave we can run at any FS */
|
|
|
|
dai->mclk = clk;
|
|
|
|
return dai->mclk;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* not supported */
|
|
|
|
return dai->clk;
|
|
|
|
}
|
|
|
|
|
|
|
|
The machine driver config_sysclk() in this example is as follows:-
|
|
|
|
|
|
|
|
unsigned int machine_config_sysclk(struct snd_soc_pcm_runtime *rtd,
|
|
|
|
struct snd_soc_clock_info *info)
|
|
|
|
{
|
|
|
|
int clk = 12288000; /* 12.288MHz */
|
|
|
|
|
|
|
|
/* who's driving the link */
|
|
|
|
if (info->bclk_master &
|
|
|
|
(SND_SOC_DAIFMT_CBM_CFM | SND_SOC_DAIFMT_CBM_CFS)) {
|
|
|
|
/* codec master */
|
|
|
|
|
|
|
|
/* check that CPU can work with clock */
|
|
|
|
if(rtd->cpu_dai->config_sysclk(rtd->cpu_dai, info, clk) < 0)
|
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
/* can codec work with this clock */
|
|
|
|
return rtd->codec_dai->config_sysclk(rtd->codec_dai, info, clk);
|
|
|
|
} else {
|
|
|
|
/* cpu master */
|
|
|
|
|
|
|
|
/* check that codec can work with clock */
|
|
|
|
if(rtd->codec_dai->config_sysclk(rtd->codec_dai, info, clk) < 0)
|
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
/* can CPU work with this clock */
|
|
|
|
return rtd->cpu_dai->config_sysclk(rtd->cpu_dai, info, clk);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Example 3
|
|
|
|
---------
|
|
|
|
|
|
|
|
Codec that masters at 8k ... 48k @256 FS. Codec can also be slave and
|
|
|
|
doesn't care about FS. The codec has an internal PLL and dividers to generate
|
|
|
|
the necessary internal clocks (for 256FS).
|
|
|
|
|
|
|
|
CPU can only be slave and doesn't care about FS.
|
|
|
|
|
|
|
|
MCLK is a non controllable 13MHz clock from the CPU.
|
|
|
|
|
|
|
|
|
|
|
|
-------- ---------
|
|
|
|
| | <----mclk--- | |
|
|
|
|
| Codec |b <----------> | CPU |
|
|
|
|
| |l <----------> | |
|
|
|
|
| | | |
|
|
|
|
-------- ---------
|
|
|
|
|
|
|
|
The codec driver has the following config_sysclock()
|
|
|
|
|
|
|
|
/* valid PCM clock dividers * 2 */
|
|
|
|
static int pcm_divs[] = {2, 6, 11, 4, 8, 12, 16};
|
|
|
|
|
|
|
|
static unsigned int config_vsysclk(struct snd_soc_codec_dai *dai,
|
|
|
|
struct snd_soc_clock_info *info, unsigned int clk)
|
|
|
|
{
|
|
|
|
int i, j, best_clk = info->fs * info->rate;
|
|
|
|
|
|
|
|
/* can we run at this clk without the PLL ? */
|
|
|
|
for (i = 0; i < ARRAY_SIZE(pcm_divs); i++) {
|
|
|
|
if ((best_clk >> 1) * pcm_divs[i] == clk) {
|
|
|
|
dai->pll_in = 0;
|
|
|
|
dai->clk_div = pcm_divs[i];
|
|
|
|
dai->mclk = best_clk;
|
|
|
|
return dai->mclk;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* now check for PLL support */
|
|
|
|
for (i = 0; i < ARRAY_SIZE(pll_div); i++) {
|
|
|
|
if (pll_div[i].pll_in == clk) {
|
|
|
|
for (j = 0; j < ARRAY_SIZE(pcm_divs); j++) {
|
|
|
|
if (pll_div[i].pll_out == pcm_divs[j] * (best_clk >> 1)) {
|
|
|
|
dai->pll_in = clk;
|
|
|
|
dai->pll_out = pll_div[i].pll_out;
|
|
|
|
dai->clk_div = pcm_divs[j];
|
|
|
|
dai->mclk = best_clk;
|
|
|
|
return dai->mclk;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* this clk is not supported */
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
The CPU I2S DAI driver has the does not need a config_sysclk() as it can slave
|
|
|
|
at any FS.
|
|
|
|
|
|
|
|
unsigned int config_sysclk(struct snd_soc_pcm_runtime *rtd,
|
|
|
|
struct snd_soc_clock_info *info)
|
|
|
|
{
|
|
|
|
/* codec has pll that generates mclk from 13MHz xtal */
|
|
|
|
return rtd->codec_dai->config_sysclk(rtd->codec_dai, info, 13000000);
|
|
|
|
}
|