[PATCH v2 1/2] mt76: mt7921: add mt7921-specific get_txpower callback

Lucid Duck lucid_duck at justthetip.ca
Thu Mar 12 10:38:12 PDT 2026


Hi Bryam, Sean,

I tested this on my MT7921AU USB adapter (Alfa AWUS036AXML, Fedora,
kernel 6.18.16, ISED/Canada). The callback itself is clean and with
one fix it gives correct values on all three bands, stable across a
full gauntlet. Nice work.


chanctx channel source (affects all mt7921 phy->chandef readers)
-----------------------------------------------------------------

This one bit me during testing and is worth flagging because it
affects more than just the callback. mt7921 uses the chanctx model,
and mt7921_add_chanctx() stores the context in dev->new_ctx without
updating phy->chandef. So phy->chandef.chan stays stuck on whatever
channel the firmware scanned at probe time -- channel 229 (6 GHz) on
my system. I confirmed via debugfs: "Tx power table (channel 229)"
while actually connected to 2.4 GHz ch6.

Because the callback reads phy->chandef.chan->max_power, it returns
12 dBm on every band. mt76_get_power_bound() has the same issue
internally since it also reads phy->chandef.chan.

The fix that worked for me: read the channel from the VIF chanctx
(falling back to phy->chandef.chan when unassociated) and inline the
SAR + delta math to avoid get_power_bound:

    rcu_read_lock();
    ctx = rcu_dereference(vif->bss_conf.chanctx_conf);
    chan = ctx ? ctx->def.chan : phy->chandef.chan;
    rcu_read_unlock();

    if (!chan)
        return mt76_get_txpower(hw, vif, link_id, dbm);

    n_chains = hweight16(phy->chainmask);
    delta = mt76_tx_power_path_delta(n_chains);
    tx_power = mt76_get_sar_power(phy, chan, chan->max_power * 2);
    tx_power -= delta;
    tx_power = mt76_get_rate_power_limits(phy, chan, &limits, tx_power);
    *dbm = DIV_ROUND_UP(tx_power + delta, 2);


A note on Sean's rate power loop direction
-------------------------------------------

Sean's feedback on both our patches points toward deriving the
reported value from mt76_connac_mcu_rate_txpower_band() -- the loop
that computes per-rate power limits before sending SKU tables to
firmware. That would sidestep the chanctx issue entirely since
txpower_cur gets written at configuration time rather than computed
at query time.

Looking at the code, mt7915 already does this (mt7915/mcu.c:3396):

    tx_power = mt76_get_rate_power_limits(...);
    mphy->txpower_cur = tx_power;

The connac equivalent calls mt76_get_rate_power_limits() in the same
loop but discards the return value. If that were fixed to match
mt7915, the existing mt76_get_txpower() would report correctly
without a per-driver callback.

That approach would also cover user txpower limits for free --
`iw dev set txpower fixed 1500` currently succeeds but the callback
reports the regulatory ceiling because it starts from chan->max_power.
The rate power loop already reads 2 * phy->hw->conf.power_level
(mt76_connac_mcu.c:2162), so user limits would be reflected
automatically.

Either way, happy to help test whatever direction v3 takes.


Test results (chanctx fix applied)
------------------------------------

Alfa AWUS036AXML (MT7921AU, USB, 2x2 MIMO)
Kernel 6.18.16-200.fc43.x86_64, ISED/Canada

    Per-band (10 runs each):
      2.4 GHz ch6:   30.00 dBm  (ISED: 30)   10/10 PASS
      5 GHz ch100:   26.00 dBm  (ISED: 26)   10/10 PASS
      6 GHz ch5:     12.00 dBm  (ISED: 12)   10/10 PASS

    Band switching:           18/18 correct
    Rapid cycling (20/band):  60/60 PASS
    Regdomain (CA/US/CA):     9/9 PASS (5 GHz: 26->24->26)
    60-min soak (5 GHz):      61/61 PASS, zero drift
    USB torture (module reload, monitor cycling, assoc interrupt):
                              20/20 PASS
    Kernel errors:            zero across all phases

Tested-by: Lucid Duck <lucid_duck at justthetip.ca>



More information about the Linux-mediatek mailing list