Skip to content

Commit c1f848f

Browse files
jbrun3tbroonie
authored andcommitted
ASoC: meson: axg-tdm-formatter: fix channel slot allocation
When the tdm lane mask is computed, the driver currently fills the 1st lane before moving on to the next. If the stream has less channels than the lanes can accommodate, slots will be disabled on the last lanes. Unfortunately, the HW distribute channels in a different way. It distribute channels in pair on each lanes before moving on the next slots. This difference leads to problems if a device has an interface with more than 1 lane and with more than 2 slots per lane. For example: a playback interface with 2 lanes and 4 slots each (total 8 slots - zero based numbering) - Playing a 8ch stream: - All slots activated by the driver - channel #2 will be played on lane #1 - slot #0 following HW placement - Playing a 4ch stream: - Lane #1 disabled by the driver - channel #2 will be played on lane #0 - slot #2 This behaviour is obviously not desirable. Change the way slots are activated on the TDM lanes to follow what the HW does and make sure each channel always get mapped to the same slot/lane. Fixes: 1a11d88 ("ASoC: meson: add tdm formatter base driver") Signed-off-by: Jerome Brunet <jbrunet@baylibre.com> Link: https://lore.kernel.org/r/20230809171931.1244502-1-jbrunet@baylibre.com Signed-off-by: Mark Brown <broonie@kernel.org>
1 parent e6475ce commit c1f848f

File tree

1 file changed

+26
-16
lines changed

1 file changed

+26
-16
lines changed

sound/soc/meson/axg-tdm-formatter.c

Lines changed: 26 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -30,27 +30,32 @@ int axg_tdm_formatter_set_channel_masks(struct regmap *map,
3030
struct axg_tdm_stream *ts,
3131
unsigned int offset)
3232
{
33-
unsigned int val, ch = ts->channels;
34-
unsigned long mask;
35-
int i, j;
33+
unsigned int ch = ts->channels;
34+
u32 val[AXG_TDM_NUM_LANES];
35+
int i, j, k;
36+
37+
/*
38+
* We need to mimick the slot distribution used by the HW to keep the
39+
* channel placement consistent regardless of the number of channel
40+
* in the stream. This is why the odd algorithm below is used.
41+
*/
42+
memset(val, 0, sizeof(*val) * AXG_TDM_NUM_LANES);
3643

3744
/*
3845
* Distribute the channels of the stream over the available slots
39-
* of each TDM lane
46+
* of each TDM lane. We need to go over the 32 slots ...
4047
*/
41-
for (i = 0; i < AXG_TDM_NUM_LANES; i++) {
42-
val = 0;
43-
mask = ts->mask[i];
44-
45-
for (j = find_first_bit(&mask, 32);
46-
(j < 32) && ch;
47-
j = find_next_bit(&mask, 32, j + 1)) {
48-
val |= 1 << j;
49-
ch -= 1;
48+
for (i = 0; (i < 32) && ch; i += 2) {
49+
/* ... of all the lanes ... */
50+
for (j = 0; j < AXG_TDM_NUM_LANES; j++) {
51+
/* ... then distribute the channels in pairs */
52+
for (k = 0; k < 2; k++) {
53+
if ((BIT(i + k) & ts->mask[j]) && ch) {
54+
val[j] |= BIT(i + k);
55+
ch -= 1;
56+
}
57+
}
5058
}
51-
52-
regmap_write(map, offset, val);
53-
offset += regmap_get_reg_stride(map);
5459
}
5560

5661
/*
@@ -63,6 +68,11 @@ int axg_tdm_formatter_set_channel_masks(struct regmap *map,
6368
return -EINVAL;
6469
}
6570

71+
for (i = 0; i < AXG_TDM_NUM_LANES; i++) {
72+
regmap_write(map, offset, val[i]);
73+
offset += regmap_get_reg_stride(map);
74+
}
75+
6676
return 0;
6777
}
6878
EXPORT_SYMBOL_GPL(axg_tdm_formatter_set_channel_masks);

0 commit comments

Comments
 (0)