1 // SPDX-License-Identifier: MIT
2 /*
3 * Copyright © 2022 Intel Corporation
4 */
5
6 #include "gt/intel_gt.h"
7 #include "gt/intel_hwconfig.h"
8 #include "i915_drv.h"
9 #include "i915_memcpy.h"
10
11 /*
12 * GuC has a blob containing hardware configuration information (HWConfig).
13 * This is formatted as a simple and flexible KLV (Key/Length/Value) table.
14 *
15 * For example, a minimal version could be:
16 * enum device_attr {
17 * ATTR_SOME_VALUE = 0,
18 * ATTR_SOME_MASK = 1,
19 * };
20 *
21 * static const u32 hwconfig[] = {
22 * ATTR_SOME_VALUE,
23 * 1, // Value Length in DWords
24 * 8, // Value
25 *
26 * ATTR_SOME_MASK,
27 * 3,
28 * 0x00FFFFFFFF, 0xFFFFFFFF, 0xFF000000,
29 * };
30 *
31 * The attribute ids are defined in a hardware spec.
32 */
33
__guc_action_get_hwconfig(struct intel_guc * guc,u32 ggtt_offset,u32 ggtt_size)34 static int __guc_action_get_hwconfig(struct intel_guc *guc,
35 u32 ggtt_offset, u32 ggtt_size)
36 {
37 u32 action[] = {
38 INTEL_GUC_ACTION_GET_HWCONFIG,
39 lower_32_bits(ggtt_offset),
40 upper_32_bits(ggtt_offset),
41 ggtt_size,
42 };
43 int ret;
44
45 ret = intel_guc_send_mmio(guc, action, ARRAY_SIZE(action), NULL, 0);
46 if (ret == -ENXIO)
47 return -ENOENT;
48
49 return ret;
50 }
51
guc_hwconfig_discover_size(struct intel_guc * guc,struct intel_hwconfig * hwconfig)52 static int guc_hwconfig_discover_size(struct intel_guc *guc, struct intel_hwconfig *hwconfig)
53 {
54 int ret;
55
56 /*
57 * Sending a query with zero offset and size will return the
58 * size of the blob.
59 */
60 ret = __guc_action_get_hwconfig(guc, 0, 0);
61 if (ret < 0)
62 return ret;
63
64 if (ret == 0)
65 return -EINVAL;
66
67 hwconfig->size = ret;
68 return 0;
69 }
70
guc_hwconfig_fill_buffer(struct intel_guc * guc,struct intel_hwconfig * hwconfig)71 static int guc_hwconfig_fill_buffer(struct intel_guc *guc, struct intel_hwconfig *hwconfig)
72 {
73 struct i915_vma *vma;
74 u32 ggtt_offset;
75 void *vaddr;
76 int ret;
77
78 GEM_BUG_ON(!hwconfig->size);
79
80 ret = intel_guc_allocate_and_map_vma(guc, hwconfig->size, &vma, &vaddr);
81 if (ret)
82 return ret;
83
84 ggtt_offset = intel_guc_ggtt_offset(guc, vma);
85
86 ret = __guc_action_get_hwconfig(guc, ggtt_offset, hwconfig->size);
87 if (ret >= 0)
88 memcpy(hwconfig->ptr, vaddr, hwconfig->size);
89
90 i915_vma_unpin_and_release(&vma, I915_VMA_RELEASE_MAP);
91
92 return ret;
93 }
94
has_table(struct drm_i915_private * i915)95 static bool has_table(struct drm_i915_private *i915)
96 {
97 if (IS_ALDERLAKE_P(i915) && !IS_ALDERLAKE_P_N(i915))
98 return true;
99 if (GRAPHICS_VER_FULL(i915) >= IP_VER(12, 55))
100 return true;
101
102 return false;
103 }
104
105 /*
106 * intel_guc_hwconfig_init - Initialize the HWConfig
107 *
108 * Retrieve the HWConfig table from the GuC and save it locally.
109 * It can then be queried on demand by other users later on.
110 */
guc_hwconfig_init(struct intel_gt * gt)111 static int guc_hwconfig_init(struct intel_gt *gt)
112 {
113 struct intel_hwconfig *hwconfig = >->info.hwconfig;
114 struct intel_guc *guc = >->uc.guc;
115 int ret;
116
117 if (!has_table(gt->i915))
118 return 0;
119
120 ret = guc_hwconfig_discover_size(guc, hwconfig);
121 if (ret)
122 return ret;
123
124 hwconfig->ptr = kmalloc(hwconfig->size, GFP_KERNEL);
125 if (!hwconfig->ptr) {
126 hwconfig->size = 0;
127 return -ENOMEM;
128 }
129
130 ret = guc_hwconfig_fill_buffer(guc, hwconfig);
131 if (ret < 0) {
132 intel_gt_fini_hwconfig(gt);
133 return ret;
134 }
135
136 return 0;
137 }
138
139 /*
140 * intel_gt_init_hwconfig - Initialize the HWConfig if available
141 *
142 * Retrieve the HWConfig table if available on the current platform.
143 */
intel_gt_init_hwconfig(struct intel_gt * gt)144 int intel_gt_init_hwconfig(struct intel_gt *gt)
145 {
146 if (!intel_uc_uses_guc(>->uc))
147 return 0;
148
149 return guc_hwconfig_init(gt);
150 }
151
152 /*
153 * intel_gt_fini_hwconfig - Finalize the HWConfig
154 *
155 * Free up the memory allocation holding the table.
156 */
intel_gt_fini_hwconfig(struct intel_gt * gt)157 void intel_gt_fini_hwconfig(struct intel_gt *gt)
158 {
159 struct intel_hwconfig *hwconfig = >->info.hwconfig;
160
161 kfree(hwconfig->ptr);
162 hwconfig->size = 0;
163 hwconfig->ptr = NULL;
164 }
165