[alsa-devel] [PATCH] ASoC: fsl: imx-wm8962: Grant hw_params() permission to reprogram FLL
Nicole Otsuka
nicoleotsuka at gmail.com
Tue Jan 7 06:57:45 CET 2014
Ping
On Wed, Dec 25, 2013 at 6:37 PM, Nicolin Chen <Guangyu.Chen at freescale.com>wrote:
> From: Nicolin Chen <b42378 at freescale.com>
>
> Previously, we couldn't use hw_params() to reclock FLL becuase there might
> be a race between two simmultaneous substreams so the FLL reprogramming
> would be changed and accordingly mulfunction. Also it wouldn't make sense
> for analogue bypass path feature of WM8962. So we adopted the DAPM way to
> control it. However, if we want to playback a different sample rate file,
> we need to wait for DAPM to change its bias_level and reconfigure FLL.
>
> After we introduced full symmetry protection in the soc-pcm, we don't need
> to worry about the race any more. And the instance by using hw_params() to
> reprogram FLL will allow us to support flexible use cases -- for example:
> 'aplay -Dhw:0 44k16bit.wav 48k24bit.wav 32k16bit.wav'.
>
> Thus this patch mainly adds FLL reprogramming code to hw_params() so as to
> enchance the sound card's capability. Meanwhile in order not to break the
> analogue bypass path feature, we make both set_bias_level() and hw_params()
> ways coexist by only reclocking FLL if requiring a different frequency and
> meanwhile not running any analogue bypass path in the background.
>
> Signed-off-by: Nicolin Chen <Guangyu.Chen at freescale.com>
> ---
> sound/soc/fsl/imx-wm8962.c | 167
> ++++++++++++++++++++++++++++++++-------------
> 1 file changed, 121 insertions(+), 46 deletions(-)
>
> diff --git a/sound/soc/fsl/imx-wm8962.c b/sound/soc/fsl/imx-wm8962.c
> index 3fd76bc..9621909 100644
> --- a/sound/soc/fsl/imx-wm8962.c
> +++ b/sound/soc/fsl/imx-wm8962.c
> @@ -17,6 +17,7 @@
> #include <linux/of_platform.h>
> #include <linux/i2c.h>
> #include <linux/slab.h>
> +#include <linux/spinlock.h>
> #include <linux/clk.h>
> #include <sound/soc.h>
> #include <sound/pcm_params.h>
> @@ -35,6 +36,8 @@ struct imx_wm8962_data {
> char platform_name[DAI_NAME_SIZE];
> struct clk *codec_clk;
> unsigned int clk_frequency;
> + unsigned int fll_frequency;
> + spinlock_t fll_lock;
> };
>
> struct imx_priv {
> @@ -50,15 +53,125 @@ static const struct snd_soc_dapm_widget
> imx_wm8962_dapm_widgets[] = {
> };
>
> static int sample_rate = 44100;
> +static unsigned int fll_frequency = 44100 * 256;
> static snd_pcm_format_t sample_format = SNDRV_PCM_FORMAT_S16_LE;
>
> +static inline u32 imx_wm8962_calculate_fll(u32 rate, snd_pcm_format_t
> format)
> +{
> + switch (format) {
> + case SNDRV_PCM_FORMAT_S24_LE:
> + return rate * 384;
> + default:
> + return rate * 256;
> + }
> +}
> +
> +static int imx_wm8962_enable_fll(struct snd_soc_dai *codec_dai)
> +{
> + struct imx_priv *priv = &card_priv;
> + struct device *dev = &priv->pdev->dev;
> + struct imx_wm8962_data *data = platform_get_drvdata(priv->pdev);
> + u32 ret = 0;
> +
> + spin_lock(&data->fll_lock);
> +
> + /* Protect FLL if it has not been disabled */
> + if (data->fll_frequency)
> + goto err_unlock;
> +
> + ret = snd_soc_dai_set_pll(codec_dai, WM8962_FLL, WM8962_FLL_MCLK,
> + data->clk_frequency, fll_frequency);
> + if (ret) {
> + dev_err(dev, "failed to start FLL: %d\n", ret);
> + goto err_unlock;
> + }
> +
> + ret = snd_soc_dai_set_sysclk(codec_dai, WM8962_SYSCLK_FLL,
> + fll_frequency, SND_SOC_CLOCK_IN);
> + if (ret)
> + dev_err(dev, "failed to set SYSCLK: %d\n", ret);
> +
> + data->fll_frequency = fll_frequency;
> +
> +err_unlock:
> + spin_unlock(&data->fll_lock);
> +
> + return ret;
> +}
> +
> +static int imx_wm8962_disable_fll(struct snd_soc_dai *codec_dai)
> +{
> + struct imx_priv *priv = &card_priv;
> + struct device *dev = &priv->pdev->dev;
> + struct imx_wm8962_data *data = platform_get_drvdata(priv->pdev);
> + int ret = 0;
> +
> + spin_lock(&data->fll_lock);
> +
> + if (!data->fll_frequency)
> + goto err_unlock;
> +
> + /* Switch to MCLK as sysclk once so as to disable FLL */
> + ret = snd_soc_dai_set_sysclk(codec_dai, WM8962_SYSCLK_MCLK,
> + 0, SND_SOC_CLOCK_IN);
> + if (ret) {
> + dev_err(dev, "failed to switch away from FLL: %d\n", ret);
> + goto err_unlock;
> + }
> +
> + /* Disable FLL so that we can reset its output freq later */
> + ret = snd_soc_dai_set_pll(codec_dai, WM8962_FLL,
> + WM8962_FLL_MCLK, 0, 0);
> + if (ret)
> + dev_err(dev, "failed to stop FLL: %d\n", ret);
> +
> + data->fll_frequency = 0;
> +
> +err_unlock:
> + spin_unlock(&data->fll_lock);
> +
> + return ret;
> +}
> +
> +static int imx_wm8962_reprogram_fll(struct snd_pcm_substream *substream)
> +{
> + struct imx_priv *priv = &card_priv;
> + struct imx_wm8962_data *data = platform_get_drvdata(priv->pdev);
> + struct snd_soc_pcm_runtime *rtd = substream->private_data;
> + struct snd_soc_dai *codec_dai = rtd->codec_dai;
> + struct snd_soc_codec *codec = rtd->codec;
> + u32 mask, ret = 0;
> +
> + mask = WM8962_MIXINL_TO_HPMIXL_MASK | WM8962_MIXINR_TO_HPMIXL_MASK
> |
> + WM8962_IN4L_TO_HPMIXL_MASK | WM8962_IN4R_TO_HPMIXL_MASK;
> + ret |= snd_soc_read(codec, WM8962_HEADPHONE_MIXER_1) & mask;
> + ret |= snd_soc_read(codec, WM8962_HEADPHONE_MIXER_2) & mask;
> + ret |= snd_soc_read(codec, WM8962_SPEAKER_MIXER_1) & mask;
> + ret |= snd_soc_read(codec, WM8962_SPEAKER_MIXER_2) & mask;
> +
> + spin_lock(&data->fll_lock);
> + /* Do not reprogram FLL if not changed or running analogue bypass
> */
> + if (fll_frequency == data->fll_frequency || ret) {
> + spin_unlock(&data->fll_lock);
> + return 0;
> + }
> + spin_unlock(&data->fll_lock);
> +
> + ret = imx_wm8962_disable_fll(codec_dai);
> + if (ret)
> + return ret;
> +
> + return imx_wm8962_enable_fll(codec_dai);
> +}
> +
> static int imx_hifi_hw_params(struct snd_pcm_substream *substream,
> struct snd_pcm_hw_params *params)
> {
> sample_rate = params_rate(params);
> sample_format = params_format(params);
> + fll_frequency = imx_wm8962_calculate_fll(sample_rate,
> sample_format);
>
> - return 0;
> + return imx_wm8962_reprogram_fll(substream);
> }
>
> static struct snd_soc_ops imx_hifi_ops = {
> @@ -70,60 +183,19 @@ static int imx_wm8962_set_bias_level(struct
> snd_soc_card *card,
> enum snd_soc_bias_level level)
> {
> struct snd_soc_dai *codec_dai = card->rtd[0].codec_dai;
> - struct imx_priv *priv = &card_priv;
> - struct imx_wm8962_data *data = platform_get_drvdata(priv->pdev);
> - struct device *dev = &priv->pdev->dev;
> - unsigned int pll_out;
> - int ret;
>
> if (dapm->dev != codec_dai->dev)
> return 0;
>
> switch (level) {
> case SND_SOC_BIAS_PREPARE:
> - if (dapm->bias_level == SND_SOC_BIAS_STANDBY) {
> - if (sample_format == SNDRV_PCM_FORMAT_S24_LE)
> - pll_out = sample_rate * 384;
> - else
> - pll_out = sample_rate * 256;
> -
> - ret = snd_soc_dai_set_pll(codec_dai, WM8962_FLL,
> - WM8962_FLL_MCLK,
> data->clk_frequency,
> - pll_out);
> - if (ret < 0) {
> - dev_err(dev, "failed to start FLL: %d\n",
> ret);
> - return ret;
> - }
> -
> - ret = snd_soc_dai_set_sysclk(codec_dai,
> - WM8962_SYSCLK_FLL, pll_out,
> - SND_SOC_CLOCK_IN);
> - if (ret < 0) {
> - dev_err(dev, "failed to set SYSCLK: %d\n",
> ret);
> - return ret;
> - }
> - }
> + if (dapm->bias_level == SND_SOC_BIAS_STANDBY)
> + return imx_wm8962_enable_fll(codec_dai);
> break;
>
> case SND_SOC_BIAS_STANDBY:
> - if (dapm->bias_level == SND_SOC_BIAS_PREPARE) {
> - ret = snd_soc_dai_set_sysclk(codec_dai,
> - WM8962_SYSCLK_MCLK,
> data->clk_frequency,
> - SND_SOC_CLOCK_IN);
> - if (ret < 0) {
> - dev_err(dev,
> - "failed to switch away from FLL:
> %d\n",
> - ret);
> - return ret;
> - }
> -
> - ret = snd_soc_dai_set_pll(codec_dai, WM8962_FLL,
> - 0, 0, 0);
> - if (ret < 0) {
> - dev_err(dev, "failed to stop FLL: %d\n",
> ret);
> - return ret;
> - }
> - }
> + if (dapm->bias_level == SND_SOC_BIAS_PREPARE)
> + return imx_wm8962_disable_fll(codec_dai);
> break;
>
> default:
> @@ -225,6 +297,9 @@ static int imx_wm8962_probe(struct platform_device
> *pdev)
> goto fail;
> }
>
> + data->fll_frequency = 0;
> + spin_lock_init(&data->fll_lock);
> +
> data->codec_clk = devm_clk_get(&codec_dev->dev, NULL);
> if (IS_ERR(data->codec_clk)) {
> ret = PTR_ERR(data->codec_clk);
> --
> 1.8.4
>
>
> _______________________________________________
> Alsa-devel mailing list
> Alsa-devel at alsa-project.org
> http://mailman.alsa-project.org/mailman/listinfo/alsa-devel
>
More information about the Alsa-devel
mailing list