* [PATCH v1] ASoC: tas2783-sdw: Add basic BPT support
@ 2026-11-03 13:38 Baojun Xu
2026-11-03 17:07 ` Pierre-Louis Bossart
2026-11-03 17:39 ` Andy Shevchenko
0 siblings, 2 replies; 3+ messages in thread
From: Baojun Xu @ 2026-11-03 13:38 UTC (permalink / raw)
To: broonie
Cc: tiwai, andriy.shevchenko, 13916275206, alsa-devel, shenghao-ding,
baojun.xu, linux-sound, linux-kernel, k-yi, henry.lo, robinchen,
niranjan.hy, pin-hao.huang, Syed.SabaKareem
Add for BPT supported.
Signed-off-by: Baojun Xu <baojun.xu@ti.com>
---
sound/soc/codecs/tas2783-sdw.c | 188 ++++++++++++++++++++++++++++++---
1 file changed, 176 insertions(+), 12 deletions(-)
diff --git a/sound/soc/codecs/tas2783-sdw.c b/sound/soc/codecs/tas2783-sdw.c
index a57230e01f75..c1c74707b633 100644
--- a/sound/soc/codecs/tas2783-sdw.c
+++ b/sound/soc/codecs/tas2783-sdw.c
@@ -47,6 +47,26 @@
#define TAS2783_CALI_GUID EFI_GUID(0x1f52d2a1, 0xbb3a, 0x457d, 0xbc, \
0x09, 0x43, 0xa3, 0xf4, 0x31, 0x0a, 0x92)
+#define TAS2783_MAX_FW_SECTIONS 256
+
+enum {
+ SDW_MSG_FLAG_READ = 0,
+ SDW_MSG_FLAG_WRITE,
+};
+
+struct sdw_bpt_section {
+ u32 addr;
+ u32 len;
+ u8 *buf;
+};
+
+struct sdw_bpt_msg {
+ struct sdw_bpt_section *sec;
+ int sections;
+ u8 dev_num;
+ u8 flags;
+};
+
static const u32 tas2783_cali_reg[] = {
TAS2783_CAL_R0,
TAS2783_CAL_INVR0,
@@ -738,7 +758,103 @@ static s32 tas_fw_get_next_file(const u8 *data, struct tas_fw_file *file)
file->dest_addr = get_unaligned_le32(&data[16]);
file->fw_data = (u8 *)&data[20];
- return file->length + sizeof(u32) * 5;
+ return file->length + FW_FL_HDR;
+}
+
+static int tas2783_fw_download_bpt(struct tas2783_prv *tas_dev,
+ const struct firmware *fmw)
+{
+ struct sdw_slave *slave = tas_dev->sdw_peripheral;
+ struct sdw_bpt_section *sections = NULL;
+ struct sdw_bus *bus = slave->bus;
+ struct sdw_bpt_msg msg = {0};
+ struct tas_fw_file file;
+ struct tas_fw_hdr hdr;
+ size_t offset = FW_DL_OFFSET;
+ size_t img_sz = fmw->size;
+ const u8 *buf = fmw->data;
+ int num_sections = 0;
+ int i, ret;
+
+ if (img_sz < FW_DL_OFFSET)
+ return -EINVAL;
+
+ tas_fw_read_hdr(buf, &hdr);
+ if (hdr.size != img_sz)
+ return -EINVAL;
+
+ while (offset < (img_sz - FW_FL_HDR)) {
+ size_t adv = (size_t)tas_fw_get_next_file(&buf[offset], &file);
+
+ /*
+ * Reject a section whose payload runs past the image. Compare
+ * file.length (not adv) against the space left after this
+ * section's header, so the check is immune to the size_t wrap
+ * in tas_fw_get_next_file() on 32-bit kernels.
+ */
+ if (file.length > img_sz - offset - FW_FL_HDR) {
+ dev_err(tas_dev->dev,
+ "Firmware section at offset %zu exceeded.\n",
+ offset);
+ return -EINVAL;
+ }
+ if (num_sections >= TAS2783_MAX_FW_SECTIONS) {
+ dev_err(tas_dev->dev,
+ "BPT fw has too many sections (max %d)\n",
+ TAS2783_MAX_FW_SECTIONS);
+ return -EINVAL;
+ }
+ offset += adv;
+ num_sections++;
+ }
+
+ if (num_sections == 0)
+ return -EINVAL;
+
+ sections = kcalloc(num_sections, sizeof(*sections), GFP_KERNEL);
+ if (!sections)
+ return -ENOMEM;
+
+ offset = FW_DL_OFFSET;
+ for (i = 0; i < num_sections && offset < (img_sz - FW_FL_HDR); i++) {
+ size_t adv = (size_t)tas_fw_get_next_file(&buf[offset], &file);
+
+ sections[i].addr = file.dest_addr;
+ sections[i].len = file.length;
+ sections[i].buf = file.fw_data;
+ offset += adv;
+
+ dev_dbg(tas_dev->dev, "BPT section %d: addr=0x%x len=%d",
+ i, file.dest_addr, file.length);
+ }
+
+ msg.sec = sections;
+ msg.sections = num_sections;
+ msg.dev_num = slave->dev_num;
+ msg.flags = SDW_MSG_FLAG_WRITE;
+
+ dev_dbg(tas_dev->dev, "BPT starting: %d sections, dev_num=%d",
+ num_sections, slave->dev_num);
+
+ ret = sdw_bpt_send_sync(bus, slave, &msg);
+
+ if (ret < 0)
+ dev_err(tas_dev->dev, "BPT firmware download failed: %d", ret);
+ else
+ dev_dbg(tas_dev->dev,
+ "BPT firmware download complete: %d sections",
+ num_sections);
+
+ kfree(sections);
+ return ret;
+}
+
+static bool tas2783_bpt_supported(struct tas2783_prv *tas_dev)
+{
+ struct sdw_slave *slave = tas_dev->sdw_peripheral;
+ struct sdw_bus *bus = slave->bus;
+
+ return bus && bus->ops && bus->ops->bpt_send_async;
}
static void tas2783_fw_ready(const struct firmware *fmw, void *context)
@@ -761,7 +877,7 @@ static void tas2783_fw_ready(const struct firmware *fmw, void *context)
if (!fmw || !fmw->data) {
if (!tas_dev->fw_use_fallback) {
tas_dev->fw_use_fallback = true;
- dev_info(tas_dev->dev,
+ dev_dbg(tas_dev->dev,
"Failed to read preferred fw binary: %s, attempting fallback binary load\n",
tas_dev->rca_binaryname);
} else {
@@ -774,6 +890,30 @@ static void tas2783_fw_ready(const struct firmware *fmw, void *context)
goto out;
}
+ /* Try BPT first if supported, fallback to legacy method */
+ dev_dbg(tas_dev->dev, "BPT check: async=%p wait=%p",
+ tas_dev->sdw_peripheral->bus->ops->bpt_send_async,
+ tas_dev->sdw_peripheral->bus->ops->bpt_wait);
+ if (tas2783_bpt_supported(tas_dev)) {
+ ret = tas2783_fw_download_bpt(tas_dev, fmw);
+
+ if (ret == 0) {
+ dev_dbg(tas_dev->dev, "Firmware downloaded via BPT");
+ cur_file = 1;
+ goto calibrate;
+ }
+ /*
+ * -ESHUTDOWN means the manager is clock-stopping the bus for
+ * system suspend and refused the transfer. The legacy fallback
+ * would only thrash the dying bus with command timeouts, so
+ * bail and let the next attach cycle (on resume) reissue it.
+ */
+ if (ret == -ESHUTDOWN)
+ goto out;
+ dev_warn(tas_dev->dev,
+ "BPT failed (%d), falling back to legacy method",
+ ret);
+ }
img_sz = fmw->size;
buf = fmw->data;
offset += tas_fw_read_hdr(buf, hdr);
@@ -834,6 +974,7 @@ static void tas2783_fw_ready(const struct firmware *fmw, void *context)
cur_file++;
}
mutex_unlock(&tas_dev->pde_lock);
+calibrate:
regcache_drop_region(tas_dev->regmap, 0, UINT_MAX);
if (cur_file == 0) {
@@ -841,6 +982,7 @@ static void tas2783_fw_ready(const struct firmware *fmw, void *context)
ret = -EINVAL;
} else {
tas2783_update_calibdata(tas_dev);
+ regmap_write(tas_dev->regmap, TASDEV_REG_SDW(0, 0, 7), 0x22);
ret = regmap_bulk_read(tas_dev->regmap, TAS2783_FW_VERSION,
&val, 4);
tas_dev->fw_version = (val[0] << 24) | (val[1] << 16) |
@@ -1216,25 +1358,32 @@ static s32 tas_fw_load(struct tas2783_prv *tas_dev, struct sdw_slave *slave)
static s32 tas_io_init(struct device *dev, struct sdw_slave *slave)
{
struct tas2783_prv *tas_dev = dev_get_drvdata(dev);
+ uint val;
s32 ret;
if (tas_dev->hw_init)
return 0;
- tas_dev->fw_dl_success = false;
-
- ret = regmap_write(tas_dev->regmap, TAS2783_SW_RESET, 0x1);
+ regmap_read(tas_dev->regmap, TASDEV_REG_SDW(0, 0, 7), &val);
+ dev_dbg(dev, "Got B0P0R7 = %02X with %d.", val, ret);
if (ret) {
- dev_err(dev, "sw reset failed, err=%d", ret);
+ dev_err(dev, "Get FW download status failed, err=%d", ret);
return ret;
}
- usleep_range(2000, 2200);
-
- tas_dev->fw_use_fallback = false;
- ret = tas_fw_load(tas_dev, slave);
- if (!ret && tas_dev->fw_use_fallback)
+ if (val != 0x22) {
+ tas_dev->fw_dl_success = false;
+ ret = regmap_write(tas_dev->regmap, TAS2783_SW_RESET, 0x1);
+ if (ret) {
+ dev_err(dev, "sw reset failed, err=%d", ret);
+ return ret;
+ }
+ usleep_range(2000, 2200);
+ regcache_cache_only(tas_dev->regmap, false);
+ tas_dev->fw_use_fallback = false;
ret = tas_fw_load(tas_dev, slave);
-
+ if (!ret && tas_dev->fw_use_fallback)
+ ret = tas_fw_load(tas_dev, slave);
+ }
if (!ret) {
if (tas_dev->sa_func_data)
ret = sdca_regmap_write_init(dev, tas_dev->regmap,
@@ -1382,6 +1531,21 @@ static s32 tas_sdw_probe(struct sdw_slave *peripheral,
if (ret)
return dev_err_probe(dev, ret,
"slave property read failed");
+ /*
+ * BPT firmware download uses DP0 and relies on the SoundWire stream
+ * framework to prepare/deprepare DP0. Ensure DP0 properties exist for
+ * the framework prepare/deprepare sequence.
+ */
+ if (!peripheral->prop.dp0_prop) {
+ peripheral->prop.dp0_prop = devm_kzalloc(dev,
+ sizeof(*peripheral->prop.dp0_prop), GFP_KERNEL);
+ if (!peripheral->prop.dp0_prop)
+ return dev_err_probe(dev, -ENOMEM,
+ "dp0_prop alloc failed");
+
+ peripheral->prop.dp0_prop->simple_ch_prep_sm = true;
+ peripheral->prop.dp0_prop->ch_prep_timeout = 25;
+ }
tas_dev = devm_kzalloc(dev, sizeof(*tas_dev), GFP_KERNEL);
if (!tas_dev)
--
2.25.1
^ permalink raw reply [flat|nested] 3+ messages in thread* Re: [PATCH v1] ASoC: tas2783-sdw: Add basic BPT support
2026-11-03 13:38 [PATCH v1] ASoC: tas2783-sdw: Add basic BPT support Baojun Xu
@ 2026-11-03 17:07 ` Pierre-Louis Bossart
2026-11-03 17:39 ` Andy Shevchenko
1 sibling, 0 replies; 3+ messages in thread
From: Pierre-Louis Bossart @ 2026-11-03 17:07 UTC (permalink / raw)
To: Baojun Xu, broonie
Cc: tiwai, andriy.shevchenko, 13916275206, alsa-devel, shenghao-ding,
linux-sound, linux-kernel, k-yi, henry.lo, robinchen,
niranjan.hy, pin-hao.huang, Syed.SabaKareem, Matthew Schwartz
> +static int tas2783_fw_download_bpt(struct tas2783_prv *tas_dev,
> + const struct firmware *fmw)
> +{
> + struct sdw_slave *slave = tas_dev->sdw_peripheral;
> + struct sdw_bpt_section *sections = NULL;
> + struct sdw_bus *bus = slave->bus;
> + struct sdw_bpt_msg msg = {0};
> + struct tas_fw_file file;
> + struct tas_fw_hdr hdr;
> + size_t offset = FW_DL_OFFSET;
> + size_t img_sz = fmw->size;
> + const u8 *buf = fmw->data;
> + int num_sections = 0;
> + int i, ret;
> +
> + if (img_sz < FW_DL_OFFSET)
> + return -EINVAL;
> +
> + tas_fw_read_hdr(buf, &hdr);
> + if (hdr.size != img_sz)
> + return -EINVAL;
> +
> + while (offset < (img_sz - FW_FL_HDR)) {
> + size_t adv = (size_t)tas_fw_get_next_file(&buf[offset], &file);
> +
> + /*
> + * Reject a section whose payload runs past the image. Compare
> + * file.length (not adv) against the space left after this
> + * section's header, so the check is immune to the size_t wrap
> + * in tas_fw_get_next_file() on 32-bit kernels.
> + */
> + if (file.length > img_sz - offset - FW_FL_HDR) {
> + dev_err(tas_dev->dev,
> + "Firmware section at offset %zu exceeded.\n",
> + offset);
> + return -EINVAL;
> + }
> + if (num_sections >= TAS2783_MAX_FW_SECTIONS) {
> + dev_err(tas_dev->dev,
> + "BPT fw has too many sections (max %d)\n",
> + TAS2783_MAX_FW_SECTIONS);
> + return -EINVAL;
> + }
> + offset += adv;
> + num_sections++;
> + }
> +
> + if (num_sections == 0)
> + return -EINVAL;
> +
> + sections = kcalloc(num_sections, sizeof(*sections), GFP_KERNEL);
> + if (!sections)
> + return -ENOMEM;
> +
> + offset = FW_DL_OFFSET;
> + for (i = 0; i < num_sections && offset < (img_sz - FW_FL_HDR); i++) {
> + size_t adv = (size_t)tas_fw_get_next_file(&buf[offset], &file);
> +
> + sections[i].addr = file.dest_addr;
> + sections[i].len = file.length;
> + sections[i].buf = file.fw_data;
> + offset += adv;
> +
> + dev_dbg(tas_dev->dev, "BPT section %d: addr=0x%x len=%d",
> + i, file.dest_addr, file.length);
> + }
> +
> + msg.sec = sections;
> + msg.sections = num_sections;
> + msg.dev_num = slave->dev_num;
> + msg.flags = SDW_MSG_FLAG_WRITE;
> +
> + dev_dbg(tas_dev->dev, "BPT starting: %d sections, dev_num=%d",
> + num_sections, slave->dev_num);
> +
> + ret = sdw_bpt_send_sync(bus, slave, &msg);
> +
> + if (ret < 0)
> + dev_err(tas_dev->dev, "BPT firmware download failed: %d", ret);
> + else
> + dev_dbg(tas_dev->dev,
> + "BPT firmware download complete: %d sections",
> + num_sections);
> +
> + kfree(sections);
> + return ret;
> +}
Before adding BPT-based optimizations to speed-up firmware download,
it'd be good to address existing problems with firmware download, e.g.
when a firmware contains volume configuration information that bypasses
regmap, see the thread in "ASoC: tas2783-sdw: keep the volume settings
across resets". Using BPT will only make things worse I am afraid,
unless there's a check that this is only used for resources not managed
by regmap?
^ permalink raw reply [flat|nested] 3+ messages in thread* Re: [PATCH v1] ASoC: tas2783-sdw: Add basic BPT support
2026-11-03 13:38 [PATCH v1] ASoC: tas2783-sdw: Add basic BPT support Baojun Xu
2026-11-03 17:07 ` Pierre-Louis Bossart
@ 2026-11-03 17:39 ` Andy Shevchenko
1 sibling, 0 replies; 3+ messages in thread
From: Andy Shevchenko @ 2026-11-03 17:39 UTC (permalink / raw)
To: Baojun Xu
Cc: broonie, tiwai, 13916275206, alsa-devel, shenghao-ding,
linux-sound, linux-kernel, k-yi, henry.lo, robinchen,
niranjan.hy, pin-hao.huang, Syed.SabaKareem
On Tue, Nov 03, 2026 at 09:38:53PM +0800, Baojun Xu wrote:
> Add for BPT supported.
...
> +enum {
> + SDW_MSG_FLAG_READ = 0,
> + SDW_MSG_FLAG_WRITE,
Is it going to HW? If not, drop duplicate assignment. Otherwise assign *all* of
the enum to make code robust against modifications.
> +};
...
> +static int tas2783_fw_download_bpt(struct tas2783_prv *tas_dev,
> + const struct firmware *fmw)
> +{
> + struct sdw_slave *slave = tas_dev->sdw_peripheral;
> + struct sdw_bpt_section *sections = NULL;
> + struct sdw_bus *bus = slave->bus;
> + struct sdw_bpt_msg msg = {0};
'0' is not required.
> + struct tas_fw_file file;
> + struct tas_fw_hdr hdr;
> + size_t offset = FW_DL_OFFSET;
> + size_t img_sz = fmw->size;
> + const u8 *buf = fmw->data;
> + int num_sections = 0;
> + int i, ret;
> +
> + if (img_sz < FW_DL_OFFSET)
> + return -EINVAL;
> +
> + tas_fw_read_hdr(buf, &hdr);
> + if (hdr.size != img_sz)
> + return -EINVAL;
> +
> + while (offset < (img_sz - FW_FL_HDR)) {
> + size_t adv = (size_t)tas_fw_get_next_file(&buf[offset], &file);
> +
> + /*
> + * Reject a section whose payload runs past the image. Compare
> + * file.length (not adv) against the space left after this
> + * section's header, so the check is immune to the size_t wrap
> + * in tas_fw_get_next_file() on 32-bit kernels.
> + */
> + if (file.length > img_sz - offset - FW_FL_HDR) {
> + dev_err(tas_dev->dev,
> + "Firmware section at offset %zu exceeded.\n",
> + offset);
> + return -EINVAL;
> + }
> + if (num_sections >= TAS2783_MAX_FW_SECTIONS) {
> + dev_err(tas_dev->dev,
> + "BPT fw has too many sections (max %d)\n",
> + TAS2783_MAX_FW_SECTIONS);
> + return -EINVAL;
> + }
> + offset += adv;
> + num_sections++;
> + }
> +
> + if (num_sections == 0)
> + return -EINVAL;
> +
> + sections = kcalloc(num_sections, sizeof(*sections), GFP_KERNEL);
__free() ?
> + if (!sections)
> + return -ENOMEM;
> +
> + offset = FW_DL_OFFSET;
> + for (i = 0; i < num_sections && offset < (img_sz - FW_FL_HDR); i++) {
for (int i = 0; i < num_sections && offset < (img_sz - FW_FL_HDR); i++) {
> + size_t adv = (size_t)tas_fw_get_next_file(&buf[offset], &file);
> +
> + sections[i].addr = file.dest_addr;
> + sections[i].len = file.length;
> + sections[i].buf = file.fw_data;
> + offset += adv;
> +
> + dev_dbg(tas_dev->dev, "BPT section %d: addr=0x%x len=%d",
> + i, file.dest_addr, file.length);
> + }
> +
> + msg.sec = sections;
> + msg.sections = num_sections;
> + msg.dev_num = slave->dev_num;
> + msg.flags = SDW_MSG_FLAG_WRITE;
> +
> + dev_dbg(tas_dev->dev, "BPT starting: %d sections, dev_num=%d",
> + num_sections, slave->dev_num);
> +
> + ret = sdw_bpt_send_sync(bus, slave, &msg);
> +
Drop blank line.
> + if (ret < 0)
> + dev_err(tas_dev->dev, "BPT firmware download failed: %d", ret);
> + else
> + dev_dbg(tas_dev->dev,
> + "BPT firmware download complete: %d sections",
> + num_sections);
> +
> + kfree(sections);
> + return ret;
> +}
...
> static s32 tas_io_init(struct device *dev, struct sdw_slave *slave)
> {
> struct tas2783_prv *tas_dev = dev_get_drvdata(dev);
> + uint val;
What is this? Use unsigned int or u32 or other better fit.
> s32 ret;
>
> if (tas_dev->hw_init)
> return 0;
--
With Best Regards,
Andy Shevchenko
^ permalink raw reply [flat|nested] 3+ messages in thread
end of thread, other threads:[~2026-11-03 17:46 UTC | newest]
Thread overview: 3+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2026-11-03 13:38 [PATCH v1] ASoC: tas2783-sdw: Add basic BPT support Baojun Xu
2026-11-03 17:07 ` Pierre-Louis Bossart
2026-11-03 17:39 ` Andy Shevchenko
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox
all inboxes | Powered by JetHome®