aboutsummaryrefslogtreecommitdiffstats
path: root/sound
diff options
context:
space:
mode:
authorMaxime Ripard <maxime.ripard@bootlin.com>2019-08-19 15:25:16 -0400
committerMark Brown <broonie@kernel.org>2019-08-20 13:31:42 -0400
commitc1d3a921d72bd21f266ca28c15213fbe78160a4b (patch)
tree2b203dbabb3c9cfa74988b11385efb0294b88e80 /sound
parentfb19739d7f688142b61d0fca476188c4fd9e937a (diff)
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: Marcus Cooper <codekipper@gmail.com> [Maxime: Fix the commit log, use a field in the quirk structure] Signed-off-by: Maxime Ripard <maxime.ripard@bootlin.com> Link: https://lore.kernel.org/r/0e5b4abf06cd3202354315201c6af44caeb20236.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.c63
1 files changed, 49 insertions, 14 deletions
diff --git a/sound/soc/sunxi/sun4i-i2s.c b/sound/soc/sunxi/sun4i-i2s.c
index acfcdb26086a..0a7f1d0f7371 100644
--- a/sound/soc/sunxi/sun4i-i2s.c
+++ b/sound/soc/sunxi/sun4i-i2s.c
@@ -129,8 +129,6 @@ struct sun4i_i2s;
129 * @has_fmt_set_lrck_period: SoC requires lrclk period to be set. 129 * @has_fmt_set_lrck_period: SoC requires lrclk period to be set.
130 * @reg_offset_txdata: offset of the tx fifo. 130 * @reg_offset_txdata: offset of the tx fifo.
131 * @sun4i_i2s_regmap: regmap config to use. 131 * @sun4i_i2s_regmap: regmap config to use.
132 * @mclk_offset: Value by which mclkdiv needs to be adjusted.
133 * @bclk_offset: Value by which bclkdiv needs to be adjusted.
134 * @field_clkdiv_mclk_en: regmap field to enable mclk output. 132 * @field_clkdiv_mclk_en: regmap field to enable mclk output.
135 * @field_fmt_wss: regmap field to set word select size. 133 * @field_fmt_wss: regmap field to set word select size.
136 * @field_fmt_sr: regmap field to set sample resolution. 134 * @field_fmt_sr: regmap field to set sample resolution.
@@ -142,8 +140,6 @@ struct sun4i_i2s_quirks {
142 bool has_fmt_set_lrck_period; 140 bool has_fmt_set_lrck_period;
143 unsigned int reg_offset_txdata; /* TX FIFO */ 141 unsigned int reg_offset_txdata; /* TX FIFO */
144 const struct regmap_config *sun4i_i2s_regmap; 142 const struct regmap_config *sun4i_i2s_regmap;
145 unsigned int mclk_offset;
146 unsigned int bclk_offset;
147 143
148 /* Register fields for i2s */ 144 /* Register fields for i2s */
149 struct reg_field field_clkdiv_mclk_en; 145 struct reg_field field_clkdiv_mclk_en;
@@ -152,6 +148,11 @@ struct sun4i_i2s_quirks {
152 struct reg_field field_fmt_bclk; 148 struct reg_field field_fmt_bclk;
153 struct reg_field field_fmt_lrclk; 149 struct reg_field field_fmt_lrclk;
154 150
151 const struct sun4i_i2s_clk_div *bclk_dividers;
152 unsigned int num_bclk_dividers;
153 const struct sun4i_i2s_clk_div *mclk_dividers;
154 unsigned int num_mclk_dividers;
155
155 unsigned long (*get_bclk_parent_rate)(const struct sun4i_i2s *); 156 unsigned long (*get_bclk_parent_rate)(const struct sun4i_i2s *);
156 s8 (*get_sr)(const struct sun4i_i2s *, int); 157 s8 (*get_sr)(const struct sun4i_i2s *, int);
157 s8 (*get_wss)(const struct sun4i_i2s *, int); 158 s8 (*get_wss)(const struct sun4i_i2s *, int);
@@ -208,6 +209,24 @@ static const struct sun4i_i2s_clk_div sun4i_i2s_mclk_div[] = {
208 /* TODO - extend divide ratio supported by newer SoCs */ 209 /* TODO - extend divide ratio supported by newer SoCs */
209}; 210};
210 211
212static const struct sun4i_i2s_clk_div sun8i_i2s_clk_div[] = {
213 { .div = 1, .val = 1 },
214 { .div = 2, .val = 2 },
215 { .div = 4, .val = 3 },
216 { .div = 6, .val = 4 },
217 { .div = 8, .val = 5 },
218 { .div = 12, .val = 6 },
219 { .div = 16, .val = 7 },
220 { .div = 24, .val = 8 },
221 { .div = 32, .val = 9 },
222 { .div = 48, .val = 10 },
223 { .div = 64, .val = 11 },
224 { .div = 96, .val = 12 },
225 { .div = 128, .val = 13 },
226 { .div = 176, .val = 14 },
227 { .div = 192, .val = 15 },
228};
229
211static unsigned long sun4i_i2s_get_bclk_parent_rate(const struct sun4i_i2s *i2s) 230static unsigned long sun4i_i2s_get_bclk_parent_rate(const struct sun4i_i2s *i2s)
212{ 231{
213 return i2s->mclk_freq; 232 return i2s->mclk_freq;
@@ -223,11 +242,12 @@ static int sun4i_i2s_get_bclk_div(struct sun4i_i2s *i2s,
223 unsigned int sampling_rate, 242 unsigned int sampling_rate,
224 unsigned int word_size) 243 unsigned int word_size)
225{ 244{
245 const struct sun4i_i2s_clk_div *dividers = i2s->variant->bclk_dividers;
226 int div = parent_rate / sampling_rate / word_size / 2; 246 int div = parent_rate / sampling_rate / word_size / 2;
227 int i; 247 int i;
228 248
229 for (i = 0; i < ARRAY_SIZE(sun4i_i2s_bclk_div); i++) { 249 for (i = 0; i < i2s->variant->num_bclk_dividers; i++) {
230 const struct sun4i_i2s_clk_div *bdiv = &sun4i_i2s_bclk_div[i]; 250 const struct sun4i_i2s_clk_div *bdiv = &dividers[i];
231 251
232 if (bdiv->div == div) 252 if (bdiv->div == div)
233 return bdiv->val; 253 return bdiv->val;
@@ -240,11 +260,12 @@ static int sun4i_i2s_get_mclk_div(struct sun4i_i2s *i2s,
240 unsigned long parent_rate, 260 unsigned long parent_rate,
241 unsigned long mclk_rate) 261 unsigned long mclk_rate)
242{ 262{
263 const struct sun4i_i2s_clk_div *dividers = i2s->variant->mclk_dividers;
243 int div = parent_rate / mclk_rate; 264 int div = parent_rate / mclk_rate;
244 int i; 265 int i;
245 266
246 for (i = 0; i < ARRAY_SIZE(sun4i_i2s_mclk_div); i++) { 267 for (i = 0; i < i2s->variant->num_mclk_dividers; i++) {
247 const struct sun4i_i2s_clk_div *mdiv = &sun4i_i2s_mclk_div[i]; 268 const struct sun4i_i2s_clk_div *mdiv = &dividers[i];
248 269
249 if (mdiv->div == div) 270 if (mdiv->div == div)
250 return mdiv->val; 271 return mdiv->val;
@@ -326,10 +347,6 @@ static int sun4i_i2s_set_clk_rate(struct snd_soc_dai *dai,
326 return -EINVAL; 347 return -EINVAL;
327 } 348 }
328 349
329 /* Adjust the clock division values if needed */
330 bclk_div += i2s->variant->bclk_offset;
331 mclk_div += i2s->variant->mclk_offset;
332
333 regmap_write(i2s->regmap, SUN4I_I2S_CLK_DIV_REG, 350 regmap_write(i2s->regmap, SUN4I_I2S_CLK_DIV_REG,
334 SUN4I_I2S_CLK_DIV_BCLK(bclk_div) | 351 SUN4I_I2S_CLK_DIV_BCLK(bclk_div) |
335 SUN4I_I2S_CLK_DIV_MCLK(mclk_div)); 352 SUN4I_I2S_CLK_DIV_MCLK(mclk_div));
@@ -969,6 +986,10 @@ static const struct sun4i_i2s_quirks sun4i_a10_i2s_quirks = {
969 .field_fmt_sr = REG_FIELD(SUN4I_I2S_FMT0_REG, 4, 5), 986 .field_fmt_sr = REG_FIELD(SUN4I_I2S_FMT0_REG, 4, 5),
970 .field_fmt_bclk = REG_FIELD(SUN4I_I2S_FMT0_REG, 6, 6), 987 .field_fmt_bclk = REG_FIELD(SUN4I_I2S_FMT0_REG, 6, 6),
971 .field_fmt_lrclk = REG_FIELD(SUN4I_I2S_FMT0_REG, 7, 7), 988 .field_fmt_lrclk = REG_FIELD(SUN4I_I2S_FMT0_REG, 7, 7),
989 .bclk_dividers = sun4i_i2s_bclk_div,
990 .num_bclk_dividers = ARRAY_SIZE(sun4i_i2s_bclk_div),
991 .mclk_dividers = sun4i_i2s_mclk_div,
992 .num_mclk_dividers = ARRAY_SIZE(sun4i_i2s_mclk_div),
972 .get_bclk_parent_rate = sun4i_i2s_get_bclk_parent_rate, 993 .get_bclk_parent_rate = sun4i_i2s_get_bclk_parent_rate,
973 .get_sr = sun4i_i2s_get_sr, 994 .get_sr = sun4i_i2s_get_sr,
974 .get_wss = sun4i_i2s_get_wss, 995 .get_wss = sun4i_i2s_get_wss,
@@ -985,6 +1006,10 @@ static const struct sun4i_i2s_quirks sun6i_a31_i2s_quirks = {
985 .field_fmt_sr = REG_FIELD(SUN4I_I2S_FMT0_REG, 4, 5), 1006 .field_fmt_sr = REG_FIELD(SUN4I_I2S_FMT0_REG, 4, 5),
986 .field_fmt_bclk = REG_FIELD(SUN4I_I2S_FMT0_REG, 6, 6), 1007 .field_fmt_bclk = REG_FIELD(SUN4I_I2S_FMT0_REG, 6, 6),
987 .field_fmt_lrclk = REG_FIELD(SUN4I_I2S_FMT0_REG, 7, 7), 1008 .field_fmt_lrclk = REG_FIELD(SUN4I_I2S_FMT0_REG, 7, 7),
1009 .bclk_dividers = sun4i_i2s_bclk_div,
1010 .num_bclk_dividers = ARRAY_SIZE(sun4i_i2s_bclk_div),
1011 .mclk_dividers = sun4i_i2s_mclk_div,
1012 .num_mclk_dividers = ARRAY_SIZE(sun4i_i2s_mclk_div),
988 .get_bclk_parent_rate = sun4i_i2s_get_bclk_parent_rate, 1013 .get_bclk_parent_rate = sun4i_i2s_get_bclk_parent_rate,
989 .get_sr = sun4i_i2s_get_sr, 1014 .get_sr = sun4i_i2s_get_sr,
990 .get_wss = sun4i_i2s_get_wss, 1015 .get_wss = sun4i_i2s_get_wss,
@@ -1001,6 +1026,10 @@ static const struct sun4i_i2s_quirks sun8i_a83t_i2s_quirks = {
1001 .field_fmt_sr = REG_FIELD(SUN4I_I2S_FMT0_REG, 4, 5), 1026 .field_fmt_sr = REG_FIELD(SUN4I_I2S_FMT0_REG, 4, 5),
1002 .field_fmt_bclk = REG_FIELD(SUN4I_I2S_FMT0_REG, 6, 6), 1027 .field_fmt_bclk = REG_FIELD(SUN4I_I2S_FMT0_REG, 6, 6),
1003 .field_fmt_lrclk = REG_FIELD(SUN4I_I2S_FMT0_REG, 7, 7), 1028 .field_fmt_lrclk = REG_FIELD(SUN4I_I2S_FMT0_REG, 7, 7),
1029 .bclk_dividers = sun8i_i2s_clk_div,
1030 .num_bclk_dividers = ARRAY_SIZE(sun8i_i2s_clk_div),
1031 .mclk_dividers = sun8i_i2s_clk_div,
1032 .num_mclk_dividers = ARRAY_SIZE(sun8i_i2s_clk_div),
1004 .get_bclk_parent_rate = sun8i_i2s_get_bclk_parent_rate, 1033 .get_bclk_parent_rate = sun8i_i2s_get_bclk_parent_rate,
1005 .get_sr = sun8i_i2s_get_sr_wss, 1034 .get_sr = sun8i_i2s_get_sr_wss,
1006 .get_wss = sun8i_i2s_get_sr_wss, 1035 .get_wss = sun8i_i2s_get_sr_wss,
@@ -1012,14 +1041,16 @@ static const struct sun4i_i2s_quirks sun8i_h3_i2s_quirks = {
1012 .has_reset = true, 1041 .has_reset = true,
1013 .reg_offset_txdata = SUN8I_I2S_FIFO_TX_REG, 1042 .reg_offset_txdata = SUN8I_I2S_FIFO_TX_REG,
1014 .sun4i_i2s_regmap = &sun8i_i2s_regmap_config, 1043 .sun4i_i2s_regmap = &sun8i_i2s_regmap_config,
1015 .mclk_offset = 1,
1016 .bclk_offset = 2,
1017 .has_fmt_set_lrck_period = true, 1044 .has_fmt_set_lrck_period = true,
1018 .field_clkdiv_mclk_en = REG_FIELD(SUN4I_I2S_CLK_DIV_REG, 8, 8), 1045 .field_clkdiv_mclk_en = REG_FIELD(SUN4I_I2S_CLK_DIV_REG, 8, 8),
1019 .field_fmt_wss = REG_FIELD(SUN4I_I2S_FMT0_REG, 0, 2), 1046 .field_fmt_wss = REG_FIELD(SUN4I_I2S_FMT0_REG, 0, 2),
1020 .field_fmt_sr = REG_FIELD(SUN4I_I2S_FMT0_REG, 4, 6), 1047 .field_fmt_sr = REG_FIELD(SUN4I_I2S_FMT0_REG, 4, 6),
1021 .field_fmt_bclk = REG_FIELD(SUN4I_I2S_FMT0_REG, 7, 7), 1048 .field_fmt_bclk = REG_FIELD(SUN4I_I2S_FMT0_REG, 7, 7),
1022 .field_fmt_lrclk = REG_FIELD(SUN4I_I2S_FMT0_REG, 19, 19), 1049 .field_fmt_lrclk = REG_FIELD(SUN4I_I2S_FMT0_REG, 19, 19),
1050 .bclk_dividers = sun8i_i2s_clk_div,
1051 .num_bclk_dividers = ARRAY_SIZE(sun8i_i2s_clk_div),
1052 .mclk_dividers = sun8i_i2s_clk_div,
1053 .num_mclk_dividers = ARRAY_SIZE(sun8i_i2s_clk_div),
1023 .get_bclk_parent_rate = sun8i_i2s_get_bclk_parent_rate, 1054 .get_bclk_parent_rate = sun8i_i2s_get_bclk_parent_rate,
1024 .get_sr = sun8i_i2s_get_sr_wss, 1055 .get_sr = sun8i_i2s_get_sr_wss,
1025 .get_wss = sun8i_i2s_get_sr_wss, 1056 .get_wss = sun8i_i2s_get_sr_wss,
@@ -1036,6 +1067,10 @@ static const struct sun4i_i2s_quirks sun50i_a64_codec_i2s_quirks = {
1036 .field_fmt_sr = REG_FIELD(SUN4I_I2S_FMT0_REG, 4, 5), 1067 .field_fmt_sr = REG_FIELD(SUN4I_I2S_FMT0_REG, 4, 5),
1037 .field_fmt_bclk = REG_FIELD(SUN4I_I2S_FMT0_REG, 6, 6), 1068 .field_fmt_bclk = REG_FIELD(SUN4I_I2S_FMT0_REG, 6, 6),
1038 .field_fmt_lrclk = REG_FIELD(SUN4I_I2S_FMT0_REG, 7, 7), 1069 .field_fmt_lrclk = REG_FIELD(SUN4I_I2S_FMT0_REG, 7, 7),
1070 .bclk_dividers = sun4i_i2s_bclk_div,
1071 .num_bclk_dividers = ARRAY_SIZE(sun4i_i2s_bclk_div),
1072 .mclk_dividers = sun4i_i2s_mclk_div,
1073 .num_mclk_dividers = ARRAY_SIZE(sun4i_i2s_mclk_div),
1039 .get_bclk_parent_rate = sun4i_i2s_get_bclk_parent_rate, 1074 .get_bclk_parent_rate = sun4i_i2s_get_bclk_parent_rate,
1040 .get_sr = sun4i_i2s_get_sr, 1075 .get_sr = sun4i_i2s_get_sr,
1041 .get_wss = sun4i_i2s_get_wss, 1076 .get_wss = sun4i_i2s_get_wss,