All of lore.kernel.org
 help / color / mirror / Atom feed
From: Peter Ujfalusi <peter.ujfalusi@ti.com>
To: Jyri Sarha <jsarha@ti.com>, alsa-devel@alsa-project.org
Cc: linux-omap@vger.kernel.org, broonie@kernel.org,
	liam.r.girdwood@linux.intel.com, lars@metafoo.de
Subject: Re: [PATCH] ASoC: davinci-mcasp: Set rule constraints if implicit BCLK divider is used
Date: Wed, 18 Mar 2015 10:08:46 +0200	[thread overview]
Message-ID: <5509328E.5020606@ti.com> (raw)
In-Reply-To: <1426604203-6467-1-git-send-email-jsarha@ti.com>

On 03/17/2015 04:56 PM, Jyri Sarha wrote:
> Set a rule constraints to allow only combinations of sample-rate and
> number of frame-bits that can be played/captured with a reasonable
> sample-rate accuracy.
> 
> Also, takes the number of serializers into account when implicitly
> selecting the BLCK divider.
> 
> Signed-off-by: Jyri Sarha <jsarha@ti.com>
> ---
> Now this works as it should.
> 
> Thanks,
> Jyri
> 
>  sound/soc/davinci/davinci-mcasp.c | 150 +++++++++++++++++++++++++++++++++++---
>  1 file changed, 140 insertions(+), 10 deletions(-)
> 
> diff --git a/sound/soc/davinci/davinci-mcasp.c b/sound/soc/davinci/davinci-mcasp.c
> index de3b155..3ecee3b 100644
> --- a/sound/soc/davinci/davinci-mcasp.c
> +++ b/sound/soc/davinci/davinci-mcasp.c
> @@ -26,6 +26,7 @@
>  #include <linux/of.h>
>  #include <linux/of_platform.h>
>  #include <linux/of_device.h>
> +#include <linux/math64.h>
>  
>  #include <sound/asoundef.h>
>  #include <sound/core.h>
> @@ -64,6 +65,11 @@ struct davinci_mcasp_context {
>  	u32	*xrsr_regs; /* for serializer configuration */
>  };
>  
> +struct davinci_mcasp_ruledata {
> +	struct davinci_mcasp *mcasp;
> +	int serializers;
> +};
> +
>  struct davinci_mcasp {
>  	struct davinci_pcm_dma_params dma_params[2];
>  	struct snd_dmaengine_dai_dma_data dma_data[2];
> @@ -98,6 +104,8 @@ struct davinci_mcasp {
>  #ifdef CONFIG_PM_SLEEP
>  	struct davinci_mcasp_context context;
>  #endif
> +
> +	struct davinci_mcasp_ruledata ruledata[2];
>  };
>  
>  static inline void mcasp_set_bits(struct davinci_mcasp *mcasp, u32 offset,
> @@ -855,6 +863,30 @@ static int mcasp_dit_hw_param(struct davinci_mcasp *mcasp,
>  	return 0;
>  }
>  
> +static int davinci_mcasp_calc_clk_div(struct davinci_mcasp *mcasp,
> +				      unsigned int bclk_freq,
> +				      int *error_ppm)
> +{
> +	int div = mcasp->sysclk_freq / bclk_freq;
> +	int rem = mcasp->sysclk_freq % bclk_freq;
> +
> +	if (rem != 0) {
> +		if (div == 0 ||
> +		    ((mcasp->sysclk_freq / div) - bclk_freq) >
> +		    (bclk_freq - (mcasp->sysclk_freq / (div+1)))) {
> +			div++;
> +			rem = rem - bclk_freq;
> +		}
> +	}
> +	if (error_ppm)
> +		*error_ppm =
> +			(div*1000000 + (int)div64_long(1000000LL*rem,
> +						       (int)bclk_freq))
> +			/div - 1000000;
> +
> +	return div;
> +}
> +
>  static int davinci_mcasp_hw_params(struct snd_pcm_substream *substream,
>  					struct snd_pcm_hw_params *params,
>  					struct snd_soc_dai *cpu_dai)
> @@ -872,16 +904,19 @@ static int davinci_mcasp_hw_params(struct snd_pcm_substream *substream,
>  	 * the machine driver, we need to calculate the ratio.
>  	 */
>  	if (mcasp->bclk_master && mcasp->bclk_div == 0 && mcasp->sysclk_freq) {
> -		unsigned int bclk_freq = snd_soc_params_to_bclk(params);
> -		unsigned int div = mcasp->sysclk_freq / bclk_freq;
> -		if (mcasp->sysclk_freq % bclk_freq != 0) {
> -			if (((mcasp->sysclk_freq / div) - bclk_freq) >
> -			    (bclk_freq - (mcasp->sysclk_freq / (div+1))))
> -				div++;
> -			dev_warn(mcasp->dev,
> -				 "Inaccurate BCLK: %u Hz / %u != %u Hz\n",
> -				 mcasp->sysclk_freq, div, bclk_freq);
> -		}
> +		uint bclk_freq = snd_soc_params_to_bclk(params);
> +		int dir = substream->stream == SNDRV_PCM_STREAM_PLAYBACK ?
> +			TX_MODE : RX_MODE;
> +		int ppm, div;
> +
> +		bclk_freq /= mcasp->ruledata[dir].serializers;

Not correct, see my comment below.

> +
> +		div = davinci_mcasp_calc_clk_div(mcasp, bclk_freq, &ppm);
> +
> +		if (ppm)
> +			dev_info(mcasp->dev, "Sample-rate is off by %d PPM\n",
> +				 ppm);
> +
>  		__davinci_mcasp_set_clkdiv(cpu_dai, 1, div, 0);
>  	}
>  
> @@ -973,6 +1008,74 @@ static int davinci_mcasp_trigger(struct snd_pcm_substream *substream,
>  	return ret;
>  }
>  
> +static const unsigned int davinci_mcasp_dai_rates[] = {
> +	8000, 11025, 16000, 22050, 32000, 44100, 48000, 64000,
> +	88200, 96000, 176400, 192000,
> +};
> +
> +#define DAVINCI_MAX_RATE_ERROR_PPM 1000
> +
> +static int davinci_mcasp_hw_rule_rate(struct snd_pcm_hw_params *params,
> +				      struct snd_pcm_hw_rule *rule)
> +{
> +	struct davinci_mcasp_ruledata *rd = rule->private;
> +	unsigned int list[ARRAY_SIZE(davinci_mcasp_dai_rates)];
> +	int frame_size;
> +	int i, count = 0;
> +
> +	frame_size = snd_soc_params_to_frame_size(params);
> +	if (frame_size < 0)
> +		return frame_size;
> +
> +	for (i = 0; i < ARRAY_SIZE(davinci_mcasp_dai_rates); i++) {
> +		uint bclk_freq = frame_size*davinci_mcasp_dai_rates[i]/
> +			rd->serializers;
> +		int ppm;
> +
> +		davinci_mcasp_calc_clk_div(rd->mcasp, bclk_freq, &ppm);
> +		if (abs(ppm) < DAVINCI_MAX_RATE_ERROR_PPM)
> +			list[count++] = davinci_mcasp_dai_rates[i];
> +
> +	}
> +	dev_dbg(rd->mcasp->dev, "%d frequencies for %u fsize (%d)\n",
> +		count, frame_size, rd->serializers);
> +
> +	return snd_interval_list(hw_param_interval(params, rule->var),
> +				 count, list, 0);
> +}
> +
> +static int davinci_mcasp_hw_rule_framebits(struct snd_pcm_hw_params *params,
> +					   struct snd_pcm_hw_rule *rule)
> +{
> +	struct davinci_mcasp_ruledata *rd = rule->private;
> +	struct snd_interval *ci =
> +		hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS);
> +	unsigned int max_chan_per_wire = ci->max / rd->serializers;

This is not correct. Serializers are working in parallel. The
max_chan_per_wire can not be higher than tdm_slots and can not be less then 2
(in IIS mode).
Let's say we have 3 tx serializers and tdm_slots is 2:
in case of stereo stream we will have 2 channels worth of bclk on wire.
In case of 3 or 4 channel audio, we will have again 2 channels worth of bclk

But if we have tdm_slots set to 6:
in case of stereo we will have 2 channels worth of bclk.
in case of 4 channel we will have 4, etc

I believe the places where you use the ->serializers need to be rechecked again.


> +	unsigned int list[max_chan_per_wire*4];
> +	int rate = params_rate(params);
> +	int chans, fbits, count = 0;
> +
> +	for (chans = ci->min; chans <= max_chan_per_wire; chans++) {
> +		for (fbits = 8; fbits <= 32; fbits += 8) {
> +			uint bclk_freq = chans*fbits*rate;
> +			int ppm;
> +
> +			davinci_mcasp_calc_clk_div(rd->mcasp, bclk_freq, &ppm);
> +			if (abs(ppm) < DAVINCI_MAX_RATE_ERROR_PPM) {
> +				list[count++] = chans*fbits*rd->serializers;
> +				BUG_ON(count > ARRAY_SIZE(list));
> +			}
> +		}
> +	}
> +
> +	dev_dbg(rd->mcasp->dev,
> +		"%d possible frame size for %d Hz (%d)\n",
> +		count, rate, rd->serializers);
> +
> +	return snd_interval_list(hw_param_interval(params, rule->var),
> +				 count, list, 0);
> +}
> +
>  static int davinci_mcasp_startup(struct snd_pcm_substream *substream,
>  				 struct snd_soc_dai *cpu_dai)
>  {
> @@ -998,6 +1101,7 @@ static int davinci_mcasp_startup(struct snd_pcm_substream *substream,
>  		if (mcasp->serial_dir[i] == dir)
>  			max_channels++;
>  	}
> +	mcasp->ruledata[dir].serializers = max_channels;
>  	max_channels *= mcasp->tdm_slots;
>  	/*
>  	 * If the already active stream has less channels than the calculated
> @@ -1012,6 +1116,32 @@ static int davinci_mcasp_startup(struct snd_pcm_substream *substream,
>  	snd_pcm_hw_constraint_minmax(substream->runtime,
>  				     SNDRV_PCM_HW_PARAM_CHANNELS,
>  				     2, max_channels);
> +
> +	/*
> +	 * If we rely on implicit BCLK divider setting we should
> +	 * set constraints based on what we can provide.
> +	 */
> +	if (mcasp->bclk_master && mcasp->bclk_div == 0 && mcasp->sysclk_freq) {
> +		int ret;
> +
> +		mcasp->ruledata[dir].mcasp = mcasp;
> +
> +		ret = snd_pcm_hw_rule_add(substream->runtime, 0,
> +					  SNDRV_PCM_HW_PARAM_RATE,
> +					  davinci_mcasp_hw_rule_rate,
> +					  &mcasp->ruledata[dir],
> +					  SNDRV_PCM_HW_PARAM_FRAME_BITS, -1);
> +		if (ret)
> +			return ret;
> +		ret = snd_pcm_hw_rule_add(substream->runtime, 0,
> +					  SNDRV_PCM_HW_PARAM_FRAME_BITS,
> +					  davinci_mcasp_hw_rule_framebits,
> +					  &mcasp->ruledata[dir],
> +					  SNDRV_PCM_HW_PARAM_RATE, -1);
> +		if (ret)
> +			return ret;
> +	}
> +
>  	return 0;
>  }
>  
> 
-- 
Péter
--
To unsubscribe from this list: send the line "unsubscribe linux-omap" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

      reply	other threads:[~2015-03-18  8:08 UTC|newest]

Thread overview: 2+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2015-03-17 14:56 [PATCH] ASoC: davinci-mcasp: Set rule constraints if implicit BCLK divider is used Jyri Sarha
2015-03-18  8:08 ` Peter Ujfalusi [this message]

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=5509328E.5020606@ti.com \
    --to=peter.ujfalusi@ti.com \
    --cc=alsa-devel@alsa-project.org \
    --cc=broonie@kernel.org \
    --cc=jsarha@ti.com \
    --cc=lars@metafoo.de \
    --cc=liam.r.girdwood@linux.intel.com \
    --cc=linux-omap@vger.kernel.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.