Linux wireless drivers development
 help / color / mirror / Atom feed
* [PATCH 0/6] wifi: rtw88: extend 88xxA support
@ 2026-10-29  5:52 Ho Jie Feng
  2026-10-29  5:52 ` [PATCH 1/6] wifi: rtw88: 8812a, 8814a: enable STBC for HT transmissions Ho Jie Feng
                   ` (5 more replies)
  0 siblings, 6 replies; 10+ messages in thread
From: Ho Jie Feng @ 2026-10-29  5:52 UTC (permalink / raw)
  To: Ping-Ke Shih; +Cc: linux-wireless, Ho Jie Feng

This series enables HT STBC transmission, VHT SU beamformed reception,
carrier-frequency-offset correction and adaptivity on supported
88xxA devices.

Tested on RTL8812AU together with the driver-frame series.
TX went from 136.0 to 144.2.
RX went from 215.5 to 249.2.

Ho Jie Feng (6):
  wifi: rtw88: 8812a, 8814a: enable STBC for HT transmissions
  wifi: rtw88: 88xxa: receive VHT SU beamformed transmissions
  wifi: rtw88: prepare frequency-offset correction for more chips
  wifi: rtw88: 88xxa: correct carrier-frequency offset
  wifi: rtw88: 88xxa: adapt channel sensing to receiver gain
  wifi: rtw88: 8812a, 8821a: respect peer A-MPDU size limits

 drivers/net/wireless/realtek/rtw88/bf.h       |   2 +
 drivers/net/wireless/realtek/rtw88/main.c     |  14 +-
 drivers/net/wireless/realtek/rtw88/main.h     |   1 +
 drivers/net/wireless/realtek/rtw88/phy.c      | 109 +++++++-
 drivers/net/wireless/realtek/rtw88/phy.h      |   2 +
 drivers/net/wireless/realtek/rtw88/reg.h      |   1 +
 drivers/net/wireless/realtek/rtw88/rtw8812a.c |  25 +-
 drivers/net/wireless/realtek/rtw88/rtw8814a.c |  68 ++++-
 drivers/net/wireless/realtek/rtw88/rtw8821a.c |  24 +-
 drivers/net/wireless/realtek/rtw88/rtw8822c.c | 100 +-------
 drivers/net/wireless/realtek/rtw88/rtw8822c.h |   3 -
 drivers/net/wireless/realtek/rtw88/rtw88xxa.c | 232 +++++++++++++++++-
 drivers/net/wireless/realtek/rtw88/rtw88xxa.h |   8 +
 13 files changed, 465 insertions(+), 124 deletions(-)


base-commit: 9b0be45d4d52fd223994e151f169743a97abd290
-- 
2.55.0


^ permalink raw reply	[flat|nested] 10+ messages in thread

* [PATCH 1/6] wifi: rtw88: 8812a, 8814a: enable STBC for HT transmissions
  2026-10-29  5:52 [PATCH 0/6] wifi: rtw88: extend 88xxA support Ho Jie Feng
@ 2026-10-29  5:52 ` Ho Jie Feng
  2026-10-29  5:52 ` [PATCH 2/6] wifi: rtw88: 88xxa: receive VHT SU beamformed transmissions Ho Jie Feng
                   ` (4 subsequent siblings)
  5 siblings, 0 replies; 10+ messages in thread
From: Ho Jie Feng @ 2026-10-29  5:52 UTC (permalink / raw)
  To: Ping-Ke Shih; +Cc: linux-wireless, Ho Jie Feng, linux-kernel

8812a and 8814a support STBC, enable it.

Signed-off-by: Ho Jie Feng <hjf3108@gmail.com>
---
 drivers/net/wireless/realtek/rtw88/main.c     | 2 +-
 drivers/net/wireless/realtek/rtw88/rtw8812a.c | 1 +
 drivers/net/wireless/realtek/rtw88/rtw8814a.c | 1 +
 3 files changed, 3 insertions(+), 1 deletion(-)

diff --git a/drivers/net/wireless/realtek/rtw88/main.c b/drivers/net/wireless/realtek/rtw88/main.c
index 0f23498b5c96e..f7d7b66844862 100644
--- a/drivers/net/wireless/realtek/rtw88/main.c
+++ b/drivers/net/wireless/realtek/rtw88/main.c
@@ -1585,7 +1585,7 @@ static void rtw_init_ht_cap(struct rtw_dev *rtwdev,
 
 	if (rtw_chip_has_rx_ldpc(rtwdev))
 		ht_cap->cap |= IEEE80211_HT_CAP_LDPC_CODING;
-	if (rtw_chip_has_tx_stbc(rtwdev))
+	if (rtw_chip_has_tx_stbc(rtwdev) && rtwdev->hal.rf_path_num > 1)
 		ht_cap->cap |= IEEE80211_HT_CAP_TX_STBC;
 
 	if (efuse->hw_cap.bw & BIT(RTW_CHANNEL_WIDTH_40))
diff --git a/drivers/net/wireless/realtek/rtw88/rtw8812a.c b/drivers/net/wireless/realtek/rtw88/rtw8812a.c
index 781b66ccde675..55f1d3d3240be 100644
--- a/drivers/net/wireless/realtek/rtw88/rtw8812a.c
+++ b/drivers/net/wireless/realtek/rtw88/rtw8812a.c
@@ -1077,6 +1077,7 @@ const struct rtw_chip_info rtw8812a_hw_spec = {
 	.rfe_defs = rtw8812a_rfe_defs,
 	.rfe_defs_size = ARRAY_SIZE(rtw8812a_rfe_defs),
 	.rx_ldpc = false,
+	.tx_stbc = true,
 	.amsdu_in_ampdu = true,
 	.hw_feature_report = false,
 	.c2h_ra_report_size = 4,
diff --git a/drivers/net/wireless/realtek/rtw88/rtw8814a.c b/drivers/net/wireless/realtek/rtw88/rtw8814a.c
index fa02aa299b5e9..5bb55606f14a1 100644
--- a/drivers/net/wireless/realtek/rtw88/rtw8814a.c
+++ b/drivers/net/wireless/realtek/rtw88/rtw8814a.c
@@ -2200,6 +2200,7 @@ const struct rtw_chip_info rtw8814a_hw_spec = {
 	.txgi_factor = 1,
 	.is_pwr_by_rate_dec = true,
 	.rx_ldpc = true,
+	.tx_stbc = true,
 	.max_power_index = 0x3f,
 	.ampdu_density = IEEE80211_HT_MPDU_DENSITY_2,
 	.amsdu_in_ampdu = false, /* RX speed is better without AMSDU */
-- 
2.55.0


^ permalink raw reply related	[flat|nested] 10+ messages in thread

* [PATCH 2/6] wifi: rtw88: 88xxa: receive VHT SU beamformed transmissions
  2026-10-29  5:52 [PATCH 0/6] wifi: rtw88: extend 88xxA support Ho Jie Feng
  2026-10-29  5:52 ` [PATCH 1/6] wifi: rtw88: 8812a, 8814a: enable STBC for HT transmissions Ho Jie Feng
@ 2026-10-29  5:52 ` Ho Jie Feng
  2026-10-29 12:33   ` Bitterblue Smith
  2026-10-29  5:52 ` [PATCH 3/6] wifi: rtw88: prepare frequency-offset correction for more chips Ho Jie Feng
                   ` (3 subsequent siblings)
  5 siblings, 1 reply; 10+ messages in thread
From: Ho Jie Feng @ 2026-10-29  5:52 UTC (permalink / raw)
  To: Ping-Ke Shih; +Cc: linux-wireless, Ho Jie Feng, linux-kernel

Implement beamformee support for 88xxa since they have
support for it.

Signed-off-by: Ho Jie Feng <hjf3108@gmail.com>
---
 drivers/net/wireless/realtek/rtw88/bf.h       |  2 +
 drivers/net/wireless/realtek/rtw88/main.c     | 12 ++--
 drivers/net/wireless/realtek/rtw88/reg.h      |  1 +
 drivers/net/wireless/realtek/rtw88/rtw8812a.c |  3 +-
 drivers/net/wireless/realtek/rtw88/rtw8814a.c | 36 ++++++++++-
 drivers/net/wireless/realtek/rtw88/rtw8821a.c |  3 +-
 drivers/net/wireless/realtek/rtw88/rtw88xxa.c | 62 +++++++++++++++++++
 drivers/net/wireless/realtek/rtw88/rtw88xxa.h |  2 +
 8 files changed, 112 insertions(+), 9 deletions(-)

diff --git a/drivers/net/wireless/realtek/rtw88/bf.h b/drivers/net/wireless/realtek/rtw88/bf.h
index a5d3010e6be6e..0aa78fb6a7e64 100644
--- a/drivers/net/wireless/realtek/rtw88/bf.h
+++ b/drivers/net/wireless/realtek/rtw88/bf.h
@@ -12,6 +12,8 @@
 #define REG_ASSOCIATED_BFMER0_INFO	0x06E4
 #define REG_ASSOCIATED_BFMER1_INFO	0x06EC
 #define REG_TX_CSI_RPT_PARAM_BW20	0x06F4
+#define REG_TX_CSI_RPT_PARAM_BW40	0x06F8
+#define REG_TX_CSI_RPT_PARAM_BW80	0x06FC
 #define REG_SND_PTCL_CTRL		0x0718
 #define BIT_DIS_CHK_VHTSIGB_CRC		BIT(6)
 #define BIT_DIS_CHK_VHTSIGA_CRC		BIT(5)
diff --git a/drivers/net/wireless/realtek/rtw88/main.c b/drivers/net/wireless/realtek/rtw88/main.c
index f7d7b66844862..0a1b5575b7299 100644
--- a/drivers/net/wireless/realtek/rtw88/main.c
+++ b/drivers/net/wireless/realtek/rtw88/main.c
@@ -1605,6 +1605,7 @@ static void rtw_init_ht_cap(struct rtw_dev *rtwdev,
 static void rtw_init_vht_cap(struct rtw_dev *rtwdev,
 			     struct ieee80211_sta_vht_cap *vht_cap)
 {
+	const struct rtw_chip_info *chip = rtwdev->chip;
 	struct rtw_efuse *efuse = &rtwdev->efuse;
 	u16 mcs_map = 0;
 	__le16 highest;
@@ -1623,10 +1624,13 @@ static void rtw_init_vht_cap(struct rtw_dev *rtwdev,
 		       0;
 	if (rtwdev->hal.rf_path_num > 1)
 		vht_cap->cap |= IEEE80211_VHT_CAP_TXSTBC;
-	vht_cap->cap |= IEEE80211_VHT_CAP_MU_BEAMFORMEE_CAPABLE |
-			IEEE80211_VHT_CAP_SU_BEAMFORMEE_CAPABLE;
-	vht_cap->cap |= (rtwdev->hal.bfee_sts_cap <<
-			IEEE80211_VHT_CAP_BEAMFORMEE_STS_SHIFT);
+	if (rtw_bf_support && chip->bfer_su_max_num) {
+		vht_cap->cap |= IEEE80211_VHT_CAP_SU_BEAMFORMEE_CAPABLE;
+		vht_cap->cap |= rtwdev->hal.bfee_sts_cap <<
+				IEEE80211_VHT_CAP_BEAMFORMEE_STS_SHIFT;
+	}
+	if (rtw_bf_support && chip->bfer_mu_max_num)
+		vht_cap->cap |= IEEE80211_VHT_CAP_MU_BEAMFORMEE_CAPABLE;
 
 	if (rtw_chip_has_rx_ldpc(rtwdev))
 		vht_cap->cap |= IEEE80211_VHT_CAP_RXLDPC;
diff --git a/drivers/net/wireless/realtek/rtw88/reg.h b/drivers/net/wireless/realtek/rtw88/reg.h
index afcdb66fdb9a1..f8ec1e2931f63 100644
--- a/drivers/net/wireless/realtek/rtw88/reg.h
+++ b/drivers/net/wireless/realtek/rtw88/reg.h
@@ -671,6 +671,7 @@
 #define REG_IQK_COM96		0x0984
 
 #define REG_FAS			0x09a4
+#define REG_CSI_CONTENT_VALUE	0x09b4
 #define REG_RXSB		0x0a00
 #define BIT_RXSB_ANA_DIV	BIT(15)
 #define REG_CCK_RX		0x0a04
diff --git a/drivers/net/wireless/realtek/rtw88/rtw8812a.c b/drivers/net/wireless/realtek/rtw88/rtw8812a.c
index 55f1d3d3240be..5d58dff4fcee7 100644
--- a/drivers/net/wireless/realtek/rtw88/rtw8812a.c
+++ b/drivers/net/wireless/realtek/rtw88/rtw8812a.c
@@ -931,7 +931,7 @@ static const struct rtw_chip_ops rtw8812a_ops = {
 	.phy_calibration	= rtw8812a_phy_calibration,
 	.cck_pd_set		= rtw_phy_cck_pd_set,
 	.pwr_track		= rtw8812a_pwr_track,
-	.config_bfee		= NULL,
+	.config_bfee		= rtw88xxa_bf_config_bfee,
 	.set_gid_table		= NULL,
 	.cfg_csi_rate		= NULL,
 	.led_set		= rtw8812a_led_set,
@@ -1084,6 +1084,7 @@ const struct rtw_chip_info rtw8812a_hw_spec = {
 	.old_datarate_fb_limit = true,
 	.usb_tx_agg_desc_num = 1,
 	.iqk_threshold = 8,
+	.bfer_su_max_num = 2,
 	.ampdu_density = IEEE80211_HT_MPDU_DENSITY_16,
 	.max_scan_ie_len = IEEE80211_MAX_DATA_LEN,
 
diff --git a/drivers/net/wireless/realtek/rtw88/rtw8814a.c b/drivers/net/wireless/realtek/rtw88/rtw8814a.c
index 5bb55606f14a1..2db05a83766d6 100644
--- a/drivers/net/wireless/realtek/rtw88/rtw8814a.c
+++ b/drivers/net/wireless/realtek/rtw88/rtw8814a.c
@@ -4,6 +4,7 @@
 
 #include <linux/usb.h>
 #include "main.h"
+#include "bf.h"
 #include "coex.h"
 #include "tx.h"
 #include "phy.h"
@@ -115,10 +116,13 @@ static void rtw8814a_init_hwcap(struct rtw_dev *rtwdev)
 			   BIT(RTW_CHANNEL_WIDTH_80);
 	efuse->hw_cap.ptcl = EFUSE_HW_CAP_PTCL_VHT;
 
-	if (hal->rf_type == RF_3T3R)
+	if (hal->rf_type == RF_3T3R) {
 		efuse->hw_cap.nss = 3;
-	else
+		hal->bfee_sts_cap = 2;
+	} else {
 		efuse->hw_cap.nss = 2;
+		hal->bfee_sts_cap = 1;
+	}
 
 	rtw_dbg(rtwdev, RTW_DBG_EFUSE,
 		"hw cap: hci=0x%02x, bw=0x%02x, ptcl=0x%02x, ant_num=%d, nss=%d\n",
@@ -2039,6 +2043,31 @@ static void rtw8814a_led_set(struct led_classdev *led,
 	rtw_write32(rtwdev, REG_GPIO_PIN_CTRL_2, led_gpio_cfg);
 }
 
+#define RTW_NDP_RX_STANDBY_TIME_8814A	0x40
+#define RTW_SND_CTRL_SOUNDING_8814A	0xDB
+
+static void rtw8814a_bf_enable_bfee_su(struct rtw_dev *rtwdev,
+				       struct rtw_vif *vif,
+				       struct rtw_bfee *bfee)
+{
+	rtw_bf_enable_bfee_su(rtwdev, vif, bfee);
+
+	rtw_write16(rtwdev, REG_SND_PTCL_CTRL + 1, 0x0202);
+	rtw_write8(rtwdev, REG_SND_PTCL_CTRL, RTW_SND_CTRL_SOUNDING_8814A);
+	rtw_write8(rtwdev, REG_SND_PTCL_CTRL + 3,
+		   RTW_NDP_RX_STANDBY_TIME_8814A);
+}
+
+static void rtw8814a_bf_config_bfee(struct rtw_dev *rtwdev,
+				    struct rtw_vif *vif,
+				    struct rtw_bfee *bfee, bool enable)
+{
+	if (enable)
+		rtw8814a_bf_enable_bfee_su(rtwdev, vif, bfee);
+	else
+		rtw_bf_remove_bfee_su(rtwdev, bfee);
+}
+
 static void rtw8814a_fill_txdesc_checksum(struct rtw_dev *rtwdev,
 					  struct rtw_tx_pkt_info *pkt_info,
 					  u8 *txdesc)
@@ -2068,7 +2097,7 @@ static const struct rtw_chip_ops rtw8814a_ops = {
 	.phy_calibration	= rtw8814a_phy_calibration,
 	.cck_pd_set		= rtw8814a_phy_cck_pd_set,
 	.pwr_track		= rtw8814a_pwr_track,
-	.config_bfee		= NULL,
+	.config_bfee		= rtw8814a_bf_config_bfee,
 	.set_gid_table		= NULL,
 	.cfg_csi_rate		= NULL,
 	.led_set		= rtw8814a_led_set,
@@ -2231,6 +2260,7 @@ const struct rtw_chip_info rtw8814a_hw_spec = {
 	.rfe_defs = rtw8814a_rfe_defs,
 	.rfe_defs_size = ARRAY_SIZE(rtw8814a_rfe_defs),
 	.iqk_threshold = 8,
+	.bfer_su_max_num = 2,
 	.max_scan_ie_len = IEEE80211_MAX_DATA_LEN,
 
 	.coex_para_ver = 0,
diff --git a/drivers/net/wireless/realtek/rtw88/rtw8821a.c b/drivers/net/wireless/realtek/rtw88/rtw8821a.c
index 6096d3b8e41be..6d106ae70d914 100644
--- a/drivers/net/wireless/realtek/rtw88/rtw8821a.c
+++ b/drivers/net/wireless/realtek/rtw88/rtw8821a.c
@@ -877,7 +877,7 @@ static const struct rtw_chip_ops rtw8821a_ops = {
 	.phy_calibration	= rtw8821a_phy_calibration,
 	.cck_pd_set		= rtw_phy_cck_pd_set,
 	.pwr_track		= rtw8821a_pwr_track,
-	.config_bfee		= NULL,
+	.config_bfee		= rtw88xxa_bf_config_bfee,
 	.set_gid_table		= NULL,
 	.cfg_csi_rate		= NULL,
 	.led_set		= rtw8821a_led_set,
@@ -1188,6 +1188,7 @@ const struct rtw_chip_info rtw8821a_hw_spec = {
 	.old_datarate_fb_limit = true,
 	.usb_tx_agg_desc_num = 6,
 	.iqk_threshold = 8,
+	.bfer_su_max_num = 2,
 	.ampdu_density = IEEE80211_HT_MPDU_DENSITY_16,
 	.max_scan_ie_len = IEEE80211_MAX_DATA_LEN,
 
diff --git a/drivers/net/wireless/realtek/rtw88/rtw88xxa.c b/drivers/net/wireless/realtek/rtw88/rtw88xxa.c
index c298fa0fb2a6b..d3dd0ebfb4024 100644
--- a/drivers/net/wireless/realtek/rtw88/rtw88xxa.c
+++ b/drivers/net/wireless/realtek/rtw88/rtw88xxa.c
@@ -138,6 +138,7 @@ static void rtw88xxa_read_usb_type(struct rtw_dev *rtwdev)
 		efuse->hw_cap.nss = 1;
 	else
 		efuse->hw_cap.nss = 2;
+	hal->bfee_sts_cap = 1;
 
 	if (rtwdev->chip->id == RTW_CHIP_TYPE_8821A)
 		goto print_hw_cap;
@@ -197,6 +198,67 @@ static void rtw88xxa_read_usb_type(struct rtw_dev *rtwdev)
 		efuse->hw_cap.ant_num, efuse->hw_cap.nss);
 }
 
+#define RTW_NDP_RX_STANDBY_TIME_88XXA	0x50
+#define RTW_SND_CTRL_REMOVE_88XXA	0xC8
+#define RTW_SND_CTRL_SOUNDING_88XXA	0xCB
+
+void rtw88xxa_bf_config_bfee(struct rtw_dev *rtwdev, struct rtw_vif *vif,
+			     struct rtw_bfee *bfee, bool enable)
+{
+	static const u32 csi_rpt_regs[] = {
+		REG_TX_CSI_RPT_PARAM_BW20,
+		REG_TX_CSI_RPT_PARAM_BW40,
+		REG_TX_CSI_RPT_PARAM_BW80,
+	};
+	struct rtw_bf_info *bf_info = &rtwdev->bf_info;
+	u8 slot = bfee->su_reg_index;
+	u32 addr_bfer_info;
+	u32 csi_param;
+	int i;
+
+	if (bfee->role != RTW_BFEE_SU || slot >= 2)
+		return;
+
+	addr_bfer_info = slot ? REG_ASSOCIATED_BFMER1_INFO :
+				      REG_ASSOCIATED_BFMER0_INFO;
+
+	rtw_dbg(rtwdev, RTW_DBG_BF, "chip %d SU beamformee %s slot %u\n",
+		rtwdev->chip->id, enable ? "enable" : "disable", slot);
+
+	if (!enable) {
+		rtw_write32(rtwdev, addr_bfer_info, 0);
+		rtw_write16(rtwdev, addr_bfer_info + 4, 0);
+
+		clear_bit(slot, bf_info->bfer_su_reg_maping);
+		bfee->su_reg_index = 0xff;
+
+		if (!bf_info->bfer_su_cnt)
+			rtw_write8(rtwdev, REG_SND_PTCL_CTRL,
+				   RTW_SND_CTRL_REMOVE_88XXA);
+		return;
+	}
+
+	if (hweight8(rtwdev->hal.antenna_rx) == 2) {
+		rtw_write32(rtwdev, REG_CSI_CONTENT_VALUE, 0);
+		csi_param = 0x01090109;
+	} else {
+		rtw_write32(rtwdev, REG_CSI_CONTENT_VALUE, 0x01081008);
+		csi_param = 0x01080108;
+	}
+
+	rtw_write8(rtwdev, REG_SND_PTCL_CTRL,
+		   RTW_SND_CTRL_SOUNDING_88XXA);
+	for (i = 0; i < ETH_ALEN; i++)
+		rtw_write8(rtwdev, addr_bfer_info + i, bfee->mac_addr[i]);
+
+	for (i = 0; i < ARRAY_SIZE(csi_rpt_regs); i++)
+		rtw_write32(rtwdev, csi_rpt_regs[i], csi_param);
+
+	rtw_write8(rtwdev, REG_SND_PTCL_CTRL + 3,
+		   RTW_NDP_RX_STANDBY_TIME_88XXA);
+}
+EXPORT_SYMBOL(rtw88xxa_bf_config_bfee);
+
 int rtw88xxa_read_efuse(struct rtw_dev *rtwdev, u8 *log_map)
 {
 	const struct rtw_chip_info *chip = rtwdev->chip;
diff --git a/drivers/net/wireless/realtek/rtw88/rtw88xxa.h b/drivers/net/wireless/realtek/rtw88/rtw88xxa.h
index d9c8db58909ca..0aaf7aa4aafdd 100644
--- a/drivers/net/wireless/realtek/rtw88/rtw88xxa.h
+++ b/drivers/net/wireless/realtek/rtw88/rtw88xxa.h
@@ -170,5 +170,7 @@ bool rtw88xxa_iqk_finish(int average, int threshold,
 void rtw88xxa_phy_pwrtrack(struct rtw_dev *rtwdev,
 			   void (*do_lck)(struct rtw_dev *rtwdev),
 			   void (*do_iqk)(struct rtw_dev *rtwdev));
+void rtw88xxa_bf_config_bfee(struct rtw_dev *rtwdev, struct rtw_vif *vif,
+			     struct rtw_bfee *bfee, bool enable);
 
 #endif
-- 
2.55.0


^ permalink raw reply related	[flat|nested] 10+ messages in thread

* [PATCH 3/6] wifi: rtw88: prepare frequency-offset correction for more chips
  2026-10-29  5:52 [PATCH 0/6] wifi: rtw88: extend 88xxA support Ho Jie Feng
  2026-10-29  5:52 ` [PATCH 1/6] wifi: rtw88: 8812a, 8814a: enable STBC for HT transmissions Ho Jie Feng
  2026-10-29  5:52 ` [PATCH 2/6] wifi: rtw88: 88xxa: receive VHT SU beamformed transmissions Ho Jie Feng
@ 2026-10-29  5:52 ` Ho Jie Feng
  2026-10-29  5:52 ` [PATCH 4/6] wifi: rtw88: 88xxa: correct carrier-frequency offset Ho Jie Feng
                   ` (2 subsequent siblings)
  5 siblings, 0 replies; 10+ messages in thread
From: Ho Jie Feng @ 2026-10-29  5:52 UTC (permalink / raw)
  To: Ping-Ke Shih; +Cc: linux-wireless, Ho Jie Feng, linux-kernel

Promote chip specific functions into the common core for cfo tracking.
These become helpers which can be used by the other drivers.

Signed-off-by: Ho Jie Feng <hjf3108@gmail.com>
---
 drivers/net/wireless/realtek/rtw88/phy.c      | 106 ++++++++++++++++++
 drivers/net/wireless/realtek/rtw88/phy.h      |   2 +
 drivers/net/wireless/realtek/rtw88/rtw8822c.c | 100 +----------------
 drivers/net/wireless/realtek/rtw88/rtw8822c.h |   3 -
 4 files changed, 113 insertions(+), 98 deletions(-)

diff --git a/drivers/net/wireless/realtek/rtw88/phy.c b/drivers/net/wireless/realtek/rtw88/phy.c
index e2515a662bb30..d0c1fb65be956 100644
--- a/drivers/net/wireless/realtek/rtw88/phy.c
+++ b/drivers/net/wireless/realtek/rtw88/phy.c
@@ -720,6 +720,112 @@ void rtw_phy_parsing_cfo(struct rtw_dev *rtwdev,
 }
 EXPORT_SYMBOL(rtw_phy_parsing_cfo);
 
+#define CFO_TRK_ENABLE_TH	20
+#define CFO_TRK_STOP_TH		10
+#define CFO_TRK_ADJ_TH		10
+
+static u8 rtw_phy_cfo_tracking_reset(struct rtw_dev *rtwdev)
+{
+	struct rtw_dm_info *dm_info = &rtwdev->dm_info;
+	struct rtw_cfo_track *cfo = &dm_info->cfo_track;
+
+	cfo->is_adjust = true;
+
+	if (cfo->crystal_cap > rtwdev->efuse.crystal_cap)
+		return cfo->crystal_cap - 1;
+	else if (cfo->crystal_cap < rtwdev->efuse.crystal_cap)
+		return cfo->crystal_cap + 1;
+
+	return cfo->crystal_cap;
+}
+
+void rtw_phy_cfo_xcap_init(struct rtw_dev *rtwdev)
+{
+	struct rtw_dm_info *dm_info = &rtwdev->dm_info;
+	struct rtw_cfo_track *cfo = &dm_info->cfo_track;
+
+	cfo->crystal_cap = rtwdev->efuse.crystal_cap;
+	cfo->is_adjust = true;
+}
+EXPORT_SYMBOL(rtw_phy_cfo_xcap_init);
+
+#define REPORT_TO_KHZ(val) ({typeof(val) _v = (val); (_v << 1) + (_v >> 1); })
+static s32 rtw_phy_cfo_calc_avg(struct rtw_dev *rtwdev, u8 path_num)
+{
+	struct rtw_dm_info *dm_info = &rtwdev->dm_info;
+	struct rtw_cfo_track *cfo = &dm_info->cfo_track;
+	s32 cfo_avg, cfo_path_sum = 0, cfo_rpt_sum;
+	u8 i;
+
+	for (i = 0; i < path_num; i++) {
+		cfo_rpt_sum = REPORT_TO_KHZ(cfo->cfo_tail[i]);
+
+		if (cfo->cfo_cnt[i])
+			cfo_avg = cfo_rpt_sum / cfo->cfo_cnt[i];
+		else
+			cfo_avg = 0;
+
+		cfo_path_sum += cfo_avg;
+	}
+
+	for (i = 0; i < path_num; i++) {
+		cfo->cfo_tail[i] = 0;
+		cfo->cfo_cnt[i] = 0;
+	}
+
+	return cfo_path_sum / path_num;
+}
+
+static void rtw_phy_cfo_need_adjust(struct rtw_dev *rtwdev, s32 cfo_avg)
+{
+	struct rtw_dm_info *dm_info = &rtwdev->dm_info;
+	struct rtw_cfo_track *cfo = &dm_info->cfo_track;
+
+	if (!cfo->is_adjust) {
+		if (abs(cfo_avg) > CFO_TRK_ENABLE_TH)
+			cfo->is_adjust = true;
+	} else {
+		if (abs(cfo_avg) <= CFO_TRK_STOP_TH)
+			cfo->is_adjust = false;
+	}
+}
+
+u8 rtw_phy_cfo_xcap_track(struct rtw_dev *rtwdev, u8 xcap_max, bool bt_hold)
+{
+	struct rtw_dm_info *dm_info = &rtwdev->dm_info;
+	struct rtw_cfo_track *cfo = &dm_info->cfo_track;
+	u8 path_num = rtwdev->hal.rf_path_num;
+	s8 crystal_cap = cfo->crystal_cap;
+	s32 cfo_avg = 0;
+
+	if (rtwdev->sta_cnt != 1)
+		return rtw_phy_cfo_tracking_reset(rtwdev);
+
+	if (cfo->packet_count == cfo->packet_count_pre)
+		return cfo->crystal_cap;
+
+	cfo->packet_count_pre = cfo->packet_count;
+	cfo_avg = rtw_phy_cfo_calc_avg(rtwdev, path_num);
+	rtw_phy_cfo_need_adjust(rtwdev, cfo_avg);
+
+	if (bt_hold) {
+		cfo->is_adjust = false;
+		return rtwdev->efuse.crystal_cap;
+	}
+
+	if (cfo->is_adjust) {
+		if (cfo_avg > CFO_TRK_ADJ_TH)
+			crystal_cap++;
+		else if (cfo_avg < -CFO_TRK_ADJ_TH)
+			crystal_cap--;
+
+		crystal_cap = clamp_t(s8, crystal_cap, 0, xcap_max);
+	}
+
+	return (u8)crystal_cap;
+}
+EXPORT_SYMBOL(rtw_phy_cfo_xcap_track);
+
 static void rtw_phy_cfo_track(struct rtw_dev *rtwdev)
 {
 	const struct rtw_chip_info *chip = rtwdev->chip;
diff --git a/drivers/net/wireless/realtek/rtw88/phy.h b/drivers/net/wireless/realtek/rtw88/phy.h
index 8a843aac7350a..b551cb860d625 100644
--- a/drivers/net/wireless/realtek/rtw88/phy.h
+++ b/drivers/net/wireless/realtek/rtw88/phy.h
@@ -68,6 +68,8 @@ void rtw_phy_adaptivity_set_mode(struct rtw_dev *rtwdev);
 void rtw_phy_cck_pd_set(struct rtw_dev *rtwdev, u8 new_lvl);
 void rtw_phy_parsing_cfo(struct rtw_dev *rtwdev,
 			 struct rtw_rx_pkt_stat *pkt_stat);
+void rtw_phy_cfo_xcap_init(struct rtw_dev *rtwdev);
+u8 rtw_phy_cfo_xcap_track(struct rtw_dev *rtwdev, u8 xcap_max, bool bt_hold);
 void rtw_phy_tx_path_diversity(struct rtw_dev *rtwdev);
 
 struct rtw_txpwr_lmt_cfg_pair {
diff --git a/drivers/net/wireless/realtek/rtw88/rtw8822c.c b/drivers/net/wireless/realtek/rtw88/rtw8822c.c
index 32d9709f2c243..54f012a238391 100644
--- a/drivers/net/wireless/realtek/rtw88/rtw8822c.c
+++ b/drivers/net/wireless/realtek/rtw88/rtw8822c.c
@@ -4243,103 +4243,13 @@ static void rtw8822c_set_crystal_cap(struct rtw_dev *rtwdev, u8 crystal_cap)
 	rtw8822c_set_crystal_cap_reg(rtwdev, crystal_cap);
 }
 
-static void rtw8822c_cfo_tracking_reset(struct rtw_dev *rtwdev)
-{
-	struct rtw_dm_info *dm_info = &rtwdev->dm_info;
-	struct rtw_cfo_track *cfo = &dm_info->cfo_track;
-
-	cfo->is_adjust = true;
-
-	if (cfo->crystal_cap > rtwdev->efuse.crystal_cap)
-		rtw8822c_set_crystal_cap(rtwdev, cfo->crystal_cap - 1);
-	else if (cfo->crystal_cap < rtwdev->efuse.crystal_cap)
-		rtw8822c_set_crystal_cap(rtwdev, cfo->crystal_cap + 1);
-}
-
-static void rtw8822c_cfo_init(struct rtw_dev *rtwdev)
-{
-	struct rtw_dm_info *dm_info = &rtwdev->dm_info;
-	struct rtw_cfo_track *cfo = &dm_info->cfo_track;
-
-	cfo->crystal_cap = rtwdev->efuse.crystal_cap;
-	cfo->is_adjust = true;
-}
-
-#define REPORT_TO_KHZ(val) ({typeof(val) _v = (val); (_v << 1) + (_v >> 1); })
-static s32 rtw8822c_cfo_calc_avg(struct rtw_dev *rtwdev, u8 path_num)
-{
-	struct rtw_dm_info *dm_info = &rtwdev->dm_info;
-	struct rtw_cfo_track *cfo = &dm_info->cfo_track;
-	s32 cfo_avg, cfo_path_sum = 0, cfo_rpt_sum;
-	u8 i;
-
-	for (i = 0; i < path_num; i++) {
-		cfo_rpt_sum = REPORT_TO_KHZ(cfo->cfo_tail[i]);
-
-		if (cfo->cfo_cnt[i])
-			cfo_avg = cfo_rpt_sum / cfo->cfo_cnt[i];
-		else
-			cfo_avg = 0;
-
-		cfo_path_sum += cfo_avg;
-	}
-
-	for (i = 0; i < path_num; i++) {
-		cfo->cfo_tail[i] = 0;
-		cfo->cfo_cnt[i] = 0;
-	}
-
-	return cfo_path_sum / path_num;
-}
-
-static void rtw8822c_cfo_need_adjust(struct rtw_dev *rtwdev, s32 cfo_avg)
-{
-	struct rtw_dm_info *dm_info = &rtwdev->dm_info;
-	struct rtw_cfo_track *cfo = &dm_info->cfo_track;
-
-	if (!cfo->is_adjust) {
-		if (abs(cfo_avg) > CFO_TRK_ENABLE_TH)
-			cfo->is_adjust = true;
-	} else {
-		if (abs(cfo_avg) <= CFO_TRK_STOP_TH)
-			cfo->is_adjust = false;
-	}
-
-	if (!rtw_coex_disabled(rtwdev)) {
-		cfo->is_adjust = false;
-		rtw8822c_set_crystal_cap(rtwdev, rtwdev->efuse.crystal_cap);
-	}
-}
-
 static void rtw8822c_cfo_track(struct rtw_dev *rtwdev)
 {
-	struct rtw_dm_info *dm_info = &rtwdev->dm_info;
-	struct rtw_cfo_track *cfo = &dm_info->cfo_track;
-	u8 path_num = rtwdev->hal.rf_path_num;
-	s8 crystal_cap = cfo->crystal_cap;
-	s32 cfo_avg = 0;
-
-	if (rtwdev->sta_cnt != 1) {
-		rtw8822c_cfo_tracking_reset(rtwdev);
-		return;
-	}
-
-	if (cfo->packet_count == cfo->packet_count_pre)
-		return;
-
-	cfo->packet_count_pre = cfo->packet_count;
-	cfo_avg = rtw8822c_cfo_calc_avg(rtwdev, path_num);
-	rtw8822c_cfo_need_adjust(rtwdev, cfo_avg);
-
-	if (cfo->is_adjust) {
-		if (cfo_avg > CFO_TRK_ADJ_TH)
-			crystal_cap++;
-		else if (cfo_avg < -CFO_TRK_ADJ_TH)
-			crystal_cap--;
+	u8 crystal_cap;
 
-		crystal_cap = clamp_t(s8, crystal_cap, 0, XCAP_MASK);
-		rtw8822c_set_crystal_cap(rtwdev, (u8)crystal_cap);
-	}
+	crystal_cap = rtw_phy_cfo_xcap_track(rtwdev, XCAP_MASK,
+					     !rtw_coex_disabled(rtwdev));
+	rtw8822c_set_crystal_cap(rtwdev, crystal_cap);
 }
 
 static const struct rtw_phy_cck_pd_reg
@@ -4985,7 +4895,7 @@ static const struct rtw_chip_ops rtw8822c_ops = {
 	.cfg_csi_rate		= rtw_bf_cfg_csi_rate,
 	.adaptivity_init	= rtw8822c_adaptivity_init,
 	.adaptivity		= rtw8822c_adaptivity,
-	.cfo_init		= rtw8822c_cfo_init,
+	.cfo_init		= rtw_phy_cfo_xcap_init,
 	.cfo_track		= rtw8822c_cfo_track,
 	.config_tx_path		= rtw8822c_config_tx_path,
 	.config_txrx_mode	= rtw8822c_config_trx_mode,
diff --git a/drivers/net/wireless/realtek/rtw88/rtw8822c.h b/drivers/net/wireless/realtek/rtw88/rtw8822c.h
index fc62b67a15f21..c27e4c394a435 100644
--- a/drivers/net/wireless/realtek/rtw88/rtw8822c.h
+++ b/drivers/net/wireless/realtek/rtw88/rtw8822c.h
@@ -181,9 +181,6 @@ const struct rtw_table name ## _tbl = {			\
 #define REG_ANAPARLDO_POW_MAC	0x0029
 #define BIT_LDOE25_PON		BIT(0)
 #define XCAP_MASK		GENMASK(6, 0)
-#define CFO_TRK_ENABLE_TH	20
-#define CFO_TRK_STOP_TH		10
-#define CFO_TRK_ADJ_TH		10
 
 #define REG_TXDFIR0		0x808
 #define REG_DFIRBW		0x810
-- 
2.55.0


^ permalink raw reply related	[flat|nested] 10+ messages in thread

* [PATCH 4/6] wifi: rtw88: 88xxa: correct carrier-frequency offset
  2026-10-29  5:52 [PATCH 0/6] wifi: rtw88: extend 88xxA support Ho Jie Feng
                   ` (2 preceding siblings ...)
  2026-10-29  5:52 ` [PATCH 3/6] wifi: rtw88: prepare frequency-offset correction for more chips Ho Jie Feng
@ 2026-10-29  5:52 ` Ho Jie Feng
  2026-10-29  5:52 ` [PATCH 5/6] wifi: rtw88: 88xxa: adapt channel sensing to receiver gain Ho Jie Feng
  2026-10-29  5:52 ` [PATCH 6/6] wifi: rtw88: 8812a, 8821a: respect peer A-MPDU size limits Ho Jie Feng
  5 siblings, 0 replies; 10+ messages in thread
From: Ho Jie Feng @ 2026-10-29  5:52 UTC (permalink / raw)
  To: Ping-Ke Shih; +Cc: linux-wireless, Ho Jie Feng, linux-kernel

Use the newly promoted helpers to implement support for 88xxa

Signed-off-by: Ho Jie Feng <hjf3108@gmail.com>
---
 drivers/net/wireless/realtek/rtw88/phy.c      |  5 +-
 drivers/net/wireless/realtek/rtw88/rtw8812a.c |  2 +
 drivers/net/wireless/realtek/rtw88/rtw8814a.c | 31 ++++++++++--
 drivers/net/wireless/realtek/rtw88/rtw8821a.c |  2 +
 drivers/net/wireless/realtek/rtw88/rtw88xxa.c | 47 +++++++++++++++----
 drivers/net/wireless/realtek/rtw88/rtw88xxa.h |  1 +
 6 files changed, 73 insertions(+), 15 deletions(-)

diff --git a/drivers/net/wireless/realtek/rtw88/phy.c b/drivers/net/wireless/realtek/rtw88/phy.c
index d0c1fb65be956..69998c0d7c1f8 100644
--- a/drivers/net/wireless/realtek/rtw88/phy.c
+++ b/drivers/net/wireless/realtek/rtw88/phy.c
@@ -687,13 +687,14 @@ static void rtw_phy_parsing_cfo_iter(void *data, u8 *mac,
 	struct rtw_rx_pkt_stat *pkt_stat = iter_data->pkt_stat;
 	struct rtw_dm_info *dm_info = &rtwdev->dm_info;
 	struct rtw_cfo_track *cfo = &dm_info->cfo_track;
+	u8 path_num = min_t(u8, rtwdev->hal.rf_path_num, 2);
 	u8 *bssid = iter_data->bssid;
 	u8 i;
 
 	if (!ether_addr_equal(vif->bss_conf.bssid, bssid))
 		return;
 
-	for (i = 0; i < rtwdev->hal.rf_path_num; i++) {
+	for (i = 0; i < path_num; i++) {
 		cfo->cfo_tail[i] += pkt_stat->cfo_tail[i];
 		cfo->cfo_cnt[i]++;
 	}
@@ -794,7 +795,7 @@ u8 rtw_phy_cfo_xcap_track(struct rtw_dev *rtwdev, u8 xcap_max, bool bt_hold)
 {
 	struct rtw_dm_info *dm_info = &rtwdev->dm_info;
 	struct rtw_cfo_track *cfo = &dm_info->cfo_track;
-	u8 path_num = rtwdev->hal.rf_path_num;
+	u8 path_num = min_t(u8, rtwdev->hal.rf_path_num, 2);
 	s8 crystal_cap = cfo->crystal_cap;
 	s32 cfo_avg = 0;
 
diff --git a/drivers/net/wireless/realtek/rtw88/rtw8812a.c b/drivers/net/wireless/realtek/rtw88/rtw8812a.c
index 5d58dff4fcee7..c8008cf43da9d 100644
--- a/drivers/net/wireless/realtek/rtw88/rtw8812a.c
+++ b/drivers/net/wireless/realtek/rtw88/rtw8812a.c
@@ -943,6 +943,8 @@ static const struct rtw_chip_ops rtw8812a_ops = {
 	.coex_set_rfe_type	= rtw8821a_coex_cfg_rfe_type,
 	.coex_set_wl_tx_power	= rtw8821a_coex_cfg_wl_tx_power,
 	.coex_set_wl_rx_gain	= rtw8821a_coex_cfg_wl_rx_gain,
+	.cfo_init		= rtw_phy_cfo_xcap_init,
+	.cfo_track		= rtw88xxa_cfo_track,
 };
 
 static const struct rtw_page_table page_table_8812a[] = {
diff --git a/drivers/net/wireless/realtek/rtw88/rtw8814a.c b/drivers/net/wireless/realtek/rtw88/rtw8814a.c
index 2db05a83766d6..3c65cd838ce15 100644
--- a/drivers/net/wireless/realtek/rtw88/rtw8814a.c
+++ b/drivers/net/wireless/realtek/rtw88/rtw8814a.c
@@ -145,7 +145,7 @@ static int rtw8814a_read_efuse(struct rtw_dev *rtwdev, u8 *log_map)
 	efuse->usb_mode_switch = u8_get_bits(map->usb_mode, BIT(4));
 	efuse->rfe_option = map->rfe_option;
 	efuse->rf_board_option = map->rf_board_option;
-	efuse->crystal_cap = map->xtal_k;
+	efuse->crystal_cap = map->xtal_k & 0x3f;
 	efuse->channel_plan = map->channel_plan;
 	efuse->country_code[0] = map->country_code[0];
 	efuse->country_code[1] = map->country_code[1];
@@ -284,9 +284,15 @@ static void rtw8814a_config_cck_rx_antenna_init(struct rtw_dev *rtwdev)
 	rtw_write32_mask(rtwdev, REG_CCKTX, BIT_CMB_CCA_2R, 1);
 }
 
+static void rtw8814a_set_crystal_cap(struct rtw_dev *rtwdev, u8 crystal_cap)
+{
+	rtw_write32_mask(rtwdev, REG_AFE_CTRL3, 0x07ff8000,
+			 crystal_cap | (crystal_cap << 6));
+}
+
 static void rtw8814a_phy_set_param(struct rtw_dev *rtwdev)
 {
-	u32 crystal_cap, val32;
+	u32 val32;
 	u8 val8, rf_path;
 
 	/* power on BB/RF domain */
@@ -308,9 +314,7 @@ static void rtw8814a_phy_set_param(struct rtw_dev *rtwdev)
 	rtw_load_table(rtwdev, rtwdev->chip->bb_tbl);
 	rtw_load_table(rtwdev, rtwdev->chip->agc_tbl);
 
-	crystal_cap = rtwdev->efuse.crystal_cap & 0x3F;
-	crystal_cap |= crystal_cap << 6;
-	rtw_write32_mask(rtwdev, REG_AFE_CTRL3, 0x07ff8000, crystal_cap);
+	rtw8814a_set_crystal_cap(rtwdev, rtwdev->efuse.crystal_cap);
 
 	rtw8814a_config_trx_path(rtwdev);
 
@@ -1269,6 +1273,8 @@ static void rtw8814a_query_phy_status(struct rtw_dev *rtwdev, u8 *phy_status,
 			pkt_stat->cfo_tail[i] = cfo[i];
 			dm_info->cfo_tail[i] = (cfo[i] * 5) >> 1;
 		}
+
+		rtw_phy_parsing_cfo(rtwdev, pkt_stat);
 	}
 }
 
@@ -2068,6 +2074,19 @@ static void rtw8814a_bf_config_bfee(struct rtw_dev *rtwdev,
 		rtw_bf_remove_bfee_su(rtwdev, bfee);
 }
 
+static void rtw8814a_cfo_track(struct rtw_dev *rtwdev)
+{
+	struct rtw_cfo_track *cfo = &rtwdev->dm_info.cfo_track;
+	u8 crystal_cap;
+
+	crystal_cap = rtw_phy_cfo_xcap_track(rtwdev, 0x3f, false);
+	if (crystal_cap == cfo->crystal_cap)
+		return;
+
+	cfo->crystal_cap = crystal_cap;
+	rtw8814a_set_crystal_cap(rtwdev, crystal_cap);
+}
+
 static void rtw8814a_fill_txdesc_checksum(struct rtw_dev *rtwdev,
 					  struct rtw_tx_pkt_info *pkt_info,
 					  u8 *txdesc)
@@ -2102,6 +2121,8 @@ static const struct rtw_chip_ops rtw8814a_ops = {
 	.cfg_csi_rate		= NULL,
 	.led_set		= rtw8814a_led_set,
 	.fill_txdesc_checksum	= rtw8814a_fill_txdesc_checksum,
+	.cfo_init		= rtw_phy_cfo_xcap_init,
+	.cfo_track		= rtw8814a_cfo_track,
 
 	.coex_set_init		= rtw8814a_coex_cfg_init,
 	.coex_set_ant_switch	= rtw8814a_coex_cfg_ant_switch,
diff --git a/drivers/net/wireless/realtek/rtw88/rtw8821a.c b/drivers/net/wireless/realtek/rtw88/rtw8821a.c
index 6d106ae70d914..17d3677ca463c 100644
--- a/drivers/net/wireless/realtek/rtw88/rtw8821a.c
+++ b/drivers/net/wireless/realtek/rtw88/rtw8821a.c
@@ -889,6 +889,8 @@ static const struct rtw_chip_ops rtw8821a_ops = {
 	.coex_set_rfe_type	= rtw8821a_coex_cfg_rfe_type,
 	.coex_set_wl_tx_power	= rtw8821a_coex_cfg_wl_tx_power,
 	.coex_set_wl_rx_gain	= rtw8821a_coex_cfg_wl_rx_gain,
+	.cfo_init		= rtw_phy_cfo_xcap_init,
+	.cfo_track		= rtw88xxa_cfo_track,
 };
 
 static const struct rtw_page_table page_table_8821a[] = {
diff --git a/drivers/net/wireless/realtek/rtw88/rtw88xxa.c b/drivers/net/wireless/realtek/rtw88/rtw88xxa.c
index d3dd0ebfb4024..586bb57a10061 100644
--- a/drivers/net/wireless/realtek/rtw88/rtw88xxa.c
+++ b/drivers/net/wireless/realtek/rtw88/rtw88xxa.c
@@ -279,6 +279,7 @@ int rtw88xxa_read_efuse(struct rtw_dev *rtwdev, u8 *log_map)
 	efuse->crystal_cap = map->xtal_k;
 	if (efuse->crystal_cap == 0xff)
 		efuse->crystal_cap = 0x20;
+	efuse->crystal_cap &= 0x3F;
 	efuse->pa_type_2g = map->pa_type;
 	efuse->pa_type_5g = map->pa_type;
 	efuse->lna_type_2g = map->lna_type_2g;
@@ -632,9 +633,19 @@ static void rtw88xxa_init_beacon_parameters(struct rtw_dev *rtwdev)
 	rtw_write16(rtwdev, REG_BCNTCFG, 0x4413);
 }
 
+static void rtw88xxa_set_crystal_cap(struct rtw_dev *rtwdev, u8 crystal_cap)
+{
+	if (rtwdev->chip->id == RTW_CHIP_TYPE_8812A)
+		rtw_write32_mask(rtwdev, REG_AFE_CTRL3, 0x7FF80000,
+				 crystal_cap | (crystal_cap << 6));
+	else
+		rtw_write32_mask(rtwdev, REG_AFE_CTRL3, 0x00FFF000,
+				 crystal_cap | (crystal_cap << 6));
+}
+
 static void rtw88xxa_phy_bb_config(struct rtw_dev *rtwdev)
 {
-	u8 val8, crystal_cap;
+	u8 val8;
 
 	/* power on BB/RF domain */
 	val8 = rtw_read8(rtwdev, REG_SYS_FUNC_EN);
@@ -653,13 +664,7 @@ static void rtw88xxa_phy_bb_config(struct rtw_dev *rtwdev)
 	rtw_load_table(rtwdev, rtwdev->chip->bb_tbl);
 	rtw_load_table(rtwdev, rtwdev->chip->agc_tbl);
 
-	crystal_cap = rtwdev->efuse.crystal_cap & 0x3F;
-	if (rtwdev->chip->id == RTW_CHIP_TYPE_8812A)
-		rtw_write32_mask(rtwdev, REG_AFE_CTRL3, 0x7FF80000,
-				 crystal_cap | (crystal_cap << 6));
-	else
-		rtw_write32_mask(rtwdev, REG_AFE_CTRL3, 0x00FFF000,
-				 crystal_cap | (crystal_cap << 6));
+	rtw88xxa_set_crystal_cap(rtwdev, rtwdev->efuse.crystal_cap & 0x3F);
 }
 
 static void rtw88xxa_phy_rf_config(struct rtw_dev *rtwdev)
@@ -1621,6 +1626,17 @@ void rtw88xxa_query_phy_status(struct rtw_dev *rtwdev, u8 *phy_status,
 			power_b = power_a;
 
 		pkt_stat->signal_power = max3(power_a, power_b, min_rx_power);
+
+		pkt_stat->cfo_tail[RF_PATH_A] =
+			(s8)le32_get_bits(rpt->w2, RTW_JGRPHY_W2_CFO_TAIL_A);
+		pkt_stat->cfo_tail[RF_PATH_B] =
+			(s8)le32_get_bits(rpt->w2, RTW_JGRPHY_W2_CFO_TAIL_B);
+
+		/* convert to kHz (used only for debugfs) */
+		for (i = RF_PATH_A; i < rtwdev->hal.rf_path_num; i++)
+			dm_info->cfo_tail[i] = (pkt_stat->cfo_tail[i] * 5) >> 1;
+
+		rtw_phy_parsing_cfo(rtwdev, pkt_stat);
 	}
 }
 EXPORT_SYMBOL(rtw88xxa_query_phy_status);
@@ -2009,6 +2025,21 @@ void rtw88xxa_phy_pwrtrack(struct rtw_dev *rtwdev,
 }
 EXPORT_SYMBOL(rtw88xxa_phy_pwrtrack);
 
+void rtw88xxa_cfo_track(struct rtw_dev *rtwdev)
+{
+	struct rtw_cfo_track *cfo = &rtwdev->dm_info.cfo_track;
+	bool bt_hold = rtwdev->efuse.btcoex && !rtw_coex_disabled(rtwdev);
+	u8 crystal_cap;
+
+	crystal_cap = rtw_phy_cfo_xcap_track(rtwdev, 0x3F, bt_hold);
+	if (crystal_cap == cfo->crystal_cap)
+		return;
+
+	cfo->crystal_cap = crystal_cap;
+	rtw88xxa_set_crystal_cap(rtwdev, crystal_cap);
+}
+EXPORT_SYMBOL(rtw88xxa_cfo_track);
+
 MODULE_AUTHOR("Realtek Corporation");
 MODULE_DESCRIPTION("Realtek 802.11ac wireless 8821a/8811a/8812a common code");
 MODULE_LICENSE("Dual BSD/GPL");
diff --git a/drivers/net/wireless/realtek/rtw88/rtw88xxa.h b/drivers/net/wireless/realtek/rtw88/rtw88xxa.h
index 0aaf7aa4aafdd..09b72d33f62e1 100644
--- a/drivers/net/wireless/realtek/rtw88/rtw88xxa.h
+++ b/drivers/net/wireless/realtek/rtw88/rtw88xxa.h
@@ -172,5 +172,6 @@ void rtw88xxa_phy_pwrtrack(struct rtw_dev *rtwdev,
 			   void (*do_iqk)(struct rtw_dev *rtwdev));
 void rtw88xxa_bf_config_bfee(struct rtw_dev *rtwdev, struct rtw_vif *vif,
 			     struct rtw_bfee *bfee, bool enable);
+void rtw88xxa_cfo_track(struct rtw_dev *rtwdev);
 
 #endif
-- 
2.55.0


^ permalink raw reply related	[flat|nested] 10+ messages in thread

* [PATCH 5/6] wifi: rtw88: 88xxa: adapt channel sensing to receiver gain
  2026-10-29  5:52 [PATCH 0/6] wifi: rtw88: extend 88xxA support Ho Jie Feng
                   ` (3 preceding siblings ...)
  2026-10-29  5:52 ` [PATCH 4/6] wifi: rtw88: 88xxa: correct carrier-frequency offset Ho Jie Feng
@ 2026-10-29  5:52 ` Ho Jie Feng
  2026-10-29  5:52 ` [PATCH 6/6] wifi: rtw88: 8812a, 8821a: respect peer A-MPDU size limits Ho Jie Feng
  5 siblings, 0 replies; 10+ messages in thread
From: Ho Jie Feng @ 2026-10-29  5:52 UTC (permalink / raw)
  To: Ping-Ke Shih; +Cc: linux-wireless, Ho Jie Feng, linux-kernel

Implement adaptivity for 88xxa, since they support it in
the hardware.

Signed-off-by: Ho Jie Feng <hjf3108@gmail.com>
---
 drivers/net/wireless/realtek/rtw88/main.h     |   1 +
 drivers/net/wireless/realtek/rtw88/rtw8812a.c |  10 ++
 drivers/net/wireless/realtek/rtw88/rtw8821a.c |  10 ++
 drivers/net/wireless/realtek/rtw88/rtw88xxa.c | 123 ++++++++++++++++++
 drivers/net/wireless/realtek/rtw88/rtw88xxa.h |   5 +
 5 files changed, 149 insertions(+)

diff --git a/drivers/net/wireless/realtek/rtw88/main.h b/drivers/net/wireless/realtek/rtw88/main.h
index d59f6e323adf0..796fea308fdf7 100644
--- a/drivers/net/wireless/realtek/rtw88/main.h
+++ b/drivers/net/wireless/realtek/rtw88/main.h
@@ -1787,6 +1787,7 @@ struct rtw_dm_info {
 	bool is_bt_iqk_timeout;
 
 	s8 l2h_th_ini;
+	s8 l2h_th_lb;
 	enum rtw_edcca_mode edcca_mode;
 	u8 scan_density;
 };
diff --git a/drivers/net/wireless/realtek/rtw88/rtw8812a.c b/drivers/net/wireless/realtek/rtw88/rtw8812a.c
index c8008cf43da9d..d93c4776e0303 100644
--- a/drivers/net/wireless/realtek/rtw88/rtw8812a.c
+++ b/drivers/net/wireless/realtek/rtw88/rtw8812a.c
@@ -945,6 +945,8 @@ static const struct rtw_chip_ops rtw8812a_ops = {
 	.coex_set_wl_rx_gain	= rtw8821a_coex_cfg_wl_rx_gain,
 	.cfo_init		= rtw_phy_cfo_xcap_init,
 	.cfo_track		= rtw88xxa_cfo_track,
+	.adaptivity_init	= rtw88xxa_adaptivity_init,
+	.adaptivity		= rtw88xxa_adaptivity,
 };
 
 static const struct rtw_page_table page_table_8812a[] = {
@@ -999,6 +1001,11 @@ static const struct rtw_hw_reg rtw8812a_dig[] = {
 	[1] = { .addr = REG_RXIGI_B, .mask = 0x7f },
 };
 
+static const struct rtw_hw_reg_offset rtw8812a_edcca_th[] = {
+	[EDCCA_TH_L2H_IDX] = {{.addr = 0x8a4, .mask = MASKBYTE0}, .offset = 0},
+	[EDCCA_TH_H2L_IDX] = {{.addr = 0x8a4, .mask = MASKBYTE1}, .offset = 0},
+};
+
 static const struct rtw_rfe_def rtw8812a_rfe_defs[] = {
 	[0] = { .phy_pg_tbl	= &rtw8812a_bb_pg_tbl,
 		.txpwr_lmt_tbl	= &rtw8812a_txpwr_lmt_tbl,
@@ -1089,6 +1096,9 @@ const struct rtw_chip_info rtw8812a_hw_spec = {
 	.bfer_su_max_num = 2,
 	.ampdu_density = IEEE80211_HT_MPDU_DENSITY_16,
 	.max_scan_ie_len = IEEE80211_MAX_DATA_LEN,
+	.edcca_th = rtw8812a_edcca_th,
+	.l2h_th_ini_cs = 10,
+	.l2h_th_ini_ad = -17,
 
 	.coex_para_ver = 0, /* no coex code in 8812au driver */
 	.bt_desired_ver = 0,
diff --git a/drivers/net/wireless/realtek/rtw88/rtw8821a.c b/drivers/net/wireless/realtek/rtw88/rtw8821a.c
index 17d3677ca463c..f7391f6aa3039 100644
--- a/drivers/net/wireless/realtek/rtw88/rtw8821a.c
+++ b/drivers/net/wireless/realtek/rtw88/rtw8821a.c
@@ -891,6 +891,8 @@ static const struct rtw_chip_ops rtw8821a_ops = {
 	.coex_set_wl_rx_gain	= rtw8821a_coex_cfg_wl_rx_gain,
 	.cfo_init		= rtw_phy_cfo_xcap_init,
 	.cfo_track		= rtw88xxa_cfo_track,
+	.adaptivity_init	= rtw88xxa_adaptivity_init,
+	.adaptivity		= rtw88xxa_adaptivity,
 };
 
 static const struct rtw_page_table page_table_8821a[] = {
@@ -944,6 +946,11 @@ static const struct rtw_hw_reg rtw8821a_dig[] = {
 	[0] = { .addr = REG_RXIGI_A, .mask = 0x7f },
 };
 
+static const struct rtw_hw_reg_offset rtw8821a_edcca_th[] = {
+	[EDCCA_TH_L2H_IDX] = {{.addr = 0x8a4, .mask = MASKBYTE0}, .offset = 0},
+	[EDCCA_TH_H2L_IDX] = {{.addr = 0x8a4, .mask = MASKBYTE1}, .offset = 0},
+};
+
 static const struct rtw_rfe_def rtw8821a_rfe_defs[] = {
 	[0] = { .phy_pg_tbl	= &rtw8821a_bb_pg_tbl,
 		.txpwr_lmt_tbl	= &rtw8821a_txpwr_lmt_tbl,
@@ -1193,6 +1200,9 @@ const struct rtw_chip_info rtw8821a_hw_spec = {
 	.bfer_su_max_num = 2,
 	.ampdu_density = IEEE80211_HT_MPDU_DENSITY_16,
 	.max_scan_ie_len = IEEE80211_MAX_DATA_LEN,
+	.edcca_th = rtw8821a_edcca_th,
+	.l2h_th_ini_cs = 10,
+	.l2h_th_ini_ad = -17,
 
 	.coex_para_ver = 20190509, /* glcoex_ver_date_8821a_1ant */
 	.bt_desired_ver = 0x62, /* But for 2 ant it's 0x5c */
diff --git a/drivers/net/wireless/realtek/rtw88/rtw88xxa.c b/drivers/net/wireless/realtek/rtw88/rtw88xxa.c
index 586bb57a10061..aff62d61f4719 100644
--- a/drivers/net/wireless/realtek/rtw88/rtw88xxa.c
+++ b/drivers/net/wireless/realtek/rtw88/rtw88xxa.c
@@ -2040,6 +2040,129 @@ void rtw88xxa_cfo_track(struct rtw_dev *rtwdev)
 }
 EXPORT_SYMBOL(rtw88xxa_cfo_track);
 
+#define EDCCA_TH_MAX_88XXA		0x7f
+#define EDCCA_L2H_CAP_88XXA		10
+#define EDCCA_L2H_INI_NORMAL_88XXA	20
+#define EDCCA_DBG_PORT_88XXA		0x209
+#define EDCCA_DC_BACKOFF_88XXA		2
+#define EDCCA_LB_START_88XXA		(EDCCA_L2H_H2L_DIFF - 30)
+#define EDCCA_LB_RETRY_88XXA		(EDCCA_LB_START_88XXA + 18)
+
+static void rtw88xxa_set_rf_mode_rx(struct rtw_dev *rtwdev, u32 data0,
+				    u32 data1)
+{
+	u8 path;
+
+	for (path = RF_PATH_A; path < rtwdev->hal.rf_path_num; path++) {
+		rtw_write_rf(rtwdev, path, RF_LUTWE, BIT(19), 1);
+		rtw_write_rf(rtwdev, path, RF_MODE_TABLE_ADDR, RFREG_MASK,
+			     0x18000);
+		rtw_write_rf(rtwdev, path, RF_MODE_TABLE_DATA0, RFREG_MASK,
+			     data0);
+		rtw_write_rf(rtwdev, path, RF_MODE_TABLE_DATA1, RFREG_MASK,
+			     data1);
+		rtw_write_rf(rtwdev, path, RF_LUTWE, BIT(19), 0);
+	}
+}
+
+static void rtw88xxa_set_lna(struct rtw_dev *rtwdev, bool enable)
+{
+	if (rtwdev->chip->id == RTW_CHIP_TYPE_8812A)
+		rtw88xxa_set_rf_mode_rx(rtwdev, 0x3f7ff,
+					enable ? 0xc26bf : 0xc22bf);
+	else
+		rtw88xxa_set_rf_mode_rx(rtwdev, 0x0002f,
+					enable ? 0xfb0bb : 0xfb09b);
+}
+
+static u8 rtw88xxa_edcca_count(struct rtw_dev *rtwdev)
+{
+	u8 cnt = 0;
+	int i;
+
+	rtw_write32(rtwdev, REG_DBGSEL, 0);
+	for (i = 0; i < 3 &&
+	     rtw_read32(rtwdev, REG_DBGRPT) & BIT_DBG_CCA_BUSY; i++)
+		usleep_range(3000, 3500);
+
+	rtw_write32(rtwdev, REG_DBGSEL, EDCCA_DBG_PORT_88XXA);
+	for (i = 0; i < 20; i++)
+		if (rtw_read32(rtwdev, REG_DBGRPT) & BIT_DBG_EDCCA_BUSY)
+			cnt++;
+
+	return cnt;
+}
+
+static s8 rtw88xxa_edcca_search_floor(struct rtw_dev *rtwdev)
+{
+	s8 l2h = EDCCA_LB_START_88XXA;
+
+	rtw_phy_set_edcca_th(rtwdev, l2h, l2h - EDCCA_L2H_H2L_DIFF);
+	msleep(30);
+
+	while (l2h < EDCCA_L2H_CAP_88XXA && rtw88xxa_edcca_count(rtwdev) > 1) {
+		l2h++;
+		rtw_phy_set_edcca_th(rtwdev, l2h, l2h - EDCCA_L2H_H2L_DIFF);
+	}
+
+	return l2h;
+}
+
+void rtw88xxa_adaptivity_init(struct rtw_dev *rtwdev)
+{
+	struct rtw_dm_info *dm_info = &rtwdev->dm_info;
+	u32 dbgsel = rtw_read32(rtwdev, REG_DBGSEL);
+	s8 l2h;
+
+	rtw_write32_clr(rtwdev, REG_TX_PTCL_CTRL, BIT_DIS_EDCCA);
+
+	rtw88xxa_set_lna(rtwdev, false);
+	l2h = rtw88xxa_edcca_search_floor(rtwdev);
+	if (l2h >= EDCCA_LB_RETRY_88XXA)
+		l2h = rtw88xxa_edcca_search_floor(rtwdev);
+	rtw88xxa_set_lna(rtwdev, true);
+	rtw_write32(rtwdev, REG_DBGSEL, dbgsel);
+
+	dm_info->l2h_th_lb = l2h + EDCCA_DC_BACKOFF_88XXA;
+	rtw_phy_set_edcca_th(rtwdev, EDCCA_TH_MAX_88XXA, EDCCA_TH_MAX_88XXA);
+
+	rtw_dbg(rtwdev, RTW_DBG_ADAPTIVITY, "EDCCA L2H floor %d\n",
+		dm_info->l2h_th_lb);
+}
+EXPORT_SYMBOL(rtw88xxa_adaptivity_init);
+
+void rtw88xxa_adaptivity(struct rtw_dev *rtwdev)
+{
+	struct rtw_dm_info *dm_info = &rtwdev->dm_info;
+	int igi_diff = EDCCA_IGI_BASE - dm_info->igi_history[0];
+	s8 l2h_lb = dm_info->l2h_th_lb;
+	s8 l2h, h2l;
+
+	/* toggle the ADC mask to work around an EDCCA hang */
+	if (rtwdev->chip->id == RTW_CHIP_TYPE_8812A) {
+		rtw_write32_set(rtwdev, REG_FPGA0_RFMOD, BIT(10));
+		rtw_write32_clr(rtwdev, REG_FPGA0_RFMOD, BIT(10));
+	}
+
+	if (dm_info->edcca_mode == RTW_EDCCA_ADAPTIVITY) {
+		l2h = min(dm_info->l2h_th_ini + igi_diff, EDCCA_L2H_CAP_88XXA);
+		h2l = l2h - EDCCA_L2H_H2L_DIFF;
+	} else {
+		l2h = EDCCA_L2H_INI_NORMAL_88XXA + igi_diff;
+		h2l = l2h - EDCCA_L2H_H2L_DIFF_NORMAL;
+	}
+
+	l2h = max(l2h, l2h_lb);
+	h2l = max_t(s8, h2l, l2h_lb - EDCCA_L2H_H2L_DIFF);
+
+	rtw_phy_set_edcca_th(rtwdev, l2h, h2l);
+
+	rtw_dbg(rtwdev, RTW_DBG_ADAPTIVITY,
+		"EDCCA mode %d, IGI 0x%x, L2H %d, H2L %d\n",
+		dm_info->edcca_mode, dm_info->igi_history[0], l2h, h2l);
+}
+EXPORT_SYMBOL(rtw88xxa_adaptivity);
+
 MODULE_AUTHOR("Realtek Corporation");
 MODULE_DESCRIPTION("Realtek 802.11ac wireless 8821a/8811a/8812a common code");
 MODULE_LICENSE("Dual BSD/GPL");
diff --git a/drivers/net/wireless/realtek/rtw88/rtw88xxa.h b/drivers/net/wireless/realtek/rtw88/rtw88xxa.h
index 09b72d33f62e1..b8719c278df73 100644
--- a/drivers/net/wireless/realtek/rtw88/rtw88xxa.h
+++ b/drivers/net/wireless/realtek/rtw88/rtw88xxa.h
@@ -139,6 +139,9 @@ struct rtw_jaguar_phy_status_rpt {
 
 #define RF18_BW_MASK		(BIT(11) | BIT(10))
 
+#define BIT_DBG_CCA_BUSY	BIT(3)
+#define BIT_DBG_EDCCA_BUSY	BIT(29)
+
 void rtw88xxa_efuse_grant(struct rtw_dev *rtwdev, bool on);
 int rtw88xxa_read_efuse(struct rtw_dev *rtwdev, u8 *log_map);
 void rtw88xxa_power_off(struct rtw_dev *rtwdev,
@@ -173,5 +176,7 @@ void rtw88xxa_phy_pwrtrack(struct rtw_dev *rtwdev,
 void rtw88xxa_bf_config_bfee(struct rtw_dev *rtwdev, struct rtw_vif *vif,
 			     struct rtw_bfee *bfee, bool enable);
 void rtw88xxa_cfo_track(struct rtw_dev *rtwdev);
+void rtw88xxa_adaptivity_init(struct rtw_dev *rtwdev);
+void rtw88xxa_adaptivity(struct rtw_dev *rtwdev);
 
 #endif
-- 
2.55.0


^ permalink raw reply related	[flat|nested] 10+ messages in thread

* [PATCH 6/6] wifi: rtw88: 8812a, 8821a: respect peer A-MPDU size limits
  2026-10-29  5:52 [PATCH 0/6] wifi: rtw88: extend 88xxA support Ho Jie Feng
                   ` (4 preceding siblings ...)
  2026-10-29  5:52 ` [PATCH 5/6] wifi: rtw88: 88xxa: adapt channel sensing to receiver gain Ho Jie Feng
@ 2026-10-29  5:52 ` Ho Jie Feng
  5 siblings, 0 replies; 10+ messages in thread
From: Ho Jie Feng @ 2026-10-29  5:52 UTC (permalink / raw)
  To: Ping-Ke Shih; +Cc: linux-wireless, Ho Jie Feng, linux-kernel

Honor the peer-advertised receive limit for transmitted A-MPDUs.

Signed-off-by: Ho Jie Feng <hjf3108@gmail.com>
---
 drivers/net/wireless/realtek/rtw88/rtw8812a.c | 9 ++++++++-
 drivers/net/wireless/realtek/rtw88/rtw8821a.c | 9 ++++++++-
 2 files changed, 16 insertions(+), 2 deletions(-)

diff --git a/drivers/net/wireless/realtek/rtw88/rtw8812a.c b/drivers/net/wireless/realtek/rtw88/rtw8812a.c
index d93c4776e0303..a3b5fae5608e0 100644
--- a/drivers/net/wireless/realtek/rtw88/rtw8812a.c
+++ b/drivers/net/wireless/realtek/rtw88/rtw8812a.c
@@ -78,6 +78,13 @@ static void rtw8812a_cfg_ldo25(struct rtw_dev *rtwdev, bool enable)
 {
 }
 
+static void rtw8812a_set_ampdu_factor(struct rtw_dev *rtwdev, u8 factor)
+{
+	factor = min_t(u8, factor, IEEE80211_VHT_MAX_AMPDU_128K);
+
+	rtw_write32(rtwdev, REG_AMPDU_MAX_LENGTH, (8192 << factor) - 1);
+}
+
 static void rtw8812a_do_lck(struct rtw_dev *rtwdev)
 {
 	u32 cont_tx, lc_cal, i;
@@ -926,7 +933,7 @@ static const struct rtw_chip_ops rtw8812a_ops = {
 	.set_tx_power_index	= rtw88xxa_set_tx_power_index,
 	.cfg_ldo25		= rtw8812a_cfg_ldo25,
 	.efuse_grant		= rtw88xxa_efuse_grant,
-	.set_ampdu_factor	= NULL,
+	.set_ampdu_factor	= rtw8812a_set_ampdu_factor,
 	.false_alarm_statistics	= rtw88xxa_false_alarm_statistics,
 	.phy_calibration	= rtw8812a_phy_calibration,
 	.cck_pd_set		= rtw_phy_cck_pd_set,
diff --git a/drivers/net/wireless/realtek/rtw88/rtw8821a.c b/drivers/net/wireless/realtek/rtw88/rtw8821a.c
index f7391f6aa3039..f597edec290f6 100644
--- a/drivers/net/wireless/realtek/rtw88/rtw8821a.c
+++ b/drivers/net/wireless/realtek/rtw88/rtw8821a.c
@@ -49,6 +49,13 @@ static void rtw8821a_cfg_ldo25(struct rtw_dev *rtwdev, bool enable)
 {
 }
 
+static void rtw8821a_set_ampdu_factor(struct rtw_dev *rtwdev, u8 factor)
+{
+	factor = min_t(u8, factor, IEEE80211_HT_MAX_AMPDU_64K);
+
+	rtw_write32(rtwdev, REG_AMPDU_MAX_LENGTH, (8192 << factor) - 1);
+}
+
 #define CAL_NUM_8821A 3
 #define MACBB_REG_NUM_8821A 8
 #define AFE_REG_NUM_8821A 4
@@ -872,7 +879,7 @@ static const struct rtw_chip_ops rtw8821a_ops = {
 	.set_tx_power_index	= rtw88xxa_set_tx_power_index,
 	.cfg_ldo25		= rtw8821a_cfg_ldo25,
 	.efuse_grant		= rtw88xxa_efuse_grant,
-	.set_ampdu_factor	= NULL,
+	.set_ampdu_factor	= rtw8821a_set_ampdu_factor,
 	.false_alarm_statistics	= rtw88xxa_false_alarm_statistics,
 	.phy_calibration	= rtw8821a_phy_calibration,
 	.cck_pd_set		= rtw_phy_cck_pd_set,
-- 
2.55.0


^ permalink raw reply related	[flat|nested] 10+ messages in thread

* Re: [PATCH 2/6] wifi: rtw88: 88xxa: receive VHT SU beamformed transmissions
  2026-10-29  5:52 ` [PATCH 2/6] wifi: rtw88: 88xxa: receive VHT SU beamformed transmissions Ho Jie Feng
@ 2026-10-29 12:33   ` Bitterblue Smith
  2026-10-29 14:13     ` Ho Jie Feng
  0 siblings, 1 reply; 10+ messages in thread
From: Bitterblue Smith @ 2026-10-29 12:33 UTC (permalink / raw)
  To: linux-wireless

On 29/10/2026 07:52, Ho Jie Feng wrote:
> Implement beamformee support for 88xxa since they have
> support for it.
> 
> Signed-off-by: Ho Jie Feng <hjf3108@gmail.com>
> ---
>  drivers/net/wireless/realtek/rtw88/bf.h       |  2 +
>  drivers/net/wireless/realtek/rtw88/main.c     | 12 ++--
>  drivers/net/wireless/realtek/rtw88/reg.h      |  1 +
>  drivers/net/wireless/realtek/rtw88/rtw8812a.c |  3 +-
>  drivers/net/wireless/realtek/rtw88/rtw8814a.c | 36 ++++++++++-
>  drivers/net/wireless/realtek/rtw88/rtw8821a.c |  3 +-
>  drivers/net/wireless/realtek/rtw88/rtw88xxa.c | 62 +++++++++++++++++++
>  drivers/net/wireless/realtek/rtw88/rtw88xxa.h |  2 +
>  8 files changed, 112 insertions(+), 9 deletions(-)
> 

Is it really this simple? The vendor driver has a lot more
beamforming code than this.

> diff --git a/drivers/net/wireless/realtek/rtw88/bf.h b/drivers/net/wireless/realtek/rtw88/bf.h
> index a5d3010e6be6e..0aa78fb6a7e64 100644
> --- a/drivers/net/wireless/realtek/rtw88/bf.h
> +++ b/drivers/net/wireless/realtek/rtw88/bf.h
> @@ -12,6 +12,8 @@
>  #define REG_ASSOCIATED_BFMER0_INFO	0x06E4
>  #define REG_ASSOCIATED_BFMER1_INFO	0x06EC
>  #define REG_TX_CSI_RPT_PARAM_BW20	0x06F4
> +#define REG_TX_CSI_RPT_PARAM_BW40	0x06F8
> +#define REG_TX_CSI_RPT_PARAM_BW80	0x06FC
>  #define REG_SND_PTCL_CTRL		0x0718
>  #define BIT_DIS_CHK_VHTSIGB_CRC		BIT(6)
>  #define BIT_DIS_CHK_VHTSIGA_CRC		BIT(5)
> diff --git a/drivers/net/wireless/realtek/rtw88/main.c b/drivers/net/wireless/realtek/rtw88/main.c
> index f7d7b66844862..0a1b5575b7299 100644
> --- a/drivers/net/wireless/realtek/rtw88/main.c
> +++ b/drivers/net/wireless/realtek/rtw88/main.c
> @@ -1605,6 +1605,7 @@ static void rtw_init_ht_cap(struct rtw_dev *rtwdev,
>  static void rtw_init_vht_cap(struct rtw_dev *rtwdev,
>  			     struct ieee80211_sta_vht_cap *vht_cap)
>  {
> +	const struct rtw_chip_info *chip = rtwdev->chip;
>  	struct rtw_efuse *efuse = &rtwdev->efuse;
>  	u16 mcs_map = 0;
>  	__le16 highest;
> @@ -1623,10 +1624,13 @@ static void rtw_init_vht_cap(struct rtw_dev *rtwdev,
>  		       0;
>  	if (rtwdev->hal.rf_path_num > 1)
>  		vht_cap->cap |= IEEE80211_VHT_CAP_TXSTBC;
> -	vht_cap->cap |= IEEE80211_VHT_CAP_MU_BEAMFORMEE_CAPABLE |
> -			IEEE80211_VHT_CAP_SU_BEAMFORMEE_CAPABLE;
> -	vht_cap->cap |= (rtwdev->hal.bfee_sts_cap <<
> -			IEEE80211_VHT_CAP_BEAMFORMEE_STS_SHIFT);
> +	if (rtw_bf_support && chip->bfer_su_max_num) {
> +		vht_cap->cap |= IEEE80211_VHT_CAP_SU_BEAMFORMEE_CAPABLE;
> +		vht_cap->cap |= rtwdev->hal.bfee_sts_cap <<
> +				IEEE80211_VHT_CAP_BEAMFORMEE_STS_SHIFT;
> +	}
> +	if (rtw_bf_support && chip->bfer_mu_max_num)
> +		vht_cap->cap |= IEEE80211_VHT_CAP_MU_BEAMFORMEE_CAPABLE;
>  
>  	if (rtw_chip_has_rx_ldpc(rtwdev))
>  		vht_cap->cap |= IEEE80211_VHT_CAP_RXLDPC;

This could be a separate patch, since it doesn't look related to 88xxa.

> diff --git a/drivers/net/wireless/realtek/rtw88/reg.h b/drivers/net/wireless/realtek/rtw88/reg.h
> index afcdb66fdb9a1..f8ec1e2931f63 100644
> --- a/drivers/net/wireless/realtek/rtw88/reg.h
> +++ b/drivers/net/wireless/realtek/rtw88/reg.h
> @@ -671,6 +671,7 @@
>  #define REG_IQK_COM96		0x0984
>  
>  #define REG_FAS			0x09a4
> +#define REG_CSI_CONTENT_VALUE	0x09b4
>  #define REG_RXSB		0x0a00
>  #define BIT_RXSB_ANA_DIV	BIT(15)
>  #define REG_CCK_RX		0x0a04
> diff --git a/drivers/net/wireless/realtek/rtw88/rtw8812a.c b/drivers/net/wireless/realtek/rtw88/rtw8812a.c
> index 55f1d3d3240be..5d58dff4fcee7 100644
> --- a/drivers/net/wireless/realtek/rtw88/rtw8812a.c
> +++ b/drivers/net/wireless/realtek/rtw88/rtw8812a.c
> @@ -931,7 +931,7 @@ static const struct rtw_chip_ops rtw8812a_ops = {
>  	.phy_calibration	= rtw8812a_phy_calibration,
>  	.cck_pd_set		= rtw_phy_cck_pd_set,
>  	.pwr_track		= rtw8812a_pwr_track,
> -	.config_bfee		= NULL,
> +	.config_bfee		= rtw88xxa_bf_config_bfee,
>  	.set_gid_table		= NULL,
>  	.cfg_csi_rate		= NULL,
>  	.led_set		= rtw8812a_led_set,
> @@ -1084,6 +1084,7 @@ const struct rtw_chip_info rtw8812a_hw_spec = {
>  	.old_datarate_fb_limit = true,
>  	.usb_tx_agg_desc_num = 1,
>  	.iqk_threshold = 8,
> +	.bfer_su_max_num = 2,
>  	.ampdu_density = IEEE80211_HT_MPDU_DENSITY_16,
>  	.max_scan_ie_len = IEEE80211_MAX_DATA_LEN,
>  
> diff --git a/drivers/net/wireless/realtek/rtw88/rtw8814a.c b/drivers/net/wireless/realtek/rtw88/rtw8814a.c
> index 5bb55606f14a1..2db05a83766d6 100644
> --- a/drivers/net/wireless/realtek/rtw88/rtw8814a.c
> +++ b/drivers/net/wireless/realtek/rtw88/rtw8814a.c
> @@ -4,6 +4,7 @@
>  
>  #include <linux/usb.h>
>  #include "main.h"
> +#include "bf.h"
>  #include "coex.h"
>  #include "tx.h"
>  #include "phy.h"
> @@ -115,10 +116,13 @@ static void rtw8814a_init_hwcap(struct rtw_dev *rtwdev)
>  			   BIT(RTW_CHANNEL_WIDTH_80);
>  	efuse->hw_cap.ptcl = EFUSE_HW_CAP_PTCL_VHT;
>  
> -	if (hal->rf_type == RF_3T3R)
> +	if (hal->rf_type == RF_3T3R) {
>  		efuse->hw_cap.nss = 3;
> -	else
> +		hal->bfee_sts_cap = 2;
> +	} else {
>  		efuse->hw_cap.nss = 2;
> +		hal->bfee_sts_cap = 1;
> +	}
>  
>  	rtw_dbg(rtwdev, RTW_DBG_EFUSE,
>  		"hw cap: hci=0x%02x, bw=0x%02x, ptcl=0x%02x, ant_num=%d, nss=%d\n",
> @@ -2039,6 +2043,31 @@ static void rtw8814a_led_set(struct led_classdev *led,
>  	rtw_write32(rtwdev, REG_GPIO_PIN_CTRL_2, led_gpio_cfg);
>  }
>  
> +#define RTW_NDP_RX_STANDBY_TIME_8814A	0x40
> +#define RTW_SND_CTRL_SOUNDING_8814A	0xDB
> +
> +static void rtw8814a_bf_enable_bfee_su(struct rtw_dev *rtwdev,
> +				       struct rtw_vif *vif,
> +				       struct rtw_bfee *bfee)
> +{
> +	rtw_bf_enable_bfee_su(rtwdev, vif, bfee);
> +
> +	rtw_write16(rtwdev, REG_SND_PTCL_CTRL + 1, 0x0202);
> +	rtw_write8(rtwdev, REG_SND_PTCL_CTRL, RTW_SND_CTRL_SOUNDING_8814A);
> +	rtw_write8(rtwdev, REG_SND_PTCL_CTRL + 3,
> +		   RTW_NDP_RX_STANDBY_TIME_8814A);
> +}
> +
> +static void rtw8814a_bf_config_bfee(struct rtw_dev *rtwdev,
> +				    struct rtw_vif *vif,
> +				    struct rtw_bfee *bfee, bool enable)
> +{
> +	if (enable)
> +		rtw8814a_bf_enable_bfee_su(rtwdev, vif, bfee);
> +	else
> +		rtw_bf_remove_bfee_su(rtwdev, bfee);
> +}
> +
>  static void rtw8814a_fill_txdesc_checksum(struct rtw_dev *rtwdev,
>  					  struct rtw_tx_pkt_info *pkt_info,
>  					  u8 *txdesc)
> @@ -2068,7 +2097,7 @@ static const struct rtw_chip_ops rtw8814a_ops = {
>  	.phy_calibration	= rtw8814a_phy_calibration,
>  	.cck_pd_set		= rtw8814a_phy_cck_pd_set,
>  	.pwr_track		= rtw8814a_pwr_track,
> -	.config_bfee		= NULL,
> +	.config_bfee		= rtw8814a_bf_config_bfee,
>  	.set_gid_table		= NULL,
>  	.cfg_csi_rate		= NULL,
>  	.led_set		= rtw8814a_led_set,
> @@ -2231,6 +2260,7 @@ const struct rtw_chip_info rtw8814a_hw_spec = {
>  	.rfe_defs = rtw8814a_rfe_defs,
>  	.rfe_defs_size = ARRAY_SIZE(rtw8814a_rfe_defs),
>  	.iqk_threshold = 8,
> +	.bfer_su_max_num = 2,
>  	.max_scan_ie_len = IEEE80211_MAX_DATA_LEN,
>  
>  	.coex_para_ver = 0,
> diff --git a/drivers/net/wireless/realtek/rtw88/rtw8821a.c b/drivers/net/wireless/realtek/rtw88/rtw8821a.c
> index 6096d3b8e41be..6d106ae70d914 100644
> --- a/drivers/net/wireless/realtek/rtw88/rtw8821a.c
> +++ b/drivers/net/wireless/realtek/rtw88/rtw8821a.c
> @@ -877,7 +877,7 @@ static const struct rtw_chip_ops rtw8821a_ops = {
>  	.phy_calibration	= rtw8821a_phy_calibration,
>  	.cck_pd_set		= rtw_phy_cck_pd_set,
>  	.pwr_track		= rtw8821a_pwr_track,
> -	.config_bfee		= NULL,
> +	.config_bfee		= rtw88xxa_bf_config_bfee,
>  	.set_gid_table		= NULL,
>  	.cfg_csi_rate		= NULL,
>  	.led_set		= rtw8821a_led_set,
> @@ -1188,6 +1188,7 @@ const struct rtw_chip_info rtw8821a_hw_spec = {
>  	.old_datarate_fb_limit = true,
>  	.usb_tx_agg_desc_num = 6,
>  	.iqk_threshold = 8,
> +	.bfer_su_max_num = 2,
>  	.ampdu_density = IEEE80211_HT_MPDU_DENSITY_16,
>  	.max_scan_ie_len = IEEE80211_MAX_DATA_LEN,
>  
> diff --git a/drivers/net/wireless/realtek/rtw88/rtw88xxa.c b/drivers/net/wireless/realtek/rtw88/rtw88xxa.c
> index c298fa0fb2a6b..d3dd0ebfb4024 100644
> --- a/drivers/net/wireless/realtek/rtw88/rtw88xxa.c
> +++ b/drivers/net/wireless/realtek/rtw88/rtw88xxa.c
> @@ -138,6 +138,7 @@ static void rtw88xxa_read_usb_type(struct rtw_dev *rtwdev)
>  		efuse->hw_cap.nss = 1;
>  	else
>  		efuse->hw_cap.nss = 2;
> +	hal->bfee_sts_cap = 1;
>  
>  	if (rtwdev->chip->id == RTW_CHIP_TYPE_8821A)
>  		goto print_hw_cap;
> @@ -197,6 +198,67 @@ static void rtw88xxa_read_usb_type(struct rtw_dev *rtwdev)
>  		efuse->hw_cap.ant_num, efuse->hw_cap.nss);
>  }
>  
> +#define RTW_NDP_RX_STANDBY_TIME_88XXA	0x50
> +#define RTW_SND_CTRL_REMOVE_88XXA	0xC8
> +#define RTW_SND_CTRL_SOUNDING_88XXA	0xCB
> +
> +void rtw88xxa_bf_config_bfee(struct rtw_dev *rtwdev, struct rtw_vif *vif,
> +			     struct rtw_bfee *bfee, bool enable)
> +{
> +	static const u32 csi_rpt_regs[] = {
> +		REG_TX_CSI_RPT_PARAM_BW20,
> +		REG_TX_CSI_RPT_PARAM_BW40,
> +		REG_TX_CSI_RPT_PARAM_BW80,
> +	};
> +	struct rtw_bf_info *bf_info = &rtwdev->bf_info;
> +	u8 slot = bfee->su_reg_index;
> +	u32 addr_bfer_info;
> +	u32 csi_param;
> +	int i;
> +
> +	if (bfee->role != RTW_BFEE_SU || slot >= 2)
> +		return;
> +
> +	addr_bfer_info = slot ? REG_ASSOCIATED_BFMER1_INFO :
> +				      REG_ASSOCIATED_BFMER0_INFO;
> +
> +	rtw_dbg(rtwdev, RTW_DBG_BF, "chip %d SU beamformee %s slot %u\n",
> +		rtwdev->chip->id, enable ? "enable" : "disable", slot);
> +
> +	if (!enable) {
> +		rtw_write32(rtwdev, addr_bfer_info, 0);
> +		rtw_write16(rtwdev, addr_bfer_info + 4, 0);
> +
> +		clear_bit(slot, bf_info->bfer_su_reg_maping);
> +		bfee->su_reg_index = 0xff;
> +
> +		if (!bf_info->bfer_su_cnt)
> +			rtw_write8(rtwdev, REG_SND_PTCL_CTRL,
> +				   RTW_SND_CTRL_REMOVE_88XXA);
> +		return;
> +	}
> +
> +	if (hweight8(rtwdev->hal.antenna_rx) == 2) {
> +		rtw_write32(rtwdev, REG_CSI_CONTENT_VALUE, 0);
> +		csi_param = 0x01090109;
> +	} else {
> +		rtw_write32(rtwdev, REG_CSI_CONTENT_VALUE, 0x01081008);
> +		csi_param = 0x01080108;
> +	}
> +
> +	rtw_write8(rtwdev, REG_SND_PTCL_CTRL,
> +		   RTW_SND_CTRL_SOUNDING_88XXA);
> +	for (i = 0; i < ETH_ALEN; i++)
> +		rtw_write8(rtwdev, addr_bfer_info + i, bfee->mac_addr[i]);
> +
> +	for (i = 0; i < ARRAY_SIZE(csi_rpt_regs); i++)
> +		rtw_write32(rtwdev, csi_rpt_regs[i], csi_param);
> +
> +	rtw_write8(rtwdev, REG_SND_PTCL_CTRL + 3,
> +		   RTW_NDP_RX_STANDBY_TIME_88XXA);
> +}
> +EXPORT_SYMBOL(rtw88xxa_bf_config_bfee);
> +
>  int rtw88xxa_read_efuse(struct rtw_dev *rtwdev, u8 *log_map)
>  {
>  	const struct rtw_chip_info *chip = rtwdev->chip;
> diff --git a/drivers/net/wireless/realtek/rtw88/rtw88xxa.h b/drivers/net/wireless/realtek/rtw88/rtw88xxa.h
> index d9c8db58909ca..0aaf7aa4aafdd 100644
> --- a/drivers/net/wireless/realtek/rtw88/rtw88xxa.h
> +++ b/drivers/net/wireless/realtek/rtw88/rtw88xxa.h
> @@ -170,5 +170,7 @@ bool rtw88xxa_iqk_finish(int average, int threshold,
>  void rtw88xxa_phy_pwrtrack(struct rtw_dev *rtwdev,
>  			   void (*do_lck)(struct rtw_dev *rtwdev),
>  			   void (*do_iqk)(struct rtw_dev *rtwdev));
> +void rtw88xxa_bf_config_bfee(struct rtw_dev *rtwdev, struct rtw_vif *vif,
> +			     struct rtw_bfee *bfee, bool enable);
>  
>  #endif


^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: [PATCH 2/6] wifi: rtw88: 88xxa: receive VHT SU beamformed transmissions
  2026-10-29 12:33   ` Bitterblue Smith
@ 2026-10-29 14:13     ` Ho Jie Feng
  2026-10-29 21:06       ` Bitterblue Smith
  0 siblings, 1 reply; 10+ messages in thread
From: Ho Jie Feng @ 2026-10-29 14:13 UTC (permalink / raw)
  To: linux-wireless, Bitterblue Smith; +Cc: Ping-Ke Shih

On 29/10/2026 14:33, Bitterblue Smith wrote:
> On 29/10/2026 07:52, Ho Jie Feng wrote:
>> Implement beamformee support for 88xxa since they have
>> support for it.
>>
>> Signed-off-by: Ho Jie Feng <hjf3108@gmail.com>
>> ---
>>  drivers/net/wireless/realtek/rtw88/bf.h       |  2 +
>>  drivers/net/wireless/realtek/rtw88/main.c     | 12 ++--
>>  drivers/net/wireless/realtek/rtw88/reg.h      |  1 +
>>  drivers/net/wireless/realtek/rtw88/rtw8812a.c |  3 +-
>>  drivers/net/wireless/realtek/rtw88/rtw8814a.c | 36 ++++++++++-
>>  drivers/net/wireless/realtek/rtw88/rtw8821a.c |  3 +-
>>  drivers/net/wireless/realtek/rtw88/rtw88xxa.c | 62 +++++++++++++++++++
>>  drivers/net/wireless/realtek/rtw88/rtw88xxa.h |  2 +
>>  8 files changed, 112 insertions(+), 9 deletions(-)
>>
>
> Is it really this simple? The vendor driver has a lot more
> beamforming code than this.

Yes, this is the beamformee code, not the beamformer,
which has way more code than this. The RTL8812AU is acting as
the target for the AP to beamform to for this one.

I have the beamformer code, which the RTL8812AU
beamforms, but I dropped it since it reduces throughput.

I can put the patch out if you wish to see it, but
won't be enabling for 88xxa.

Reference:
https://github.com/aircrack-ng/rtl8812au/blob/v5.6.4.2/hal/phydm/txbf/haltxbfjaguar.c#L229

Vendor driver and rtw88 can be sometimes confusing for
the bfee vs bfer distinction.

>> ...
>>  	if (rtwdev->hal.rf_path_num > 1)
>>  		vht_cap->cap |= IEEE80211_VHT_CAP_TXSTBC;
>> -	vht_cap->cap |= IEEE80211_VHT_CAP_MU_BEAMFORMEE_CAPABLE |
>> -			IEEE80211_VHT_CAP_SU_BEAMFORMEE_CAPABLE;
>> -	vht_cap->cap |= (rtwdev->hal.bfee_sts_cap <<
>> -			IEEE80211_VHT_CAP_BEAMFORMEE_STS_SHIFT);
>> +	if (rtw_bf_support && chip->bfer_su_max_num) {
>> +		vht_cap->cap |= IEEE80211_VHT_CAP_SU_BEAMFORMEE_CAPABLE;
>> +		vht_cap->cap |= rtwdev->hal.bfee_sts_cap <<
>> +				IEEE80211_VHT_CAP_BEAMFORMEE_STS_SHIFT;
>> +	}
>> +	if (rtw_bf_support && chip->bfer_mu_max_num)
>> +		vht_cap->cap |= IEEE80211_VHT_CAP_MU_BEAMFORMEE_CAPABLE;
>>  
>>  	if (rtw_chip_has_rx_ldpc(rtwdev))
>>  		vht_cap->cap |= IEEE80211_VHT_CAP_RXLDPC;
>
> This could be a separate patch, since it doesn't look related to 88xxa.

I can put this in v2.

>> ...

^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: [PATCH 2/6] wifi: rtw88: 88xxa: receive VHT SU beamformed transmissions
  2026-10-29 14:13     ` Ho Jie Feng
@ 2026-10-29 21:06       ` Bitterblue Smith
  0 siblings, 0 replies; 10+ messages in thread
From: Bitterblue Smith @ 2026-10-29 21:06 UTC (permalink / raw)
  To: Ho Jie Feng, linux-wireless; +Cc: Ping-Ke Shih

On 29/10/2026 16:13, Ho Jie Feng wrote:
> On 29/10/2026 14:33, Bitterblue Smith wrote:
>> On 29/10/2026 07:52, Ho Jie Feng wrote:
>>> Implement beamformee support for 88xxa since they have
>>> support for it.
>>>
>>> Signed-off-by: Ho Jie Feng <hjf3108@gmail.com>
>>> ---
>>>  drivers/net/wireless/realtek/rtw88/bf.h       |  2 +
>>>  drivers/net/wireless/realtek/rtw88/main.c     | 12 ++--
>>>  drivers/net/wireless/realtek/rtw88/reg.h      |  1 +
>>>  drivers/net/wireless/realtek/rtw88/rtw8812a.c |  3 +-
>>>  drivers/net/wireless/realtek/rtw88/rtw8814a.c | 36 ++++++++++-
>>>  drivers/net/wireless/realtek/rtw88/rtw8821a.c |  3 +-
>>>  drivers/net/wireless/realtek/rtw88/rtw88xxa.c | 62 +++++++++++++++++++
>>>  drivers/net/wireless/realtek/rtw88/rtw88xxa.h |  2 +
>>>  8 files changed, 112 insertions(+), 9 deletions(-)
>>>
>>
>> Is it really this simple? The vendor driver has a lot more
>> beamforming code than this.
> 
> Yes, this is the beamformee code, not the beamformer,
> which has way more code than this. The RTL8812AU is acting as
> the target for the AP to beamform to for this one.
> 

Oh, I see. I didn't look very closely at the vendor code.

> I have the beamformer code, which the RTL8812AU
> beamforms, but I dropped it since it reduces throughput.
> 
> I can put the patch out if you wish to see it, but
> won't be enabling for 88xxa.
> 
> Reference:
> https://github.com/aircrack-ng/rtl8812au/blob/v5.6.4.2/hal/phydm/txbf/haltxbfjaguar.c#L229
> 

By the way, there is a newer version:
https://github.com/morrownr/8812au-20210820

I don't know if there are any differences in the beamforming code.

> Vendor driver and rtw88 can be sometimes confusing for
> the bfee vs bfer distinction.
> 
>>> ...
>>>  	if (rtwdev->hal.rf_path_num > 1)
>>>  		vht_cap->cap |= IEEE80211_VHT_CAP_TXSTBC;
>>> -	vht_cap->cap |= IEEE80211_VHT_CAP_MU_BEAMFORMEE_CAPABLE |
>>> -			IEEE80211_VHT_CAP_SU_BEAMFORMEE_CAPABLE;
>>> -	vht_cap->cap |= (rtwdev->hal.bfee_sts_cap <<
>>> -			IEEE80211_VHT_CAP_BEAMFORMEE_STS_SHIFT);
>>> +	if (rtw_bf_support && chip->bfer_su_max_num) {
>>> +		vht_cap->cap |= IEEE80211_VHT_CAP_SU_BEAMFORMEE_CAPABLE;
>>> +		vht_cap->cap |= rtwdev->hal.bfee_sts_cap <<
>>> +				IEEE80211_VHT_CAP_BEAMFORMEE_STS_SHIFT;
>>> +	}
>>> +	if (rtw_bf_support && chip->bfer_mu_max_num)
>>> +		vht_cap->cap |= IEEE80211_VHT_CAP_MU_BEAMFORMEE_CAPABLE;
>>>  
>>>  	if (rtw_chip_has_rx_ldpc(rtwdev))
>>>  		vht_cap->cap |= IEEE80211_VHT_CAP_RXLDPC;
>>
>> This could be a separate patch, since it doesn't look related to 88xxa.
> 
> I can put this in v2.
> 
>>> ...


^ permalink raw reply	[flat|nested] 10+ messages in thread

end of thread, other threads:[~2026-10-29 21:06 UTC | newest]

Thread overview: 10+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-10-29  5:52 [PATCH 0/6] wifi: rtw88: extend 88xxA support Ho Jie Feng
2026-10-29  5:52 ` [PATCH 1/6] wifi: rtw88: 8812a, 8814a: enable STBC for HT transmissions Ho Jie Feng
2026-10-29  5:52 ` [PATCH 2/6] wifi: rtw88: 88xxa: receive VHT SU beamformed transmissions Ho Jie Feng
2026-10-29 12:33   ` Bitterblue Smith
2026-10-29 14:13     ` Ho Jie Feng
2026-10-29 21:06       ` Bitterblue Smith
2026-10-29  5:52 ` [PATCH 3/6] wifi: rtw88: prepare frequency-offset correction for more chips Ho Jie Feng
2026-10-29  5:52 ` [PATCH 4/6] wifi: rtw88: 88xxa: correct carrier-frequency offset Ho Jie Feng
2026-10-29  5:52 ` [PATCH 5/6] wifi: rtw88: 88xxa: adapt channel sensing to receiver gain Ho Jie Feng
2026-10-29  5:52 ` [PATCH 6/6] wifi: rtw88: 8812a, 8821a: respect peer A-MPDU size limits Ho Jie Feng

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox