--- a/drivers/net/wireless/ath/ath11k/core.h
+++ b/drivers/net/wireless/ath/ath11k/core.h
@@ -120,6 +120,7 @@ struct ath11k_skb_cb {
 	u32 cipher;
 	struct ath11k *ar;
 	struct ieee80211_vif *vif;
+	u32 pkt_offset;
 } __packed;
 
 struct ath11k_skb_rxcb {
--- a/drivers/net/wireless/ath/ath11k/dp_tx.c
+++ b/drivers/net/wireless/ath/ath11k/dp_tx.c
@@ -79,6 +79,42 @@ enum hal_encrypt_type ath11k_dp_tx_get_e
 	}
 }
 
+#define HTT_META_DATA_ALIGNMENT	0x8
+
+static int ath11k_dp_metadata_align_skb(struct sk_buff *skb, u8 align_len)
+{
+	if (unlikely(skb_cow_head(skb, align_len)))
+		return -ENOMEM;
+
+	skb_push(skb, align_len);
+	memset(skb->data, 0, align_len);
+	return 0;
+}
+
+static int ath11k_dp_prepare_htt_metadata(struct sk_buff *skb,
+					 u8 *htt_metadata_size)
+{
+	u8 htt_desc_size;
+	/* Size rounded of multiple of 8 bytes */
+	u8 htt_desc_size_aligned;
+	int ret;
+	struct htt_tx_msdu_desc_ext *desc_ext;
+
+	htt_desc_size = sizeof(struct htt_tx_msdu_desc_ext);
+	htt_desc_size_aligned = ALIGN(htt_desc_size, HTT_META_DATA_ALIGNMENT);
+
+	ret = ath11k_dp_metadata_align_skb(skb, htt_desc_size_aligned);
+	if (unlikely(ret))
+		return ret;
+
+	desc_ext = (struct htt_tx_msdu_desc_ext *)skb->data;
+	desc_ext->valid_encrypt_type = 1;
+	desc_ext->encrypt_type = 0;
+	desc_ext->host_tx_desc_pool = 1;
+	*htt_metadata_size = htt_desc_size_aligned;
+	return 0;
+}
+
 int ath11k_dp_tx(struct ath11k *ar, struct ath11k_vif *arvif,
 		 struct ath11k_sta *arsta, struct sk_buff *skb)
 {
@@ -97,6 +133,9 @@ int ath11k_dp_tx(struct ath11k *ar, stru
 	u32 ring_selector = 0;
 	u8 ring_map = 0;
 	bool tcl_ring_retry;
+	bool is_diff_encap = false;
+	u8 align_pad;
+	u8 htt_meta_size = 0;
 
 	if (unlikely(test_bit(ATH11K_FLAG_CRASH_FLUSH, &ar->ab->dev_flags)))
 		return -ESHUTDOWN;
@@ -189,7 +228,12 @@ tcl_ring_sel:
 
 	switch (ti.encap_type) {
 	case HAL_TCL_ENCAP_TYPE_NATIVE_WIFI:
-		ath11k_dp_tx_encap_nwifi(skb);
+		if ((arvif->vif->offload_flags & IEEE80211_OFFLOAD_ENCAP_ENABLED) &&
+		    (skb->protocol == cpu_to_be16(ETH_P_PAE) ||
+		     ieee80211_is_qos_nullfunc(hdr->frame_control)))
+			is_diff_encap = true;
+		else
+			ath11k_dp_tx_encap_nwifi(skb);
 		break;
 	case HAL_TCL_ENCAP_TYPE_RAW:
 		if (!test_bit(ATH11K_FLAG_RAW_MODE, &ab->dev_flags)) {
@@ -208,6 +252,33 @@ tcl_ring_sel:
 		goto fail_remove_idr;
 	}
 
+	/* Add metadata for software encryption of vlan group traffic */
+	if ((!test_bit(ATH11K_FLAG_HW_CRYPTO_DISABLED, &ar->ab->dev_flags) &&
+	     !(info->control.flags & IEEE80211_TX_CTL_HW_80211_ENCAP) &&
+	     !info->control.hw_key && ieee80211_has_protected(hdr->frame_control)) ||
+	    is_diff_encap) {
+		/* HW requirement is that metadata should always point to a
+		 * 8-byte aligned address. So we add alignment pad to start of
+		 * buffer. HTT Metadata should be ensured to be multiple of 8-bytes
+		 * to get 8-byte aligned start address along with align_pad added
+		 */
+		align_pad = ((unsigned long)skb->data) & (HTT_META_DATA_ALIGNMENT - 1);
+		ret = ath11k_dp_metadata_align_skb(skb, align_pad);
+		if (unlikely(ret))
+			goto fail_remove_idr;
+
+		ti.pkt_offset += align_pad;
+		ret = ath11k_dp_prepare_htt_metadata(skb, &htt_meta_size);
+		if (unlikely(ret))
+			goto fail_remove_idr;
+
+		ti.pkt_offset += htt_meta_size;
+		ti.meta_data_flags |= HTT_TCL_META_DATA_VALID_HTT;
+		ti.flags0 |= FIELD_PREP(HAL_TCL_DATA_CMD_INFO1_TO_FW, 1);
+		ti.encap_type = HAL_TCL_ENCAP_TYPE_RAW;
+		ti.encrypt_type = HAL_ENCRYPT_TYPE_OPEN;
+	}
+
 	ti.paddr = dma_map_single(ab->dev, skb->data, skb->len, DMA_TO_DEVICE);
 	if (unlikely(dma_mapping_error(ab->dev, ti.paddr))) {
 		atomic_inc(&ab->soc_stats.tx_err.misc_fail);
@@ -216,7 +287,8 @@ tcl_ring_sel:
 		goto fail_remove_idr;
 	}
 
-	ti.data_len = skb->len;
+	ti.data_len = skb->len - ti.pkt_offset;
+	skb_cb->pkt_offset = ti.pkt_offset;
 	skb_cb->paddr = ti.paddr;
 	skb_cb->vif = arvif->vif;
 	skb_cb->ar = ar;
@@ -272,6 +344,8 @@ fail_unmap_dma:
 	dma_unmap_single(ab->dev, ti.paddr, ti.data_len, DMA_TO_DEVICE);
 
 fail_remove_idr:
+	if (ti.pkt_offset)
+		skb_pull(skb, ti.pkt_offset);
 	spin_lock_bh(&tx_ring->tx_idr_lock);
 	idr_remove(&tx_ring->txbuf_idr,
 		   FIELD_GET(DP_TX_DESC_ID_MSDU_ID, ti.desc_id));
@@ -348,6 +422,10 @@ ath11k_dp_tx_htt_tx_complete_buf(struct
 		return;
 	}
 
+	if (skb_cb->pkt_offset)
+		/* Removing the alignment and htt meta data */
+		skb_pull(msdu, skb_cb->pkt_offset);
+
 	memset(&info->status, 0, sizeof(info->status));
 
 	if (ts->acked) {
--- a/drivers/net/wireless/ath/ath11k/dp_tx.h
+++ b/drivers/net/wireless/ath/ath11k/dp_tx.h
@@ -17,6 +17,203 @@ struct ath11k_dp_htt_wbm_tx_status {
 	u16 peer_id;
 };
 
+/* htt_tx_msdu_desc_ext
+ *
+ * valid_pwr
+ *		If set, tx pwr spec is valid
+ *
+ * valid_mcs_mask
+ *		If set, tx MCS mask is valid
+ *
+ * valid_nss_mask
+ *		If set, tx Nss mask is valid
+ *
+ * valid_preamble_type
+ *		If set, tx preamble spec is valid
+ *
+ * valid_retries
+ *		If set, tx retries spec is valid
+ *
+ * valid_bw_info
+ *		If set, tx dyn_bw and bw_mask are valid
+ *
+ * valid_guard_interval
+ *		If set, tx guard intv spec is valid
+ *
+ * valid_chainmask
+ *		If set, tx chainmask is valid
+ *
+ * valid_encrypt_type
+ *		If set, encrypt type is valid
+ *
+ * valid_key_flags
+ *		If set, key flags is valid
+ *
+ * valid_expire_tsf
+ *		If set, tx expire TSF spec is valid
+ *
+ * valid_chanfreq
+ *		If set, chanfreq is valid
+ *
+ * is_dsrc
+ *		If set, MSDU is a DSRC frame
+ *
+ * guard_interval
+ *		0.4us, 0.8us, 1.6us, 3.2us
+ *
+ * encrypt_type
+ *		0 = NO_ENCRYPT,
+ *		1 = ENCRYPT,
+ *		2 ~ 3 - Reserved
+ *
+ * retry_limit
+ *		Specify the maximum number of transmissions, including the
+ *		initial transmission, to attempt before giving up if no ack
+ *		is received.
+ *		If the tx rate is specified, then all retries shall use the
+ *		same rate as the initial transmission.
+ *		If no tx rate is specified, the target can choose whether to
+ *		retain the original rate during the retransmissions, or to
+ *		fall back to a more robust rate.
+ *
+ * use_dcm_11ax
+ *		If set, Use Dual subcarrier modulation.
+ *		Valid only for 11ax preamble types HE_SU
+ *		and HE_EXT_SU
+ *
+ * ltf_subtype_11ax
+ *		Takes enum values of htt_11ax_ltf_subtype_t
+ *		Valid only for 11ax preamble types HE_SU
+ *		and HE_EXT_SU
+ *
+ * dyn_bw
+ *		0 = static bw, 1 = dynamic bw
+ *
+ * bw_mask
+ *		Valid only if dyn_bw == 0 (static bw).
+ *
+ * host_tx_desc_pool
+ *		If set, Firmware allocates tx_descriptors
+ *		in WAL_BUFFERID_TX_HOST_DATA_EXP,instead
+ *		of WAL_BUFFERID_TX_TCL_DATA_EXP.
+ *		Use cases:
+ *		Any time firmware uses TQM-BYPASS for Data
+ *		TID, firmware expect host to set this bit.
+ *
+ * power
+ *		Unit of the power field is 0.5 dbm
+ *		signed value ranging from -64dbm to 63.5 dbm
+ *
+ * mcs_mask
+ *		mcs bit mask of 0 ~ 11
+ *		Setting more than one MCS isn't currently
+ *		supported by the target but is supported
+ *		in the interface in case in the future
+ *		the target supports specifications of
+ *		a limited set of MCS values.
+ *
+ * nss_mask
+ *		Nss bit mask 0 ~ 7
+ *		Setting more than one Nss isn't currently
+ *		supported by the target but is supported
+ *		in the interface in case in the future
+ *		the target supports specifications of
+ *		a limited set of Nss values.
+ *
+ * pream_type
+ *		Preamble types
+ *
+ * update_peer_cache
+ *		When set these custom values will be
+ *		used for all packets, until the next
+ *		update via this ext header.
+ *		This is to make sure not all packets
+ *		need to include this header.
+ *
+ * chain_mask
+ *		Specify which chains to transmit from
+ *
+ * key_flags
+ *		Key Index and related flags - used in mesh mode
+ *
+ * chanfreq
+ *		Channel frequency: This identifies the desired channel
+ *		frequency (in MHz) for tx frames. This is used by FW to help
+ *		determine when it is safe to transmit or drop frames for
+ *		off-channel operation.
+ *		The default value of zero indicates to FW that the corresponding
+ *		VDEV's home channel (if there is one) is the desired channel
+ *		frequency.
+ *
+ * expire_tsf_lo
+ *		tx expiry time (TSF) LSBs
+ *
+ * expire_tsf_hi
+ *		tx expiry time (TSF) MSBs
+ *
+ * learning_frame
+ *		When this flag is set, this frame will be dropped by FW
+ *		rather than being enqueued to the Transmit Queue Manager (TQM) HW.
+ *
+ * send_as_standalone
+ *		This will indicate if the msdu needs to be sent as a singleton PPDU,
+ *		i.e. with no A-MSDU or A-MPDU aggregation.
+ *		The scope is extended to other use-cases.
+ *
+ * is_host_opaque_valid
+ *		Set this bit to 1 if the host_opaque_cookie is populated
+ *		with valid information.
+ *
+ * host_opaque_cookie
+ *		Host opaque cookie for special frames
+ */
+struct htt_tx_msdu_desc_ext {
+	u32
+		valid_pwr            :  1,
+		valid_mcs_mask       :  1,
+		valid_nss_mask       :  1,
+		valid_preamble_type  :  1,
+		valid_retries        :  1,
+		valid_bw_info        :  1,
+		valid_guard_interval :  1,
+		valid_chainmask      :  1,
+		valid_encrypt_type   :  1,
+		valid_key_flags      :  1,
+		valid_expire_tsf     :  1,
+		valid_chanfreq       :  1,
+		is_dsrc              :  1,
+		guard_interval       :  2,
+		encrypt_type         :  2,
+		retry_limit          :  4,
+		use_dcm_11ax         :  1,
+		ltf_subtype_11ax     :  2,
+		dyn_bw               :  1,
+		bw_mask              :  6,
+		host_tx_desc_pool    :  1;
+	u32
+		power                :  8,
+		mcs_mask             : 12,
+		nss_mask             :  8,
+		pream_type           :  3,
+		update_peer_cache    :  1;
+	u32
+		chain_mask           :  8,
+		key_flags            :  8,
+		chanfreq             : 16;
+
+	u32 expire_tsf_lo;
+	u32 expire_tsf_hi;
+
+	u32
+		learning_frame       :  1,
+		send_as_standalone   :  1,
+		is_host_opaque_valid :  1,
+		rsvd0                : 29;
+	u32
+		host_opaque_cookie   : 16,
+		rsvd1                : 16;
+} __packed;
+
 void ath11k_dp_tx_update_txcompl(struct ath11k *ar, struct hal_tx_status *ts);
 int ath11k_dp_tx_htt_h2t_ver_req_msg(struct ath11k_base *ab);
 int ath11k_dp_tx(struct ath11k *ar, struct ath11k_vif *arvif,
--- a/drivers/net/wireless/ath/ath11k/mac.c
+++ b/drivers/net/wireless/ath/ath11k/mac.c
@@ -10324,6 +10324,9 @@ static int __ath11k_mac_register(struct
 		goto err_free_if_combs;
 	}
 
+	ar->hw->wiphy->interface_modes |= BIT(NL80211_IFTYPE_AP_VLAN);
+	ar->hw->wiphy->software_iftypes |= BIT(NL80211_IFTYPE_AP_VLAN);
+
 	if (!ab->hw_params.supports_monitor)
 		/* There's a race between calling ieee80211_register_hw()
 		 * and here where the monitor mode is enabled for a little
--- a/net/mac80211/tx.c
+++ b/net/mac80211/tx.c
@@ -40,6 +40,10 @@
 
 /* misc utils */
 
+static void ieee80211_8023_xmit(struct ieee80211_sub_if_data *sdata,
+				struct net_device *dev, struct sta_info *sta,
+				struct ieee80211_key *key, struct sk_buff *skb);
+
 static __le16 ieee80211_duration(struct ieee80211_tx_data *tx,
 				 struct sk_buff *skb, int group_addr,
 				 int next_frag_len)
@@ -4257,6 +4261,7 @@ void __ieee80211_subif_start_xmit(struct
 {
 	struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
 	struct ieee80211_local *local = sdata->local;
+	struct ieee80211_key *key = NULL;
 	struct sta_info *sta;
 	struct sk_buff *next;
 	int len = skb->len;
@@ -4281,5 +4286,15 @@ void __ieee80211_subif_start_xmit(struct
 	if (IS_ERR(sta))
 		sta = NULL;
 
+	if (sdata->vif.type == NL80211_IFTYPE_AP_VLAN &&
+	    get_bss_sdata(sdata)->vif.offload_flags & IEEE80211_OFFLOAD_ENCAP_ENABLED &&
+	    !is_multicast_ether_addr(skb->data)) {
+		if (sta)
+			key = rcu_dereference(sta->ptk[sta->ptk_idx]);
+		ieee80211_8023_xmit(sdata, dev, sta, key, skb);
+		rcu_read_unlock();
+		return;
+	}
+
 	skb_set_queue_mapping(skb, ieee80211_select_queue(sdata, sta, skb));
 	ieee80211_aggr_check(sdata, sta, skb);
