| // 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_ALDERLAKE_P_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 = gt_to_guc(gt); |
| 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; |
| } |