summaryrefslogtreecommitdiffstats
path: root/sound/soc/sunxi
diff options
context:
space:
mode:
authorMaxime Ripard <maxime.ripard@bootlin.com>2019-08-19 21:25:11 +0200
committerMark Brown <broonie@kernel.org>2019-08-20 18:30:00 +0100
commitd70be625f25af7a2bc91b7d17d205f6071f08f2f (patch)
tree9434180c9d68075a60375b1ff80f744b28da49d0 /sound/soc/sunxi
parentdd28d54c248fa37794dfa3d23a24ff99a1d3b6ac (diff)
downloadlinux-d70be625f25af7a2bc91b7d17d205f6071f08f2f.tar.gz
linux-d70be625f25af7a2bc91b7d17d205f6071f08f2f.tar.bz2
linux-d70be625f25af7a2bc91b7d17d205f6071f08f2f.zip
ASoC: sun4i-i2s: Move the channel configuration to a callback
The two main generations of our I2S controller require a slightly different channel 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: 21faaea1343f ("ASoC: sun4i-i2s: Add support for A83T") Signed-off-by: Maxime Ripard <maxime.ripard@bootlin.com> Link: https://lore.kernel.org/r/6414463de69584e8227fa495b13aa5f4798e1f0e.1566242458.git-series.maxime.ripard@bootlin.com Signed-off-by: Mark Brown <broonie@kernel.org>
Diffstat (limited to 'sound/soc/sunxi')
-rw-r--r--sound/soc/sunxi/sun4i-i2s.c156
1 files changed, 69 insertions, 87 deletions
diff --git a/sound/soc/sunxi/sun4i-i2s.c b/sound/soc/sunxi/sun4i-i2s.c
index 2c909c6cafa9..42e45c9a947a 100644
--- a/sound/soc/sunxi/sun4i-i2s.c
+++ b/sound/soc/sunxi/sun4i-i2s.c
@@ -80,6 +80,7 @@
#define SUN4I_I2S_TX_CNT_REG 0x2c
#define SUN4I_I2S_TX_CHAN_SEL_REG 0x30
+#define SUN4I_I2S_CHAN_SEL_MASK GENMASK(2, 0)
#define SUN4I_I2S_CHAN_SEL(num_chan) (((num_chan) - 1) << 0)
#define SUN4I_I2S_TX_CHAN_MAP_REG 0x34
@@ -122,8 +123,6 @@ struct sun4i_i2s;
* @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_chcfg: tx and rx slot number need to be set.
- * @has_chsel_tx_chen: SoC requires that the tx channels are enabled.
* @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.
@@ -135,17 +134,11 @@ struct sun4i_i2s;
* @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.
- * @field_txchanmap: location of the tx channel mapping register.
- * @field_rxchanmap: location of the rx channel mapping register.
- * @field_txchansel: location of the tx channel select bit fields.
- * @field_rxchansel: location of the rx channel select bit fields.
*/
struct sun4i_i2s_quirks {
bool has_reset;
bool has_slave_select_bit;
bool has_fmt_set_lrck_period;
- bool has_chcfg;
- bool has_chsel_tx_chen;
bool has_chsel_offset;
unsigned int reg_offset_txdata; /* TX FIFO */
const struct regmap_config *sun4i_i2s_regmap;
@@ -159,13 +152,11 @@ struct sun4i_i2s_quirks {
struct reg_field field_fmt_bclk;
struct reg_field field_fmt_lrclk;
struct reg_field field_fmt_mode;
- struct reg_field field_txchanmap;
- struct reg_field field_rxchanmap;
- struct reg_field field_txchansel;
- struct reg_field field_rxchansel;
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 *);
};
struct sun4i_i2s {
@@ -186,10 +177,6 @@ struct sun4i_i2s {
struct regmap_field *field_fmt_bclk;
struct regmap_field *field_fmt_lrclk;
struct regmap_field *field_fmt_mode;
- struct regmap_field *field_txchanmap;
- struct regmap_field *field_rxchanmap;
- struct regmap_field *field_txchansel;
- struct regmap_field *field_rxchansel;
const struct sun4i_i2s_quirks *variant;
};
@@ -380,44 +367,77 @@ static s8 sun8i_i2s_get_sr_wss(const struct sun4i_i2s *i2s, int width)
return (width - 8) / 4 + 1;
}
-static int sun4i_i2s_hw_params(struct snd_pcm_substream *substream,
- struct snd_pcm_hw_params *params,
- struct snd_soc_dai *dai)
+static int sun4i_i2s_set_chan_cfg(const struct sun4i_i2s *i2s,
+ const struct snd_pcm_hw_params *params)
{
- struct sun4i_i2s *i2s = snd_soc_dai_get_drvdata(dai);
- int sr, wss, channels;
- u32 width;
+ unsigned int channels = params_channels(params);
- channels = params_channels(params);
- if (channels != 2) {
- dev_err(dai->dev, "Unsupported number of channels: %d\n",
- channels);
+ if (channels != 2)
return -EINVAL;
- }
- if (i2s->variant->has_chcfg) {
- regmap_update_bits(i2s->regmap, SUN8I_I2S_CHAN_CFG_REG,
- SUN8I_I2S_CHAN_CFG_TX_SLOT_NUM_MASK,
- SUN8I_I2S_CHAN_CFG_TX_SLOT_NUM(channels));
- regmap_update_bits(i2s->regmap, SUN8I_I2S_CHAN_CFG_REG,
- SUN8I_I2S_CHAN_CFG_RX_SLOT_NUM_MASK,
- SUN8I_I2S_CHAN_CFG_RX_SLOT_NUM(channels));
- }
+ /* Map the channels for playback and capture */
+ regmap_write(i2s->regmap, SUN4I_I2S_TX_CHAN_MAP_REG, 0x76543210);
+ regmap_write(i2s->regmap, SUN4I_I2S_RX_CHAN_MAP_REG, 0x00003210);
+
+ /* Configure the channels */
+ regmap_update_bits(i2s->regmap, SUN4I_I2S_TX_CHAN_SEL_REG,
+ SUN4I_I2S_CHAN_SEL_MASK,
+ SUN4I_I2S_CHAN_SEL(channels));
+ regmap_update_bits(i2s->regmap, SUN4I_I2S_RX_CHAN_SEL_REG,
+ SUN4I_I2S_CHAN_SEL_MASK,
+ SUN4I_I2S_CHAN_SEL(channels));
+
+ return 0;
+}
+
+static int sun8i_i2s_set_chan_cfg(const struct sun4i_i2s *i2s,
+ const struct snd_pcm_hw_params *params)
+{
+ unsigned int channels = params_channels(params);
+
+ if (channels != 2)
+ return -EINVAL;
/* Map the channels for playback and capture */
- regmap_field_write(i2s->field_txchanmap, 0x76543210);
- regmap_field_write(i2s->field_rxchanmap, 0x00003210);
+ regmap_write(i2s->regmap, SUN8I_I2S_TX_CHAN_MAP_REG, 0x76543210);
+ regmap_write(i2s->regmap, SUN8I_I2S_RX_CHAN_MAP_REG, 0x76543210);
/* Configure the channels */
- regmap_field_write(i2s->field_txchansel,
+ regmap_update_bits(i2s->regmap, SUN8I_I2S_TX_CHAN_SEL_REG,
+ SUN4I_I2S_CHAN_SEL_MASK,
SUN4I_I2S_CHAN_SEL(channels));
- regmap_field_write(i2s->field_rxchansel,
+
+ regmap_update_bits(i2s->regmap, SUN8I_I2S_RX_CHAN_SEL_REG,
+ SUN4I_I2S_CHAN_SEL_MASK,
SUN4I_I2S_CHAN_SEL(channels));
- if (i2s->variant->has_chsel_tx_chen)
- regmap_update_bits(i2s->regmap, SUN8I_I2S_TX_CHAN_SEL_REG,
- SUN8I_I2S_TX_CHAN_EN_MASK,
- SUN8I_I2S_TX_CHAN_EN(channels));
+ regmap_update_bits(i2s->regmap, SUN8I_I2S_CHAN_CFG_REG,
+ SUN8I_I2S_CHAN_CFG_TX_SLOT_NUM_MASK,
+ SUN8I_I2S_CHAN_CFG_TX_SLOT_NUM(channels));
+ regmap_update_bits(i2s->regmap, SUN8I_I2S_CHAN_CFG_REG,
+ SUN8I_I2S_CHAN_CFG_RX_SLOT_NUM_MASK,
+ SUN8I_I2S_CHAN_CFG_RX_SLOT_NUM(channels));
+
+ regmap_update_bits(i2s->regmap, SUN8I_I2S_TX_CHAN_SEL_REG,
+ SUN8I_I2S_TX_CHAN_EN_MASK,
+ SUN8I_I2S_TX_CHAN_EN(channels));
+
+ return 0;
+}
+
+static int sun4i_i2s_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct sun4i_i2s *i2s = snd_soc_dai_get_drvdata(dai);
+ int ret, sr, wss;
+ u32 width;
+
+ ret = i2s->variant->set_chan_cfg(i2s, params);
+ if (ret < 0) {
+ dev_err(dai->dev, "Invalid channel configuration\n");
+ return ret;
+ }
switch (params_physical_width(params)) {
case 16:
@@ -915,12 +935,9 @@ static const struct sun4i_i2s_quirks sun4i_a10_i2s_quirks = {
.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),
- .field_txchanmap = REG_FIELD(SUN4I_I2S_TX_CHAN_MAP_REG, 0, 31),
- .field_rxchanmap = REG_FIELD(SUN4I_I2S_RX_CHAN_MAP_REG, 0, 31),
- .field_txchansel = REG_FIELD(SUN4I_I2S_TX_CHAN_SEL_REG, 0, 2),
- .field_rxchansel = REG_FIELD(SUN4I_I2S_RX_CHAN_SEL_REG, 0, 2),
.get_sr = sun4i_i2s_get_sr,
.get_wss = sun4i_i2s_get_wss,
+ .set_chan_cfg = sun4i_i2s_set_chan_cfg,
};
static const struct sun4i_i2s_quirks sun6i_a31_i2s_quirks = {
@@ -934,12 +951,9 @@ static const struct sun4i_i2s_quirks sun6i_a31_i2s_quirks = {
.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),
- .field_txchanmap = REG_FIELD(SUN4I_I2S_TX_CHAN_MAP_REG, 0, 31),
- .field_rxchanmap = REG_FIELD(SUN4I_I2S_RX_CHAN_MAP_REG, 0, 31),
- .field_txchansel = REG_FIELD(SUN4I_I2S_TX_CHAN_SEL_REG, 0, 2),
- .field_rxchansel = REG_FIELD(SUN4I_I2S_RX_CHAN_SEL_REG, 0, 2),
.get_sr = sun4i_i2s_get_sr,
.get_wss = sun4i_i2s_get_wss,
+ .set_chan_cfg = sun4i_i2s_set_chan_cfg,
};
static const struct sun4i_i2s_quirks sun8i_a83t_i2s_quirks = {
@@ -953,12 +967,9 @@ static const struct sun4i_i2s_quirks sun8i_a83t_i2s_quirks = {
.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),
- .field_txchanmap = REG_FIELD(SUN4I_I2S_TX_CHAN_MAP_REG, 0, 31),
- .field_rxchanmap = REG_FIELD(SUN4I_I2S_RX_CHAN_MAP_REG, 0, 31),
- .field_txchansel = REG_FIELD(SUN4I_I2S_TX_CHAN_SEL_REG, 0, 2),
- .field_rxchansel = REG_FIELD(SUN4I_I2S_RX_CHAN_SEL_REG, 0, 2),
.get_sr = sun8i_i2s_get_sr_wss,
.get_wss = sun8i_i2s_get_sr_wss,
+ .set_chan_cfg = sun8i_i2s_set_chan_cfg,
};
static const struct sun4i_i2s_quirks sun8i_h3_i2s_quirks = {
@@ -968,8 +979,6 @@ static const struct sun4i_i2s_quirks sun8i_h3_i2s_quirks = {
.mclk_offset = 1,
.bclk_offset = 2,
.has_fmt_set_lrck_period = true,
- .has_chcfg = true,
- .has_chsel_tx_chen = 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),
@@ -977,12 +986,9 @@ static const struct sun4i_i2s_quirks sun8i_h3_i2s_quirks = {
.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),
- .field_txchanmap = REG_FIELD(SUN8I_I2S_TX_CHAN_MAP_REG, 0, 31),
- .field_rxchanmap = REG_FIELD(SUN8I_I2S_RX_CHAN_MAP_REG, 0, 31),
- .field_txchansel = REG_FIELD(SUN8I_I2S_TX_CHAN_SEL_REG, 0, 2),
- .field_rxchansel = REG_FIELD(SUN8I_I2S_RX_CHAN_SEL_REG, 0, 2),
.get_sr = sun8i_i2s_get_sr_wss,
.get_wss = sun8i_i2s_get_sr_wss,
+ .set_chan_cfg = sun8i_i2s_set_chan_cfg,
};
static const struct sun4i_i2s_quirks sun50i_a64_codec_i2s_quirks = {
@@ -996,12 +1002,9 @@ static const struct sun4i_i2s_quirks sun50i_a64_codec_i2s_quirks = {
.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),
- .field_txchanmap = REG_FIELD(SUN4I_I2S_TX_CHAN_MAP_REG, 0, 31),
- .field_rxchanmap = REG_FIELD(SUN4I_I2S_RX_CHAN_MAP_REG, 0, 31),
- .field_txchansel = REG_FIELD(SUN4I_I2S_TX_CHAN_SEL_REG, 0, 2),
- .field_rxchansel = REG_FIELD(SUN4I_I2S_RX_CHAN_SEL_REG, 0, 2),
.get_sr = sun4i_i2s_get_sr,
.get_wss = sun4i_i2s_get_wss,
+ .set_chan_cfg = sun4i_i2s_set_chan_cfg,
};
static int sun4i_i2s_init_regmap_fields(struct device *dev,
@@ -1043,28 +1046,7 @@ static int sun4i_i2s_init_regmap_fields(struct device *dev,
if (IS_ERR(i2s->field_fmt_mode))
return PTR_ERR(i2s->field_fmt_mode);
- i2s->field_txchanmap =
- devm_regmap_field_alloc(dev, i2s->regmap,
- i2s->variant->field_txchanmap);
- if (IS_ERR(i2s->field_txchanmap))
- return PTR_ERR(i2s->field_txchanmap);
-
- i2s->field_rxchanmap =
- devm_regmap_field_alloc(dev, i2s->regmap,
- i2s->variant->field_rxchanmap);
- if (IS_ERR(i2s->field_rxchanmap))
- return PTR_ERR(i2s->field_rxchanmap);
-
- i2s->field_txchansel =
- devm_regmap_field_alloc(dev, i2s->regmap,
- i2s->variant->field_txchansel);
- if (IS_ERR(i2s->field_txchansel))
- return PTR_ERR(i2s->field_txchansel);
-
- i2s->field_rxchansel =
- devm_regmap_field_alloc(dev, i2s->regmap,
- i2s->variant->field_rxchansel);
- return PTR_ERR_OR_ZERO(i2s->field_rxchansel);
+ return 0;
}
static int sun4i_i2s_probe(struct platform_device *pdev)