From: John Harrison <john.c.harri...@intel.com>

Implement support for fetching the hardware description table from the
GuC. The call is made twice - once without a destination buffer to
query the size and then a second time to fill in the buffer.

Note that the table is only available on ADL-P and later platforms.

Cc: Michal Wajdeczko <michal.wajdec...@intel.com>
Signed-off-by: Rodrigo Vivi <rodrigo.v...@intel.com>
Signed-off-by: John Harrison <john.c.harri...@intel.com>
---
 drivers/gpu/drm/i915/Makefile                 |   1 +
 .../gpu/drm/i915/gt/uc/abi/guc_actions_abi.h  |   1 +
 .../gpu/drm/i915/gt/uc/abi/guc_errors_abi.h   |   4 +
 drivers/gpu/drm/i915/gt/uc/intel_guc.c        |   3 +-
 drivers/gpu/drm/i915/gt/uc/intel_guc.h        |   2 +
 .../gpu/drm/i915/gt/uc/intel_guc_hwconfig.c   | 156 ++++++++++++++++++
 .../gpu/drm/i915/gt/uc/intel_guc_hwconfig.h   |  19 +++
 drivers/gpu/drm/i915/gt/uc/intel_uc.c         |   6 +
 8 files changed, 191 insertions(+), 1 deletion(-)
 create mode 100644 drivers/gpu/drm/i915/gt/uc/intel_guc_hwconfig.c
 create mode 100644 drivers/gpu/drm/i915/gt/uc/intel_guc_hwconfig.h

diff --git a/drivers/gpu/drm/i915/Makefile b/drivers/gpu/drm/i915/Makefile
index bde3d4f462a9..bce7c597f1df 100644
--- a/drivers/gpu/drm/i915/Makefile
+++ b/drivers/gpu/drm/i915/Makefile
@@ -189,6 +189,7 @@ i915-y += gt/uc/intel_uc.o \
          gt/uc/intel_guc_log.o \
          gt/uc/intel_guc_log_debugfs.o \
          gt/uc/intel_guc_submission.o \
+         gt/uc/intel_guc_hwconfig.o \
          gt/uc/intel_huc.o \
          gt/uc/intel_huc_debugfs.o \
          gt/uc/intel_huc_fw.o
diff --git a/drivers/gpu/drm/i915/gt/uc/abi/guc_actions_abi.h 
b/drivers/gpu/drm/i915/gt/uc/abi/guc_actions_abi.h
index 2d6198e63ebe..30c854c34d8f 100644
--- a/drivers/gpu/drm/i915/gt/uc/abi/guc_actions_abi.h
+++ b/drivers/gpu/drm/i915/gt/uc/abi/guc_actions_abi.h
@@ -126,6 +126,7 @@ enum intel_guc_action {
        INTEL_GUC_ACTION_EXIT_S_STATE = 0x502,
        INTEL_GUC_ACTION_SLPC_REQUEST = 0x3003,
        INTEL_GUC_ACTION_AUTHENTICATE_HUC = 0x4000,
+       INTEL_GUC_ACTION_GET_HWCONFIG = 0x4100,
        INTEL_GUC_ACTION_REGISTER_COMMAND_TRANSPORT_BUFFER = 0x4505,
        INTEL_GUC_ACTION_DEREGISTER_COMMAND_TRANSPORT_BUFFER = 0x4506,
        INTEL_GUC_ACTION_LIMIT
diff --git a/drivers/gpu/drm/i915/gt/uc/abi/guc_errors_abi.h 
b/drivers/gpu/drm/i915/gt/uc/abi/guc_errors_abi.h
index 488b6061ee89..f9e2a6aaef4a 100644
--- a/drivers/gpu/drm/i915/gt/uc/abi/guc_errors_abi.h
+++ b/drivers/gpu/drm/i915/gt/uc/abi/guc_errors_abi.h
@@ -8,6 +8,10 @@
 
 enum intel_guc_response_status {
        INTEL_GUC_RESPONSE_STATUS_SUCCESS = 0x0,
+       INTEL_GUC_RESPONSE_NOT_SUPPORTED = 0x20,
+       INTEL_GUC_RESPONSE_NO_ATTRIBUTE_TABLE = 0x201,
+       INTEL_GUC_RESPONSE_NO_DECRYPTION_KEY = 0x202,
+       INTEL_GUC_RESPONSE_DECRYPTION_FAILED = 0x204,
        INTEL_GUC_RESPONSE_STATUS_GENERIC_FAIL = 0xF000,
 };
 
diff --git a/drivers/gpu/drm/i915/gt/uc/intel_guc.c 
b/drivers/gpu/drm/i915/gt/uc/intel_guc.c
index 6661dcb02239..e810b56c49f7 100644
--- a/drivers/gpu/drm/i915/gt/uc/intel_guc.c
+++ b/drivers/gpu/drm/i915/gt/uc/intel_guc.c
@@ -400,13 +400,14 @@ int intel_guc_send_mmio(struct intel_guc *guc, const u32 
*request, u32 len,
        /*
         * No GuC command should ever take longer than 10ms.
         * Fast commands should still complete in 10us.
+        * Except for the hwconfig table query, which takes ~50ms.
         */
        ret = __intel_wait_for_register_fw(uncore,
                                           guc_send_reg(guc, 0),
                                           GUC_HXG_MSG_0_ORIGIN,
                                           FIELD_PREP(GUC_HXG_MSG_0_ORIGIN,
                                                      GUC_HXG_ORIGIN_GUC),
-                                          10, 10, &header);
+                                          10, 100, &header);
        if (unlikely(ret)) {
 timeout:
                drm_err(&i915->drm, "mmio request %#x: no reply %x\n",
diff --git a/drivers/gpu/drm/i915/gt/uc/intel_guc.h 
b/drivers/gpu/drm/i915/gt/uc/intel_guc.h
index 72e4653222e2..5988d0311cfc 100644
--- a/drivers/gpu/drm/i915/gt/uc/intel_guc.h
+++ b/drivers/gpu/drm/i915/gt/uc/intel_guc.h
@@ -12,6 +12,7 @@
 #include "intel_guc_ct.h"
 #include "intel_guc_log.h"
 #include "intel_guc_reg.h"
+#include "intel_guc_hwconfig.h"
 #include "intel_uc_fw.h"
 #include "i915_utils.h"
 #include "i915_vma.h"
@@ -27,6 +28,7 @@ struct intel_guc {
        struct intel_uc_fw fw;
        struct intel_guc_log log;
        struct intel_guc_ct ct;
+       struct intel_guc_hwconfig hwconfig;
 
        /* intel_guc_recv interrupt related state */
        spinlock_t irq_lock;
diff --git a/drivers/gpu/drm/i915/gt/uc/intel_guc_hwconfig.c 
b/drivers/gpu/drm/i915/gt/uc/intel_guc_hwconfig.c
new file mode 100644
index 000000000000..af4fc9fdbaaf
--- /dev/null
+++ b/drivers/gpu/drm/i915/gt/uc/intel_guc_hwconfig.c
@@ -0,0 +1,156 @@
+// SPDX-License-Identifier: MIT
+/*
+ * Copyright © 2021 Intel Corporation
+ */
+
+#include "gt/intel_gt.h"
+#include "i915_drv.h"
+#include "i915_memcpy.h"
+#include "intel_guc_hwconfig.h"
+
+static inline struct intel_guc *hwconfig_to_guc(struct intel_guc_hwconfig 
*hwconfig)
+{
+       return container_of(hwconfig, struct intel_guc, hwconfig);
+}
+
+/*
+ * 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_hwconfig *hwconfig,
+                                    u32 ggtt_offset, u32 ggtt_size)
+{
+       struct intel_guc *guc = hwconfig_to_guc(hwconfig);
+       struct drm_i915_private *i915 = guc_to_gt(guc)->i915;
+       u32 action[] = {
+               INTEL_GUC_ACTION_GET_HWCONFIG,
+               ggtt_offset,
+               ggtt_size,
+               (INTEL_DEVID(i915) << 16) | INTEL_REVID(i915),
+       };
+       int ret;
+
+       ret = intel_guc_send_mmio(guc, action, ARRAY_SIZE(action), NULL, 0);
+       if (ret == -ENXIO)
+               return -ENOENT;
+
+       if (!ggtt_size && !ret)
+               ret = -EINVAL;
+
+       return ret;
+}
+
+static int guc_hwconfig_discover_size(struct intel_guc_hwconfig *hwconfig)
+{
+       int ret;
+
+       /* Sending a query with too small a table will return the size of the 
table */
+       ret = __guc_action_get_hwconfig(hwconfig, 0, 0);
+       if (ret < 0)
+               return ret;
+
+       hwconfig->size = ret;
+       return 0;
+}
+
+static int guc_hwconfig_fill_buffer(struct intel_guc_hwconfig *hwconfig)
+{
+       struct intel_guc *guc = hwconfig_to_guc(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(hwconfig, 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)) {
+               if (IS_ADLP_GT_STEP(i915, STEP_A0, STEP_A0))
+                       return false;
+
+               return true;
+       }
+
+       return false;
+}
+
+/**
+ * intel_guc_hwconfig_fini - Finalize the HWConfig
+ *
+ * Free up the memory allocation holding the table.
+ */
+void intel_guc_hwconfig_fini(struct intel_guc_hwconfig *hwconfig)
+{
+       kfree(hwconfig->ptr);
+       hwconfig->size = 0;
+       hwconfig->ptr = NULL;
+}
+
+/**
+ * intel_guc_hwconfig_init - Initialize the HWConfig
+ *
+ * Retrieve the HWConfig table from the GuC and save it away in a local memory
+ * allocation. It can then be queried on demand by other users later on.
+ */
+int intel_guc_hwconfig_init(struct intel_guc_hwconfig *hwconfig)
+{
+       struct intel_guc *guc = hwconfig_to_guc(hwconfig);
+       struct drm_i915_private *i915 = guc_to_gt(guc)->i915;
+       int ret;
+
+       if (!has_table(i915))
+               return 0;
+
+       ret = guc_hwconfig_discover_size(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(hwconfig);
+       if (ret < 0) {
+               intel_guc_hwconfig_fini(hwconfig);
+               return ret;
+       }
+
+       return 0;
+}
diff --git a/drivers/gpu/drm/i915/gt/uc/intel_guc_hwconfig.h 
b/drivers/gpu/drm/i915/gt/uc/intel_guc_hwconfig.h
new file mode 100644
index 000000000000..fdd7f0d6e938
--- /dev/null
+++ b/drivers/gpu/drm/i915/gt/uc/intel_guc_hwconfig.h
@@ -0,0 +1,19 @@
+/* SPDX-License-Identifier: MIT */
+/*
+ * Copyright © 2021 Intel Corporation
+ */
+
+#ifndef _INTEL_GUC_HWCONFIG_H_
+#define _INTEL_GUC_HWCONFIG_H_
+
+#include <linux/types.h>
+
+struct intel_guc_hwconfig {
+       u32 size;
+       void *ptr;
+};
+
+int intel_guc_hwconfig_init(struct intel_guc_hwconfig *hwconfig);
+void intel_guc_hwconfig_fini(struct intel_guc_hwconfig *hwconfig);
+
+#endif /* _INTEL_GUC_HWCONFIG_H_ */
diff --git a/drivers/gpu/drm/i915/gt/uc/intel_uc.c 
b/drivers/gpu/drm/i915/gt/uc/intel_uc.c
index 6d8b9233214e..6df596152937 100644
--- a/drivers/gpu/drm/i915/gt/uc/intel_uc.c
+++ b/drivers/gpu/drm/i915/gt/uc/intel_uc.c
@@ -496,6 +496,10 @@ static int __uc_init_hw(struct intel_uc *uc)
        if (ret)
                goto err_log_capture;
 
+       ret = intel_guc_hwconfig_init(&guc->hwconfig);
+       if (ret)
+               drm_err(&i915->drm, "Failed to retrieve hwconfig table: %d\n", 
ret);
+
        ret = guc_enable_communication(guc);
        if (ret)
                goto err_log_capture;
@@ -552,6 +556,8 @@ static void __uc_fini_hw(struct intel_uc *uc)
        if (intel_uc_uses_guc_submission(uc))
                intel_guc_submission_disable(guc);
 
+       intel_guc_hwconfig_fini(&guc->hwconfig);
+
        __uc_sanitize(uc);
 }
 
-- 
2.25.1

_______________________________________________
Intel-gfx mailing list
Intel-gfx@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/intel-gfx

Reply via email to