[Why]
DMCU (Display MicroController Unit) is an on-GPU microcontroller
in AMD graphics cards that is used in features for
embedded displays such as Panel Self-Refresh

DMCU is part of the DM IP block

[How]
DMCU is added as an option in the enum AMDGPU_UCODE_ID

DMCU needs two pieces of firmware - the initial eram and the
interrupt vectors.  These are treated as seperate pieces of
firmware and loaded by PSP

The loading occurs in the sw_init hook of DM

If the firmware is not found, the sw_init hook returns without error.
DMCU is not a requirement for DM to run.

v2: Move dmcu firmware loading into its own function, properly
release firmware, add debug messages

v3: Use one binary file which contains both firmware pieces

Signed-off-by: David Francis <[email protected]>
---
 drivers/gpu/drm/amd/amdgpu/amdgpu_ucode.c     | 21 ++++-
 drivers/gpu/drm/amd/amdgpu/amdgpu_ucode.h     | 10 ++
 drivers/gpu/drm/amd/amdgpu/psp_v10_0.c        |  6 ++
 .../gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c | 94 ++++++++++++++++++-
 .../gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.h |  2 +
 5 files changed, 130 insertions(+), 3 deletions(-)

diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_ucode.c 
b/drivers/gpu/drm/amd/amdgpu/amdgpu_ucode.c
index a942fd28dae8..3b1af1cecf14 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_ucode.c
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_ucode.c
@@ -322,6 +322,7 @@ static int amdgpu_ucode_init_single_fw(struct amdgpu_device 
*adev,
 {
        const struct common_firmware_header *header = NULL;
        const struct gfx_firmware_header_v1_0 *cp_hdr = NULL;
+       const struct dmcu_firmware_header_v1_0 *dmcu_hdr = NULL;
 
        if (NULL == ucode->fw)
                return 0;
@@ -333,8 +334,8 @@ static int amdgpu_ucode_init_single_fw(struct amdgpu_device 
*adev,
                return 0;
 
        header = (const struct common_firmware_header *)ucode->fw->data;
-
        cp_hdr = (const struct gfx_firmware_header_v1_0 *)ucode->fw->data;
+       dmcu_hdr = (const struct dmcu_firmware_header_v1_0 *)ucode->fw->data;
 
        if (adev->firmware.load_type != AMDGPU_FW_LOAD_PSP ||
            (ucode->ucode_id != AMDGPU_UCODE_ID_CP_MEC1 &&
@@ -343,7 +344,9 @@ static int amdgpu_ucode_init_single_fw(struct amdgpu_device 
*adev,
             ucode->ucode_id != AMDGPU_UCODE_ID_CP_MEC2_JT &&
             ucode->ucode_id != AMDGPU_UCODE_ID_RLC_RESTORE_LIST_CNTL &&
             ucode->ucode_id != AMDGPU_UCODE_ID_RLC_RESTORE_LIST_GPM_MEM &&
-            ucode->ucode_id != AMDGPU_UCODE_ID_RLC_RESTORE_LIST_SRM_MEM)) {
+            ucode->ucode_id != AMDGPU_UCODE_ID_RLC_RESTORE_LIST_SRM_MEM &&
+                ucode->ucode_id != AMDGPU_UCODE_ID_DMCU_ERAM &&
+                ucode->ucode_id != AMDGPU_UCODE_ID_DMCU_INTV)) {
                ucode->ucode_size = le32_to_cpu(header->ucode_size_bytes);
 
                memcpy(ucode->kaddr, (void *)((uint8_t *)ucode->fw->data +
@@ -365,6 +368,20 @@ static int amdgpu_ucode_init_single_fw(struct 
amdgpu_device *adev,
                                              
le32_to_cpu(header->ucode_array_offset_bytes) +
                                              le32_to_cpu(cp_hdr->jt_offset) * 
4),
                       ucode->ucode_size);
+       } else if (ucode->ucode_id == AMDGPU_UCODE_ID_DMCU_ERAM) {
+               ucode->ucode_size = le32_to_cpu(header->ucode_size_bytes) -
+                               le32_to_cpu(dmcu_hdr->intv_size) * 4;
+
+               memcpy(ucode->kaddr, (void *)((uint8_t *)ucode->fw->data +
+                                             
le32_to_cpu(header->ucode_array_offset_bytes)),
+                      ucode->ucode_size);
+       } else if (ucode->ucode_id == AMDGPU_UCODE_ID_DMCU_INTV) {
+               ucode->ucode_size = le32_to_cpu(dmcu_hdr->intv_size) * 4;
+
+               memcpy(ucode->kaddr, (void *)((uint8_t *)ucode->fw->data +
+                                             
le32_to_cpu(header->ucode_array_offset_bytes) +
+                                             
le32_to_cpu(dmcu_hdr->intv_offset) * 4),
+                      ucode->ucode_size);
        } else if (ucode->ucode_id == AMDGPU_UCODE_ID_RLC_RESTORE_LIST_CNTL) {
                ucode->ucode_size = 
adev->gfx.rlc.save_restore_list_cntl_size_bytes;
                memcpy(ucode->kaddr, adev->gfx.rlc.save_restore_list_cntl,
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_ucode.h 
b/drivers/gpu/drm/amd/amdgpu/amdgpu_ucode.h
index b358e7519987..13bd540709b6 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_ucode.h
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_ucode.h
@@ -157,6 +157,13 @@ struct gpu_info_firmware_header_v1_0 {
        uint16_t version_minor; /* version */
 };
 
+/* version_major=1, version_minor=0 */
+struct dmcu_firmware_header_v1_0 {
+       struct common_firmware_header header;
+       uint32_t intv_offset; /* interrupt vectors offset from end of header, 
in words */
+       uint32_t intv_size;  /* size of interrupt vectors, in words */
+};
+
 /* header is fixed size */
 union amdgpu_firmware_header {
        struct common_firmware_header common;
@@ -170,6 +177,7 @@ union amdgpu_firmware_header {
        struct sdma_firmware_header_v1_0 sdma;
        struct sdma_firmware_header_v1_1 sdma_v1_1;
        struct gpu_info_firmware_header_v1_0 gpu_info;
+       struct dmcu_firmware_header_v1_0 dmcu;
        uint8_t raw[0x100];
 };
 
@@ -196,6 +204,8 @@ enum AMDGPU_UCODE_ID {
        AMDGPU_UCODE_ID_UVD1,
        AMDGPU_UCODE_ID_VCE,
        AMDGPU_UCODE_ID_VCN,
+       AMDGPU_UCODE_ID_DMCU_ERAM,
+       AMDGPU_UCODE_ID_DMCU_INTV,
        AMDGPU_UCODE_ID_MAXIMUM,
 };
 
diff --git a/drivers/gpu/drm/amd/amdgpu/psp_v10_0.c 
b/drivers/gpu/drm/amd/amdgpu/psp_v10_0.c
index 02be34e72ed9..240dc8c85867 100644
--- a/drivers/gpu/drm/amd/amdgpu/psp_v10_0.c
+++ b/drivers/gpu/drm/amd/amdgpu/psp_v10_0.c
@@ -91,6 +91,12 @@ psp_v10_0_get_fw_type(struct amdgpu_firmware_info *ucode, 
enum psp_gfx_fw_type *
        case AMDGPU_UCODE_ID_VCN:
                *type = GFX_FW_TYPE_VCN;
                break;
+       case AMDGPU_UCODE_ID_DMCU_ERAM:
+               *type = GFX_FW_TYPE_DMCU_ERAM;
+               break;
+       case AMDGPU_UCODE_ID_DMCU_INTV:
+               *type = GFX_FW_TYPE_DMCU_ISR;
+               break;
        case AMDGPU_UCODE_ID_MAXIMUM:
        default:
                return -EINVAL;
diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c 
b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c
index 5103eba75cb3..35d25b92c4d3 100644
--- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c
+++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c
@@ -30,6 +30,7 @@
 #include "vid.h"
 #include "amdgpu.h"
 #include "amdgpu_display.h"
+#include "amdgpu_ucode.h"
 #include "atom.h"
 #include "amdgpu_dm.h"
 #include "amdgpu_pm.h"
@@ -50,6 +51,7 @@
 #include <linux/version.h>
 #include <linux/types.h>
 #include <linux/pm_runtime.h>
+#include <linux/firmware.h>
 
 #include <drm/drmP.h>
 #include <drm/drm_atomic.h>
@@ -71,6 +73,9 @@
 
 #include "modules/inc/mod_freesync.h"
 
+#define FIRMWARE_RAVEN_DMCU            "amdgpu/raven_dmcu.bin"
+MODULE_FIRMWARE(FIRMWARE_RAVEN_DMCU);
+
 /* basic init/fini API */
 static int amdgpu_dm_init(struct amdgpu_device *adev);
 static void amdgpu_dm_fini(struct amdgpu_device *adev);
@@ -514,13 +519,100 @@ static void amdgpu_dm_fini(struct amdgpu_device *adev)
        return;
 }
 
-static int dm_sw_init(void *handle)
+static int load_dmcu_fw(struct amdgpu_device *adev)
 {
+       const char *fw_name_dmcu;
+       int r;
+       const struct dmcu_firmware_header_v1_0 *hdr;
+
+       switch(adev->asic_type) {
+       case CHIP_BONAIRE:
+       case CHIP_HAWAII:
+       case CHIP_KAVERI:
+       case CHIP_KABINI:
+       case CHIP_MULLINS:
+       case CHIP_TONGA:
+       case CHIP_FIJI:
+       case CHIP_CARRIZO:
+       case CHIP_STONEY:
+       case CHIP_POLARIS11:
+       case CHIP_POLARIS10:
+       case CHIP_POLARIS12:
+       case CHIP_VEGAM:
+       case CHIP_VEGA10:
+       case CHIP_VEGA12:
+       case CHIP_VEGA20:
+               return 0;
+       case CHIP_RAVEN:
+               fw_name_dmcu = FIRMWARE_RAVEN_DMCU;
+               break;
+       default:
+               DRM_ERROR("Unsupported ASIC type: 0x%X\n", adev->asic_type);
+               return -1;
+       }
+
+       if (adev->firmware.load_type != AMDGPU_FW_LOAD_PSP)
+       {
+               DRM_DEBUG_KMS("dm: DMCU firmware not supported on direct or SMU 
loading\n");
+               return 0;
+       }
+
+       r = request_firmware_direct(&adev->dm.fw_dmcu, fw_name_dmcu, adev->dev);
+       if (r == -ENOENT)
+       {
+               /* DMCU firmware is not necessary, so don't raise a fuss if 
it's missing */
+               DRM_DEBUG_KMS("dm: DMCU firmware not found\n");
+               adev->dm.fw_dmcu = NULL;
+               return 0;
+       }
+       if (r) {
+               dev_err(adev->dev, "amdgpu_dm: Can't load firmware \"%s\"\n",
+                       fw_name_dmcu);
+               return r;
+       }
+
+       r = amdgpu_ucode_validate(adev->dm.fw_dmcu);
+       if (r) {
+               dev_err(adev->dev, "amdgpu_dm: Can't validate firmware 
\"%s\"\n",
+                       fw_name_dmcu);
+               release_firmware(adev->dm.fw_dmcu);
+               adev->dm.fw_dmcu = NULL;
+               return r;
+       }
+
+       hdr = (const struct dmcu_firmware_header_v1_0 *)adev->dm.fw_dmcu->data;
+       adev->firmware.ucode[AMDGPU_UCODE_ID_DMCU_ERAM].ucode_id = 
AMDGPU_UCODE_ID_DMCU_ERAM;
+       adev->firmware.ucode[AMDGPU_UCODE_ID_DMCU_ERAM].fw = adev->dm.fw_dmcu;
+       adev->firmware.fw_size +=
+               ALIGN(le32_to_cpu(hdr->header.ucode_size_bytes) - 
le32_to_cpu(hdr->intv_size) * 4, PAGE_SIZE);
+
+       adev->firmware.ucode[AMDGPU_UCODE_ID_DMCU_INTV].ucode_id = 
AMDGPU_UCODE_ID_DMCU_INTV;
+       adev->firmware.ucode[AMDGPU_UCODE_ID_DMCU_INTV].fw = adev->dm.fw_dmcu;
+       adev->firmware.fw_size +=
+               ALIGN(le32_to_cpu(hdr->intv_size) * 4, PAGE_SIZE);
+
+       DRM_DEBUG_KMS("PSP loading DMCU firmware\n");
+
        return 0;
 }
 
+static int dm_sw_init(void *handle)
+{
+       struct amdgpu_device *adev = (struct amdgpu_device *)handle;
+
+       return load_dmcu_fw(adev);
+}
+
 static int dm_sw_fini(void *handle)
 {
+       struct amdgpu_device *adev = (struct amdgpu_device *)handle;
+
+       if(adev->dm.fw_dmcu)
+       {
+               release_firmware(adev->dm.fw_dmcu);
+               adev->dm.fw_dmcu = NULL;
+       }
+
        return 0;
 }
 
diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.h 
b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.h
index c159584c04f7..9a57c654943a 100644
--- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.h
+++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.h
@@ -129,6 +129,8 @@ struct amdgpu_display_manager {
        struct drm_atomic_state *cached_state;
 
        struct dm_comressor_info compressor;
+
+       const struct firmware *fw_dmcu;
 };
 
 struct amdgpu_dm_connector {
-- 
2.17.1

_______________________________________________
amd-gfx mailing list
[email protected]
https://lists.freedesktop.org/mailman/listinfo/amd-gfx

Reply via email to