projects/linux·blob master · refs

655 lines · 19.3 KiB

1// SPDX-License-Identifier: MIT
2/*
3 * Copyright 2026 Advanced Micro Devices, Inc.
4 *
5 * Permission is hereby granted, free of charge, to any person obtaining a
6 * copy of this software and associated documentation files (the "Software"),
7 * to deal in the Software without restriction, including without limitation
8 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
9 * and/or sell copies of the Software, and to permit persons to whom the
10 * Software is furnished to do so, subject to the following conditions:
11 *
12 * The above copyright notice and this permission notice shall be included in
13 * all copies or substantial portions of the Software.
14 *
15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
18 * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
19 * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
20 * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
21 * OTHER DEALINGS IN THE SOFTWARE.
22 *
23 * Authors: AMD
24 *
25 */
26
27#include <linux/types.h>
28#include <drm/drm_vblank.h>
29
30#include "dc.h"
31#include "amdgpu.h"
32#include "amdgpu_dm_ism.h"
33#include "amdgpu_dm_crtc.h"
34#include "amdgpu_dm_trace.h"
35#include "dm_helpers.h"
36
37
38/**
39 * dm_ism_next_state - Get next state based on current state and event
40 * @current_state: current ISM state
41 * @event: event being processed
42 * @next_state: place to store the next state
43 *
44 * This function defines the idle state management FSM. Invalid transitions
45 * are ignored and will not progress the FSM.
46 */
47STATIC_IFN_KUNIT
48bool dm_ism_next_state(enum amdgpu_dm_ism_state current_state,
49 enum amdgpu_dm_ism_event event,
50 enum amdgpu_dm_ism_state *next_state)
51{
52 switch (STATE_EVENT(current_state, event)) {
53 case STATE_EVENT(DM_ISM_STATE_FULL_POWER_RUNNING,
54 DM_ISM_EVENT_ENTER_IDLE_REQUESTED):
55 *next_state = DM_ISM_STATE_HYSTERESIS_WAITING;
56 break;
57 case STATE_EVENT(DM_ISM_STATE_FULL_POWER_RUNNING,
58 DM_ISM_EVENT_BEGIN_CURSOR_UPDATE):
59 *next_state = DM_ISM_STATE_FULL_POWER_BUSY;
60 break;
61
62 case STATE_EVENT(DM_ISM_STATE_FULL_POWER_BUSY,
63 DM_ISM_EVENT_ENTER_IDLE_REQUESTED):
64 *next_state = DM_ISM_STATE_HYSTERESIS_BUSY;
65 break;
66 case STATE_EVENT(DM_ISM_STATE_FULL_POWER_BUSY,
67 DM_ISM_EVENT_END_CURSOR_UPDATE):
68 *next_state = DM_ISM_STATE_FULL_POWER_RUNNING;
69 break;
70
71 case STATE_EVENT(DM_ISM_STATE_HYSTERESIS_WAITING,
72 DM_ISM_EVENT_EXIT_IDLE_REQUESTED):
73 *next_state = DM_ISM_STATE_TIMER_ABORTED;
74 break;
75 case STATE_EVENT(DM_ISM_STATE_HYSTERESIS_WAITING,
76 DM_ISM_EVENT_BEGIN_CURSOR_UPDATE):
77 *next_state = DM_ISM_STATE_HYSTERESIS_BUSY;
78 break;
79 case STATE_EVENT(DM_ISM_STATE_HYSTERESIS_WAITING,
80 DM_ISM_EVENT_TIMER_ELAPSED):
81 *next_state = DM_ISM_STATE_OPTIMIZED_IDLE;
82 break;
83 case STATE_EVENT(DM_ISM_STATE_HYSTERESIS_WAITING,
84 DM_ISM_EVENT_IMMEDIATE):
85 *next_state = DM_ISM_STATE_OPTIMIZED_IDLE;
86 break;
87
88 case STATE_EVENT(DM_ISM_STATE_HYSTERESIS_BUSY,
89 DM_ISM_EVENT_EXIT_IDLE_REQUESTED):
90 *next_state = DM_ISM_STATE_FULL_POWER_BUSY;
91 break;
92 case STATE_EVENT(DM_ISM_STATE_HYSTERESIS_BUSY,
93 DM_ISM_EVENT_END_CURSOR_UPDATE):
94 *next_state = DM_ISM_STATE_HYSTERESIS_WAITING;
95 break;
96
97 case STATE_EVENT(DM_ISM_STATE_OPTIMIZED_IDLE,
98 DM_ISM_EVENT_EXIT_IDLE_REQUESTED):
99 *next_state = DM_ISM_STATE_FULL_POWER_RUNNING;
100 break;
101 case STATE_EVENT(DM_ISM_STATE_OPTIMIZED_IDLE,
102 DM_ISM_EVENT_BEGIN_CURSOR_UPDATE):
103 *next_state = DM_ISM_STATE_HYSTERESIS_BUSY;
104 break;
105 case STATE_EVENT(DM_ISM_STATE_OPTIMIZED_IDLE,
106 DM_ISM_EVENT_SSO_TIMER_ELAPSED):
107 case STATE_EVENT(DM_ISM_STATE_OPTIMIZED_IDLE,
108 DM_ISM_EVENT_IMMEDIATE):
109 *next_state = DM_ISM_STATE_OPTIMIZED_IDLE_SSO;
110 break;
111
112 case STATE_EVENT(DM_ISM_STATE_OPTIMIZED_IDLE_SSO,
113 DM_ISM_EVENT_EXIT_IDLE_REQUESTED):
114 *next_state = DM_ISM_STATE_FULL_POWER_RUNNING;
115 break;
116 case STATE_EVENT(DM_ISM_STATE_OPTIMIZED_IDLE_SSO,
117 DM_ISM_EVENT_BEGIN_CURSOR_UPDATE):
118 *next_state = DM_ISM_STATE_HYSTERESIS_BUSY;
119 break;
120
121 case STATE_EVENT(DM_ISM_STATE_TIMER_ABORTED,
122 DM_ISM_EVENT_IMMEDIATE):
123 *next_state = DM_ISM_STATE_FULL_POWER_RUNNING;
124 break;
125
126 default:
127 return false;
128 }
129 return true;
130}
131EXPORT_IF_KUNIT(dm_ism_next_state);
132
133STATIC_IFN_KUNIT
134uint64_t dm_ism_get_sso_delay(const struct amdgpu_dm_ism *ism,
135 const struct dc_stream_state *stream)
136{
137 const struct amdgpu_dm_ism_config *config = &ism->config;
138 uint32_t v_total, h_total;
139 uint64_t one_frame_ns, sso_delay_ns;
140
141 if (!stream)
142 return 0;
143
144 if (!config->sso_num_frames)
145 return 0;
146
147 v_total = stream->timing.v_total;
148 h_total = stream->timing.h_total;
149
150 one_frame_ns = div64_u64(v_total * h_total * 10000000ull,
151 stream->timing.pix_clk_100hz);
152 sso_delay_ns = config->sso_num_frames * one_frame_ns;
153
154 return sso_delay_ns;
155}
156EXPORT_IF_KUNIT(dm_ism_get_sso_delay);
157
158/**
159 * dm_ism_get_idle_allow_delay - Calculate hysteresis-based idle allow delay
160 * @ism: ISM instance containing configuration, history, and current state
161 * @stream: display stream used to derive frame timing values for delay
162 *
163 * Calculates the delay before allowing idle optimizations based on recent
164 * idle history and the current stream timing.
165 */
166STATIC_IFN_KUNIT
167uint64_t dm_ism_get_idle_allow_delay(const struct amdgpu_dm_ism *ism,
168 const struct dc_stream_state *stream)
169{
170 const struct amdgpu_dm_ism_config *config = &ism->config;
171 uint32_t v_total, h_total;
172 uint64_t one_frame_ns, short_idle_ns, old_hist_ns;
173 uint32_t history_size;
174 int pos;
175 uint32_t short_idle_count = 0;
176 uint64_t ret_ns = 0;
177
178 if (!stream)
179 return 0;
180
181 if (!config->filter_num_frames)
182 return 0;
183 if (!config->filter_entry_count)
184 return 0;
185 if (!config->activation_num_delay_frames)
186 return 0;
187
188 v_total = stream->timing.v_total;
189 h_total = stream->timing.h_total;
190
191 one_frame_ns = div64_u64(v_total * h_total * 10000000ull,
192 stream->timing.pix_clk_100hz);
193
194 short_idle_ns = config->filter_num_frames * one_frame_ns;
195 old_hist_ns = config->filter_old_history_threshold * one_frame_ns;
196
197 /*
198 * Look back into the recent history and count how many times we entered
199 * idle power state for a short duration of time
200 */
201 history_size = min(
202 max(config->filter_history_size, config->filter_entry_count),
203 AMDGPU_DM_IDLE_HIST_LEN);
204 pos = ism->next_record_idx;
205
206 for (int k = 0; k < history_size; k++) {
207 if (pos <= 0 || pos > AMDGPU_DM_IDLE_HIST_LEN)
208 pos = AMDGPU_DM_IDLE_HIST_LEN;
209 pos -= 1;
210
211 if (ism->records[pos].duration_ns <= short_idle_ns)
212 short_idle_count += 1;
213
214 if (short_idle_count >= config->filter_entry_count)
215 break;
216
217 if (old_hist_ns > 0 &&
218 ism->last_idle_timestamp_ns - ism->records[pos].timestamp_ns > old_hist_ns)
219 break;
220 }
221
222 if (short_idle_count >= config->filter_entry_count)
223 ret_ns = config->activation_num_delay_frames * one_frame_ns;
224
225 return ret_ns;
226}
227EXPORT_IF_KUNIT(dm_ism_get_idle_allow_delay);
228
229/**
230 * dm_ism_insert_record - Insert a record into the circular history buffer
231 * @ism: ISM instance
232 */
233STATIC_IFN_KUNIT
234void dm_ism_insert_record(struct amdgpu_dm_ism *ism)
235{
236 struct amdgpu_dm_ism_record *record;
237
238 if (ism->next_record_idx < 0 ||
239 ism->next_record_idx >= AMDGPU_DM_IDLE_HIST_LEN)
240 ism->next_record_idx = 0;
241
242 record = &ism->records[ism->next_record_idx];
243 ism->next_record_idx += 1;
244
245 record->timestamp_ns = ktime_get_ns();
246 record->duration_ns =
247 record->timestamp_ns - ism->last_idle_timestamp_ns;
248}
249EXPORT_IF_KUNIT(dm_ism_insert_record);
250
251
252STATIC_IFN_KUNIT
253void dm_ism_set_last_idle_ts(struct amdgpu_dm_ism *ism)
254{
255 ism->last_idle_timestamp_ns = ktime_get_ns();
256}
257EXPORT_IF_KUNIT(dm_ism_set_last_idle_ts);
258
259
260STATIC_IFN_KUNIT
261bool dm_ism_trigger_event(struct amdgpu_dm_ism *ism,
262 enum amdgpu_dm_ism_event event)
263{
264 enum amdgpu_dm_ism_state next_state;
265
266 bool gotNextState = dm_ism_next_state(ism->current_state, event,
267 &next_state);
268
269 if (gotNextState) {
270 ism->previous_state = ism->current_state;
271 ism->current_state = next_state;
272 }
273
274 return gotNextState;
275}
276EXPORT_IF_KUNIT(dm_ism_trigger_event);
277
278
279static void dm_ism_commit_idle_optimization_state(struct amdgpu_dm_ism *ism,
280 struct dc_stream_state *stream,
281 bool vblank_enabled,
282 bool allow_panel_sso)
283{
284 struct amdgpu_crtc *acrtc = ism_to_amdgpu_crtc(ism);
285 struct amdgpu_device *adev = drm_to_adev(acrtc->base.dev);
286 struct amdgpu_display_manager *dm = &adev->dm;
287
288 trace_amdgpu_dm_ism_commit(dm->active_vblank_irq_count,
289 vblank_enabled,
290 allow_panel_sso);
291
292 /*
293 * If there is an active vblank requestor, or if SSO is being engaged,
294 * then disallow idle optimizations.
295 */
296 if (vblank_enabled || allow_panel_sso)
297 dc_allow_idle_optimizations(dm->dc, false);
298
299 /*
300 * Control PSR based on vblank requirements from OS
301 *
302 * If panel supports PSR SU/Replay, there's no need to exit self-refresh
303 * when OS is submitting fast atomic commits, as they can allow
304 * self-refresh during vblank periods.
305 */
306 if (stream && stream->link) {
307 /*
308 * If the OS requires vblank events (or vblank is otherwise enabled),
309 * do not allow static screen optimizations.
310 *
311 * Keep ism->allow_static_screen_optimizations unchanged so the
312 * hysteresis-based decision can be reused once vblank is disabled.
313 */
314 allow_panel_sso = allow_panel_sso && !vblank_enabled;
315 amdgpu_dm_crtc_set_static_screen_optimze(
316 dm, stream, allow_panel_sso,
317 acrtc->dm_irq_params.allow_sr_entry);
318 }
319
320 /*
321 * Check for any active drm vblank requestors on other CRTCs
322 * (dm->active_vblank_irq_count) before allowing HW-wide idle
323 * optimizations.
324 *
325 * There's no need to have a "balanced" check when disallowing idle
326 * optimizations at the start of this func -- we should disallow
327 * whenever there's *an* active CRTC.
328 */
329 if (!vblank_enabled && dm->active_vblank_irq_count == 0) {
330 dc_post_update_surfaces_to_stream(dm->dc);
331 dc_allow_idle_optimizations(dm->dc, true);
332 }
333}
334
335STATIC_IFN_KUNIT
336enum amdgpu_dm_ism_event dm_ism_dispatch_next_event(
337 enum amdgpu_dm_ism_state current_state,
338 uint64_t delay_ns,
339 uint64_t sso_delay_ns)
340{
341 switch (current_state) {
342 case DM_ISM_STATE_HYSTERESIS_WAITING:
343 if (delay_ns == 0)
344 return DM_ISM_EVENT_IMMEDIATE;
345 break;
346 case DM_ISM_STATE_OPTIMIZED_IDLE:
347 if (sso_delay_ns == 0)
348 return DM_ISM_EVENT_IMMEDIATE;
349 break;
350 case DM_ISM_STATE_TIMER_ABORTED:
351 return DM_ISM_EVENT_IMMEDIATE;
352 default:
353 break;
354 }
355 return DM_ISM_NUM_EVENTS;
356}
357EXPORT_IF_KUNIT(dm_ism_dispatch_next_event);
358
359static enum amdgpu_dm_ism_event dm_ism_dispatch_power_state(
360 struct amdgpu_dm_ism *ism,
361 struct dm_crtc_state *acrtc_state)
362{
363 const struct amdgpu_dm_ism_config *config = &ism->config;
364 uint64_t delay_ns = 0, sso_delay_ns = 0;
365
366 switch (ism->previous_state) {
367 case DM_ISM_STATE_HYSTERESIS_WAITING:
368 /*
369 * Stop the timer if it was set, and we're not running from the
370 * idle allow worker.
371 */
372 if (ism->current_state != DM_ISM_STATE_OPTIMIZED_IDLE &&
373 ism->current_state != DM_ISM_STATE_OPTIMIZED_IDLE_SSO)
374 cancel_delayed_work(&ism->delayed_work);
375 break;
376 case DM_ISM_STATE_OPTIMIZED_IDLE:
377 if (ism->current_state == DM_ISM_STATE_OPTIMIZED_IDLE_SSO)
378 break;
379 /* If idle disallow, cancel SSO work and insert record */
380 cancel_delayed_work(&ism->sso_delayed_work);
381 dm_ism_insert_record(ism);
382 dm_ism_commit_idle_optimization_state(ism, acrtc_state->stream,
383 true, false);
384 break;
385 case DM_ISM_STATE_OPTIMIZED_IDLE_SSO:
386 /* Disable idle optimization */
387 dm_ism_insert_record(ism);
388 dm_ism_commit_idle_optimization_state(ism, acrtc_state->stream,
389 true, false);
390 break;
391 default:
392 break;
393 }
394
395 switch (ism->current_state) {
396 case DM_ISM_STATE_HYSTERESIS_WAITING:
397 dm_ism_set_last_idle_ts(ism);
398 delay_ns = dm_ism_get_idle_allow_delay(ism, acrtc_state->stream);
399 /* Schedule worker */
400 if (delay_ns > 0)
401 mod_delayed_work(system_dfl_wq, &ism->delayed_work,
402 nsecs_to_jiffies(delay_ns));
403 break;
404 case DM_ISM_STATE_OPTIMIZED_IDLE:
405 sso_delay_ns = dm_ism_get_sso_delay(ism, acrtc_state->stream);
406 if (sso_delay_ns > 0) {
407 /*
408 * If sso_num_frames is less than hysteresis frames, it
409 * indicates that allowing idle here, then disallowing
410 * idle after sso_num_frames has expired, will likely
411 * have a negative power impact. Skip idle allow here,
412 * and let the sso_delayed_work handle it.
413 */
414 if (config->sso_num_frames >= config->filter_num_frames)
415 dm_ism_commit_idle_optimization_state(ism, acrtc_state->stream,
416 false, false);
417
418 mod_delayed_work(system_dfl_wq,
419 &ism->sso_delayed_work,
420 nsecs_to_jiffies(sso_delay_ns));
421 }
422 break;
423 case DM_ISM_STATE_OPTIMIZED_IDLE_SSO:
424 /* Enable static screen optimizations. */
425 dm_ism_commit_idle_optimization_state(ism, acrtc_state->stream,
426 false, true);
427 break;
428 case DM_ISM_STATE_TIMER_ABORTED:
429 dm_ism_insert_record(ism);
430 dm_ism_commit_idle_optimization_state(ism, acrtc_state->stream,
431 true, false);
432 break;
433 default:
434 break;
435 }
436
437 return dm_ism_dispatch_next_event(ism->current_state, delay_ns, sso_delay_ns);
438}
439
440static char *dm_ism_events_str[DM_ISM_NUM_EVENTS] = {
441 [DM_ISM_EVENT_IMMEDIATE] = "IMMEDIATE",
442 [DM_ISM_EVENT_ENTER_IDLE_REQUESTED] = "ENTER_IDLE_REQUESTED",
443 [DM_ISM_EVENT_EXIT_IDLE_REQUESTED] = "EXIT_IDLE_REQUESTED",
444 [DM_ISM_EVENT_BEGIN_CURSOR_UPDATE] = "BEGIN_CURSOR_UPDATE",
445 [DM_ISM_EVENT_END_CURSOR_UPDATE] = "END_CURSOR_UPDATE",
446 [DM_ISM_EVENT_TIMER_ELAPSED] = "TIMER_ELAPSED",
447 [DM_ISM_EVENT_SSO_TIMER_ELAPSED] = "SSO_TIMER_ELAPSED",
448};
449
450static char *dm_ism_states_str[DM_ISM_NUM_STATES] = {
451 [DM_ISM_STATE_FULL_POWER_RUNNING] = "FULL_POWER_RUNNING",
452 [DM_ISM_STATE_FULL_POWER_BUSY] = "FULL_POWER_BUSY",
453 [DM_ISM_STATE_HYSTERESIS_WAITING] = "HYSTERESIS_WAITING",
454 [DM_ISM_STATE_HYSTERESIS_BUSY] = "HYSTERESIS_BUSY",
455 [DM_ISM_STATE_OPTIMIZED_IDLE] = "OPTIMIZED_IDLE",
456 [DM_ISM_STATE_OPTIMIZED_IDLE_SSO] = "OPTIMIZED_IDLE_SSO",
457 [DM_ISM_STATE_TIMER_ABORTED] = "TIMER_ABORTED",
458};
459
460
461void amdgpu_dm_ism_commit_event(struct amdgpu_dm_ism *ism,
462 enum amdgpu_dm_ism_event event)
463{
464 enum amdgpu_dm_ism_event next_event = event;
465 struct amdgpu_crtc *acrtc = ism_to_amdgpu_crtc(ism);
466 struct amdgpu_device *adev = drm_to_adev(acrtc->base.dev);
467 struct amdgpu_display_manager *dm = &adev->dm;
468 struct dm_crtc_state *acrtc_state = to_dm_crtc_state(acrtc->base.state);
469
470 /* ISM transitions must be called with dc_lock held */
471 lockdep_assert_held(&dm->dc_lock);
472
473 /* ISM should not run after dc is destroyed */
474 ASSERT(dm->dc);
475
476 if (!acrtc_state) {
477 trace_amdgpu_dm_ism_event(acrtc->crtc_id, "NO_STATE",
478 "NO_STATE", "N/A");
479 return;
480 }
481
482 do {
483 bool transition = dm_ism_trigger_event(ism, event);
484
485 next_event = DM_ISM_NUM_EVENTS;
486 if (transition) {
487 trace_amdgpu_dm_ism_event(
488 acrtc->crtc_id,
489 dm_ism_states_str[ism->previous_state],
490 dm_ism_states_str[ism->current_state],
491 dm_ism_events_str[event]);
492 next_event = dm_ism_dispatch_power_state(ism, acrtc_state);
493 } else {
494 trace_amdgpu_dm_ism_event(
495 acrtc->crtc_id,
496 dm_ism_states_str[ism->current_state],
497 dm_ism_states_str[ism->current_state],
498 dm_ism_events_str[event]);
499 }
500
501 event = next_event;
502
503 } while (next_event < DM_ISM_NUM_EVENTS);
504}
505EXPORT_IF_KUNIT(amdgpu_dm_ism_commit_event);
506
507
508static void dm_ism_delayed_work_func(struct work_struct *work)
509{
510 struct amdgpu_dm_ism *ism =
511 container_of(work, struct amdgpu_dm_ism, delayed_work.work);
512 struct amdgpu_crtc *acrtc = ism_to_amdgpu_crtc(ism);
513 struct amdgpu_device *adev = drm_to_adev(acrtc->base.dev);
514 struct amdgpu_display_manager *dm = &adev->dm;
515
516 guard(mutex)(&dm->dc_lock);
517
518 amdgpu_dm_ism_commit_event(ism, DM_ISM_EVENT_TIMER_ELAPSED);
519}
520
521static void dm_ism_sso_delayed_work_func(struct work_struct *work)
522{
523 struct amdgpu_dm_ism *ism =
524 container_of(work, struct amdgpu_dm_ism, sso_delayed_work.work);
525 struct amdgpu_crtc *acrtc = ism_to_amdgpu_crtc(ism);
526 struct amdgpu_device *adev = drm_to_adev(acrtc->base.dev);
527 struct amdgpu_display_manager *dm = &adev->dm;
528
529 guard(mutex)(&dm->dc_lock);
530
531 amdgpu_dm_ism_commit_event(ism, DM_ISM_EVENT_SSO_TIMER_ELAPSED);
532}
533
534/**
535 * amdgpu_dm_ism_disable - Quiesce ISM workers
536 *
537 * @dm: The amdgpu display manager
538 *
539 * Cancels and disables any pending or in-flight ISM delayed work and waits
540 * for in-progress work to finish. After this returns, no ISM worker can run
541 * and subsequent mod_delayed_work() calls become no-ops via
542 * clear_pending_if_disabled().
543 *
544 * Must NOT be called with dc_lock held: the workers themselves take dc_lock,
545 * so a synchronous wait under dc_lock would deadlock.
546 *
547 * The caller is responsible for driving the FSM back to FULL_POWER_RUNNING
548 * (under dc_lock) by calling amdgpu_dm_ism_force_full_power().
549 */
550void amdgpu_dm_ism_disable(struct amdgpu_display_manager *dm)
551{
552 struct drm_crtc *crtc;
553 struct amdgpu_crtc *acrtc;
554 struct amdgpu_dm_ism *ism;
555
556 /*
557 * Caller must NOT hold dc_lock: the ISM delayed work handlers
558 * acquire dc_lock themselves, so waiting for them via
559 * disable_delayed_work_sync() while holding dc_lock would
560 * self-deadlock against an in-flight worker.
561 */
562 lockdep_assert_not_held(&dm->dc_lock);
563
564 drm_for_each_crtc(crtc, dm->ddev) {
565 acrtc = to_amdgpu_crtc(crtc);
566 ism = &acrtc->ism;
567
568 disable_delayed_work_sync(&ism->delayed_work);
569 disable_delayed_work_sync(&ism->sso_delayed_work);
570 }
571}
572EXPORT_IF_KUNIT(amdgpu_dm_ism_disable);
573
574/**
575 * amdgpu_dm_ism_force_full_power - Force every CRTC's ISM FSM to FULL_POWER
576 *
577 * @dm: The amdgpu display manager
578 *
579 * Sends DM_ISM_EVENT_EXIT_IDLE_REQUESTED to every CRTC's ISM, leaving each
580 * FSM in FULL_POWER_RUNNING. Intended to be paired with
581 * amdgpu_dm_ism_disable(): callers should first quiesce workers (without
582 * dc_lock), then take dc_lock and call this helper.
583 *
584 * Must be called with dc_lock held.
585 */
586void amdgpu_dm_ism_force_full_power(struct amdgpu_display_manager *dm)
587{
588 struct drm_crtc *crtc;
589 struct amdgpu_crtc *acrtc;
590
591 /*
592 * Caller must hold dc_lock: commit_event() drives the FSM and
593 * may touch dc state via dc_allow_idle_optimizations() etc.
594 */
595 lockdep_assert_held(&dm->dc_lock);
596
597 drm_for_each_crtc(crtc, dm->ddev) {
598 acrtc = to_amdgpu_crtc(crtc);
599
600 /*
601 * When disabled, leave in FULL_POWER_RUNNING state.
602 * EXIT_IDLE will not queue any work.
603 */
604 amdgpu_dm_ism_commit_event(&acrtc->ism,
605 DM_ISM_EVENT_EXIT_IDLE_REQUESTED);
606 }
607}
608EXPORT_IF_KUNIT(amdgpu_dm_ism_force_full_power);
609
610/**
611 * amdgpu_dm_ism_enable - enable the ISM
612 *
613 * @dm: The amdgpu display manager
614 *
615 * Re-enable the idle state manager by enabling work that was disabled by
616 * amdgpu_dm_ism_disable.
617 */
618void amdgpu_dm_ism_enable(struct amdgpu_display_manager *dm)
619{
620 struct drm_crtc *crtc;
621 struct amdgpu_crtc *acrtc;
622 struct amdgpu_dm_ism *ism;
623
624 drm_for_each_crtc(crtc, dm->ddev) {
625 acrtc = to_amdgpu_crtc(crtc);
626 ism = &acrtc->ism;
627
628 enable_delayed_work(&ism->delayed_work);
629 enable_delayed_work(&ism->sso_delayed_work);
630 }
631}
632EXPORT_IF_KUNIT(amdgpu_dm_ism_enable);
633
634void amdgpu_dm_ism_init(struct amdgpu_dm_ism *ism,
635 struct amdgpu_dm_ism_config *config)
636{
637 ism->config = *config;
638
639 ism->current_state = DM_ISM_STATE_FULL_POWER_RUNNING;
640 ism->previous_state = DM_ISM_STATE_FULL_POWER_RUNNING;
641 ism->next_record_idx = 0;
642 ism->last_idle_timestamp_ns = 0;
643
644 INIT_DELAYED_WORK(&ism->delayed_work, dm_ism_delayed_work_func);
645 INIT_DELAYED_WORK(&ism->sso_delayed_work, dm_ism_sso_delayed_work_func);
646}
647EXPORT_IF_KUNIT(amdgpu_dm_ism_init);
648
649
650void amdgpu_dm_ism_fini(struct amdgpu_dm_ism *ism)
651{
652 cancel_delayed_work_sync(&ism->sso_delayed_work);
653 cancel_delayed_work_sync(&ism->delayed_work);
654}
655EXPORT_IF_KUNIT(amdgpu_dm_ism_fini);