Contributors: 13
Author Tokens Token Proportion Commits Commit Proportion
Vinay Belgaumkar 231 51.68% 7 22.58%
Matthew Brost 141 31.54% 2 6.45%
Michal Wajdeczko 13 2.91% 6 19.35%
Matt Roper 12 2.68% 2 6.45%
Rodrigo Vivi 10 2.24% 3 9.68%
Himal Prasad Ghimiray 8 1.79% 3 9.68%
Michał Winiarski 8 1.79% 1 3.23%
Brian Welty 8 1.79% 1 3.23%
Matthew Auld 8 1.79% 2 6.45%
Ashutosh Dixit 3 0.67% 1 3.23%
Daniele Ceraolo Spurio 2 0.45% 1 3.23%
Riana Tauro 2 0.45% 1 3.23%
Lucas De Marchi 1 0.22% 1 3.23%
Total 447 31


// SPDX-License-Identifier: MIT
/*
 * Copyright © 2026 Intel Corporation
 */

#include <drm/drm_print.h>

#include "abi/guc_actions_slpc_abi.h"
#include "xe_device.h"
#include "xe_force_wake.h"
#include "xe_gt.h"
#include "xe_gt_idle.h"
#include "xe_gt_printk.h"
#include "xe_guc.h"
#include "xe_guc_ct.h"
#include "xe_guc_pc.h"
#include "xe_guc_rc.h"
#include "xe_pm.h"

/**
 * DOC: GuC RC (Render C-states)
 *
 * GuC handles the GT transition to deeper C-states in conjunction with Pcode.
 * GuC RC can be enabled independently of the frequency component in SLPC,
 * which is also controlled by GuC.
 *
 * This file will contain all H2G related logic for handling Render C-states.
 * There are some calls to xe_gt_idle, where we enable host C6 when GuC RC is
 * skipped. GuC RC is mostly independent of xe_guc_pc with the exception of
 * functions that override the mode for which we have to rely on the SLPC H2G
 * calls.
 */

static int guc_action_setup_gucrc(struct xe_guc *guc, u32 control)
{
	u32 action[] = {
		GUC_ACTION_HOST2GUC_SETUP_PC_GUCRC,
		control,
	};
	int ret;

	ret = xe_guc_ct_send(&guc->ct, action, ARRAY_SIZE(action), 0, 0);
	if (ret && !(xe_device_wedged(guc_to_xe(guc)) && ret == -ECANCELED))
		xe_gt_err(guc_to_gt(guc),
			  "GuC RC setup %s(%u) failed (%pe)\n",
			   control == GUCRC_HOST_CONTROL ? "HOST_CONTROL" :
			   control == GUCRC_FIRMWARE_CONTROL ? "FIRMWARE_CONTROL" :
			   "UNKNOWN", control, ERR_PTR(ret));
	return ret;
}

/**
 * xe_guc_rc_disable() - Disable GuC RC
 * @guc: Xe GuC instance
 *
 * Disables GuC RC by taking control of RC6 back from GuC.
 */
void xe_guc_rc_disable(struct xe_guc *guc)
{
	struct xe_device *xe = guc_to_xe(guc);
	struct xe_gt *gt = guc_to_gt(guc);

	if (!xe->info.skip_guc_pc && xe->info.platform != XE_PVC)
		if (guc_action_setup_gucrc(guc, GUCRC_HOST_CONTROL))
			return;

	xe_gt_WARN_ON(gt, xe_gt_idle_disable_c6(gt));
}

static void xe_guc_rc_fini_hw(void *arg)
{
	struct xe_guc *guc = arg;
	struct xe_device *xe = guc_to_xe(guc);
	struct xe_gt *gt = guc_to_gt(guc);

	if (xe_device_wedged(xe))
		return;

	CLASS(xe_force_wake, fw_ref)(gt_to_fw(gt), XE_FW_GT);
	xe_guc_rc_disable(guc);
}

/**
 * xe_guc_rc_init() - Init GuC RC
 * @guc: Xe GuC instance
 *
 * Add callback action for GuC RC
 *
 * Return: 0 on success, negative error code on error.
 */
int xe_guc_rc_init(struct xe_guc *guc)
{
	struct xe_device *xe = guc_to_xe(guc);
	struct xe_gt *gt = guc_to_gt(guc);

	xe_gt_assert(gt, xe_device_uc_enabled(xe));

	return devm_add_action_or_reset(xe->drm.dev, xe_guc_rc_fini_hw, guc);
}

/**
 * xe_guc_rc_enable() - Enable GuC RC feature if applicable
 * @guc: Xe GuC instance
 *
 * Enables GuC RC feature.
 *
 * Return: 0 on success, negative error code on error.
 */
int xe_guc_rc_enable(struct xe_guc *guc)
{
	struct xe_device *xe = guc_to_xe(guc);
	struct xe_gt *gt = guc_to_gt(guc);

	xe_gt_assert(gt, xe_device_uc_enabled(xe));

	CLASS(xe_force_wake, fw_ref)(gt_to_fw(gt), XE_FW_GT);
	if (!xe_force_wake_ref_has_domain(fw_ref.domains, XE_FW_GT))
		return -ETIMEDOUT;

	if (xe->info.platform == XE_PVC) {
		xe_guc_rc_disable(guc);
		return 0;
	}

	if (xe->info.skip_guc_pc) {
		xe_gt_idle_enable_c6(gt);
		return 0;
	}

	return guc_action_setup_gucrc(guc, GUCRC_FIRMWARE_CONTROL);
}