xref: /linux/drivers/gpu/drm/xe/xe_guc_rc.c (revision 0fc8f6200d2313278fbf4539bbab74677c685531)
1 // SPDX-License-Identifier: MIT
2 /*
3  * Copyright © 2026 Intel Corporation
4  */
5 
6 #include <drm/drm_print.h>
7 
8 #include "abi/guc_actions_slpc_abi.h"
9 #include "xe_device.h"
10 #include "xe_force_wake.h"
11 #include "xe_gt.h"
12 #include "xe_gt_idle.h"
13 #include "xe_gt_printk.h"
14 #include "xe_guc.h"
15 #include "xe_guc_ct.h"
16 #include "xe_guc_pc.h"
17 #include "xe_guc_rc.h"
18 #include "xe_pm.h"
19 
20 /**
21  * DOC: GuC RC (Render C-states)
22  *
23  * GuC handles the GT transition to deeper C-states in conjunction with Pcode.
24  * GuC RC can be enabled independently of the frequency component in SLPC,
25  * which is also controlled by GuC.
26  *
27  * This file will contain all H2G related logic for handling Render C-states.
28  * There are some calls to xe_gt_idle, where we enable host C6 when GuC RC is
29  * skipped. GuC RC is mostly independent of xe_guc_pc with the exception of
30  * functions that override the mode for which we have to rely on the SLPC H2G
31  * calls.
32  */
33 
34 static int guc_action_setup_gucrc(struct xe_guc *guc, u32 control)
35 {
36 	u32 action[] = {
37 		GUC_ACTION_HOST2GUC_SETUP_PC_GUCRC,
38 		control,
39 	};
40 	int ret;
41 
42 	ret = xe_guc_ct_send(&guc->ct, action, ARRAY_SIZE(action), 0, 0);
43 	if (ret && !(xe_device_wedged(guc_to_xe(guc)) && ret == -ECANCELED))
44 		xe_gt_err(guc_to_gt(guc),
45 			  "GuC RC setup %s(%u) failed (%pe)\n",
46 			   control == GUCRC_HOST_CONTROL ? "HOST_CONTROL" :
47 			   control == GUCRC_FIRMWARE_CONTROL ? "FIRMWARE_CONTROL" :
48 			   "UNKNOWN", control, ERR_PTR(ret));
49 	return ret;
50 }
51 
52 /**
53  * xe_guc_rc_disable() - Disable GuC RC
54  * @guc: Xe GuC instance
55  *
56  * Disables GuC RC by taking control of RC6 back from GuC.
57  */
58 void xe_guc_rc_disable(struct xe_guc *guc)
59 {
60 	struct xe_device *xe = guc_to_xe(guc);
61 	struct xe_gt *gt = guc_to_gt(guc);
62 
63 	if (!xe->info.skip_guc_pc && xe->info.platform != XE_PVC)
64 		if (guc_action_setup_gucrc(guc, GUCRC_HOST_CONTROL))
65 			return;
66 
67 	xe_gt_WARN_ON(gt, xe_gt_idle_disable_c6(gt));
68 }
69 
70 static void xe_guc_rc_fini_hw(void *arg)
71 {
72 	struct xe_guc *guc = arg;
73 	struct xe_device *xe = guc_to_xe(guc);
74 	struct xe_gt *gt = guc_to_gt(guc);
75 
76 	if (xe_device_wedged(xe))
77 		return;
78 
79 	CLASS(xe_force_wake, fw_ref)(gt_to_fw(gt), XE_FW_GT);
80 	xe_guc_rc_disable(guc);
81 }
82 
83 /**
84  * xe_guc_rc_init() - Init GuC RC
85  * @guc: Xe GuC instance
86  *
87  * Add callback action for GuC RC
88  *
89  * Return: 0 on success, negative error code on error.
90  */
91 int xe_guc_rc_init(struct xe_guc *guc)
92 {
93 	struct xe_device *xe = guc_to_xe(guc);
94 	struct xe_gt *gt = guc_to_gt(guc);
95 
96 	xe_gt_assert(gt, xe_device_uc_enabled(xe));
97 
98 	return devm_add_action_or_reset(xe->drm.dev, xe_guc_rc_fini_hw, guc);
99 }
100 
101 /**
102  * xe_guc_rc_enable() - Enable GuC RC feature if applicable
103  * @guc: Xe GuC instance
104  *
105  * Enables GuC RC feature.
106  *
107  * Return: 0 on success, negative error code on error.
108  */
109 int xe_guc_rc_enable(struct xe_guc *guc)
110 {
111 	struct xe_device *xe = guc_to_xe(guc);
112 	struct xe_gt *gt = guc_to_gt(guc);
113 
114 	xe_gt_assert(gt, xe_device_uc_enabled(xe));
115 
116 	CLASS(xe_force_wake, fw_ref)(gt_to_fw(gt), XE_FW_GT);
117 	if (!xe_force_wake_ref_has_domain(fw_ref.domains, XE_FW_GT))
118 		return -ETIMEDOUT;
119 
120 	if (xe->info.platform == XE_PVC) {
121 		xe_guc_rc_disable(guc);
122 		return 0;
123 	}
124 
125 	if (xe->info.skip_guc_pc) {
126 		xe_gt_idle_enable_c6(gt);
127 		return 0;
128 	}
129 
130 	return guc_action_setup_gucrc(guc, GUCRC_FIRMWARE_CONTROL);
131 }
132