Commit a4c8ea2d authored by Raffaele Recalcati's avatar Raffaele Recalcati Committed by Mark Brown

ASoC: DaVinci: Added two clocking possibilities to McBSP (I2S)

    Added two clocking options for dm365 McBSP peripheral when used
    with I2S timings, that are SND_SOC_DAIFMT_CBS_CFS (the cpu generates
    clock and frame sync) and SND_SOC_DAIFMT_CBS_CFM (the cpu gets clock
    from external pin and generates frame sync).
    A slave clock management can be important when the external codec needs
    the system clock and the bit clock synchronized (tested with uda1345).
    This patch has been developed against the:
        http://git.kernel.org/pub/scm/linux/kernel/git/khilman/linux-davinci.git
    git tree and has been tested on bmx board (similar to dm365 evm, but using
    uda1345 as external audio codec).
Signed-off-by: default avatarRaffaele Recalcati <raffaele.recalcati@bticino.it>
Signed-off-by: default avatarDavide Bonfanti <davide.bonfanti@bticino.it>
Acked-by: default avatarLiam Girdwood <lrg@slimlogic.co.uk>
Acked-by: default avatarSudhakar Rajashekhara <sudhakar.raj@ti.com>
Signed-off-by: default avatarMark Brown <broonie@opensource.wolfsonmicro.com>
parent 088fbab4
...@@ -26,6 +26,7 @@ ...@@ -26,6 +26,7 @@
#include <mach/asp.h> #include <mach/asp.h>
#include "davinci-pcm.h" #include "davinci-pcm.h"
#include "davinci-i2s.h"
/* /*
...@@ -68,16 +69,21 @@ ...@@ -68,16 +69,21 @@
#define DAVINCI_MCBSP_RCR_RDATDLY(v) ((v) << 16) #define DAVINCI_MCBSP_RCR_RDATDLY(v) ((v) << 16)
#define DAVINCI_MCBSP_RCR_RFIG (1 << 18) #define DAVINCI_MCBSP_RCR_RFIG (1 << 18)
#define DAVINCI_MCBSP_RCR_RWDLEN2(v) ((v) << 21) #define DAVINCI_MCBSP_RCR_RWDLEN2(v) ((v) << 21)
#define DAVINCI_MCBSP_RCR_RFRLEN2(v) ((v) << 24)
#define DAVINCI_MCBSP_RCR_RPHASE BIT(31)
#define DAVINCI_MCBSP_XCR_XWDLEN1(v) ((v) << 5) #define DAVINCI_MCBSP_XCR_XWDLEN1(v) ((v) << 5)
#define DAVINCI_MCBSP_XCR_XFRLEN1(v) ((v) << 8) #define DAVINCI_MCBSP_XCR_XFRLEN1(v) ((v) << 8)
#define DAVINCI_MCBSP_XCR_XDATDLY(v) ((v) << 16) #define DAVINCI_MCBSP_XCR_XDATDLY(v) ((v) << 16)
#define DAVINCI_MCBSP_XCR_XFIG (1 << 18) #define DAVINCI_MCBSP_XCR_XFIG (1 << 18)
#define DAVINCI_MCBSP_XCR_XWDLEN2(v) ((v) << 21) #define DAVINCI_MCBSP_XCR_XWDLEN2(v) ((v) << 21)
#define DAVINCI_MCBSP_XCR_XFRLEN2(v) ((v) << 24)
#define DAVINCI_MCBSP_XCR_XPHASE BIT(31)
#define DAVINCI_MCBSP_SRGR_FWID(v) ((v) << 8) #define DAVINCI_MCBSP_SRGR_FWID(v) ((v) << 8)
#define DAVINCI_MCBSP_SRGR_FPER(v) ((v) << 16) #define DAVINCI_MCBSP_SRGR_FPER(v) ((v) << 16)
#define DAVINCI_MCBSP_SRGR_FSGM (1 << 28) #define DAVINCI_MCBSP_SRGR_FSGM (1 << 28)
#define DAVINCI_MCBSP_SRGR_CLKSM BIT(29)
#define DAVINCI_MCBSP_PCR_CLKRP (1 << 0) #define DAVINCI_MCBSP_PCR_CLKRP (1 << 0)
#define DAVINCI_MCBSP_PCR_CLKXP (1 << 1) #define DAVINCI_MCBSP_PCR_CLKXP (1 << 1)
...@@ -144,6 +150,9 @@ struct davinci_mcbsp_dev { ...@@ -144,6 +150,9 @@ struct davinci_mcbsp_dev {
* won't end up being swapped because of the underrun. * won't end up being swapped because of the underrun.
*/ */
unsigned enable_channel_combine:1; unsigned enable_channel_combine:1;
unsigned int fmt;
int clk_div;
}; };
static inline void davinci_mcbsp_write_reg(struct davinci_mcbsp_dev *dev, static inline void davinci_mcbsp_write_reg(struct davinci_mcbsp_dev *dev,
...@@ -254,10 +263,12 @@ static int davinci_i2s_set_dai_fmt(struct snd_soc_dai *cpu_dai, ...@@ -254,10 +263,12 @@ static int davinci_i2s_set_dai_fmt(struct snd_soc_dai *cpu_dai,
struct davinci_mcbsp_dev *dev = cpu_dai->private_data; struct davinci_mcbsp_dev *dev = cpu_dai->private_data;
unsigned int pcr; unsigned int pcr;
unsigned int srgr; unsigned int srgr;
/* Attention srgr is updated by hw_params! */
srgr = DAVINCI_MCBSP_SRGR_FSGM | srgr = DAVINCI_MCBSP_SRGR_FSGM |
DAVINCI_MCBSP_SRGR_FPER(DEFAULT_BITPERSAMPLE * 2 - 1) | DAVINCI_MCBSP_SRGR_FPER(DEFAULT_BITPERSAMPLE * 2 - 1) |
DAVINCI_MCBSP_SRGR_FWID(DEFAULT_BITPERSAMPLE - 1); DAVINCI_MCBSP_SRGR_FWID(DEFAULT_BITPERSAMPLE - 1);
dev->fmt = fmt;
/* set master/slave audio interface */ /* set master/slave audio interface */
switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
case SND_SOC_DAIFMT_CBS_CFS: case SND_SOC_DAIFMT_CBS_CFS:
...@@ -372,6 +383,18 @@ static int davinci_i2s_set_dai_fmt(struct snd_soc_dai *cpu_dai, ...@@ -372,6 +383,18 @@ static int davinci_i2s_set_dai_fmt(struct snd_soc_dai *cpu_dai,
return 0; return 0;
} }
static int davinci_i2s_dai_set_clkdiv(struct snd_soc_dai *cpu_dai,
int div_id, int div)
{
struct davinci_mcbsp_dev *dev = cpu_dai->private_data;
if (div_id != DAVINCI_MCBSP_CLKGDV)
return -ENODEV;
dev->clk_div = div;
return 0;
}
static int davinci_i2s_hw_params(struct snd_pcm_substream *substream, static int davinci_i2s_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params, struct snd_pcm_hw_params *params,
struct snd_soc_dai *dai) struct snd_soc_dai *dai)
...@@ -380,8 +403,8 @@ static int davinci_i2s_hw_params(struct snd_pcm_substream *substream, ...@@ -380,8 +403,8 @@ static int davinci_i2s_hw_params(struct snd_pcm_substream *substream,
struct davinci_pcm_dma_params *dma_params = struct davinci_pcm_dma_params *dma_params =
&dev->dma_params[substream->stream]; &dev->dma_params[substream->stream];
struct snd_interval *i = NULL; struct snd_interval *i = NULL;
int mcbsp_word_length; int mcbsp_word_length, master;
unsigned int rcr, xcr, srgr; unsigned int rcr, xcr, srgr, clk_div, freq, framesize;
u32 spcr; u32 spcr;
snd_pcm_format_t fmt; snd_pcm_format_t fmt;
unsigned element_cnt = 1; unsigned element_cnt = 1;
...@@ -396,12 +419,47 @@ static int davinci_i2s_hw_params(struct snd_pcm_substream *substream, ...@@ -396,12 +419,47 @@ static int davinci_i2s_hw_params(struct snd_pcm_substream *substream,
davinci_mcbsp_write_reg(dev, DAVINCI_MCBSP_SPCR_REG, spcr); davinci_mcbsp_write_reg(dev, DAVINCI_MCBSP_SPCR_REG, spcr);
} }
master = dev->fmt & SND_SOC_DAIFMT_MASTER_MASK;
fmt = params_format(params);
mcbsp_word_length = asp_word_length[fmt];
switch (master) {
case SND_SOC_DAIFMT_CBS_CFS:
freq = clk_get_rate(dev->clk);
srgr = DAVINCI_MCBSP_SRGR_FSGM |
DAVINCI_MCBSP_SRGR_CLKSM;
srgr |= DAVINCI_MCBSP_SRGR_FWID(mcbsp_word_length *
8 - 1);
/* symmetric waveforms */
clk_div = freq / (mcbsp_word_length * 16) /
params->rate_num * params->rate_den;
srgr |= DAVINCI_MCBSP_SRGR_FPER(mcbsp_word_length *
16 - 1);
clk_div &= 0xFF;
srgr |= clk_div;
break;
case SND_SOC_DAIFMT_CBM_CFS:
srgr = DAVINCI_MCBSP_SRGR_FSGM;
clk_div = dev->clk_div - 1;
srgr |= DAVINCI_MCBSP_SRGR_FWID(mcbsp_word_length * 8 - 1);
srgr |= DAVINCI_MCBSP_SRGR_FPER(mcbsp_word_length * 16 - 1);
clk_div &= 0xFF;
srgr |= clk_div;
break;
case SND_SOC_DAIFMT_CBM_CFM:
/* Clock and frame sync given from external sources */
i = hw_param_interval(params, SNDRV_PCM_HW_PARAM_SAMPLE_BITS); i = hw_param_interval(params, SNDRV_PCM_HW_PARAM_SAMPLE_BITS);
srgr = DAVINCI_MCBSP_SRGR_FSGM; srgr = DAVINCI_MCBSP_SRGR_FSGM;
srgr |= DAVINCI_MCBSP_SRGR_FWID(snd_interval_value(i) - 1); srgr |= DAVINCI_MCBSP_SRGR_FWID(snd_interval_value(i) - 1);
pr_debug("%s - %d FWID set: re-read srgr = %X\n",
__func__, __LINE__, snd_interval_value(i) - 1);
i = hw_param_interval(params, SNDRV_PCM_HW_PARAM_FRAME_BITS); i = hw_param_interval(params, SNDRV_PCM_HW_PARAM_FRAME_BITS);
srgr |= DAVINCI_MCBSP_SRGR_FPER(snd_interval_value(i) - 1); srgr |= DAVINCI_MCBSP_SRGR_FPER(snd_interval_value(i) - 1);
break;
default:
return -EINVAL;
}
davinci_mcbsp_write_reg(dev, DAVINCI_MCBSP_SRGR_REG, srgr); davinci_mcbsp_write_reg(dev, DAVINCI_MCBSP_SRGR_REG, srgr);
rcr = DAVINCI_MCBSP_RCR_RFIG; rcr = DAVINCI_MCBSP_RCR_RFIG;
...@@ -426,12 +484,41 @@ static int davinci_i2s_hw_params(struct snd_pcm_substream *substream, ...@@ -426,12 +484,41 @@ static int davinci_i2s_hw_params(struct snd_pcm_substream *substream,
element_cnt = 1; element_cnt = 1;
fmt = double_fmt[fmt]; fmt = double_fmt[fmt];
} }
switch (master) {
case SND_SOC_DAIFMT_CBS_CFS:
case SND_SOC_DAIFMT_CBS_CFM:
rcr |= DAVINCI_MCBSP_RCR_RFRLEN2(0);
xcr |= DAVINCI_MCBSP_XCR_XFRLEN2(0);
rcr |= DAVINCI_MCBSP_RCR_RPHASE;
xcr |= DAVINCI_MCBSP_XCR_XPHASE;
break;
case SND_SOC_DAIFMT_CBM_CFM:
case SND_SOC_DAIFMT_CBM_CFS:
rcr |= DAVINCI_MCBSP_RCR_RFRLEN2(element_cnt - 1);
xcr |= DAVINCI_MCBSP_XCR_XFRLEN2(element_cnt - 1);
break;
default:
return -EINVAL;
}
} }
dma_params->acnt = dma_params->data_type = data_type[fmt]; dma_params->acnt = dma_params->data_type = data_type[fmt];
dma_params->fifo_level = 0; dma_params->fifo_level = 0;
mcbsp_word_length = asp_word_length[fmt]; mcbsp_word_length = asp_word_length[fmt];
switch (master) {
case SND_SOC_DAIFMT_CBS_CFS:
case SND_SOC_DAIFMT_CBS_CFM:
rcr |= DAVINCI_MCBSP_RCR_RFRLEN1(0);
xcr |= DAVINCI_MCBSP_XCR_XFRLEN1(0);
break;
case SND_SOC_DAIFMT_CBM_CFM:
case SND_SOC_DAIFMT_CBM_CFS:
rcr |= DAVINCI_MCBSP_RCR_RFRLEN1(element_cnt - 1); rcr |= DAVINCI_MCBSP_RCR_RFRLEN1(element_cnt - 1);
xcr |= DAVINCI_MCBSP_XCR_XFRLEN1(element_cnt - 1); xcr |= DAVINCI_MCBSP_XCR_XFRLEN1(element_cnt - 1);
break;
default:
return -EINVAL;
}
rcr |= DAVINCI_MCBSP_RCR_RWDLEN1(mcbsp_word_length) | rcr |= DAVINCI_MCBSP_RCR_RWDLEN1(mcbsp_word_length) |
DAVINCI_MCBSP_RCR_RWDLEN2(mcbsp_word_length); DAVINCI_MCBSP_RCR_RWDLEN2(mcbsp_word_length);
...@@ -442,6 +529,10 @@ static int davinci_i2s_hw_params(struct snd_pcm_substream *substream, ...@@ -442,6 +529,10 @@ static int davinci_i2s_hw_params(struct snd_pcm_substream *substream,
davinci_mcbsp_write_reg(dev, DAVINCI_MCBSP_XCR_REG, xcr); davinci_mcbsp_write_reg(dev, DAVINCI_MCBSP_XCR_REG, xcr);
else else
davinci_mcbsp_write_reg(dev, DAVINCI_MCBSP_RCR_REG, rcr); davinci_mcbsp_write_reg(dev, DAVINCI_MCBSP_RCR_REG, rcr);
pr_debug("%s - %d srgr=%X\n", __func__, __LINE__, srgr);
pr_debug("%s - %d xcr=%X\n", __func__, __LINE__, xcr);
pr_debug("%s - %d rcr=%X\n", __func__, __LINE__, rcr);
return 0; return 0;
} }
...@@ -500,6 +591,7 @@ static struct snd_soc_dai_ops davinci_i2s_dai_ops = { ...@@ -500,6 +591,7 @@ static struct snd_soc_dai_ops davinci_i2s_dai_ops = {
.trigger = davinci_i2s_trigger, .trigger = davinci_i2s_trigger,
.hw_params = davinci_i2s_hw_params, .hw_params = davinci_i2s_hw_params,
.set_fmt = davinci_i2s_set_dai_fmt, .set_fmt = davinci_i2s_set_dai_fmt,
.set_clkdiv = davinci_i2s_dai_set_clkdiv,
}; };
......
...@@ -12,6 +12,11 @@ ...@@ -12,6 +12,11 @@
#ifndef _DAVINCI_I2S_H #ifndef _DAVINCI_I2S_H
#define _DAVINCI_I2S_H #define _DAVINCI_I2S_H
/* McBSP dividers */
enum davinci_mcbsp_div {
DAVINCI_MCBSP_CLKGDV, /* Sample rate generator divider */
};
extern struct snd_soc_dai davinci_i2s_dai; extern struct snd_soc_dai davinci_i2s_dai;
#endif #endif
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