From e1cbead2d390eb7cfc1a24c1cb21a68b99428950 Mon Sep 17 00:00:00 2001 From: Bard Liao Date: Mon, 25 May 2026 22:22:12 +0800 Subject: [PATCH 1/5] soundwire: cadence_master: add BRA_NumBytes[8] support The header[1] indicates the BRA_NumBytes[7:0] and header[0] indicates the BRA_NumBytes[8]. The existing code doesn't handle BRA_NumBytes[8] therefore the maximum BRA number of a frame is limited to 255. Fixes: fe8a9cf75c1e ("soundwire: pass sdw_bpt_section to cdns BPT helpers") Signed-off-by: Bard Liao --- drivers/soundwire/cadence_master.c | 24 ++++++++++++++++++------ 1 file changed, 18 insertions(+), 6 deletions(-) diff --git a/drivers/soundwire/cadence_master.c b/drivers/soundwire/cadence_master.c index fa6e2421050dc0..4747dada61be27 100644 --- a/drivers/soundwire/cadence_master.c +++ b/drivers/soundwire/cadence_master.c @@ -2363,7 +2363,9 @@ int sdw_cdns_prepare_write_dma_buffer(u8 dev_num, struct sdw_bpt_section *sec, i p_data = sec[i].buf; while (section_size >= data_per_frame) { - header[1] = data_per_frame; + header[0] &= ~BIT(0); + header[0] |= (data_per_frame >> 8) & BIT(0); + header[1] = data_per_frame & 0xFF; header[2] = start_register >> 24 & 0xFF; header[3] = start_register >> 16 & 0xFF; header[4] = start_register >> 8 & 0xFF; @@ -2389,7 +2391,9 @@ int sdw_cdns_prepare_write_dma_buffer(u8 dev_num, struct sdw_bpt_section *sec, i } if (section_size) { - header[1] = section_size; + header[0] &= ~BIT(0); + header[0] |= (section_size >> 8) & BIT(0); + header[1] = section_size & 0xFF; header[2] = start_register >> 24 & 0xFF; header[3] = start_register >> 16 & 0xFF; header[4] = start_register >> 8 & 0xFF; @@ -2440,7 +2444,9 @@ int sdw_cdns_prepare_read_dma_buffer(u8 dev_num, struct sdw_bpt_section *sec, in start_register = sec[i].addr; data_size = sec[i].len; while (data_size >= data_per_frame) { - header[1] = data_per_frame; + header[0] &= ~BIT(0); + header[0] |= (data_per_frame >> 8) & BIT(0); + header[1] = data_per_frame & 0xFF; header[2] = start_register >> 24 & 0xFF; header[3] = start_register >> 16 & 0xFF; header[4] = start_register >> 8 & 0xFF; @@ -2464,7 +2470,9 @@ int sdw_cdns_prepare_read_dma_buffer(u8 dev_num, struct sdw_bpt_section *sec, in } if (data_size) { - header[1] = data_size; + header[0] &= ~BIT(0); + header[0] |= (data_size >> 8) & BIT(0); + header[1] = data_size & 0xFF; header[2] = start_register >> 24 & 0xFF; header[3] = start_register >> 16 & 0xFF; header[4] = start_register >> 8 & 0xFF; @@ -2487,7 +2495,9 @@ int sdw_cdns_prepare_read_dma_buffer(u8 dev_num, struct sdw_bpt_section *sec, in /* Add fake frame */ header[0] &= ~GENMASK(7, 6); /* Set inactive flag in BPT/BRA frame heade */ while (fake_size >= data_per_frame) { - header[1] = data_per_frame; + header[0] &= ~BIT(0); + header[0] |= (data_per_frame >> 8) & BIT(0); + header[1] = data_per_frame & 0xFF; ret = sdw_cdns_prepare_read_pd0_buffer(header, SDW_CDNS_BRA_HDR, p_dma_buffer, dma_buffer_size, &dma_data_written, counter); @@ -2503,7 +2513,9 @@ int sdw_cdns_prepare_read_dma_buffer(u8 dev_num, struct sdw_bpt_section *sec, in } if (fake_size) { - header[1] = fake_size; + header[0] &= ~BIT(0); + header[0] |= (fake_size >> 8) & BIT(0); + header[1] = fake_size & 0xFF; ret = sdw_cdns_prepare_read_pd0_buffer(header, SDW_CDNS_BRA_HDR, p_dma_buffer, dma_buffer_size, &dma_data_written, counter); From cb6b645ed469b6d0b6b0fd3e711f9e139af72928 Mon Sep 17 00:00:00 2001 From: Richard Fitzgerald Date: Tue, 24 Mar 2026 16:20:04 +0000 Subject: [PATCH 2/5] soundwire: Add bra_block_alignment property support Add a property to struct sdw_slave_prop equivalent to the Disco property "mipi-sdw-bra-mode-block-alignment". The SoundWire Disco specification defines this as: "The data payload size for this BRA Mode shall be an integer multiple of the value of this Property." Change-Id: I27ab84b0ed0f236a5eae58600400a4c386132480 Signed-off-by: Richard Fitzgerald Co-developed-by: Bard Liao Signed-off-by: Bard Liao --- drivers/soundwire/mipi_disco.c | 3 +++ include/linux/soundwire/sdw.h | 3 +++ 2 files changed, 6 insertions(+) diff --git a/drivers/soundwire/mipi_disco.c b/drivers/soundwire/mipi_disco.c index c69b78cd0b6209..b122bd1e7321ba 100644 --- a/drivers/soundwire/mipi_disco.c +++ b/drivers/soundwire/mipi_disco.c @@ -471,6 +471,9 @@ int sdw_slave_read_prop(struct sdw_slave *slave) device_property_read_u32(dev, "mipi-sdw-sdca-interrupt-register-list", &prop->sdca_interrupt_register_list); + device_property_read_u32(dev, "mipi-sdw-bra-mode-block-alignment", + &prop->bra_block_alignment); + prop->commit_register_supported = mipi_device_property_read_bool(dev, "mipi-sdw-commit-register-supported"); diff --git a/include/linux/soundwire/sdw.h b/include/linux/soundwire/sdw.h index 0845182f75f9ab..edf8c41e48c88b 100644 --- a/include/linux/soundwire/sdw.h +++ b/include/linux/soundwire/sdw.h @@ -364,6 +364,8 @@ struct sdw_dpn_prop { * @commit_register_supported: is PCP_Commit register supported * @scp_int1_mask: SCP_INT1_MASK desired settings * @lane_maps: Lane mapping for the slave, only valid if lane_control_support is set + * @bra_block_alignment: If non-zero the length of data in a BRA frame must be + * a multiple of this number of bytes. * @clock_reg_supported: the Peripheral implements the clock base and scale * registers introduced with the SoundWire 1.2 specification. SDCA devices * do not need to set this boolean property as the registers are required. @@ -394,6 +396,7 @@ struct sdw_slave_prop { u8 commit_register_supported; u8 scp_int1_mask; u8 lane_maps[SDW_MAX_LANES]; + u32 bra_block_alignment; bool clock_reg_supported; bool use_domain_irq; }; From a8f7674dc3edc47fe7e86e85dab2cdf371483d7b Mon Sep 17 00:00:00 2001 From: Bard Liao Date: Wed, 25 Mar 2026 09:54:54 +0800 Subject: [PATCH 3/5] soundwire: intel: handle Peripheral bra_block_alignment The data pre frame size should be a multiple of bra_block_alignment. Signed-off-by: Bard Liao --- drivers/soundwire/cadence_master.c | 11 +++++++++++ drivers/soundwire/cadence_master.h | 1 + drivers/soundwire/intel_ace2x.c | 2 ++ 3 files changed, 14 insertions(+) diff --git a/drivers/soundwire/cadence_master.c b/drivers/soundwire/cadence_master.c index 4747dada61be27..6a0510d84d06a4 100644 --- a/drivers/soundwire/cadence_master.c +++ b/drivers/soundwire/cadence_master.c @@ -2139,6 +2139,7 @@ EXPORT_SYMBOL(sdw_cdns_bpt_find_bandwidth); int sdw_cdns_bpt_find_buffer_sizes(int command, /* 0: write, 1: read */ int row, int col, unsigned int data_bytes, unsigned int requested_bytes_per_frame, + unsigned int bra_block_alignment, unsigned int *data_per_frame, unsigned int *pdi0_buffer_size, unsigned int *pdi1_buffer_size, unsigned int *num_frames) { @@ -2163,6 +2164,16 @@ int sdw_cdns_bpt_find_buffer_sizes(int command, /* 0: write, 1: read */ if (requested_bytes_per_frame < actual_bpt_bytes) actual_bpt_bytes = requested_bytes_per_frame; + if (bra_block_alignment) { + /* align to a multiple of bra_block_alignment */ + if (actual_bpt_bytes < bra_block_alignment) { + pr_err("effective bytes per frame %u is smaller than block alignment %u\n", + actual_bpt_bytes, bra_block_alignment); + return -EINVAL; + } + actual_bpt_bytes -= (actual_bpt_bytes % bra_block_alignment); + } + *data_per_frame = actual_bpt_bytes; if (data_bytes < actual_bpt_bytes) diff --git a/drivers/soundwire/cadence_master.h b/drivers/soundwire/cadence_master.h index 668f807cff4b23..f4e41a9ab16589 100644 --- a/drivers/soundwire/cadence_master.h +++ b/drivers/soundwire/cadence_master.h @@ -218,6 +218,7 @@ int sdw_cdns_bpt_find_bandwidth(int command, /* 0: write, 1: read */ int sdw_cdns_bpt_find_buffer_sizes(int command, /* 0: write, 1: read */ int row, int col, unsigned int data_bytes, unsigned int requested_bytes_per_frame, + unsigned int bra_block_alignment, unsigned int *data_per_frame, unsigned int *pdi0_buffer_size, unsigned int *pdi1_buffer_size, unsigned int *num_frames); diff --git a/drivers/soundwire/intel_ace2x.c b/drivers/soundwire/intel_ace2x.c index 6cd3a873237501..2c40949d5892e0 100644 --- a/drivers/soundwire/intel_ace2x.c +++ b/drivers/soundwire/intel_ace2x.c @@ -173,6 +173,7 @@ static int intel_ace2x_bpt_open_stream(struct sdw_intel *sdw, struct sdw_slave * ret = sdw_cdns_bpt_find_buffer_sizes(command, cdns->bus.params.row, cdns->bus.params.col, msg->sec[i].len, SDW_BPT_MSG_MAX_BYTES, + slave->prop.bra_block_alignment, &data_per_frame, &pdi0_buffer_size_, &pdi1_buffer_size_, &num_frames_); if (ret < 0) @@ -197,6 +198,7 @@ static int intel_ace2x_bpt_open_stream(struct sdw_intel *sdw, struct sdw_slave * ret = sdw_cdns_bpt_find_buffer_sizes(command, cdns->bus.params.row, cdns->bus.params.col, data_per_frame, SDW_BPT_MSG_MAX_BYTES, + slave->prop.bra_block_alignment, &data_per_frame, &pdi0_buf_size_pre_frame, &pdi1_buf_size_pre_frame, &fake_num_frames); if (ret < 0) From 46ef26e593fdbf5a581371a4a1babd2187a8c1e2 Mon Sep 17 00:00:00 2001 From: Bard Liao Date: Wed, 25 Mar 2026 13:02:49 +0800 Subject: [PATCH 4/5] soundwire: get mipi-sdw-bra-mode-max-data-per-frame property Get the mipi-sdw-bra-mode-max-data-per-frame property which indicates the maximum data payload size (in bytes per frame excluding header, CRC, and footer) for the BRA Mode. Signed-off-by: Bard Liao --- drivers/soundwire/mipi_disco.c | 3 +++ include/linux/soundwire/sdw.h | 3 +++ 2 files changed, 6 insertions(+) diff --git a/drivers/soundwire/mipi_disco.c b/drivers/soundwire/mipi_disco.c index b122bd1e7321ba..cfccaa33f4380b 100644 --- a/drivers/soundwire/mipi_disco.c +++ b/drivers/soundwire/mipi_disco.c @@ -474,6 +474,9 @@ int sdw_slave_read_prop(struct sdw_slave *slave) device_property_read_u32(dev, "mipi-sdw-bra-mode-block-alignment", &prop->bra_block_alignment); + device_property_read_u32(dev, "mipi-sdw-bra-mode-max-data-per-frame", + &prop->bra_max_data_per_frame); + prop->commit_register_supported = mipi_device_property_read_bool(dev, "mipi-sdw-commit-register-supported"); diff --git a/include/linux/soundwire/sdw.h b/include/linux/soundwire/sdw.h index edf8c41e48c88b..0d4d3ddeda17d0 100644 --- a/include/linux/soundwire/sdw.h +++ b/include/linux/soundwire/sdw.h @@ -366,6 +366,8 @@ struct sdw_dpn_prop { * @lane_maps: Lane mapping for the slave, only valid if lane_control_support is set * @bra_block_alignment: If non-zero the length of data in a BRA frame must be * a multiple of this number of bytes. + * @bra_max_data_per_frame: If non-zero the maximum data payload size (in bytes per + * frame excluding header, CRC, and footer) for this BRA Mode * @clock_reg_supported: the Peripheral implements the clock base and scale * registers introduced with the SoundWire 1.2 specification. SDCA devices * do not need to set this boolean property as the registers are required. @@ -397,6 +399,7 @@ struct sdw_slave_prop { u8 scp_int1_mask; u8 lane_maps[SDW_MAX_LANES]; u32 bra_block_alignment; + u32 bra_max_data_per_frame; bool clock_reg_supported; bool use_domain_irq; }; From 2da3fc0c6fced0626664b4ea4285f6ef19b97b4f Mon Sep 17 00:00:00 2001 From: Bard Liao Date: Wed, 25 Mar 2026 13:11:09 +0800 Subject: [PATCH 5/5] soundwire: intel_ace2x: handle the max_data_per_frame property The optional property indicates the maximum data payload size for the BRA mode. Signed-off-by: Bard Liao --- drivers/soundwire/intel_ace2x.c | 18 ++++++++++++++++-- include/linux/soundwire/sdw.h | 8 ++++++++ 2 files changed, 24 insertions(+), 2 deletions(-) diff --git a/drivers/soundwire/intel_ace2x.c b/drivers/soundwire/intel_ace2x.c index 2c40949d5892e0..2f654b620d3a7d 100644 --- a/drivers/soundwire/intel_ace2x.c +++ b/drivers/soundwire/intel_ace2x.c @@ -57,6 +57,7 @@ static int intel_ace2x_bpt_open_stream(struct sdw_intel *sdw, struct sdw_slave * struct sdw_port_config *pconfig; unsigned int pdi0_buf_size_pre_frame; unsigned int pdi1_buf_size_pre_frame; + unsigned int max_data_per_frame; unsigned int pdi0_buffer_size_; unsigned int pdi1_buffer_size_; unsigned int pdi0_buffer_size; @@ -168,11 +169,24 @@ static int intel_ace2x_bpt_open_stream(struct sdw_intel *sdw, struct sdw_slave * pdi0_buffer_size = 0; pdi1_buffer_size = 0; num_frames = 0; + + if (slave->prop.bra_max_data_per_frame) { + max_data_per_frame = slave->prop.bra_max_data_per_frame; + if (max_data_per_frame > SDW_BRA_MAX_BYTES_PER_FRAME) { + dev_warn(&slave->dev, + "BRA max_data_per_frame %u exceeds limit %u, clamping\n", + max_data_per_frame, SDW_BRA_MAX_BYTES_PER_FRAME); + max_data_per_frame = SDW_BRA_MAX_BYTES_PER_FRAME; + } + } else { + max_data_per_frame = SDW_BRA_MAX_BYTES_PER_FRAME; + } + /* Add up pdi buffer size and frame numbers of each BPT sections */ for (i = 0; i < msg->sections; i++) { ret = sdw_cdns_bpt_find_buffer_sizes(command, cdns->bus.params.row, cdns->bus.params.col, - msg->sec[i].len, SDW_BPT_MSG_MAX_BYTES, + msg->sec[i].len, max_data_per_frame, slave->prop.bra_block_alignment, &data_per_frame, &pdi0_buffer_size_, &pdi1_buffer_size_, &num_frames_); @@ -197,7 +211,7 @@ static int intel_ace2x_bpt_open_stream(struct sdw_intel *sdw, struct sdw_slave * /* Get buffer size of a full frame */ ret = sdw_cdns_bpt_find_buffer_sizes(command, cdns->bus.params.row, cdns->bus.params.col, - data_per_frame, SDW_BPT_MSG_MAX_BYTES, + data_per_frame, max_data_per_frame, slave->prop.bra_block_alignment, &data_per_frame, &pdi0_buf_size_pre_frame, &pdi1_buf_size_pre_frame, &fake_num_frames); diff --git a/include/linux/soundwire/sdw.h b/include/linux/soundwire/sdw.h index 0d4d3ddeda17d0..f8067411b8ebb5 100644 --- a/include/linux/soundwire/sdw.h +++ b/include/linux/soundwire/sdw.h @@ -844,6 +844,14 @@ struct sdw_defer { */ #define SDW_BPT_MSG_MAX_BYTES (1024 * 1024) +/* + * According to mipi SoundWire DisCo Specification_v2-1, + * this maximum value shall not exceed 470. + * Note that the largest number of bytes accessible by a single BRA operation is limited to 470 + * bytes when using lane 0, but goes up to 502 bytes when using one of the optional extra lanes. + */ +#define SDW_BRA_MAX_BYTES_PER_FRAME 470 + struct sdw_bpt_msg; /**