blob: 347774e0c9b7fe7c03c5130b1131a338705aa143 [file] [log] [blame]
developer05f3b2b2024-08-19 19:17:34 +08001From bdbfbe8865914946cbce6c901224785cf5cd7d3c Mon Sep 17 00:00:00 2001
2From: Allen Ye <allen.ye@mediatek.com>
3Date: Thu, 1 Aug 2024 11:25:03 +0800
4Subject: [PATCH 198/199] mtk: mt76: mt7996: Add AFC and lpi power support
5
6This patch receiving and storing the power table from hostapd vendor cmd.
7The power table would be use to compare the sku table in standard power
8mode every time when setting power and mt76 would set the minimum values
9between afc and sku table to fw.
10The AFC table format is like below:
11col\row bw20 bw40 ... ru26 ... ru3472
12chan 1
13chan 6
14...
15chan 233
16To compare the afc and sku table, mt76 will find the power list of current
17channel in AFC table. And mtk sku table design serveral rates for each
18bandwidth, so a power value of afc table could compare with serveral
19rates in mtk sku table.
20
21 - Add a new vendor attibute lpi mode, due to the lpi is tuntime decided by AFC
22not like wifi6 switch lpi and standard power should reload interface.
23
24Add dump afc table and information.
25Add the bf on value offset logic. The offset is antenna/beamform gain
26offset = 10 * (log(num of ant) - log(num of NSS)).
27
28Signed-off-by: Allen Ye <allen.ye@mediatek.com>
29---
30 mt76.h | 2 +
31 mt7996/mac.c | 2 +-
32 mt7996/main.c | 2 +-
33 mt7996/mcu.c | 186 ++++++++++++++++++++++++++++++++++++++++---
34 mt7996/mcu.h | 139 ++++++++++++++++++++++++++++++++
35 mt7996/mt7996.h | 2 +
36 mt7996/mtk_debugfs.c | 37 +++++++++
37 mt7996/vendor.c | 81 +++++++++++++++----
38 mt7996/vendor.h | 3 +
39 9 files changed, 429 insertions(+), 25 deletions(-)
40
41diff --git a/mt76.h b/mt76.h
42index 58e9b0b7..4b473123 100644
43--- a/mt76.h
44+++ b/mt76.h
45@@ -1063,6 +1063,8 @@ struct mt76_dev {
46 bool lpi_psd;
47 bool lpi_bcn_enhance;
48 bool mgmt_pwr_enhance;
49+ bool lpi_mode;
50+ s8 **afc_power_table;
51
52 #ifdef CONFIG_NL80211_TESTMODE
53 const struct mt76_testmode_ops *test_ops;
54diff --git a/mt7996/mac.c b/mt7996/mac.c
55index c0f282d1..60223bea 100644
56--- a/mt7996/mac.c
57+++ b/mt7996/mac.c
58@@ -896,7 +896,7 @@ void mt7996_mac_write_txwi(struct mt7996_dev *dev, __le32 *txwi,
59 if (mcast)
60 val |= MT_TXD6_DIS_MAT;
61 if (dev->mt76.phys[band_idx]->cap.has_6ghz &&
62- dev->mt76.lpi_bcn_enhance &&
63+ dev->mt76.lpi_mode && dev->mt76.lpi_bcn_enhance &&
64 ieee80211_is_mgmt(hdr->frame_control))
65 val |= FIELD_PREP(MT_TXD6_BW, FW_CDBW_80MHZ);
66 txwi[6] |= cpu_to_le32(val);
67diff --git a/mt7996/main.c b/mt7996/main.c
68index 7be2a38a..58e02083 100644
69--- a/mt7996/main.c
70+++ b/mt7996/main.c
71@@ -891,7 +891,7 @@ mt7996_get_rates_table(struct ieee80211_hw *hw, struct ieee80211_bss_conf *conf,
72
73 if (dev->cert_mode && phy->mt76->band_idx == MT_BAND2)
74 rate = 0x0200;
75- else if (mphy->dev->lpi_bcn_enhance)
76+ else if (mphy->dev->lpi_mode && mphy->dev->lpi_bcn_enhance)
77 rate = FR_RATE_IDX_OFDM_6M;
78
79 /* odd index for driver, even index for firmware */
80diff --git a/mt7996/mcu.c b/mt7996/mcu.c
81index 7096b8a0..48bdbf73 100644
82--- a/mt7996/mcu.c
83+++ b/mt7996/mcu.c
84@@ -6374,7 +6374,8 @@ int mt7996_mcu_set_fixed_rate_table(struct mt7996_phy *phy, u8 table_idx,
85
86 if (beacon) {
87 req.spe_idx_sel = SPE_IXD_SELECT_TXD;
88- req.spe_idx = dev->mt76.mgmt_pwr_enhance ? 0 : 24 + band_idx;
89+ req.spe_idx = dev->mt76.lpi_mode && dev->mt76.mgmt_pwr_enhance ?
90+ 0 : 24 + band_idx;
91 phy->beacon_rate = rate_idx;
92 } else {
93 req.spe_idx_sel = SPE_IXD_SELECT_BMC_WTBL;
94@@ -6657,6 +6658,126 @@ mt7996_update_max_txpower_cur(struct mt7996_phy *phy, int tx_power)
95 mphy->txpower_cur = e2p_power_limit;
96 }
97
98+static int mt7996_afc_update_power_limit(struct mt7996_dev *dev,
99+ struct ieee80211_channel *chan,
100+ struct mt76_power_limits *la,
101+ struct mt76_power_path_limits *la_path,
102+ int *tx_power,
103+ struct cfg80211_chan_def *chandef)
104+{
105+ s8 *power_list, bw320_offset, target_power;
106+ int table_idx, i, bw, mcs, ru, eht, path;
107+ s8 bf_offset_ofdm[] = {6, 10, 12, 14};
108+ s8 bf_offset[] = {0, 6, 10, 12, 14, 0, 4, 6, 8, 0, 3, 5, 0, 2, 0};
109+
110+ table_idx = chan->hw_value / 4;
111+ if (table_idx < 0 || table_idx > MAX_CHANNEL_NUM_6G ||
112+ !dev->mt76.afc_power_table[table_idx])
113+ return -EINVAL;
114+
115+ power_list = dev->mt76.afc_power_table[table_idx];
116+
117+ switch (chan->center_freq) {
118+ case 31:
119+ case 95:
120+ case 159:
121+ bw320_offset = 1;
122+ break;
123+ case 63:
124+ case 127:
125+ case 191:
126+ bw320_offset = 2;
127+ break;
128+ default:
129+ bw320_offset = 0;
130+ break;
131+ }
132+
133+ if (chandef) {
134+ switch (chandef->width) {
135+ case NL80211_CHAN_WIDTH_20:
136+ target_power = power_list[afc_power_bw20];
137+ break;
138+ case NL80211_CHAN_WIDTH_40:
139+ target_power = power_list[afc_power_bw40];
140+ break;
141+ case NL80211_CHAN_WIDTH_80:
142+ target_power = power_list[afc_power_bw80];
143+ break;
144+ case NL80211_CHAN_WIDTH_160:
145+ target_power = power_list[afc_power_bw160];
146+ break;
147+ case NL80211_CHAN_WIDTH_320:
148+ if (bw320_offset == 1)
149+ target_power = power_list[afc_power_bw320_1];
150+ else
151+ target_power = power_list[afc_power_bw320_2];
152+ break;
153+ default:
154+ break;
155+ }
156+ *tx_power = min_t(int, *tx_power, target_power);
157+ }
158+
159+ target_power = min_t(s8, (s8)*tx_power, power_list[afc_power_bw20]);
160+ for (i = 0; i < sizeof(la->cck); i++)
161+ la->cck[i] = min_t(s8, la->cck[i], power_list[afc_power_bw20]);
162+ for (i = 0; i < sizeof(la->ofdm); i++)
163+ la->ofdm[i] = min_t(s8, la->ofdm[i], target_power);
164+
165+ for (i = 0; i < sizeof(la_path->cck); i++)
166+ la_path->cck[i] = min_t(s8, la_path->cck[i], power_list[afc_power_bw20]);
167+ for (i = 0; i < sizeof(la_path->ofdm); i++)
168+ la_path->ofdm[i] = min_t(s8, la_path->ofdm[i], target_power);
169+ for (i = 0; i < sizeof(la_path->ofdm_bf); i++) {
170+ la_path->ofdm_bf[i] =
171+ min_t(s8, la_path->ofdm_bf[i],
172+ target_power - bf_offset_ofdm[i]);
173+ }
174+
175+ for (bw = afc_power_bw20; bw < afc_power_table_num; bw++) {
176+ if ((bw == afc_power_bw320_1 && bw320_offset == 2) ||
177+ (bw == afc_power_bw320_2 && bw320_offset == 1))
178+ continue;
179+
180+ if (power_list[bw] == AFC_INVALID_POWER)
181+ continue;
182+
183+ /* Negative index means doesn't need to update powers of the type. */
184+ if (mt7996_get_bw_power_table_idx(bw, &mcs, &ru, &eht, &path))
185+ return -EINVAL;
186+
187+ if (mcs >= 0) {
188+ for (i = 0; i < sizeof(la->mcs[0]); i++)
189+ la->mcs[mcs][i] =
190+ min_t(s8, la->mcs[mcs][i], power_list[bw]);
191+ }
192+
193+ if (ru >= 0) {
194+ for (i = 0; i < sizeof(la->ru[0]); i++)
195+ la->ru[ru][i] = min_t(s8, la->ru[ru][i], power_list[bw]);
196+ }
197+
198+ if (eht >= 0) {
199+ for (i = 0; i < sizeof(la->eht[0]); i++)
200+ la->eht[eht][i] =
201+ min_t(s8, la->eht[eht][i], power_list[bw]);
202+ }
203+
204+ if (path >= 0) {
205+ for (i = 0; i < sizeof(la_path->ru[0]); i++) {
206+ la_path->ru[path][i] =
207+ min_t(s8, la_path->ru[path][i], power_list[bw]);
208+ la_path->ru_bf[path][i] =
209+ min_t(s8, la_path->ru_bf[path][i],
210+ power_list[bw] - bf_offset[i]);
211+ }
212+ }
213+ }
214+
215+ return 0;
216+}
217+
218 bool mt7996_is_psd_country(char *country)
219 {
220 char psd_country_list[][3] = {"US", "KR", "BR", "CL", "MY", ""};
221@@ -6704,15 +6825,22 @@ int mt7996_mcu_set_txpower_sku(struct mt7996_phy *phy,
222 txpower_setting = 127;
223 txpower_limit = mt7996_get_power_bound(phy, txpower_setting);
224
225- if (phy->sku_limit_en) {
226- txpower_limit = mt76_get_rate_power_limits(mphy, mphy->chandef.chan,
227- &la, &la_path, txpower_limit);
228- mt7996_update_max_txpower_cur(phy, txpower_limit);
229- } else {
230+ if (!phy->sku_limit_en) {
231 mt7996_update_max_txpower_cur(phy, txpower_limit);
232 return 0;
233 }
234
235+ txpower_limit = mt76_get_rate_power_limits(mphy, mphy->chandef.chan,
236+ &la, &la_path, txpower_limit);
237+ if(phy->mt76->cap.has_6ghz && dev->mt76.afc_power_table) {
238+ ret = mt7996_afc_update_power_limit(dev, mphy->main_chan, &la, &la_path,
239+ &txpower_limit, &mphy->chandef);
240+ if (ret)
241+ return ret;
242+ }
243+
244+ mt7996_update_max_txpower_cur(phy, txpower_limit);
245+
246 skb = mt76_mcu_msg_alloc(&dev->mt76, NULL,
247 sizeof(req) + MT7996_SKU_PATH_NUM);
248 if (!skb)
249@@ -6725,7 +6853,7 @@ int mt7996_mcu_set_txpower_sku(struct mt7996_phy *phy,
250 /* FW would compensate for PSD countries
251 * driver doesn't need to do it
252 */
253- if (phy->mt76->cap.has_6ghz && mphy->dev->lpi_psd &&
254+ if (phy->mt76->cap.has_6ghz && mphy->dev->lpi_mode && mphy->dev->lpi_psd &&
255 !mt7996_is_psd_country(dev->mt76.alpha2)) {
256 switch (mphy->chandef.width) {
257 case NL80211_CHAN_WIDTH_20:
258@@ -6791,7 +6919,7 @@ int mt7996_mcu_set_txpower_sku(struct mt7996_phy *phy,
259 /* FW would NOT compensate in the case of BF backoff table
260 * driver needs to compensate for LPI PSD
261 */
262- if (phy->mt76->cap.has_6ghz && mphy->dev->lpi_psd) {
263+ if (phy->mt76->cap.has_6ghz && mphy->dev->lpi_mode && mphy->dev->lpi_psd) {
264 switch (mphy->chandef.width) {
265 case NL80211_CHAN_WIDTH_20:
266 skb_put_data(skb, &la_path.ru[5], sizeof(la_path.ofdm));
267@@ -6853,13 +6981,53 @@ int mt7996_mcu_set_lpi_psd(struct mt7996_phy *phy, u8 enable)
268 .tag = cpu_to_le16(UNI_BAND_CONFIG_LPI_CTRL),
269 .len = cpu_to_le16(sizeof(req) - 4),
270 .lpi_enable = enable,
271- .psd_limit = enable ? mt7996_is_psd_country(dev->mt76.alpha2) : 0,
272+ .psd_limit = enable && dev->mt76.lpi_mode ?
273+ mt7996_is_psd_country(dev->mt76.alpha2) : 0,
274 };
275
276 return mt76_mcu_send_msg(&phy->dev->mt76, MCU_WM_UNI_CMD(BAND_CONFIG),
277 &req, sizeof(req), false);
278 }
279
280+int mt7996_alloc_afc_table(struct mt7996_dev *dev)
281+{
282+ struct mt76_dev *mdev = &dev->mt76;
283+ int i;
284+
285+ mdev->afc_power_table =
286+ (s8**)devm_kzalloc(dev->mt76.dev,
287+ MAX_CHANNEL_NUM_6G * sizeof(s8*),
288+ GFP_KERNEL);
289+
290+ if (!mdev->afc_power_table)
291+ return -ENOMEM;
292+
293+ for (i = 0; i < MAX_CHANNEL_NUM_6G; i++) {
294+ mdev->afc_power_table[i] =
295+ (s8*)devm_kzalloc(dev->mt76.dev,
296+ afc_power_table_num * sizeof(s8),
297+ GFP_KERNEL);
298+ if (!mdev->afc_power_table[i]) {
299+ mt7996_free_afc_table(dev);
300+ return -ENOMEM;
301+ }
302+ }
303+ return 0;
304+}
305+
306+void mt7996_free_afc_table(struct mt7996_dev *dev)
307+{
308+ struct mt76_dev *mdev = &dev->mt76;
309+ int i;
310+
311+ if (mdev->afc_power_table) {
312+ for (i = 0; i < MAX_CHANNEL_NUM_6G; i++)
313+ devm_kfree(mdev->dev, mdev->afc_power_table[i]);
314+ devm_kfree(mdev->dev, mdev->afc_power_table);
315+ }
316+ mdev->afc_power_table = NULL;
317+}
318+
319 int mt7996_mcu_cp_support(struct mt7996_dev *dev, u8 mode)
320 {
321 __le32 cp_mode;
322diff --git a/mt7996/mcu.h b/mt7996/mcu.h
323index dab4700e..33ba3774 100644
324--- a/mt7996/mcu.h
325+++ b/mt7996/mcu.h
326@@ -1295,6 +1295,145 @@ enum {
327 UNI_TXPOWER_SHOW_INFO = 7,
328 };
329
330+#define MAX_CHANNEL_NUM_6G 59
331+#define AFC_INVALID_POWER 127
332+enum afc_table_info {
333+ afc_power_bw20,
334+ afc_power_bw40,
335+ afc_power_bw80,
336+ afc_power_bw160,
337+ afc_power_bw320_1,
338+ afc_power_bw320_2,
339+ afc_power_ru26,
340+ afc_power_ru52,
341+ afc_power_ru78,
342+ afc_power_ru106,
343+ afc_power_ru132,
344+ afc_power_ru726,
345+ afc_power_ru1480,
346+ afc_power_ru1772,
347+ afc_power_ru2476,
348+ afc_power_ru2988,
349+ afc_power_ru3472,
350+ afc_power_table_num,
351+};
352+
353+static inline int mt7996_get_bw_power_table_idx(int bw, int *mcs, int *ru, int *eht,
354+ int *path)
355+{
356+ switch (bw) {
357+ case afc_power_bw20:
358+ *mcs = 0;
359+ *ru = 3;
360+ *eht = 3;
361+ *path = 5;
362+ break;
363+ case afc_power_bw40:
364+ *mcs = 1;
365+ *ru = 4;
366+ *eht = 4;
367+ *path = 6;
368+ break;
369+ case afc_power_bw80:
370+ *mcs = 2;
371+ *ru = 5;
372+ *eht = 5;
373+ *path = 8;
374+ break;
375+ case afc_power_bw160:
376+ *mcs = 3;
377+ *ru = 6;
378+ *eht = 6;
379+ *path = 11;
380+ break;
381+ case afc_power_bw320_1:
382+ *mcs = -1;
383+ *ru = -1;
384+ *eht = 7;
385+ *path = 15;
386+ break;
387+ case afc_power_bw320_2:
388+ *mcs = -1;
389+ *ru = -1;
390+ *eht = 7;
391+ *path = 15;
392+ break;
393+ case afc_power_ru26:
394+ *mcs = -1;
395+ *ru = 0;
396+ *eht = 0;
397+ *path = 0;
398+ break;
399+ case afc_power_ru52:
400+ *mcs = -1;
401+ *ru = 1;
402+ *eht = 1;
403+ *path = 1;
404+ break;
405+ case afc_power_ru78:
406+ *mcs = -1;
407+ *ru = -1;
408+ *eht = 8;
409+ *path = 2;
410+ break;
411+ case afc_power_ru106:
412+ *mcs = -1;
413+ *ru = 2;
414+ *eht = 2;
415+ *path = 3;
416+ break;
417+ case afc_power_ru132:
418+ *mcs = -1;
419+ *ru = -1;
420+ *eht = 9;
421+ *path = 4;
422+ break;
423+ case afc_power_ru726:
424+ *mcs = -1;
425+ *ru = -1;
426+ *eht = 10;
427+ *path = 7;
428+ break;
429+ case afc_power_ru1480:
430+ *mcs = -1;
431+ *ru = -1;
432+ *eht = 11;
433+ *path = 9;
434+ break;
435+ case afc_power_ru1772:
436+ *mcs = -1;
437+ *ru = -1;
438+ *eht = 12;
439+ *path = 10;
440+ break;
441+ case afc_power_ru2476:
442+ *mcs = -1;
443+ *ru = -1;
444+ *eht = 13;
445+ *path = 12;
446+ break;
447+ case afc_power_ru2988:
448+ *mcs = -1;
449+ *ru = -1;
450+ *eht = 14;
451+ *path = 13;
452+ break;
453+ case afc_power_ru3472:
454+ *mcs = -1;
455+ *ru = -1;
456+ *eht = 15;
457+ *path = 14;
458+ break;
459+ default:
460+ *mcs = -1;
461+ *ru = -1;
462+ *eht = -1;
463+ *path = -1;
464+ return -EINVAL;
465+ }
466+ return 0;
467+}
468+
469 enum {
470 UNI_CMD_ACCESS_REG_BASIC = 0x0,
471 UNI_CMD_ACCESS_RF_REG_BASIC,
472diff --git a/mt7996/mt7996.h b/mt7996/mt7996.h
473index 7866c706..e8571e9f 100644
474--- a/mt7996/mt7996.h
475+++ b/mt7996/mt7996.h
476@@ -1170,6 +1170,8 @@ int mt7996_mcu_set_thermal_throttling(struct mt7996_phy *phy, u8 state);
477 int mt7996_mcu_set_thermal_protect(struct mt7996_phy *phy, bool enable);
478 int mt7996_mcu_set_txpower_sku(struct mt7996_phy *phy,
479 int txpower_setting);
480+int mt7996_alloc_afc_table(struct mt7996_dev *dev);
481+void mt7996_free_afc_table(struct mt7996_dev *dev);
482 int mt7996_mcu_rdd_cmd(struct mt7996_dev *dev, int cmd, u8 index,
483 u8 rx_sel, u8 val);
484 int mt7996_mcu_rdd_background_disable_timer(struct mt7996_dev *dev,
485diff --git a/mt7996/mtk_debugfs.c b/mt7996/mtk_debugfs.c
486index a4703b07..63e63a33 100644
487--- a/mt7996/mtk_debugfs.c
488+++ b/mt7996/mtk_debugfs.c
489@@ -2523,6 +2523,11 @@ mt7996_get_txpower_info(struct file *file, char __user *user_buf,
490 len += scnprintf(buf + len, size - len,
491 " RegDB: %s\n",
492 !np ? "enable" : "disable");
493+ len += scnprintf(buf + len, size - len,
494+ " sku_index: %d\n", phy->mt76->sku_idx);
495+ len += scnprintf(buf + len, size - len,
496+ " lpi: %s\n",
497+ phy->mt76->dev->lpi_mode ? "enable" : "disable");
498 ret = simple_read_from_buffer(user_buf, count, ppos, buf, len);
499
500 out:
501@@ -4390,6 +4395,37 @@ static int mt7996_pp_alg_show(struct seq_file *s, void *data)
502 }
503 DEFINE_SHOW_ATTRIBUTE(mt7996_pp_alg);
504
505+static int mt7996_afc_table_show(struct seq_file *s, void *data)
506+{
507+ struct mt7996_dev *dev = s->private;
508+
509+ char str[200] = {0}, *pos;
510+ char *end = str + sizeof(str);
511+ int i, j;
512+
513+ if (!dev->mt76.afc_power_table || !dev->mt76.afc_power_table[0]) {
514+ seq_printf(s, "afc table doesn't exist.\n");
515+ return 0;
516+ }
517+
518+ seq_printf(s, "bw/ru : 20 40 80 160 320-1 320-2 26 52 78 "
519+ "106 132 726 1480 1772 2476 2988 3472\n");
520+ for(i = 0; i < MAX_CHANNEL_NUM_6G; i ++) {
521+ pos = str;
522+ for (j = 0; j < afc_power_table_num; j ++) {
523+ pos += snprintf(pos, end - pos, "%5d ",
524+ dev->mt76.afc_power_table[i][j]);
525+ }
526+ seq_printf(s, "ch %3d: %s\n", i * 4 + 1, str);
527+ memset(str, 0, sizeof(str));
528+ }
529+ seq_printf(s, "Unit : 0.5 dBm\n");
530+ seq_printf(s, "NOTE : power of the table is translated to single path.\n");
531+
532+ return 0;
533+}
534+DEFINE_SHOW_ATTRIBUTE(mt7996_afc_table);
535+
536 void mt7996_mtk_init_band_debugfs(struct mt7996_phy *phy, struct dentry *dir)
537 {
538 /* agg */
539@@ -4516,6 +4552,7 @@ void mt7996_mtk_init_dev_debugfs(struct mt7996_dev *dev, struct dentry *dir)
540
541 debugfs_create_file("muru_dbg", 0200, dir, dev, &fops_muru_dbg_info);
542 debugfs_create_bool("mgmt_pwr_enhance", 0600, dir, &dev->mt76.mgmt_pwr_enhance);
543+ debugfs_create_file("afc_table", 0200, dir, dev, &mt7996_afc_table_fops);
544 }
545
546 #endif
547diff --git a/mt7996/vendor.c b/mt7996/vendor.c
548index e13a148a..867c277d 100644
549--- a/mt7996/vendor.c
550+++ b/mt7996/vendor.c
551@@ -162,6 +162,8 @@ txpower_ctrl_policy[NUM_MTK_VENDOR_ATTRS_TXPOWER_CTRL] = {
552 [MTK_VENDOR_ATTR_TXPOWER_CTRL_SKU_IDX] = { .type = NLA_U8 },
553 [MTK_VENDOR_ATTR_TXPOWER_CTRL_LPI_BCN_ENHANCE] = { .type = NLA_U8 },
554 [MTK_VENDOR_ATTR_TXPOWER_CTRL_LINK_ID] = { .type = NLA_U8 },
555+ [MTK_VENDOR_ATTR_TXPOWER_CTRL_AFC_TABLE] = { .type = NLA_BINARY },
556+ [MTK_VENDOR_ATTR_TXPOWER_CTRL_AFC_LPI] = { .type = NLA_U8 },
557 };
558
559 struct mt7996_amnt_data {
560@@ -1454,6 +1456,34 @@ out:
561 return err;
562 }
563
564+static int mt7996_parse_afc_table(struct mt7996_dev *dev, struct nlattr *tb, int delta)
565+{
566+ int ch, bw, err = 0;
567+ struct mt76_dev *mdev = &dev->mt76;
568+ s8 **table;
569+
570+ if (!mdev->afc_power_table)
571+ err = mt7996_alloc_afc_table(dev);
572+
573+ if (err) {
574+ mt7996_free_afc_table(dev);
575+ return err;
576+ }
577+
578+ table = nla_data(tb);
579+
580+ for (ch = 0; ch < MAX_CHANNEL_NUM_6G; ch++) {
581+ memcpy(mdev->afc_power_table[ch], table[ch],
582+ afc_power_table_num * sizeof(s8));
583+ for (bw = 0; bw < afc_power_table_num; bw++)
584+ if (mdev->afc_power_table[ch][bw] != AFC_INVALID_POWER)
585+ mdev->afc_power_table[ch][bw] -= delta;
586+ }
587+
588+ return 0;
589+}
590+
591+
592 static int mt7996_vendor_txpower_ctrl(struct wiphy *wiphy,
593 struct wireless_dev *wdev,
594 const void *data,
595@@ -1463,12 +1493,13 @@ static int mt7996_vendor_txpower_ctrl(struct wiphy *wiphy,
596 struct mt7996_dev *dev;
597 struct mt7996_phy *phy;
598 struct mt76_phy *mphy;
599+ struct mt76_dev *mdev;
600 struct ieee80211_vif *vif = wdev_to_ieee80211_vif(wdev);
601 struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv;
602 struct mt7996_bss_conf *mconf;
603- struct nlattr *tb[NUM_MTK_VENDOR_ATTRS_TXPOWER_CTRL];
604- struct mt76_power_limits la = {};
605- struct mt76_power_path_limits la_path = {};
606+ struct nlattr *tb[NUM_MTK_VENDOR_ATTRS_TXPOWER_CTRL], *table;
607+ struct mt76_power_limits *la;
608+ struct mt76_power_path_limits *la_path;
609 int err, current_txpower, delta;
610 u8 val, link_id = 0, idx;
611
612@@ -1496,6 +1527,14 @@ static int mt7996_vendor_txpower_ctrl(struct wiphy *wiphy,
613 rcu_read_unlock();
614
615 mphy = phy->mt76;
616+ mdev = mphy->dev;
617+ delta = mt76_tx_power_nss_delta(hweight16(mphy->chainmask));
618+
619+ if (mphy->cap.has_6ghz &&
620+ tb[MTK_VENDOR_ATTR_TXPOWER_CTRL_AFC_LPI]) {
621+ val = nla_get_u8(tb[MTK_VENDOR_ATTR_TXPOWER_CTRL_AFC_LPI]);
622+ mphy->dev->lpi_mode = !!val;
623+ }
624
625 if (mphy->cap.has_6ghz &&
626 tb[MTK_VENDOR_ATTR_TXPOWER_CTRL_LPI_PSD]) {
627@@ -1504,7 +1543,7 @@ static int mt7996_vendor_txpower_ctrl(struct wiphy *wiphy,
628
629 err = mt7996_mcu_set_lpi_psd(phy, val);
630 if (err)
631- return err;
632+ goto out;
633 }
634
635 if (tb[MTK_VENDOR_ATTR_TXPOWER_CTRL_SKU_IDX]) {
636@@ -1515,19 +1554,22 @@ static int mt7996_vendor_txpower_ctrl(struct wiphy *wiphy,
637
638 phy->sku_limit_en = true;
639 phy->sku_path_en = true;
640- mt76_get_rate_power_limits(mphy, mphy->chandef.chan, &la, &la_path, 127);
641- if (!la_path.ofdm[0])
642+ la = kzalloc(sizeof(struct mt76_power_limits), GFP_KERNEL);
643+ la_path = kzalloc(sizeof(struct mt76_power_path_limits), GFP_KERNEL);
644+
645+ mt76_get_rate_power_limits(mphy, mphy->chandef.chan, la, la_path, 127);
646+ if (!la_path->ofdm[0])
647 phy->sku_path_en = false;
648
649 dev = phy->dev;
650 err = mt7996_mcu_set_tx_power_ctrl(phy, UNI_TXPOWER_SKU_POWER_LIMIT_CTRL,
651 dev->dbg.sku_disable ? 0 : phy->sku_limit_en);
652 if (err)
653- return err;
654+ goto out;
655 err = mt7996_mcu_set_tx_power_ctrl(phy, UNI_TXPOWER_BACKOFF_POWER_LIMIT_CTRL,
656 dev->dbg.sku_disable ? 0 : phy->sku_path_en);
657 if (err)
658- return err;
659+ goto out;
660 }
661
662 if (mphy->cap.has_6ghz &&
663@@ -1536,19 +1578,30 @@ static int mt7996_vendor_txpower_ctrl(struct wiphy *wiphy,
664 mphy->dev->lpi_bcn_enhance = val;
665 idx = MT7996_BEACON_RATES_TBL + 2 * phy->mt76->band_idx;
666
667- err = mt7996_mcu_set_fixed_rate_table(phy, idx, FR_RATE_IDX_OFDM_6M, true);
668+ err = mt7996_mcu_set_fixed_rate_table(phy, idx, FR_RATE_IDX_OFDM_6M,
669+ true);
670 if (err)
671- return err;
672+ goto out;
673+ }
674+
675+ if (mphy->cap.has_6ghz) {
676+ table = tb[MTK_VENDOR_ATTR_TXPOWER_CTRL_AFC_TABLE];
677+ if (table) {
678+ err = mt7996_parse_afc_table(dev, table, delta);
679+ if (err)
680+ goto out;
681+ } else
682+ mt7996_free_afc_table(dev);
683 }
684
685- delta = mt76_tx_power_nss_delta(hweight16(mphy->chainmask));
686 current_txpower = DIV_ROUND_UP(mphy->txpower_cur + delta, 2);
687
688 err = mt7996_mcu_set_txpower_sku(phy, current_txpower);
689- if (err)
690- return err;
691
692- return 0;
693+out:
694+ kfree(la);
695+ kfree(la_path);
696+ return err;
697 }
698
699 static const struct wiphy_vendor_command mt7996_vendor_commands[] = {
700diff --git a/mt7996/vendor.h b/mt7996/vendor.h
701index 71800590..cd84a492 100644
702--- a/mt7996/vendor.h
703+++ b/mt7996/vendor.h
704@@ -338,12 +338,15 @@ enum mtk_vendor_attr_txpower_ctrl {
705 MTK_VENDOR_ATTR_TXPOWER_CTRL_SKU_IDX,
706 MTK_VENDOR_ATTR_TXPOWER_CTRL_LPI_BCN_ENHANCE,
707 MTK_VENDOR_ATTR_TXPOWER_CTRL_LINK_ID,
708+ MTK_VENDOR_ATTR_TXPOWER_CTRL_AFC_TABLE,
709+ MTK_VENDOR_ATTR_TXPOWER_CTRL_AFC_LPI,
710
711 /* keep last */
712 NUM_MTK_VENDOR_ATTRS_TXPOWER_CTRL,
713 MTK_VENDOR_ATTR_TXPOWER_CTRL_MAX =
714 NUM_MTK_VENDOR_ATTRS_TXPOWER_CTRL - 1
715 };
716+
717 #endif
718
719 #endif
720--
7212.18.0
722