#define HE_PHY(p, c)                   u8_get_bits(c, IEEE80211_HE_PHY_##p)
 #define HE_MAC(m, c)                   u8_get_bits(c, IEEE80211_HE_MAC_##m)
 
+static bool sr_scene_detect = true;
+module_param(sr_scene_detect, bool, 0644);
+MODULE_PARM_DESC(sr_scene_detect, "Enable firmware scene detection algorithm");
+
 static u8
 mt7915_mcu_get_sta_nss(u16 mcs_map)
 {
                                 sizeof(req), true);
 }
 
-int mt7915_mcu_add_obss_spr(struct mt7915_dev *dev, struct ieee80211_vif *vif,
-                           bool enable)
+static int
+mt7915_mcu_enable_obss_spr(struct mt7915_phy *phy, u8 action, u8 val)
+{
+       struct mt7915_dev *dev = phy->dev;
+       struct mt7915_mcu_sr_ctrl req = {
+               .action = action,
+               .argnum = 1,
+               .band_idx = phy != &dev->phy,
+               .val = cpu_to_le32(val),
+       };
+
+       return mt76_mcu_send_msg(&dev->mt76, MCU_EXT_CMD(SET_SPR), &req,
+                                sizeof(req), true);
+}
+
+static int
+mt7915_mcu_set_obss_spr_pd(struct mt7915_phy *phy,
+                          struct ieee80211_he_obss_pd *he_obss_pd)
+{
+       struct mt7915_dev *dev = phy->dev;
+       struct {
+               struct mt7915_mcu_sr_ctrl ctrl;
+               struct {
+                       u8 pd_th_non_srg;
+                       u8 pd_th_srg;
+                       u8 period_offs;
+                       u8 rcpi_src;
+                       __le16 obss_pd_min;
+                       __le16 obss_pd_min_srg;
+                       u8 resp_txpwr_mode;
+                       u8 txpwr_restrict_mode;
+                       u8 txpwr_ref;
+                       u8 rsv[3];
+               } __packed param;
+       } __packed req = {
+               .ctrl = {
+                       .action = SPR_SET_PARAM,
+                       .argnum = 9,
+                       .band_idx = phy != &dev->phy,
+               },
+       };
+       int ret;
+       u8 max_th = 82, non_srg_max_th = 62;
+
+       /* disable firmware dynamical PD asjustment */
+       ret = mt7915_mcu_enable_obss_spr(phy, SPR_ENABLE_DPD, false);
+       if (ret)
+               return ret;
+
+       if (he_obss_pd->sr_ctrl &
+           IEEE80211_HE_SPR_NON_SRG_OBSS_PD_SR_DISALLOWED)
+               req.param.pd_th_non_srg = max_th;
+       else if (he_obss_pd->sr_ctrl & IEEE80211_HE_SPR_NON_SRG_OFFSET_PRESENT)
+               req.param.pd_th_non_srg  = max_th - he_obss_pd->non_srg_max_offset;
+       else
+               req.param.pd_th_non_srg  = non_srg_max_th;
+
+       if (he_obss_pd->sr_ctrl & IEEE80211_HE_SPR_SRG_INFORMATION_PRESENT)
+               req.param.pd_th_srg = max_th - he_obss_pd->max_offset;
+
+       req.param.obss_pd_min = cpu_to_le16(82);
+       req.param.obss_pd_min_srg = cpu_to_le16(82);
+       req.param.txpwr_restrict_mode = 2;
+       req.param.txpwr_ref = 21;
+
+       return mt76_mcu_send_msg(&dev->mt76, MCU_EXT_CMD(SET_SPR), &req,
+                                sizeof(req), true);
+}
+
+static int
+mt7915_mcu_set_obss_spr_siga(struct mt7915_phy *phy, struct ieee80211_vif *vif,
+                            struct ieee80211_he_obss_pd *he_obss_pd)
 {
-#define MT_SPR_ENABLE          1
        struct mt7915_vif *mvif = (struct mt7915_vif *)vif->drv_priv;
+       struct mt7915_dev *dev = phy->dev;
+       u8 omac = mvif->mt76.omac_idx;
        struct {
-               u8 action;
-               u8 arg_num;
-               u8 band_idx;
-               u8 status;
-               u8 drop_tx_idx;
-               u8 sta_idx;     /* 256 sta */
-               u8 rsv[2];
-               __le32 val;
+               struct mt7915_mcu_sr_ctrl ctrl;
+               struct {
+                       u8 omac;
+                       u8 rsv[3];
+                       u8 flag[20];
+               } __packed siga;
+       } __packed req = {
+               .ctrl = {
+                       .action = SPR_SET_SIGA,
+                       .argnum = 1,
+                       .band_idx = phy != &dev->phy,
+               },
+               .siga = {
+                       .omac = omac > HW_BSSID_MAX ? omac - 12 : omac,
+               },
+       };
+       int ret;
+
+       if (he_obss_pd->sr_ctrl & IEEE80211_HE_SPR_HESIGA_SR_VAL15_ALLOWED)
+               req.siga.flag[req.siga.omac] = 0xf;
+       else
+               return 0;
+
+       /* switch to normal AP mode */
+       ret = mt7915_mcu_enable_obss_spr(phy, SPR_ENABLE_MODE, 0);
+       if (ret)
+               return ret;
+
+       return mt76_mcu_send_msg(&dev->mt76, MCU_EXT_CMD(SET_SPR), &req,
+                                sizeof(req), true);
+}
+
+static int
+mt7915_mcu_set_obss_spr_bitmap(struct mt7915_phy *phy,
+                              struct ieee80211_he_obss_pd *he_obss_pd)
+{
+       struct mt7915_dev *dev = phy->dev;
+       struct {
+               struct mt7915_mcu_sr_ctrl ctrl;
+               struct {
+                       __le32 color_l[2];
+                       __le32 color_h[2];
+                       __le32 bssid_l[2];
+                       __le32 bssid_h[2];
+               } __packed bitmap;
        } __packed req = {
-               .action = MT_SPR_ENABLE,
-               .arg_num = 1,
-               .band_idx = mvif->mt76.band_idx,
-               .val = cpu_to_le32(enable),
+               .ctrl = {
+                       .action = SPR_SET_SRG_BITMAP,
+                       .argnum = 4,
+                       .band_idx = phy != &dev->phy,
+               },
        };
+       u32 bitmap;
+
+       memcpy(&bitmap, he_obss_pd->bss_color_bitmap, sizeof(bitmap));
+       req.bitmap.color_l[req.ctrl.band_idx] = cpu_to_le32(bitmap);
+
+       memcpy(&bitmap, he_obss_pd->bss_color_bitmap + 4, sizeof(bitmap));
+       req.bitmap.color_h[req.ctrl.band_idx] = cpu_to_le32(bitmap);
+
+       memcpy(&bitmap, he_obss_pd->partial_bssid_bitmap, sizeof(bitmap));
+       req.bitmap.bssid_l[req.ctrl.band_idx] = cpu_to_le32(bitmap);
+
+       memcpy(&bitmap, he_obss_pd->partial_bssid_bitmap + 4, sizeof(bitmap));
+       req.bitmap.bssid_h[req.ctrl.band_idx] = cpu_to_le32(bitmap);
 
        return mt76_mcu_send_msg(&dev->mt76, MCU_EXT_CMD(SET_SPR), &req,
                                 sizeof(req), true);
 }
 
+int mt7915_mcu_add_obss_spr(struct mt7915_phy *phy, struct ieee80211_vif *vif,
+                           struct ieee80211_he_obss_pd *he_obss_pd)
+{
+       int ret;
+
+       /* enable firmware scene detection algorithms */
+       ret = mt7915_mcu_enable_obss_spr(phy, SPR_ENABLE_SD, sr_scene_detect);
+       if (ret)
+               return ret;
+
+       /* firmware dynamically adjusts PD threshold so skip manual control */
+       if (sr_scene_detect && !he_obss_pd->enable)
+               return 0;
+
+       /* enable spatial reuse */
+       ret = mt7915_mcu_enable_obss_spr(phy, SPR_ENABLE, he_obss_pd->enable);
+       if (ret)
+               return ret;
+
+       if (sr_scene_detect || !he_obss_pd->enable)
+               return 0;
+
+       ret = mt7915_mcu_enable_obss_spr(phy, SPR_ENABLE_TX, true);
+       if (ret)
+               return ret;
+
+       /* set SRG/non-SRG OBSS PD threshold */
+       ret = mt7915_mcu_set_obss_spr_pd(phy, he_obss_pd);
+       if (ret)
+               return ret;
+
+       /* Set SR prohibit */
+       ret = mt7915_mcu_set_obss_spr_siga(phy, vif, he_obss_pd);
+       if (ret)
+               return ret;
+
+       /* set SRG BSS color/BSSID bitmap */
+       return mt7915_mcu_set_obss_spr_bitmap(phy, he_obss_pd);
+}
+
 int mt7915_mcu_get_rx_rate(struct mt7915_phy *phy, struct ieee80211_vif *vif,
                           struct ieee80211_sta *sta, struct rate_info *rate)
 {