summaryrefslogtreecommitdiff
path: root/sound/soc/meson/aiu-encoder-i2s.c
diff options
context:
space:
mode:
authorMartin Blumenstingl <martin.blumenstingl@googlemail.com>2020-02-20 21:57:11 +0100
committerMark Brown <broonie@kernel.org>2020-02-21 13:30:47 +0000
commit3e25c44598aa44134207ad7b3c5ad6b586135777 (patch)
treef23b0311200163f96aa90fc8b338e90aa696e6fe /sound/soc/meson/aiu-encoder-i2s.c
parentedc761805302db6d63916694d0cdb7468864a47a (diff)
ASoC: meson: aiu: add support for the Meson8 and Meson8b SoC families
The AIU audio controller on the Meson8 and Meson8b SoC families is compatible with the one found in the later GXBB family. Add compatible strings for these two older SoC families so the driver can be loaded for them. Instead of using the I2S divider from the AIU_CLK_CTRL_MORE register we need to use the I2S divider from the AIU_CLK_CTRL register. This older register is less flexible because it only supports four divider settings (1, 2, 4, 8) compared to the AIU_CLK_CTRL_MORE register (which supports dividers in the range 0..64). Signed-off-by: Martin Blumenstingl <martin.blumenstingl@googlemail.com> Reviewed-by: Jerome Brunet <jbrunet@baylibre.com> Link: https://lore.kernel.org/r/20200220205711.77953-4-martin.blumenstingl@googlemail.com Signed-off-by: Mark Brown <broonie@kernel.org>
Diffstat (limited to 'sound/soc/meson/aiu-encoder-i2s.c')
-rw-r--r--sound/soc/meson/aiu-encoder-i2s.c92
1 files changed, 70 insertions, 22 deletions
diff --git a/sound/soc/meson/aiu-encoder-i2s.c b/sound/soc/meson/aiu-encoder-i2s.c
index 4900e38e7e49..cc73b5d5c2b7 100644
--- a/sound/soc/meson/aiu-encoder-i2s.c
+++ b/sound/soc/meson/aiu-encoder-i2s.c
@@ -111,34 +111,40 @@ static int aiu_encoder_i2s_setup_desc(struct snd_soc_component *component,
return 0;
}
-static int aiu_encoder_i2s_set_clocks(struct snd_soc_component *component,
- struct snd_pcm_hw_params *params)
+static int aiu_encoder_i2s_set_legacy_div(struct snd_soc_component *component,
+ struct snd_pcm_hw_params *params,
+ unsigned int bs)
{
- struct aiu *aiu = snd_soc_component_get_drvdata(component);
- unsigned int srate = params_rate(params);
- unsigned int fs, bs;
-
- /* Get the oversampling factor */
- fs = DIV_ROUND_CLOSEST(clk_get_rate(aiu->i2s.clks[MCLK].clk), srate);
+ switch (bs) {
+ case 1:
+ case 2:
+ case 4:
+ case 8:
+ /* These are the only valid legacy dividers */
+ break;
- if (fs % 64)
+ default:
+ dev_err(component->dev, "Unsupported i2s divider: %u\n", bs);
return -EINVAL;
+ };
- /* Send data MSB first */
- snd_soc_component_update_bits(component, AIU_I2S_DAC_CFG,
- AIU_I2S_DAC_CFG_MSB_FIRST,
- AIU_I2S_DAC_CFG_MSB_FIRST);
+ snd_soc_component_update_bits(component, AIU_CLK_CTRL,
+ AIU_CLK_CTRL_I2S_DIV,
+ FIELD_PREP(AIU_CLK_CTRL_I2S_DIV,
+ __ffs(bs)));
- /* Set bclk to lrlck ratio */
- snd_soc_component_update_bits(component, AIU_CODEC_DAC_LRCLK_CTRL,
- AIU_CODEC_DAC_LRCLK_CTRL_DIV,
- FIELD_PREP(AIU_CODEC_DAC_LRCLK_CTRL_DIV,
- 64 - 1));
+ snd_soc_component_update_bits(component, AIU_CLK_CTRL_MORE,
+ AIU_CLK_CTRL_MORE_I2S_DIV,
+ FIELD_PREP(AIU_CLK_CTRL_MORE_I2S_DIV,
+ 0));
- /* Use CLK_MORE for mclk to bclk divider */
- snd_soc_component_update_bits(component, AIU_CLK_CTRL,
- AIU_CLK_CTRL_I2S_DIV, 0);
+ return 0;
+}
+static int aiu_encoder_i2s_set_more_div(struct snd_soc_component *component,
+ struct snd_pcm_hw_params *params,
+ unsigned int bs)
+{
/*
* NOTE: this HW is odd.
* In most configuration, the i2s divider is 'mclk / blck'.
@@ -146,7 +152,6 @@ static int aiu_encoder_i2s_set_clocks(struct snd_soc_component *component,
* increased by 50% to get the correct output rate.
* No idea why !
*/
- bs = fs / 64;
if (params_width(params) == 16 && params_channels(params) == 8) {
if (bs % 2) {
dev_err(component->dev,
@@ -156,11 +161,54 @@ static int aiu_encoder_i2s_set_clocks(struct snd_soc_component *component,
bs += bs / 2;
}
+ /* Use CLK_MORE for mclk to bclk divider */
+ snd_soc_component_update_bits(component, AIU_CLK_CTRL,
+ AIU_CLK_CTRL_I2S_DIV,
+ FIELD_PREP(AIU_CLK_CTRL_I2S_DIV, 0));
+
snd_soc_component_update_bits(component, AIU_CLK_CTRL_MORE,
AIU_CLK_CTRL_MORE_I2S_DIV,
FIELD_PREP(AIU_CLK_CTRL_MORE_I2S_DIV,
bs - 1));
+ return 0;
+}
+
+static int aiu_encoder_i2s_set_clocks(struct snd_soc_component *component,
+ struct snd_pcm_hw_params *params)
+{
+ struct aiu *aiu = snd_soc_component_get_drvdata(component);
+ unsigned int srate = params_rate(params);
+ unsigned int fs, bs;
+ int ret;
+
+ /* Get the oversampling factor */
+ fs = DIV_ROUND_CLOSEST(clk_get_rate(aiu->i2s.clks[MCLK].clk), srate);
+
+ if (fs % 64)
+ return -EINVAL;
+
+ /* Send data MSB first */
+ snd_soc_component_update_bits(component, AIU_I2S_DAC_CFG,
+ AIU_I2S_DAC_CFG_MSB_FIRST,
+ AIU_I2S_DAC_CFG_MSB_FIRST);
+
+ /* Set bclk to lrlck ratio */
+ snd_soc_component_update_bits(component, AIU_CODEC_DAC_LRCLK_CTRL,
+ AIU_CODEC_DAC_LRCLK_CTRL_DIV,
+ FIELD_PREP(AIU_CODEC_DAC_LRCLK_CTRL_DIV,
+ 64 - 1));
+
+ bs = fs / 64;
+
+ if (aiu->platform->has_clk_ctrl_more_i2s_div)
+ ret = aiu_encoder_i2s_set_more_div(component, params, bs);
+ else
+ ret = aiu_encoder_i2s_set_legacy_div(component, params, bs);
+
+ if (ret)
+ return ret;
+
/* Make sure amclk is used for HDMI i2s as well */
snd_soc_component_update_bits(component, AIU_CLK_CTRL_MORE,
AIU_CLK_CTRL_MORE_HDMI_AMCLK,