Unverified Commit c1d3a921 authored by Maxime Ripard's avatar Maxime Ripard Committed by Mark Brown
Browse files

ASoC: sun4i-i2s: Fix the MCLK and BCLK dividers on newer SoCs



From: Marcus Cooper <codekipper@gmail.com>

The clock division dividers have changed between the older (A10/A31) and
newer (H3, A64, etc) SoCs.

While this was addressed through an offset on some SoCs, it was missing
some dividers as well, so the support wasn't perfect. Let's introduce a
pointer in the quirk structure for the divider calculation functions to use
so we can have the proper range now.

Signed-off-by: default avatarMarcus Cooper <codekipper@gmail.com>
[Maxime: Fix the commit log, use a field in the quirk structure]
Signed-off-by: default avatarMaxime Ripard <maxime.ripard@bootlin.com>
Link: https://lore.kernel.org/r/0e5b4abf06cd3202354315201c6af44caeb20236.1566242458.git-series.maxime.ripard@bootlin.com


Signed-off-by: default avatarMark Brown <broonie@kernel.org>
parent fb19739d
Loading
Loading
Loading
Loading
+49 −14
Original line number Diff line number Diff line
@@ -129,8 +129,6 @@ struct sun4i_i2s;
 * @has_fmt_set_lrck_period: SoC requires lrclk period to be set.
 * @reg_offset_txdata: offset of the tx fifo.
 * @sun4i_i2s_regmap: regmap config to use.
 * @mclk_offset: Value by which mclkdiv needs to be adjusted.
 * @bclk_offset: Value by which bclkdiv needs to be adjusted.
 * @field_clkdiv_mclk_en: regmap field to enable mclk output.
 * @field_fmt_wss: regmap field to set word select size.
 * @field_fmt_sr: regmap field to set sample resolution.
@@ -142,8 +140,6 @@ struct sun4i_i2s_quirks {
	bool				has_fmt_set_lrck_period;
	unsigned int			reg_offset_txdata;	/* TX FIFO */
	const struct regmap_config	*sun4i_i2s_regmap;
	unsigned int			mclk_offset;
	unsigned int			bclk_offset;

	/* Register fields for i2s */
	struct reg_field		field_clkdiv_mclk_en;
@@ -152,6 +148,11 @@ struct sun4i_i2s_quirks {
	struct reg_field		field_fmt_bclk;
	struct reg_field		field_fmt_lrclk;

	const struct sun4i_i2s_clk_div	*bclk_dividers;
	unsigned int			num_bclk_dividers;
	const struct sun4i_i2s_clk_div	*mclk_dividers;
	unsigned int			num_mclk_dividers;

	unsigned long (*get_bclk_parent_rate)(const struct sun4i_i2s *);
	s8	(*get_sr)(const struct sun4i_i2s *, int);
	s8	(*get_wss)(const struct sun4i_i2s *, int);
@@ -208,6 +209,24 @@ static const struct sun4i_i2s_clk_div sun4i_i2s_mclk_div[] = {
	/* TODO - extend divide ratio supported by newer SoCs */
};

static const struct sun4i_i2s_clk_div sun8i_i2s_clk_div[] = {
	{ .div = 1, .val = 1 },
	{ .div = 2, .val = 2 },
	{ .div = 4, .val = 3 },
	{ .div = 6, .val = 4 },
	{ .div = 8, .val = 5 },
	{ .div = 12, .val = 6 },
	{ .div = 16, .val = 7 },
	{ .div = 24, .val = 8 },
	{ .div = 32, .val = 9 },
	{ .div = 48, .val = 10 },
	{ .div = 64, .val = 11 },
	{ .div = 96, .val = 12 },
	{ .div = 128, .val = 13 },
	{ .div = 176, .val = 14 },
	{ .div = 192, .val = 15 },
};

static unsigned long sun4i_i2s_get_bclk_parent_rate(const struct sun4i_i2s *i2s)
{
	return i2s->mclk_freq;
@@ -223,11 +242,12 @@ static int sun4i_i2s_get_bclk_div(struct sun4i_i2s *i2s,
				  unsigned int sampling_rate,
				  unsigned int word_size)
{
	const struct sun4i_i2s_clk_div *dividers = i2s->variant->bclk_dividers;
	int div = parent_rate / sampling_rate / word_size / 2;
	int i;

	for (i = 0; i < ARRAY_SIZE(sun4i_i2s_bclk_div); i++) {
		const struct sun4i_i2s_clk_div *bdiv = &sun4i_i2s_bclk_div[i];
	for (i = 0; i < i2s->variant->num_bclk_dividers; i++) {
		const struct sun4i_i2s_clk_div *bdiv = &dividers[i];

		if (bdiv->div == div)
			return bdiv->val;
@@ -240,11 +260,12 @@ static int sun4i_i2s_get_mclk_div(struct sun4i_i2s *i2s,
				  unsigned long parent_rate,
				  unsigned long mclk_rate)
{
	const struct sun4i_i2s_clk_div *dividers = i2s->variant->mclk_dividers;
	int div = parent_rate / mclk_rate;
	int i;

	for (i = 0; i < ARRAY_SIZE(sun4i_i2s_mclk_div); i++) {
		const struct sun4i_i2s_clk_div *mdiv = &sun4i_i2s_mclk_div[i];
	for (i = 0; i < i2s->variant->num_mclk_dividers; i++) {
		const struct sun4i_i2s_clk_div *mdiv = &dividers[i];

		if (mdiv->div == div)
			return mdiv->val;
@@ -326,10 +347,6 @@ static int sun4i_i2s_set_clk_rate(struct snd_soc_dai *dai,
		return -EINVAL;
	}

	/* Adjust the clock division values if needed */
	bclk_div += i2s->variant->bclk_offset;
	mclk_div += i2s->variant->mclk_offset;

	regmap_write(i2s->regmap, SUN4I_I2S_CLK_DIV_REG,
		     SUN4I_I2S_CLK_DIV_BCLK(bclk_div) |
		     SUN4I_I2S_CLK_DIV_MCLK(mclk_div));
@@ -969,6 +986,10 @@ static const struct sun4i_i2s_quirks sun4i_a10_i2s_quirks = {
	.field_fmt_sr		= REG_FIELD(SUN4I_I2S_FMT0_REG, 4, 5),
	.field_fmt_bclk		= REG_FIELD(SUN4I_I2S_FMT0_REG, 6, 6),
	.field_fmt_lrclk	= REG_FIELD(SUN4I_I2S_FMT0_REG, 7, 7),
	.bclk_dividers		= sun4i_i2s_bclk_div,
	.num_bclk_dividers	= ARRAY_SIZE(sun4i_i2s_bclk_div),
	.mclk_dividers		= sun4i_i2s_mclk_div,
	.num_mclk_dividers	= ARRAY_SIZE(sun4i_i2s_mclk_div),
	.get_bclk_parent_rate	= sun4i_i2s_get_bclk_parent_rate,
	.get_sr			= sun4i_i2s_get_sr,
	.get_wss		= sun4i_i2s_get_wss,
@@ -985,6 +1006,10 @@ static const struct sun4i_i2s_quirks sun6i_a31_i2s_quirks = {
	.field_fmt_sr		= REG_FIELD(SUN4I_I2S_FMT0_REG, 4, 5),
	.field_fmt_bclk		= REG_FIELD(SUN4I_I2S_FMT0_REG, 6, 6),
	.field_fmt_lrclk	= REG_FIELD(SUN4I_I2S_FMT0_REG, 7, 7),
	.bclk_dividers		= sun4i_i2s_bclk_div,
	.num_bclk_dividers	= ARRAY_SIZE(sun4i_i2s_bclk_div),
	.mclk_dividers		= sun4i_i2s_mclk_div,
	.num_mclk_dividers	= ARRAY_SIZE(sun4i_i2s_mclk_div),
	.get_bclk_parent_rate	= sun4i_i2s_get_bclk_parent_rate,
	.get_sr			= sun4i_i2s_get_sr,
	.get_wss		= sun4i_i2s_get_wss,
@@ -1001,6 +1026,10 @@ static const struct sun4i_i2s_quirks sun8i_a83t_i2s_quirks = {
	.field_fmt_sr		= REG_FIELD(SUN4I_I2S_FMT0_REG, 4, 5),
	.field_fmt_bclk		= REG_FIELD(SUN4I_I2S_FMT0_REG, 6, 6),
	.field_fmt_lrclk	= REG_FIELD(SUN4I_I2S_FMT0_REG, 7, 7),
	.bclk_dividers		= sun8i_i2s_clk_div,
	.num_bclk_dividers	= ARRAY_SIZE(sun8i_i2s_clk_div),
	.mclk_dividers		= sun8i_i2s_clk_div,
	.num_mclk_dividers	= ARRAY_SIZE(sun8i_i2s_clk_div),
	.get_bclk_parent_rate	= sun8i_i2s_get_bclk_parent_rate,
	.get_sr			= sun8i_i2s_get_sr_wss,
	.get_wss		= sun8i_i2s_get_sr_wss,
@@ -1012,14 +1041,16 @@ static const struct sun4i_i2s_quirks sun8i_h3_i2s_quirks = {
	.has_reset		= true,
	.reg_offset_txdata	= SUN8I_I2S_FIFO_TX_REG,
	.sun4i_i2s_regmap	= &sun8i_i2s_regmap_config,
	.mclk_offset		= 1,
	.bclk_offset		= 2,
	.has_fmt_set_lrck_period = true,
	.field_clkdiv_mclk_en	= REG_FIELD(SUN4I_I2S_CLK_DIV_REG, 8, 8),
	.field_fmt_wss		= REG_FIELD(SUN4I_I2S_FMT0_REG, 0, 2),
	.field_fmt_sr		= REG_FIELD(SUN4I_I2S_FMT0_REG, 4, 6),
	.field_fmt_bclk		= REG_FIELD(SUN4I_I2S_FMT0_REG, 7, 7),
	.field_fmt_lrclk	= REG_FIELD(SUN4I_I2S_FMT0_REG, 19, 19),
	.bclk_dividers		= sun8i_i2s_clk_div,
	.num_bclk_dividers	= ARRAY_SIZE(sun8i_i2s_clk_div),
	.mclk_dividers		= sun8i_i2s_clk_div,
	.num_mclk_dividers	= ARRAY_SIZE(sun8i_i2s_clk_div),
	.get_bclk_parent_rate	= sun8i_i2s_get_bclk_parent_rate,
	.get_sr			= sun8i_i2s_get_sr_wss,
	.get_wss		= sun8i_i2s_get_sr_wss,
@@ -1036,6 +1067,10 @@ static const struct sun4i_i2s_quirks sun50i_a64_codec_i2s_quirks = {
	.field_fmt_sr		= REG_FIELD(SUN4I_I2S_FMT0_REG, 4, 5),
	.field_fmt_bclk		= REG_FIELD(SUN4I_I2S_FMT0_REG, 6, 6),
	.field_fmt_lrclk	= REG_FIELD(SUN4I_I2S_FMT0_REG, 7, 7),
	.bclk_dividers		= sun4i_i2s_bclk_div,
	.num_bclk_dividers	= ARRAY_SIZE(sun4i_i2s_bclk_div),
	.mclk_dividers		= sun4i_i2s_mclk_div,
	.num_mclk_dividers	= ARRAY_SIZE(sun4i_i2s_mclk_div),
	.get_bclk_parent_rate	= sun4i_i2s_get_bclk_parent_rate,
	.get_sr			= sun4i_i2s_get_sr,
	.get_wss		= sun4i_i2s_get_wss,