diff options
Diffstat (limited to 'sound/soc/meson/aiu-encoder-i2s.c')
-rw-r--r-- | sound/soc/meson/aiu-encoder-i2s.c | 92 |
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, |