diff --git a/doc/guides/nics/nfp.rst b/doc/guides/nics/nfp.rst
index 8034b2a668..fffff00f1e 100644
--- a/doc/guides/nics/nfp.rst
+++ b/doc/guides/nics/nfp.rst
@@ -271,3 +271,25 @@ the header. The hash value is 32 bit which need 1 data field.
    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
    |                            Hash value                         |
    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+
+NFP_NET_META_VLAN
+Metadata with L2 (1W/4B)
+::
+
+   ----------------------------------------------------------------
+      3                   2                   1                   0
+    1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0
+   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+   |f|            reserved   | tpid| PCP |p|   vlan outermost VID  |
+   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+                                   ^                               ^
+                             NOTE: |             TCI               |
+                                   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+   f 0 -> not stripping
+   1 -> stripping
+
+   tpid 0 -> RTE_ETHER_TYPE_VLAN 0x8100 IEEE 802.1Q VLAN tagging
+        1 -> RTE_ETHER_TYPE_QINQ 0x88a8 IEEE 802.1ad QINQ tagging
+   Tpid just be stored, now we don't handle it
+
+   The vlan[0] is the innermost VLAN
diff --git a/drivers/net/nfp/nfp_ctrl.h b/drivers/net/nfp/nfp_ctrl.h
index 372d537462..a90846fddf 100644
--- a/drivers/net/nfp/nfp_ctrl.h
+++ b/drivers/net/nfp/nfp_ctrl.h
@@ -30,8 +30,18 @@
 #define NFP_NET_META_FIELD_SIZE         4
 #define NFP_NET_META_FIELD_MASK ((1 << NFP_NET_META_FIELD_SIZE) - 1)
 
+/* Working with metadata vlan api (NFD version >= 2.0) */
+#define NFP_NET_META_VLAN_INFO          16
+#define NFP_NET_META_VLAN_OFFLOAD       31
+#define NFP_NET_META_VLAN_TPID          3
+#define NFP_NET_META_VLAN_MASK          ((1 << NFP_NET_META_VLAN_INFO) - 1)
+#define NFP_NET_META_VLAN_TPID_MASK     ((1 << NFP_NET_META_VLAN_TPID) - 1)
+#define NFP_NET_META_TPID(d)            (((d) >> NFP_NET_META_VLAN_INFO) & \
+						NFP_NET_META_VLAN_TPID_MASK)
+
 /* Prepend field types */
 #define NFP_NET_META_HASH               1 /* next field carries hash type */
+#define NFP_NET_META_VLAN               4
 
 /* Hash type pre-pended when a RSS hash was computed */
 #define NFP_NET_RSS_NONE                0
diff --git a/drivers/net/nfp/nfp_rxtx.c b/drivers/net/nfp/nfp_rxtx.c
index 320387eb7c..fb271e6d96 100644
--- a/drivers/net/nfp/nfp_rxtx.c
+++ b/drivers/net/nfp/nfp_rxtx.c
@@ -124,6 +124,7 @@ nfp_net_parse_meta(struct nfp_meta_parsed *meta,
 		struct rte_mbuf *mbuf)
 {
 	uint32_t meta_info;
+	uint32_t vlan_info;
 	uint8_t *meta_offset;
 	struct nfp_net_hw *hw = rxq->hw;
 
@@ -145,6 +146,15 @@ nfp_net_parse_meta(struct nfp_meta_parsed *meta,
 			meta->hash = rte_be_to_cpu_32(*(rte_be32_t *)meta_offset);
 			meta->hash_type = meta_info & NFP_NET_META_FIELD_MASK;
 			break;
+		case NFP_NET_META_VLAN:
+			vlan_info = rte_be_to_cpu_32(*(rte_be32_t *)meta_offset);
+			meta->vlan[meta->vlan_layer].offload =
+					vlan_info >> NFP_NET_META_VLAN_OFFLOAD;
+			meta->vlan[meta->vlan_layer].tci =
+					vlan_info & NFP_NET_META_VLAN_MASK;
+			meta->vlan[meta->vlan_layer].tpid = NFP_NET_META_TPID(vlan_info);
+			++meta->vlan_layer;
+			break;
 		default:
 			/* Unsupported metadata can be a performance issue */
 			return;
@@ -213,6 +223,40 @@ nfp_net_parse_meta_hash(const struct nfp_meta_parsed *meta,
 	}
 }
 
+/*
+ * nfp_net_parse_meta_vlan() - Set mbuf vlan_strip data based on metadata info
+ *
+ * The VLAN info TPID and TCI are prepended to the packet data.
+ * Extract and decode it and set the mbuf fields.
+ */
+static void
+nfp_net_parse_meta_vlan(const struct nfp_meta_parsed *meta,
+		struct nfp_net_rx_desc *rxd,
+		struct nfp_net_rxq *rxq,
+		struct rte_mbuf *mb)
+{
+	struct nfp_net_hw *hw = rxq->hw;
+
+	if ((hw->ctrl & NFP_NET_CFG_CTRL_RXVLAN) == 0)
+		return;
+
+	/*
+	 * The nic support the two way to send the VLAN info,
+	 * 1. According the metadata to send the VLAN info
+	 * 2. According the descriptor to sned the VLAN info
+	 *
+	 * If the nic doesn't send the VLAN info, it is not necessary
+	 * to do anything.
+	 */
+	if (meta->vlan_layer >= 1 && meta->vlan[0].offload != 0) {
+		mb->vlan_tci = rte_cpu_to_le_32(meta->vlan[0].tci);
+		mb->ol_flags |= RTE_MBUF_F_RX_VLAN | RTE_MBUF_F_RX_VLAN_STRIPPED;
+	} else if ((rxd->rxd.flags & PCIE_DESC_RX_VLAN) != 0) {
+		mb->vlan_tci = rte_cpu_to_le_32(rxd->rxd.vlan);
+		mb->ol_flags |= RTE_MBUF_F_RX_VLAN | RTE_MBUF_F_RX_VLAN_STRIPPED;
+	}
+}
+
 /*
  * RX path design:
  *
@@ -349,16 +393,11 @@ nfp_net_recv_pkts(void *rx_queue, struct rte_mbuf **rx_pkts, uint16_t nb_pkts)
 		memset(&meta, 0, sizeof(meta));
 		nfp_net_parse_meta(&meta, rxds, rxq, mb);
 		nfp_net_parse_meta_hash(&meta, rxds, rxq, mb);
+		nfp_net_parse_meta_vlan(&meta, rxds, rxq, mb);
 
 		/* Checking the checksum flag */
 		nfp_net_rx_cksum(rxq, rxds, mb);
 
-		if ((rxds->rxd.flags & PCIE_DESC_RX_VLAN) &&
-		    (hw->ctrl & NFP_NET_CFG_CTRL_RXVLAN)) {
-			mb->vlan_tci = rte_cpu_to_le_32(rxds->rxd.vlan);
-			mb->ol_flags |= RTE_MBUF_F_RX_VLAN | RTE_MBUF_F_RX_VLAN_STRIPPED;
-		}
-
 		/* Adding the mbuf to the mbuf array passed by the app */
 		rx_pkts[avail++] = mb;
 
diff --git a/drivers/net/nfp/nfp_rxtx.h b/drivers/net/nfp/nfp_rxtx.h
index 4cbf4080c5..a24ec9e560 100644
--- a/drivers/net/nfp/nfp_rxtx.h
+++ b/drivers/net/nfp/nfp_rxtx.h
@@ -25,6 +25,9 @@
 #define RTE_MBUF_DMA_ADDR_DEFAULT(mb) \
 	((uint64_t)((mb)->buf_iova + RTE_PKTMBUF_HEADROOM))
 
+/* Maximum number of supported VLANs in parsed form packet metadata. */
+#define NFP_META_MAX_VLANS       2
+
 /*
  * struct nfp_meta_parsed - Record metadata parsed from packet
  *
@@ -33,10 +36,29 @@
  *
  * @hash: RSS hash value
  * @hash_type: RSS hash type
+ * @vlan_layer: The layers of VLAN info which are passed from nic.
+ *              Only this number of entries of the @vlan array are valid.
+ *
+ * @vlan: Holds information parses from NFP_NET_META_VLAN. The inner most vlan
+ *        starts at position 0 and only @vlan_layer entries contain valid
+ *        information.
+ *
+ *        Currently only 1 layer of vlan is supported,
+ *        vlan[0] - vlan strip info
+ *
+ * @vlan.offload:  Flag indicates whether VLAN is offloaded
+ * @vlan.tpid: Vlan TPID
+ * @vlan.tci: Vlan TCI including PCP + Priority + VID
  */
 struct nfp_meta_parsed {
 	uint32_t hash;
 	uint8_t hash_type;
+	uint8_t vlan_layer;
+	struct {
+		uint8_t offload;
+		uint8_t tpid;
+		uint16_t tci;
+	} vlan[NFP_META_MAX_VLANS];
 };
 
 /*
