/* 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;
        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,
        .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,
 };
 
 #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,
        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,
        .route_setup = sof_ipc4_route_setup,
        .route_free = sof_ipc4_route_free,
        .dai_config = sof_ipc4_dai_config,
+       .parse_manifest = sof_ipc4_parse_manifest,
 };
 
  * @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;
        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
 
 #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.
 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;
 }