diff --git a/doc/guides/nics/features/octeontx2.ini b/doc/guides/nics/features/octeontx2.ini
index f811c38e3..3567e3f63 100644
--- a/doc/guides/nics/features/octeontx2.ini
+++ b/doc/guides/nics/features/octeontx2.ini
@@ -21,6 +21,7 @@ RSS hash             = Y
 RSS key update       = Y
 RSS reta update      = Y
 Inner RSS            = Y
+VLAN filter          = Y
 Flow control         = Y
 Flow API             = Y
 VLAN offload         = Y
diff --git a/doc/guides/nics/features/octeontx2_vec.ini b/doc/guides/nics/features/octeontx2_vec.ini
index 77c3a5637..7edc80348 100644
--- a/doc/guides/nics/features/octeontx2_vec.ini
+++ b/doc/guides/nics/features/octeontx2_vec.ini
@@ -21,6 +21,7 @@ RSS hash             = Y
 RSS key update       = Y
 RSS reta update      = Y
 Inner RSS            = Y
+VLAN filter          = Y
 Flow control         = Y
 Flow API             = Y
 VLAN offload         = Y
diff --git a/doc/guides/nics/features/octeontx2_vf.ini b/doc/guides/nics/features/octeontx2_vf.ini
index 4571a1e78..fcc1ddc03 100644
--- a/doc/guides/nics/features/octeontx2_vf.ini
+++ b/doc/guides/nics/features/octeontx2_vf.ini
@@ -17,6 +17,7 @@ RSS hash             = Y
 RSS key update       = Y
 RSS reta update      = Y
 Inner RSS            = Y
+VLAN filter          = Y
 Flow API             = Y
 VLAN offload         = Y
 QinQ offload         = Y
diff --git a/drivers/net/octeontx2/otx2_ethdev.c b/drivers/net/octeontx2/otx2_ethdev.c
index 362e46941..175e80e44 100644
--- a/drivers/net/octeontx2/otx2_ethdev.c
+++ b/drivers/net/octeontx2/otx2_ethdev.c
@@ -1345,6 +1345,8 @@ static const struct eth_dev_ops otx2_eth_dev_ops = {
 	.timesync_read_time       = otx2_nix_timesync_read_time,
 	.timesync_write_time      = otx2_nix_timesync_write_time,
 	.vlan_offload_set         = otx2_nix_vlan_offload_set,
+	.vlan_filter_set	  = otx2_nix_vlan_filter_set,
+	.vlan_strip_queue_set	  = otx2_nix_vlan_strip_queue_set,
 };
 
 static inline int
diff --git a/drivers/net/octeontx2/otx2_ethdev.h b/drivers/net/octeontx2/otx2_ethdev.h
index 816371c37..a3babe51a 100644
--- a/drivers/net/octeontx2/otx2_ethdev.h
+++ b/drivers/net/octeontx2/otx2_ethdev.h
@@ -469,6 +469,9 @@ int otx2_eth_dev_ptp_info_update(struct otx2_dev *dev, bool ptp_en);
 int otx2_nix_vlan_offload_init(struct rte_eth_dev *eth_dev);
 int otx2_nix_vlan_fini(struct rte_eth_dev *eth_dev);
 int otx2_nix_vlan_offload_set(struct rte_eth_dev *eth_dev, int mask);
-
+int otx2_nix_vlan_filter_set(struct rte_eth_dev *eth_dev, uint16_t vlan_id,
+			     int on);
+void otx2_nix_vlan_strip_queue_set(struct rte_eth_dev *dev,
+				   uint16_t queue, int on);
 
 #endif /* __OTX2_ETHDEV_H__ */
diff --git a/drivers/net/octeontx2/otx2_vlan.c b/drivers/net/octeontx2/otx2_vlan.c
index d9880d069..3e60da099 100644
--- a/drivers/net/octeontx2/otx2_vlan.c
+++ b/drivers/net/octeontx2/otx2_vlan.c
@@ -21,8 +21,8 @@ enum vtag_cfg_dir {
 };
 
 static int
-__rte_unused nix_vlan_mcam_enb_dis(struct otx2_eth_dev *dev,
-				   uint32_t entry, const int enable)
+nix_vlan_mcam_enb_dis(struct otx2_eth_dev *dev,
+		      uint32_t entry, const int enable)
 {
 	struct npc_mcam_ena_dis_entry_req *req;
 	struct otx2_mbox *mbox = dev->mbox;
@@ -366,6 +366,8 @@ nix_vlan_hw_filter(struct rte_eth_dev *eth_dev, const uint8_t enable,
 		   uint16_t vlan_id)
 {
 	struct otx2_eth_dev *dev = otx2_eth_pmd_priv(eth_dev);
+	struct otx2_vlan_info *vlan = &dev->vlan_info;
+	struct vlan_entry *entry;
 	int rc = -EINVAL;
 
 	if (!vlan_id && enable) {
@@ -379,6 +381,24 @@ nix_vlan_hw_filter(struct rte_eth_dev *eth_dev, const uint8_t enable,
 		return 0;
 	}
 
+	/* Enable/disable existing vlan filter entries */
+	TAILQ_FOREACH(entry, &vlan->fltr_tbl, next) {
+		if (vlan_id) {
+			if (entry->vlan_id == vlan_id) {
+				rc = nix_vlan_mcam_enb_dis(dev,
+							   entry->mcam_idx,
+							   enable);
+				if (rc)
+					return rc;
+			}
+		} else {
+			rc = nix_vlan_mcam_enb_dis(dev, entry->mcam_idx,
+						   enable);
+			if (rc)
+				return rc;
+		}
+	}
+
 	if (!vlan_id && !enable) {
 		rc = nix_vlan_handle_default_rx_entry(eth_dev, false, true,
 						      enable);
@@ -393,6 +413,80 @@ nix_vlan_hw_filter(struct rte_eth_dev *eth_dev, const uint8_t enable,
 	return 0;
 }
 
+/* Enable/disable vlan filtering for the given vlan_id */
+int
+otx2_nix_vlan_filter_set(struct rte_eth_dev *eth_dev, uint16_t vlan_id,
+			 int on)
+{
+	struct otx2_eth_dev *dev = otx2_eth_pmd_priv(eth_dev);
+	struct otx2_vlan_info *vlan = &dev->vlan_info;
+	struct vlan_entry *entry;
+	int entry_exists = 0;
+	int rc = -EINVAL;
+	int mcam_idx;
+
+	if (!vlan_id) {
+		otx2_err("Vlan Id can't be zero");
+		return rc;
+	}
+
+	if (!vlan->def_rx_mcam_idx) {
+		otx2_err("Vlan Filtering is disabled, enable it first");
+		return rc;
+	}
+
+	if (on) {
+		TAILQ_FOREACH(entry, &vlan->fltr_tbl, next) {
+			if (entry->vlan_id == vlan_id) {
+				/* Vlan entry already exists */
+				entry_exists = 1;
+				/* mcam entry already allocated */
+				if (entry->mcam_idx) {
+					rc = nix_vlan_hw_filter(eth_dev, on,
+								vlan_id);
+					return rc;
+				}
+			}
+		}
+
+		if (!entry_exists) {
+			entry = rte_zmalloc("otx2_nix_vlan_entry",
+					    sizeof(struct vlan_entry), 0);
+			if (!entry) {
+				otx2_err("Failed to allocate memory");
+				return -ENOMEM;
+			}
+		}
+
+		/* Enables vlan_id & mac address based filtering */
+		mcam_idx = nix_vlan_mcam_config(eth_dev, vlan_id,
+						VLAN_ID_MATCH |
+						MAC_ADDR_MATCH);
+		if (mcam_idx < 0) {
+			otx2_err("Failed to config vlan mcam");
+			TAILQ_REMOVE(&vlan->fltr_tbl, entry, next);
+			rte_free(entry);
+			return mcam_idx;
+		}
+
+		entry->mcam_idx = mcam_idx;
+		if (!entry_exists) {
+			entry->vlan_id  = vlan_id;
+			TAILQ_INSERT_HEAD(&vlan->fltr_tbl, entry, next);
+		}
+	} else {
+		TAILQ_FOREACH(entry, &vlan->fltr_tbl, next) {
+			if (entry->vlan_id == vlan_id) {
+				nix_vlan_mcam_free(dev, entry->mcam_idx);
+				TAILQ_REMOVE(&vlan->fltr_tbl, entry, next);
+				rte_free(entry);
+				break;
+			}
+		}
+	}
+	return 0;
+}
+
 /* Configure double vlan(qinq) on or off */
 static int
 otx2_nix_config_double_vlan(struct rte_eth_dev *eth_dev,
@@ -497,6 +591,13 @@ otx2_nix_vlan_offload_set(struct rte_eth_dev *eth_dev, int mask)
 	return rc;
 }
 
+void otx2_nix_vlan_strip_queue_set(__rte_unused struct rte_eth_dev *dev,
+				   __rte_unused uint16_t queue,
+				   __rte_unused int on)
+{
+	otx2_err("Not Supported");
+}
+
 static int
 nix_vlan_rx_mkex_offset(uint64_t mask)
 {
@@ -549,6 +650,27 @@ nix_vlan_get_mkex_info(struct otx2_eth_dev *dev)
 	return 0;
 }
 
+static void nix_vlan_reinstall_vlan_filters(struct rte_eth_dev *eth_dev)
+{
+	struct otx2_eth_dev *dev = otx2_eth_pmd_priv(eth_dev);
+	struct vlan_entry *entry;
+	int rc;
+
+	/* VLAN filters can't be set without setting filtern on */
+	rc = nix_vlan_handle_default_rx_entry(eth_dev, false, true, true);
+	if (rc) {
+		otx2_err("Failed to reinstall vlan filters");
+		return;
+	}
+
+	while ((entry = TAILQ_FIRST(&dev->vlan_info.fltr_tbl)) != NULL) {
+		rc = otx2_nix_vlan_filter_set(eth_dev, entry->vlan_id, true);
+		if (rc)
+			otx2_err("Failed to reinstall filter for vlan:%d",
+				 entry->vlan_id);
+	}
+}
+
 int
 otx2_nix_vlan_offload_init(struct rte_eth_dev *eth_dev)
 {
@@ -564,6 +686,11 @@ otx2_nix_vlan_offload_init(struct rte_eth_dev *eth_dev)
 		}
 
 		TAILQ_INIT(&dev->vlan_info.fltr_tbl);
+	} else {
+		/* Reinstall all mcam entries now if filter offload is set */
+		if (eth_dev->data->dev_conf.rxmode.offloads &
+		    DEV_RX_OFFLOAD_VLAN_FILTER)
+			nix_vlan_reinstall_vlan_filters(eth_dev);
 	}
 
 	mask =
@@ -582,8 +709,24 @@ otx2_nix_vlan_fini(struct rte_eth_dev *eth_dev)
 {
 	struct otx2_eth_dev *dev = otx2_eth_pmd_priv(eth_dev);
 	struct otx2_vlan_info *vlan = &dev->vlan_info;
+	struct vlan_entry *entry;
 	int rc;
 
+	while ((entry = TAILQ_FIRST(&vlan->fltr_tbl)) != NULL) {
+		if (!dev->configured) {
+			rc = nix_vlan_mcam_free(dev, entry->mcam_idx);
+			if (rc)
+				return rc;
+			TAILQ_REMOVE(&vlan->fltr_tbl, entry, next);
+			rte_free(entry);
+		} else {
+			/* MCAM entries freed by flow_fini & lf_free on
+			 * port stop.
+			 */
+			entry->mcam_idx = 0;
+		}
+	}
+
 	if (!dev->configured) {
 		if (vlan->def_rx_mcam_idx) {
 			rc = nix_vlan_mcam_free(dev, vlan->def_rx_mcam_idx);
