aboutsummaryrefslogtreecommitdiffstats
path: root/sound
diff options
context:
space:
mode:
authorMaxime Ripard <maxime.ripard@bootlin.com>2019-08-19 15:25:11 -0400
committerMark Brown <broonie@kernel.org>2019-08-20 13:30:00 -0400
commitd70be625f25af7a2bc91b7d17d205f6071f08f2f (patch)
tree9434180c9d68075a60375b1ff80f744b28da49d0 /sound
parentdd28d54c248fa37794dfa3d23a24ff99a1d3b6ac (diff)
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')
-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 @@
80#define SUN4I_I2S_TX_CNT_REG 0x2c 80#define SUN4I_I2S_TX_CNT_REG 0x2c
81 81
82#define SUN4I_I2S_TX_CHAN_SEL_REG 0x30 82#define SUN4I_I2S_TX_CHAN_SEL_REG 0x30
83#define SUN4I_I2S_CHAN_SEL_MASK GENMASK(2, 0)
83#define SUN4I_I2S_CHAN_SEL(num_chan) (((num_chan) - 1) << 0) 84#define SUN4I_I2S_CHAN_SEL(num_chan) (((num_chan) - 1) << 0)
84 85
85#define SUN4I_I2S_TX_CHAN_MAP_REG 0x34 86#define SUN4I_I2S_TX_CHAN_MAP_REG 0x34
@@ -122,8 +123,6 @@ struct sun4i_i2s;
122 * @has_reset: SoC needs reset deasserted. 123 * @has_reset: SoC needs reset deasserted.
123 * @has_slave_select_bit: SoC has a bit to enable slave mode. 124 * @has_slave_select_bit: SoC has a bit to enable slave mode.
124 * @has_fmt_set_lrck_period: SoC requires lrclk period to be set. 125 * @has_fmt_set_lrck_period: SoC requires lrclk period to be set.
125 * @has_chcfg: tx and rx slot number need to be set.
126 * @has_chsel_tx_chen: SoC requires that the tx channels are enabled.
127 * @has_chsel_offset: SoC uses offset for selecting dai operational mode. 126 * @has_chsel_offset: SoC uses offset for selecting dai operational mode.
128 * @reg_offset_txdata: offset of the tx fifo. 127 * @reg_offset_txdata: offset of the tx fifo.
129 * @sun4i_i2s_regmap: regmap config to use. 128 * @sun4i_i2s_regmap: regmap config to use.
@@ -135,17 +134,11 @@ struct sun4i_i2s;
135 * @field_fmt_bclk: regmap field to set clk polarity. 134 * @field_fmt_bclk: regmap field to set clk polarity.
136 * @field_fmt_lrclk: regmap field to set frame polarity. 135 * @field_fmt_lrclk: regmap field to set frame polarity.
137 * @field_fmt_mode: regmap field to set the operational mode. 136 * @field_fmt_mode: regmap field to set the operational mode.
138 * @field_txchanmap: location of the tx channel mapping register.
139 * @field_rxchanmap: location of the rx channel mapping register.
140 * @field_txchansel: location of the tx channel select bit fields.
141 * @field_rxchansel: location of the rx channel select bit fields.
142 */ 137 */
143struct sun4i_i2s_quirks { 138struct sun4i_i2s_quirks {
144 bool has_reset; 139 bool has_reset;
145 bool has_slave_select_bit; 140 bool has_slave_select_bit;
146 bool has_fmt_set_lrck_period; 141 bool has_fmt_set_lrck_period;
147 bool has_chcfg;
148 bool has_chsel_tx_chen;
149 bool has_chsel_offset; 142 bool has_chsel_offset;
150 unsigned int reg_offset_txdata; /* TX FIFO */ 143 unsigned int reg_offset_txdata; /* TX FIFO */
151 const struct regmap_config *sun4i_i2s_regmap; 144 const struct regmap_config *sun4i_i2s_regmap;
@@ -159,13 +152,11 @@ struct sun4i_i2s_quirks {
159 struct reg_field field_fmt_bclk; 152 struct reg_field field_fmt_bclk;
160 struct reg_field field_fmt_lrclk; 153 struct reg_field field_fmt_lrclk;
161 struct reg_field field_fmt_mode; 154 struct reg_field field_fmt_mode;
162 struct reg_field field_txchanmap;
163 struct reg_field field_rxchanmap;
164 struct reg_field field_txchansel;
165 struct reg_field field_rxchansel;
166 155
167 s8 (*get_sr)(const struct sun4i_i2s *, int); 156 s8 (*get_sr)(const struct sun4i_i2s *, int);
168 s8 (*get_wss)(const struct sun4i_i2s *, int); 157 s8 (*get_wss)(const struct sun4i_i2s *, int);
158 int (*set_chan_cfg)(const struct sun4i_i2s *,
159 const struct snd_pcm_hw_params *);
169}; 160};
170 161
171struct sun4i_i2s { 162struct sun4i_i2s {
@@ -186,10 +177,6 @@ struct sun4i_i2s {
186 struct regmap_field *field_fmt_bclk; 177 struct regmap_field *field_fmt_bclk;
187 struct regmap_field *field_fmt_lrclk; 178 struct regmap_field *field_fmt_lrclk;
188 struct regmap_field *field_fmt_mode; 179 struct regmap_field *field_fmt_mode;
189 struct regmap_field *field_txchanmap;
190 struct regmap_field *field_rxchanmap;
191 struct regmap_field *field_txchansel;
192 struct regmap_field *field_rxchansel;
193 180
194 const struct sun4i_i2s_quirks *variant; 181 const struct sun4i_i2s_quirks *variant;
195}; 182};
@@ -380,44 +367,77 @@ static s8 sun8i_i2s_get_sr_wss(const struct sun4i_i2s *i2s, int width)
380 return (width - 8) / 4 + 1; 367 return (width - 8) / 4 + 1;
381} 368}
382 369
383static int sun4i_i2s_hw_params(struct snd_pcm_substream *substream, 370static int sun4i_i2s_set_chan_cfg(const struct sun4i_i2s *i2s,
384 struct snd_pcm_hw_params *params, 371 const struct snd_pcm_hw_params *params)
385 struct snd_soc_dai *dai)
386{ 372{
387 struct sun4i_i2s *i2s = snd_soc_dai_get_drvdata(dai); 373 unsigned int channels = params_channels(params);
388 int sr, wss, channels;
389 u32 width;
390 374
391 channels = params_channels(params); 375 if (channels != 2)
392 if (channels != 2) {
393 dev_err(dai->dev, "Unsupported number of channels: %d\n",
394 channels);
395 return -EINVAL; 376 return -EINVAL;
396 }
397 377
398 if (i2s->variant->has_chcfg) { 378 /* Map the channels for playback and capture */
399 regmap_update_bits(i2s->regmap, SUN8I_I2S_CHAN_CFG_REG, 379 regmap_write(i2s->regmap, SUN4I_I2S_TX_CHAN_MAP_REG, 0x76543210);
400 SUN8I_I2S_CHAN_CFG_TX_SLOT_NUM_MASK, 380 regmap_write(i2s->regmap, SUN4I_I2S_RX_CHAN_MAP_REG, 0x00003210);
401 SUN8I_I2S_CHAN_CFG_TX_SLOT_NUM(channels)); 381
402 regmap_update_bits(i2s->regmap, SUN8I_I2S_CHAN_CFG_REG, 382 /* Configure the channels */
403 SUN8I_I2S_CHAN_CFG_RX_SLOT_NUM_MASK, 383 regmap_update_bits(i2s->regmap, SUN4I_I2S_TX_CHAN_SEL_REG,
404 SUN8I_I2S_CHAN_CFG_RX_SLOT_NUM(channels)); 384 SUN4I_I2S_CHAN_SEL_MASK,
405 } 385 SUN4I_I2S_CHAN_SEL(channels));
386 regmap_update_bits(i2s->regmap, SUN4I_I2S_RX_CHAN_SEL_REG,
387 SUN4I_I2S_CHAN_SEL_MASK,
388 SUN4I_I2S_CHAN_SEL(channels));
389
390 return 0;
391}
392
393static int sun8i_i2s_set_chan_cfg(const struct sun4i_i2s *i2s,
394 const struct snd_pcm_hw_params *params)
395{
396 unsigned int channels = params_channels(params);
397
398 if (channels != 2)
399 return -EINVAL;
406 400
407 /* Map the channels for playback and capture */ 401 /* Map the channels for playback and capture */
408 regmap_field_write(i2s->field_txchanmap, 0x76543210); 402 regmap_write(i2s->regmap, SUN8I_I2S_TX_CHAN_MAP_REG, 0x76543210);
409 regmap_field_write(i2s->field_rxchanmap, 0x00003210); 403 regmap_write(i2s->regmap, SUN8I_I2S_RX_CHAN_MAP_REG, 0x76543210);
410 404
411 /* Configure the channels */ 405 /* Configure the channels */
412 regmap_field_write(i2s->field_txchansel, 406 regmap_update_bits(i2s->regmap, SUN8I_I2S_TX_CHAN_SEL_REG,
407 SUN4I_I2S_CHAN_SEL_MASK,
413 SUN4I_I2S_CHAN_SEL(channels)); 408 SUN4I_I2S_CHAN_SEL(channels));
414 regmap_field_write(i2s->field_rxchansel, 409
410 regmap_update_bits(i2s->regmap, SUN8I_I2S_RX_CHAN_SEL_REG,
411 SUN4I_I2S_CHAN_SEL_MASK,
415 SUN4I_I2S_CHAN_SEL(channels)); 412 SUN4I_I2S_CHAN_SEL(channels));
416 413
417 if (i2s->variant->has_chsel_tx_chen) 414 regmap_update_bits(i2s->regmap, SUN8I_I2S_CHAN_CFG_REG,
418 regmap_update_bits(i2s->regmap, SUN8I_I2S_TX_CHAN_SEL_REG, 415 SUN8I_I2S_CHAN_CFG_TX_SLOT_NUM_MASK,
419 SUN8I_I2S_TX_CHAN_EN_MASK, 416 SUN8I_I2S_CHAN_CFG_TX_SLOT_NUM(channels));
420 SUN8I_I2S_TX_CHAN_EN(channels)); 417 regmap_update_bits(i2s->regmap, SUN8I_I2S_CHAN_CFG_REG,
418 SUN8I_I2S_CHAN_CFG_RX_SLOT_NUM_MASK,
419 SUN8I_I2S_CHAN_CFG_RX_SLOT_NUM(channels));
420
421 regmap_update_bits(i2s->regmap, SUN8I_I2S_TX_CHAN_SEL_REG,
422 SUN8I_I2S_TX_CHAN_EN_MASK,
423 SUN8I_I2S_TX_CHAN_EN(channels));
424
425 return 0;
426}
427
428static int sun4i_i2s_hw_params(struct snd_pcm_substream *substream,
429 struct snd_pcm_hw_params *params,
430 struct snd_soc_dai *dai)
431{
432 struct sun4i_i2s *i2s = snd_soc_dai_get_drvdata(dai);
433 int ret, sr, wss;
434 u32 width;
435
436 ret = i2s->variant->set_chan_cfg(i2s, params);
437 if (ret < 0) {
438 dev_err(dai->dev, "Invalid channel configuration\n");
439 return ret;
440 }
421 441
422 switch (params_physical_width(params)) { 442 switch (params_physical_width(params)) {
423 case 16: 443 case 16:
@@ -915,12 +935,9 @@ static const struct sun4i_i2s_quirks sun4i_a10_i2s_quirks = {
915 .field_fmt_lrclk = REG_FIELD(SUN4I_I2S_FMT0_REG, 7, 7), 935 .field_fmt_lrclk = REG_FIELD(SUN4I_I2S_FMT0_REG, 7, 7),
916 .has_slave_select_bit = true, 936 .has_slave_select_bit = true,
917 .field_fmt_mode = REG_FIELD(SUN4I_I2S_FMT0_REG, 0, 1), 937 .field_fmt_mode = REG_FIELD(SUN4I_I2S_FMT0_REG, 0, 1),
918 .field_txchanmap = REG_FIELD(SUN4I_I2S_TX_CHAN_MAP_REG, 0, 31),
919 .field_rxchanmap = REG_FIELD(SUN4I_I2S_RX_CHAN_MAP_REG, 0, 31),
920 .field_txchansel = REG_FIELD(SUN4I_I2S_TX_CHAN_SEL_REG, 0, 2),
921 .field_rxchansel = REG_FIELD(SUN4I_I2S_RX_CHAN_SEL_REG, 0, 2),
922 .get_sr = sun4i_i2s_get_sr, 938 .get_sr = sun4i_i2s_get_sr,
923 .get_wss = sun4i_i2s_get_wss, 939 .get_wss = sun4i_i2s_get_wss,
940 .set_chan_cfg = sun4i_i2s_set_chan_cfg,
924}; 941};
925 942
926static const struct sun4i_i2s_quirks sun6i_a31_i2s_quirks = { 943static const struct sun4i_i2s_quirks sun6i_a31_i2s_quirks = {
@@ -934,12 +951,9 @@ static const struct sun4i_i2s_quirks sun6i_a31_i2s_quirks = {
934 .field_fmt_lrclk = REG_FIELD(SUN4I_I2S_FMT0_REG, 7, 7), 951 .field_fmt_lrclk = REG_FIELD(SUN4I_I2S_FMT0_REG, 7, 7),
935 .has_slave_select_bit = true, 952 .has_slave_select_bit = true,
936 .field_fmt_mode = REG_FIELD(SUN4I_I2S_FMT0_REG, 0, 1), 953 .field_fmt_mode = REG_FIELD(SUN4I_I2S_FMT0_REG, 0, 1),
937 .field_txchanmap = REG_FIELD(SUN4I_I2S_TX_CHAN_MAP_REG, 0, 31),
938 .field_rxchanmap = REG_FIELD(SUN4I_I2S_RX_CHAN_MAP_REG, 0, 31),
939 .field_txchansel = REG_FIELD(SUN4I_I2S_TX_CHAN_SEL_REG, 0, 2),
940 .field_rxchansel = REG_FIELD(SUN4I_I2S_RX_CHAN_SEL_REG, 0, 2),
941 .get_sr = sun4i_i2s_get_sr, 954 .get_sr = sun4i_i2s_get_sr,
942 .get_wss = sun4i_i2s_get_wss, 955 .get_wss = sun4i_i2s_get_wss,
956 .set_chan_cfg = sun4i_i2s_set_chan_cfg,
943}; 957};
944 958
945static const struct sun4i_i2s_quirks sun8i_a83t_i2s_quirks = { 959static const struct sun4i_i2s_quirks sun8i_a83t_i2s_quirks = {
@@ -953,12 +967,9 @@ static const struct sun4i_i2s_quirks sun8i_a83t_i2s_quirks = {
953 .field_fmt_lrclk = REG_FIELD(SUN4I_I2S_FMT0_REG, 7, 7), 967 .field_fmt_lrclk = REG_FIELD(SUN4I_I2S_FMT0_REG, 7, 7),
954 .has_slave_select_bit = true, 968 .has_slave_select_bit = true,
955 .field_fmt_mode = REG_FIELD(SUN4I_I2S_FMT0_REG, 0, 1), 969 .field_fmt_mode = REG_FIELD(SUN4I_I2S_FMT0_REG, 0, 1),
956 .field_txchanmap = REG_FIELD(SUN4I_I2S_TX_CHAN_MAP_REG, 0, 31),
957 .field_rxchanmap = REG_FIELD(SUN4I_I2S_RX_CHAN_MAP_REG, 0, 31),
958 .field_txchansel = REG_FIELD(SUN4I_I2S_TX_CHAN_SEL_REG, 0, 2),
959 .field_rxchansel = REG_FIELD(SUN4I_I2S_RX_CHAN_SEL_REG, 0, 2),
960 .get_sr = sun8i_i2s_get_sr_wss, 970 .get_sr = sun8i_i2s_get_sr_wss,
961 .get_wss = sun8i_i2s_get_sr_wss, 971 .get_wss = sun8i_i2s_get_sr_wss,
972 .set_chan_cfg = sun8i_i2s_set_chan_cfg,
962}; 973};
963 974
964static const struct sun4i_i2s_quirks sun8i_h3_i2s_quirks = { 975static const struct sun4i_i2s_quirks sun8i_h3_i2s_quirks = {
@@ -968,8 +979,6 @@ static const struct sun4i_i2s_quirks sun8i_h3_i2s_quirks = {
968 .mclk_offset = 1, 979 .mclk_offset = 1,
969 .bclk_offset = 2, 980 .bclk_offset = 2,
970 .has_fmt_set_lrck_period = true, 981 .has_fmt_set_lrck_period = true,
971 .has_chcfg = true,
972 .has_chsel_tx_chen = true,
973 .has_chsel_offset = true, 982 .has_chsel_offset = true,
974 .field_clkdiv_mclk_en = REG_FIELD(SUN4I_I2S_CLK_DIV_REG, 8, 8), 983 .field_clkdiv_mclk_en = REG_FIELD(SUN4I_I2S_CLK_DIV_REG, 8, 8),
975 .field_fmt_wss = REG_FIELD(SUN4I_I2S_FMT0_REG, 0, 2), 984 .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 = {
977 .field_fmt_bclk = REG_FIELD(SUN4I_I2S_FMT0_REG, 7, 7), 986 .field_fmt_bclk = REG_FIELD(SUN4I_I2S_FMT0_REG, 7, 7),
978 .field_fmt_lrclk = REG_FIELD(SUN4I_I2S_FMT0_REG, 19, 19), 987 .field_fmt_lrclk = REG_FIELD(SUN4I_I2S_FMT0_REG, 19, 19),
979 .field_fmt_mode = REG_FIELD(SUN4I_I2S_CTRL_REG, 4, 5), 988 .field_fmt_mode = REG_FIELD(SUN4I_I2S_CTRL_REG, 4, 5),
980 .field_txchanmap = REG_FIELD(SUN8I_I2S_TX_CHAN_MAP_REG, 0, 31),
981 .field_rxchanmap = REG_FIELD(SUN8I_I2S_RX_CHAN_MAP_REG, 0, 31),
982 .field_txchansel = REG_FIELD(SUN8I_I2S_TX_CHAN_SEL_REG, 0, 2),
983 .field_rxchansel = REG_FIELD(SUN8I_I2S_RX_CHAN_SEL_REG, 0, 2),
984 .get_sr = sun8i_i2s_get_sr_wss, 989 .get_sr = sun8i_i2s_get_sr_wss,
985 .get_wss = sun8i_i2s_get_sr_wss, 990 .get_wss = sun8i_i2s_get_sr_wss,
991 .set_chan_cfg = sun8i_i2s_set_chan_cfg,
986}; 992};
987 993
988static const struct sun4i_i2s_quirks sun50i_a64_codec_i2s_quirks = { 994static 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 = {
996 .field_fmt_bclk = REG_FIELD(SUN4I_I2S_FMT0_REG, 6, 6), 1002 .field_fmt_bclk = REG_FIELD(SUN4I_I2S_FMT0_REG, 6, 6),
997 .field_fmt_lrclk = REG_FIELD(SUN4I_I2S_FMT0_REG, 7, 7), 1003 .field_fmt_lrclk = REG_FIELD(SUN4I_I2S_FMT0_REG, 7, 7),
998 .field_fmt_mode = REG_FIELD(SUN4I_I2S_FMT0_REG, 0, 1), 1004 .field_fmt_mode = REG_FIELD(SUN4I_I2S_FMT0_REG, 0, 1),
999 .field_txchanmap = REG_FIELD(SUN4I_I2S_TX_CHAN_MAP_REG, 0, 31),
1000 .field_rxchanmap = REG_FIELD(SUN4I_I2S_RX_CHAN_MAP_REG, 0, 31),
1001 .field_txchansel = REG_FIELD(SUN4I_I2S_TX_CHAN_SEL_REG, 0, 2),
1002 .field_rxchansel = REG_FIELD(SUN4I_I2S_RX_CHAN_SEL_REG, 0, 2),
1003 .get_sr = sun4i_i2s_get_sr, 1005 .get_sr = sun4i_i2s_get_sr,
1004 .get_wss = sun4i_i2s_get_wss, 1006 .get_wss = sun4i_i2s_get_wss,
1007 .set_chan_cfg = sun4i_i2s_set_chan_cfg,
1005}; 1008};
1006 1009
1007static int sun4i_i2s_init_regmap_fields(struct device *dev, 1010static int sun4i_i2s_init_regmap_fields(struct device *dev,
@@ -1043,28 +1046,7 @@ static int sun4i_i2s_init_regmap_fields(struct device *dev,
1043 if (IS_ERR(i2s->field_fmt_mode)) 1046 if (IS_ERR(i2s->field_fmt_mode))
1044 return PTR_ERR(i2s->field_fmt_mode); 1047 return PTR_ERR(i2s->field_fmt_mode);
1045 1048
1046 i2s->field_txchanmap = 1049 return 0;
1047 devm_regmap_field_alloc(dev, i2s->regmap,
1048 i2s->variant->field_txchanmap);
1049 if (IS_ERR(i2s->field_txchanmap))
1050 return PTR_ERR(i2s->field_txchanmap);
1051
1052 i2s->field_rxchanmap =
1053 devm_regmap_field_alloc(dev, i2s->regmap,
1054 i2s->variant->field_rxchanmap);
1055 if (IS_ERR(i2s->field_rxchanmap))
1056 return PTR_ERR(i2s->field_rxchanmap);
1057
1058 i2s->field_txchansel =
1059 devm_regmap_field_alloc(dev, i2s->regmap,
1060 i2s->variant->field_txchansel);
1061 if (IS_ERR(i2s->field_txchansel))
1062 return PTR_ERR(i2s->field_txchansel);
1063
1064 i2s->field_rxchansel =
1065 devm_regmap_field_alloc(dev, i2s->regmap,
1066 i2s->variant->field_rxchansel);
1067 return PTR_ERR_OR_ZERO(i2s->field_rxchansel);
1068} 1050}
1069 1051
1070static int sun4i_i2s_probe(struct platform_device *pdev) 1052static int sun4i_i2s_probe(struct platform_device *pdev)