blob: 29e68e9b53017ae39577f13f0ca4e392cb93b7f6 [file] [log] [blame]
// 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)