diff --git a/drivers/common/cnxk/roc_npc.c b/drivers/common/cnxk/roc_npc.c
index e6a5036..bb55f3d 100644
--- a/drivers/common/cnxk/roc_npc.c
+++ b/drivers/common/cnxk/roc_npc.c
@@ -6,6 +6,23 @@
 #include "roc_priv.h"
 
 int
+roc_npc_vtag_actions_get(struct roc_npc *roc_npc)
+{
+	struct npc *npc = roc_npc_to_npc_priv(roc_npc);
+
+	return npc->vtag_actions;
+}
+
+int
+roc_npc_vtag_actions_sub_return(struct roc_npc *roc_npc, uint32_t count)
+{
+	struct npc *npc = roc_npc_to_npc_priv(roc_npc);
+
+	npc->vtag_actions -= count;
+	return npc->vtag_actions;
+}
+
+int
 roc_npc_mcam_free_counter(struct roc_npc *roc_npc, uint16_t ctr_id)
 {
 	struct npc *npc = roc_npc_to_npc_priv(roc_npc);
@@ -330,6 +347,7 @@ npc_parse_actions(struct npc *npc, const struct roc_npc_attr *attr,
 	const struct roc_npc_action_mark *act_mark;
 	const struct roc_npc_action_queue *act_q;
 	const struct roc_npc_action_vf *vf_act;
+	bool vlan_insert_action = false;
 	int sel_act, req_act = 0;
 	uint16_t pf_func, vf_id;
 	int errcode = 0;
@@ -417,25 +435,69 @@ npc_parse_actions(struct npc *npc, const struct roc_npc_attr *attr,
 			req_act |= ROC_NPC_ACTION_TYPE_SEC;
 			rq = 0;
 			break;
+		case ROC_NPC_ACTION_TYPE_VLAN_STRIP:
+			req_act |= ROC_NPC_ACTION_TYPE_VLAN_STRIP;
+			break;
+		case ROC_NPC_ACTION_TYPE_VLAN_INSERT:
+			req_act |= ROC_NPC_ACTION_TYPE_VLAN_INSERT;
+			break;
+		case ROC_NPC_ACTION_TYPE_VLAN_ETHTYPE_INSERT:
+			req_act |= ROC_NPC_ACTION_TYPE_VLAN_ETHTYPE_INSERT;
+			break;
+		case ROC_NPC_ACTION_TYPE_VLAN_PCP_INSERT:
+			req_act |= ROC_NPC_ACTION_TYPE_VLAN_PCP_INSERT;
+			break;
 		default:
 			errcode = NPC_ERR_ACTION_NOTSUP;
 			goto err_exit;
 		}
 	}
 
+	if (req_act & (ROC_NPC_ACTION_TYPE_VLAN_INSERT |
+		       ROC_NPC_ACTION_TYPE_VLAN_ETHTYPE_INSERT |
+		       ROC_NPC_ACTION_TYPE_VLAN_PCP_INSERT))
+		vlan_insert_action = true;
+
+	if ((req_act & (ROC_NPC_ACTION_TYPE_VLAN_INSERT |
+			ROC_NPC_ACTION_TYPE_VLAN_ETHTYPE_INSERT |
+			ROC_NPC_ACTION_TYPE_VLAN_PCP_INSERT)) ==
+	    ROC_NPC_ACTION_TYPE_VLAN_PCP_INSERT) {
+		plt_err("PCP insert action can't be supported alone");
+		errcode = NPC_ERR_ACTION_NOTSUP;
+		goto err_exit;
+	}
+
+	/* Both STRIP and INSERT actions are not supported */
+	if (vlan_insert_action && (req_act & ROC_NPC_ACTION_TYPE_VLAN_STRIP)) {
+		errcode = NPC_ERR_ACTION_NOTSUP;
+		goto err_exit;
+	}
+
 	/* Check if actions specified are compatible */
 	if (attr->egress) {
-		/* Only DROP/COUNT is supported */
-		if (!(req_act & ROC_NPC_ACTION_TYPE_DROP)) {
+		if (req_act & ROC_NPC_ACTION_TYPE_VLAN_STRIP) {
+			plt_err("VLAN pop action is not supported on Egress");
 			errcode = NPC_ERR_ACTION_NOTSUP;
 			goto err_exit;
-		} else if (req_act & ~(ROC_NPC_ACTION_TYPE_DROP |
-				       ROC_NPC_ACTION_TYPE_COUNT)) {
+		}
+
+		if (req_act & ROC_NPC_ACTION_TYPE_DROP) {
+			flow->npc_action = NIX_TX_ACTIONOP_DROP;
+		} else if ((req_act & ROC_NPC_ACTION_TYPE_COUNT) ||
+			   vlan_insert_action) {
+			flow->npc_action = NIX_TX_ACTIONOP_UCAST_DEFAULT;
+		} else {
+			plt_err("Unsupported action for egress");
 			errcode = NPC_ERR_ACTION_NOTSUP;
 			goto err_exit;
 		}
-		flow->npc_action = NIX_TX_ACTIONOP_DROP;
+
 		goto set_pf_func;
+	} else {
+		if (vlan_insert_action) {
+			errcode = NPC_ERR_ACTION_NOTSUP;
+			goto err_exit;
+		}
 	}
 
 	/* We have already verified the attr, this is ingress.
@@ -463,6 +525,13 @@ npc_parse_actions(struct npc *npc, const struct roc_npc_attr *attr,
 		goto err_exit;
 	}
 
+	if (req_act & ROC_NPC_ACTION_TYPE_VLAN_STRIP)
+		npc->vtag_actions++;
+
+	/* Only VLAN action is provided */
+	if (req_act == ROC_NPC_ACTION_TYPE_VLAN_STRIP)
+		flow->npc_action = NIX_RX_ACTIONOP_UCAST;
+
 	/* Set NIX_RX_ACTIONOP */
 	if (req_act & (ROC_NPC_ACTION_TYPE_PF | ROC_NPC_ACTION_TYPE_VF)) {
 		flow->npc_action = NIX_RX_ACTIONOP_UCAST;
@@ -774,6 +843,161 @@ roc_npc_mark_actions_sub_return(struct roc_npc *roc_npc, uint32_t count)
 	return npc->mark_actions;
 }
 
+static int
+npc_vtag_cfg_delete(struct roc_npc *roc_npc, struct roc_npc_flow *flow)
+{
+	struct roc_nix *roc_nix = roc_npc->roc_nix;
+	struct nix_vtag_config *vtag_cfg;
+	struct nix_vtag_config_rsp *rsp;
+	struct mbox *mbox;
+	struct nix *nix;
+	int rc = 0;
+
+	union {
+		uint64_t reg;
+		struct nix_tx_vtag_action_s act;
+	} tx_vtag_action;
+
+	nix = roc_nix_to_nix_priv(roc_nix);
+	mbox = (&nix->dev)->mbox;
+
+	tx_vtag_action.reg = flow->vtag_action;
+	vtag_cfg = mbox_alloc_msg_nix_vtag_cfg(mbox);
+
+	if (vtag_cfg == NULL)
+		return -ENOSPC;
+
+	vtag_cfg->cfg_type = VTAG_TX;
+	vtag_cfg->vtag_size = NIX_VTAGSIZE_T4;
+	vtag_cfg->tx.vtag0_idx = tx_vtag_action.act.vtag0_def;
+	vtag_cfg->tx.free_vtag0 = true;
+
+	rc = mbox_process_msg(mbox, (void *)&rsp);
+	if (rc)
+		return rc;
+
+	return 0;
+}
+
+static int
+npc_vtag_action_program(struct roc_npc *roc_npc,
+			const struct roc_npc_action actions[],
+			struct roc_npc_flow *flow)
+{
+	uint16_t vlan_id = 0, vlan_ethtype = ROC_ETHER_TYPE_VLAN;
+	struct npc *npc = roc_npc_to_npc_priv(roc_npc);
+	struct roc_nix *roc_nix = roc_npc->roc_nix;
+	struct nix_vtag_config *vtag_cfg;
+	struct nix_vtag_config_rsp *rsp;
+	uint64_t rx_vtag_action = 0;
+	uint8_t vlan_pcp = 0;
+	struct mbox *mbox;
+	struct nix *nix;
+	int rc;
+
+	union {
+		uint64_t reg;
+		struct nix_tx_vtag_action_s act;
+	} tx_vtag_action;
+
+	nix = roc_nix_to_nix_priv(roc_nix);
+	mbox = (&nix->dev)->mbox;
+
+	flow->vtag_insert_enabled = false;
+
+	for (; actions->type != ROC_NPC_ACTION_TYPE_END; actions++) {
+		if (actions->type == ROC_NPC_ACTION_TYPE_VLAN_STRIP) {
+			if (npc->vtag_actions == 1) {
+				vtag_cfg = mbox_alloc_msg_nix_vtag_cfg(mbox);
+
+				if (vtag_cfg == NULL)
+					return -ENOSPC;
+
+				vtag_cfg->cfg_type = VTAG_RX;
+				vtag_cfg->rx.strip_vtag = 1;
+				/* Always capture */
+				vtag_cfg->rx.capture_vtag = 1;
+				vtag_cfg->vtag_size = NIX_VTAGSIZE_T4;
+				vtag_cfg->rx.vtag_type = 0;
+
+				rc = mbox_process(mbox);
+				if (rc)
+					return rc;
+			}
+
+			rx_vtag_action |= (NIX_RX_VTAGACTION_VTAG_VALID << 15);
+			rx_vtag_action |= ((uint64_t)NPC_LID_LB << 8);
+			rx_vtag_action |= NIX_RX_VTAGACTION_VTAG0_RELPTR;
+			flow->vtag_action = rx_vtag_action;
+		} else if (actions->type == ROC_NPC_ACTION_TYPE_VLAN_INSERT) {
+			const struct roc_npc_action_of_set_vlan_vid *vtag =
+				(const struct roc_npc_action_of_set_vlan_vid *)
+					actions->conf;
+			vlan_id = plt_be_to_cpu_16(vtag->vlan_vid);
+			if (vlan_id > 0xfff) {
+				plt_err("Invalid vlan_id for set vlan action");
+				return -EINVAL;
+			}
+			flow->vtag_insert_enabled = true;
+		} else if (actions->type ==
+			   ROC_NPC_ACTION_TYPE_VLAN_ETHTYPE_INSERT) {
+			const struct roc_npc_action_of_push_vlan *ethtype =
+				(const struct roc_npc_action_of_push_vlan *)
+					actions->conf;
+			vlan_ethtype = plt_be_to_cpu_16(ethtype->ethertype);
+			if (vlan_ethtype != ROC_ETHER_TYPE_VLAN &&
+			    vlan_ethtype != ROC_ETHER_TYPE_QINQ) {
+				plt_err("Invalid ethtype specified for push"
+					" vlan action");
+				return -EINVAL;
+			}
+			flow->vtag_insert_enabled = true;
+		} else if (actions->type ==
+			   ROC_NPC_ACTION_TYPE_VLAN_PCP_INSERT) {
+			const struct roc_npc_action_of_set_vlan_pcp *pcp =
+				(const struct roc_npc_action_of_set_vlan_pcp *)
+					actions->conf;
+			vlan_pcp = pcp->vlan_pcp;
+			if (vlan_pcp > 0x7) {
+				plt_err("Invalid PCP value for pcp action");
+				return -EINVAL;
+			}
+			flow->vtag_insert_enabled = true;
+		}
+	}
+
+	if (flow->vtag_insert_enabled) {
+		vtag_cfg = mbox_alloc_msg_nix_vtag_cfg(mbox);
+
+		if (vtag_cfg == NULL)
+			return -ENOSPC;
+
+		vtag_cfg->cfg_type = VTAG_TX;
+		vtag_cfg->vtag_size = NIX_VTAGSIZE_T4;
+		vtag_cfg->tx.vtag0 =
+			((vlan_ethtype << 16) | (vlan_pcp << 13) | vlan_id);
+
+		vtag_cfg->tx.cfg_vtag0 = 1;
+		rc = mbox_process_msg(mbox, (void *)&rsp);
+		if (rc)
+			return rc;
+
+		if (rsp->vtag0_idx < 0) {
+			plt_err("Failed to config TX VTAG action");
+			return -EINVAL;
+		}
+
+		tx_vtag_action.reg = 0;
+		tx_vtag_action.act.vtag0_def = rsp->vtag0_idx;
+		tx_vtag_action.act.vtag0_lid = NPC_LID_LA;
+		tx_vtag_action.act.vtag0_op = NIX_TX_VTAGOP_INSERT;
+		tx_vtag_action.act.vtag0_relptr =
+			NIX_TX_VTAGACTION_VTAG0_RELPTR;
+		flow->vtag_action = tx_vtag_action.reg;
+	}
+	return 0;
+}
+
 struct roc_npc_flow *
 roc_npc_flow_create(struct roc_npc *roc_npc, const struct roc_npc_attr *attr,
 		    const struct roc_npc_item_info pattern[],
@@ -798,6 +1022,12 @@ roc_npc_flow_create(struct roc_npc *roc_npc, const struct roc_npc_attr *attr,
 		goto err_exit;
 	}
 
+	rc = npc_vtag_action_program(roc_npc, actions, flow);
+	if (rc != 0) {
+		*errcode = rc;
+		goto err_exit;
+	}
+
 	parse_state.is_vf = !roc_nix_is_pf(roc_npc->roc_nix);
 
 	rc = npc_program_mcam(npc, &parse_state, 1);
@@ -858,23 +1088,20 @@ roc_npc_flow_destroy(struct roc_npc *roc_npc, struct roc_npc_flow *flow)
 {
 	struct npc *npc = roc_npc_to_npc_priv(roc_npc);
 	struct plt_bitmap *bmap;
-	uint16_t match_id;
 	int rc;
 
-	match_id = (flow->npc_action >> NPC_RX_ACT_MATCH_OFFSET) &
-		   NPC_RX_ACT_MATCH_MASK;
-
-	if (match_id && match_id < NPC_ACTION_FLAG_DEFAULT) {
-		if (npc->mark_actions == 0)
-			return NPC_ERR_PARAM;
-	}
-
 	rc = npc_rss_group_free(npc, flow);
 	if (rc != 0) {
 		plt_err("Failed to free rss action rc = %d", rc);
 		return rc;
 	}
 
+	if (flow->vtag_insert_enabled) {
+		rc = npc_vtag_cfg_delete(roc_npc, flow);
+		if (rc != 0)
+			return rc;
+	}
+
 	rc = npc_mcam_free_entry(npc, flow->mcam_id);
 	if (rc != 0)
 		return rc;
diff --git a/drivers/common/cnxk/roc_npc.h b/drivers/common/cnxk/roc_npc.h
index cf6f732..2c0a536 100644
--- a/drivers/common/cnxk/roc_npc.h
+++ b/drivers/common/cnxk/roc_npc.h
@@ -62,6 +62,10 @@ enum roc_npc_action_type {
 	ROC_NPC_ACTION_TYPE_COUNT = (1 << 9),
 	ROC_NPC_ACTION_TYPE_PF = (1 << 10),
 	ROC_NPC_ACTION_TYPE_VF = (1 << 11),
+	ROC_NPC_ACTION_TYPE_VLAN_STRIP = (1 << 12),
+	ROC_NPC_ACTION_TYPE_VLAN_INSERT = (1 << 13),
+	ROC_NPC_ACTION_TYPE_VLAN_ETHTYPE_INSERT = (1 << 14),
+	ROC_NPC_ACTION_TYPE_VLAN_PCP_INSERT = (1 << 15),
 };
 
 struct roc_npc_action {
@@ -83,6 +87,18 @@ struct roc_npc_action_queue {
 	uint16_t index; /**< Queue index to use. */
 };
 
+struct roc_npc_action_of_push_vlan {
+	uint16_t ethertype; /**< EtherType. */
+};
+
+struct roc_npc_action_of_set_vlan_vid {
+	uint16_t vlan_vid; /**< VLAN id. */
+};
+
+struct roc_npc_action_of_set_vlan_pcp {
+	uint8_t vlan_pcp; /**< VLAN priority. */
+};
+
 struct roc_npc_attr {
 	uint32_t priority;	/**< Rule priority level within group. */
 	uint32_t ingress : 1;	/**< Rule applies to ingress traffic. */
@@ -107,6 +123,7 @@ struct roc_npc_flow {
 	uint64_t mcam_mask[ROC_NPC_MAX_MCAM_WIDTH_DWORDS];
 	uint64_t npc_action;
 	uint64_t vtag_action;
+	bool vtag_insert_enabled;
 #define ROC_NPC_MAX_FLOW_PATTERNS 32
 	struct roc_npc_flow_dump_data dump_data[ROC_NPC_MAX_FLOW_PATTERNS];
 	uint16_t num_patterns;
@@ -138,6 +155,10 @@ enum roc_npc_intf {
 	ROC_NPC_INTF_MAX = 2,
 };
 
+enum flow_vtag_cfg_dir { VTAG_TX, VTAG_RX };
+#define ROC_ETHER_TYPE_VLAN 0x8100 /**< IEEE 802.1Q VLAN tagging. */
+#define ROC_ETHER_TYPE_QINQ 0x88A8 /**< IEEE 802.1ad QinQ tagging. */
+
 struct roc_npc {
 	struct roc_nix *roc_nix;
 	uint8_t switch_header_type;
@@ -199,4 +220,7 @@ void __roc_api roc_npc_flow_mcam_dump(FILE *file, struct roc_npc *roc_npc,
 int __roc_api roc_npc_mark_actions_get(struct roc_npc *roc_npc);
 int __roc_api roc_npc_mark_actions_sub_return(struct roc_npc *roc_npc,
 					      uint32_t count);
+int __roc_api roc_npc_vtag_actions_get(struct roc_npc *roc_npc);
+int __roc_api roc_npc_vtag_actions_sub_return(struct roc_npc *roc_npc,
+					      uint32_t count);
 #endif /* _ROC_NPC_H_ */
diff --git a/drivers/common/cnxk/roc_npc_mcam.c b/drivers/common/cnxk/roc_npc_mcam.c
index ff0676d..8ccaaad 100644
--- a/drivers/common/cnxk/roc_npc_mcam.c
+++ b/drivers/common/cnxk/roc_npc_mcam.c
@@ -546,7 +546,7 @@ npc_mcam_alloc_and_write(struct npc *npc, struct roc_npc_flow *flow,
 	 *
 	 * Second approach is used now.
 	 */
-	req->entry_data.vtag_action = 0ULL;
+	req->entry_data.vtag_action = flow->vtag_action;
 
 	for (idx = 0; idx < ROC_NPC_MAX_MCAM_WIDTH_DWORDS; idx++) {
 		req->entry_data.kw[idx] = flow->mcam_data[idx];
diff --git a/drivers/common/cnxk/roc_npc_priv.h b/drivers/common/cnxk/roc_npc_priv.h
index 961583b..484c3ae 100644
--- a/drivers/common/cnxk/roc_npc_priv.h
+++ b/drivers/common/cnxk/roc_npc_priv.h
@@ -350,6 +350,7 @@ struct npc {
 	uint16_t flow_max_priority;		/* Max priority for flow */
 	uint16_t switch_header_type; /* Suppprted switch header type */
 	uint32_t mark_actions;	     /* Number of mark actions */
+	uint32_t vtag_actions;	     /* vtag insert/strip actions */
 	uint16_t pf_func;	     /* pf_func of device */
 	npc_dxcfg_t prx_dxcfg;	     /* intf, lid, lt, extract */
 	npc_fxcfg_t prx_fxcfg;	     /* Flag extract */
diff --git a/drivers/common/cnxk/version.map b/drivers/common/cnxk/version.map
index 554459b..8a5c839 100644
--- a/drivers/common/cnxk/version.map
+++ b/drivers/common/cnxk/version.map
@@ -167,6 +167,8 @@ INTERNAL {
 	roc_npc_init;
 	roc_npc_mark_actions_get;
 	roc_npc_mark_actions_sub_return;
+	roc_npc_vtag_actions_get;
+	roc_npc_vtag_actions_sub_return;
 	roc_npc_mcam_alloc_entries;
 	roc_npc_mcam_alloc_entry;
 	roc_npc_mcam_clear_counter;
