165 lines
3.4 KiB
C
165 lines
3.4 KiB
C
// SPDX-License-Identifier: MIT
|
|
/*
|
|
* Copyright © 2022 Intel Corporation
|
|
*/
|
|
|
|
#include "gt/intel_gt.h"
|
|
#include "gt/intel_hwconfig.h"
|
|
#include "i915_drv.h"
|
|
#include "i915_memcpy.h"
|
|
|
|
/*
|
|
* GuC has a blob containing hardware configuration information (HWConfig).
|
|
* This is formatted as a simple and flexible KLV (Key/Length/Value) table.
|
|
*
|
|
* For example, a minimal version could be:
|
|
* enum device_attr {
|
|
* ATTR_SOME_VALUE = 0,
|
|
* ATTR_SOME_MASK = 1,
|
|
* };
|
|
*
|
|
* static const u32 hwconfig[] = {
|
|
* ATTR_SOME_VALUE,
|
|
* 1, // Value Length in DWords
|
|
* 8, // Value
|
|
*
|
|
* ATTR_SOME_MASK,
|
|
* 3,
|
|
* 0x00FFFFFFFF, 0xFFFFFFFF, 0xFF000000,
|
|
* };
|
|
*
|
|
* The attribute ids are defined in a hardware spec.
|
|
*/
|
|
|
|
static int __guc_action_get_hwconfig(struct intel_guc *guc,
|
|
u32 ggtt_offset, u32 ggtt_size)
|
|
{
|
|
u32 action[] = {
|
|
INTEL_GUC_ACTION_GET_HWCONFIG,
|
|
lower_32_bits(ggtt_offset),
|
|
upper_32_bits(ggtt_offset),
|
|
ggtt_size,
|
|
};
|
|
int ret;
|
|
|
|
ret = intel_guc_send_mmio(guc, action, ARRAY_SIZE(action), NULL, 0);
|
|
if (ret == -ENXIO)
|
|
return -ENOENT;
|
|
|
|
return ret;
|
|
}
|
|
|
|
static int guc_hwconfig_discover_size(struct intel_guc *guc, struct intel_hwconfig *hwconfig)
|
|
{
|
|
int ret;
|
|
|
|
/*
|
|
* Sending a query with zero offset and size will return the
|
|
* size of the blob.
|
|
*/
|
|
ret = __guc_action_get_hwconfig(guc, 0, 0);
|
|
if (ret < 0)
|
|
return ret;
|
|
|
|
if (ret == 0)
|
|
return -EINVAL;
|
|
|
|
hwconfig->size = ret;
|
|
return 0;
|
|
}
|
|
|
|
static int guc_hwconfig_fill_buffer(struct intel_guc *guc, struct intel_hwconfig *hwconfig)
|
|
{
|
|
struct i915_vma *vma;
|
|
u32 ggtt_offset;
|
|
void *vaddr;
|
|
int ret;
|
|
|
|
GEM_BUG_ON(!hwconfig->size);
|
|
|
|
ret = intel_guc_allocate_and_map_vma(guc, hwconfig->size, &vma, &vaddr);
|
|
if (ret)
|
|
return ret;
|
|
|
|
ggtt_offset = intel_guc_ggtt_offset(guc, vma);
|
|
|
|
ret = __guc_action_get_hwconfig(guc, ggtt_offset, hwconfig->size);
|
|
if (ret >= 0)
|
|
memcpy(hwconfig->ptr, vaddr, hwconfig->size);
|
|
|
|
i915_vma_unpin_and_release(&vma, I915_VMA_RELEASE_MAP);
|
|
|
|
return ret;
|
|
}
|
|
|
|
static bool has_table(struct drm_i915_private *i915)
|
|
{
|
|
if (IS_ALDERLAKE_P(i915) && !IS_ADLP_N(i915))
|
|
return true;
|
|
if (GRAPHICS_VER_FULL(i915) >= IP_VER(12, 55))
|
|
return true;
|
|
|
|
return false;
|
|
}
|
|
|
|
/**
|
|
* intel_guc_hwconfig_init - Initialize the HWConfig
|
|
*
|
|
* Retrieve the HWConfig table from the GuC and save it locally.
|
|
* It can then be queried on demand by other users later on.
|
|
*/
|
|
static int guc_hwconfig_init(struct intel_gt *gt)
|
|
{
|
|
struct intel_hwconfig *hwconfig = >->info.hwconfig;
|
|
struct intel_guc *guc = >->uc.guc;
|
|
int ret;
|
|
|
|
if (!has_table(gt->i915))
|
|
return 0;
|
|
|
|
ret = guc_hwconfig_discover_size(guc, hwconfig);
|
|
if (ret)
|
|
return ret;
|
|
|
|
hwconfig->ptr = kmalloc(hwconfig->size, GFP_KERNEL);
|
|
if (!hwconfig->ptr) {
|
|
hwconfig->size = 0;
|
|
return -ENOMEM;
|
|
}
|
|
|
|
ret = guc_hwconfig_fill_buffer(guc, hwconfig);
|
|
if (ret < 0) {
|
|
intel_gt_fini_hwconfig(gt);
|
|
return ret;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* intel_gt_init_hwconfig - Initialize the HWConfig if available
|
|
*
|
|
* Retrieve the HWConfig table if available on the current platform.
|
|
*/
|
|
int intel_gt_init_hwconfig(struct intel_gt *gt)
|
|
{
|
|
if (!intel_uc_uses_guc(>->uc))
|
|
return 0;
|
|
|
|
return guc_hwconfig_init(gt);
|
|
}
|
|
|
|
/**
|
|
* intel_gt_fini_hwconfig - Finalize the HWConfig
|
|
*
|
|
* Free up the memory allocation holding the table.
|
|
*/
|
|
void intel_gt_fini_hwconfig(struct intel_gt *gt)
|
|
{
|
|
struct intel_hwconfig *hwconfig = >->info.hwconfig;
|
|
|
|
kfree(hwconfig->ptr);
|
|
hwconfig->size = 0;
|
|
hwconfig->ptr = NULL;
|
|
}
|