From 6988a3a9e0545a7bc5856a6886573915dad85370 Mon Sep 17 00:00:00 2001 From: Wallace Wang Date: Fri, 5 Jun 2009 18:13:51 +0800 Subject: ENGR00096014-2 ASRC-SSI per_2_per transfer Add ASRC-SSI p2p transfer support Signed-off-by: Wallace Wang --- sound/soc/codecs/sgtl5000.c | 23 ++++-- sound/soc/codecs/sgtl5000.h | 3 + sound/soc/imx/imx-3stack-sgtl5000.c | 153 +++++++++++++++++++++++++++++++++++- sound/soc/imx/imx-pcm.c | 26 +++++- 4 files changed, 194 insertions(+), 11 deletions(-) (limited to 'sound') diff --git a/sound/soc/codecs/sgtl5000.c b/sound/soc/codecs/sgtl5000.c index 2618ae506763..94bcaa7bd554 100644 --- a/sound/soc/codecs/sgtl5000.c +++ b/sound/soc/codecs/sgtl5000.c @@ -29,6 +29,7 @@ struct sgtl5000_priv { int master; int fmt; int rev; + int lrclk; }; static int sgtl5000_set_bias_level(struct snd_soc_codec *codec, @@ -482,8 +483,16 @@ static int sgtl5000_set_dai_sysclk(struct snd_soc_dai *codec_dai, struct snd_soc_codec *codec = codec_dai->codec; struct sgtl5000_priv *sgtl5000 = codec->private_data; - sgtl5000->sysclk = freq; - + switch (clk_id) { + case SGTL5000_SYSCLK: + sgtl5000->sysclk = freq; + break; + case SGTL5000_LRCLK: + sgtl5000->lrclk = freq; + break; + default: + return -EINVAL; + } return 0; } @@ -514,7 +523,6 @@ static int sgtl5000_pcm_hw_params(struct snd_pcm_substream *substream, struct snd_soc_device *socdev = rtd->socdev; struct snd_soc_codec *codec = socdev->codec; struct sgtl5000_priv *sgtl5000 = codec->private_data; - int fs = params_rate(params); int channels = params_channels(params); int clk_ctl = 0; int pll_ctl = 0; @@ -537,7 +545,7 @@ static int sgtl5000_pcm_hw_params(struct snd_pcm_substream *substream, sgtl5000_write(codec, SGTL5000_CHIP_ANA_TEST2, reg); } - switch (fs) { + switch (sgtl5000->lrclk) { case 32000: clk_ctl |= SGTL5000_SYS_FS_32k << SGTL5000_SYS_FS_SHIFT; break; @@ -551,7 +559,8 @@ static int sgtl5000_pcm_hw_params(struct snd_pcm_substream *substream, clk_ctl |= SGTL5000_SYS_FS_96k << SGTL5000_SYS_FS_SHIFT; break; default: - pr_err("%s: sample rate %d not supported\n", __func__, fs); + pr_err("%s: sample rate %d not supported\n", __func__, + sgtl5000->lrclk); return -EFAULT; } @@ -583,7 +592,7 @@ static int sgtl5000_pcm_hw_params(struct snd_pcm_substream *substream, div2 = 0; in = sgtl5000->sysclk; } - if (fs == 44100) + if (sgtl5000->lrclk == 44100) out = 180633600; else out = 196608000; @@ -627,7 +636,7 @@ static int sgtl5000_pcm_hw_params(struct snd_pcm_substream *substream, } pr_debug("fs=%d,clk_ctl=%d,pll_ctl=%d,i2s_ctl=%d,div2=%d\n", - fs, clk_ctl, pll_ctl, i2s_ctl, div2); + sgtl5000->lrclk, clk_ctl, pll_ctl, i2s_ctl, div2); if ((clk_ctl & SGTL5000_MCLK_FREQ_MASK) == SGTL5000_MCLK_FREQ_PLL) { sgtl5000_write(codec, SGTL5000_CHIP_PLL_CTRL, pll_ctl); diff --git a/sound/soc/codecs/sgtl5000.h b/sound/soc/codecs/sgtl5000.h index 692147442454..d70552f23994 100644 --- a/sound/soc/codecs/sgtl5000.h +++ b/sound/soc/codecs/sgtl5000.h @@ -393,6 +393,9 @@ extern struct snd_soc_codec_device soc_codec_dev_sgtl5000; #define SGTL5000_DAP_MIX_EN 0x0010 #define SGTL5000_DAP_EN 0x0001 +#define SGTL5000_SYSCLK 0x00 +#define SGTL5000_LRCLK 0x01 + struct sgtl5000_platform_data { int vddio; /* voltage of VDDIO (mv) */ int vdda; /* voltage of vdda (mv) */ diff --git a/sound/soc/imx/imx-3stack-sgtl5000.c b/sound/soc/imx/imx-3stack-sgtl5000.c index 0d22b308422a..d38c0b26218b 100644 --- a/sound/soc/imx/imx-3stack-sgtl5000.c +++ b/sound/soc/imx/imx-3stack-sgtl5000.c @@ -41,6 +41,27 @@ #include "imx-ssi.h" #include "imx-pcm.h" +#if defined(CONFIG_MXC_ASRC) || defined(CONFIG_MXC_ASRC_MODULE) +#include + +static unsigned int sgtl5000_rates[] = { + 0, + 32000, + 44100, + 48000, + 96000, +}; + +struct asrc_esai { + unsigned int cpu_dai_rates; + unsigned int codec_dai_rates; + enum asrc_pair_index asrc_index; + unsigned int output_sample_rate; +}; + +static struct asrc_esai asrc_ssi_data; +#endif + /* SSI BCLK and LRC master */ #define SGTL5000_SSI_MASTER 1 @@ -63,6 +84,7 @@ static int imx_3stack_audio_hw_params(struct snd_pcm_substream *substream, struct snd_soc_dai *cpu_dai = machine->cpu_dai; struct snd_soc_dai *codec_dai = machine->codec_dai; struct imx_3stack_priv *priv = &machine_priv; + unsigned int rate = params_rate(params); int ret = 0; unsigned int channels = params_channels(params); @@ -73,7 +95,47 @@ static int imx_3stack_audio_hw_params(struct snd_pcm_substream *substream, return 0; priv->hw = 1; - snd_soc_dai_set_sysclk(codec_dai, 0, priv->sysclk, 0); +#if defined(CONFIG_MXC_ASRC) || defined(CONFIG_MXC_ASRC_MODULE) + if ((asrc_ssi_data.output_sample_rate != 0) + && (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)) { + unsigned int asrc_input_rate = rate; + unsigned int channel = params_channels(params); + struct mxc_runtime_data *pcm_data = + substream->runtime->private_data; + struct asrc_config config; + struct mxc_audio_platform_data *plat; + struct imx_3stack_priv *priv = &machine_priv; + int retVal = 0; + retVal = asrc_req_pair(channel, &asrc_ssi_data.asrc_index); + if (retVal < 0) { + pr_err("asrc_req_pair fail\n"); + return -1; + } + config.pair = asrc_ssi_data.asrc_index; + config.channel_num = channel; + config.input_sample_rate = asrc_input_rate; + config.output_sample_rate = asrc_ssi_data.output_sample_rate; + config.inclk = INCLK_NONE; + config.word_width = 32; + plat = priv->pdev->dev.platform_data; + if (plat->src_port == 1) + config.outclk = OUTCLK_SSI1_TX; + else + config.outclk = OUTCLK_SSI2_TX; + retVal = asrc_config_pair(&config); + if (retVal < 0) { + pr_err("Fail to config asrc\n"); + asrc_release_pair(asrc_ssi_data.asrc_index); + return retVal; + } + rate = asrc_ssi_data.output_sample_rate; + pcm_data->asrc_index = asrc_ssi_data.asrc_index; + pcm_data->asrc_enable = 1; + } +#endif + + snd_soc_dai_set_sysclk(codec_dai, SGTL5000_SYSCLK, priv->sysclk, 0); + snd_soc_dai_set_sysclk(codec_dai, SGTL5000_LRCLK, rate, 0); #if SGTL5000_SSI_MASTER dai_format = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | @@ -109,8 +171,7 @@ static int imx_3stack_audio_hw_params(struct snd_pcm_substream *substream, /* set i.MX active slot mask */ snd_soc_dai_set_tdm_slot(cpu_dai, - channels == 1 ? 0xfffffffe : 0xfffffffc, - 2); + channels == 1 ? 0xfffffffe : 0xfffffffc, 2); /* set the SSI system clock as input (unused) */ snd_soc_dai_set_sysclk(cpu_dai, IMX_SSP_SYS_CLK, 0, SND_SOC_CLOCK_IN); @@ -118,10 +179,49 @@ static int imx_3stack_audio_hw_params(struct snd_pcm_substream *substream, return 0; } +static int imx_3stack_startup(struct snd_pcm_substream *substream) +{ +#if defined(CONFIG_MXC_ASRC) || defined(CONFIG_MXC_ASRC_MODULE) + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { + if (asrc_ssi_data.output_sample_rate != 0) { + struct snd_soc_pcm_runtime *rtd = + substream->private_data; + struct snd_soc_dai_link *pcm_link = rtd->dai; + struct snd_soc_dai *cpu_dai = pcm_link->cpu_dai; + struct snd_soc_dai *codec_dai = pcm_link->codec_dai; + asrc_ssi_data.cpu_dai_rates = cpu_dai->playback.rates; + asrc_ssi_data.codec_dai_rates = + codec_dai->playback.rates; + cpu_dai->playback.rates = + SNDRV_PCM_RATE_8000_192000 | SNDRV_PCM_RATE_KNOT; + codec_dai->playback.rates = + SNDRV_PCM_RATE_8000_192000 | SNDRV_PCM_RATE_KNOT; + } + } +#endif + return 0; +} + static void imx_3stack_shutdown(struct snd_pcm_substream *substream) { struct imx_3stack_priv *priv = &machine_priv; +#if defined(CONFIG_MXC_ASRC) || defined(CONFIG_MXC_ASRC_MODULE) + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { + if (asrc_ssi_data.output_sample_rate != 0) { + struct snd_soc_pcm_runtime *rtd = + substream->private_data; + struct snd_soc_dai_link *pcm_link = rtd->dai; + struct snd_soc_dai *cpu_dai = pcm_link->cpu_dai; + struct snd_soc_dai *codec_dai = pcm_link->codec_dai; + codec_dai->playback.rates = + asrc_ssi_data.codec_dai_rates; + cpu_dai->playback.rates = asrc_ssi_data.cpu_dai_rates; + asrc_release_pair(asrc_ssi_data.asrc_index); + } + } +#endif + priv->hw = 0; } @@ -129,6 +229,7 @@ static void imx_3stack_shutdown(struct snd_pcm_substream *substream) * imx_3stack SGTL5000 audio DAI opserations. */ static struct snd_soc_ops imx_3stack_ops = { + .startup = imx_3stack_startup, .shutdown = imx_3stack_shutdown, .hw_params = imx_3stack_audio_hw_params, }; @@ -341,10 +442,56 @@ static const struct snd_kcontrol_new sgtl5000_machine_controls[] = { sgtl5000_set_spk), }; +#if defined(CONFIG_MXC_ASRC) || defined(CONFIG_MXC_ASRC_MODULE) +static int asrc_func; + +static const char *asrc_function[] = + { "disable", "32KHz", "44.1KHz", "48KHz", "96KHz" }; + +static const struct soc_enum asrc_enum[] = { + SOC_ENUM_SINGLE_EXT(5, asrc_function), +}; + +static int asrc_get_rate(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + ucontrol->value.enumerated.item[0] = asrc_func; + return 0; +} + +static int asrc_set_rate(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + if (asrc_func == ucontrol->value.enumerated.item[0]) + return 0; + + asrc_func = ucontrol->value.enumerated.item[0]; + asrc_ssi_data.output_sample_rate = sgtl5000_rates[asrc_func]; + + return 1; +} + +static const struct snd_kcontrol_new asrc_controls[] = { + SOC_ENUM_EXT("ASRC", asrc_enum[0], asrc_get_rate, + asrc_set_rate), +}; +#endif + static int imx_3stack_sgtl5000_init(struct snd_soc_codec *codec) { int i, ret; +#if defined(CONFIG_MXC_ASRC) || defined(CONFIG_MXC_ASRC_MODULE) + for (i = 0; i < ARRAY_SIZE(asrc_controls); i++) { + ret = snd_ctl_add(codec->card, + snd_soc_cnew(&asrc_controls[i], codec, NULL)); + if (ret < 0) + return ret; + } + asrc_ssi_data.output_sample_rate = sgtl5000_rates[asrc_func]; + +#endif + /* Add imx_3stack specific controls */ for (i = 0; i < ARRAY_SIZE(sgtl5000_machine_controls); i++) { ret = snd_ctl_add(codec->card, diff --git a/sound/soc/imx/imx-pcm.c b/sound/soc/imx/imx-pcm.c index 8f98b5af4b3b..9b7fed4b1894 100644 --- a/sound/soc/imx/imx-pcm.c +++ b/sound/soc/imx/imx-pcm.c @@ -35,6 +35,7 @@ #include "imx-esai.h" #if defined(CONFIG_MXC_ASRC) || defined(CONFIG_MXC_ASRC_MODULE) +#include #include #endif @@ -150,7 +151,27 @@ static int imx_get_sdma_transfer(int format, int dai_port, struct snd_pcm_runtime *runtime = substream->runtime; struct mxc_runtime_data *prtd = runtime->private_data; if (prtd->asrc_enable == 1) { - if (dai_port & IMX_DAI_ESAI_TX) { + if (dai_port == IMX_DAI_SSI0) { + if (prtd->asrc_index == 0) + transfer = MXC_DMA_ASRCA_SSI1_TX0; + else if (prtd->asrc_index == 1) + transfer = MXC_DMA_ASRCB_SSI1_TX0; + } else if (dai_port == IMX_DAI_SSI1) { + if (prtd->asrc_index == 0) + transfer = MXC_DMA_ASRCA_SSI1_TX1; + else if (prtd->asrc_index == 1) + transfer = MXC_DMA_ASRCB_SSI1_TX1; + } else if (dai_port == IMX_DAI_SSI2) { + if (prtd->asrc_index == 0) + transfer = MXC_DMA_ASRCA_SSI2_TX0; + else if (prtd->asrc_index == 1) + transfer = MXC_DMA_ASRCB_SSI2_TX0; + } else if (dai_port == IMX_DAI_SSI3) { + if (prtd->asrc_index == 0) + transfer = MXC_DMA_ASRCA_SSI2_TX1; + else if (prtd->asrc_index == 1) + transfer = MXC_DMA_ASRCB_SSI2_TX1; + } else if (dai_port & IMX_DAI_ESAI_TX) { if (prtd->asrc_index == 0) transfer = MXC_DMA_ASRCA_ESAI; else if (prtd->asrc_index == 1) @@ -443,6 +464,9 @@ static int imx_pcm_trigger(struct snd_pcm_substream *substream, int cmd) if (prtd->asrc_enable == 1) { ret = mxc_dma_enable(prtd->dma_asrc); asrc_start_conv(prtd->asrc_index); + /* There is underrun, if immediately enable SSI after + start ASRC */ + mdelay(1); } #endif break; -- cgit v1.2.3