Re: [PATCH 4/4] Bluetooth: qca: combine NVM and calibration data for QCC2072

From: Bartosz Golaszewski

Date: Mon Jun 01 2026 - 04:30:01 EST


On Fri, 29 May 2026 20:04:31 +0200, Yepuri Siddu
<yepuri.siddu@xxxxxxxxxxxxxxxx> said:
> QCC2072 requires the NVM and calibration data to be delivered to the
> controller bundled together in an outer TLV of type 4. After loading
> the NVM file, load the calibration file (qca/ornbcscal<ver>.bin) and
> combine both into a single buffer with the outer TLV header before
> passing it to qca_tlv_check_data().
>
> The outer TLV header encodes the combined payload length in the high
> 24 bits and type 4 in the low 8 bits of the type_len field.
>
> If the calibration file is unavailable, fall back to downloading the
> NVM alone.
>
> Signed-off-by: Yepuri Siddu <yepuri.siddu@xxxxxxxxxxxxxxxx>
> ---
> drivers/bluetooth/btqca.c | 47 +++++++++++++++++++++++++++++++++++++++
> 1 file changed, 47 insertions(+)
>
> diff --git a/drivers/bluetooth/btqca.c b/drivers/bluetooth/btqca.c
> index 0ef7546e7c7a..37db1cd9e8cf 100644
> --- a/drivers/bluetooth/btqca.c
> +++ b/drivers/bluetooth/btqca.c
> @@ -612,6 +612,53 @@ static int qca_download_firmware(struct hci_dev *hdev,
> memcpy(data, fw->data, size);
> release_firmware(fw);
>
> + /* For QCC2072, combine the NVM (type 2) with the calibration file
> + * into a single TLV of outer type 4.
> + */
> + if (soc_type == QCA_QCC2072 && config->type == TLV_TYPE_NVM) {
> + const struct firmware *calib_fw = NULL;
> + char calib_name[32];
> + u8 *combined_data = NULL;
> + size_t inner_len, combined_size;
> + struct tlv_type_hdr *outer_hdr;
> + int err;
> +
> + snprintf(calib_name, sizeof(calib_name),
> + "qca/ornbcscal%02x.bin", rom_ver);
> + err = request_firmware(&calib_fw, calib_name, &hdev->dev);
> + if (err) {
> + bt_dev_err(hdev, "QCA Failed to request file: %s (%d)",
> + calib_name, err);
> + goto skip_combination;

How about providing a separate function to handle it and avoiding the
objectively ugly label?

> + }
> +
> + bt_dev_info(hdev, "QCA Downloading %s", calib_name);
> +
> + inner_len = size + calib_fw->size;
> + combined_size = sizeof(*outer_hdr) + inner_len;
> + combined_data = vmalloc(combined_size);
> + if (!combined_data) {
> + bt_dev_warn(hdev,
> + "QCA Failed to allocate memory for file: %s",
> + calib_name);
> + release_firmware(calib_fw);
> + goto skip_combination;
> + }
> +
> + outer_hdr = (struct tlv_type_hdr *)combined_data;
> + /* high 24 bits = payload length, low 8 bits = type */
> + outer_hdr->type_len = cpu_to_le32((inner_len << 8) | 4);
> + memcpy(combined_data + sizeof(*outer_hdr), data, size);
> + memcpy(combined_data + sizeof(*outer_hdr) + size,
> + calib_fw->data, calib_fw->size);
> + release_firmware(calib_fw);
> + vfree(data);
> + data = combined_data;
> + size = combined_size;

Otherwise it looks ok to me.

Bartosz

> +skip_combination:
> + ;
> + }
> +
> ret = qca_tlv_check_data(hdev, config, data, size, soc_type);
> if (ret)
> goto out;
> --
> 2.34.1
>
>