The mt7921 driver never updates phy->txpower_cur from the rate power configuration sent to firmware, causing mt76_get_txpower() to report bogus values to userspace (typically 3 dBm) regardless of actual regulatory or SAR limits. Two issues are addressed: 1. The rate power loop in mt76_connac_mcu_rate_txpower_band() computes the correct bounded TX power per channel via mt76_get_rate_power_limits() but discards the return value. Capture it and store to phy->txpower_cur when processing the current channel, matching how mt7915 handles this in mt7915_mcu_set_txpower_sku(). Subtract the multi-chain path delta before storing, since mt76_get_txpower() adds it back when reporting -- consistent with mt7915's use of mt76_get_power_bound() which performs the same subtraction. 2. mt7921 uses the chanctx model but its add_chanctx callback does not update phy->chandef, leaving it stale after association. The rate power loop's channel comparison then fails silently. Sync phy->chandef from ctx->def in add_chanctx and change_chanctx, and recompute txpower_cur via a lightweight helper that performs the same bounded power calculation for the current channel without reprogramming firmware rate tables. Tested on Alfa AWUS036AXML (MT7921AU), kernel 6.19.8, Canada: Before: iw dev wlan0 info shows "txpower 3.00 dBm" (wrong) After: 2.4GHz 36 dBm, 5GHz 23 dBm, 6GHz 12 dBm (match regulatory) Cc: stable@vger.kernel.org Fixes: 1c099ab44727 ("mt76: mt7921: add MAC support") Signed-off-by: Lucid Duck --- Changes since v3: - Removed mt7921_set_tx_sar_pwr() from add_chanctx and change_chanctx. Channel transitions don't change underlying power constraints, so reprogramming the full rate table is unnecessary. Replaced with a lightweight helper that recomputes txpower_cur locally. - Removed IEEE80211_CONF_CHANGE_CHANNEL trigger from config(). - Removed BSS_CHANGED_TXPOWER handler from bss_info_changed(). Writing per-vif txpower into per-HW hw->conf.power_level breaks multi-vif semantics. User txpower limits need a different approach (follow-up). - Subtracted path delta before storing txpower_cur. The connac rate loop stores total bounded power, but mt76_get_txpower() adds the multi-chain path delta when reporting. mt7915 accounts for this via mt76_get_power_bound(), which subtracts the delta before storing. Without the same subtraction, reported values were inflated by 3 dBm on 2x2 devices. .../wireless/mediatek/mt76/mt76_connac_mcu.c | 14 +++++++-- .../net/wireless/mediatek/mt76/mt7921/main.c | 30 +++++++++++++++++++ 2 files changed, 41 insertions(+), 3 deletions(-) diff --git a/drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.c b/drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.c index 16db0f208..e26a2cb39 100644 --- a/drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.c +++ b/drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.c @@ -2193,14 +2193,22 @@ mt76_connac_mcu_rate_txpower_band(struct mt76_phy *phy, .hw_value = ch_list[idx], .band = band, }; - s8 reg_power, sar_power; + s8 reg_power, sar_power, max_power; reg_power = mt76_connac_get_ch_power(phy, &chan, tx_power); sar_power = mt76_get_sar_power(phy, &chan, reg_power); - mt76_get_rate_power_limits(phy, &chan, limits, - sar_power); + max_power = mt76_get_rate_power_limits(phy, &chan, + limits, + sar_power); + + if (phy->chandef.chan && + phy->chandef.chan->hw_value == ch_list[idx] && + phy->chandef.chan->band == band) + phy->txpower_cur = max_power - + mt76_tx_power_path_delta( + hweight16(phy->chainmask)); tx_power_tlv.last_msg = ch_list[idx] == last_ch; sku_tlbv.channel = ch_list[idx]; diff --git a/drivers/net/wireless/mediatek/mt76/mt7921/main.c b/drivers/net/wireless/mediatek/mt76/mt7921/main.c index 5881040ac..a77ae5791 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7921/main.c +++ b/drivers/net/wireless/mediatek/mt76/mt7921/main.c @@ -1355,13 +1355,39 @@ mt7921_stop_ap(struct ieee80211_hw *hw, struct ieee80211_vif *vif, mt792x_mutex_release(dev); } +static void mt7921_update_txpower_cur(struct mt76_phy *phy) +{ + struct mt76_power_limits limits; + struct ieee80211_channel *chan = phy->chandef.chan; + int n_chains = hweight16(phy->chainmask); + s8 reg_power, sar_power, max_power; + int tx_power; + + if (!chan) + return; + + tx_power = 2 * phy->hw->conf.power_level; + if (!tx_power) + tx_power = 127; + + reg_power = mt76_connac_get_ch_power(phy, chan, tx_power); + sar_power = mt76_get_sar_power(phy, chan, reg_power); + max_power = mt76_get_rate_power_limits(phy, chan, &limits, sar_power); + + phy->txpower_cur = max_power - mt76_tx_power_path_delta(n_chains); +} + static int mt7921_add_chanctx(struct ieee80211_hw *hw, struct ieee80211_chanctx_conf *ctx) { struct mt792x_dev *dev = mt792x_hw_dev(hw); + struct mt76_phy *mphy = hw->priv; dev->new_ctx = ctx; + mphy->chandef = ctx->def; + mt7921_update_txpower_cur(mphy); + return 0; } @@ -1396,6 +1422,10 @@ mt7921_change_chanctx(struct ieee80211_hw *hw, mt7921_mcu_config_sniffer(mvif, ctx); else mt76_connac_mcu_uni_set_chctx(mvif->phy->mt76, &mvif->bss_conf.mt76, ctx); + + phy->mt76->chandef = ctx->def; + mt7921_update_txpower_cur(phy->mt76); + mt792x_mutex_release(phy->dev); } -- 2.53.0