| // Copyright 2017 The Fuchsia Authors | 
 | // | 
 | // Use of this source code is governed by a MIT-style | 
 | // license that can be found in the LICENSE file or at | 
 | // https://opensource.org/licenses/MIT | 
 |  | 
 | #include <lib/cbuf.h> | 
 | #include <reg.h> | 
 | #include <stdio.h> | 
 | #include <string.h> | 
 | #include <trace.h> | 
 | #include <zircon/boot/driver-config.h> | 
 |  | 
 | #include <arch/arm64/periphmap.h> | 
 | #include <arch/arm64/smccc.h> | 
 | #include <dev/interrupt.h> | 
 | #include <kernel/thread.h> | 
 | #include <pdev/driver.h> | 
 |  | 
 | static vaddr_t preset_base; | 
 | static vaddr_t hiu_base; | 
 | static vaddr_t hdmitx_base; | 
 |  | 
 | #define TOP_OFFSET_MASK (0x0UL << 24) | 
 | #define DWC_OFFSET_MASK (0x10UL << 24) | 
 | #define HDMITX_ADDR_PORT (0x00) | 
 | #define HDMITX_DATA_PORT (0x04) | 
 |  | 
 | #define DISPLAY_MASK(start, count) (((1 << (count)) - 1) << (start)) | 
 | #define DISPLAY_SET_MASK(mask, start, count, value) \ | 
 |   ((mask & ~DISPLAY_MASK(start, count)) | (((value) << (start)) & DISPLAY_MASK(start, count))) | 
 |  | 
 | #define READ32_PRESET_REG(a) readl(preset_base + a) | 
 | #define WRITE32_PRESET_REG(a, v) writel(v, preset_base + a) | 
 |  | 
 | #define READ32_HDMITX_REG(a) readl(hdmitx_base + a) | 
 | #define WRITE32_HDMITX_REG(a, v) writel(v, hdmitx_base + a) | 
 |  | 
 | #define READ32_HHI_REG(a) readl(hiu_base + a) | 
 | #define WRITE32_HHI_REG(a, v) writel(v, hiu_base + a) | 
 |  | 
 | #define SET_BIT32(x, dest, value, count, start)                                    \ | 
 |   WRITE32_##x##_REG(dest, (READ32_##x##_REG(dest) & ~DISPLAY_MASK(start, count)) | \ | 
 |                               (((value) << (start)) & DISPLAY_MASK(start, count))) | 
 |  | 
 | #define HHI_HDMI_CLK_CNTL (0x73 << 2) | 
 | #define HHI_GCLK_MPEG2 (0x52 << 2) | 
 | #define HHI_MEM_PD_REG0 (0x40 << 2) | 
 | #define PRESET0_REGISTER (0x404) | 
 | #define PRESET2_REGISTER (0x40C) | 
 | #define HDMITX_TOP_SW_RESET (TOP_OFFSET_MASK + 0x000) | 
 | #define HDMITX_TOP_CLK_CNTL (TOP_OFFSET_MASK + 0x001) | 
 | #define HDMITX_DWC_MC_LOCKONCLOCK (DWC_OFFSET_MASK + 0x4006) | 
 | #define HDMITX_DWC_MC_CLKDIS (DWC_OFFSET_MASK + 0x4001) | 
 | #define HDMITX_DWC_A_APIINTMSK (DWC_OFFSET_MASK + 0x5008) | 
 | #define HDMITX_DWC_A_VIDPOLCFG (DWC_OFFSET_MASK + 0x5009) | 
 | #define HDMITX_DWC_A_OESSWCFG (DWC_OFFSET_MASK + 0x500A) | 
 |  | 
 | static void hdmitx_writereg(uint32_t addr, uint32_t data) { | 
 |   // determine if we are writing to HDMI TOP (AMLOGIC Wrapper) or HDMI IP | 
 |   uint32_t offset = (addr & DWC_OFFSET_MASK) >> 24; | 
 |   addr = addr & 0xffff; | 
 |   WRITE32_HDMITX_REG(HDMITX_ADDR_PORT + offset, addr); | 
 |   WRITE32_HDMITX_REG(HDMITX_ADDR_PORT + offset, addr);  // FIXME: Need to write twice! | 
 |   WRITE32_HDMITX_REG(HDMITX_DATA_PORT + offset, data); | 
 | } | 
 |  | 
 | static void s912_hdcp_init(const void* driver_data, uint32_t length) { | 
 |   ASSERT(length >= sizeof(dcfg_amlogic_hdcp_driver_t)); | 
 |   auto driver = static_cast<const dcfg_amlogic_hdcp_driver_t*>(driver_data); | 
 |   ASSERT(driver->preset_phys && driver->hiu_phys && driver->hdmitx_phys); | 
 |  | 
 |   // get virtual addresses of our peripheral bases | 
 |   preset_base = periph_paddr_to_vaddr(driver->preset_phys); | 
 |   hiu_base = periph_paddr_to_vaddr(driver->hiu_phys); | 
 |   hdmitx_base = periph_paddr_to_vaddr(driver->hdmitx_phys); | 
 |   ASSERT(preset_base && hiu_base && hdmitx_base); | 
 |  | 
 |   // enable clocks | 
 |   SET_BIT32(HHI, HHI_HDMI_CLK_CNTL, 0x0100, 16, 0); | 
 |  | 
 |   // enable clk81 (needed for HDMI module and a bunch of other modules) | 
 |   SET_BIT32(HHI, HHI_GCLK_MPEG2, 1, 1, 4); | 
 |  | 
 |   // power up HDMI Memory (bits 15:8) | 
 |   SET_BIT32(HHI, HHI_MEM_PD_REG0, 0, 8, 8); | 
 |  | 
 |   // reset hdmi related blocks (HIU, HDMI SYS, HDMI_TX) | 
 |   WRITE32_PRESET_REG(PRESET0_REGISTER, (1 << 19)); | 
 |   WRITE32_PRESET_REG(PRESET2_REGISTER, (1 << 15)); | 
 |   WRITE32_PRESET_REG(PRESET2_REGISTER, (1 << 2)); | 
 |  | 
 |   // // Bring HDMI out of reset | 
 |   hdmitx_writereg(HDMITX_TOP_SW_RESET, 0); | 
 |   spin(200); | 
 |   hdmitx_writereg(HDMITX_TOP_CLK_CNTL, 0x000000ff); | 
 |   hdmitx_writereg(HDMITX_DWC_MC_LOCKONCLOCK, 0xff); | 
 |   hdmitx_writereg(HDMITX_DWC_MC_CLKDIS, 0x00); | 
 |  | 
 |   /* Configure HDCP */ | 
 |   uint32_t data32 = 0; | 
 |   data32 |= (0 << 7); | 
 |   data32 |= (0 << 6); | 
 |   data32 |= (0 << 4); | 
 |   data32 |= (0 << 3); | 
 |   data32 |= (0 << 2); | 
 |   data32 |= (0 << 1); | 
 |   data32 |= (1 << 0); | 
 |   hdmitx_writereg(HDMITX_DWC_A_APIINTMSK, data32); | 
 |  | 
 |   data32 = 0; | 
 |   data32 |= (0 << 5); | 
 |   data32 |= (1 << 4); | 
 |   data32 |= (1 << 3); | 
 |   data32 |= (1 << 1); | 
 |   hdmitx_writereg(HDMITX_DWC_A_VIDPOLCFG, data32); | 
 |  | 
 |   hdmitx_writereg(HDMITX_DWC_A_OESSWCFG, 0x40); | 
 |  | 
 |   arm_smccc_smc(0x82000012, 0, 0, 0, 0, 0, 0, 0); | 
 | } | 
 |  | 
 | LK_PDEV_INIT(s912_hdcp_init, KDRV_AMLOGIC_HDCP, s912_hdcp_init, LK_INIT_LEVEL_PLATFORM) |