drm/amd/display: Add Freesync HDMI support to DMCU
authorStylon Wang <stylon.wang@amd.com>
Fri, 4 Dec 2020 04:08:31 +0000 (12:08 +0800)
committerAlex Deucher <alexander.deucher@amd.com>
Tue, 2 Feb 2021 17:11:41 +0000 (12:11 -0500)
[Why]
Adding support for Freesync HDMI to DC and DMCU

[How]
Create DC interface and implementation on top of DMCU to support
parsing CEA blocks in DMCU.

Signed-off-by: Stylon Wang <stylon.wang@amd.com>
Reviewed-by: Hersen Wu <hersenxs.wu@amd.com>
Acked-by: Anson Jacob <Anson.Jacob@amd.com>
Tested-by: Daniel Wheeler <daniel.wheeler@amd.com>
Signed-off-by: Alex Deucher <alexander.deucher@amd.com>
drivers/gpu/drm/amd/display/dc/Makefile
drivers/gpu/drm/amd/display/dc/dc_edid_parser.c [new file with mode: 0644]
drivers/gpu/drm/amd/display/dc/dc_edid_parser.h [new file with mode: 0644]
drivers/gpu/drm/amd/display/dc/dce/dce_dmcu.c
drivers/gpu/drm/amd/display/dc/dce/dce_dmcu.h
drivers/gpu/drm/amd/display/dc/inc/hw/dmcu.h

index bf8fe04..5bf2f23 100644 (file)
@@ -69,5 +69,7 @@ AMD_DISPLAY_FILES += $(AMD_DISPLAY_CORE)
 AMD_DISPLAY_FILES += $(AMD_DM_REG_UPDATE)
 
 DC_DMUB += dc_dmub_srv.o
+DC_EDID += dc_edid_parser.o
 AMD_DISPLAY_DMUB = $(addprefix $(AMDDALPATH)/dc/,$(DC_DMUB))
-AMD_DISPLAY_FILES += $(AMD_DISPLAY_DMUB)
+AMD_DISPLAY_EDID = $(addprefix $(AMDDALPATH)/dc/,$(DC_EDID))
+AMD_DISPLAY_FILES += $(AMD_DISPLAY_DMUB) $(AMD_DISPLAY_EDID)
diff --git a/drivers/gpu/drm/amd/display/dc/dc_edid_parser.c b/drivers/gpu/drm/amd/display/dc/dc_edid_parser.c
new file mode 100644 (file)
index 0000000..0db5b49
--- /dev/null
@@ -0,0 +1,80 @@
+/*
+ * Copyright 2021 Advanced Micro Devices, Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Authors: AMD
+ *
+ */
+
+#include "dce/dce_dmcu.h"
+#include "dc_edid_parser.h"
+
+bool dc_edid_parser_send_cea(struct dc *dc,
+               int offset,
+               int total_length,
+               uint8_t *data,
+               int length)
+{
+       struct dmcu *dmcu = dc->res_pool->dmcu;
+
+       if (dmcu &&
+           dmcu->funcs->is_dmcu_initialized(dmcu) &&
+           dmcu->funcs->send_edid_cea) {
+               return dmcu->funcs->send_edid_cea(dmcu,
+                               offset,
+                               total_length,
+                               data,
+                               length);
+       }
+
+       return false;
+}
+
+bool dc_edid_parser_recv_cea_ack(struct dc *dc, int *offset)
+{
+       struct dmcu *dmcu = dc->res_pool->dmcu;
+
+       if (dmcu &&
+           dmcu->funcs->is_dmcu_initialized(dmcu) &&
+           dmcu->funcs->recv_edid_cea_ack) {
+               return dmcu->funcs->recv_edid_cea_ack(dmcu, offset);
+       }
+
+       return false;
+}
+
+bool dc_edid_parser_recv_amd_vsdb(struct dc *dc,
+               int *version,
+               int *min_frame_rate,
+               int *max_frame_rate)
+{
+       struct dmcu *dmcu = dc->res_pool->dmcu;
+
+       if (dmcu &&
+           dmcu->funcs->is_dmcu_initialized(dmcu) &&
+           dmcu->funcs->recv_amd_vsdb) {
+               return dmcu->funcs->recv_amd_vsdb(dmcu,
+                               version,
+                               min_frame_rate,
+                               max_frame_rate);
+       }
+
+       return false;
+}
diff --git a/drivers/gpu/drm/amd/display/dc/dc_edid_parser.h b/drivers/gpu/drm/amd/display/dc/dc_edid_parser.h
new file mode 100644 (file)
index 0000000..da67ec0
--- /dev/null
@@ -0,0 +1,44 @@
+/*
+ * Copyright 2021 Advanced Micro Devices, Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Authors: AMD
+ *
+ */
+
+#ifndef _DC_EDID_PARSER_H_
+#define _DC_EDID_PARSER_H_
+
+#include "core_types.h"
+
+bool dc_edid_parser_send_cea(struct dc *dc,
+               int offset,
+               int total_length,
+               uint8_t *data,
+               int length);
+
+bool dc_edid_parser_recv_cea_ack(struct dc *dc, int *offset);
+
+bool dc_edid_parser_recv_amd_vsdb(struct dc *dc,
+               int *version,
+               int *min_frame_rate,
+               int *max_frame_rate);
+
+#endif /* _DC_EDID_PARSER_H_ */
index 30264fc..ddc789d 100644 (file)
@@ -57,6 +57,9 @@
 #define MCP_SYNC_PHY_LOCK 0x90
 #define MCP_SYNC_PHY_UNLOCK 0x91
 #define MCP_BL_SET_PWM_FRAC 0x6A  /* Enable or disable Fractional PWM */
+#define MCP_SEND_EDID_CEA 0xA0
+#define EDID_CEA_CMD_ACK 1
+#define EDID_CEA_CMD_NACK 2
 #define MASTER_COMM_CNTL_REG__MASTER_COMM_INTERRUPT_MASK   0x00000001L
 
 // PSP FW version
@@ -811,6 +814,120 @@ static bool dcn20_unlock_phy(struct dmcu *dmcu)
        return true;
 }
 
+static bool dcn10_send_edid_cea(struct dmcu *dmcu,
+               int offset,
+               int total_length,
+               uint8_t *data,
+               int length)
+{
+       struct dce_dmcu *dmcu_dce = TO_DCE_DMCU(dmcu);
+       uint32_t header, data1, data2;
+
+       /* If microcontroller is not running, do nothing */
+       if (dmcu->dmcu_state != DMCU_RUNNING)
+               return false;
+
+       if (length > 8 || length <= 0)
+               return false;
+
+       header = ((uint32_t)offset & 0xFFFF) << 16 | (total_length & 0xFFFF);
+       data1 = (((uint32_t)data[0]) << 24) | (((uint32_t)data[1]) << 16) |
+               (((uint32_t)data[2]) << 8) | ((uint32_t)data[3]);
+       data2 = (((uint32_t)data[4]) << 24) | (((uint32_t)data[5]) << 16) |
+               (((uint32_t)data[6]) << 8) | ((uint32_t)data[7]);
+
+       /* waitDMCUReadyForCmd */
+       REG_WAIT(MASTER_COMM_CNTL_REG, MASTER_COMM_INTERRUPT, 0, 1, 10000);
+
+       /* setDMCUParam_Cmd */
+       REG_UPDATE(MASTER_COMM_CMD_REG, MASTER_COMM_CMD_REG_BYTE0, MCP_SEND_EDID_CEA);
+
+       REG_WRITE(MASTER_COMM_DATA_REG1, header);
+       REG_WRITE(MASTER_COMM_DATA_REG2, data1);
+       REG_WRITE(MASTER_COMM_DATA_REG3, data2);
+
+       /* notifyDMCUMsg */
+       REG_UPDATE(MASTER_COMM_CNTL_REG, MASTER_COMM_INTERRUPT, 1);
+
+       /* waitDMCUReadyForCmd */
+       REG_WAIT(MASTER_COMM_CNTL_REG, MASTER_COMM_INTERRUPT, 0, 1, 10000);
+
+       return true;
+}
+
+static bool dcn10_get_scp_results(struct dmcu *dmcu,
+               uint32_t *cmd,
+               uint32_t *data1,
+               uint32_t *data2,
+               uint32_t *data3)
+{
+       struct dce_dmcu *dmcu_dce = TO_DCE_DMCU(dmcu);
+
+       /* If microcontroller is not running, do nothing */
+       if (dmcu->dmcu_state != DMCU_RUNNING)
+               return false;
+
+       *cmd = REG_READ(SLAVE_COMM_CMD_REG);
+       *data1 =  REG_READ(SLAVE_COMM_DATA_REG1);
+       *data2 =  REG_READ(SLAVE_COMM_DATA_REG2);
+       *data3 =  REG_READ(SLAVE_COMM_DATA_REG3);
+
+       /* clear SCP interrupt */
+       REG_UPDATE(SLAVE_COMM_CNTL_REG, SLAVE_COMM_INTERRUPT, 0);
+
+       return true;
+}
+
+static bool dcn10_recv_amd_vsdb(struct dmcu *dmcu,
+               int *version,
+               int *min_frame_rate,
+               int *max_frame_rate)
+{
+       uint32_t data[4];
+       int cmd, ack, len;
+
+       if (!dcn10_get_scp_results(dmcu, &data[0], &data[1], &data[2], &data[3]))
+               return false;
+
+       cmd = data[0] & 0x3FF;
+       len = (data[0] >> 10) & 0x3F;
+       ack = data[1];
+
+       if (cmd != MCP_SEND_EDID_CEA || ack != EDID_CEA_CMD_ACK || len != 12)
+               return false;
+
+       if ((data[2] & 0xFF)) {
+               *version = (data[2] >> 8) & 0xFF;
+               *min_frame_rate = (data[3] >> 16) & 0xFFFF;
+               *max_frame_rate = data[3] & 0xFFFF;
+               return true;
+       }
+
+       return false;
+}
+
+static bool dcn10_recv_edid_cea_ack(struct dmcu *dmcu, int *offset)
+{
+       uint32_t data[4];
+       int cmd, ack;
+
+       if (!dcn10_get_scp_results(dmcu,
+                               &data[0], &data[1], &data[2], &data[3]))
+               return false;
+
+       cmd = data[0] & 0x3FF;
+       ack = data[1];
+
+       if (cmd != MCP_SEND_EDID_CEA)
+               return false;
+
+       if (ack == EDID_CEA_CMD_ACK)
+               return true;
+
+       *offset = data[2]; /* nack */
+       return false;
+}
+
 #endif //(CONFIG_DRM_AMD_DC_DCN)
 
 static const struct dmcu_funcs dce_funcs = {
@@ -833,6 +950,9 @@ static const struct dmcu_funcs dcn10_funcs = {
        .get_psr_state = dcn10_get_dmcu_psr_state,
        .set_psr_wait_loop = dcn10_psr_wait_loop,
        .get_psr_wait_loop = dcn10_get_psr_wait_loop,
+       .send_edid_cea = dcn10_send_edid_cea,
+       .recv_amd_vsdb = dcn10_recv_amd_vsdb,
+       .recv_edid_cea_ack = dcn10_recv_edid_cea_ack,
        .is_dmcu_initialized = dcn10_is_dmcu_initialized
 };
 
index cefb7f5..ff726b3 100644 (file)
        SR(MASTER_COMM_DATA_REG3), \
        SR(MASTER_COMM_CMD_REG), \
        SR(MASTER_COMM_CNTL_REG), \
+       SR(SLAVE_COMM_DATA_REG1), \
+       SR(SLAVE_COMM_DATA_REG2), \
+       SR(SLAVE_COMM_DATA_REG3), \
+       SR(SLAVE_COMM_CMD_REG), \
        SR(DMCU_IRAM_RD_CTRL), \
        SR(DMCU_IRAM_RD_DATA), \
        SR(DMCU_INTERRUPT_TO_UC_EN_MASK), \
        DMCU_SF(MASTER_COMM_CMD_REG, \
                        MASTER_COMM_CMD_REG_BYTE0, mask_sh), \
        DMCU_SF(MASTER_COMM_CNTL_REG, MASTER_COMM_INTERRUPT, mask_sh), \
+       DMCU_SF(SLAVE_COMM_CNTL_REG, SLAVE_COMM_INTERRUPT, mask_sh), \
        DMCU_SF(DMCU_INTERRUPT_TO_UC_EN_MASK, \
                        STATIC_SCREEN1_INT_TO_UC_EN, mask_sh), \
        DMCU_SF(DMCU_INTERRUPT_TO_UC_EN_MASK, \
        type UC_IN_RESET; \
        type MASTER_COMM_CMD_REG_BYTE0; \
        type MASTER_COMM_INTERRUPT; \
+       type SLAVE_COMM_INTERRUPT; \
        type DPHY_RX_FAST_TRAINING_CAPABLE; \
        type DPHY_LOAD_BS_COUNT; \
        type STATIC_SCREEN1_INT_TO_UC_EN; \
@@ -211,6 +217,11 @@ struct dce_dmcu_registers {
        uint32_t MASTER_COMM_DATA_REG3;
        uint32_t MASTER_COMM_CMD_REG;
        uint32_t MASTER_COMM_CNTL_REG;
+       uint32_t SLAVE_COMM_DATA_REG1;
+       uint32_t SLAVE_COMM_DATA_REG2;
+       uint32_t SLAVE_COMM_DATA_REG3;
+       uint32_t SLAVE_COMM_CMD_REG;
+       uint32_t SLAVE_COMM_CNTL_REG;
        uint32_t DMCU_IRAM_RD_CTRL;
        uint32_t DMCU_IRAM_RD_DATA;
        uint32_t DMCU_INTERRUPT_TO_UC_EN_MASK;
index 69d9fbf..cd1c0dc 100644 (file)
@@ -74,6 +74,16 @@ struct dmcu_funcs {
        bool (*is_dmcu_initialized)(struct dmcu *dmcu);
        bool (*lock_phy)(struct dmcu *dmcu);
        bool (*unlock_phy)(struct dmcu *dmcu);
+       bool (*send_edid_cea)(struct dmcu *dmcu,
+                       int offset,
+                       int total_length,
+                       uint8_t *data,
+                       int length);
+       bool (*recv_amd_vsdb)(struct dmcu *dmcu,
+                       int *version,
+                       int *min_frame_rate,
+                       int *max_frame_rate);
+       bool (*recv_edid_cea_ack)(struct dmcu *dmcu, int *offset);
 };
 
 #endif