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>
This commit is contained in:
Martin Blumenstingl 2020-02-20 21:57:11 +01:00 committed by Mark Brown
parent edc7618053
commit 3e25c44598
No known key found for this signature in database
GPG Key ID: 24D68B725D5487D0
4 changed files with 82 additions and 24 deletions

View File

@ -10,7 +10,7 @@ config SND_MESON_AIU
imply SND_SOC_HDMI_CODEC if DRM_MESON_DW_HDMI
help
Select Y or M to add support for the Audio output subsystem found
in the Amlogic GX SoC family
in the Amlogic Meson8, Meson8b and GX SoC families
config SND_MESON_AXG_FIFO
tristate

View File

@ -111,12 +111,76 @@ static int aiu_encoder_i2s_setup_desc(struct snd_soc_component *component,
return 0;
}
static int aiu_encoder_i2s_set_legacy_div(struct snd_soc_component *component,
struct snd_pcm_hw_params *params,
unsigned int bs)
{
switch (bs) {
case 1:
case 2:
case 4:
case 8:
/* These are the only valid legacy dividers */
break;
default:
dev_err(component->dev, "Unsupported i2s divider: %u\n", bs);
return -EINVAL;
};
snd_soc_component_update_bits(component, AIU_CLK_CTRL,
AIU_CLK_CTRL_I2S_DIV,
FIELD_PREP(AIU_CLK_CTRL_I2S_DIV,
__ffs(bs)));
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));
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'.
* However, in 16 bits - 8ch mode, this factor needs to be
* increased by 50% to get the correct output rate.
* No idea why !
*/
if (params_width(params) == 16 && params_channels(params) == 8) {
if (bs % 2) {
dev_err(component->dev,
"Cannot increase i2s divider by 50%%\n");
return -EINVAL;
}
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);
@ -135,31 +199,15 @@ static int aiu_encoder_i2s_set_clocks(struct snd_soc_component *component,
FIELD_PREP(AIU_CODEC_DAC_LRCLK_CTRL_DIV,
64 - 1));
/* Use CLK_MORE for mclk to bclk divider */
snd_soc_component_update_bits(component, AIU_CLK_CTRL,
AIU_CLK_CTRL_I2S_DIV, 0);
/*
* NOTE: this HW is odd.
* In most configuration, the i2s divider is 'mclk / blck'.
* However, in 16 bits - 8ch mode, this factor needs to be
* 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,
"Cannot increase i2s divider by 50%%\n");
return -EINVAL;
}
bs += bs / 2;
}
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));
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,

View File

@ -351,15 +351,24 @@ static int aiu_remove(struct platform_device *pdev)
static const struct aiu_platform_data aiu_gxbb_pdata = {
.has_acodec = false,
.has_clk_ctrl_more_i2s_div = true,
};
static const struct aiu_platform_data aiu_gxl_pdata = {
.has_acodec = true,
.has_clk_ctrl_more_i2s_div = true,
};
static const struct aiu_platform_data aiu_meson8_pdata = {
.has_acodec = false,
.has_clk_ctrl_more_i2s_div = false,
};
static const struct of_device_id aiu_of_match[] = {
{ .compatible = "amlogic,aiu-gxbb", .data = &aiu_gxbb_pdata },
{ .compatible = "amlogic,aiu-gxl", .data = &aiu_gxl_pdata },
{ .compatible = "amlogic,aiu-meson8", .data = &aiu_meson8_pdata },
{ .compatible = "amlogic,aiu-meson8b", .data = &aiu_meson8_pdata },
{}
};
MODULE_DEVICE_TABLE(of, aiu_of_match);

View File

@ -29,6 +29,7 @@ struct aiu_interface {
struct aiu_platform_data {
bool has_acodec;
bool has_clk_ctrl_more_i2s_div;
};
struct aiu {