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