Unverified Commit 323aa1f0 authored by Ranjani Sridharan's avatar Ranjani Sridharan Committed by Mark Brown
Browse files

ASoC: SOF: Add a new IPC op for parsing topology manifest

parent 4453d24d
Loading
Loading
Loading
Loading
+48 −0
Original line number Diff line number Diff line
@@ -17,6 +17,9 @@
/* Full volume for default values */
#define VOL_ZERO_DB	BIT(VOLUME_FWL)

/* size of tplg ABI in bytes */
#define SOF_IPC3_TPLG_ABI_SIZE 3

struct sof_widget_data {
	int ctrl_type;
	int ipc_cmd;
@@ -2303,6 +2306,50 @@ static int sof_ipc3_dai_get_clk(struct snd_sof_dev *sdev, struct snd_sof_dai *da
	return -EINVAL;
}

static int sof_ipc3_parse_manifest(struct snd_soc_component *scomp, int index,
				   struct snd_soc_tplg_manifest *man)
{
	u32 size = le32_to_cpu(man->priv.size);
	u32 abi_version;

	/* backward compatible with tplg without ABI info */
	if (!size) {
		dev_dbg(scomp->dev, "No topology ABI info\n");
		return 0;
	}

	if (size != SOF_IPC3_TPLG_ABI_SIZE) {
		dev_err(scomp->dev, "%s: Invalid topology ABI size: %u\n",
			__func__, size);
		return -EINVAL;
	}

	dev_info(scomp->dev,
		 "Topology: ABI %d:%d:%d Kernel ABI %hhu:%hhu:%hhu\n",
		 man->priv.data[0], man->priv.data[1], man->priv.data[2],
		 SOF_ABI_MAJOR, SOF_ABI_MINOR, SOF_ABI_PATCH);

	abi_version = SOF_ABI_VER(man->priv.data[0], man->priv.data[1], man->priv.data[2]);

	if (SOF_ABI_VERSION_INCOMPATIBLE(SOF_ABI_VERSION, abi_version)) {
		dev_err(scomp->dev, "%s: Incompatible topology ABI version\n", __func__);
		return -EINVAL;
	}

	if (SOF_ABI_VERSION_MINOR(abi_version) > SOF_ABI_MINOR) {
		if (!IS_ENABLED(CONFIG_SND_SOC_SOF_STRICT_ABI_CHECKS)) {
			dev_warn(scomp->dev, "%s: Topology ABI is more recent than kernel\n",
				 __func__);
		} else {
			dev_err(scomp->dev, "%s: Topology ABI is more recent than kernel\n",
				__func__);
			return -EINVAL;
		}
	}

	return 0;
}

/* token list for each topology object */
static enum sof_tokens host_token_list[] = {
	SOF_CORE_TOKENS,
@@ -2413,4 +2460,5 @@ const struct sof_ipc_tplg_ops ipc3_tplg_ops = {
	.dai_get_clk = sof_ipc3_dai_get_clk,
	.set_up_all_pipelines = sof_ipc3_set_up_all_pipelines,
	.tear_down_all_pipelines = sof_ipc3_tear_down_all_pipelines,
	.parse_manifest = sof_ipc3_parse_manifest,
};
+63 −0
Original line number Diff line number Diff line
@@ -16,6 +16,7 @@
#include "ops.h"

#define SOF_IPC4_GAIN_PARAM_ID  0
#define SOF_IPC4_TPLG_ABI_SIZE 6

static const struct sof_topology_token ipc4_sched_tokens[] = {
	{SOF_TKN_SCHED_LP_MODE, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32,
@@ -1317,6 +1318,67 @@ static int sof_ipc4_dai_config(struct snd_sof_dev *sdev, struct snd_sof_widget *
	return 0;
}

static int sof_ipc4_parse_manifest(struct snd_soc_component *scomp, int index,
				   struct snd_soc_tplg_manifest *man)
{
	struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp);
	struct sof_ipc4_fw_data *ipc4_data = sdev->private;
	struct sof_manifest_tlv *manifest_tlv;
	struct sof_manifest *manifest;
	u32 size = le32_to_cpu(man->priv.size);
	u8 *man_ptr = man->priv.data;
	u32 len_check;
	int i;

	if (!size || size < SOF_IPC4_TPLG_ABI_SIZE) {
		dev_err(scomp->dev, "%s: Invalid topology ABI size: %u\n",
			__func__, size);
		return -EINVAL;
	}

	manifest = (struct sof_manifest *)man_ptr;

	dev_info(scomp->dev,
		 "Topology: ABI %d:%d:%d Kernel ABI %u:%u:%u\n",
		  le16_to_cpu(manifest->abi_major), le16_to_cpu(manifest->abi_minor),
		  le16_to_cpu(manifest->abi_patch),
		  SOF_ABI_MAJOR, SOF_ABI_MINOR, SOF_ABI_PATCH);

	/* TODO: Add ABI compatibility check */

	/* no more data after the ABI version */
	if (size <= SOF_IPC4_TPLG_ABI_SIZE)
		return 0;

	manifest_tlv = manifest->items;
	len_check = sizeof(struct sof_manifest);
	for (i = 0; i < le16_to_cpu(manifest->count); i++) {
		len_check += sizeof(struct sof_manifest_tlv) + le32_to_cpu(manifest_tlv->size);
		if (len_check > size)
			return -EINVAL;

		switch (le32_to_cpu(manifest_tlv->type)) {
		case SOF_MANIFEST_DATA_TYPE_NHLT:
			/* no NHLT in BIOS, so use the one from topology manifest */
			if (ipc4_data->nhlt)
				break;
			ipc4_data->nhlt = devm_kmemdup(sdev->dev, manifest_tlv->data,
						       le32_to_cpu(manifest_tlv->size), GFP_KERNEL);
			if (!ipc4_data->nhlt)
				return -ENOMEM;
			break;
		default:
			dev_warn(scomp->dev, "Skipping unknown manifest data type %d\n",
				 manifest_tlv->type);
			break;
		}
		man_ptr += sizeof(struct sof_manifest_tlv) + le32_to_cpu(manifest_tlv->size);
		manifest_tlv = (struct sof_manifest_tlv *)man_ptr;
	}

	return 0;
}

static enum sof_tokens host_token_list[] = {
	SOF_COMP_TOKENS,
	SOF_AUDIO_FMT_NUM_TOKENS,
@@ -1402,4 +1464,5 @@ const struct sof_ipc_tplg_ops ipc4_tplg_ops = {
	.route_setup = sof_ipc4_route_setup,
	.route_free = sof_ipc4_route_free,
	.dai_config = sof_ipc4_dai_config,
	.parse_manifest = sof_ipc4_parse_manifest,
};
+3 −0
Original line number Diff line number Diff line
@@ -168,6 +168,7 @@ struct sof_ipc_tplg_widget_ops {
 * @dai_get_clk: Function pointer for getting the DAI clock setting
 * @set_up_all_pipelines: Function pointer for setting up all topology pipelines
 * @tear_down_all_pipelines: Function pointer for tearing down all topology pipelines
 * @parse_manifest: Optional function pointer for ipc4 specific parsing of topology manifest
 */
struct sof_ipc_tplg_ops {
	const struct sof_ipc_tplg_widget_ops *widget;
@@ -185,6 +186,8 @@ struct sof_ipc_tplg_ops {
	int (*dai_get_clk)(struct snd_sof_dev *sdev, struct snd_sof_dai *dai, int clk_type);
	int (*set_up_all_pipelines)(struct snd_sof_dev *sdev, bool verify);
	int (*tear_down_all_pipelines)(struct snd_sof_dev *sdev, bool verify);
	int (*parse_manifest)(struct snd_soc_component *scomp, int index,
			      struct snd_soc_tplg_manifest *man);
};

/** struct snd_sof_tuple - Tuple info
+4 −41
Original line number Diff line number Diff line
@@ -36,9 +36,6 @@
#define TLV_STEP	1
#define TLV_MUTE	2

/* size of tplg abi in byte */
#define SOF_TPLG_ABI_SIZE 3

/**
 * sof_update_ipc_object - Parse multiple sets of tokens within the token array associated with the
 *			    token ID.
@@ -2020,45 +2017,11 @@ static int sof_complete(struct snd_soc_component *scomp)
static int sof_manifest(struct snd_soc_component *scomp, int index,
			struct snd_soc_tplg_manifest *man)
{
	u32 size;
	u32 abi_version;

	size = le32_to_cpu(man->priv.size);

	/* backward compatible with tplg without ABI info */
	if (!size) {
		dev_dbg(scomp->dev, "No topology ABI info\n");
		return 0;
	}

	if (size != SOF_TPLG_ABI_SIZE) {
		dev_err(scomp->dev, "error: invalid topology ABI size\n");
		return -EINVAL;
	}

	dev_info(scomp->dev,
		 "Topology: ABI %d:%d:%d Kernel ABI %d:%d:%d\n",
		 man->priv.data[0], man->priv.data[1],
		 man->priv.data[2], SOF_ABI_MAJOR, SOF_ABI_MINOR,
		 SOF_ABI_PATCH);

	abi_version = SOF_ABI_VER(man->priv.data[0],
				  man->priv.data[1],
				  man->priv.data[2]);

	if (SOF_ABI_VERSION_INCOMPATIBLE(SOF_ABI_VERSION, abi_version)) {
		dev_err(scomp->dev, "error: incompatible topology ABI version\n");
		return -EINVAL;
	}
	struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp);
	const struct sof_ipc_tplg_ops *ipc_tplg_ops = sdev->ipc->ops->tplg;

	if (SOF_ABI_VERSION_MINOR(abi_version) > SOF_ABI_MINOR) {
		if (!IS_ENABLED(CONFIG_SND_SOC_SOF_STRICT_ABI_CHECKS)) {
			dev_warn(scomp->dev, "warn: topology ABI is more recent than kernel\n");
		} else {
			dev_err(scomp->dev, "error: topology ABI is more recent than kernel\n");
			return -EINVAL;
		}
	}
	if (ipc_tplg_ops->parse_manifest)
		return ipc_tplg_ops->parse_manifest(scomp, index, man);

	return 0;
}