提交 2a01e5f3 编写于 作者: M Mark Brown

ASoC: Use CODEC as clock master on AT91SAM9G20-EK

This simplifies the driver by removing the need to manually
configure dividers within the CPU and improve audio performance
by ensuring that the optimal phase relationships between the
clocks in the system are maintained.

Note that currently this means that for playback to work the
Output Mixer HiFi switch must be enabled since otherwise CODEC
will not generate the DAC clock.
Signed-off-by: NMark Brown <broonie@opensource.wolfsonmicro.com>
上级 4934482d
...@@ -87,102 +87,17 @@ static int at91sam9g20ek_hw_params(struct snd_pcm_substream *substream, ...@@ -87,102 +87,17 @@ static int at91sam9g20ek_hw_params(struct snd_pcm_substream *substream,
struct snd_soc_pcm_runtime *rtd = substream->private_data; struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct snd_soc_dai *codec_dai = rtd->dai->codec_dai; struct snd_soc_dai *codec_dai = rtd->dai->codec_dai;
struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai; struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai;
struct atmel_ssc_info *ssc_p = cpu_dai->private_data;
struct ssc_device *ssc = ssc_p->ssc;
int ret; int ret;
unsigned int rate;
int cmr_div, period;
if (ssc == NULL) {
printk(KERN_INFO "at91sam9g20ek_hw_params: ssc is NULL!\n");
return -EINVAL;
}
/* set codec DAI configuration */ /* set codec DAI configuration */
ret = snd_soc_dai_set_fmt(codec_dai, SND_SOC_DAIFMT_I2S | ret = snd_soc_dai_set_fmt(codec_dai, SND_SOC_DAIFMT_I2S |
SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBS_CFS); SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBM_CFM);
if (ret < 0) if (ret < 0)
return ret; return ret;
/* set cpu DAI configuration */ /* set cpu DAI configuration */
ret = snd_soc_dai_set_fmt(cpu_dai, SND_SOC_DAIFMT_I2S | ret = snd_soc_dai_set_fmt(cpu_dai, SND_SOC_DAIFMT_I2S |
SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBS_CFS); SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBM_CFM);
if (ret < 0)
return ret;
/*
* The SSC clock dividers depend on the sample rate. The CMR.DIV
* field divides the system master clock MCK to drive the SSC TK
* signal which provides the codec BCLK. The TCMR.PERIOD and
* RCMR.PERIOD fields further divide the BCLK signal to drive
* the SSC TF and RF signals which provide the codec DACLRC and
* ADCLRC clocks.
*
* The dividers were determined through trial and error, where a
* CMR.DIV value is chosen such that the resulting BCLK value is
* divisible, or almost divisible, by (2 * sample rate), and then
* the TCMR.PERIOD or RCMR.PERIOD is BCLK / (2 * sample rate) - 1.
*/
rate = params_rate(params);
switch (rate) {
case 8000:
cmr_div = 55; /* BCLK = 133MHz/(2*55) = 1.209MHz */
period = 74; /* LRC = BCLK/(2*(74+1)) ~= 8060,6Hz */
break;
case 11025:
cmr_div = 67; /* BCLK = 133MHz/(2*60) = 1.108MHz */
period = 45; /* LRC = BCLK/(2*(49+1)) = 11083,3Hz */
break;
case 16000:
cmr_div = 63; /* BCLK = 133MHz/(2*63) = 1.055MHz */
period = 32; /* LRC = BCLK/(2*(32+1)) = 15993,2Hz */
break;
case 22050:
cmr_div = 52; /* BCLK = 133MHz/(2*52) = 1.278MHz */
period = 28; /* LRC = BCLK/(2*(28+1)) = 22049Hz */
break;
case 32000:
cmr_div = 66; /* BCLK = 133MHz/(2*66) = 1.007MHz */
period = 15; /* LRC = BCLK/(2*(15+1)) = 31486,742Hz */
break;
case 44100:
cmr_div = 29; /* BCLK = 133MHz/(2*29) = 2.293MHz */
period = 25; /* LRC = BCLK/(2*(25+1)) = 44098Hz */
break;
case 48000:
cmr_div = 33; /* BCLK = 133MHz/(2*33) = 2.015MHz */
period = 20; /* LRC = BCLK/(2*(20+1)) = 47979,79Hz */
break;
case 88200:
cmr_div = 29; /* BCLK = 133MHz/(2*29) = 2.293MHz */
period = 12; /* LRC = BCLK/(2*(12+1)) = 88196Hz */
break;
case 96000:
cmr_div = 23; /* BCLK = 133MHz/(2*23) = 2.891MHz */
period = 14; /* LRC = BCLK/(2*(14+1)) = 96376Hz */
break;
default:
printk(KERN_WARNING "unsupported rate %d"
" on at91sam9g20ek board\n", rate);
return -EINVAL;
}
/* set the MCK divider for BCLK */
ret = snd_soc_dai_set_clkdiv(cpu_dai, ATMEL_SSC_CMR_DIV, cmr_div);
if (ret < 0)
return ret;
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
/* set the BCLK divider for DACLRC */
ret = snd_soc_dai_set_clkdiv(cpu_dai,
ATMEL_SSC_TCMR_PERIOD, period);
} else {
/* set the BCLK divider for ADCLRC */
ret = snd_soc_dai_set_clkdiv(cpu_dai,
ATMEL_SSC_RCMR_PERIOD, period);
}
if (ret < 0) if (ret < 0)
return ret; return ret;
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册