]> git.ipfire.org Git - thirdparty/linux.git/commitdiff
wifi: rtw89: constrain TX power according to dynamic antenna power table
authorKuan-Chung Chen <damon.chen@realtek.com>
Wed, 30 Apr 2025 05:51:57 +0000 (13:51 +0800)
committerPing-Ke Shih <pkshih@realtek.com>
Mon, 5 May 2025 02:24:21 +0000 (10:24 +0800)
Dynamic Antenna Gain (DAG) adjusts TX power based on antenna gain. To
prevent signal distortion from excessive power increases, a dynamic
antenna power table limits the maximum adjustable TX power.

Signed-off-by: Kuan-Chung Chen <damon.chen@realtek.com>
Signed-off-by: Ping-Ke Shih <pkshih@realtek.com>
Link: https://patch.msgid.link/20250430055157.13623-3-pkshih@realtek.com
drivers/net/wireless/realtek/rtw89/core.h
drivers/net/wireless/realtek/rtw89/fw.c
drivers/net/wireless/realtek/rtw89/fw.h
drivers/net/wireless/realtek/rtw89/phy.c

index 20c0bab2600a8c18ed2c57d70bdab23990a5403e..dfc578ad8c6490fad2a682f24777bd52e367f027 100644 (file)
@@ -3989,7 +3989,11 @@ struct rtw89_rfe_parms {
        struct rtw89_txpwr_rule_2ghz rule_2ghz;
        struct rtw89_txpwr_rule_5ghz rule_5ghz;
        struct rtw89_txpwr_rule_6ghz rule_6ghz;
+       struct rtw89_txpwr_rule_2ghz rule_da_2ghz;
+       struct rtw89_txpwr_rule_5ghz rule_da_5ghz;
+       struct rtw89_txpwr_rule_6ghz rule_da_6ghz;
        struct rtw89_tx_shape tx_shape;
+       bool has_da;
 };
 
 struct rtw89_rfe_parms_conf {
@@ -4084,9 +4088,15 @@ struct rtw89_rfe_data {
        struct rtw89_txpwr_lmt_2ghz_data lmt_2ghz;
        struct rtw89_txpwr_lmt_5ghz_data lmt_5ghz;
        struct rtw89_txpwr_lmt_6ghz_data lmt_6ghz;
+       struct rtw89_txpwr_lmt_2ghz_data da_lmt_2ghz;
+       struct rtw89_txpwr_lmt_5ghz_data da_lmt_5ghz;
+       struct rtw89_txpwr_lmt_6ghz_data da_lmt_6ghz;
        struct rtw89_txpwr_lmt_ru_2ghz_data lmt_ru_2ghz;
        struct rtw89_txpwr_lmt_ru_5ghz_data lmt_ru_5ghz;
        struct rtw89_txpwr_lmt_ru_6ghz_data lmt_ru_6ghz;
+       struct rtw89_txpwr_lmt_ru_2ghz_data da_lmt_ru_2ghz;
+       struct rtw89_txpwr_lmt_ru_5ghz_data da_lmt_ru_5ghz;
+       struct rtw89_txpwr_lmt_ru_6ghz_data da_lmt_ru_6ghz;
        struct rtw89_tx_shape_lmt_data tx_shape_lmt;
        struct rtw89_tx_shape_lmt_ru_data tx_shape_lmt_ru;
        struct rtw89_rfe_parms rfe_parms;
index 4a6455555daed45abee385654efbfd66eff75f72..23f4dea64dc6517eabfa633c4a8408af7859e213 100644 (file)
@@ -1301,6 +1301,18 @@ static const struct rtw89_fw_element_handler __fw_element_handlers[] = {
                rtw89_fw_recognize_txpwr_from_elm,
                { .offset = offsetof(struct rtw89_rfe_data, lmt_6ghz.conf) }, NULL,
        },
+       [RTW89_FW_ELEMENT_ID_TXPWR_DA_LMT_2GHZ] = {
+               rtw89_fw_recognize_txpwr_from_elm,
+               { .offset = offsetof(struct rtw89_rfe_data, da_lmt_2ghz.conf) }, NULL,
+       },
+       [RTW89_FW_ELEMENT_ID_TXPWR_DA_LMT_5GHZ] = {
+               rtw89_fw_recognize_txpwr_from_elm,
+               { .offset = offsetof(struct rtw89_rfe_data, da_lmt_5ghz.conf) }, NULL,
+       },
+       [RTW89_FW_ELEMENT_ID_TXPWR_DA_LMT_6GHZ] = {
+               rtw89_fw_recognize_txpwr_from_elm,
+               { .offset = offsetof(struct rtw89_rfe_data, da_lmt_6ghz.conf) }, NULL,
+       },
        [RTW89_FW_ELEMENT_ID_TXPWR_LMT_RU_2GHZ] = {
                rtw89_fw_recognize_txpwr_from_elm,
                { .offset = offsetof(struct rtw89_rfe_data, lmt_ru_2ghz.conf) }, NULL,
@@ -1313,6 +1325,18 @@ static const struct rtw89_fw_element_handler __fw_element_handlers[] = {
                rtw89_fw_recognize_txpwr_from_elm,
                { .offset = offsetof(struct rtw89_rfe_data, lmt_ru_6ghz.conf) }, NULL,
        },
+       [RTW89_FW_ELEMENT_ID_TXPWR_DA_LMT_RU_2GHZ] = {
+               rtw89_fw_recognize_txpwr_from_elm,
+               { .offset = offsetof(struct rtw89_rfe_data, da_lmt_ru_2ghz.conf) }, NULL,
+       },
+       [RTW89_FW_ELEMENT_ID_TXPWR_DA_LMT_RU_5GHZ] = {
+               rtw89_fw_recognize_txpwr_from_elm,
+               { .offset = offsetof(struct rtw89_rfe_data, da_lmt_ru_5ghz.conf) }, NULL,
+       },
+       [RTW89_FW_ELEMENT_ID_TXPWR_DA_LMT_RU_6GHZ] = {
+               rtw89_fw_recognize_txpwr_from_elm,
+               { .offset = offsetof(struct rtw89_rfe_data, da_lmt_ru_6ghz.conf) }, NULL,
+       },
        [RTW89_FW_ELEMENT_ID_TX_SHAPE_LMT] = {
                rtw89_fw_recognize_txpwr_from_elm,
                { .offset = offsetof(struct rtw89_rfe_data, tx_shape_lmt.conf) }, NULL,
@@ -9319,6 +9343,26 @@ void rtw89_fw_load_tx_shape_lmt_ru(struct rtw89_tx_shape_lmt_ru_data *data)
        }
 }
 
+static bool rtw89_fw_has_da_txpwr_table(struct rtw89_dev *rtwdev,
+                                       const struct rtw89_rfe_parms *parms)
+{
+       const struct rtw89_chip_info *chip = rtwdev->chip;
+
+       if (chip->support_bands & BIT(NL80211_BAND_2GHZ) &&
+           !(parms->rule_da_2ghz.lmt && parms->rule_da_2ghz.lmt_ru))
+               return false;
+
+       if (chip->support_bands & BIT(NL80211_BAND_5GHZ) &&
+           !(parms->rule_da_5ghz.lmt && parms->rule_da_5ghz.lmt_ru))
+               return false;
+
+       if (chip->support_bands & BIT(NL80211_BAND_6GHZ) &&
+           !(parms->rule_da_6ghz.lmt && parms->rule_da_6ghz.lmt_ru))
+               return false;
+
+       return true;
+}
+
 const struct rtw89_rfe_parms *
 rtw89_load_rfe_data_from_fw(struct rtw89_dev *rtwdev,
                            const struct rtw89_rfe_parms *init)
@@ -9355,6 +9399,21 @@ rtw89_load_rfe_data_from_fw(struct rtw89_dev *rtwdev,
                parms->rule_6ghz.lmt = &rfe_data->lmt_6ghz.v;
        }
 
+       if (rtw89_txpwr_conf_valid(&rfe_data->da_lmt_2ghz.conf)) {
+               rtw89_fw_load_txpwr_lmt_2ghz(&rfe_data->da_lmt_2ghz);
+               parms->rule_da_2ghz.lmt = &rfe_data->da_lmt_2ghz.v;
+       }
+
+       if (rtw89_txpwr_conf_valid(&rfe_data->da_lmt_5ghz.conf)) {
+               rtw89_fw_load_txpwr_lmt_5ghz(&rfe_data->da_lmt_5ghz);
+               parms->rule_da_5ghz.lmt = &rfe_data->da_lmt_5ghz.v;
+       }
+
+       if (rtw89_txpwr_conf_valid(&rfe_data->da_lmt_6ghz.conf)) {
+               rtw89_fw_load_txpwr_lmt_6ghz(&rfe_data->da_lmt_6ghz);
+               parms->rule_da_6ghz.lmt = &rfe_data->da_lmt_6ghz.v;
+       }
+
        if (rtw89_txpwr_conf_valid(&rfe_data->lmt_ru_2ghz.conf)) {
                rtw89_fw_load_txpwr_lmt_ru_2ghz(&rfe_data->lmt_ru_2ghz);
                parms->rule_2ghz.lmt_ru = &rfe_data->lmt_ru_2ghz.v;
@@ -9370,6 +9429,21 @@ rtw89_load_rfe_data_from_fw(struct rtw89_dev *rtwdev,
                parms->rule_6ghz.lmt_ru = &rfe_data->lmt_ru_6ghz.v;
        }
 
+       if (rtw89_txpwr_conf_valid(&rfe_data->da_lmt_ru_2ghz.conf)) {
+               rtw89_fw_load_txpwr_lmt_ru_2ghz(&rfe_data->da_lmt_ru_2ghz);
+               parms->rule_da_2ghz.lmt_ru = &rfe_data->da_lmt_ru_2ghz.v;
+       }
+
+       if (rtw89_txpwr_conf_valid(&rfe_data->da_lmt_ru_5ghz.conf)) {
+               rtw89_fw_load_txpwr_lmt_ru_5ghz(&rfe_data->da_lmt_ru_5ghz);
+               parms->rule_da_5ghz.lmt_ru = &rfe_data->da_lmt_ru_5ghz.v;
+       }
+
+       if (rtw89_txpwr_conf_valid(&rfe_data->da_lmt_ru_6ghz.conf)) {
+               rtw89_fw_load_txpwr_lmt_ru_6ghz(&rfe_data->da_lmt_ru_6ghz);
+               parms->rule_da_6ghz.lmt_ru = &rfe_data->da_lmt_ru_6ghz.v;
+       }
+
        if (rtw89_txpwr_conf_valid(&rfe_data->tx_shape_lmt.conf)) {
                rtw89_fw_load_tx_shape_lmt(&rfe_data->tx_shape_lmt);
                parms->tx_shape.lmt = &rfe_data->tx_shape_lmt.v;
@@ -9380,5 +9454,7 @@ rtw89_load_rfe_data_from_fw(struct rtw89_dev *rtwdev,
                parms->tx_shape.lmt_ru = &rfe_data->tx_shape_lmt_ru.v;
        }
 
+       parms->has_da = rtw89_fw_has_da_txpwr_table(rtwdev, parms);
+
        return parms;
 }
index bb1b653085cf9492d8d7f5a8aaef8dd20e358cce..559f3f6190ebde6bc843c8c64f590a69eed9b3ef 100644 (file)
@@ -3908,6 +3908,12 @@ enum rtw89_fw_element_id {
        RTW89_FW_ELEMENT_ID_TXPWR_TRK = 18,
        RTW89_FW_ELEMENT_ID_RFKLOG_FMT = 19,
        RTW89_FW_ELEMENT_ID_REGD = 20,
+       RTW89_FW_ELEMENT_ID_TXPWR_DA_LMT_2GHZ = 21,
+       RTW89_FW_ELEMENT_ID_TXPWR_DA_LMT_5GHZ = 22,
+       RTW89_FW_ELEMENT_ID_TXPWR_DA_LMT_6GHZ = 23,
+       RTW89_FW_ELEMENT_ID_TXPWR_DA_LMT_RU_2GHZ = 24,
+       RTW89_FW_ELEMENT_ID_TXPWR_DA_LMT_RU_5GHZ = 25,
+       RTW89_FW_ELEMENT_ID_TXPWR_DA_LMT_RU_6GHZ = 26,
 
        RTW89_FW_ELEMENT_ID_NUM,
 };
index e358cb83dae802ee264813cfd29a47193967771c..76a2e26d4a10b429e03e6b046d7f0e4112020a12 100644 (file)
@@ -2034,19 +2034,10 @@ static s8 rtw89_phy_ant_gain_query(struct rtw89_dev *rtwdev,
                   ant_gain->offset[path][subband_h]);
 }
 
-static s8 rtw89_phy_ant_gain_offset(struct rtw89_dev *rtwdev, u8 band, u32 center_freq)
+static s8 rtw89_phy_ant_gain_offset(struct rtw89_dev *rtwdev, u32 center_freq)
 {
-       struct rtw89_ant_gain_info *ant_gain = &rtwdev->ant_gain;
-       const struct rtw89_chip_info *chip = rtwdev->chip;
-       u8 regd = rtw89_regd_get(rtwdev, band);
        s8 offset_patha, offset_pathb;
 
-       if (!chip->support_ant_gain)
-               return 0;
-
-       if (ant_gain->block_country || !(ant_gain->regd_enabled & BIT(regd)))
-               return 0;
-
        offset_patha = rtw89_phy_ant_gain_query(rtwdev, RF_PATH_A, center_freq);
        offset_pathb = rtw89_phy_ant_gain_query(rtwdev, RF_PATH_B, center_freq);
 
@@ -2056,18 +2047,31 @@ static s8 rtw89_phy_ant_gain_offset(struct rtw89_dev *rtwdev, u8 band, u32 cente
        return max(offset_patha, offset_pathb);
 }
 
-s16 rtw89_phy_ant_gain_pwr_offset(struct rtw89_dev *rtwdev,
-                                 const struct rtw89_chan *chan)
+static bool rtw89_can_apply_ant_gain(struct rtw89_dev *rtwdev, u8 band)
 {
+       const struct rtw89_rfe_parms *rfe_parms = rtwdev->rfe_parms;
        struct rtw89_ant_gain_info *ant_gain = &rtwdev->ant_gain;
        const struct rtw89_chip_info *chip = rtwdev->chip;
-       u8 regd = rtw89_regd_get(rtwdev, chan->band_type);
-       s8 offset_patha, offset_pathb;
+       u8 regd = rtw89_regd_get(rtwdev, band);
 
        if (!chip->support_ant_gain)
-               return 0;
+               return false;
 
        if (ant_gain->block_country || !(ant_gain->regd_enabled & BIT(regd)))
+               return false;
+
+       if (!rfe_parms->has_da)
+               return false;
+
+       return true;
+}
+
+s16 rtw89_phy_ant_gain_pwr_offset(struct rtw89_dev *rtwdev,
+                                 const struct rtw89_chan *chan)
+{
+       s8 offset_patha, offset_pathb;
+
+       if (!rtw89_can_apply_ant_gain(rtwdev, chan->band_type))
                return 0;
 
        if (RTW89_CHK_FW_FEATURE(NO_POWER_DIFFERENCE, &rtwdev->fw))
@@ -2083,14 +2087,10 @@ EXPORT_SYMBOL(rtw89_phy_ant_gain_pwr_offset);
 int rtw89_print_ant_gain(struct rtw89_dev *rtwdev, char *buf, size_t bufsz,
                         const struct rtw89_chan *chan)
 {
-       struct rtw89_ant_gain_info *ant_gain = &rtwdev->ant_gain;
-       const struct rtw89_chip_info *chip = rtwdev->chip;
-       u8 regd = rtw89_regd_get(rtwdev, chan->band_type);
        char *p = buf, *end = buf + bufsz;
        s8 offset_patha, offset_pathb;
 
-       if (!(chip->support_ant_gain && (ant_gain->regd_enabled & BIT(regd))) ||
-           ant_gain->block_country) {
+       if (!rtw89_can_apply_ant_gain(rtwdev, chan->band_type)) {
                p += scnprintf(p, end - p, "no DAG is applied\n");
                goto out;
        }
@@ -2255,24 +2255,31 @@ s8 rtw89_phy_read_txpwr_limit(struct rtw89_dev *rtwdev, u8 band,
                              u8 bw, u8 ntx, u8 rs, u8 bf, u8 ch)
 {
        const struct rtw89_rfe_parms *rfe_parms = rtwdev->rfe_parms;
+       const struct rtw89_txpwr_rule_2ghz *rule_da_2ghz = &rfe_parms->rule_da_2ghz;
+       const struct rtw89_txpwr_rule_5ghz *rule_da_5ghz = &rfe_parms->rule_da_5ghz;
+       const struct rtw89_txpwr_rule_6ghz *rule_da_6ghz = &rfe_parms->rule_da_6ghz;
        const struct rtw89_txpwr_rule_2ghz *rule_2ghz = &rfe_parms->rule_2ghz;
        const struct rtw89_txpwr_rule_5ghz *rule_5ghz = &rfe_parms->rule_5ghz;
        const struct rtw89_txpwr_rule_6ghz *rule_6ghz = &rfe_parms->rule_6ghz;
        struct rtw89_regulatory_info *regulatory = &rtwdev->regulatory;
        enum nl80211_band nl_band = rtw89_hw_to_nl80211_band(band);
+       bool has_ant_gain = rtw89_can_apply_ant_gain(rtwdev, band);
        u32 freq = ieee80211_channel_to_frequency(ch, nl_band);
        u8 ch_idx = rtw89_channel_to_idx(rtwdev, band, ch);
+       s8 lmt = 0, da_lmt = S8_MAX, sar, offset = 0;
        u8 regd = rtw89_regd_get(rtwdev, band);
        u8 reg6 = regulatory->reg_6ghz_power;
        struct rtw89_sar_parm sar_parm = {
                .center_freq = freq,
                .ntx = ntx,
        };
-       s8 lmt = 0, sar, offset;
        s8 cstr;
 
        switch (band) {
        case RTW89_BAND_2G:
+               if (has_ant_gain)
+                       da_lmt = (*rule_da_2ghz->lmt)[bw][ntx][rs][bf][regd][ch_idx];
+
                lmt = (*rule_2ghz->lmt)[bw][ntx][rs][bf][regd][ch_idx];
                if (lmt)
                        break;
@@ -2280,6 +2287,9 @@ s8 rtw89_phy_read_txpwr_limit(struct rtw89_dev *rtwdev, u8 band,
                lmt = (*rule_2ghz->lmt)[bw][ntx][rs][bf][RTW89_WW][ch_idx];
                break;
        case RTW89_BAND_5G:
+               if (has_ant_gain)
+                       da_lmt = (*rule_da_5ghz->lmt)[bw][ntx][rs][bf][regd][ch_idx];
+
                lmt = (*rule_5ghz->lmt)[bw][ntx][rs][bf][regd][ch_idx];
                if (lmt)
                        break;
@@ -2287,6 +2297,9 @@ s8 rtw89_phy_read_txpwr_limit(struct rtw89_dev *rtwdev, u8 band,
                lmt = (*rule_5ghz->lmt)[bw][ntx][rs][bf][RTW89_WW][ch_idx];
                break;
        case RTW89_BAND_6G:
+               if (has_ant_gain)
+                       da_lmt = (*rule_da_6ghz->lmt)[bw][ntx][rs][bf][regd][reg6][ch_idx];
+
                lmt = (*rule_6ghz->lmt)[bw][ntx][rs][bf][regd][reg6][ch_idx];
                if (lmt)
                        break;
@@ -2300,8 +2313,11 @@ s8 rtw89_phy_read_txpwr_limit(struct rtw89_dev *rtwdev, u8 band,
                return 0;
        }
 
-       offset = rtw89_phy_ant_gain_offset(rtwdev, band, freq);
-       lmt = rtw89_phy_txpwr_rf_to_mac(rtwdev, lmt + offset);
+       da_lmt = da_lmt ?: S8_MAX;
+       if (da_lmt != S8_MAX)
+               offset = rtw89_phy_ant_gain_offset(rtwdev, freq);
+
+       lmt = rtw89_phy_txpwr_rf_to_mac(rtwdev, min(lmt + offset, da_lmt));
        sar = rtw89_query_sar(rtwdev, &sar_parm);
        cstr = rtw89_phy_get_tpe_constraint(rtwdev, band);
 
@@ -2519,24 +2535,31 @@ s8 rtw89_phy_read_txpwr_limit_ru(struct rtw89_dev *rtwdev, u8 band,
                                 u8 ru, u8 ntx, u8 ch)
 {
        const struct rtw89_rfe_parms *rfe_parms = rtwdev->rfe_parms;
+       const struct rtw89_txpwr_rule_2ghz *rule_da_2ghz = &rfe_parms->rule_da_2ghz;
+       const struct rtw89_txpwr_rule_5ghz *rule_da_5ghz = &rfe_parms->rule_da_5ghz;
+       const struct rtw89_txpwr_rule_6ghz *rule_da_6ghz = &rfe_parms->rule_da_6ghz;
        const struct rtw89_txpwr_rule_2ghz *rule_2ghz = &rfe_parms->rule_2ghz;
        const struct rtw89_txpwr_rule_5ghz *rule_5ghz = &rfe_parms->rule_5ghz;
        const struct rtw89_txpwr_rule_6ghz *rule_6ghz = &rfe_parms->rule_6ghz;
        struct rtw89_regulatory_info *regulatory = &rtwdev->regulatory;
        enum nl80211_band nl_band = rtw89_hw_to_nl80211_band(band);
+       bool has_ant_gain = rtw89_can_apply_ant_gain(rtwdev, band);
        u32 freq = ieee80211_channel_to_frequency(ch, nl_band);
        u8 ch_idx = rtw89_channel_to_idx(rtwdev, band, ch);
+       s8 lmt_ru = 0, da_lmt_ru = S8_MAX, sar, offset = 0;
        u8 regd = rtw89_regd_get(rtwdev, band);
        u8 reg6 = regulatory->reg_6ghz_power;
        struct rtw89_sar_parm sar_parm = {
                .center_freq = freq,
                .ntx = ntx,
        };
-       s8 lmt_ru = 0, sar, offset;
        s8 cstr;
 
        switch (band) {
        case RTW89_BAND_2G:
+               if (has_ant_gain)
+                       da_lmt_ru = (*rule_da_2ghz->lmt_ru)[ru][ntx][regd][ch_idx];
+
                lmt_ru = (*rule_2ghz->lmt_ru)[ru][ntx][regd][ch_idx];
                if (lmt_ru)
                        break;
@@ -2544,6 +2567,9 @@ s8 rtw89_phy_read_txpwr_limit_ru(struct rtw89_dev *rtwdev, u8 band,
                lmt_ru = (*rule_2ghz->lmt_ru)[ru][ntx][RTW89_WW][ch_idx];
                break;
        case RTW89_BAND_5G:
+               if (has_ant_gain)
+                       da_lmt_ru = (*rule_da_5ghz->lmt_ru)[ru][ntx][regd][ch_idx];
+
                lmt_ru = (*rule_5ghz->lmt_ru)[ru][ntx][regd][ch_idx];
                if (lmt_ru)
                        break;
@@ -2551,6 +2577,9 @@ s8 rtw89_phy_read_txpwr_limit_ru(struct rtw89_dev *rtwdev, u8 band,
                lmt_ru = (*rule_5ghz->lmt_ru)[ru][ntx][RTW89_WW][ch_idx];
                break;
        case RTW89_BAND_6G:
+               if (has_ant_gain)
+                       da_lmt_ru = (*rule_da_6ghz->lmt_ru)[ru][ntx][regd][reg6][ch_idx];
+
                lmt_ru = (*rule_6ghz->lmt_ru)[ru][ntx][regd][reg6][ch_idx];
                if (lmt_ru)
                        break;
@@ -2564,8 +2593,11 @@ s8 rtw89_phy_read_txpwr_limit_ru(struct rtw89_dev *rtwdev, u8 band,
                return 0;
        }
 
-       offset = rtw89_phy_ant_gain_offset(rtwdev, band, freq);
-       lmt_ru = rtw89_phy_txpwr_rf_to_mac(rtwdev, lmt_ru + offset);
+       da_lmt_ru = da_lmt_ru ?: S8_MAX;
+       if (da_lmt_ru != S8_MAX)
+               offset = rtw89_phy_ant_gain_offset(rtwdev, freq);
+
+       lmt_ru = rtw89_phy_txpwr_rf_to_mac(rtwdev, min(lmt_ru + offset, da_lmt_ru));
        sar = rtw89_query_sar(rtwdev, &sar_parm);
        cstr = rtw89_phy_get_tpe_constraint(rtwdev, band);