From 1d63d4c62680ea920791d1d3d8503497539239a3 Mon Sep 17 00:00:00 2001
From: Evelyn Tsai <evelyn.tsai@mediatek.com>
Date: Sat, 3 Jun 2023 06:11:44 +0800
Subject: [PATCH 3005/3010] wifi: mt76: mt7996: add wifi6 cert patch

Signed-off-by: Shurong Wen <shurong.wen@mediatek.com>
---
 mt7996/mac.c     |  12 +++
 mt7996/main.c    |  27 ++++++-
 mt7996/mcu.c     |  12 +++
 mt7996/mcu.h     |   4 +
 mt7996/mt7996.h  |   2 +
 mt7996/mtk_mcu.c | 188 ++++++++++++++++++++++++++++++++++++++++++++++
 mt7996/mtk_mcu.h | 190 +++++++++++++++++++++++++++++++++++++++++++++--
 mt7996/vendor.c  | 169 +++++++++++++++++++++++++++++++++++------
 8 files changed, 575 insertions(+), 29 deletions(-)

diff --git a/mt7996/mac.c b/mt7996/mac.c
index e7e7298..14f4fdd 100644
--- a/mt7996/mac.c
+++ b/mt7996/mac.c
@@ -11,6 +11,7 @@
 #include "mac.h"
 #include "mcu.h"
 #include "mt7996_trace.h"
+#include "vendor.h"
 
 #define to_rssi(field, rcpi)	((FIELD_GET(field, rcpi) - 220) / 2)
 
@@ -3161,6 +3162,17 @@ void mt7996_mac_update_stats(struct mt7996_phy *phy)
 	}
 }
 
+void mt7996_set_wireless_amsdu(struct ieee80211_hw *hw, u8 en)
+{
+	struct mt76_phy *mphy = hw->priv;
+	struct mt76_dev *mdev = mphy->dev;
+
+	if (en)
+		ieee80211_hw_set(hw, SUPPORTS_AMSDU_IN_AMPDU);
+	else
+		ieee80211_hw_clear(hw, SUPPORTS_AMSDU_IN_AMPDU);
+}
+
 void mt7996_mac_sta_rc_work(struct work_struct *work)
 {
 	struct mt7996_dev *dev = container_of(work, struct mt7996_dev, rc_work);
diff --git a/mt7996/main.c b/mt7996/main.c
index 70e30e4..d646938 100644
--- a/mt7996/main.c
+++ b/mt7996/main.c
@@ -682,6 +682,10 @@ int mt7996_mac_sta_add(struct mt76_dev *mdev, struct ieee80211_vif *vif,
 	u8 band_idx = mvif->phy->mt76->band_idx;
 	int ret, idx;
 
+#ifdef CONFIG_MTK_VENDOR
+	struct mt7996_phy *phy = &dev->phy;
+#endif
+
 	idx = mt76_wcid_alloc(dev->mt76.wcid_mask, MT7996_WTBL_STA);
 	if (idx < 0)
 		return -ENOSPC;
@@ -708,7 +712,28 @@ int mt7996_mac_sta_add(struct mt76_dev *mdev, struct ieee80211_vif *vif,
 	if (ret)
 		return ret;
 
-	return mt7996_mcu_add_rate_ctrl(dev, vif, sta, false);
+	ret = mt7996_mcu_add_rate_ctrl(dev, vif, sta, false);
+	if (ret)
+		return ret;
+
+#ifdef CONFIG_MTK_VENDOR
+	switch (band_idx) {
+	case MT_BAND1:
+		phy = mt7996_phy2(dev);
+		break;
+	case MT_BAND2:
+		phy = mt7996_phy3(dev);
+		break;
+	case MT_BAND0:
+	default:
+		break;
+	}
+
+	if (phy && phy->muru_onoff & MUMIMO_DL_CERT)
+		mt7996_mcu_set_mimo(phy);
+#endif
+
+	return 0;
 }
 
 void mt7996_mac_sta_remove(struct mt76_dev *mdev, struct ieee80211_vif *vif,
diff --git a/mt7996/mcu.c b/mt7996/mcu.c
index 3b2df70..81bfef7 100644
--- a/mt7996/mcu.c
+++ b/mt7996/mcu.c
@@ -4899,6 +4899,18 @@ void mt7996_set_wireless_vif(void *data, u8 *mac, struct ieee80211_vif *vif)
 	val = FIELD_GET(RATE_CFG_VAL, *((u32 *)data));
 
 	switch (mode) {
+	case RATE_PARAM_FIXED_OFDMA:
+		if (val == 3)
+			phy->muru_onoff = OFDMA_DL;
+		else
+			phy->muru_onoff = val;
+		break;
+	case RATE_PARAM_FIXED_MIMO:
+		if (val == 0)
+			phy->muru_onoff = MUMIMO_DL_CERT | MUMIMO_DL;
+		else
+			phy->muru_onoff = MUMIMO_UL;
+		break;
 	case RATE_PARAM_AUTO_MU:
 		if (val < 0 || val > 15) {
 			printk("Wrong value! The value is between 0-15.\n");
diff --git a/mt7996/mcu.h b/mt7996/mcu.h
index 5a431dc..ff17982 100644
--- a/mt7996/mcu.h
+++ b/mt7996/mcu.h
@@ -613,6 +613,8 @@ enum {
 	RATE_PARAM_FIXED_GI = 11,
 	RATE_PARAM_AUTO = 20,
 #ifdef CONFIG_MTK_VENDOR
+	RATE_PARAM_FIXED_MIMO = 30,
+	RATE_PARAM_FIXED_OFDMA = 31,
 	RATE_PARAM_AUTO_MU = 32,
 #endif
 };
@@ -625,6 +627,7 @@ enum {
 #define OFDMA_UL                       BIT(1)
 #define MUMIMO_DL                      BIT(2)
 #define MUMIMO_UL                      BIT(3)
+#define MUMIMO_DL_CERT                 BIT(4)
 
 enum {
 	BF_SOUNDING_ON = 1,
@@ -717,6 +720,7 @@ enum {
 enum {
 	UNI_WSYS_CONFIG_FW_LOG_CTRL,
 	UNI_WSYS_CONFIG_FW_DBG_CTRL,
+	UNI_CMD_CERT_CFG = 6,
 };
 
 enum {
diff --git a/mt7996/mt7996.h b/mt7996/mt7996.h
index 9c6bcf3..1c66f28 100644
--- a/mt7996/mt7996.h
+++ b/mt7996/mt7996.h
@@ -902,6 +902,8 @@ void mt7996_vendor_register(struct mt7996_phy *phy);
 void mt7996_vendor_amnt_fill_rx(struct mt7996_phy *phy, struct sk_buff *skb);
 int mt7996_vendor_amnt_sta_remove(struct mt7996_phy *phy,
 				  struct ieee80211_sta *sta);
+void mt7996_set_wireless_amsdu(struct ieee80211_hw *hw, u8 en);
+void mt7996_mcu_set_mimo(struct mt7996_phy *phy);
 #endif
 
 #ifdef CONFIG_MTK_DEBUG
diff --git a/mt7996/mtk_mcu.c b/mt7996/mtk_mcu.c
index 3eb3f98..04dad92 100644
--- a/mt7996/mtk_mcu.c
+++ b/mt7996/mtk_mcu.c
@@ -9,6 +9,7 @@
 #include "mcu.h"
 #include "mac.h"
 #include "mtk_mcu.h"
+#include "mtk_mcu_i.h"
 
 #ifdef CONFIG_MTK_DEBUG
 
@@ -116,4 +117,191 @@ int mt7996_mcu_set_bypass_smthint(struct mt7996_phy *phy, u8 val)
 				 true);
 }
 
+int mt7996_mcu_set_bsrp_ctrl(struct mt7996_phy *phy, u16 interval,
+			     u16 ru_alloc, u32 trig_type, u8 trig_flow, u8 ext_cmd)
+{
+	struct mt7996_dev *dev = phy->dev;
+	struct {
+		u8 _rsv[4];
+
+		__le16 tag;
+		__le16 len;
+
+		__le16 interval;
+		__le16 ru_alloc;
+		__le32 trigger_type;
+		u8 trigger_flow;
+		u8 ext_cmd_bsrp;
+		u8 band_bitmap;
+		u8 _rsv2;
+	} __packed req = {
+		.tag = cpu_to_le16(UNI_CMD_MURU_BSRP_CTRL),
+		.len = cpu_to_le16(sizeof(req) - 4),
+		.interval = cpu_to_le16(interval),
+		.ru_alloc = cpu_to_le16(ru_alloc),
+		.trigger_type = cpu_to_le32(trig_type),
+		.trigger_flow = trig_flow,
+		.ext_cmd_bsrp = ext_cmd,
+		.band_bitmap = BIT(phy->mt76->band_idx),
+	};
+
+	return mt76_mcu_send_msg(&dev->mt76, MCU_WM_UNI_CMD(MURU), &req,
+				 sizeof(req), false);
+}
+
+int mt7996_mcu_set_rfeature_trig_type(struct mt7996_phy *phy, u8 enable, u8 trig_type)
+{
+	struct mt7996_dev *dev = phy->dev;
+	int ret = 0;
+	char buf[] = "01:00:00:1B";
+
+	if (enable) {
+		ret = mt7996_mcu_set_muru_cmd(dev, UNI_CMD_MURU_SET_TRIG_TYPE, trig_type);
+		if (ret)
+			return ret;
+	}
+
+	switch (trig_type) {
+	case CAPI_BASIC:
+		return mt7996_mcu_set_bsrp_ctrl(phy, 5, 67, 0, 0, enable);
+	case CAPI_BRP:
+		return mt7996_mcu_set_txbf_snd_info(phy, buf);
+	case CAPI_MU_BAR:
+		return mt7996_mcu_set_muru_cmd(dev, UNI_CMD_MURU_SET_MUDL_ACK_POLICY,
+					       MU_DL_ACK_POLICY_MU_BAR);
+	case CAPI_BSRP:
+		return mt7996_mcu_set_bsrp_ctrl(phy, 5, 67, 4, 0, enable);
+	default:
+		return 0;
+	}
+}
+
+int mt7996_mcu_set_muru_cfg(struct mt7996_phy *phy, struct mt7996_muru *muru)
+{
+	struct mt7996_dev *dev = phy->dev;
+	struct {
+		u8 _rsv[4];
+
+		__le16 tag;
+		__le16 len;
+
+		u8 version;
+		u8 revision;
+		u8 _rsv2[2];
+
+		struct mt7996_muru muru;
+	} __packed req = {
+		.tag = cpu_to_le16(UNI_CMD_MURU_MUNUAL_CONFIG),
+		.len = cpu_to_le16(sizeof(req) - 4),
+		.version = UNI_CMD_MURU_VER_EHT,
+	};
+
+	memcpy(&req.muru, muru, sizeof(struct mt7996_muru));
+
+	return mt76_mcu_send_msg(&dev->mt76, MCU_WM_UNI_CMD(MURU), &req,
+				 sizeof(req), false);
+}
+
+int mt7996_set_muru_cfg(struct mt7996_phy *phy, u8 action, u8 val)
+{
+	struct mt7996_muru muru;
+	struct mt7996_muru_dl *dl = &muru.dl;
+	struct mt7996_muru_ul *ul = &muru.ul;
+	struct mt7996_muru_comm *comm = &muru.comm;
+
+	memset(&muru, 0, sizeof(muru));
+
+	switch (action) {
+	case MURU_DL_USER_CNT:
+		dl->user_num = val;
+		comm->ppdu_format = MURU_PPDU_HE_MU;
+		comm->sch_type = MURU_OFDMA_SCH_TYPE_DL;
+		muru.cfg_comm = cpu_to_le32(MURU_COMM_SET);
+		muru.cfg_dl = cpu_to_le32(MURU_USER_CNT);
+		return mt7996_mcu_set_muru_cfg(phy, &muru);
+	case MURU_UL_USER_CNT:
+		ul->user_num = val;
+		comm->ppdu_format = MURU_PPDU_HE_TRIG;
+		comm->sch_type = MURU_OFDMA_SCH_TYPE_UL;
+		muru.cfg_comm = cpu_to_le32(MURU_COMM_SET);
+		muru.cfg_ul = cpu_to_le32(MURU_USER_CNT);
+		return mt7996_mcu_set_muru_cfg(phy, &muru);
+	default:
+		return 0;
+	}
+}
+
+void mt7996_mcu_set_ppdu_tx_type(struct mt7996_phy *phy, u8 ppdu_type)
+{
+	struct mt7996_dev *dev = phy->dev;
+	int enable_su;
+
+	switch (ppdu_type) {
+	case CAPI_SU:
+		enable_su = 1;
+		mt7996_mcu_set_muru_cmd(dev, UNI_CMD_MURU_SUTX_CTRL, enable_su);
+		mt7996_set_muru_cfg(phy, MURU_DL_USER_CNT, 0);
+		break;
+	case CAPI_MU:
+		enable_su = 0;
+		mt7996_mcu_set_muru_cmd(dev, UNI_CMD_MURU_SUTX_CTRL, enable_su);
+		break;
+	default:
+		break;
+	}
+}
+
+void mt7996_mcu_set_nusers_ofdma(struct mt7996_phy *phy, u8 type, u8 ofdma_user_cnt)
+{
+	struct mt7996_dev *dev = phy->dev;
+	int enable_su = 0;
+
+	mt7996_mcu_set_muru_cmd(dev, UNI_CMD_MURU_SUTX_CTRL, enable_su);
+	mt7996_mcu_set_muru_cmd(dev, UNI_CMD_MURU_SET_MUDL_ACK_POLICY, MU_DL_ACK_POLICY_SU_BAR);
+	mt7996_mcu_muru_set_prot_frame_thr(dev, 9999);
+
+	switch (type) {
+	case MURU_UL_USER_CNT:
+		mt7996_set_muru_cfg(phy, MURU_UL_USER_CNT, ofdma_user_cnt);
+		break;
+	case MURU_DL_USER_CNT:
+	default:
+		mt7996_set_muru_cfg(phy, MURU_DL_USER_CNT, ofdma_user_cnt);
+		break;
+	}
+}
+
+void mt7996_mcu_set_mimo(struct mt7996_phy *phy)
+{
+	struct mt7996_dev *dev = phy->dev;
+	int disable_ra = 1;
+	char buf[] = "2 134 0 1 0 1 2 2 2";
+	int force_mu = 1;
+
+	mt7996_mcu_set_muru_cmd(dev, UNI_CMD_MURU_SET_MUDL_ACK_POLICY, MU_DL_ACK_POLICY_SU_BAR);
+	mt7996_mcu_set_muru_fixed_rate_enable(dev, UNI_CMD_MURU_FIXED_RATE_CTRL, disable_ra);
+	mt7996_mcu_set_muru_fixed_rate_parameter(dev, UNI_CMD_MURU_FIXED_GROUP_RATE_CTRL, buf);
+	mt7996_mcu_set_muru_cmd(dev, UNI_CMD_MURU_SET_FORCE_MU, force_mu);
+}
+
+void mt7996_mcu_set_cert(struct mt7996_phy *phy, u8 type)
+{
+	struct mt7996_dev *dev = phy->dev;
+	struct {
+		u8 _rsv[4];
+
+		__le16 tag;
+		__le16 len;
+		u8 action;
+		u8 _rsv2[3];
+	} __packed req = {
+		.tag = cpu_to_le16(UNI_CMD_CERT_CFG),
+		.len = cpu_to_le16(sizeof(req) - 4),
+		.action = type, /* 1: CAPI Enable */
+	};
+
+	mt76_mcu_send_msg(&dev->mt76, MCU_WM_UNI_CMD(WSYS_CONFIG), &req,
+			  sizeof(req), false);
+}
+
 #endif
diff --git a/mt7996/mtk_mcu.h b/mt7996/mtk_mcu.h
index bbc986f..62b8165 100644
--- a/mt7996/mtk_mcu.h
+++ b/mt7996/mtk_mcu.h
@@ -92,17 +92,197 @@ enum txpower_event {
 #endif
 
 enum {
+	UNI_CMD_MURU_BSRP_CTRL = 0x01,
 	UNI_CMD_MURU_SUTX_CTRL = 0x10,
-	UNI_CMD_MURU_FIXED_RATE_CTRL,
-	UNI_CMD_MURU_FIXED_GROUP_RATE_CTRL,
+	UNI_CMD_MURU_FIXED_RATE_CTRL = 0x11,
+	UNI_CMD_MURU_FIXED_GROUP_RATE_CTRL = 0x12,
 	UNI_CMD_MURU_SET_FORCE_MU = 0x33,
 	UNI_CMD_MURU_MUNUAL_CONFIG = 0x64,
-	UNI_CMD_MURU_SET_MUDL_ACK_POLICY = 0xC9,
-	UNI_CMD_MURU_SET_TRIG_TYPE,
-	UNI_CMD_MURU_SET_20M_DYN_ALGO,
+	UNI_CMD_MURU_SET_MUDL_ACK_POLICY = 0xC8,
+	UNI_CMD_MURU_SET_TRIG_TYPE = 0xC9,
+	UNI_CMD_MURU_SET_20M_DYN_ALGO = 0xCA,
 	UNI_CMD_MURU_PROT_FRAME_THR = 0xCC,
 	UNI_CMD_MURU_SET_CERT_MU_EDCA_OVERRIDE,
 };
 
+int mt7996_mcu_set_rfeature_trig_type(struct mt7996_phy *phy, u8 enable, u8 trig_type);
+void mt7996_mcu_set_ppdu_tx_type(struct mt7996_phy *phy, u8 ppdu_type);
+void mt7996_mcu_set_nusers_ofdma(struct mt7996_phy *phy, u8 type, u8 ofdma_user_cnt);
+void mt7996_mcu_set_cert(struct mt7996_phy *phy, u8 type);
 int mt7996_mcu_set_bypass_smthint(struct mt7996_phy *phy, u8 val);
+
+#ifdef CONFIG_MTK_VENDOR
+struct mt7996_muru_comm {
+	u8 pda_pol;
+	u8 band;
+	u8 spe_idx;
+	u8 proc_type;
+
+	__le16 mlo_ctrl;
+	u8 sch_type;
+	u8 ppdu_format;
+	u8 ac;
+	u8 _rsv[3];
+};
+
+struct mt7996_muru_dl {
+	u8 user_num;
+	u8 tx_mode;
+	u8 bw;
+	u8 gi;
+
+	u8 ltf;
+	u8 mcs;
+	u8 dcm;
+	u8 cmprs;
+
+	__le16 ru[16];
+
+	u8 c26[2];
+	u8 ack_policy;
+	u8 tx_power;
+
+	__le16 mu_ppdu_duration;
+	u8 agc_disp_order;
+	u8 _rsv1;
+
+	u8 agc_disp_pol;
+	u8 agc_disp_ratio;
+	__le16 agc_disp_linkMFG;
+
+	__le16 prmbl_punc_bmp;
+	u8 _rsv2[2];
+
+	struct {
+		__le16 wlan_idx;
+		u8 ru_alloc_seg;
+		u8 ru_idx;
+		u8 ldpc;
+		u8 nss;
+		u8 mcs;
+		u8 mu_group_idx;
+		u8 vht_groud_id;
+		u8 vht_up;
+		u8 he_start_stream;
+		u8 he_mu_spatial;
+		__le16 tx_power_alpha;
+		u8 ack_policy;
+		u8 ru_allo_ps160;
+	} usr[16];
+};
+
+struct mt7996_muru_ul {
+	u8 user_num;
+	u8 tx_mode;
+
+	u8 ba_type;
+	u8 _rsv;
+
+	u8 bw;
+	u8 gi_ltf;
+	__le16 ul_len;
+
+	__le16 trig_cnt;
+	u8 pad;
+	u8 trig_type;
+
+	__le16 trig_intv;
+	u8 trig_ta[ETH_ALEN];
+	__le16 ul_ru[16];
+
+	u8 c26[2];
+	__le16 agc_disp_linkMFG;
+
+	u8 agc_disp_mu_len;
+	u8 agc_disp_pol;
+	u8 agc_disp_ratio;
+	u8 agc_disp_pu_idx;
+
+	struct {
+		__le16 wlan_idx;
+		u8 ru_alloc;
+		u8 ru_idx;
+		u8 ldpc;
+		u8 nss;
+		u8 mcs;
+		u8 target_rssi;
+		__le32 trig_pkt_size;
+		u8 ru_allo_ps160;
+		u8 _rsv2[3];
+	} usr[16];
+};
+
+struct mt7996_muru_dbg {
+	/* HE TB RX Debug */
+	__le32 rx_hetb_nonsf_en_bitmap;
+	__le32 rx_hetb_cfg[2];
+};
+
+struct mt7996_muru {
+	__le32 cfg_comm;
+	__le32 cfg_dl;
+	__le32 cfg_ul;
+	__le32 cfg_dbg;
+
+	struct mt7996_muru_comm comm;
+	struct mt7996_muru_dl dl;
+	struct mt7996_muru_ul ul;
+	struct mt7996_muru_dbg dbg;
+};
+
+
+#define MURU_PPDU_HE_TRIG	BIT(2)
+#define MURU_PPDU_HE_MU		BIT(3)
+
+#define MURU_OFDMA_SCH_TYPE_DL	BIT(0)
+#define MURU_OFDMA_SCH_TYPE_UL	BIT(1)
+
+/* Common Config */
+#define MURU_COMM_PPDU_FMT	BIT(0)
+#define MURU_COMM_SCH_TYPE	BIT(1)
+#define MURU_COMM_BAND		BIT(2)
+#define MURU_COMM_WMM		BIT(3)
+#define MURU_COMM_SPE_IDX	BIT(4)
+#define MURU_COMM_PROC_TYPE	BIT(5)
+#define MURU_COMM_SET		(MURU_COMM_PPDU_FMT | MURU_COMM_BAND | \
+				 MURU_COMM_WMM | MURU_COMM_SPE_IDX)
+
+/* DL&UL User config */
+#define MURU_USER_CNT		BIT(4)
+
+enum {
+	CAPI_SU,
+	CAPI_MU,
+	CAPI_ER_SU,
+	CAPI_TB,
+	CAPI_LEGACY
+};
+
+enum {
+	CAPI_BASIC,
+	CAPI_BRP,
+	CAPI_MU_BAR,
+	CAPI_MU_RTS,
+	CAPI_BSRP,
+	CAPI_GCR_MU_BAR,
+	CAPI_BQRP,
+	CAPI_NDP_FRP,
+};
+
+enum {
+	MURU_UPDATE = 0,
+	MURU_DL_USER_CNT,
+	MURU_UL_USER_CNT,
+	MURU_DL_INIT,
+	MURU_UL_INIT,
+};
+
+enum {
+	MU_DL_ACK_POLICY_MU_BAR = 3,
+	MU_DL_ACK_POLICY_TF_FOR_ACK = 4,
+	MU_DL_ACK_POLICY_SU_BAR = 5,
+};
+
+#endif
+
 #endif
diff --git a/mt7996/vendor.c b/mt7996/vendor.c
index d044929..9a10ec3 100644
--- a/mt7996/vendor.c
+++ b/mt7996/vendor.c
@@ -8,6 +8,7 @@
 #include "mt7996.h"
 #include "mcu.h"
 #include "vendor.h"
+#include "mtk_mcu.h"
 
 #ifdef CONFIG_MTK_VENDOR
 static const struct nla_policy
@@ -22,6 +23,12 @@ wireless_ctrl_policy[NUM_MTK_VENDOR_ATTRS_WIRELESS_CTRL] = {
 	[MTK_VENDOR_ATTR_WIRELESS_CTRL_AMPDU] = {.type = NLA_U8 },
 	[MTK_VENDOR_ATTR_WIRELESS_CTRL_CERT] = {.type = NLA_U8 },
 	[MTK_VENDOR_ATTR_WIRELESS_CTRL_RTS_SIGTA] = {.type = NLA_U8 },
+	[MTK_VENDOR_ATTR_WIRELESS_CTRL_FIXED_MCS] = {.type = NLA_U8 },
+	[MTK_VENDOR_ATTR_WIRELESS_CTRL_OFDMA] = {.type = NLA_U8 },
+	[MTK_VENDOR_ATTR_WIRELESS_CTRL_PPDU_TX_TYPE] = {.type = NLA_U8 },
+	[MTK_VENDOR_ATTR_WIRELESS_CTRL_NUSERS_OFDMA] = {.type = NLA_U8 },
+	[MTK_VENDOR_ATTR_WIRELESS_CTRL_MIMO] = {.type = NLA_U8 },
+	[MTK_VENDOR_ATTR_WIRELESS_CTRL_BA_BUFFER_SIZE] = {.type = NLA_U16 },
 };
 
 static const struct nla_policy
@@ -53,6 +60,17 @@ bss_color_ctrl_policy[NUM_MTK_VENDOR_ATTRS_BSS_COLOR_CTRL] = {
 	[MTK_VENDOR_ATTR_AVAL_BSS_COLOR_BMP] = { .type = NLA_U64 },
 };
 
+static const struct nla_policy
+rfeature_ctrl_policy[NUM_MTK_VENDOR_ATTRS_RFEATURE_CTRL] = {
+	[MTK_VENDOR_ATTR_RFEATURE_CTRL_HE_GI] = {.type = NLA_U8 },
+	[MTK_VENDOR_ATTR_RFEATURE_CTRL_HE_LTF] = { .type = NLA_U8 },
+	[MTK_VENDOR_ATTR_RFEATURE_CTRL_TRIG_TYPE_CFG] = { .type = NLA_NESTED },
+	[MTK_VENDOR_ATTR_RFEATURE_CTRL_TRIG_TYPE_EN] = { .type = NLA_U8 },
+	[MTK_VENDOR_ATTR_RFEATURE_CTRL_TRIG_TYPE] = { .type = NLA_U8 },
+	[MTK_VENDOR_ATTR_RFEATURE_CTRL_ACK_PLCY] = { .type = NLA_U8 },
+	[MTK_VENDOR_ATTR_RFEATURE_CTRL_TRIG_TXBF] = { .type = NLA_U8 },
+};
+
 struct mt7996_amnt_data {
 	u8 idx;
 	u8 addr[ETH_ALEN];
@@ -138,29 +156,6 @@ void mt7996_set_wireless_rts_sigta(struct ieee80211_hw *hw, u8 value) {
 	mt7996_mcu_set_rts_thresh(phy, 500);
 }
 
-static int mt7996_vendor_wireless_ctrl(struct wiphy *wiphy, struct wireless_dev *wdev,
-				       const void *data, int data_len)
-{
-	struct ieee80211_hw *hw = wiphy_to_ieee80211_hw(wiphy);
-	struct nlattr *tb[NUM_MTK_VENDOR_ATTRS_WIRELESS_CTRL];
-	int err;
-	u8 val8;
-
-	err = nla_parse(tb, MTK_VENDOR_ATTR_WIRELESS_CTRL_MAX, data, data_len,
-			wireless_ctrl_policy, NULL);
-	if (err)
-		return err;
-
-	if (tb[MTK_VENDOR_ATTR_WIRELESS_CTRL_CERT]) {
-		val8 = nla_get_u8(tb[MTK_VENDOR_ATTR_WIRELESS_CTRL_CERT]);
-		mt7996_set_wireless_cert(hw, val8);
-	} else if (tb[MTK_VENDOR_ATTR_WIRELESS_CTRL_RTS_SIGTA]) {
-		val8 = nla_get_u8(tb[MTK_VENDOR_ATTR_WIRELESS_CTRL_RTS_SIGTA]);
-		mt7996_set_wireless_rts_sigta(hw, val8);
-	}
-	return 0;
-}
-
 static int
 mt7996_vendor_wireless_ctrl_dump(struct wiphy *wiphy, struct wireless_dev *wdev,
 				 struct sk_buff *skb, const void *data, int data_len,
@@ -523,6 +518,123 @@ mt7996_vendor_bss_color_ctrl_dump(struct wiphy *wiphy, struct wireless_dev *wdev
 	return len;
 }
 
+static int mt7996_vendor_rfeature_ctrl(struct wiphy *wiphy,
+				       struct wireless_dev *wdev,
+				       const void *data,
+				       int data_len)
+{
+	struct ieee80211_hw *hw = wiphy_to_ieee80211_hw(wiphy);
+	struct mt7996_phy *phy = mt7996_hw_phy(hw);
+	struct mt7996_dev *dev = phy->dev;
+	struct nlattr *tb[NUM_MTK_VENDOR_ATTRS_RFEATURE_CTRL];
+	int err;
+	u32 val;
+
+	err = nla_parse(tb, MTK_VENDOR_ATTR_RFEATURE_CTRL_MAX, data, data_len,
+			rfeature_ctrl_policy, NULL);
+	if (err)
+		return err;
+
+	val = CAPI_RFEATURE_CHANGED;
+
+	if (tb[MTK_VENDOR_ATTR_RFEATURE_CTRL_TRIG_TYPE_CFG]) {
+		u8 enable, trig_type;
+		int rem;
+		struct nlattr *cur;
+
+		nla_for_each_nested(cur, tb[MTK_VENDOR_ATTR_RFEATURE_CTRL_TRIG_TYPE_CFG], rem) {
+			switch (nla_type(cur)) {
+			case MTK_VENDOR_ATTR_RFEATURE_CTRL_TRIG_TYPE_EN:
+				enable = nla_get_u8(cur);
+				break;
+			case MTK_VENDOR_ATTR_RFEATURE_CTRL_TRIG_TYPE:
+				trig_type = nla_get_u8(cur);
+				break;
+			default:
+				return -EINVAL;
+			};
+		}
+
+		err = mt7996_mcu_set_rfeature_trig_type(phy, enable, trig_type);
+		if (err)
+			return err;
+	} else if (tb[MTK_VENDOR_ATTR_RFEATURE_CTRL_ACK_PLCY]) {
+		u8 ack_policy;
+
+		ack_policy = nla_get_u8(tb[MTK_VENDOR_ATTR_RFEATURE_CTRL_ACK_PLCY]);
+#define HE_TB_PPDU_ACK 4
+		switch (ack_policy) {
+		case HE_TB_PPDU_ACK:
+			return mt7996_mcu_set_muru_cmd(dev, UNI_CMD_MURU_SET_MUDL_ACK_POLICY,
+						       ack_policy);
+		default:
+			return 0;
+		}
+	}
+
+	return 0;
+}
+
+static int mt7996_vendor_wireless_ctrl(struct wiphy *wiphy,
+				       struct wireless_dev *wdev,
+				       const void *data,
+				       int data_len)
+{
+	struct ieee80211_hw *hw = wiphy_to_ieee80211_hw(wiphy);
+	struct mt7996_phy *phy = mt7996_hw_phy(hw);
+	struct mt7996_dev *dev = phy->dev;
+	struct nlattr *tb[NUM_MTK_VENDOR_ATTRS_WIRELESS_CTRL];
+	int err;
+	u8 val8;
+	u16 val16;
+	u32 val32;
+
+	err = nla_parse(tb, MTK_VENDOR_ATTR_WIRELESS_CTRL_MAX, data, data_len,
+			wireless_ctrl_policy, NULL);
+	if (err)
+		return err;
+
+	val32 = CAPI_WIRELESS_CHANGED;
+
+	if (tb[MTK_VENDOR_ATTR_WIRELESS_CTRL_OFDMA]) {
+		val8 = nla_get_u8(tb[MTK_VENDOR_ATTR_WIRELESS_CTRL_OFDMA]);
+		val32 |= FIELD_PREP(RATE_CFG_MODE, RATE_PARAM_FIXED_OFDMA) |
+			 FIELD_PREP(RATE_CFG_VAL, val8);
+		ieee80211_iterate_active_interfaces_atomic(hw, IEEE80211_IFACE_ITER_RESUME_ALL,
+			mt7996_set_wireless_vif, &val32);
+		if (val8 == 3) /* DL20and80 */
+			mt7996_mcu_set_muru_cmd(dev, UNI_CMD_MURU_SET_20M_DYN_ALGO, 1);
+	} else if (tb[MTK_VENDOR_ATTR_WIRELESS_CTRL_BA_BUFFER_SIZE]) {
+		val16 = nla_get_u16(tb[MTK_VENDOR_ATTR_WIRELESS_CTRL_BA_BUFFER_SIZE]);
+		hw->max_tx_aggregation_subframes = val16;
+		hw->max_rx_aggregation_subframes = val16;
+	} else if (tb[MTK_VENDOR_ATTR_WIRELESS_CTRL_PPDU_TX_TYPE]) {
+		val8 = nla_get_u8(tb[MTK_VENDOR_ATTR_WIRELESS_CTRL_PPDU_TX_TYPE]);
+		mt7996_mcu_set_ppdu_tx_type(phy, val8);
+	} else if (tb[MTK_VENDOR_ATTR_WIRELESS_CTRL_NUSERS_OFDMA]) {
+		val8 = nla_get_u8(tb[MTK_VENDOR_ATTR_WIRELESS_CTRL_NUSERS_OFDMA]);
+		if (phy->muru_onoff & OFDMA_UL)
+			mt7996_mcu_set_nusers_ofdma(phy, MURU_UL_USER_CNT, val8);
+		else
+			mt7996_mcu_set_nusers_ofdma(phy, MURU_DL_USER_CNT, val8);
+	} else if (tb[MTK_VENDOR_ATTR_WIRELESS_CTRL_MIMO]) {
+		val8 = nla_get_u8(tb[MTK_VENDOR_ATTR_WIRELESS_CTRL_MIMO]);
+		val32 |= FIELD_PREP(RATE_CFG_MODE, RATE_PARAM_FIXED_MIMO) |
+			 FIELD_PREP(RATE_CFG_VAL, val8);
+		ieee80211_iterate_active_interfaces_atomic(hw, IEEE80211_IFACE_ITER_RESUME_ALL,
+			mt7996_set_wireless_vif, &val32);
+	} else if (tb[MTK_VENDOR_ATTR_WIRELESS_CTRL_CERT]) {
+		val8 = nla_get_u8(tb[MTK_VENDOR_ATTR_WIRELESS_CTRL_CERT]);
+		mt7996_mcu_set_cert(phy, val8);
+		mt7996_mcu_set_bypass_smthint(phy, val8);
+	} else if (tb[MTK_VENDOR_ATTR_WIRELESS_CTRL_AMSDU]) {
+		val8 = nla_get_u8(tb[MTK_VENDOR_ATTR_WIRELESS_CTRL_AMSDU]);
+		mt7996_set_wireless_amsdu(hw, val8);
+	}
+
+	return 0;
+}
+
 static const struct wiphy_vendor_command mt7996_vendor_commands[] = {
 	{
 		.info = {
@@ -571,6 +683,17 @@ static const struct wiphy_vendor_command mt7996_vendor_commands[] = {
 		.policy = bss_color_ctrl_policy,
 		.maxattr = MTK_VENDOR_ATTR_BSS_COLOR_CTRL_MAX,
 	},
+	{
+		.info = {
+			.vendor_id = MTK_NL80211_VENDOR_ID,
+			.subcmd = MTK_NL80211_VENDOR_SUBCMD_RFEATURE_CTRL,
+		},
+		.flags = WIPHY_VENDOR_CMD_NEED_NETDEV |
+			WIPHY_VENDOR_CMD_NEED_RUNNING,
+		.doit = mt7996_vendor_rfeature_ctrl,
+		.policy = rfeature_ctrl_policy,
+		.maxattr = MTK_VENDOR_ATTR_RFEATURE_CTRL_MAX,
+	},
 };
 
 void mt7996_vendor_register(struct mt7996_phy *phy)
-- 
2.18.0

