1 | // SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause |
2 | /* Copyright(c) 2019-2020 Realtek Corporation |
3 | */ |
4 | |
5 | #include "acpi.h" |
6 | #include "debug.h" |
7 | #include "phy.h" |
8 | #include "reg.h" |
9 | #include "sar.h" |
10 | |
11 | #define RTW89_TAS_FACTOR 2 /* unit: 0.25 dBm */ |
12 | #define RTW89_TAS_DPR_GAP (1 << RTW89_TAS_FACTOR) |
13 | #define RTW89_TAS_DELTA (2 << RTW89_TAS_FACTOR) |
14 | |
15 | static enum rtw89_sar_subband rtw89_sar_get_subband(struct rtw89_dev *rtwdev, |
16 | u32 center_freq) |
17 | { |
18 | switch (center_freq) { |
19 | default: |
20 | rtw89_debug(rtwdev, mask: RTW89_DBG_SAR, |
21 | fmt: "center freq: %u to SAR subband is unhandled\n" , |
22 | center_freq); |
23 | fallthrough; |
24 | case 2412 ... 2484: |
25 | return RTW89_SAR_2GHZ_SUBBAND; |
26 | case 5180 ... 5320: |
27 | return RTW89_SAR_5GHZ_SUBBAND_1_2; |
28 | case 5500 ... 5720: |
29 | return RTW89_SAR_5GHZ_SUBBAND_2_E; |
30 | case 5745 ... 5825: |
31 | return RTW89_SAR_5GHZ_SUBBAND_3; |
32 | case 5955 ... 6155: |
33 | return RTW89_SAR_6GHZ_SUBBAND_5_L; |
34 | case 6175 ... 6415: |
35 | return RTW89_SAR_6GHZ_SUBBAND_5_H; |
36 | case 6435 ... 6515: |
37 | return RTW89_SAR_6GHZ_SUBBAND_6; |
38 | case 6535 ... 6695: |
39 | return RTW89_SAR_6GHZ_SUBBAND_7_L; |
40 | case 6715 ... 6855: |
41 | return RTW89_SAR_6GHZ_SUBBAND_7_H; |
42 | |
43 | /* freq 6875 (ch 185, 20MHz) spans RTW89_SAR_6GHZ_SUBBAND_7_H |
44 | * and RTW89_SAR_6GHZ_SUBBAND_8, so directly describe it with |
45 | * struct rtw89_sar_span in the following. |
46 | */ |
47 | |
48 | case 6895 ... 7115: |
49 | return RTW89_SAR_6GHZ_SUBBAND_8; |
50 | } |
51 | } |
52 | |
53 | struct rtw89_sar_span { |
54 | enum rtw89_sar_subband subband_low; |
55 | enum rtw89_sar_subband subband_high; |
56 | }; |
57 | |
58 | #define RTW89_SAR_SPAN_VALID(span) ((span)->subband_high) |
59 | |
60 | #define RTW89_SAR_6GHZ_SPAN_HEAD 6145 |
61 | #define RTW89_SAR_6GHZ_SPAN_IDX(center_freq) \ |
62 | ((((int)(center_freq) - RTW89_SAR_6GHZ_SPAN_HEAD) / 5) / 2) |
63 | |
64 | #define RTW89_DECL_SAR_6GHZ_SPAN(center_freq, subband_l, subband_h) \ |
65 | [RTW89_SAR_6GHZ_SPAN_IDX(center_freq)] = { \ |
66 | .subband_low = RTW89_SAR_6GHZ_ ## subband_l, \ |
67 | .subband_high = RTW89_SAR_6GHZ_ ## subband_h, \ |
68 | } |
69 | |
70 | /* Since 6GHz SAR subbands are not edge aligned, some cases span two SAR |
71 | * subbands. In the following, we describe each of them with rtw89_sar_span. |
72 | */ |
73 | static const struct rtw89_sar_span rtw89_sar_overlapping_6ghz[] = { |
74 | RTW89_DECL_SAR_6GHZ_SPAN(6145, SUBBAND_5_L, SUBBAND_5_H), |
75 | RTW89_DECL_SAR_6GHZ_SPAN(6165, SUBBAND_5_L, SUBBAND_5_H), |
76 | RTW89_DECL_SAR_6GHZ_SPAN(6185, SUBBAND_5_L, SUBBAND_5_H), |
77 | RTW89_DECL_SAR_6GHZ_SPAN(6505, SUBBAND_6, SUBBAND_7_L), |
78 | RTW89_DECL_SAR_6GHZ_SPAN(6525, SUBBAND_6, SUBBAND_7_L), |
79 | RTW89_DECL_SAR_6GHZ_SPAN(6545, SUBBAND_6, SUBBAND_7_L), |
80 | RTW89_DECL_SAR_6GHZ_SPAN(6665, SUBBAND_7_L, SUBBAND_7_H), |
81 | RTW89_DECL_SAR_6GHZ_SPAN(6705, SUBBAND_7_L, SUBBAND_7_H), |
82 | RTW89_DECL_SAR_6GHZ_SPAN(6825, SUBBAND_7_H, SUBBAND_8), |
83 | RTW89_DECL_SAR_6GHZ_SPAN(6865, SUBBAND_7_H, SUBBAND_8), |
84 | RTW89_DECL_SAR_6GHZ_SPAN(6875, SUBBAND_7_H, SUBBAND_8), |
85 | RTW89_DECL_SAR_6GHZ_SPAN(6885, SUBBAND_7_H, SUBBAND_8), |
86 | }; |
87 | |
88 | static int rtw89_query_sar_config_common(struct rtw89_dev *rtwdev, |
89 | u32 center_freq, s32 *cfg) |
90 | { |
91 | struct rtw89_sar_cfg_common *rtwsar = &rtwdev->sar.cfg_common; |
92 | const struct rtw89_sar_span *span = NULL; |
93 | enum rtw89_sar_subband subband_l, subband_h; |
94 | int idx; |
95 | |
96 | if (center_freq >= RTW89_SAR_6GHZ_SPAN_HEAD) { |
97 | idx = RTW89_SAR_6GHZ_SPAN_IDX(center_freq); |
98 | /* To decrease size of rtw89_sar_overlapping_6ghz[], |
99 | * RTW89_SAR_6GHZ_SPAN_IDX() truncates the leading NULLs |
100 | * to make first span as index 0 of the table. So, if center |
101 | * frequency is less than the first one, it will get netative. |
102 | */ |
103 | if (idx >= 0 && idx < ARRAY_SIZE(rtw89_sar_overlapping_6ghz)) |
104 | span = &rtw89_sar_overlapping_6ghz[idx]; |
105 | } |
106 | |
107 | if (span && RTW89_SAR_SPAN_VALID(span)) { |
108 | subband_l = span->subband_low; |
109 | subband_h = span->subband_high; |
110 | } else { |
111 | subband_l = rtw89_sar_get_subband(rtwdev, center_freq); |
112 | subband_h = subband_l; |
113 | } |
114 | |
115 | rtw89_debug(rtwdev, mask: RTW89_DBG_SAR, |
116 | fmt: "center_freq %u: SAR subband {%u, %u}\n" , |
117 | center_freq, subband_l, subband_h); |
118 | |
119 | if (!rtwsar->set[subband_l] && !rtwsar->set[subband_h]) |
120 | return -ENODATA; |
121 | |
122 | if (!rtwsar->set[subband_l]) |
123 | *cfg = rtwsar->cfg[subband_h]; |
124 | else if (!rtwsar->set[subband_h]) |
125 | *cfg = rtwsar->cfg[subband_l]; |
126 | else |
127 | *cfg = min(rtwsar->cfg[subband_l], rtwsar->cfg[subband_h]); |
128 | |
129 | return 0; |
130 | } |
131 | |
132 | static const |
133 | struct rtw89_sar_handler rtw89_sar_handlers[RTW89_SAR_SOURCE_NR] = { |
134 | [RTW89_SAR_SOURCE_COMMON] = { |
135 | .descr_sar_source = "RTW89_SAR_SOURCE_COMMON" , |
136 | .txpwr_factor_sar = 2, |
137 | .query_sar_config = rtw89_query_sar_config_common, |
138 | }, |
139 | }; |
140 | |
141 | #define rtw89_sar_set_src(_dev, _src, _cfg_name, _cfg_data) \ |
142 | do { \ |
143 | typeof(_src) _s = (_src); \ |
144 | typeof(_dev) _d = (_dev); \ |
145 | BUILD_BUG_ON(!rtw89_sar_handlers[_s].descr_sar_source); \ |
146 | BUILD_BUG_ON(!rtw89_sar_handlers[_s].query_sar_config); \ |
147 | lockdep_assert_held(&_d->mutex); \ |
148 | _d->sar._cfg_name = *(_cfg_data); \ |
149 | _d->sar.src = _s; \ |
150 | } while (0) |
151 | |
152 | static s8 rtw89_txpwr_sar_to_mac(struct rtw89_dev *rtwdev, u8 fct, s32 cfg) |
153 | { |
154 | const u8 fct_mac = rtwdev->chip->txpwr_factor_mac; |
155 | s32 cfg_mac; |
156 | |
157 | cfg_mac = fct > fct_mac ? |
158 | cfg >> (fct - fct_mac) : cfg << (fct_mac - fct); |
159 | |
160 | return (s8)clamp_t(s32, cfg_mac, |
161 | RTW89_SAR_TXPWR_MAC_MIN, |
162 | RTW89_SAR_TXPWR_MAC_MAX); |
163 | } |
164 | |
165 | static s8 rtw89_txpwr_tas_to_sar(const struct rtw89_sar_handler *sar_hdl, |
166 | s8 cfg) |
167 | { |
168 | const u8 fct = sar_hdl->txpwr_factor_sar; |
169 | |
170 | if (fct > RTW89_TAS_FACTOR) |
171 | return cfg << (fct - RTW89_TAS_FACTOR); |
172 | else |
173 | return cfg >> (RTW89_TAS_FACTOR - fct); |
174 | } |
175 | |
176 | static s8 rtw89_txpwr_sar_to_tas(const struct rtw89_sar_handler *sar_hdl, |
177 | s8 cfg) |
178 | { |
179 | const u8 fct = sar_hdl->txpwr_factor_sar; |
180 | |
181 | if (fct > RTW89_TAS_FACTOR) |
182 | return cfg >> (fct - RTW89_TAS_FACTOR); |
183 | else |
184 | return cfg << (RTW89_TAS_FACTOR - fct); |
185 | } |
186 | |
187 | s8 rtw89_query_sar(struct rtw89_dev *rtwdev, u32 center_freq) |
188 | { |
189 | const enum rtw89_sar_sources src = rtwdev->sar.src; |
190 | /* its members are protected by rtw89_sar_set_src() */ |
191 | const struct rtw89_sar_handler *sar_hdl = &rtw89_sar_handlers[src]; |
192 | struct rtw89_tas_info *tas = &rtwdev->tas; |
193 | s8 delta; |
194 | int ret; |
195 | s32 cfg; |
196 | u8 fct; |
197 | |
198 | lockdep_assert_held(&rtwdev->mutex); |
199 | |
200 | if (src == RTW89_SAR_SOURCE_NONE) |
201 | return RTW89_SAR_TXPWR_MAC_MAX; |
202 | |
203 | ret = sar_hdl->query_sar_config(rtwdev, center_freq, &cfg); |
204 | if (ret) |
205 | return RTW89_SAR_TXPWR_MAC_MAX; |
206 | |
207 | if (tas->enable) { |
208 | switch (tas->state) { |
209 | case RTW89_TAS_STATE_DPR_OFF: |
210 | return RTW89_SAR_TXPWR_MAC_MAX; |
211 | case RTW89_TAS_STATE_DPR_ON: |
212 | delta = rtw89_txpwr_tas_to_sar(sar_hdl, cfg: tas->delta); |
213 | cfg -= delta; |
214 | break; |
215 | case RTW89_TAS_STATE_DPR_FORBID: |
216 | default: |
217 | break; |
218 | } |
219 | } |
220 | |
221 | fct = sar_hdl->txpwr_factor_sar; |
222 | |
223 | return rtw89_txpwr_sar_to_mac(rtwdev, fct, cfg); |
224 | } |
225 | |
226 | void rtw89_print_sar(struct seq_file *m, struct rtw89_dev *rtwdev, u32 center_freq) |
227 | { |
228 | const enum rtw89_sar_sources src = rtwdev->sar.src; |
229 | /* its members are protected by rtw89_sar_set_src() */ |
230 | const struct rtw89_sar_handler *sar_hdl = &rtw89_sar_handlers[src]; |
231 | const u8 fct_mac = rtwdev->chip->txpwr_factor_mac; |
232 | int ret; |
233 | s32 cfg; |
234 | u8 fct; |
235 | |
236 | lockdep_assert_held(&rtwdev->mutex); |
237 | |
238 | if (src == RTW89_SAR_SOURCE_NONE) { |
239 | seq_puts(m, s: "no SAR is applied\n" ); |
240 | return; |
241 | } |
242 | |
243 | seq_printf(m, fmt: "source: %d (%s)\n" , src, sar_hdl->descr_sar_source); |
244 | |
245 | ret = sar_hdl->query_sar_config(rtwdev, center_freq, &cfg); |
246 | if (ret) { |
247 | seq_printf(m, fmt: "config: return code: %d\n" , ret); |
248 | seq_printf(m, fmt: "assign: max setting: %d (unit: 1/%lu dBm)\n" , |
249 | RTW89_SAR_TXPWR_MAC_MAX, BIT(fct_mac)); |
250 | return; |
251 | } |
252 | |
253 | fct = sar_hdl->txpwr_factor_sar; |
254 | |
255 | seq_printf(m, fmt: "config: %d (unit: 1/%lu dBm)\n" , cfg, BIT(fct)); |
256 | } |
257 | |
258 | void rtw89_print_tas(struct seq_file *m, struct rtw89_dev *rtwdev) |
259 | { |
260 | struct rtw89_tas_info *tas = &rtwdev->tas; |
261 | |
262 | if (!tas->enable) { |
263 | seq_puts(m, s: "no TAS is applied\n" ); |
264 | return; |
265 | } |
266 | |
267 | seq_printf(m, fmt: "DPR gap: %d\n" , tas->dpr_gap); |
268 | seq_printf(m, fmt: "TAS delta: %d\n" , tas->delta); |
269 | } |
270 | |
271 | static int rtw89_apply_sar_common(struct rtw89_dev *rtwdev, |
272 | const struct rtw89_sar_cfg_common *sar) |
273 | { |
274 | enum rtw89_sar_sources src; |
275 | int ret = 0; |
276 | |
277 | mutex_lock(&rtwdev->mutex); |
278 | |
279 | src = rtwdev->sar.src; |
280 | if (src != RTW89_SAR_SOURCE_NONE && src != RTW89_SAR_SOURCE_COMMON) { |
281 | rtw89_warn(rtwdev, "SAR source: %d is in use" , src); |
282 | ret = -EBUSY; |
283 | goto exit; |
284 | } |
285 | |
286 | rtw89_sar_set_src(rtwdev, RTW89_SAR_SOURCE_COMMON, cfg_common, sar); |
287 | rtw89_core_set_chip_txpwr(rtwdev); |
288 | |
289 | exit: |
290 | mutex_unlock(lock: &rtwdev->mutex); |
291 | return ret; |
292 | } |
293 | |
294 | static const struct cfg80211_sar_freq_ranges rtw89_common_sar_freq_ranges[] = { |
295 | { .start_freq = 2412, .end_freq = 2484, }, |
296 | { .start_freq = 5180, .end_freq = 5320, }, |
297 | { .start_freq = 5500, .end_freq = 5720, }, |
298 | { .start_freq = 5745, .end_freq = 5825, }, |
299 | { .start_freq = 5955, .end_freq = 6155, }, |
300 | { .start_freq = 6175, .end_freq = 6415, }, |
301 | { .start_freq = 6435, .end_freq = 6515, }, |
302 | { .start_freq = 6535, .end_freq = 6695, }, |
303 | { .start_freq = 6715, .end_freq = 6875, }, |
304 | { .start_freq = 6875, .end_freq = 7115, }, |
305 | }; |
306 | |
307 | static_assert(RTW89_SAR_SUBBAND_NR == |
308 | ARRAY_SIZE(rtw89_common_sar_freq_ranges)); |
309 | |
310 | const struct cfg80211_sar_capa rtw89_sar_capa = { |
311 | .type = NL80211_SAR_TYPE_POWER, |
312 | .num_freq_ranges = ARRAY_SIZE(rtw89_common_sar_freq_ranges), |
313 | .freq_ranges = rtw89_common_sar_freq_ranges, |
314 | }; |
315 | |
316 | int rtw89_ops_set_sar_specs(struct ieee80211_hw *hw, |
317 | const struct cfg80211_sar_specs *sar) |
318 | { |
319 | struct rtw89_dev *rtwdev = hw->priv; |
320 | struct rtw89_sar_cfg_common sar_common = {0}; |
321 | u8 fct; |
322 | u32 freq_start; |
323 | u32 freq_end; |
324 | s32 power; |
325 | u32 i, idx; |
326 | |
327 | if (sar->type != NL80211_SAR_TYPE_POWER) |
328 | return -EINVAL; |
329 | |
330 | fct = rtw89_sar_handlers[RTW89_SAR_SOURCE_COMMON].txpwr_factor_sar; |
331 | |
332 | for (i = 0; i < sar->num_sub_specs; i++) { |
333 | idx = sar->sub_specs[i].freq_range_index; |
334 | if (idx >= ARRAY_SIZE(rtw89_common_sar_freq_ranges)) |
335 | return -EINVAL; |
336 | |
337 | freq_start = rtw89_common_sar_freq_ranges[idx].start_freq; |
338 | freq_end = rtw89_common_sar_freq_ranges[idx].end_freq; |
339 | power = sar->sub_specs[i].power; |
340 | |
341 | rtw89_debug(rtwdev, mask: RTW89_DBG_SAR, |
342 | fmt: "On freq %u to %u, set SAR limit %d (unit: 1/%lu dBm)\n" , |
343 | freq_start, freq_end, power, BIT(fct)); |
344 | |
345 | sar_common.set[idx] = true; |
346 | sar_common.cfg[idx] = power; |
347 | } |
348 | |
349 | return rtw89_apply_sar_common(rtwdev, sar: &sar_common); |
350 | } |
351 | |
352 | static void rtw89_tas_state_update(struct rtw89_dev *rtwdev) |
353 | { |
354 | const enum rtw89_sar_sources src = rtwdev->sar.src; |
355 | /* its members are protected by rtw89_sar_set_src() */ |
356 | const struct rtw89_sar_handler *sar_hdl = &rtw89_sar_handlers[src]; |
357 | struct rtw89_tas_info *tas = &rtwdev->tas; |
358 | s32 txpwr_avg = tas->total_txpwr / RTW89_TAS_MAX_WINDOW / PERCENT; |
359 | s32 dpr_on_threshold, dpr_off_threshold, cfg; |
360 | enum rtw89_tas_state state = tas->state; |
361 | const struct rtw89_chan *chan; |
362 | int ret; |
363 | |
364 | lockdep_assert_held(&rtwdev->mutex); |
365 | |
366 | if (src == RTW89_SAR_SOURCE_NONE) |
367 | return; |
368 | |
369 | chan = rtw89_chan_get(rtwdev, idx: RTW89_SUB_ENTITY_0); |
370 | ret = sar_hdl->query_sar_config(rtwdev, chan->freq, &cfg); |
371 | if (ret) |
372 | return; |
373 | |
374 | cfg = rtw89_txpwr_sar_to_tas(sar_hdl, cfg); |
375 | |
376 | if (tas->delta >= cfg) { |
377 | rtw89_debug(rtwdev, mask: RTW89_DBG_SAR, |
378 | fmt: "TAS delta exceed SAR limit\n" ); |
379 | state = RTW89_TAS_STATE_DPR_FORBID; |
380 | goto out; |
381 | } |
382 | |
383 | dpr_on_threshold = cfg; |
384 | dpr_off_threshold = cfg - tas->dpr_gap; |
385 | rtw89_debug(rtwdev, mask: RTW89_DBG_SAR, |
386 | fmt: "DPR_ON thold: %d, DPR_OFF thold: %d, txpwr_avg: %d\n" , |
387 | dpr_on_threshold, dpr_off_threshold, txpwr_avg); |
388 | |
389 | if (txpwr_avg >= dpr_on_threshold) |
390 | state = RTW89_TAS_STATE_DPR_ON; |
391 | else if (txpwr_avg < dpr_off_threshold) |
392 | state = RTW89_TAS_STATE_DPR_OFF; |
393 | |
394 | out: |
395 | if (tas->state == state) |
396 | return; |
397 | |
398 | rtw89_debug(rtwdev, mask: RTW89_DBG_SAR, |
399 | fmt: "TAS old state: %d, new state: %d\n" , tas->state, state); |
400 | tas->state = state; |
401 | rtw89_core_set_chip_txpwr(rtwdev); |
402 | } |
403 | |
404 | void rtw89_tas_init(struct rtw89_dev *rtwdev) |
405 | { |
406 | struct rtw89_tas_info *tas = &rtwdev->tas; |
407 | struct rtw89_acpi_dsm_result res = {}; |
408 | int ret; |
409 | u8 val; |
410 | |
411 | ret = rtw89_acpi_evaluate_dsm(rtwdev, func: RTW89_ACPI_DSM_FUNC_TAS_EN, res: &res); |
412 | if (ret) { |
413 | rtw89_debug(rtwdev, mask: RTW89_DBG_SAR, |
414 | fmt: "acpi: cannot get TAS: %d\n" , ret); |
415 | return; |
416 | } |
417 | |
418 | val = res.u.value; |
419 | switch (val) { |
420 | case 0: |
421 | tas->enable = false; |
422 | break; |
423 | case 1: |
424 | tas->enable = true; |
425 | break; |
426 | default: |
427 | break; |
428 | } |
429 | |
430 | if (!tas->enable) { |
431 | rtw89_debug(rtwdev, mask: RTW89_DBG_SAR, fmt: "TAS not enable\n" ); |
432 | return; |
433 | } |
434 | |
435 | tas->dpr_gap = RTW89_TAS_DPR_GAP; |
436 | tas->delta = RTW89_TAS_DELTA; |
437 | } |
438 | |
439 | void rtw89_tas_reset(struct rtw89_dev *rtwdev) |
440 | { |
441 | struct rtw89_tas_info *tas = &rtwdev->tas; |
442 | |
443 | if (!tas->enable) |
444 | return; |
445 | |
446 | memset(&tas->txpwr_history, 0, sizeof(tas->txpwr_history)); |
447 | tas->total_txpwr = 0; |
448 | tas->cur_idx = 0; |
449 | tas->state = RTW89_TAS_STATE_DPR_OFF; |
450 | } |
451 | |
452 | static const struct rtw89_reg_def txpwr_regs[] = { |
453 | {R_PATH0_TXPWR, B_PATH0_TXPWR}, |
454 | {R_PATH1_TXPWR, B_PATH1_TXPWR}, |
455 | }; |
456 | |
457 | void rtw89_tas_track(struct rtw89_dev *rtwdev) |
458 | { |
459 | struct rtw89_env_monitor_info *env = &rtwdev->env_monitor; |
460 | const enum rtw89_sar_sources src = rtwdev->sar.src; |
461 | u8 max_nss_num = rtwdev->chip->rf_path_num; |
462 | struct rtw89_tas_info *tas = &rtwdev->tas; |
463 | s16 tmp, txpwr, instant_txpwr = 0; |
464 | u32 val; |
465 | int i; |
466 | |
467 | if (!tas->enable || src == RTW89_SAR_SOURCE_NONE) |
468 | return; |
469 | |
470 | if (env->ccx_watchdog_result != RTW89_PHY_ENV_MON_IFS_CLM) |
471 | return; |
472 | |
473 | for (i = 0; i < max_nss_num; i++) { |
474 | val = rtw89_phy_read32_mask(rtwdev, addr: txpwr_regs[i].addr, |
475 | mask: txpwr_regs[i].mask); |
476 | tmp = sign_extend32(value: val, index: 8); |
477 | if (tmp <= 0) |
478 | return; |
479 | instant_txpwr += tmp; |
480 | } |
481 | |
482 | instant_txpwr /= max_nss_num; |
483 | /* in unit of 0.25 dBm multiply by percentage */ |
484 | txpwr = instant_txpwr * env->ifs_clm_tx_ratio; |
485 | tas->total_txpwr += txpwr - tas->txpwr_history[tas->cur_idx]; |
486 | tas->txpwr_history[tas->cur_idx] = txpwr; |
487 | rtw89_debug(rtwdev, mask: RTW89_DBG_SAR, |
488 | fmt: "instant_txpwr: %d, tx_ratio: %d, txpwr: %d\n" , |
489 | instant_txpwr, env->ifs_clm_tx_ratio, txpwr); |
490 | |
491 | tas->cur_idx = (tas->cur_idx + 1) % RTW89_TAS_MAX_WINDOW; |
492 | |
493 | rtw89_tas_state_update(rtwdev); |
494 | } |
495 | |