Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 2cc86890 authored by Amitkumar Karwar's avatar Amitkumar Karwar Committed by Marcel Holtmann
Browse files

Bluetooth: btmrvl: add calibration data download support



A text file containing calibration data in hex format can
be provided at following path:

/lib/firmware/mrvl/sd8797_caldata.conf

The data will be downloaded to firmware during initialization.

Reviewed-by: default avatarMike Frysinger <vapier@chromium.org>
Signed-off-by: default avatarAmitkumar Karwar <akarwar@marvell.com>
Signed-off-by: default avatarBing Zhao <bzhao@marvell.com>
Signed-off-by: default avatarHyuckjoo Lee <hyuckjoo.lee@samsung.com>
Signed-off-by: default avatarMarcel Holtmann <marcel@holtmann.org>
parent 4b245722
Loading
Loading
Loading
Loading
+8 −0
Original line number Diff line number Diff line
@@ -23,6 +23,8 @@
#include <linux/bitops.h>
#include <linux/slab.h>
#include <net/bluetooth/bluetooth.h>
#include <linux/ctype.h>
#include <linux/firmware.h>

#define BTM_HEADER_LEN			4
#define BTM_UPLD_SIZE			2312
@@ -41,6 +43,8 @@ struct btmrvl_thread {
struct btmrvl_device {
	void *card;
	struct hci_dev *hcidev;
	struct device *dev;
	const char *cal_data;

	u8 dev_type;

@@ -91,6 +95,7 @@ struct btmrvl_private {
#define BT_CMD_HOST_SLEEP_CONFIG	0x59
#define BT_CMD_HOST_SLEEP_ENABLE	0x5A
#define BT_CMD_MODULE_CFG_REQ		0x5B
#define BT_CMD_LOAD_CONFIG_DATA		0x61

/* Sub-commands: Module Bringup/Shutdown Request/Response */
#define MODULE_BRINGUP_REQ		0xF1
@@ -116,6 +121,9 @@ struct btmrvl_private {
#define PS_SLEEP			0x01
#define PS_AWAKE			0x00

#define BT_CMD_DATA_SIZE		32
#define BT_CAL_DATA_SIZE		28

struct btmrvl_event {
	u8 ec;		/* event counter */
	u8 length;
+116 −0
Original line number Diff line number Diff line
@@ -432,12 +432,128 @@ static int btmrvl_open(struct hci_dev *hdev)
	return 0;
}

/*
 * This function parses provided calibration data input. It should contain
 * hex bytes separated by space or new line character. Here is an example.
 * 00 1C 01 37 FF FF FF FF 02 04 7F 01
 * CE BA 00 00 00 2D C6 C0 00 00 00 00
 * 00 F0 00 00
 */
static int btmrvl_parse_cal_cfg(const u8 *src, u32 len, u8 *dst, u32 dst_size)
{
	const u8 *s = src;
	u8 *d = dst;
	int ret;
	u8 tmp[3];

	tmp[2] = '\0';
	while ((s - src) <= len - 2) {
		if (isspace(*s)) {
			s++;
			continue;
		}

		if (isxdigit(*s)) {
			if ((d - dst) >= dst_size) {
				BT_ERR("calibration data file too big!!!");
				return -EINVAL;
			}

			memcpy(tmp, s, 2);

			ret = kstrtou8(tmp, 16, d++);
			if (ret < 0)
				return ret;

			s += 2;
		} else {
			return -EINVAL;
		}
	}
	if (d == dst)
		return -EINVAL;

	return 0;
}

static int btmrvl_load_cal_data(struct btmrvl_private *priv,
				u8 *config_data)
{
	int i, ret;
	u8 data[BT_CMD_DATA_SIZE];

	data[0] = 0x00;
	data[1] = 0x00;
	data[2] = 0x00;
	data[3] = BT_CMD_DATA_SIZE - 4;

	/* Swap cal-data bytes. Each four bytes are swapped. Considering 4
	 * byte SDIO header offset, mapping of input and output bytes will be
	 * {3, 2, 1, 0} -> {0+4, 1+4, 2+4, 3+4},
	 * {7, 6, 5, 4} -> {4+4, 5+4, 6+4, 7+4} */
	for (i = 4; i < BT_CMD_DATA_SIZE; i++)
		data[i] = config_data[(i / 4) * 8 - 1 - i];

	print_hex_dump_bytes("Calibration data: ",
			     DUMP_PREFIX_OFFSET, data, BT_CMD_DATA_SIZE);

	ret = btmrvl_send_sync_cmd(priv, BT_CMD_LOAD_CONFIG_DATA, data,
				   BT_CMD_DATA_SIZE);
	if (ret)
		BT_ERR("Failed to download caibration data\n");

	return 0;
}

static int
btmrvl_process_cal_cfg(struct btmrvl_private *priv, u8 *data, u32 size)
{
	u8 cal_data[BT_CAL_DATA_SIZE];
	int ret;

	ret = btmrvl_parse_cal_cfg(data, size, cal_data, sizeof(cal_data));
	if (ret)
		return ret;

	ret = btmrvl_load_cal_data(priv, cal_data);
	if (ret) {
		BT_ERR("Fail to load calibrate data");
		return ret;
	}

	return 0;
}

static int btmrvl_cal_data_config(struct btmrvl_private *priv)
{
	const struct firmware *cfg;
	int ret;
	const char *cal_data = priv->btmrvl_dev.cal_data;

	if (!cal_data)
		return 0;

	ret = request_firmware(&cfg, cal_data, priv->btmrvl_dev.dev);
	if (ret < 0) {
		BT_DBG("Failed to get %s file, skipping cal data download",
		       cal_data);
		return 0;
	}

	ret = btmrvl_process_cal_cfg(priv, (u8 *)cfg->data, cfg->size);
	release_firmware(cfg);
	return ret;
}

static int btmrvl_setup(struct hci_dev *hdev)
{
	struct btmrvl_private *priv = hci_get_drvdata(hdev);

	btmrvl_send_module_cfg_cmd(priv, MODULE_BRINGUP_REQ);

	if (btmrvl_cal_data_config(priv))
		BT_ERR("Set cal data failed");

	priv->btmrvl_dev.psmode = 1;
	btmrvl_enable_ps(priv);

+8 −1
Original line number Diff line number Diff line
@@ -18,7 +18,6 @@
 * this warranty disclaimer.
 **/

#include <linux/firmware.h>
#include <linux/slab.h>

#include <linux/mmc/sdio_ids.h>
@@ -102,6 +101,7 @@ static const struct btmrvl_sdio_card_reg btmrvl_reg_88xx = {
static const struct btmrvl_sdio_device btmrvl_sdio_sd8688 = {
	.helper		= "mrvl/sd8688_helper.bin",
	.firmware	= "mrvl/sd8688.bin",
	.cal_data	= NULL,
	.reg		= &btmrvl_reg_8688,
	.sd_blksz_fw_dl	= 64,
};
@@ -109,6 +109,7 @@ static const struct btmrvl_sdio_device btmrvl_sdio_sd8688 = {
static const struct btmrvl_sdio_device btmrvl_sdio_sd8787 = {
	.helper		= NULL,
	.firmware	= "mrvl/sd8787_uapsta.bin",
	.cal_data	= NULL,
	.reg		= &btmrvl_reg_87xx,
	.sd_blksz_fw_dl	= 256,
};
@@ -116,6 +117,7 @@ static const struct btmrvl_sdio_device btmrvl_sdio_sd8787 = {
static const struct btmrvl_sdio_device btmrvl_sdio_sd8797 = {
	.helper		= NULL,
	.firmware	= "mrvl/sd8797_uapsta.bin",
	.cal_data	= "mrvl/sd8797_caldata.conf",
	.reg		= &btmrvl_reg_87xx,
	.sd_blksz_fw_dl	= 256,
};
@@ -123,6 +125,7 @@ static const struct btmrvl_sdio_device btmrvl_sdio_sd8797 = {
static const struct btmrvl_sdio_device btmrvl_sdio_sd8897 = {
	.helper		= NULL,
	.firmware	= "mrvl/sd8897_uapsta.bin",
	.cal_data	= NULL,
	.reg		= &btmrvl_reg_88xx,
	.sd_blksz_fw_dl	= 256,
};
@@ -1006,6 +1009,7 @@ static int btmrvl_sdio_probe(struct sdio_func *func,
		struct btmrvl_sdio_device *data = (void *) id->driver_data;
		card->helper = data->helper;
		card->firmware = data->firmware;
		card->cal_data = data->cal_data;
		card->reg = data->reg;
		card->sd_blksz_fw_dl = data->sd_blksz_fw_dl;
	}
@@ -1034,6 +1038,8 @@ static int btmrvl_sdio_probe(struct sdio_func *func,
	}

	card->priv = priv;
	priv->btmrvl_dev.dev = &card->func->dev;
	priv->btmrvl_dev.cal_data = card->cal_data;

	/* Initialize the interface specific function pointers */
	priv->hw_host_to_card = btmrvl_sdio_host_to_card;
@@ -1216,4 +1222,5 @@ MODULE_FIRMWARE("mrvl/sd8688_helper.bin");
MODULE_FIRMWARE("mrvl/sd8688.bin");
MODULE_FIRMWARE("mrvl/sd8787_uapsta.bin");
MODULE_FIRMWARE("mrvl/sd8797_uapsta.bin");
MODULE_FIRMWARE("mrvl/sd8797_caldata.conf");
MODULE_FIRMWARE("mrvl/sd8897_uapsta.bin");
+2 −0
Original line number Diff line number Diff line
@@ -85,6 +85,7 @@ struct btmrvl_sdio_card {
	u32 ioport;
	const char *helper;
	const char *firmware;
	const char *cal_data;
	const struct btmrvl_sdio_card_reg *reg;
	u16 sd_blksz_fw_dl;
	u8 rx_unit;
@@ -94,6 +95,7 @@ struct btmrvl_sdio_card {
struct btmrvl_sdio_device {
	const char *helper;
	const char *firmware;
	const char *cal_data;
	const struct btmrvl_sdio_card_reg *reg;
	u16 sd_blksz_fw_dl;
};