Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

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

ASoC: sun4i-i2s: Move the format configuration to a callback



The two main generations of our I2S controller require a slightly different
format configuration, mostly because of a quite different register layout
and some additional registers being needed on the newer generation.

This used to be controlled through a bunch of booleans, however this proved
to be quite impractical, especially since a bunch of SoCs forgot to set
those parameters and therefore were broken from that point of view.

Fixes: 21faaea1 ("ASoC: sun4i-i2s: Add support for A83T")
Signed-off-by: default avatarMaxime Ripard <maxime.ripard@bootlin.com>
Link: https://lore.kernel.org/r/dc818644c3e40734e7a97247c994b1fca1c3c047.1566242458.git-series.maxime.ripard@bootlin.com


Signed-off-by: default avatarMark Brown <broonie@kernel.org>
parent d70be625
Loading
Loading
Loading
Loading
+106 −93
Original line number Diff line number Diff line
@@ -93,6 +93,11 @@
#define SUN8I_I2S_CTRL_BCLK_OUT			BIT(18)
#define SUN8I_I2S_CTRL_LRCK_OUT			BIT(17)

#define SUN8I_I2S_CTRL_MODE_MASK		GENMASK(5, 4)
#define SUN8I_I2S_CTRL_MODE_RIGHT		(2 << 4)
#define SUN8I_I2S_CTRL_MODE_LEFT		(1 << 4)
#define SUN8I_I2S_CTRL_MODE_PCM			(0 << 4)

#define SUN8I_I2S_FMT0_LRCK_PERIOD_MASK		GENMASK(17, 8)
#define SUN8I_I2S_FMT0_LRCK_PERIOD(period)	((period - 1) << 8)

@@ -121,9 +126,7 @@ struct sun4i_i2s;
 * struct sun4i_i2s_quirks - Differences between SoC variants.
 *
 * @has_reset: SoC needs reset deasserted.
 * @has_slave_select_bit: SoC has a bit to enable slave mode.
 * @has_fmt_set_lrck_period: SoC requires lrclk period to be set.
 * @has_chsel_offset: SoC uses offset for selecting dai operational mode.
 * @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.
@@ -133,13 +136,10 @@ struct sun4i_i2s;
 * @field_fmt_sr: regmap field to set sample resolution.
 * @field_fmt_bclk: regmap field to set clk polarity.
 * @field_fmt_lrclk: regmap field to set frame polarity.
 * @field_fmt_mode: regmap field to set the operational mode.
 */
struct sun4i_i2s_quirks {
	bool				has_reset;
	bool				has_slave_select_bit;
	bool				has_fmt_set_lrck_period;
	bool				has_chsel_offset;
	unsigned int			reg_offset_txdata;	/* TX FIFO */
	const struct regmap_config	*sun4i_i2s_regmap;
	unsigned int			mclk_offset;
@@ -151,12 +151,12 @@ struct sun4i_i2s_quirks {
	struct reg_field		field_fmt_sr;
	struct reg_field		field_fmt_bclk;
	struct reg_field		field_fmt_lrclk;
	struct reg_field		field_fmt_mode;

	s8	(*get_sr)(const struct sun4i_i2s *, int);
	s8	(*get_wss)(const struct sun4i_i2s *, int);
	int	(*set_chan_cfg)(const struct sun4i_i2s *,
				const struct snd_pcm_hw_params *);
	int	(*set_fmt)(const struct sun4i_i2s *, unsigned int);
};

struct sun4i_i2s {
@@ -176,7 +176,6 @@ struct sun4i_i2s {
	struct regmap_field	*field_fmt_sr;
	struct regmap_field	*field_fmt_bclk;
	struct regmap_field	*field_fmt_lrclk;
	struct regmap_field	*field_fmt_mode;

	const struct sun4i_i2s_quirks	*variant;
};
@@ -465,52 +464,117 @@ static int sun4i_i2s_hw_params(struct snd_pcm_substream *substream,
				      params_width(params));
}

static int sun4i_i2s_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
static int sun4i_i2s_set_soc_fmt(const struct sun4i_i2s *i2s,
				 unsigned int fmt)
{
	struct sun4i_i2s *i2s = snd_soc_dai_get_drvdata(dai);
	u32 val;
	u32 offset = 0;
	u32 bclk_polarity = SUN4I_I2S_FMT0_POLARITY_NORMAL;
	u32 lrclk_polarity = SUN4I_I2S_FMT0_POLARITY_NORMAL;

	/* DAI Mode */
	switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
	case SND_SOC_DAIFMT_I2S:
		val = SUN4I_I2S_FMT0_FMT_I2S;
		offset = 1;
		break;

	case SND_SOC_DAIFMT_LEFT_J:
		val = SUN4I_I2S_FMT0_FMT_LEFT_J;
		break;

	case SND_SOC_DAIFMT_RIGHT_J:
		val = SUN4I_I2S_FMT0_FMT_RIGHT_J;
		break;

	default:
		dev_err(dai->dev, "Unsupported format: %d\n",
			fmt & SND_SOC_DAIFMT_FORMAT_MASK);
		return -EINVAL;
	}

	if (i2s->variant->has_chsel_offset) {
		/*
		 * offset being set indicates that we're connected to an i2s
		 * device, however offset is only used on the sun8i block and
		 * i2s shares the same setting with the LJ format. Increment
		 * val so that the bit to value to write is correct.
		 */
		if (offset > 0)
			val++;
		/* blck offset determines whether i2s or LJ */
	regmap_update_bits(i2s->regmap, SUN4I_I2S_FMT0_REG,
			   SUN4I_I2S_FMT0_FMT_MASK, val);

	/* DAI clock master masks */
	switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
	case SND_SOC_DAIFMT_CBS_CFS:
		/* BCLK and LRCLK master */
		val = SUN4I_I2S_CTRL_MODE_MASTER;
		break;

	case SND_SOC_DAIFMT_CBM_CFM:
		/* BCLK and LRCLK slave */
		val = SUN4I_I2S_CTRL_MODE_SLAVE;
		break;

	default:
		return -EINVAL;
	}
	regmap_update_bits(i2s->regmap, SUN4I_I2S_CTRL_REG,
			   SUN4I_I2S_CTRL_MODE_MASK, val);
	return 0;
}

static int sun8i_i2s_set_soc_fmt(const struct sun4i_i2s *i2s,
				 unsigned int fmt)
{
	u32 mode, val;
	u8 offset;

	/* DAI Mode */
	switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
	case SND_SOC_DAIFMT_I2S:
		mode = SUN8I_I2S_CTRL_MODE_LEFT;
		offset = 1;
		break;

	case SND_SOC_DAIFMT_LEFT_J:
		mode = SUN8I_I2S_CTRL_MODE_LEFT;
		offset = 0;
		break;

	case SND_SOC_DAIFMT_RIGHT_J:
		mode = SUN8I_I2S_CTRL_MODE_RIGHT;
		offset = 0;
		break;

	default:
		return -EINVAL;
	}

	regmap_update_bits(i2s->regmap, SUN4I_I2S_CTRL_REG,
			   SUN8I_I2S_CTRL_MODE_MASK, mode);
	regmap_update_bits(i2s->regmap, SUN8I_I2S_TX_CHAN_SEL_REG,
			   SUN8I_I2S_TX_CHAN_OFFSET_MASK,
			   SUN8I_I2S_TX_CHAN_OFFSET(offset));

	regmap_update_bits(i2s->regmap, SUN8I_I2S_RX_CHAN_SEL_REG,
			   SUN8I_I2S_TX_CHAN_OFFSET_MASK,
			   SUN8I_I2S_TX_CHAN_OFFSET(offset));

	/* DAI clock master masks */
	switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
	case SND_SOC_DAIFMT_CBS_CFS:
		/* BCLK and LRCLK master */
		val = SUN8I_I2S_CTRL_BCLK_OUT |	SUN8I_I2S_CTRL_LRCK_OUT;
		break;

	case SND_SOC_DAIFMT_CBM_CFM:
		/* BCLK and LRCLK slave */
		val = 0;
		break;

	default:
		return -EINVAL;
	}

	regmap_update_bits(i2s->regmap, SUN4I_I2S_CTRL_REG,
			   SUN8I_I2S_CTRL_BCLK_OUT | SUN8I_I2S_CTRL_LRCK_OUT,
			   val);

	return 0;
}

	regmap_field_write(i2s->field_fmt_mode, val);
static int sun4i_i2s_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
{
	struct sun4i_i2s *i2s = snd_soc_dai_get_drvdata(dai);
	u32 bclk_polarity = SUN4I_I2S_FMT0_POLARITY_NORMAL;
	u32 lrclk_polarity = SUN4I_I2S_FMT0_POLARITY_NORMAL;
	int ret;

	/* DAI clock polarity */
	switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
@@ -538,50 +602,10 @@ static int sun4i_i2s_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
	regmap_field_write(i2s->field_fmt_bclk, bclk_polarity);
	regmap_field_write(i2s->field_fmt_lrclk, lrclk_polarity);

	if (i2s->variant->has_slave_select_bit) {
		/* DAI clock master masks */
		switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
		case SND_SOC_DAIFMT_CBS_CFS:
			/* BCLK and LRCLK master */
			val = SUN4I_I2S_CTRL_MODE_MASTER;
			break;
		case SND_SOC_DAIFMT_CBM_CFM:
			/* BCLK and LRCLK slave */
			val = SUN4I_I2S_CTRL_MODE_SLAVE;
			break;
		default:
			dev_err(dai->dev, "Unsupported slave setting: %d\n",
				fmt & SND_SOC_DAIFMT_MASTER_MASK);
			return -EINVAL;
		}
		regmap_update_bits(i2s->regmap, SUN4I_I2S_CTRL_REG,
				   SUN4I_I2S_CTRL_MODE_MASK,
				   val);
	} else {
		/*
		 * The newer i2s block does not have a slave select bit,
		 * instead the clk pins are configured as inputs.
		 */
		/* DAI clock master masks */
		switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
		case SND_SOC_DAIFMT_CBS_CFS:
			/* BCLK and LRCLK master */
			val = SUN8I_I2S_CTRL_BCLK_OUT |
				SUN8I_I2S_CTRL_LRCK_OUT;
			break;
		case SND_SOC_DAIFMT_CBM_CFM:
			/* BCLK and LRCLK slave */
			val = 0;
			break;
		default:
			dev_err(dai->dev, "Unsupported slave setting: %d\n",
				fmt & SND_SOC_DAIFMT_MASTER_MASK);
			return -EINVAL;
		}
		regmap_update_bits(i2s->regmap, SUN4I_I2S_CTRL_REG,
				   SUN8I_I2S_CTRL_BCLK_OUT |
				   SUN8I_I2S_CTRL_LRCK_OUT,
				   val);
	ret = i2s->variant->set_fmt(i2s, fmt);
	if (ret) {
		dev_err(dai->dev, "Unsupported format configuration\n");
		return ret;
	}

	/* Set significant bits in our FIFOs */
@@ -933,11 +957,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),
	.has_slave_select_bit	= true,
	.field_fmt_mode		= REG_FIELD(SUN4I_I2S_FMT0_REG, 0, 1),
	.get_sr			= sun4i_i2s_get_sr,
	.get_wss		= sun4i_i2s_get_wss,
	.set_chan_cfg		= sun4i_i2s_set_chan_cfg,
	.set_fmt		= sun4i_i2s_set_soc_fmt,
};

static const struct sun4i_i2s_quirks sun6i_a31_i2s_quirks = {
@@ -949,11 +972,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),
	.has_slave_select_bit	= true,
	.field_fmt_mode		= REG_FIELD(SUN4I_I2S_FMT0_REG, 0, 1),
	.get_sr			= sun4i_i2s_get_sr,
	.get_wss		= sun4i_i2s_get_wss,
	.set_chan_cfg		= sun4i_i2s_set_chan_cfg,
	.set_fmt		= sun4i_i2s_set_soc_fmt,
};

static const struct sun4i_i2s_quirks sun8i_a83t_i2s_quirks = {
@@ -965,11 +987,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),
	.has_slave_select_bit	= true,
	.field_fmt_mode		= REG_FIELD(SUN4I_I2S_FMT0_REG, 0, 1),
	.get_sr			= sun8i_i2s_get_sr_wss,
	.get_wss		= sun8i_i2s_get_sr_wss,
	.set_chan_cfg		= sun8i_i2s_set_chan_cfg,
	.set_fmt		= sun8i_i2s_set_soc_fmt,
};

static const struct sun4i_i2s_quirks sun8i_h3_i2s_quirks = {
@@ -979,32 +1000,30 @@ static const struct sun4i_i2s_quirks sun8i_h3_i2s_quirks = {
	.mclk_offset		= 1,
	.bclk_offset		= 2,
	.has_fmt_set_lrck_period = true,
	.has_chsel_offset	= 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),
	.field_fmt_mode		= REG_FIELD(SUN4I_I2S_CTRL_REG, 4, 5),
	.get_sr			= sun8i_i2s_get_sr_wss,
	.get_wss		= sun8i_i2s_get_sr_wss,
	.set_chan_cfg		= sun8i_i2s_set_chan_cfg,
	.set_fmt		= sun8i_i2s_set_soc_fmt,
};

static const struct sun4i_i2s_quirks sun50i_a64_codec_i2s_quirks = {
	.has_reset		= true,
	.reg_offset_txdata	= SUN8I_I2S_FIFO_TX_REG,
	.sun4i_i2s_regmap	= &sun4i_i2s_regmap_config,
	.has_slave_select_bit	= true,
	.field_clkdiv_mclk_en	= REG_FIELD(SUN4I_I2S_CLK_DIV_REG, 7, 7),
	.field_fmt_wss		= REG_FIELD(SUN4I_I2S_FMT0_REG, 2, 3),
	.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),
	.field_fmt_mode		= REG_FIELD(SUN4I_I2S_FMT0_REG, 0, 1),
	.get_sr			= sun4i_i2s_get_sr,
	.get_wss		= sun4i_i2s_get_wss,
	.set_chan_cfg		= sun4i_i2s_set_chan_cfg,
	.set_fmt		= sun4i_i2s_set_soc_fmt,
};

static int sun4i_i2s_init_regmap_fields(struct device *dev,
@@ -1040,12 +1059,6 @@ static int sun4i_i2s_init_regmap_fields(struct device *dev,
	if (IS_ERR(i2s->field_fmt_lrclk))
		return PTR_ERR(i2s->field_fmt_lrclk);

	i2s->field_fmt_mode =
			devm_regmap_field_alloc(dev, i2s->regmap,
						i2s->variant->field_fmt_mode);
	if (IS_ERR(i2s->field_fmt_mode))
		return PTR_ERR(i2s->field_fmt_mode);

	return 0;
}