[alsa-devel] [PATCH] ALSA: ASoC: support more sample rates on raumfeld devices
Add support for sample rates other than 44100Khz on raumfeld audio devices. Force the codec's supported sample rates as the table of supported rates which is calculated at run-time does not suffice: if we call snd_soc_dai_set_sysclk() at stream startup, half of the possible rates are missing.
Use the external audio clock generator to provide double data rate clocks as the PXA's internal baud generator does anything but what's described in the datasheets.
Signed-off-by: Daniel Mack daniel@caiaq.de Cc: Mark Brown broonie@opensource.wolfsonmicro.com Cc: Timur Tabi timur@freescale.com
Signed-off-by: Daniel Mack daniel@caiaq.de --- sound/soc/pxa/raumfeld.c | 61 ++++++++++++++++++++++++++++++---------------- 1 files changed, 40 insertions(+), 21 deletions(-)
diff --git a/sound/soc/pxa/raumfeld.c b/sound/soc/pxa/raumfeld.c index e713181..8d65a49 100644 --- a/sound/soc/pxa/raumfeld.c +++ b/sound/soc/pxa/raumfeld.c @@ -41,7 +41,9 @@ static struct i2c_board_info max9486_hwmon_info = { };
#define MAX9485_MCLK_FREQ_112896 0x22 -#define MAX9485_MCLK_FREQ_122880 0x23 +#define MAX9485_MCLK_FREQ_122880 0x23 +#define MAX9485_MCLK_FREQ_225792 0x32 +#define MAX9485_MCLK_FREQ_245760 0x33
static void set_max9485_clk(char clk) { @@ -71,9 +73,18 @@ static int raumfeld_cs4270_startup(struct snd_pcm_substream *substream) struct snd_soc_pcm_runtime *rtd = substream->private_data; struct snd_soc_dai *codec_dai = rtd->dai->codec_dai;
- set_max9485_clk(MAX9485_MCLK_FREQ_112896); + codec_dai->playback.rates = SNDRV_PCM_RATE_44100 | + SNDRV_PCM_RATE_48000 | + SNDRV_PCM_RATE_88200 | + SNDRV_PCM_RATE_96000; + codec_dai->playback.rate_min = 44100; + codec_dai->playback.rate_max = 48000;
- return snd_soc_dai_set_sysclk(codec_dai, 0, 11289600, 0); + codec_dai->capture.rates = codec_dai->playback.rates; + codec_dai->capture.rate_min = codec_dai->playback.rate_min; + codec_dai->capture.rate_max = codec_dai->playback.rate_max; + + return 0; }
static int raumfeld_cs4270_hw_params(struct snd_pcm_substream *substream, @@ -86,20 +97,24 @@ static int raumfeld_cs4270_hw_params(struct snd_pcm_substream *substream, int ret = 0;
switch (params_rate(params)) { - case 8000: - case 16000: + case 44100: + set_max9485_clk(MAX9485_MCLK_FREQ_112896); + clk = 11289600; + break; case 48000: - case 96000: set_max9485_clk(MAX9485_MCLK_FREQ_122880); clk = 12288000; break; - case 11025: - case 22050: - case 44100: case 88200: - set_max9485_clk(MAX9485_MCLK_FREQ_112896); - clk = 11289600; + set_max9485_clk(MAX9485_MCLK_FREQ_225792); + clk = 22579200; + break; + case 96000: + set_max9485_clk(MAX9485_MCLK_FREQ_245760); + clk = 24576000; break; + default: + return -EINVAL; }
fmt = SND_SOC_DAIFMT_I2S | @@ -128,7 +143,7 @@ static int raumfeld_cs4270_hw_params(struct snd_pcm_substream *substream, if (ret < 0) return ret;
- ret = snd_soc_dai_set_sysclk(cpu_dai, PXA_SSP_CLK_EXT, 0, 1); + ret = snd_soc_dai_set_sysclk(cpu_dai, PXA_SSP_CLK_EXT, clk, 1); if (ret < 0) return ret;
@@ -181,20 +196,24 @@ static int raumfeld_ak4104_hw_params(struct snd_pcm_substream *substream, int fmt, ret = 0, clk = 0;
switch (params_rate(params)) { - case 8000: - case 16000: + case 44100: + set_max9485_clk(MAX9485_MCLK_FREQ_112896); + clk = 11289600; + break; case 48000: - case 96000: set_max9485_clk(MAX9485_MCLK_FREQ_122880); clk = 12288000; break; - case 11025: - case 22050: - case 44100: case 88200: - set_max9485_clk(MAX9485_MCLK_FREQ_112896); - clk = 11289600; + set_max9485_clk(MAX9485_MCLK_FREQ_225792); + clk = 22579200; + break; + case 96000: + set_max9485_clk(MAX9485_MCLK_FREQ_245760); + clk = 24576000; break; + default: + return -EINVAL; }
fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF; @@ -217,7 +236,7 @@ static int raumfeld_ak4104_hw_params(struct snd_pcm_substream *substream, if (ret < 0) return ret;
- ret = snd_soc_dai_set_sysclk(cpu_dai, PXA_SSP_CLK_EXT, 0, 1); + ret = snd_soc_dai_set_sysclk(cpu_dai, PXA_SSP_CLK_EXT, clk, 1); if (ret < 0) return ret;
On Wed, Jan 13, 2010 at 11:25:38AM +0100, Daniel Mack wrote:
Add support for sample rates other than 44100Khz on raumfeld audio devices. Force the codec's supported sample rates as the table of supported rates which is calculated at run-time does not suffice: if we call snd_soc_dai_set_sysclk() at stream startup, half of the possible rates are missing.
I'm not massively enthusiastic about this - I'd much rather be able to give the CODEC a hook to let it know what MCLK rates it can have and a callback to update them. It's a perfect case for the clock API but of course we can't use that off-SoC :/
Mark Brown wrote:
On Wed, Jan 13, 2010 at 11:25:38AM +0100, Daniel Mack wrote:
Add support for sample rates other than 44100Khz on raumfeld audio devices. Force the codec's supported sample rates as the table of supported rates which is calculated at run-time does not suffice: if we call snd_soc_dai_set_sysclk() at stream startup, half of the possible rates are missing.
I'm not massively enthusiastic about this - I'd much rather be able to give the CODEC a hook to let it know what MCLK rates it can have and a callback to update them. It's a perfect case for the clock API but of course we can't use that off-SoC :/
This is how I was planning on doing it for the P1022DS.
On Wed, Jan 13, 2010 at 01:02:09PM +0000, Mark Brown wrote:
On Wed, Jan 13, 2010 at 11:25:38AM +0100, Daniel Mack wrote:
Add support for sample rates other than 44100Khz on raumfeld audio devices. Force the codec's supported sample rates as the table of supported rates which is calculated at run-time does not suffice: if we call snd_soc_dai_set_sysclk() at stream startup, half of the possible rates are missing.
I'm not massively enthusiastic about this -
Me neither, and I agree to your objections.
I'd much rather be able to give the CODEC a hook to let it know what MCLK rates it can have and a callback to update them. It's a perfect case for the clock API but of course we can't use that off-SoC :/
I thought about this too and disliked it as well. It would make the codec be in charge of dealing with clock changes, which is - in this case particularily at least - not apropriate as the codec is slave to all clocks.
For this application, the codec driver would need to be as dumb as possible: I would just get the information about which MCLK and LRCLK it runs with and it could reject invalid configurations by return value. But that would imply major changes to the clocking logic in the cs4270 driver, which is fine as-is when it acts as master.
I'm uncertain and hence I just went the other way which bypasses this logic for now, also not to break existing platforms.
Daniel
On Wed, Jan 13, 2010 at 04:43:20PM +0100, Daniel Mack wrote:
For this application, the codec driver would need to be as dumb as possible: I would just get the information about which MCLK and LRCLK it runs with and it could reject invalid configurations by return value. But that would imply major changes to the clocking logic in the cs4270 driver, which is fine as-is when it acts as master.
That's not quite true. The problem you're seeing is that the CODEC driver has variable constraints depending on the master clock, so it at least needs to know what master clocks it might get so it can specify the constraints. Even more ideally than what I suggested we'd be able to use a clock API with constraint handling which we could use to figure stuff out but obviously tackling that is very much an uphill struggle.
I'm uncertain and hence I just went the other way which bypasses this logic for now, also not to break existing platforms.
It doesn't do anything for maintainability of the stack, though, especially if other people try to copy it.
If you're going to do anything that doesn't involve the core API I'd have the CODEC driver manually apply the constraints from MCLK in the startup function iff an MCLK is currently set. Machines with fixed MCLKs can set the MCLK on init() and machines with dynamic MCLKs can set the value in hw_params() and reset it when shutting down, imposing any constraints that do exist themselves. The rates fixed in the DAI would be all the rates that can possibly be used.
For setups with variable MCLKs, the current logic of limiting the available sampling rates at startup time is not sufficient. We need to be able to change the setting at a later point, and so the codec must offer all possible rates until the hw_params are given.
This patches allows that by passing 0 as 'freq' argument to cs4270_set_dai_sysclk().
Signed-off-by: Daniel Mack daniel@caiaq.de Cc: Mark Brown broonie@opensource.wolfsonmicro.com Cc: Timur Tabi timur@freescale.com --- sound/soc/codecs/cs4270.c | 40 ++++++++++++++++++++++++++-------------- 1 files changed, 26 insertions(+), 14 deletions(-)
diff --git a/sound/soc/codecs/cs4270.c b/sound/soc/codecs/cs4270.c index ffe122d..37ed60d 100644 --- a/sound/soc/codecs/cs4270.c +++ b/sound/soc/codecs/cs4270.c @@ -192,6 +192,11 @@ static struct cs4270_mode_ratios cs4270_mode_ratios[] = { * This function must be called by the machine driver's 'startup' function, * otherwise the list of supported sample rates will not be available in * time for ALSA. + * + * For setups with variable MCLKs, pass 0 as 'freq' argument. This will cause + * theoretically possible sample rates to be enabled. Call it again with a + * proper value set one the external clock is set (most probably you would do + * that from a machine's driver 'hw_param' hook. */ static int cs4270_set_dai_sysclk(struct snd_soc_dai *codec_dai, int clk_id, unsigned int freq, int dir) @@ -205,20 +210,27 @@ static int cs4270_set_dai_sysclk(struct snd_soc_dai *codec_dai,
cs4270->mclk = freq;
- for (i = 0; i < NUM_MCLK_RATIOS; i++) { - unsigned int rate = freq / cs4270_mode_ratios[i].ratio; - rates |= snd_pcm_rate_to_rate_bit(rate); - if (rate < rate_min) - rate_min = rate; - if (rate > rate_max) - rate_max = rate; - } - /* FIXME: soc should support a rate list */ - rates &= ~SNDRV_PCM_RATE_KNOT; + if (cs4270->mclk) { + for (i = 0; i < NUM_MCLK_RATIOS; i++) { + unsigned int rate = freq / cs4270_mode_ratios[i].ratio; + rates |= snd_pcm_rate_to_rate_bit(rate); + if (rate < rate_min) + rate_min = rate; + if (rate > rate_max) + rate_max = rate; + } + /* FIXME: soc should support a rate list */ + rates &= ~SNDRV_PCM_RATE_KNOT;
- if (!rates) { - dev_err(codec->dev, "could not find a valid sample rate\n"); - return -EINVAL; + if (!rates) { + dev_err(codec->dev, "could not find a valid sample rate\n"); + return -EINVAL; + } + } else { + /* enable all possible rates */ + rates = SNDRV_PCM_RATE_8000_192000; + rate_min = 8000; + rate_max = 192000; }
codec_dai->playback.rates = rates;
Add support for sample rates other than 44100Khz on raumfeld audio devices. At startup time, call snd_soc_dai_set_sysclk() with 0 as 'freq' argument so it offers all the sample rates. Later, the function is called again to give proper constraints.
Use the external audio clock generator to provide double data rate clocks as the PXA's internal baud generator does anything but what's described in the datasheets.
Signed-off-by: Daniel Mack daniel@caiaq.de Cc: Mark Brown broonie@opensource.wolfsonmicro.com Cc: Timur Tabi timur@freescale.com --- sound/soc/pxa/raumfeld.c | 61 ++++++++++++++++++++++++++++++---------------- 1 files changed, 40 insertions(+), 21 deletions(-)
diff --git a/sound/soc/pxa/raumfeld.c b/sound/soc/pxa/raumfeld.c index e713181..66e0dd8 100644 --- a/sound/soc/pxa/raumfeld.c +++ b/sound/soc/pxa/raumfeld.c @@ -41,7 +41,9 @@ static struct i2c_board_info max9486_hwmon_info = { };
#define MAX9485_MCLK_FREQ_112896 0x22 -#define MAX9485_MCLK_FREQ_122880 0x23 +#define MAX9485_MCLK_FREQ_122880 0x23 +#define MAX9485_MCLK_FREQ_225792 0x32 +#define MAX9485_MCLK_FREQ_245760 0x33
static void set_max9485_clk(char clk) { @@ -71,9 +73,17 @@ static int raumfeld_cs4270_startup(struct snd_pcm_substream *substream) struct snd_soc_pcm_runtime *rtd = substream->private_data; struct snd_soc_dai *codec_dai = rtd->dai->codec_dai;
- set_max9485_clk(MAX9485_MCLK_FREQ_112896); + /* set freq to 0 to enable all possible codec sample rates */ + return snd_soc_dai_set_sysclk(codec_dai, 0, 0, 0); +}
- return snd_soc_dai_set_sysclk(codec_dai, 0, 11289600, 0); +static void raumfeld_cs4270_shutdown(struct snd_pcm_substream *substream) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_dai *codec_dai = rtd->dai->codec_dai; + + /* set freq to 0 to enable all possible codec sample rates */ + snd_soc_dai_set_sysclk(codec_dai, 0, 0, 0); }
static int raumfeld_cs4270_hw_params(struct snd_pcm_substream *substream, @@ -86,20 +96,24 @@ static int raumfeld_cs4270_hw_params(struct snd_pcm_substream *substream, int ret = 0;
switch (params_rate(params)) { - case 8000: - case 16000: + case 44100: + set_max9485_clk(MAX9485_MCLK_FREQ_112896); + clk = 11289600; + break; case 48000: - case 96000: set_max9485_clk(MAX9485_MCLK_FREQ_122880); clk = 12288000; break; - case 11025: - case 22050: - case 44100: case 88200: - set_max9485_clk(MAX9485_MCLK_FREQ_112896); - clk = 11289600; + set_max9485_clk(MAX9485_MCLK_FREQ_225792); + clk = 22579200; break; + case 96000: + set_max9485_clk(MAX9485_MCLK_FREQ_245760); + clk = 24576000; + break; + default: + return -EINVAL; }
fmt = SND_SOC_DAIFMT_I2S | @@ -128,7 +142,7 @@ static int raumfeld_cs4270_hw_params(struct snd_pcm_substream *substream, if (ret < 0) return ret;
- ret = snd_soc_dai_set_sysclk(cpu_dai, PXA_SSP_CLK_EXT, 0, 1); + ret = snd_soc_dai_set_sysclk(cpu_dai, PXA_SSP_CLK_EXT, clk, 1); if (ret < 0) return ret;
@@ -137,6 +151,7 @@ static int raumfeld_cs4270_hw_params(struct snd_pcm_substream *substream,
static struct snd_soc_ops raumfeld_cs4270_ops = { .startup = raumfeld_cs4270_startup, + .shutdown = raumfeld_cs4270_shutdown, .hw_params = raumfeld_cs4270_hw_params, };
@@ -181,20 +196,24 @@ static int raumfeld_ak4104_hw_params(struct snd_pcm_substream *substream, int fmt, ret = 0, clk = 0;
switch (params_rate(params)) { - case 8000: - case 16000: + case 44100: + set_max9485_clk(MAX9485_MCLK_FREQ_112896); + clk = 11289600; + break; case 48000: - case 96000: set_max9485_clk(MAX9485_MCLK_FREQ_122880); clk = 12288000; break; - case 11025: - case 22050: - case 44100: case 88200: - set_max9485_clk(MAX9485_MCLK_FREQ_112896); - clk = 11289600; + set_max9485_clk(MAX9485_MCLK_FREQ_225792); + clk = 22579200; + break; + case 96000: + set_max9485_clk(MAX9485_MCLK_FREQ_245760); + clk = 24576000; break; + default: + return -EINVAL; }
fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF; @@ -217,7 +236,7 @@ static int raumfeld_ak4104_hw_params(struct snd_pcm_substream *substream, if (ret < 0) return ret;
- ret = snd_soc_dai_set_sysclk(cpu_dai, PXA_SSP_CLK_EXT, 0, 1); + ret = snd_soc_dai_set_sysclk(cpu_dai, PXA_SSP_CLK_EXT, clk, 1); if (ret < 0) return ret;
Daniel Mack wrote:
For setups with variable MCLKs, the current logic of limiting the available sampling rates at startup time is not sufficient. We need to be able to change the setting at a later point, and so the codec must offer all possible rates until the hw_params are given.
This patches allows that by passing 0 as 'freq' argument to cs4270_set_dai_sysclk().
Signed-off-by: Daniel Mack daniel@caiaq.de Cc: Mark Brown broonie@opensource.wolfsonmicro.com Cc: Timur Tabi timur@freescale.com
Acked-by: Timur Tabi timur@freescale.com
On Fri, Jan 15, 2010 at 11:08:56AM -0600, Timur Tabi wrote:
Daniel Mack wrote:
For setups with variable MCLKs, the current logic of limiting the available sampling rates at startup time is not sufficient. We need to be able to change the setting at a later point, and so the codec must offer all possible rates until the hw_params are given.
Signed-off-by: Daniel Mack daniel@caiaq.de
Acked-by: Timur Tabi timur@freescale.com
Applied both patches, thanks.
participants (3)
-
Daniel Mack
-
Mark Brown
-
Timur Tabi