All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH v3] ASoC: atmel_ssc_dai: Allow more rates
@ 2015-02-09 15:08 ` Peter Rosin
  0 siblings, 0 replies; 7+ messages in thread
From: Peter Rosin @ 2015-02-09 15:08 UTC (permalink / raw)
  To: alsa-devel
  Cc: Peter Rosin, Bo Shen, Liam Girdwood, Mark Brown, Jaroslav Kysela,
	Takashi Iwai, linux-kernel, linux-arm-kernel

From: Peter Rosin <peda@axentia.se>

When the SSC acts as BCK master, use a ratnum rule to limit
the rate instead of only doing the standard rates. When the SSC
acts as BCK slave, allow any BCK frequency up to the SSC master
clock, divided by either of 2, 3 or 6.

Put a cap at 384kHz. Who's /ever/ going to need more than that?

The divider of 2, 3 or 6 is selected based on the Serial Clock Ratio
Considerations section from the SSC documentation:

    The Transmitter and the Receiver can be programmed to operate
    with the clock signals provided on either the TK or RK pins.
    This allows the SSC to support many slave-mode data transfers.
    In this case, the maximum clock speed allowed on the RK pin is:
    - Peripheral clock divided by 2 if Receiver Frame Synchro is input
    - Peripheral clock divided by 3 if Receiver Frame Synchro is output
    In addition, the maximum clock speed allowed on the TK pin is:
    - Peripheral clock divided by 6 if Transmit Frame Synchro is input
    - Peripheral clock divided by 2 if Transmit Frame Synchro is output

Signed-off-by: Peter Rosin <peda@axentia.se>
---
 sound/soc/atmel/atmel_ssc_dai.c |  111 +++++++++++++++++++++++++++++++++++++--
 sound/soc/atmel/atmel_ssc_dai.h |    1 +
 2 files changed, 108 insertions(+), 4 deletions(-)

Changes since v2:

- Killed the 500ppm reduction. Just trust the nominal clock
  rates of the given clocks.

- Don't multiply the given SSC clk rate by two to get to the
  SSC master clock rate. That factor was the result of some
  confusion on my part. The SSC clk *is* the SSC master clock.

Changes since v1:

- I have checked all Atmel datasheets I could get my hands on (and
  that seemed relevant), and in every instance where they have
  described the SSC, the description have the exact rate limitations
  on the RK and TK pin that I have implemented here.

- Improved the comments.

- Rebased on top of the latest patches from Bo Chen.

diff --git a/sound/soc/atmel/atmel_ssc_dai.c b/sound/soc/atmel/atmel_ssc_dai.c
index 379ac2a6ab16..6b8e64899205 100644
--- a/sound/soc/atmel/atmel_ssc_dai.c
+++ b/sound/soc/atmel/atmel_ssc_dai.c
@@ -187,6 +187,94 @@ static irqreturn_t atmel_ssc_interrupt(int irq, void *dev_id)
 	return IRQ_HANDLED;
 }
 
+/*
+ * When the bit clock is input, limit the maximum rate according to the
+ * Serial Clock Ratio Considerations section from the SSC documentation:
+ *
+ *   The Transmitter and the Receiver can be programmed to operate
+ *   with the clock signals provided on either the TK or RK pins.
+ *   This allows the SSC to support many slave-mode data transfers.
+ *   In this case, the maximum clock speed allowed on the RK pin is:
+ *   - Peripheral clock divided by 2 if Receiver Frame Synchro is input
+ *   - Peripheral clock divided by 3 if Receiver Frame Synchro is output
+ *   In addition, the maximum clock speed allowed on the TK pin is:
+ *   - Peripheral clock divided by 6 if Transmit Frame Synchro is input
+ *   - Peripheral clock divided by 2 if Transmit Frame Synchro is output
+ *
+ * When the bit clock is output, limit the rate according to the
+ * SSC divider restrictions.
+ */
+static int atmel_ssc_hw_rule_rate(struct snd_pcm_hw_params *params,
+				  struct snd_pcm_hw_rule *rule)
+{
+	struct atmel_ssc_info *ssc_p = rule->private;
+	struct ssc_device *ssc = ssc_p->ssc;
+	struct snd_interval *i = hw_param_interval(params, rule->var);
+	struct snd_interval t;
+	struct snd_ratnum r = {
+		.den_min = 1,
+		.den_max = 4095,
+		.den_step = 1,
+	};
+	unsigned int num = 0, den = 0;
+	int frame_size;
+	int mck_div = 2;
+	int ret;
+
+	frame_size = snd_soc_params_to_frame_size(params);
+	if (frame_size < 0)
+		return frame_size;
+
+	switch (ssc_p->daifmt & SND_SOC_DAIFMT_MASTER_MASK) {
+	case SND_SOC_DAIFMT_CBM_CFS:
+		if ((ssc_p->dir_mask & SSC_DIR_MASK_CAPTURE)
+		    && ssc->clk_from_rk_pin)
+			/* Receiver Frame Synchro (i.e. capture)
+			 * is output (format is _CFS) and the RK pin
+			 * is used for input (format is _CBM_).
+			 */
+			mck_div = 3;
+		break;
+
+	case SND_SOC_DAIFMT_CBM_CFM:
+		if ((ssc_p->dir_mask & SSC_DIR_MASK_PLAYBACK)
+		    && !ssc->clk_from_rk_pin)
+			/* Transmit Frame Synchro (i.e. playback)
+			 * is input (format is _CFM) and the TK pin
+			 * is used for input (format _CBM_ but not
+			 * using the RK pin).
+			 */
+			mck_div = 6;
+		break;
+	}
+
+	switch (ssc_p->daifmt & SND_SOC_DAIFMT_MASTER_MASK) {
+	case SND_SOC_DAIFMT_CBS_CFS:
+		r.num = ssc_p->mck_rate / mck_div / frame_size;
+
+		ret = snd_interval_ratnum(i, 1, &r, &num, &den);
+		if (ret >= 0 && den && rule->var == SNDRV_PCM_HW_PARAM_RATE) {
+			params->rate_num = num;
+			params->rate_den = den;
+		}
+		break;
+
+	case SND_SOC_DAIFMT_CBM_CFS:
+	case SND_SOC_DAIFMT_CBM_CFM:
+		t.min = 8000;
+		t.max = ssc_p->mck_rate / mck_div / frame_size;
+		t.openmin = t.openmax = 0;
+		t.integer = 0;
+		ret = snd_interval_refine(i, &t);
+		break;
+
+	default:
+		ret = -EINVAL;
+		break;
+	}
+
+	return ret;
+}
 
 /*-------------------------------------------------------------------------*\
  * DAI functions
@@ -200,6 +288,7 @@ static int atmel_ssc_startup(struct snd_pcm_substream *substream,
 	struct atmel_ssc_info *ssc_p = &ssc_info[dai->id];
 	struct atmel_pcm_dma_params *dma_params;
 	int dir, dir_mask;
+	int ret;
 
 	pr_debug("atmel_ssc_startup: SSC_SR=0x%u\n",
 		ssc_readl(ssc_p->ssc->regs, SR));
@@ -207,6 +296,7 @@ static int atmel_ssc_startup(struct snd_pcm_substream *substream,
 	/* Enable PMC peripheral clock for this SSC */
 	pr_debug("atmel_ssc_dai: Starting clock\n");
 	clk_enable(ssc_p->ssc->clk);
+	ssc_p->mck_rate = clk_get_rate(ssc_p->ssc->clk);
 
 	/* Reset the SSC to keep it at a clean status */
 	ssc_writel(ssc_p->ssc->regs, CR, SSC_BIT(CR_SWRST));
@@ -219,6 +309,17 @@ static int atmel_ssc_startup(struct snd_pcm_substream *substream,
 		dir_mask = SSC_DIR_MASK_CAPTURE;
 	}
 
+	ret = snd_pcm_hw_rule_add(substream->runtime, 0,
+				  SNDRV_PCM_HW_PARAM_RATE,
+				  atmel_ssc_hw_rule_rate,
+				  ssc_p,
+				  SNDRV_PCM_HW_PARAM_FRAME_BITS,
+				  SNDRV_PCM_HW_PARAM_CHANNELS, -1);
+	if (ret < 0) {
+		dev_err(dai->dev, "Failed to specify rate rule: %d\n", ret);
+		return ret;
+	}
+
 	dma_params = &ssc_dma_params[dai->id][dir];
 	dma_params->ssc = ssc_p->ssc;
 	dma_params->substream = substream;
@@ -783,8 +884,6 @@ static int atmel_ssc_resume(struct snd_soc_dai *cpu_dai)
 #  define atmel_ssc_resume	NULL
 #endif /* CONFIG_PM */
 
-#define ATMEL_SSC_RATES (SNDRV_PCM_RATE_8000_96000)
-
 #define ATMEL_SSC_FORMATS (SNDRV_PCM_FMTBIT_S8     | SNDRV_PCM_FMTBIT_S16_LE |\
 			  SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE)
 
@@ -804,12 +903,16 @@ static struct snd_soc_dai_driver atmel_ssc_dai = {
 		.playback = {
 			.channels_min = 1,
 			.channels_max = 2,
-			.rates = ATMEL_SSC_RATES,
+			.rates = SNDRV_PCM_RATE_CONTINUOUS,
+			.rate_min = 8000,
+			.rate_max = 384000,
 			.formats = ATMEL_SSC_FORMATS,},
 		.capture = {
 			.channels_min = 1,
 			.channels_max = 2,
-			.rates = ATMEL_SSC_RATES,
+			.rates = SNDRV_PCM_RATE_CONTINUOUS,
+			.rate_min = 8000,
+			.rate_max = 384000,
 			.formats = ATMEL_SSC_FORMATS,},
 		.ops = &atmel_ssc_dai_ops,
 };
diff --git a/sound/soc/atmel/atmel_ssc_dai.h b/sound/soc/atmel/atmel_ssc_dai.h
index b1f08d511495..80b153857a88 100644
--- a/sound/soc/atmel/atmel_ssc_dai.h
+++ b/sound/soc/atmel/atmel_ssc_dai.h
@@ -115,6 +115,7 @@ struct atmel_ssc_info {
 	unsigned short rcmr_period;
 	struct atmel_pcm_dma_params *dma_params[2];
 	struct atmel_ssc_state ssc_state;
+	unsigned long mck_rate;
 };
 
 int atmel_ssc_set_audio(int ssc_id);
-- 
1.7.10.4


^ permalink raw reply related	[flat|nested] 7+ messages in thread

* [PATCH v3] ASoC: atmel_ssc_dai: Allow more rates
@ 2015-02-09 15:08 ` Peter Rosin
  0 siblings, 0 replies; 7+ messages in thread
From: Peter Rosin @ 2015-02-09 15:08 UTC (permalink / raw)
  To: linux-arm-kernel

From: Peter Rosin <peda@axentia.se>

When the SSC acts as BCK master, use a ratnum rule to limit
the rate instead of only doing the standard rates. When the SSC
acts as BCK slave, allow any BCK frequency up to the SSC master
clock, divided by either of 2, 3 or 6.

Put a cap at 384kHz. Who's /ever/ going to need more than that?

The divider of 2, 3 or 6 is selected based on the Serial Clock Ratio
Considerations section from the SSC documentation:

    The Transmitter and the Receiver can be programmed to operate
    with the clock signals provided on either the TK or RK pins.
    This allows the SSC to support many slave-mode data transfers.
    In this case, the maximum clock speed allowed on the RK pin is:
    - Peripheral clock divided by 2 if Receiver Frame Synchro is input
    - Peripheral clock divided by 3 if Receiver Frame Synchro is output
    In addition, the maximum clock speed allowed on the TK pin is:
    - Peripheral clock divided by 6 if Transmit Frame Synchro is input
    - Peripheral clock divided by 2 if Transmit Frame Synchro is output

Signed-off-by: Peter Rosin <peda@axentia.se>
---
 sound/soc/atmel/atmel_ssc_dai.c |  111 +++++++++++++++++++++++++++++++++++++--
 sound/soc/atmel/atmel_ssc_dai.h |    1 +
 2 files changed, 108 insertions(+), 4 deletions(-)

Changes since v2:

- Killed the 500ppm reduction. Just trust the nominal clock
  rates of the given clocks.

- Don't multiply the given SSC clk rate by two to get to the
  SSC master clock rate. That factor was the result of some
  confusion on my part. The SSC clk *is* the SSC master clock.

Changes since v1:

- I have checked all Atmel datasheets I could get my hands on (and
  that seemed relevant), and in every instance where they have
  described the SSC, the description have the exact rate limitations
  on the RK and TK pin that I have implemented here.

- Improved the comments.

- Rebased on top of the latest patches from Bo Chen.

diff --git a/sound/soc/atmel/atmel_ssc_dai.c b/sound/soc/atmel/atmel_ssc_dai.c
index 379ac2a6ab16..6b8e64899205 100644
--- a/sound/soc/atmel/atmel_ssc_dai.c
+++ b/sound/soc/atmel/atmel_ssc_dai.c
@@ -187,6 +187,94 @@ static irqreturn_t atmel_ssc_interrupt(int irq, void *dev_id)
 	return IRQ_HANDLED;
 }
 
+/*
+ * When the bit clock is input, limit the maximum rate according to the
+ * Serial Clock Ratio Considerations section from the SSC documentation:
+ *
+ *   The Transmitter and the Receiver can be programmed to operate
+ *   with the clock signals provided on either the TK or RK pins.
+ *   This allows the SSC to support many slave-mode data transfers.
+ *   In this case, the maximum clock speed allowed on the RK pin is:
+ *   - Peripheral clock divided by 2 if Receiver Frame Synchro is input
+ *   - Peripheral clock divided by 3 if Receiver Frame Synchro is output
+ *   In addition, the maximum clock speed allowed on the TK pin is:
+ *   - Peripheral clock divided by 6 if Transmit Frame Synchro is input
+ *   - Peripheral clock divided by 2 if Transmit Frame Synchro is output
+ *
+ * When the bit clock is output, limit the rate according to the
+ * SSC divider restrictions.
+ */
+static int atmel_ssc_hw_rule_rate(struct snd_pcm_hw_params *params,
+				  struct snd_pcm_hw_rule *rule)
+{
+	struct atmel_ssc_info *ssc_p = rule->private;
+	struct ssc_device *ssc = ssc_p->ssc;
+	struct snd_interval *i = hw_param_interval(params, rule->var);
+	struct snd_interval t;
+	struct snd_ratnum r = {
+		.den_min = 1,
+		.den_max = 4095,
+		.den_step = 1,
+	};
+	unsigned int num = 0, den = 0;
+	int frame_size;
+	int mck_div = 2;
+	int ret;
+
+	frame_size = snd_soc_params_to_frame_size(params);
+	if (frame_size < 0)
+		return frame_size;
+
+	switch (ssc_p->daifmt & SND_SOC_DAIFMT_MASTER_MASK) {
+	case SND_SOC_DAIFMT_CBM_CFS:
+		if ((ssc_p->dir_mask & SSC_DIR_MASK_CAPTURE)
+		    && ssc->clk_from_rk_pin)
+			/* Receiver Frame Synchro (i.e. capture)
+			 * is output (format is _CFS) and the RK pin
+			 * is used for input (format is _CBM_).
+			 */
+			mck_div = 3;
+		break;
+
+	case SND_SOC_DAIFMT_CBM_CFM:
+		if ((ssc_p->dir_mask & SSC_DIR_MASK_PLAYBACK)
+		    && !ssc->clk_from_rk_pin)
+			/* Transmit Frame Synchro (i.e. playback)
+			 * is input (format is _CFM) and the TK pin
+			 * is used for input (format _CBM_ but not
+			 * using the RK pin).
+			 */
+			mck_div = 6;
+		break;
+	}
+
+	switch (ssc_p->daifmt & SND_SOC_DAIFMT_MASTER_MASK) {
+	case SND_SOC_DAIFMT_CBS_CFS:
+		r.num = ssc_p->mck_rate / mck_div / frame_size;
+
+		ret = snd_interval_ratnum(i, 1, &r, &num, &den);
+		if (ret >= 0 && den && rule->var == SNDRV_PCM_HW_PARAM_RATE) {
+			params->rate_num = num;
+			params->rate_den = den;
+		}
+		break;
+
+	case SND_SOC_DAIFMT_CBM_CFS:
+	case SND_SOC_DAIFMT_CBM_CFM:
+		t.min = 8000;
+		t.max = ssc_p->mck_rate / mck_div / frame_size;
+		t.openmin = t.openmax = 0;
+		t.integer = 0;
+		ret = snd_interval_refine(i, &t);
+		break;
+
+	default:
+		ret = -EINVAL;
+		break;
+	}
+
+	return ret;
+}
 
 /*-------------------------------------------------------------------------*\
  * DAI functions
@@ -200,6 +288,7 @@ static int atmel_ssc_startup(struct snd_pcm_substream *substream,
 	struct atmel_ssc_info *ssc_p = &ssc_info[dai->id];
 	struct atmel_pcm_dma_params *dma_params;
 	int dir, dir_mask;
+	int ret;
 
 	pr_debug("atmel_ssc_startup: SSC_SR=0x%u\n",
 		ssc_readl(ssc_p->ssc->regs, SR));
@@ -207,6 +296,7 @@ static int atmel_ssc_startup(struct snd_pcm_substream *substream,
 	/* Enable PMC peripheral clock for this SSC */
 	pr_debug("atmel_ssc_dai: Starting clock\n");
 	clk_enable(ssc_p->ssc->clk);
+	ssc_p->mck_rate = clk_get_rate(ssc_p->ssc->clk);
 
 	/* Reset the SSC to keep it at a clean status */
 	ssc_writel(ssc_p->ssc->regs, CR, SSC_BIT(CR_SWRST));
@@ -219,6 +309,17 @@ static int atmel_ssc_startup(struct snd_pcm_substream *substream,
 		dir_mask = SSC_DIR_MASK_CAPTURE;
 	}
 
+	ret = snd_pcm_hw_rule_add(substream->runtime, 0,
+				  SNDRV_PCM_HW_PARAM_RATE,
+				  atmel_ssc_hw_rule_rate,
+				  ssc_p,
+				  SNDRV_PCM_HW_PARAM_FRAME_BITS,
+				  SNDRV_PCM_HW_PARAM_CHANNELS, -1);
+	if (ret < 0) {
+		dev_err(dai->dev, "Failed to specify rate rule: %d\n", ret);
+		return ret;
+	}
+
 	dma_params = &ssc_dma_params[dai->id][dir];
 	dma_params->ssc = ssc_p->ssc;
 	dma_params->substream = substream;
@@ -783,8 +884,6 @@ static int atmel_ssc_resume(struct snd_soc_dai *cpu_dai)
 #  define atmel_ssc_resume	NULL
 #endif /* CONFIG_PM */
 
-#define ATMEL_SSC_RATES (SNDRV_PCM_RATE_8000_96000)
-
 #define ATMEL_SSC_FORMATS (SNDRV_PCM_FMTBIT_S8     | SNDRV_PCM_FMTBIT_S16_LE |\
 			  SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE)
 
@@ -804,12 +903,16 @@ static struct snd_soc_dai_driver atmel_ssc_dai = {
 		.playback = {
 			.channels_min = 1,
 			.channels_max = 2,
-			.rates = ATMEL_SSC_RATES,
+			.rates = SNDRV_PCM_RATE_CONTINUOUS,
+			.rate_min = 8000,
+			.rate_max = 384000,
 			.formats = ATMEL_SSC_FORMATS,},
 		.capture = {
 			.channels_min = 1,
 			.channels_max = 2,
-			.rates = ATMEL_SSC_RATES,
+			.rates = SNDRV_PCM_RATE_CONTINUOUS,
+			.rate_min = 8000,
+			.rate_max = 384000,
 			.formats = ATMEL_SSC_FORMATS,},
 		.ops = &atmel_ssc_dai_ops,
 };
diff --git a/sound/soc/atmel/atmel_ssc_dai.h b/sound/soc/atmel/atmel_ssc_dai.h
index b1f08d511495..80b153857a88 100644
--- a/sound/soc/atmel/atmel_ssc_dai.h
+++ b/sound/soc/atmel/atmel_ssc_dai.h
@@ -115,6 +115,7 @@ struct atmel_ssc_info {
 	unsigned short rcmr_period;
 	struct atmel_pcm_dma_params *dma_params[2];
 	struct atmel_ssc_state ssc_state;
+	unsigned long mck_rate;
 };
 
 int atmel_ssc_set_audio(int ssc_id);
-- 
1.7.10.4

^ permalink raw reply related	[flat|nested] 7+ messages in thread

* Re: [PATCH v3] ASoC: atmel_ssc_dai: Allow more rates
  2015-02-09 15:08 ` Peter Rosin
  (?)
@ 2015-02-10  5:16   ` Bo Shen
  -1 siblings, 0 replies; 7+ messages in thread
From: Bo Shen @ 2015-02-10  5:16 UTC (permalink / raw)
  To: Peter Rosin, alsa-devel
  Cc: Peter Rosin, Liam Girdwood, Mark Brown, Jaroslav Kysela,
	Takashi Iwai, linux-kernel, linux-arm-kernel

Hi Peter,

On 02/09/2015 11:08 PM, Peter Rosin wrote:
> From: Peter Rosin <peda@axentia.se>
>
> When the SSC acts as BCK master, use a ratnum rule to limit
> the rate instead of only doing the standard rates. When the SSC
> acts as BCK slave, allow any BCK frequency up to the SSC master
> clock, divided by either of 2, 3 or 6.
>
> Put a cap at 384kHz. Who's /ever/ going to need more than that?
>
> The divider of 2, 3 or 6 is selected based on the Serial Clock Ratio
> Considerations section from the SSC documentation:
>
>      The Transmitter and the Receiver can be programmed to operate
>      with the clock signals provided on either the TK or RK pins.
>      This allows the SSC to support many slave-mode data transfers.
>      In this case, the maximum clock speed allowed on the RK pin is:
>      - Peripheral clock divided by 2 if Receiver Frame Synchro is input
>      - Peripheral clock divided by 3 if Receiver Frame Synchro is output
>      In addition, the maximum clock speed allowed on the TK pin is:
>      - Peripheral clock divided by 6 if Transmit Frame Synchro is input
>      - Peripheral clock divided by 2 if Transmit Frame Synchro is output
>
> Signed-off-by: Peter Rosin <peda@axentia.se>

Thanks for your patch.

Acked-by: Bo Shen <voice.shen@atmel.com>

> ---
>   sound/soc/atmel/atmel_ssc_dai.c |  111 +++++++++++++++++++++++++++++++++++++--
>   sound/soc/atmel/atmel_ssc_dai.h |    1 +
>   2 files changed, 108 insertions(+), 4 deletions(-)
>
> Changes since v2:
>
> - Killed the 500ppm reduction. Just trust the nominal clock
>    rates of the given clocks.
>
> - Don't multiply the given SSC clk rate by two to get to the
>    SSC master clock rate. That factor was the result of some
>    confusion on my part. The SSC clk *is* the SSC master clock.
>
> Changes since v1:
>
> - I have checked all Atmel datasheets I could get my hands on (and
>    that seemed relevant), and in every instance where they have
>    described the SSC, the description have the exact rate limitations
>    on the RK and TK pin that I have implemented here.
>
> - Improved the comments.
>
> - Rebased on top of the latest patches from Bo Chen.
>
> diff --git a/sound/soc/atmel/atmel_ssc_dai.c b/sound/soc/atmel/atmel_ssc_dai.c
> index 379ac2a6ab16..6b8e64899205 100644
> --- a/sound/soc/atmel/atmel_ssc_dai.c
> +++ b/sound/soc/atmel/atmel_ssc_dai.c
> @@ -187,6 +187,94 @@ static irqreturn_t atmel_ssc_interrupt(int irq, void *dev_id)
>   	return IRQ_HANDLED;
>   }
>
> +/*
> + * When the bit clock is input, limit the maximum rate according to the
> + * Serial Clock Ratio Considerations section from the SSC documentation:
> + *
> + *   The Transmitter and the Receiver can be programmed to operate
> + *   with the clock signals provided on either the TK or RK pins.
> + *   This allows the SSC to support many slave-mode data transfers.
> + *   In this case, the maximum clock speed allowed on the RK pin is:
> + *   - Peripheral clock divided by 2 if Receiver Frame Synchro is input
> + *   - Peripheral clock divided by 3 if Receiver Frame Synchro is output
> + *   In addition, the maximum clock speed allowed on the TK pin is:
> + *   - Peripheral clock divided by 6 if Transmit Frame Synchro is input
> + *   - Peripheral clock divided by 2 if Transmit Frame Synchro is output
> + *
> + * When the bit clock is output, limit the rate according to the
> + * SSC divider restrictions.
> + */
> +static int atmel_ssc_hw_rule_rate(struct snd_pcm_hw_params *params,
> +				  struct snd_pcm_hw_rule *rule)
> +{
> +	struct atmel_ssc_info *ssc_p = rule->private;
> +	struct ssc_device *ssc = ssc_p->ssc;
> +	struct snd_interval *i = hw_param_interval(params, rule->var);
> +	struct snd_interval t;
> +	struct snd_ratnum r = {
> +		.den_min = 1,
> +		.den_max = 4095,
> +		.den_step = 1,
> +	};
> +	unsigned int num = 0, den = 0;
> +	int frame_size;
> +	int mck_div = 2;
> +	int ret;
> +
> +	frame_size = snd_soc_params_to_frame_size(params);
> +	if (frame_size < 0)
> +		return frame_size;
> +
> +	switch (ssc_p->daifmt & SND_SOC_DAIFMT_MASTER_MASK) {
> +	case SND_SOC_DAIFMT_CBM_CFS:
> +		if ((ssc_p->dir_mask & SSC_DIR_MASK_CAPTURE)
> +		    && ssc->clk_from_rk_pin)
> +			/* Receiver Frame Synchro (i.e. capture)
> +			 * is output (format is _CFS) and the RK pin
> +			 * is used for input (format is _CBM_).
> +			 */
> +			mck_div = 3;
> +		break;
> +
> +	case SND_SOC_DAIFMT_CBM_CFM:
> +		if ((ssc_p->dir_mask & SSC_DIR_MASK_PLAYBACK)
> +		    && !ssc->clk_from_rk_pin)
> +			/* Transmit Frame Synchro (i.e. playback)
> +			 * is input (format is _CFM) and the TK pin
> +			 * is used for input (format _CBM_ but not
> +			 * using the RK pin).
> +			 */
> +			mck_div = 6;
> +		break;
> +	}
> +
> +	switch (ssc_p->daifmt & SND_SOC_DAIFMT_MASTER_MASK) {
> +	case SND_SOC_DAIFMT_CBS_CFS:
> +		r.num = ssc_p->mck_rate / mck_div / frame_size;
> +
> +		ret = snd_interval_ratnum(i, 1, &r, &num, &den);
> +		if (ret >= 0 && den && rule->var == SNDRV_PCM_HW_PARAM_RATE) {
> +			params->rate_num = num;
> +			params->rate_den = den;
> +		}
> +		break;
> +
> +	case SND_SOC_DAIFMT_CBM_CFS:
> +	case SND_SOC_DAIFMT_CBM_CFM:
> +		t.min = 8000;
> +		t.max = ssc_p->mck_rate / mck_div / frame_size;
> +		t.openmin = t.openmax = 0;
> +		t.integer = 0;
> +		ret = snd_interval_refine(i, &t);
> +		break;
> +
> +	default:
> +		ret = -EINVAL;
> +		break;
> +	}
> +
> +	return ret;
> +}
>
>   /*-------------------------------------------------------------------------*\
>    * DAI functions
> @@ -200,6 +288,7 @@ static int atmel_ssc_startup(struct snd_pcm_substream *substream,
>   	struct atmel_ssc_info *ssc_p = &ssc_info[dai->id];
>   	struct atmel_pcm_dma_params *dma_params;
>   	int dir, dir_mask;
> +	int ret;
>
>   	pr_debug("atmel_ssc_startup: SSC_SR=0x%u\n",
>   		ssc_readl(ssc_p->ssc->regs, SR));
> @@ -207,6 +296,7 @@ static int atmel_ssc_startup(struct snd_pcm_substream *substream,
>   	/* Enable PMC peripheral clock for this SSC */
>   	pr_debug("atmel_ssc_dai: Starting clock\n");
>   	clk_enable(ssc_p->ssc->clk);
> +	ssc_p->mck_rate = clk_get_rate(ssc_p->ssc->clk);
>
>   	/* Reset the SSC to keep it at a clean status */
>   	ssc_writel(ssc_p->ssc->regs, CR, SSC_BIT(CR_SWRST));
> @@ -219,6 +309,17 @@ static int atmel_ssc_startup(struct snd_pcm_substream *substream,
>   		dir_mask = SSC_DIR_MASK_CAPTURE;
>   	}
>
> +	ret = snd_pcm_hw_rule_add(substream->runtime, 0,
> +				  SNDRV_PCM_HW_PARAM_RATE,
> +				  atmel_ssc_hw_rule_rate,
> +				  ssc_p,
> +				  SNDRV_PCM_HW_PARAM_FRAME_BITS,
> +				  SNDRV_PCM_HW_PARAM_CHANNELS, -1);
> +	if (ret < 0) {
> +		dev_err(dai->dev, "Failed to specify rate rule: %d\n", ret);
> +		return ret;
> +	}
> +
>   	dma_params = &ssc_dma_params[dai->id][dir];
>   	dma_params->ssc = ssc_p->ssc;
>   	dma_params->substream = substream;
> @@ -783,8 +884,6 @@ static int atmel_ssc_resume(struct snd_soc_dai *cpu_dai)
>   #  define atmel_ssc_resume	NULL
>   #endif /* CONFIG_PM */
>
> -#define ATMEL_SSC_RATES (SNDRV_PCM_RATE_8000_96000)
> -
>   #define ATMEL_SSC_FORMATS (SNDRV_PCM_FMTBIT_S8     | SNDRV_PCM_FMTBIT_S16_LE |\
>   			  SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE)
>
> @@ -804,12 +903,16 @@ static struct snd_soc_dai_driver atmel_ssc_dai = {
>   		.playback = {
>   			.channels_min = 1,
>   			.channels_max = 2,
> -			.rates = ATMEL_SSC_RATES,
> +			.rates = SNDRV_PCM_RATE_CONTINUOUS,
> +			.rate_min = 8000,
> +			.rate_max = 384000,
>   			.formats = ATMEL_SSC_FORMATS,},
>   		.capture = {
>   			.channels_min = 1,
>   			.channels_max = 2,
> -			.rates = ATMEL_SSC_RATES,
> +			.rates = SNDRV_PCM_RATE_CONTINUOUS,
> +			.rate_min = 8000,
> +			.rate_max = 384000,
>   			.formats = ATMEL_SSC_FORMATS,},
>   		.ops = &atmel_ssc_dai_ops,
>   };
> diff --git a/sound/soc/atmel/atmel_ssc_dai.h b/sound/soc/atmel/atmel_ssc_dai.h
> index b1f08d511495..80b153857a88 100644
> --- a/sound/soc/atmel/atmel_ssc_dai.h
> +++ b/sound/soc/atmel/atmel_ssc_dai.h
> @@ -115,6 +115,7 @@ struct atmel_ssc_info {
>   	unsigned short rcmr_period;
>   	struct atmel_pcm_dma_params *dma_params[2];
>   	struct atmel_ssc_state ssc_state;
> +	unsigned long mck_rate;
>   };
>
>   int atmel_ssc_set_audio(int ssc_id);
>

Best Regards,
Bo Shen


^ permalink raw reply	[flat|nested] 7+ messages in thread

* Re: [PATCH v3] ASoC: atmel_ssc_dai: Allow more rates
@ 2015-02-10  5:16   ` Bo Shen
  0 siblings, 0 replies; 7+ messages in thread
From: Bo Shen @ 2015-02-10  5:16 UTC (permalink / raw)
  To: Peter Rosin, alsa-devel
  Cc: Peter Rosin, Liam Girdwood, Mark Brown, Jaroslav Kysela,
	Takashi Iwai, linux-kernel, linux-arm-kernel

Hi Peter,

On 02/09/2015 11:08 PM, Peter Rosin wrote:
> From: Peter Rosin <peda@axentia.se>
>
> When the SSC acts as BCK master, use a ratnum rule to limit
> the rate instead of only doing the standard rates. When the SSC
> acts as BCK slave, allow any BCK frequency up to the SSC master
> clock, divided by either of 2, 3 or 6.
>
> Put a cap at 384kHz. Who's /ever/ going to need more than that?
>
> The divider of 2, 3 or 6 is selected based on the Serial Clock Ratio
> Considerations section from the SSC documentation:
>
>      The Transmitter and the Receiver can be programmed to operate
>      with the clock signals provided on either the TK or RK pins.
>      This allows the SSC to support many slave-mode data transfers.
>      In this case, the maximum clock speed allowed on the RK pin is:
>      - Peripheral clock divided by 2 if Receiver Frame Synchro is input
>      - Peripheral clock divided by 3 if Receiver Frame Synchro is output
>      In addition, the maximum clock speed allowed on the TK pin is:
>      - Peripheral clock divided by 6 if Transmit Frame Synchro is input
>      - Peripheral clock divided by 2 if Transmit Frame Synchro is output
>
> Signed-off-by: Peter Rosin <peda@axentia.se>

Thanks for your patch.

Acked-by: Bo Shen <voice.shen@atmel.com>

> ---
>   sound/soc/atmel/atmel_ssc_dai.c |  111 +++++++++++++++++++++++++++++++++++++--
>   sound/soc/atmel/atmel_ssc_dai.h |    1 +
>   2 files changed, 108 insertions(+), 4 deletions(-)
>
> Changes since v2:
>
> - Killed the 500ppm reduction. Just trust the nominal clock
>    rates of the given clocks.
>
> - Don't multiply the given SSC clk rate by two to get to the
>    SSC master clock rate. That factor was the result of some
>    confusion on my part. The SSC clk *is* the SSC master clock.
>
> Changes since v1:
>
> - I have checked all Atmel datasheets I could get my hands on (and
>    that seemed relevant), and in every instance where they have
>    described the SSC, the description have the exact rate limitations
>    on the RK and TK pin that I have implemented here.
>
> - Improved the comments.
>
> - Rebased on top of the latest patches from Bo Chen.
>
> diff --git a/sound/soc/atmel/atmel_ssc_dai.c b/sound/soc/atmel/atmel_ssc_dai.c
> index 379ac2a6ab16..6b8e64899205 100644
> --- a/sound/soc/atmel/atmel_ssc_dai.c
> +++ b/sound/soc/atmel/atmel_ssc_dai.c
> @@ -187,6 +187,94 @@ static irqreturn_t atmel_ssc_interrupt(int irq, void *dev_id)
>   	return IRQ_HANDLED;
>   }
>
> +/*
> + * When the bit clock is input, limit the maximum rate according to the
> + * Serial Clock Ratio Considerations section from the SSC documentation:
> + *
> + *   The Transmitter and the Receiver can be programmed to operate
> + *   with the clock signals provided on either the TK or RK pins.
> + *   This allows the SSC to support many slave-mode data transfers.
> + *   In this case, the maximum clock speed allowed on the RK pin is:
> + *   - Peripheral clock divided by 2 if Receiver Frame Synchro is input
> + *   - Peripheral clock divided by 3 if Receiver Frame Synchro is output
> + *   In addition, the maximum clock speed allowed on the TK pin is:
> + *   - Peripheral clock divided by 6 if Transmit Frame Synchro is input
> + *   - Peripheral clock divided by 2 if Transmit Frame Synchro is output
> + *
> + * When the bit clock is output, limit the rate according to the
> + * SSC divider restrictions.
> + */
> +static int atmel_ssc_hw_rule_rate(struct snd_pcm_hw_params *params,
> +				  struct snd_pcm_hw_rule *rule)
> +{
> +	struct atmel_ssc_info *ssc_p = rule->private;
> +	struct ssc_device *ssc = ssc_p->ssc;
> +	struct snd_interval *i = hw_param_interval(params, rule->var);
> +	struct snd_interval t;
> +	struct snd_ratnum r = {
> +		.den_min = 1,
> +		.den_max = 4095,
> +		.den_step = 1,
> +	};
> +	unsigned int num = 0, den = 0;
> +	int frame_size;
> +	int mck_div = 2;
> +	int ret;
> +
> +	frame_size = snd_soc_params_to_frame_size(params);
> +	if (frame_size < 0)
> +		return frame_size;
> +
> +	switch (ssc_p->daifmt & SND_SOC_DAIFMT_MASTER_MASK) {
> +	case SND_SOC_DAIFMT_CBM_CFS:
> +		if ((ssc_p->dir_mask & SSC_DIR_MASK_CAPTURE)
> +		    && ssc->clk_from_rk_pin)
> +			/* Receiver Frame Synchro (i.e. capture)
> +			 * is output (format is _CFS) and the RK pin
> +			 * is used for input (format is _CBM_).
> +			 */
> +			mck_div = 3;
> +		break;
> +
> +	case SND_SOC_DAIFMT_CBM_CFM:
> +		if ((ssc_p->dir_mask & SSC_DIR_MASK_PLAYBACK)
> +		    && !ssc->clk_from_rk_pin)
> +			/* Transmit Frame Synchro (i.e. playback)
> +			 * is input (format is _CFM) and the TK pin
> +			 * is used for input (format _CBM_ but not
> +			 * using the RK pin).
> +			 */
> +			mck_div = 6;
> +		break;
> +	}
> +
> +	switch (ssc_p->daifmt & SND_SOC_DAIFMT_MASTER_MASK) {
> +	case SND_SOC_DAIFMT_CBS_CFS:
> +		r.num = ssc_p->mck_rate / mck_div / frame_size;
> +
> +		ret = snd_interval_ratnum(i, 1, &r, &num, &den);
> +		if (ret >= 0 && den && rule->var == SNDRV_PCM_HW_PARAM_RATE) {
> +			params->rate_num = num;
> +			params->rate_den = den;
> +		}
> +		break;
> +
> +	case SND_SOC_DAIFMT_CBM_CFS:
> +	case SND_SOC_DAIFMT_CBM_CFM:
> +		t.min = 8000;
> +		t.max = ssc_p->mck_rate / mck_div / frame_size;
> +		t.openmin = t.openmax = 0;
> +		t.integer = 0;
> +		ret = snd_interval_refine(i, &t);
> +		break;
> +
> +	default:
> +		ret = -EINVAL;
> +		break;
> +	}
> +
> +	return ret;
> +}
>
>   /*-------------------------------------------------------------------------*\
>    * DAI functions
> @@ -200,6 +288,7 @@ static int atmel_ssc_startup(struct snd_pcm_substream *substream,
>   	struct atmel_ssc_info *ssc_p = &ssc_info[dai->id];
>   	struct atmel_pcm_dma_params *dma_params;
>   	int dir, dir_mask;
> +	int ret;
>
>   	pr_debug("atmel_ssc_startup: SSC_SR=0x%u\n",
>   		ssc_readl(ssc_p->ssc->regs, SR));
> @@ -207,6 +296,7 @@ static int atmel_ssc_startup(struct snd_pcm_substream *substream,
>   	/* Enable PMC peripheral clock for this SSC */
>   	pr_debug("atmel_ssc_dai: Starting clock\n");
>   	clk_enable(ssc_p->ssc->clk);
> +	ssc_p->mck_rate = clk_get_rate(ssc_p->ssc->clk);
>
>   	/* Reset the SSC to keep it at a clean status */
>   	ssc_writel(ssc_p->ssc->regs, CR, SSC_BIT(CR_SWRST));
> @@ -219,6 +309,17 @@ static int atmel_ssc_startup(struct snd_pcm_substream *substream,
>   		dir_mask = SSC_DIR_MASK_CAPTURE;
>   	}
>
> +	ret = snd_pcm_hw_rule_add(substream->runtime, 0,
> +				  SNDRV_PCM_HW_PARAM_RATE,
> +				  atmel_ssc_hw_rule_rate,
> +				  ssc_p,
> +				  SNDRV_PCM_HW_PARAM_FRAME_BITS,
> +				  SNDRV_PCM_HW_PARAM_CHANNELS, -1);
> +	if (ret < 0) {
> +		dev_err(dai->dev, "Failed to specify rate rule: %d\n", ret);
> +		return ret;
> +	}
> +
>   	dma_params = &ssc_dma_params[dai->id][dir];
>   	dma_params->ssc = ssc_p->ssc;
>   	dma_params->substream = substream;
> @@ -783,8 +884,6 @@ static int atmel_ssc_resume(struct snd_soc_dai *cpu_dai)
>   #  define atmel_ssc_resume	NULL
>   #endif /* CONFIG_PM */
>
> -#define ATMEL_SSC_RATES (SNDRV_PCM_RATE_8000_96000)
> -
>   #define ATMEL_SSC_FORMATS (SNDRV_PCM_FMTBIT_S8     | SNDRV_PCM_FMTBIT_S16_LE |\
>   			  SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE)
>
> @@ -804,12 +903,16 @@ static struct snd_soc_dai_driver atmel_ssc_dai = {
>   		.playback = {
>   			.channels_min = 1,
>   			.channels_max = 2,
> -			.rates = ATMEL_SSC_RATES,
> +			.rates = SNDRV_PCM_RATE_CONTINUOUS,
> +			.rate_min = 8000,
> +			.rate_max = 384000,
>   			.formats = ATMEL_SSC_FORMATS,},
>   		.capture = {
>   			.channels_min = 1,
>   			.channels_max = 2,
> -			.rates = ATMEL_SSC_RATES,
> +			.rates = SNDRV_PCM_RATE_CONTINUOUS,
> +			.rate_min = 8000,
> +			.rate_max = 384000,
>   			.formats = ATMEL_SSC_FORMATS,},
>   		.ops = &atmel_ssc_dai_ops,
>   };
> diff --git a/sound/soc/atmel/atmel_ssc_dai.h b/sound/soc/atmel/atmel_ssc_dai.h
> index b1f08d511495..80b153857a88 100644
> --- a/sound/soc/atmel/atmel_ssc_dai.h
> +++ b/sound/soc/atmel/atmel_ssc_dai.h
> @@ -115,6 +115,7 @@ struct atmel_ssc_info {
>   	unsigned short rcmr_period;
>   	struct atmel_pcm_dma_params *dma_params[2];
>   	struct atmel_ssc_state ssc_state;
> +	unsigned long mck_rate;
>   };
>
>   int atmel_ssc_set_audio(int ssc_id);
>

Best Regards,
Bo Shen

^ permalink raw reply	[flat|nested] 7+ messages in thread

* [PATCH v3] ASoC: atmel_ssc_dai: Allow more rates
@ 2015-02-10  5:16   ` Bo Shen
  0 siblings, 0 replies; 7+ messages in thread
From: Bo Shen @ 2015-02-10  5:16 UTC (permalink / raw)
  To: linux-arm-kernel

Hi Peter,

On 02/09/2015 11:08 PM, Peter Rosin wrote:
> From: Peter Rosin <peda@axentia.se>
>
> When the SSC acts as BCK master, use a ratnum rule to limit
> the rate instead of only doing the standard rates. When the SSC
> acts as BCK slave, allow any BCK frequency up to the SSC master
> clock, divided by either of 2, 3 or 6.
>
> Put a cap at 384kHz. Who's /ever/ going to need more than that?
>
> The divider of 2, 3 or 6 is selected based on the Serial Clock Ratio
> Considerations section from the SSC documentation:
>
>      The Transmitter and the Receiver can be programmed to operate
>      with the clock signals provided on either the TK or RK pins.
>      This allows the SSC to support many slave-mode data transfers.
>      In this case, the maximum clock speed allowed on the RK pin is:
>      - Peripheral clock divided by 2 if Receiver Frame Synchro is input
>      - Peripheral clock divided by 3 if Receiver Frame Synchro is output
>      In addition, the maximum clock speed allowed on the TK pin is:
>      - Peripheral clock divided by 6 if Transmit Frame Synchro is input
>      - Peripheral clock divided by 2 if Transmit Frame Synchro is output
>
> Signed-off-by: Peter Rosin <peda@axentia.se>

Thanks for your patch.

Acked-by: Bo Shen <voice.shen@atmel.com>

> ---
>   sound/soc/atmel/atmel_ssc_dai.c |  111 +++++++++++++++++++++++++++++++++++++--
>   sound/soc/atmel/atmel_ssc_dai.h |    1 +
>   2 files changed, 108 insertions(+), 4 deletions(-)
>
> Changes since v2:
>
> - Killed the 500ppm reduction. Just trust the nominal clock
>    rates of the given clocks.
>
> - Don't multiply the given SSC clk rate by two to get to the
>    SSC master clock rate. That factor was the result of some
>    confusion on my part. The SSC clk *is* the SSC master clock.
>
> Changes since v1:
>
> - I have checked all Atmel datasheets I could get my hands on (and
>    that seemed relevant), and in every instance where they have
>    described the SSC, the description have the exact rate limitations
>    on the RK and TK pin that I have implemented here.
>
> - Improved the comments.
>
> - Rebased on top of the latest patches from Bo Chen.
>
> diff --git a/sound/soc/atmel/atmel_ssc_dai.c b/sound/soc/atmel/atmel_ssc_dai.c
> index 379ac2a6ab16..6b8e64899205 100644
> --- a/sound/soc/atmel/atmel_ssc_dai.c
> +++ b/sound/soc/atmel/atmel_ssc_dai.c
> @@ -187,6 +187,94 @@ static irqreturn_t atmel_ssc_interrupt(int irq, void *dev_id)
>   	return IRQ_HANDLED;
>   }
>
> +/*
> + * When the bit clock is input, limit the maximum rate according to the
> + * Serial Clock Ratio Considerations section from the SSC documentation:
> + *
> + *   The Transmitter and the Receiver can be programmed to operate
> + *   with the clock signals provided on either the TK or RK pins.
> + *   This allows the SSC to support many slave-mode data transfers.
> + *   In this case, the maximum clock speed allowed on the RK pin is:
> + *   - Peripheral clock divided by 2 if Receiver Frame Synchro is input
> + *   - Peripheral clock divided by 3 if Receiver Frame Synchro is output
> + *   In addition, the maximum clock speed allowed on the TK pin is:
> + *   - Peripheral clock divided by 6 if Transmit Frame Synchro is input
> + *   - Peripheral clock divided by 2 if Transmit Frame Synchro is output
> + *
> + * When the bit clock is output, limit the rate according to the
> + * SSC divider restrictions.
> + */
> +static int atmel_ssc_hw_rule_rate(struct snd_pcm_hw_params *params,
> +				  struct snd_pcm_hw_rule *rule)
> +{
> +	struct atmel_ssc_info *ssc_p = rule->private;
> +	struct ssc_device *ssc = ssc_p->ssc;
> +	struct snd_interval *i = hw_param_interval(params, rule->var);
> +	struct snd_interval t;
> +	struct snd_ratnum r = {
> +		.den_min = 1,
> +		.den_max = 4095,
> +		.den_step = 1,
> +	};
> +	unsigned int num = 0, den = 0;
> +	int frame_size;
> +	int mck_div = 2;
> +	int ret;
> +
> +	frame_size = snd_soc_params_to_frame_size(params);
> +	if (frame_size < 0)
> +		return frame_size;
> +
> +	switch (ssc_p->daifmt & SND_SOC_DAIFMT_MASTER_MASK) {
> +	case SND_SOC_DAIFMT_CBM_CFS:
> +		if ((ssc_p->dir_mask & SSC_DIR_MASK_CAPTURE)
> +		    && ssc->clk_from_rk_pin)
> +			/* Receiver Frame Synchro (i.e. capture)
> +			 * is output (format is _CFS) and the RK pin
> +			 * is used for input (format is _CBM_).
> +			 */
> +			mck_div = 3;
> +		break;
> +
> +	case SND_SOC_DAIFMT_CBM_CFM:
> +		if ((ssc_p->dir_mask & SSC_DIR_MASK_PLAYBACK)
> +		    && !ssc->clk_from_rk_pin)
> +			/* Transmit Frame Synchro (i.e. playback)
> +			 * is input (format is _CFM) and the TK pin
> +			 * is used for input (format _CBM_ but not
> +			 * using the RK pin).
> +			 */
> +			mck_div = 6;
> +		break;
> +	}
> +
> +	switch (ssc_p->daifmt & SND_SOC_DAIFMT_MASTER_MASK) {
> +	case SND_SOC_DAIFMT_CBS_CFS:
> +		r.num = ssc_p->mck_rate / mck_div / frame_size;
> +
> +		ret = snd_interval_ratnum(i, 1, &r, &num, &den);
> +		if (ret >= 0 && den && rule->var == SNDRV_PCM_HW_PARAM_RATE) {
> +			params->rate_num = num;
> +			params->rate_den = den;
> +		}
> +		break;
> +
> +	case SND_SOC_DAIFMT_CBM_CFS:
> +	case SND_SOC_DAIFMT_CBM_CFM:
> +		t.min = 8000;
> +		t.max = ssc_p->mck_rate / mck_div / frame_size;
> +		t.openmin = t.openmax = 0;
> +		t.integer = 0;
> +		ret = snd_interval_refine(i, &t);
> +		break;
> +
> +	default:
> +		ret = -EINVAL;
> +		break;
> +	}
> +
> +	return ret;
> +}
>
>   /*-------------------------------------------------------------------------*\
>    * DAI functions
> @@ -200,6 +288,7 @@ static int atmel_ssc_startup(struct snd_pcm_substream *substream,
>   	struct atmel_ssc_info *ssc_p = &ssc_info[dai->id];
>   	struct atmel_pcm_dma_params *dma_params;
>   	int dir, dir_mask;
> +	int ret;
>
>   	pr_debug("atmel_ssc_startup: SSC_SR=0x%u\n",
>   		ssc_readl(ssc_p->ssc->regs, SR));
> @@ -207,6 +296,7 @@ static int atmel_ssc_startup(struct snd_pcm_substream *substream,
>   	/* Enable PMC peripheral clock for this SSC */
>   	pr_debug("atmel_ssc_dai: Starting clock\n");
>   	clk_enable(ssc_p->ssc->clk);
> +	ssc_p->mck_rate = clk_get_rate(ssc_p->ssc->clk);
>
>   	/* Reset the SSC to keep it at a clean status */
>   	ssc_writel(ssc_p->ssc->regs, CR, SSC_BIT(CR_SWRST));
> @@ -219,6 +309,17 @@ static int atmel_ssc_startup(struct snd_pcm_substream *substream,
>   		dir_mask = SSC_DIR_MASK_CAPTURE;
>   	}
>
> +	ret = snd_pcm_hw_rule_add(substream->runtime, 0,
> +				  SNDRV_PCM_HW_PARAM_RATE,
> +				  atmel_ssc_hw_rule_rate,
> +				  ssc_p,
> +				  SNDRV_PCM_HW_PARAM_FRAME_BITS,
> +				  SNDRV_PCM_HW_PARAM_CHANNELS, -1);
> +	if (ret < 0) {
> +		dev_err(dai->dev, "Failed to specify rate rule: %d\n", ret);
> +		return ret;
> +	}
> +
>   	dma_params = &ssc_dma_params[dai->id][dir];
>   	dma_params->ssc = ssc_p->ssc;
>   	dma_params->substream = substream;
> @@ -783,8 +884,6 @@ static int atmel_ssc_resume(struct snd_soc_dai *cpu_dai)
>   #  define atmel_ssc_resume	NULL
>   #endif /* CONFIG_PM */
>
> -#define ATMEL_SSC_RATES (SNDRV_PCM_RATE_8000_96000)
> -
>   #define ATMEL_SSC_FORMATS (SNDRV_PCM_FMTBIT_S8     | SNDRV_PCM_FMTBIT_S16_LE |\
>   			  SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE)
>
> @@ -804,12 +903,16 @@ static struct snd_soc_dai_driver atmel_ssc_dai = {
>   		.playback = {
>   			.channels_min = 1,
>   			.channels_max = 2,
> -			.rates = ATMEL_SSC_RATES,
> +			.rates = SNDRV_PCM_RATE_CONTINUOUS,
> +			.rate_min = 8000,
> +			.rate_max = 384000,
>   			.formats = ATMEL_SSC_FORMATS,},
>   		.capture = {
>   			.channels_min = 1,
>   			.channels_max = 2,
> -			.rates = ATMEL_SSC_RATES,
> +			.rates = SNDRV_PCM_RATE_CONTINUOUS,
> +			.rate_min = 8000,
> +			.rate_max = 384000,
>   			.formats = ATMEL_SSC_FORMATS,},
>   		.ops = &atmel_ssc_dai_ops,
>   };
> diff --git a/sound/soc/atmel/atmel_ssc_dai.h b/sound/soc/atmel/atmel_ssc_dai.h
> index b1f08d511495..80b153857a88 100644
> --- a/sound/soc/atmel/atmel_ssc_dai.h
> +++ b/sound/soc/atmel/atmel_ssc_dai.h
> @@ -115,6 +115,7 @@ struct atmel_ssc_info {
>   	unsigned short rcmr_period;
>   	struct atmel_pcm_dma_params *dma_params[2];
>   	struct atmel_ssc_state ssc_state;
> +	unsigned long mck_rate;
>   };
>
>   int atmel_ssc_set_audio(int ssc_id);
>

Best Regards,
Bo Shen

^ permalink raw reply	[flat|nested] 7+ messages in thread

* Re: [PATCH v3] ASoC: atmel_ssc_dai: Allow more rates
  2015-02-09 15:08 ` Peter Rosin
@ 2015-02-10  7:12   ` Mark Brown
  -1 siblings, 0 replies; 7+ messages in thread
From: Mark Brown @ 2015-02-10  7:12 UTC (permalink / raw)
  To: Peter Rosin
  Cc: alsa-devel, Peter Rosin, Bo Shen, Liam Girdwood, Jaroslav Kysela,
	Takashi Iwai, linux-kernel, linux-arm-kernel

[-- Attachment #1: Type: text/plain, Size: 364 bytes --]

On Mon, Feb 09, 2015 at 04:08:25PM +0100, Peter Rosin wrote:
> From: Peter Rosin <peda@axentia.se>
> 
> When the SSC acts as BCK master, use a ratnum rule to limit
> the rate instead of only doing the standard rates. When the SSC
> acts as BCK slave, allow any BCK frequency up to the SSC master
> clock, divided by either of 2, 3 or 6.

Applied, thanks.

[-- Attachment #2: Digital signature --]
[-- Type: application/pgp-signature, Size: 473 bytes --]

^ permalink raw reply	[flat|nested] 7+ messages in thread

* [PATCH v3] ASoC: atmel_ssc_dai: Allow more rates
@ 2015-02-10  7:12   ` Mark Brown
  0 siblings, 0 replies; 7+ messages in thread
From: Mark Brown @ 2015-02-10  7:12 UTC (permalink / raw)
  To: linux-arm-kernel

On Mon, Feb 09, 2015 at 04:08:25PM +0100, Peter Rosin wrote:
> From: Peter Rosin <peda@axentia.se>
> 
> When the SSC acts as BCK master, use a ratnum rule to limit
> the rate instead of only doing the standard rates. When the SSC
> acts as BCK slave, allow any BCK frequency up to the SSC master
> clock, divided by either of 2, 3 or 6.

Applied, thanks.
-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 473 bytes
Desc: Digital signature
URL: <http://lists.infradead.org/pipermail/linux-arm-kernel/attachments/20150210/9f182c48/attachment.sig>

^ permalink raw reply	[flat|nested] 7+ messages in thread

end of thread, other threads:[~2015-02-10  7:50 UTC | newest]

Thread overview: 7+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2015-02-09 15:08 [PATCH v3] ASoC: atmel_ssc_dai: Allow more rates Peter Rosin
2015-02-09 15:08 ` Peter Rosin
2015-02-10  5:16 ` Bo Shen
2015-02-10  5:16   ` Bo Shen
2015-02-10  5:16   ` Bo Shen
2015-02-10  7:12 ` Mark Brown
2015-02-10  7:12   ` Mark Brown

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.