[PATCH v3 5/5] soundwire: amd: honor peripheral BRA block alignment

From: Syed Saba Kareem

Date: Mon Oct 05 2026 - 05:25:12 EST


From: Syed Saba Kareem <Syed.SabaKareem@xxxxxxx>

The MIPI DisCo property "mipi-sdw-bra-mode-block-alignment" (parsed into
sdw_slave_prop.bra_block_alignment) states that the BRA data payload must
be an integer multiple of the advertised value. Some peripherals only
ingest the BRA payload in fixed-size blocks and mis-assemble a trailing
partial block otherwise.

The ACP BRA descriptor programs BytesPerFrame (frame_format[19:11]), which
is the number of data payload bytes the engine packs into each BRA frame.
Derive it as before, then, when the peripheral advertises a non-zero
alignment, round bytes_per_frame down to the nearest multiple of it; the
engine simply uses more frames to cover the same transfer_length. This
mirrors the Cadence/Intel handling in sdw_cdns_bpt_find_buffer_sizes().

To read the property, amd_sdw_calculate_bra_params() now takes the
sdw_slave directly instead of just its device number, and derives
dev_addr from it.

Peripherals that do not advertise the property leave bra_block_alignment
at 0, so the new path is skipped and behavior is unchanged.

Signed-off-by: Syed Saba Kareem <Syed.SabaKareem@xxxxxxx>
---
drivers/soundwire/amd_manager.c | 30 +++++++++++++++++++++++++-----
1 file changed, 25 insertions(+), 5 deletions(-)

diff --git a/drivers/soundwire/amd_manager.c b/drivers/soundwire/amd_manager.c
index 078e13b01ac9..9da60e8681f0 100644
--- a/drivers/soundwire/amd_manager.c
+++ b/drivers/soundwire/amd_manager.c
@@ -901,7 +901,7 @@ static int amd_sdw_port_enable(struct sdw_bus *bus,

static int amd_sdw_calculate_bra_params(struct amd_sdw_manager *amd_manager,
struct amd_bra_params *params,
- u8 peripheral_addr)
+ struct sdw_slave *slave)
{
struct sdw_bus *bus = &amd_manager->bus;
u32 nr = bus->params.row; /* current rows - NOT enlarged */
@@ -912,6 +912,7 @@ static int amd_sdw_calculate_bra_params(struct amd_sdw_manager *amd_manager,
u32 sample_interval;
u32 bits_per_frame;
u32 bpf;
+ u32 block_alignment = slave->prop.bra_block_alignment;

params->hstart = hstart;
params->hstop = hstop;
@@ -943,14 +944,33 @@ static int amd_sdw_calculate_bra_params(struct amd_sdw_manager *amd_manager,
if (bpf > 511)
bpf = 511;

+ /*
+ * If the peripheral advertises the DisCo BRA block-alignment property
+ * (mipi-sdw-bra-mode-block-alignment), the data payload carried in each
+ * BRA frame must be an integer multiple of that value. Round
+ * bytes_per_frame down to the nearest multiple; the ACP BRA engine then
+ * packs an aligned payload per frame and simply uses more frames to
+ * cover the same transfer_length. When the property is absent (0) the
+ * value is left unchanged.
+ */
+ if (block_alignment) {
+ if (bpf < block_alignment) {
+ dev_err(amd_manager->dev,
+ "BPT: bytes_per_frame %u smaller than block alignment %u\n",
+ bpf, block_alignment);
+ return -EINVAL;
+ }
+ bpf -= bpf % block_alignment;
+ }
+
params->sample_interval = sample_interval;
params->bytes_per_frame = bpf;
- params->dev_addr = peripheral_addr;
+ params->dev_addr = (u8)slave->dev_num;

dev_dbg(amd_manager->dev,
"BPT calc_params: NR=%u NC=%u col_width=%u WL=%u SI=%u BPF=%u hstart=%u hstop=%u dev=%u\n",
nr, nc, col_width, params->word_length, sample_interval,
- bpf, hstart, hstop, peripheral_addr);
+ bpf, hstart, hstop, slave->dev_num);
return 0;
}

@@ -1356,7 +1376,7 @@ static int amd_sdw_bra_transfer(struct amd_sdw_manager *amd_manager,
int ret;

ret = amd_sdw_calculate_bra_params(amd_manager, &params,
- (u8)slave->dev_num);
+ slave);
if (ret < 0)
return ret;

@@ -1780,7 +1800,7 @@ static int amd_sdw_bpt_wait(struct sdw_bus *bus,
* to register read/write commands.
*/
ret = amd_sdw_calculate_bra_params(amd_manager, &prep_params,
- (u8)slave->dev_num);
+ slave);
if (ret < 0) {
dev_err(amd_manager->dev,
"BPT: failed to calc params: %d\n", ret);
--
2.43.0