diff --git a/drivers/common/cnxk/roc_npc.h b/drivers/common/cnxk/roc_npc.h
index 8b57678863..6ab185e188 100644
--- a/drivers/common/cnxk/roc_npc.h
+++ b/drivers/common/cnxk/roc_npc.h
@@ -58,6 +58,60 @@ struct roc_npc_flow_item_raw {
 	const uint8_t *pattern; /**< Byte string to look for. */
 };
 
+struct roc_ether_addr {
+	uint8_t addr_bytes[PLT_ETHER_ADDR_LEN]; /**< Addr bytes in tx order */
+} plt_aligned(2);
+
+struct roc_ether_hdr {
+	struct roc_ether_addr d_addr; /**< Destination address. */
+	PLT_STD_C11
+	union {
+		struct roc_ether_addr s_addr; /**< Source address. */
+		struct {
+			struct roc_ether_addr S_addr;
+		} S_un; /**< Do not use directly; use s_addr instead.*/
+	};
+	uint16_t ether_type; /**< Frame type. */
+} plt_aligned(2);
+
+PLT_STD_C11
+struct roc_npc_flow_item_eth {
+	union {
+		struct {
+			/*
+			 * These fields are retained
+			 * for compatibility.
+			 * Please switch to the new header field below.
+			 */
+			struct roc_ether_addr dst; /**< Destination MAC. */
+			struct roc_ether_addr src; /**< Source MAC. */
+			uint16_t type;		   /**< EtherType or TPID. */
+		};
+		struct roc_ether_hdr hdr;
+	};
+	uint32_t has_vlan : 1; /**< Packet header contains at least one VLAN. */
+	uint32_t reserved : 31; /**< Reserved, must be zero. */
+};
+
+struct roc_vlan_hdr {
+	uint16_t vlan_tci; /**< Priority (3) + CFI (1) + Identifier Code (12) */
+	uint16_t eth_proto; /**< Ethernet type of encapsulated frame. */
+} __plt_packed;
+
+PLT_STD_C11
+struct roc_npc_flow_item_vlan {
+	union {
+		struct {
+			uint16_t tci;	     /**< Tag control information. */
+			uint16_t inner_type; /**< Inner EtherType or TPID. */
+		};
+		struct roc_vlan_hdr hdr;
+	};
+	uint32_t has_more_vlan : 1;
+	/**< Packet header contains at least one more VLAN, after this VLAN. */
+	uint32_t reserved : 31; /**< Reserved, must be zero. */
+};
+
 #define ROC_NPC_MAX_ACTION_COUNT 19
 
 enum roc_npc_action_type {
@@ -97,7 +151,7 @@ struct roc_npc_action_vf {
 };
 
 struct roc_npc_action_port_id {
-	uint32_t original : 1;	/**< Use original DPDK port ID if possible. */
+	uint32_t original : 1;	/**< Use original port ID if possible. */
 	uint32_t reserved : 31; /**< Reserved, must be zero. */
 	uint32_t id;		/**< port ID. */
 };
diff --git a/drivers/common/cnxk/roc_npc_mcam.c b/drivers/common/cnxk/roc_npc_mcam.c
index 7ed1fd3d6b..29bfc072c9 100644
--- a/drivers/common/cnxk/roc_npc_mcam.c
+++ b/drivers/common/cnxk/roc_npc_mcam.c
@@ -613,6 +613,28 @@ npc_mcam_alloc_and_write(struct npc *npc, struct roc_npc_flow *flow,
 	return 0;
 }
 
+static void
+npc_set_vlan_ltype(struct npc_parse_state *pst)
+{
+	uint64_t val, mask;
+	uint8_t lb_offset;
+
+	lb_offset =
+		__builtin_popcount(pst->npc->keyx_supp_nmask[pst->nix_intf] &
+				   ((1ULL << NPC_LTYPE_LB_OFFSET) - 1));
+	lb_offset *= 4;
+
+	mask = ~((0xfULL << lb_offset));
+	pst->flow->mcam_data[0] &= mask;
+	pst->flow->mcam_mask[0] &= mask;
+	/* NPC_LT_LB_CTAG: 0b0010, NPC_LT_LB_STAG_QINQ: 0b0011
+	 * Set LB layertype/mask as 0b0010/0b1110 to match both.
+	 */
+	val = ((uint64_t)(NPC_LT_LB_CTAG & NPC_LT_LB_STAG_QINQ)) << lb_offset;
+	pst->flow->mcam_data[0] |= val;
+	pst->flow->mcam_mask[0] |= (0xeULL << lb_offset);
+}
+
 int
 npc_program_mcam(struct npc *npc, struct npc_parse_state *pst, bool mcam_alloc)
 {
@@ -651,12 +673,16 @@ npc_program_mcam(struct npc *npc, struct npc_parse_state *pst, bool mcam_alloc)
 		if (layer_info) {
 			for (idx = 0; idx <= 2; idx++) {
 				if (layer_info & (1 << idx)) {
-					if (idx == 2)
+					if (idx == 2) {
 						data = lt;
-					else if (idx == 1)
+						mask = 0xf;
+					} else if (idx == 1) {
 						data = ((flags >> 4) & 0xf);
-					else
+						mask = ((flags >> 4) & 0xf);
+					} else {
 						data = (flags & 0xf);
+						mask = (flags & 0xf);
+					}
 
 					if (data_off >= 64) {
 						data_off = 0;
@@ -664,7 +690,7 @@ npc_program_mcam(struct npc *npc, struct npc_parse_state *pst, bool mcam_alloc)
 					}
 					key_data[index] |=
 						((uint64_t)data << data_off);
-					mask = 0xf;
+
 					if (lt == 0)
 						mask = 0;
 					key_mask[index] |=
@@ -680,6 +706,9 @@ npc_program_mcam(struct npc *npc, struct npc_parse_state *pst, bool mcam_alloc)
 	memcpy(pst->flow->mcam_data, key_data, key_len);
 	memcpy(pst->flow->mcam_mask, key_mask, key_len);
 
+	if (pst->set_vlan_ltype_mask)
+		npc_set_vlan_ltype(pst);
+
 	if (pst->is_vf) {
 		(void)mbox_alloc_msg_npc_read_base_steer_rule(npc->mbox);
 		rc = mbox_process_msg(npc->mbox, (void *)&base_rule_rsp);
diff --git a/drivers/common/cnxk/roc_npc_parse.c b/drivers/common/cnxk/roc_npc_parse.c
index c9ab9aef28..75724661da 100644
--- a/drivers/common/cnxk/roc_npc_parse.c
+++ b/drivers/common/cnxk/roc_npc_parse.c
@@ -167,6 +167,7 @@ npc_parse_higig2_hdr(struct npc_parse_state *pst)
 int
 npc_parse_la(struct npc_parse_state *pst)
 {
+	const struct roc_npc_flow_item_eth *eth_item;
 	uint8_t hw_mask[NPC_MAX_EXTRACT_HW_LEN];
 	struct npc_parse_item_info info;
 	int lid, lt;
@@ -176,6 +177,8 @@ npc_parse_la(struct npc_parse_state *pst)
 	if (pst->pattern->type != ROC_NPC_ITEM_TYPE_ETH)
 		return 0;
 
+	eth_item = pst->pattern->spec;
+
 	lid = NPC_LID_LA;
 	lt = NPC_LT_LA_ETHER;
 	info.hw_hdr_len = 0;
@@ -196,7 +199,7 @@ npc_parse_la(struct npc_parse_state *pst)
 
 	/* Prepare for parsing the item */
 	info.hw_mask = &hw_mask;
-	info.len = pst->pattern->size;
+	info.len = sizeof(eth_item->hdr);
 	npc_get_hw_supp_mask(pst, &info, lid, lt);
 	info.spec = NULL;
 	info.mask = NULL;
@@ -206,13 +209,22 @@ npc_parse_la(struct npc_parse_state *pst)
 	if (rc)
 		return rc;
 
-	/* Update pst if not validate only? clash check? */
-	return npc_update_parse_state(pst, &info, lid, lt, 0);
+	rc = npc_update_parse_state(pst, &info, lid, lt, 0);
+	if (rc)
+		return rc;
+
+	if (eth_item && eth_item->has_vlan)
+		pst->set_vlan_ltype_mask = true;
+
+	return 0;
 }
 
+#define NPC_MAX_SUPPORTED_VLANS 3
+
 int
 npc_parse_lb(struct npc_parse_state *pst)
 {
+	const struct roc_npc_flow_item_vlan *vlan_item[NPC_MAX_SUPPORTED_VLANS];
 	const struct roc_npc_item_info *pattern = pst->pattern;
 	const struct roc_npc_item_info *last_pattern;
 	const struct roc_npc_flow_item_raw *raw_spec;
@@ -240,10 +252,14 @@ npc_parse_lb(struct npc_parse_state *pst)
 		 * supported on first tag only.
 		 */
 		info.hw_mask = NULL;
-		info.len = pst->pattern->size;
+		info.len = sizeof(vlan_item[0]->hdr);
 
 		pattern = pst->pattern;
 		while (pattern->type == ROC_NPC_ITEM_TYPE_VLAN) {
+			if (nr_vlans > NPC_MAX_SUPPORTED_VLANS - 1)
+				return NPC_ERR_PATTERN_NOTSUP;
+
+			vlan_item[nr_vlans] = pattern->spec;
 			nr_vlans++;
 
 			/* Basic validation of Second/Third vlan item */
@@ -260,12 +276,35 @@ npc_parse_lb(struct npc_parse_state *pst)
 		switch (nr_vlans) {
 		case 1:
 			lt = NPC_LT_LB_CTAG;
+			if (vlan_item[0] && vlan_item[0]->has_more_vlan)
+				lt = NPC_LT_LB_STAG_QINQ;
 			break;
 		case 2:
+			if (vlan_item[1] && vlan_item[1]->has_more_vlan) {
+				if (!(pst->npc->keyx_supp_nmask[pst->nix_intf] &
+				      0x3ULL << NPC_LFLAG_LB_OFFSET))
+					return NPC_ERR_PATTERN_NOTSUP;
+
+				/* This lflag value will match either one of
+				 * NPC_F_LB_L_WITH_STAG_STAG,
+				 * NPC_F_LB_L_WITH_QINQ_CTAG,
+				 * NPC_F_LB_L_WITH_QINQ_QINQ and
+				 * NPC_F_LB_L_WITH_ITAG (0b0100 to 0b0111). For
+				 * NPC_F_LB_L_WITH_ITAG, ltype is NPC_LT_LB_ETAG
+				 * hence will not match.
+				 */
+
+				lflags = NPC_F_LB_L_WITH_QINQ_CTAG &
+					 NPC_F_LB_L_WITH_QINQ_QINQ &
+					 NPC_F_LB_L_WITH_STAG_STAG;
+			} else {
+				lflags = NPC_F_LB_L_WITH_CTAG;
+			}
 			lt = NPC_LT_LB_STAG_QINQ;
-			lflags = NPC_F_STAG_CTAG;
 			break;
 		case 3:
+			if (vlan_item[2] && vlan_item[2]->has_more_vlan)
+				return NPC_ERR_PATTERN_NOTSUP;
 			lt = NPC_LT_LB_STAG_QINQ;
 			lflags = NPC_F_STAG_STAG_CTAG;
 			break;
@@ -294,10 +333,15 @@ npc_parse_lb(struct npc_parse_state *pst)
 		}
 		info.len = pattern->size;
 	} else if (pst->pattern->type == ROC_NPC_ITEM_TYPE_QINQ) {
+		vlan_item[0] = pst->pattern->spec;
 		info.hw_mask = NULL;
-		info.len = pst->pattern->size;
+		info.len = sizeof(vlan_item[0]->hdr);
 		lt = NPC_LT_LB_STAG_QINQ;
 		lflags = NPC_F_STAG_CTAG;
+		if (vlan_item[0] && vlan_item[0]->has_more_vlan) {
+			lflags = NPC_F_LB_L_WITH_QINQ_CTAG &
+				 NPC_F_LB_L_WITH_QINQ_QINQ;
+		}
 	} else if (pst->pattern->type == ROC_NPC_ITEM_TYPE_RAW) {
 		raw_spec = pst->pattern->spec;
 		if (raw_spec->relative)
diff --git a/drivers/common/cnxk/roc_npc_priv.h b/drivers/common/cnxk/roc_npc_priv.h
index 59e6c307cd..12320f0ced 100644
--- a/drivers/common/cnxk/roc_npc_priv.h
+++ b/drivers/common/cnxk/roc_npc_priv.h
@@ -67,6 +67,11 @@
 #define NPC_ACTION_MAX_VLAN_PARAMS    3
 #define NPC_ACTION_MAX_VLANS_STRIPPED 2
 
+#define NPC_LTYPE_OFFSET_START 7
+/* LB OFFSET : START + LA (2b flags + 1b ltype) + LB (2b flags) */
+#define NPC_LTYPE_LB_OFFSET (NPC_LTYPE_OFFSET_START + 5)
+#define NPC_LFLAG_LB_OFFSET (NPC_LTYPE_OFFSET_START + 3)
+
 struct npc_action_vtag_info {
 	uint16_t vlan_id;
 	uint16_t vlan_ethtype;
@@ -176,6 +181,8 @@ struct npc_parse_state {
 	uint8_t *mcam_data; /* point to flow->mcam_data + key_len */
 	uint8_t *mcam_mask; /* point to flow->mcam_mask + key_len */
 	bool is_vf;
+	/* adjust ltype in MCAM to match at least one vlan */
+	bool set_vlan_ltype_mask;
 };
 
 enum npc_kpu_parser_flag {
diff --git a/drivers/common/cnxk/roc_platform.h b/drivers/common/cnxk/roc_platform.h
index adcd2faec6..babf56b1d5 100644
--- a/drivers/common/cnxk/roc_platform.h
+++ b/drivers/common/cnxk/roc_platform.h
@@ -11,6 +11,7 @@
 #include <rte_byteorder.h>
 #include <rte_common.h>
 #include <rte_cycles.h>
+#include <rte_ether.h>
 #include <rte_interrupts.h>
 #include <rte_io.h>
 #include <rte_log.h>
@@ -56,6 +57,9 @@
 #define PLT_INIT		 RTE_INIT
 #define PLT_TAILQ_FOREACH_SAFE	 RTE_TAILQ_FOREACH_SAFE
 
+#ifndef PLT_ETHER_ADDR_LEN
+#define PLT_ETHER_ADDR_LEN RTE_ETHER_ADDR_LEN
+#endif
 /** Divide ceil */
 #define PLT_DIV_CEIL(x, y)			\
 	({					\
@@ -84,6 +88,7 @@
 #define plt_cpu_to_be_64 rte_cpu_to_be_64
 #define plt_be_to_cpu_64 rte_be_to_cpu_64
 
+#define plt_aligned	    __rte_aligned
 #define plt_align32pow2	    rte_align32pow2
 #define plt_align32prevpow2 rte_align32prevpow2
 
