@@ -703,6 +703,171 @@ static void rtw89_mcc_set_duration_gc_sta(struct rtw89_dev *rtwdev)
aux->duration = dur_aux;
}
+struct rtw89_mcc_mod_dur_data {
+ u16 available;
+ struct {
+ u16 dur;
+ u16 room;
+ } parm[NUM_OF_RTW89_MCC_ROLES];
+};
+
+static int rtw89_mcc_mod_dur_get_iterator(struct rtw89_dev *rtwdev,
+ struct rtw89_mcc_role *mcc_role,
+ unsigned int ordered_idx,
+ void *data)
+{
+ struct rtw89_mcc_mod_dur_data *p = data;
+ u16 min;
+
+ p->parm[ordered_idx].dur = mcc_role->duration;
+
+ if (mcc_role->is_go)
+ min = RTW89_MCC_MIN_GO_DURATION;
+ else
+ min = RTW89_MCC_MIN_STA_DURATION;
+
+ p->parm[ordered_idx].room = max_t(s32, p->parm[ordered_idx].dur - min, 0);
+
+ rtw89_debug(rtwdev, RTW89_DBG_CHAN,
+ "MCC mod dur: chk role[%u]: dur %u, min %u, room %u\n",
+ ordered_idx, p->parm[ordered_idx].dur, min,
+ p->parm[ordered_idx].room);
+
+ p->available += p->parm[ordered_idx].room;
+ return 0;
+}
+
+static int rtw89_mcc_mod_dur_put_iterator(struct rtw89_dev *rtwdev,
+ struct rtw89_mcc_role *mcc_role,
+ unsigned int ordered_idx,
+ void *data)
+{
+ struct rtw89_mcc_mod_dur_data *p = data;
+
+ mcc_role->duration = p->parm[ordered_idx].dur;
+
+ rtw89_debug(rtwdev, RTW89_DBG_CHAN,
+ "MCC mod dur: set role[%u]: dur %u\n",
+ ordered_idx, p->parm[ordered_idx].dur);
+ return 0;
+}
+
+static void rtw89_mcc_mod_duration_dual_2ghz_with_bt(struct rtw89_dev *rtwdev)
+{
+ struct rtw89_mcc_info *mcc = &rtwdev->mcc;
+ struct rtw89_mcc_config *config = &mcc->config;
+ struct rtw89_mcc_mod_dur_data data = {};
+ u16 mcc_intvl = config->mcc_interval;
+ u16 bt_dur = mcc->bt_role.duration;
+ u16 wifi_dur;
+
+ rtw89_debug(rtwdev, RTW89_DBG_CHAN,
+ "MCC mod dur (dual 2ghz): mcc_intvl %u, raw bt_dur %u\n",
+ mcc_intvl, bt_dur);
+
+ rtw89_iterate_mcc_roles(rtwdev, rtw89_mcc_mod_dur_get_iterator, &data);
+
+ bt_dur = clamp_t(u16, bt_dur, 1, data.available / 3);
+ wifi_dur = mcc_intvl - bt_dur;
+
+ if (data.parm[0].room <= data.parm[1].room) {
+ data.parm[0].dur -= min_t(u16, bt_dur / 2, data.parm[0].room);
+ data.parm[1].dur = wifi_dur - data.parm[0].dur;
+ } else {
+ data.parm[1].dur -= min_t(u16, bt_dur / 2, data.parm[1].room);
+ data.parm[0].dur = wifi_dur - data.parm[1].dur;
+ }
+
+ rtw89_iterate_mcc_roles(rtwdev, rtw89_mcc_mod_dur_put_iterator, &data);
+
+ rtw89_debug(rtwdev, RTW89_DBG_CHAN, "MCC mod dur: set bt: dur %u\n", bt_dur);
+ mcc->bt_role.duration = bt_dur;
+}
+
+static
+void rtw89_mcc_mod_duration_diff_band_with_bt(struct rtw89_dev *rtwdev,
+ struct rtw89_mcc_role *role_2ghz,
+ struct rtw89_mcc_role *role_non_2ghz)
+{
+ struct rtw89_mcc_info *mcc = &rtwdev->mcc;
+ struct rtw89_mcc_config *config = &mcc->config;
+ u16 dur_2ghz, dur_non_2ghz;
+ u16 bt_dur, mcc_intvl;
+
+ dur_2ghz = role_2ghz->duration;
+ dur_non_2ghz = role_non_2ghz->duration;
+ mcc_intvl = config->mcc_interval;
+ bt_dur = mcc->bt_role.duration;
+
+ rtw89_debug(rtwdev, RTW89_DBG_CHAN,
+ "MCC mod dur (diff band): mcc_intvl %u, bt_dur %u\n",
+ mcc_intvl, bt_dur);
+
+ rtw89_debug(rtwdev, RTW89_DBG_CHAN,
+ "MCC mod dur: check dur_2ghz %u, dur_non_2ghz %u\n",
+ dur_2ghz, dur_non_2ghz);
+
+ if (dur_non_2ghz >= bt_dur) {
+ rtw89_debug(rtwdev, RTW89_DBG_CHAN,
+ "MCC mod dur: dur_non_2ghz is enough for bt\n");
+ return;
+ }
+
+ dur_non_2ghz = bt_dur;
+ dur_2ghz = mcc_intvl - dur_non_2ghz;
+
+ if (role_non_2ghz->limit.enable) {
+ rtw89_debug(rtwdev, RTW89_DBG_CHAN,
+ "MCC mod dur: dur_non_2ghz is limited with max %u\n",
+ role_non_2ghz->limit.max_dur);
+
+ dur_non_2ghz = min(dur_non_2ghz, role_non_2ghz->limit.max_dur);
+ dur_2ghz = mcc_intvl - dur_non_2ghz;
+ }
+
+ rtw89_debug(rtwdev, RTW89_DBG_CHAN,
+ "MCC mod dur: set dur_2ghz %u, dur_non_2ghz %u\n",
+ dur_2ghz, dur_non_2ghz);
+
+ role_2ghz->duration = dur_2ghz;
+ role_non_2ghz->duration = dur_non_2ghz;
+}
+
+static bool rtw89_mcc_duration_decision_on_bt(struct rtw89_dev *rtwdev)
+{
+ struct rtw89_mcc_info *mcc = &rtwdev->mcc;
+ struct rtw89_mcc_role *ref = &mcc->role_ref;
+ struct rtw89_mcc_role *aux = &mcc->role_aux;
+ struct rtw89_mcc_bt_role *bt_role = &mcc->bt_role;
+
+ if (!bt_role->duration)
+ return false;
+
+ if (ref->is_2ghz && aux->is_2ghz) {
+ rtw89_debug(rtwdev, RTW89_DBG_CHAN,
+ "MCC dual roles are on 2GHz; consider BT duration\n");
+
+ rtw89_mcc_mod_duration_dual_2ghz_with_bt(rtwdev);
+ return true;
+ }
+
+ if (!ref->is_2ghz && !aux->is_2ghz) {
+ rtw89_debug(rtwdev, RTW89_DBG_CHAN,
+ "MCC dual roles are not on 2GHz; ignore BT duration\n");
+ return false;
+ }
+
+ rtw89_debug(rtwdev, RTW89_DBG_CHAN,
+ "MCC one role is on 2GHz; modify another for BT duration\n");
+
+ if (ref->is_2ghz)
+ rtw89_mcc_mod_duration_diff_band_with_bt(rtwdev, ref, aux);
+ else
+ rtw89_mcc_mod_duration_diff_band_with_bt(rtwdev, aux, ref);
+
+ return false;
+}
+
static void rtw89_mcc_sync_tbtt(struct rtw89_dev *rtwdev,
struct rtw89_mcc_role *tgt,
struct rtw89_mcc_role *src,
@@ -790,6 +955,7 @@ static int rtw89_mcc_fill_config(struct rtw89_dev *rtwdev)
struct rtw89_mcc_role *ref = &mcc->role_ref;
struct rtw89_mcc_role *aux = &mcc->role_aux;
struct rtw89_mcc_config *config = &mcc->config;
+ bool hdl_bt;
memset(config, 0, sizeof(*config));
@@ -816,6 +982,9 @@ static int rtw89_mcc_fill_config(struct rtw89_dev *rtwdev)
return -EFAULT;
}
+ hdl_bt = rtw89_mcc_duration_decision_on_bt(rtwdev);
+ rtw89_debug(rtwdev, RTW89_DBG_CHAN, "MCC handle bt: %d\n", hdl_bt);
+
rtw89_mcc_set_default_pattern(rtwdev);
return rtw89_mcc_fill_start_tsf(rtwdev);
}