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

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.comSigned-off-by: default avatarMark Brown <broonie@kernel.org>
parent d70be625
...@@ -93,6 +93,11 @@ ...@@ -93,6 +93,11 @@
#define SUN8I_I2S_CTRL_BCLK_OUT BIT(18) #define SUN8I_I2S_CTRL_BCLK_OUT BIT(18)
#define SUN8I_I2S_CTRL_LRCK_OUT BIT(17) #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_MASK GENMASK(17, 8)
#define SUN8I_I2S_FMT0_LRCK_PERIOD(period) ((period - 1) << 8) #define SUN8I_I2S_FMT0_LRCK_PERIOD(period) ((period - 1) << 8)
...@@ -121,9 +126,7 @@ struct sun4i_i2s; ...@@ -121,9 +126,7 @@ struct sun4i_i2s;
* struct sun4i_i2s_quirks - Differences between SoC variants. * struct sun4i_i2s_quirks - Differences between SoC variants.
* *
* @has_reset: SoC needs reset deasserted. * @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_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. * @reg_offset_txdata: offset of the tx fifo.
* @sun4i_i2s_regmap: regmap config to use. * @sun4i_i2s_regmap: regmap config to use.
* @mclk_offset: Value by which mclkdiv needs to be adjusted. * @mclk_offset: Value by which mclkdiv needs to be adjusted.
...@@ -133,13 +136,10 @@ struct sun4i_i2s; ...@@ -133,13 +136,10 @@ struct sun4i_i2s;
* @field_fmt_sr: regmap field to set sample resolution. * @field_fmt_sr: regmap field to set sample resolution.
* @field_fmt_bclk: regmap field to set clk polarity. * @field_fmt_bclk: regmap field to set clk polarity.
* @field_fmt_lrclk: regmap field to set frame polarity. * @field_fmt_lrclk: regmap field to set frame polarity.
* @field_fmt_mode: regmap field to set the operational mode.
*/ */
struct sun4i_i2s_quirks { struct sun4i_i2s_quirks {
bool has_reset; bool has_reset;
bool has_slave_select_bit;
bool has_fmt_set_lrck_period; bool has_fmt_set_lrck_period;
bool has_chsel_offset;
unsigned int reg_offset_txdata; /* TX FIFO */ unsigned int reg_offset_txdata; /* TX FIFO */
const struct regmap_config *sun4i_i2s_regmap; const struct regmap_config *sun4i_i2s_regmap;
unsigned int mclk_offset; unsigned int mclk_offset;
...@@ -151,12 +151,12 @@ struct sun4i_i2s_quirks { ...@@ -151,12 +151,12 @@ struct sun4i_i2s_quirks {
struct reg_field field_fmt_sr; struct reg_field field_fmt_sr;
struct reg_field field_fmt_bclk; struct reg_field field_fmt_bclk;
struct reg_field field_fmt_lrclk; struct reg_field field_fmt_lrclk;
struct reg_field field_fmt_mode;
s8 (*get_sr)(const struct sun4i_i2s *, int); s8 (*get_sr)(const struct sun4i_i2s *, int);
s8 (*get_wss)(const struct sun4i_i2s *, int); s8 (*get_wss)(const struct sun4i_i2s *, int);
int (*set_chan_cfg)(const struct sun4i_i2s *, int (*set_chan_cfg)(const struct sun4i_i2s *,
const struct snd_pcm_hw_params *); const struct snd_pcm_hw_params *);
int (*set_fmt)(const struct sun4i_i2s *, unsigned int);
}; };
struct sun4i_i2s { struct sun4i_i2s {
...@@ -176,7 +176,6 @@ struct sun4i_i2s { ...@@ -176,7 +176,6 @@ struct sun4i_i2s {
struct regmap_field *field_fmt_sr; struct regmap_field *field_fmt_sr;
struct regmap_field *field_fmt_bclk; struct regmap_field *field_fmt_bclk;
struct regmap_field *field_fmt_lrclk; struct regmap_field *field_fmt_lrclk;
struct regmap_field *field_fmt_mode;
const struct sun4i_i2s_quirks *variant; const struct sun4i_i2s_quirks *variant;
}; };
...@@ -465,52 +464,117 @@ static int sun4i_i2s_hw_params(struct snd_pcm_substream *substream, ...@@ -465,52 +464,117 @@ static int sun4i_i2s_hw_params(struct snd_pcm_substream *substream,
params_width(params)); 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 val;
u32 offset = 0;
u32 bclk_polarity = SUN4I_I2S_FMT0_POLARITY_NORMAL;
u32 lrclk_polarity = SUN4I_I2S_FMT0_POLARITY_NORMAL;
/* DAI Mode */ /* DAI Mode */
switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
case SND_SOC_DAIFMT_I2S: case SND_SOC_DAIFMT_I2S:
val = SUN4I_I2S_FMT0_FMT_I2S; val = SUN4I_I2S_FMT0_FMT_I2S;
offset = 1;
break; break;
case SND_SOC_DAIFMT_LEFT_J: case SND_SOC_DAIFMT_LEFT_J:
val = SUN4I_I2S_FMT0_FMT_LEFT_J; val = SUN4I_I2S_FMT0_FMT_LEFT_J;
break; break;
case SND_SOC_DAIFMT_RIGHT_J: case SND_SOC_DAIFMT_RIGHT_J:
val = SUN4I_I2S_FMT0_FMT_RIGHT_J; val = SUN4I_I2S_FMT0_FMT_RIGHT_J;
break; break;
default: default:
dev_err(dai->dev, "Unsupported format: %d\n",
fmt & SND_SOC_DAIFMT_FORMAT_MASK);
return -EINVAL; return -EINVAL;
} }
if (i2s->variant->has_chsel_offset) { regmap_update_bits(i2s->regmap, SUN4I_I2S_FMT0_REG,
/* SUN4I_I2S_FMT0_FMT_MASK, val);
* offset being set indicates that we're connected to an i2s
* device, however offset is only used on the sun8i block and /* DAI clock master masks */
* i2s shares the same setting with the LJ format. Increment switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
* val so that the bit to value to write is correct. case SND_SOC_DAIFMT_CBS_CFS:
*/ /* BCLK and LRCLK master */
if (offset > 0) val = SUN4I_I2S_CTRL_MODE_MASTER;
val++; break;
/* blck offset determines whether i2s or LJ */
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, regmap_update_bits(i2s->regmap, SUN8I_I2S_TX_CHAN_SEL_REG,
SUN8I_I2S_TX_CHAN_OFFSET_MASK, SUN8I_I2S_TX_CHAN_OFFSET_MASK,
SUN8I_I2S_TX_CHAN_OFFSET(offset)); SUN8I_I2S_TX_CHAN_OFFSET(offset));
regmap_update_bits(i2s->regmap, SUN8I_I2S_RX_CHAN_SEL_REG, regmap_update_bits(i2s->regmap, SUN8I_I2S_RX_CHAN_SEL_REG,
SUN8I_I2S_TX_CHAN_OFFSET_MASK, SUN8I_I2S_TX_CHAN_OFFSET_MASK,
SUN8I_I2S_TX_CHAN_OFFSET(offset)); 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_field_write(i2s->field_fmt_mode, val); regmap_update_bits(i2s->regmap, SUN4I_I2S_CTRL_REG,
SUN8I_I2S_CTRL_BCLK_OUT | SUN8I_I2S_CTRL_LRCK_OUT,
val);
return 0;
}
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 */ /* DAI clock polarity */
switch (fmt & SND_SOC_DAIFMT_INV_MASK) { 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) ...@@ -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_bclk, bclk_polarity);
regmap_field_write(i2s->field_fmt_lrclk, lrclk_polarity); regmap_field_write(i2s->field_fmt_lrclk, lrclk_polarity);
if (i2s->variant->has_slave_select_bit) { ret = i2s->variant->set_fmt(i2s, fmt);
/* DAI clock master masks */ if (ret) {
switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { dev_err(dai->dev, "Unsupported format configuration\n");
case SND_SOC_DAIFMT_CBS_CFS: return ret;
/* 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);
} }
/* Set significant bits in our FIFOs */ /* Set significant bits in our FIFOs */
...@@ -933,11 +957,10 @@ static const struct sun4i_i2s_quirks sun4i_a10_i2s_quirks = { ...@@ -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_sr = REG_FIELD(SUN4I_I2S_FMT0_REG, 4, 5),
.field_fmt_bclk = REG_FIELD(SUN4I_I2S_FMT0_REG, 6, 6), .field_fmt_bclk = REG_FIELD(SUN4I_I2S_FMT0_REG, 6, 6),
.field_fmt_lrclk = REG_FIELD(SUN4I_I2S_FMT0_REG, 7, 7), .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_sr = sun4i_i2s_get_sr,
.get_wss = sun4i_i2s_get_wss, .get_wss = sun4i_i2s_get_wss,
.set_chan_cfg = sun4i_i2s_set_chan_cfg, .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 = { static const struct sun4i_i2s_quirks sun6i_a31_i2s_quirks = {
...@@ -949,11 +972,10 @@ 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_sr = REG_FIELD(SUN4I_I2S_FMT0_REG, 4, 5),
.field_fmt_bclk = REG_FIELD(SUN4I_I2S_FMT0_REG, 6, 6), .field_fmt_bclk = REG_FIELD(SUN4I_I2S_FMT0_REG, 6, 6),
.field_fmt_lrclk = REG_FIELD(SUN4I_I2S_FMT0_REG, 7, 7), .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_sr = sun4i_i2s_get_sr,
.get_wss = sun4i_i2s_get_wss, .get_wss = sun4i_i2s_get_wss,
.set_chan_cfg = sun4i_i2s_set_chan_cfg, .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 = { static const struct sun4i_i2s_quirks sun8i_a83t_i2s_quirks = {
...@@ -965,11 +987,10 @@ 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_sr = REG_FIELD(SUN4I_I2S_FMT0_REG, 4, 5),
.field_fmt_bclk = REG_FIELD(SUN4I_I2S_FMT0_REG, 6, 6), .field_fmt_bclk = REG_FIELD(SUN4I_I2S_FMT0_REG, 6, 6),
.field_fmt_lrclk = REG_FIELD(SUN4I_I2S_FMT0_REG, 7, 7), .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_sr = sun8i_i2s_get_sr_wss,
.get_wss = sun8i_i2s_get_sr_wss, .get_wss = sun8i_i2s_get_sr_wss,
.set_chan_cfg = sun8i_i2s_set_chan_cfg, .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 = { static const struct sun4i_i2s_quirks sun8i_h3_i2s_quirks = {
...@@ -979,32 +1000,30 @@ 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, .mclk_offset = 1,
.bclk_offset = 2, .bclk_offset = 2,
.has_fmt_set_lrck_period = true, .has_fmt_set_lrck_period = true,
.has_chsel_offset = true,
.field_clkdiv_mclk_en = REG_FIELD(SUN4I_I2S_CLK_DIV_REG, 8, 8), .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_wss = REG_FIELD(SUN4I_I2S_FMT0_REG, 0, 2),
.field_fmt_sr = REG_FIELD(SUN4I_I2S_FMT0_REG, 4, 6), .field_fmt_sr = REG_FIELD(SUN4I_I2S_FMT0_REG, 4, 6),
.field_fmt_bclk = REG_FIELD(SUN4I_I2S_FMT0_REG, 7, 7), .field_fmt_bclk = REG_FIELD(SUN4I_I2S_FMT0_REG, 7, 7),
.field_fmt_lrclk = REG_FIELD(SUN4I_I2S_FMT0_REG, 19, 19), .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_sr = sun8i_i2s_get_sr_wss,
.get_wss = sun8i_i2s_get_sr_wss, .get_wss = sun8i_i2s_get_sr_wss,
.set_chan_cfg = sun8i_i2s_set_chan_cfg, .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 = { static const struct sun4i_i2s_quirks sun50i_a64_codec_i2s_quirks = {
.has_reset = true, .has_reset = true,
.reg_offset_txdata = SUN8I_I2S_FIFO_TX_REG, .reg_offset_txdata = SUN8I_I2S_FIFO_TX_REG,
.sun4i_i2s_regmap = &sun4i_i2s_regmap_config, .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_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_wss = REG_FIELD(SUN4I_I2S_FMT0_REG, 2, 3),
.field_fmt_sr = REG_FIELD(SUN4I_I2S_FMT0_REG, 4, 5), .field_fmt_sr = REG_FIELD(SUN4I_I2S_FMT0_REG, 4, 5),
.field_fmt_bclk = REG_FIELD(SUN4I_I2S_FMT0_REG, 6, 6), .field_fmt_bclk = REG_FIELD(SUN4I_I2S_FMT0_REG, 6, 6),
.field_fmt_lrclk = REG_FIELD(SUN4I_I2S_FMT0_REG, 7, 7), .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_sr = sun4i_i2s_get_sr,
.get_wss = sun4i_i2s_get_wss, .get_wss = sun4i_i2s_get_wss,
.set_chan_cfg = sun4i_i2s_set_chan_cfg, .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, static int sun4i_i2s_init_regmap_fields(struct device *dev,
...@@ -1040,12 +1059,6 @@ 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)) if (IS_ERR(i2s->field_fmt_lrclk))
return PTR_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; return 0;
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment