drm/amdgpu: add cgs_get_firmware_info interface v2
authorJammy Zhou <Jammy.Zhou@amd.com>
Wed, 13 May 2015 10:58:05 +0000 (18:58 +0800)
committerAlex Deucher <alexander.deucher@amd.com>
Mon, 17 Aug 2015 20:50:11 +0000 (16:50 -0400)
This new interface can be used by IP components to retrieve the
firmware information from the core driver.

v2: fix one typo

Signed-off-by: Jammy Zhou <Jammy.Zhou@amd.com>
Signed-off-by: Rex Zhu <Rex.Zhou@amd.com>
Signed-off-by: Young Yang <Young.Yang@amd.com>
Reviewed-by: Alex Deucher <alexander.deucher@amd.com>
drivers/gpu/drm/amd/amdgpu/amdgpu_cgs.c
drivers/gpu/drm/amd/include/cgs_common.h

index ac0f124cf6d073d4daa4a6b90ec24a79f5995472..520d01715737b816ca6d2658c356a0fdb62f7624 100644 (file)
 #include <linux/slab.h>
 #include <linux/pci.h>
 #include <drm/drmP.h>
+#include <linux/firmware.h>
 #include <drm/amdgpu_drm.h>
 #include "amdgpu.h"
 #include "cgs_linux.h"
 #include "atom.h"
+#include "amdgpu_ucode.h"
+
 
 struct amdgpu_cgs_device {
        struct cgs_device base;
@@ -611,6 +614,122 @@ static int amdgpu_cgs_irq_put(void *cgs_device, unsigned src_id, unsigned type)
        return amdgpu_irq_put(adev, adev->irq.sources[src_id], type);
 }
 
+static uint32_t fw_type_convert(void *cgs_device, uint32_t fw_type)
+{
+       CGS_FUNC_ADEV;
+       enum AMDGPU_UCODE_ID result = AMDGPU_UCODE_ID_MAXIMUM;
+
+       switch (fw_type) {
+       case CGS_UCODE_ID_SDMA0:
+               result = AMDGPU_UCODE_ID_SDMA0;
+               break;
+       case CGS_UCODE_ID_SDMA1:
+               result = AMDGPU_UCODE_ID_SDMA1;
+               break;
+       case CGS_UCODE_ID_CP_CE:
+               result = AMDGPU_UCODE_ID_CP_CE;
+               break;
+       case CGS_UCODE_ID_CP_PFP:
+               result = AMDGPU_UCODE_ID_CP_PFP;
+               break;
+       case CGS_UCODE_ID_CP_ME:
+               result = AMDGPU_UCODE_ID_CP_ME;
+               break;
+       case CGS_UCODE_ID_CP_MEC:
+       case CGS_UCODE_ID_CP_MEC_JT1:
+               result = AMDGPU_UCODE_ID_CP_MEC1;
+               break;
+       case CGS_UCODE_ID_CP_MEC_JT2:
+               if (adev->asic_type == CHIP_TONGA)
+                       result = AMDGPU_UCODE_ID_CP_MEC2;
+               else if (adev->asic_type == CHIP_CARRIZO)
+                       result = AMDGPU_UCODE_ID_CP_MEC1;
+               break;
+       case CGS_UCODE_ID_RLC_G:
+               result = AMDGPU_UCODE_ID_RLC_G;
+               break;
+       default:
+               DRM_ERROR("Firmware type not supported\n");
+       }
+       return result;
+}
+
+static int amdgpu_cgs_get_firmware_info(void *cgs_device,
+                                       enum cgs_ucode_id type,
+                                       struct cgs_firmware_info *info)
+{
+       CGS_FUNC_ADEV;
+
+       if (CGS_UCODE_ID_SMU != type) {
+               uint64_t gpu_addr;
+               uint32_t data_size;
+               const struct gfx_firmware_header_v1_0 *header;
+               enum AMDGPU_UCODE_ID id;
+               struct amdgpu_firmware_info *ucode;
+
+               id = fw_type_convert(cgs_device, type);
+               ucode = &adev->firmware.ucode[id];
+               if (ucode->fw == NULL)
+                       return -EINVAL;
+
+               gpu_addr  = ucode->mc_addr;
+               header = (const struct gfx_firmware_header_v1_0 *)ucode->fw->data;
+               data_size = le32_to_cpu(header->header.ucode_size_bytes);
+
+               if ((type == CGS_UCODE_ID_CP_MEC_JT1) ||
+                   (type == CGS_UCODE_ID_CP_MEC_JT2)) {
+                       gpu_addr += le32_to_cpu(header->jt_offset) << 2;
+                       data_size = le32_to_cpu(header->jt_size) << 2;
+               }
+               info->mc_addr = gpu_addr;
+               info->image_size = data_size;
+               info->version = (uint16_t)le32_to_cpu(header->header.ucode_version);
+               info->feature_version = (uint16_t)le32_to_cpu(header->ucode_feature_version);
+       } else {
+               char fw_name[30] = {0};
+               int err = 0;
+               uint32_t ucode_size;
+               uint32_t ucode_start_address;
+               const uint8_t *src;
+               const struct smc_firmware_header_v1_0 *hdr;
+
+               switch (adev->asic_type) {
+               case CHIP_TONGA:
+                       strcpy(fw_name, "amdgpu/tonga_smc.bin");
+                       break;
+               default:
+                       DRM_ERROR("SMC firmware not supported\n");
+                       return -EINVAL;
+               }
+
+               err = request_firmware(&adev->pm.fw, fw_name, adev->dev);
+               if (err) {
+                       DRM_ERROR("Failed to request firmware\n");
+                       return err;
+               }
+
+               err = amdgpu_ucode_validate(adev->pm.fw);
+               if (err) {
+                       DRM_ERROR("Failed to load firmware \"%s\"", fw_name);
+                       release_firmware(adev->pm.fw);
+                       adev->pm.fw = NULL;
+                       return err;
+               }
+
+               hdr = (const struct smc_firmware_header_v1_0 *) adev->pm.fw->data;
+               adev->pm.fw_version = le32_to_cpu(hdr->header.ucode_version);
+               ucode_size = le32_to_cpu(hdr->header.ucode_size_bytes);
+               ucode_start_address = le32_to_cpu(hdr->ucode_start_addr);
+               src = (const uint8_t *)(adev->pm.fw->data +
+                      le32_to_cpu(hdr->header.ucode_array_offset_bytes));
+
+               info->version = adev->pm.fw_version;
+               info->image_size = ucode_size;
+               info->kptr = (void *)src;
+       }
+       return 0;
+}
+
 static const struct cgs_ops amdgpu_cgs_ops = {
        amdgpu_cgs_gpu_mem_info,
        amdgpu_cgs_gmap_kmem,
@@ -640,7 +759,8 @@ static const struct cgs_ops amdgpu_cgs_ops = {
        amdgpu_cgs_pm_request_clock,
        amdgpu_cgs_pm_request_engine,
        amdgpu_cgs_pm_query_clock_limits,
-       amdgpu_cgs_set_camera_voltages
+       amdgpu_cgs_set_camera_voltages,
+       amdgpu_cgs_get_firmware_info
 };
 
 static const struct cgs_os_ops amdgpu_cgs_os_ops = {
index f8cdb8875d896340e50d58befd90ed0475d312aa..d586c24a3d310d39d2cf154b0501cc7a1c81df12 100644 (file)
@@ -24,6 +24,7 @@
 #ifndef _CGS_COMMON_H
 #define _CGS_COMMON_H
 
+
 /**
  * enum cgs_gpu_mem_type - GPU memory types
  */
@@ -85,6 +86,24 @@ enum cgs_voltage_planes {
        /* ... */
 };
 
+/*
+ * enum cgs_ucode_id - Firmware types for different IPs
+ */
+enum cgs_ucode_id {
+       CGS_UCODE_ID_SMU = 0,
+       CGS_UCODE_ID_SDMA0,
+       CGS_UCODE_ID_SDMA1,
+       CGS_UCODE_ID_CP_CE,
+       CGS_UCODE_ID_CP_PFP,
+       CGS_UCODE_ID_CP_ME,
+       CGS_UCODE_ID_CP_MEC,
+       CGS_UCODE_ID_CP_MEC_JT1,
+       CGS_UCODE_ID_CP_MEC_JT2,
+       CGS_UCODE_ID_GMCON_RENG,
+       CGS_UCODE_ID_RLC_G,
+       CGS_UCODE_ID_MAXIMUM,
+};
+
 /**
  * struct cgs_clock_limits - Clock limits
  *
@@ -96,6 +115,17 @@ struct cgs_clock_limits {
        unsigned sustainable;   /**< Thermally sustainable frequency */
 };
 
+/**
+ * struct cgs_firmware_info - Firmware information
+ */
+struct cgs_firmware_info {
+       uint16_t                version;
+       uint16_t                feature_version;
+       uint32_t                image_size;
+       uint64_t                mc_addr;
+       void                    *kptr;
+};
+
 typedef unsigned long cgs_handle_t;
 
 /**
@@ -442,6 +472,18 @@ typedef int (*cgs_pm_query_clock_limits_t)(void *cgs_device,
  */
 typedef int (*cgs_set_camera_voltages_t)(void *cgs_device, uint32_t mask,
                                         const uint32_t *voltages);
+/**
+ * cgs_get_firmware_info - Get the firmware information from core driver
+ * @cgs_device: opaque device handle
+ * @type: the firmware type
+ * @info: returend firmware information
+ *
+ * Return: 0 on success, -errno otherwise
+ */
+typedef int (*cgs_get_firmware_info)(void *cgs_device,
+                                    enum cgs_ucode_id type,
+                                    struct cgs_firmware_info *info);
+
 
 struct cgs_ops {
        /* memory management calls (similar to KFD interface) */
@@ -478,6 +520,8 @@ struct cgs_ops {
        cgs_pm_request_engine_t pm_request_engine;
        cgs_pm_query_clock_limits_t pm_query_clock_limits;
        cgs_set_camera_voltages_t set_camera_voltages;
+       /* Firmware Info */
+       cgs_get_firmware_info get_firmware_info;
        /* ACPI (TODO) */
 };
 
@@ -559,5 +603,7 @@ struct cgs_device
        CGS_CALL(pm_query_clock_limits,dev,clock,limits)
 #define cgs_set_camera_voltages(dev,mask,voltages)     \
        CGS_CALL(set_camera_voltages,dev,mask,voltages)
+#define cgs_get_firmware_info(dev, type, info) \
+       CGS_CALL(get_firmware_info, dev, type, info)
 
 #endif /* _CGS_COMMON_H */