From e3a793c89b11cc364dd1745894a291fd5df54d61 Mon Sep 17 00:00:00 2001 From: Molly Sophia Date: Tue, 21 Nov 2023 11:08:51 +0000 Subject: [PATCH] DwHdmiQp: Add initial support for DwHdmi I2CM ...and dump the EDID Signed-off-by: Molly Sophia --- .../Rockchip/Include/Library/DwHdmiQpLib.h | 32 ++ .../Protocol/RockchipConnectorProtocol.h | 4 +- .../Rockchip/Library/DisplayLib/DwHdmiQpLib.c | 380 +++++++++++++++++- .../Library/DisplayLib/DwHdmiQpLib.inf | 1 + .../Rockchip/RK3588/RK3588Base.dsc.inc | 1 + .../Silicon/Rockchip/RockchipPkg.dec | 1 + 6 files changed, 416 insertions(+), 3 deletions(-) diff --git a/edk2-rockchip/Silicon/Rockchip/Include/Library/DwHdmiQpLib.h b/edk2-rockchip/Silicon/Rockchip/Include/Library/DwHdmiQpLib.h index 30e969acd..b74b65983 100644 --- a/edk2-rockchip/Silicon/Rockchip/Include/Library/DwHdmiQpLib.h +++ b/edk2-rockchip/Silicon/Rockchip/Include/Library/DwHdmiQpLib.h @@ -914,11 +914,43 @@ #define PMU1CRU_SOFTRST_CON03 0xA0C #define PMU1CRU_SOFTRST_CON04 0xA10 +struct DwHdmiQpI2c { + BOOLEAN Cmp; + UINT32 Stat; + UINT32 PinMux; + + UINT8 SlaveReg; + BOOLEAN IsSegment; + BOOLEAN IsRegAddr; +}; + struct DwHdmiQpDevice { UINT32 Id; BOOLEAN ForceHpd; + struct DwHdmiQpI2c I2c; + UINT32 ScdcIntr; + UINT32 FltIntr; + UINT32 EarcIntr; + + BOOLEAN FltCmp; }; +struct i2c_msg { + UINT16 addr; + UINT16 flags; +#define I2C_M_RD 0x0001 /* guaranteed to be 0x0001! */ +#define I2C_M_TEN 0x0010 /* use only if I2C_FUNC_10BIT_ADDR */ +#define I2C_M_DMA_SAFE 0x0200 /* use only in kernel space */ +#define I2C_M_RECV_LEN 0x0400 /* use only if I2C_FUNC_SMBUS_READ_BLOCK_DATA */ +#define I2C_M_NO_RD_ACK 0x0800 /* use only if I2C_FUNC_PROTOCOL_MANGLING */ +#define I2C_M_IGNORE_NAK 0x1000 /* use only if I2C_FUNC_PROTOCOL_MANGLING */ +#define I2C_M_REV_DIR_ADDR 0x2000 /* use only if I2C_FUNC_PROTOCOL_MANGLING */ +#define I2C_M_NOSTART 0x4000 /* use only if I2C_FUNC_NOSTART */ +#define I2C_M_STOP 0x8000 /* use only if I2C_FUNC_PROTOCOL_MANGLING */ + UINT16 len; + UINT8 *buf; +}; + /* Rockchip Htx Phy */ struct RockchipHdptxPhyHdmi { diff --git a/edk2-rockchip/Silicon/Rockchip/Include/Protocol/RockchipConnectorProtocol.h b/edk2-rockchip/Silicon/Rockchip/Include/Protocol/RockchipConnectorProtocol.h index 2ec8eb25b..6c5bd5f79 100644 --- a/edk2-rockchip/Silicon/Rockchip/Include/Protocol/RockchipConnectorProtocol.h +++ b/edk2-rockchip/Silicon/Rockchip/Include/Protocol/RockchipConnectorProtocol.h @@ -55,7 +55,7 @@ EFI_STATUS typedef EFI_STATUS -(EFIAPI *ROCKCHIP_CONNECTOR_GET_EDIE) ( +(EFIAPI *ROCKCHIP_CONNECTOR_GET_EDID) ( IN ROCKCHIP_CONNECTOR_PROTOCOL *This, IN OUT DISPLAY_STATE *DisplayState ); @@ -95,7 +95,7 @@ struct _ROCKCHIP_CONNECTOR_PROTOCOL { ROCKCHIP_CONNECTOR_DEINIT Deinit; ROCKCHIP_CONNECTOR_DETECT Detect; ROCKCHIP_CONNECTOR_GET_TIMING GetTiming; - ROCKCHIP_CONNECTOR_GET_EDIE GetEdid; + ROCKCHIP_CONNECTOR_GET_EDID GetEdid; ROCKCHIP_CONNECTOR_PREPARE Prepare; ROCKCHIP_CONNECTOR_ENABLE Enable; ROCKCHIP_CONNECTOR_DISABLE Disable; diff --git a/edk2-rockchip/Silicon/Rockchip/Library/DisplayLib/DwHdmiQpLib.c b/edk2-rockchip/Silicon/Rockchip/Library/DisplayLib/DwHdmiQpLib.c index e96c485e9..f26d428a8 100644 --- a/edk2-rockchip/Silicon/Rockchip/Library/DisplayLib/DwHdmiQpLib.c +++ b/edk2-rockchip/Silicon/Rockchip/Library/DisplayLib/DwHdmiQpLib.c @@ -19,6 +19,7 @@ #include #include #include +#include #include @@ -84,6 +85,14 @@ #define HDMI20_MAX_RATE 600000 #define HDMI_8K60_RATE 2376000 +#define DDC_CI_ADDR 0x37 +#define DDC_SEGMENT_ADDR 0x30 + +#define HDMI_EDID_LEN 512 +#define HDMI_EDID_BLOCK_LEN 128 + +#define DDC_ADDR 0x50 + VOID DwHdmiQpRegWrite ( OUT struct DwHdmiQpDevice *Hdmi, @@ -136,7 +145,8 @@ DwHdmiQpRegMod ( BASE = HDMI1_BASE; Val = MmioRead32(BASE + Offset); - Val |= Value & Mask; + Val &= ~Mask; + Val |= Value; MmioWrite32(BASE + Offset, Val); }; @@ -179,6 +189,369 @@ DwHdmiQpSetIomux( } }; +STATIC +BOOLEAN +DwHdmiI2cPollForIrq( + OUT struct DwHdmiQpDevice *Hdmi +) +{ + struct DwHdmiQpI2c *I2c = &Hdmi->I2c; + UINT32 Stat; + + Stat = DwHdmiQpRegRead(Hdmi, MAINUNIT_1_INT_STATUS); + I2c->Stat = Stat & (I2CM_OP_DONE_IRQ | I2CM_READ_REQUEST_IRQ | + I2CM_NACK_RCVD_IRQ); + Hdmi->ScdcIntr = Stat& (SCDC_UPD_FLAGS_RD_IRQ | + SCDC_UPD_FLAGS_CHG_IRQ | + SCDC_UPD_FLAGS_CLR_IRQ | + SCDC_RR_REPLY_STOP_IRQ | + SCDC_NACK_RCVD_IRQ); + + Hdmi->FltIntr = Stat & (FLT_EXIT_TO_LTSP_IRQ | + FLT_EXIT_TO_LTS4_IRQ | + FLT_EXIT_TO_LTSL_IRQ); + + DEBUG((DEBUG_VERBOSE, "i2c main unit irq:%02x\n", Stat)); + if (I2c->Stat) { + DwHdmiQpRegWrite(Hdmi, I2c->Stat, MAINUNIT_1_INT_CLEAR); + I2c->Cmp = TRUE; + } + + if (Hdmi->FltIntr) { + DEBUG((DEBUG_VERBOSE, "i2c flt irq:%02x\n", Hdmi->FltIntr)); + DwHdmiQpRegWrite(Hdmi, Hdmi->FltIntr, MAINUNIT_1_INT_CLEAR); + Hdmi->FltCmp = TRUE; + } + + if (Hdmi->ScdcIntr) { + UINT8 val; + + DEBUG((DEBUG_VERBOSE, "i2c scdc irq:%02x\n", Hdmi->ScdcIntr)); + DwHdmiQpRegWrite(Hdmi, Hdmi->ScdcIntr, MAINUNIT_1_INT_CLEAR); + val = DwHdmiQpRegRead(Hdmi, SCDC_STATUS0); + + /* frl start */ + if (val & BIT(4)) { + DwHdmiQpRegMod(Hdmi, 0, SCDC_UPD_FLAGS_POLL_EN | + SCDC_UPD_FLAGS_AUTO_CLR, SCDC_CONFIG0); + DwHdmiQpRegMod(Hdmi, 0, SCDC_UPD_FLAGS_RD_IRQ, + MAINUNIT_1_INT_MASK_N); + DEBUG((DEBUG_VERBOSE, "frl start\n")); + } + } + + if (Stat) + return TRUE; + + return FALSE; +} + +STATIC +EFI_STATUS +DwHdmiI2cRead( + IN struct DwHdmiQpDevice *Hdmi, + UINT8 *Buf, + UINTN Length +) +{ + EFI_STATUS Status = EFI_SUCCESS; + struct DwHdmiQpI2c *I2c = &Hdmi->I2c; + + if (!I2c->IsRegAddr) { + DEBUG((DEBUG_INFO, "Set read register address to 0\n")); + I2c->SlaveReg = 0x0; + I2c->IsRegAddr = TRUE; + } + + while (Length--) { + I2c->Cmp = FALSE; + DwHdmiQpRegMod(Hdmi, I2c->SlaveReg++ << 12, I2CM_ADDR, I2CM_INTERFACE_CONTROL0); + if (I2c->IsSegment) + DwHdmiQpRegMod(Hdmi, I2CM_EXT_READ, I2CM_WR_MASK, + I2CM_INTERFACE_CONTROL0); + else + DwHdmiQpRegMod(Hdmi, I2CM_FM_READ, I2CM_WR_MASK, + I2CM_INTERFACE_CONTROL0); + + // Wait for transfer done here + int timeout = 10 * 1000; + int interval = 100; + BOOLEAN ret; + while (timeout) { + ret = DwHdmiI2cPollForIrq(Hdmi); + if (I2c->Cmp) + break; + + timeout -= interval; + MicroSecondDelay(interval); + } + + if (!timeout && !ret) { + DEBUG((DEBUG_ERROR, "HDMI I2C read time out!\n")); + DwHdmiQpRegWrite(Hdmi, 0x01, I2CM_CONTROL0); + Status = EFI_TIMEOUT; + goto exit; + } + + if (I2c->Stat & I2CM_NACK_RCVD_IRQ) { + DEBUG((DEBUG_ERROR, "HDMI I2C read error\n")); + DwHdmiQpRegWrite(Hdmi, 0x01, I2CM_CONTROL0); + Status = EFI_DEVICE_ERROR; + goto exit; + } + + MicroSecondDelay(500); + + *Buf = DwHdmiQpRegRead(Hdmi, I2CM_INTERFACE_RDDATA_0_3) & 0xff; + Buf++; + DEBUG((DEBUG_VERBOSE, "i2c read succeed I2c->Stat = %02x 0x%02x RegAddr=%02x\n", I2c->Stat, + DwHdmiQpRegRead(Hdmi, I2CM_INTERFACE_RDDATA_0_3) & 0xff, + I2c->SlaveReg-1)); + + DwHdmiQpRegMod(Hdmi, 0, I2CM_WR_MASK, I2CM_INTERFACE_CONTROL0); + } + I2c->IsSegment = FALSE; + +exit: + return Status; +} + +STATIC +EFI_STATUS +DwHdmiI2cWrite( + IN struct DwHdmiQpDevice *Hdmi, + UINT8 *Buf, + UINTN Length +) +{ + EFI_STATUS Status = EFI_SUCCESS; + struct DwHdmiQpI2c *I2c = &Hdmi->I2c; + + if (!I2c->IsRegAddr) { + I2c->SlaveReg = Buf[0]; + Length--; + Buf++; + I2c->IsRegAddr = TRUE; + } + + while (Length--) { + I2c->Cmp = FALSE; + DwHdmiQpRegWrite(Hdmi, *Buf, I2CM_INTERFACE_WRDATA_0_3); + Buf++; + DwHdmiQpRegMod(Hdmi, I2c->SlaveReg++ << 12, I2CM_ADDR, I2CM_INTERFACE_CONTROL0); + DwHdmiQpRegMod(Hdmi, I2CM_FM_WRITE, I2CM_WR_MASK, + I2CM_INTERFACE_CONTROL0); + + // Wait for transfer done here + int timeout = 10 * 1000; + int interval = 100; + BOOLEAN ret; + while (timeout) { + ret = DwHdmiI2cPollForIrq(Hdmi); + if (I2c->Cmp) + break; + + timeout -= interval; + MicroSecondDelay(interval); + } + + if (!timeout && !ret) { + DEBUG((DEBUG_ERROR, "HDMI I2C write time out!\n")); + DwHdmiQpRegWrite(Hdmi, 0x01, I2CM_CONTROL0); + Status = EFI_TIMEOUT; + goto exit; + } + + /* Check for error condition on the bus */ + if (I2c->Stat & I2CM_NACK_RCVD_IRQ) { + DEBUG((DEBUG_ERROR, "HDMI I2C write nack!\n")); + DwHdmiQpRegWrite(Hdmi, 0x01, I2CM_CONTROL0); + Status = EFI_DEVICE_ERROR; + goto exit; + } + + DwHdmiQpRegMod(Hdmi, 0, I2CM_WR_MASK, I2CM_INTERFACE_CONTROL0); + } + + DEBUG((DEBUG_VERBOSE, "HDMI I2C write done! I2c->Stat = %02x\n", I2c->Stat)); + +exit: + return Status; +} + +STATIC +EFI_STATUS +DwHdmiQpI2cXfer( + IN struct DwHdmiQpDevice *Hdmi, + IN struct i2c_msg *Msgs, + IN INTN Num +) +{ + EFI_STATUS Status = EFI_SUCCESS; + struct DwHdmiQpI2c *I2c = &Hdmi->I2c; + UINT8 Addr = Msgs[0].addr; + + if (Addr == DDC_CI_ADDR) + /* + * The internal I2C controller does not support the multi-byte + * read and write operations needed for DDC/CI. + * TOFIX: Blacklist the DDC/CI address until we filter out + * unsupported I2C operations. + */ + return EFI_UNSUPPORTED; + + DEBUG((DEBUG_VERBOSE, "HDMI I2C xfer: Num: %d, Addr: %02x\n", + Num, Addr)); + + for (int i = 0; i < Num; i++) { + if (Msgs[i].len == 0) { + DEBUG((DEBUG_ERROR, "Unsupported transfer %d/%d, no data\n", + i + 1, Num)); + return EFI_UNSUPPORTED; + } + } + + /* Unmute DONE and ERROR interrupts */ + DwHdmiQpRegMod(Hdmi, I2CM_NACK_RCVD_MASK_N | I2CM_OP_DONE_MASK_N, + I2CM_NACK_RCVD_MASK_N | I2CM_OP_DONE_MASK_N, + MAINUNIT_1_INT_MASK_N); + + if (Addr == DDC_SEGMENT_ADDR && Msgs[0].len == 1) + Addr = DDC_ADDR; + + DwHdmiQpRegMod(Hdmi, Addr << 5, I2CM_SLVADDR, I2CM_INTERFACE_CONTROL0); + + /* Set slave device register address on transfer */ + I2c->IsRegAddr = FALSE; + + /* Set segment pointer for I2C extended read mode operation */ + I2c->IsSegment = FALSE; + + for (int i = 0; i < Num; i++) { + DEBUG((DEBUG_VERBOSE, "xfer: num: %d/%d, len: %d, flags: %x\n", + i + 1, Num, Msgs[i].len, Msgs[i].flags)); + + if (Msgs[i].addr == DDC_SEGMENT_ADDR && Msgs[i].len == 1) { + I2c->IsSegment = TRUE; + DwHdmiQpRegMod(Hdmi, DDC_SEGMENT_ADDR, I2CM_SEG_ADDR, + I2CM_INTERFACE_CONTROL1); + DwHdmiQpRegMod(Hdmi, *Msgs[i].buf << 7, I2CM_SEG_PTR, + I2CM_INTERFACE_CONTROL1); + } else { + if (Msgs[i].flags & I2C_M_RD) + Status = DwHdmiI2cRead(Hdmi, Msgs[i].buf, Msgs[i].len); + else + Status = DwHdmiI2cWrite(Hdmi, Msgs[i].buf, Msgs[i].len); + } + + if (Status) + break; + } + + if (Status) + return Status; + + /* Mute DONE and ERROR interrupts */ + DwHdmiQpRegMod(Hdmi, 0, I2CM_OP_DONE_MASK_N | I2CM_NACK_RCVD_MASK_N, + MAINUNIT_1_INT_MASK_N); + + return Status; +} + +STATIC +VOID +DwHdmiI2cInit( + OUT struct DwHdmiQpDevice *Hdmi +) +{ + UINT32 BaseAddr; + if (Hdmi->Id) + BaseAddr = HDMI1_BASE; + else + BaseAddr = HDMI0_BASE; + + /* Software reset */ + DwHdmiQpRegWrite(Hdmi, 0x01, I2CM_CONTROL0); + + DwHdmiQpRegMod(Hdmi, 0, I2CM_FM_EN, I2CM_INTERFACE_CONTROL0); + + /* Clear DONE and ERROR interrupts */ + DwHdmiQpRegWrite(Hdmi, I2CM_OP_DONE_CLEAR | I2CM_NACK_RCVD_CLEAR, + MAINUNIT_1_INT_CLEAR); +} + +STATIC +VOID +DumpEdid(IN struct DwHdmiQpDevice *Hdmi) +{ + DEBUG((DEBUG_INIT, "DwHdmiQpLib.c: Dumping EDID: \n")); + UINT8 EDID[EDID_SIZE]; + UINT8 BaseAddr = 0x0; + struct i2c_msg msgs[] = { + { + .addr = DDC_ADDR, + .flags = 0, + .len = 1, + .buf = &BaseAddr, + }, { + .addr = DDC_ADDR, + .flags = I2C_M_RD, + .len = EDID_SIZE, + .buf = EDID, + } + }; + if(DwHdmiQpI2cXfer(Hdmi, msgs, 2)) { + return; + } + for (int i = 0; i < EDID_SIZE; i++) { + DEBUG((DEBUG_INIT, "%02x ", EDID[i])); + if (!((i + 1) % 8)) + DEBUG((DEBUG_INIT, "\n")); + } +} + +VOID +DwHdmiQpI2cSetIomux( + OUT struct DwHdmiQpDevice *Hdmi + ) +{ + if (!Hdmi->Id) { + switch (Hdmi->I2c.PinMux) { + case 0: + GpioPinSetFunction(4, GPIO_PIN_PB7, 0x5); + GpioPinSetFunction(4, GPIO_PIN_PC0, 0x5); + break; + case 1: + GpioPinSetFunction(0, GPIO_PIN_PD5, 0xb); + GpioPinSetFunction(0, GPIO_PIN_PD4, 0xb); + break; + case 2: + GpioPinSetFunction(3, GPIO_PIN_PC7, 0x5); + GpioPinSetFunction(3, GPIO_PIN_PD0, 0x5); + break; + default: + break; + } + } else { + switch (Hdmi->I2c.PinMux) { + case 0: + GpioPinSetFunction(2, GPIO_PIN_PB4, 0x4); + GpioPinSetFunction(2, GPIO_PIN_PB5, 0x4); + break; + case 1: + GpioPinSetFunction(3, GPIO_PIN_PC5, 0x5); + GpioPinSetFunction(3, GPIO_PIN_PC6, 0x5); + break; + case 2: + GpioPinSetFunction(1, GPIO_PIN_PA3, 0x5); + GpioPinSetFunction(1, GPIO_PIN_PA4, 0x5); + break; + default: + break; + } + } +}; + EFI_STATUS DwHdmiQpConnectorPreInit ( OUT ROCKCHIP_CONNECTOR_PROTOCOL *This, @@ -193,6 +566,7 @@ DwHdmiQpConnectorPreInit ( DEBUG ((DEBUG_INIT, "DwHdmiQpConnectorPreInit")); ConnectorState->Type = DRM_MODE_CONNECTOR_HDMIA; Hdmi->Id = Hdptx.Id = PcdGet32(PcdHdmiId); + Hdmi->I2c.PinMux = PcdGet32(PcdHdmiDDCI2CPinMux); if (Hdmi->Id) ConnectorState->OutputInterface = VOP_OUTPUT_IF_HDMI1; @@ -200,6 +574,8 @@ DwHdmiQpConnectorPreInit ( ConnectorState->OutputInterface = VOP_OUTPUT_IF_HDMI0; DwHdmiQpSetIomux(Hdmi); + DwHdmiQpI2cSetIomux(Hdmi); + DwHdmiI2cInit(Hdmi); HdptxRopllCmnConfig(&Hdptx); DEBUG ((DEBUG_INFO, "%a hdmi pre init success\n", __func__)); @@ -308,6 +684,8 @@ DwHdmiQpSetup( DwHdmiQpRegMod(Hdmi, HDCP2_BYPASS, HDCP2_BYPASS, HDCP2LOGIC_CONFIG0); DwHdmiQpRegMod(Hdmi, KEEPOUT_REKEY_ALWAYS, KEEPOUT_REKEY_CFG, FRAME_COMPOSER_CONFIG9); DwHdmiQpRegWrite(Hdmi, 0, FLT_CONFIG0); + + DumpEdid(Hdmi); //enable phy output HdptxRopllTmdsModeConfig(&Hdptx); diff --git a/edk2-rockchip/Silicon/Rockchip/Library/DisplayLib/DwHdmiQpLib.inf b/edk2-rockchip/Silicon/Rockchip/Library/DisplayLib/DwHdmiQpLib.inf index 6385b62a6..8279c9b75 100644 --- a/edk2-rockchip/Silicon/Rockchip/Library/DisplayLib/DwHdmiQpLib.inf +++ b/edk2-rockchip/Silicon/Rockchip/Library/DisplayLib/DwHdmiQpLib.inf @@ -50,6 +50,7 @@ [Pcd] gRockchipTokenSpaceGuid.PcdLcdPixelFormat gRockchipTokenSpaceGuid.PcdHdmiId + gRockchipTokenSpaceGuid.PcdHdmiDDCI2CPinMux gEfiMdeModulePkgTokenSpaceGuid.PcdVideoHorizontalResolution gEfiMdeModulePkgTokenSpaceGuid.PcdVideoVerticalResolution diff --git a/edk2-rockchip/Silicon/Rockchip/RK3588/RK3588Base.dsc.inc b/edk2-rockchip/Silicon/Rockchip/RK3588/RK3588Base.dsc.inc index 80cd199a8..af4f73b09 100644 --- a/edk2-rockchip/Silicon/Rockchip/RK3588/RK3588Base.dsc.inc +++ b/edk2-rockchip/Silicon/Rockchip/RK3588/RK3588Base.dsc.inc @@ -224,6 +224,7 @@ #gRockchipTokenSpaceGuid.PcdEdpId|0x00000001 #edp1 gRockchipTokenSpaceGuid.PcdHdmiId|0x00000000 #hdmi0 #gRockchipTokenSpaceGuid.PcdHdmiId|0x00000001 #hdmi1 + gRockchipTokenSpaceGuid.PcdHdmiDDCI2CPinMux|0x00000000 #hdmitx_i2c_m0 # # CPU Performance default values diff --git a/edk2-rockchip/Silicon/Rockchip/RockchipPkg.dec b/edk2-rockchip/Silicon/Rockchip/RockchipPkg.dec index 1bcc2fb2d..f3a8e4ae7 100644 --- a/edk2-rockchip/Silicon/Rockchip/RockchipPkg.dec +++ b/edk2-rockchip/Silicon/Rockchip/RockchipPkg.dec @@ -86,6 +86,7 @@ gRockchipTokenSpaceGuid.PcdLcdPixelFormat|0|UINT32|0x32000001 gRockchipTokenSpaceGuid.PcdEdpId|0|UINT32|0x32000003 gRockchipTokenSpaceGuid.PcdHdmiId|0|UINT32|0x32000004 + gRockchipTokenSpaceGuid.PcdHdmiDDCI2CPinMux|0|UINT32|0x32000005 gRockchipTokenSpaceGuid.PcdRkMtlMailBoxBase|0x0010f000|UINT64|0x00001000 gRockchipTokenSpaceGuid.PcdRkMtlMailBoxSize|0x100|UINT32|0x00001001