// Copyright 2018 The Fuchsia Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "vpu.h" #include "vpu-regs.h" #include "hhi-regs.h" #include "vpp-regs.h" #include #include namespace astro_display { namespace { constexpr uint32_t kVpuMux = 0; constexpr uint32_t kVpuDiv = 3; constexpr int16_t RGB709_to_YUV709l_coeff[24] = { 0x0000, 0x0000, 0x0000, 0x00bb, 0x0275, 0x003f, 0x1f99, 0x1ea6, 0x01c2, 0x01c2, 0x1e67, 0x1fd7, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0040, 0x0200, 0x0200, 0x0000, 0x0000, 0x0000, }; constexpr int16_t YUV709l_to_RGB709_coeff12[24] = { -256, -2048, -2048, 4788, 0, 7372, 4788, -876, -2190, 4788, 8686, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, }; } // namespace // AOBUS Register #define AOBUS_GEN_PWR_SLEEP0 (0x03a << 2) // CBUS Reset Register #define RESET0_LEVEL (0x0420 << 2) #define RESET1_LEVEL (0x0421 << 2) #define RESET2_LEVEL (0x0422 << 2) #define RESET4_LEVEL (0x0424 << 2) #define RESET7_LEVEL (0x0427 << 2) #define READ32_VPU_REG(a) vpu_regs_->Read(a) #define WRITE32_VPU_REG(a, v) vpu_regs_->Write(a, v) #define READ32_HHI_REG(a) hhi_regs_->Read(a) #define WRITE32_HHI_REG(a, v) hhi_regs_->Write(a, v) #define READ32_AOBUS_REG(a) aobus_regs_->Read(a) #define WRITE32_AOBUS_REG(a, v) aobus_regs_->Write(a, v) #define READ32_CBUS_REG(a) cbus_regs_->Read(a) #define WRITE32_CBUS_REG(a, v) cbus_regs_->Write(a, v) zx_status_t Vpu::Init(zx_device_t* parent) { if (initialized_) { return ZX_OK; } zx_status_t status = device_get_protocol(parent, ZX_PROTOCOL_PLATFORM_DEV, &pdev_); if (status != ZX_OK) { return status; } // Map VPU registers status = pdev_map_mmio_buffer(&pdev_, MMIO_VPU, ZX_CACHE_POLICY_UNCACHED_DEVICE, &mmio_vpu_); if (status != ZX_OK) { DISP_ERROR("vpu: Could not map VPU mmio\n"); return status; } vpu_regs_ = fbl::make_unique(io_buffer_virt(&mmio_vpu_)); // Map HHI registers status = pdev_map_mmio_buffer(&pdev_, MMIO_HHI, ZX_CACHE_POLICY_UNCACHED_DEVICE, &mmio_hhi_); if (status != ZX_OK) { DISP_ERROR("vpu: Could not map HHI mmio\n"); return status; } hhi_regs_ = fbl::make_unique(io_buffer_virt(&mmio_hhi_)); // Map AOBUS registers status = pdev_map_mmio_buffer(&pdev_, MMIO_AOBUS, ZX_CACHE_POLICY_UNCACHED_DEVICE, &mmio_aobus_); if (status != ZX_OK) { DISP_ERROR("vpu: Could not map AOBUS mmio\n"); return status; } aobus_regs_ = fbl::make_unique(io_buffer_virt(&mmio_aobus_)); // Map CBUS registers status = pdev_map_mmio_buffer(&pdev_, MMIO_CBUS, ZX_CACHE_POLICY_UNCACHED_DEVICE, &mmio_cbus_); if (status != ZX_OK) { DISP_ERROR("vpu: Could not map CBUS mmio\n"); return status; } cbus_regs_ = fbl::make_unique(io_buffer_virt(&mmio_cbus_)); // VPU object is ready to be used initialized_ = true; return ZX_OK; } void Vpu::VppInit() { ZX_DEBUG_ASSERT(initialized_); // init vpu fifo control register SET_BIT32(VPU, VPP_OFIFO_SIZE, 0xFFF, 0, 12); WRITE32_REG(VPU, VPP_HOLD_LINES, 0x08080808); // default probe_sel, for highlight en SET_BIT32(VPU, VPP_MATRIX_CTRL, 0x7, 12, 3); // setting up os1 for rgb -> yuv limit const int16_t* m = RGB709_to_YUV709l_coeff; // VPP WRAP OSD1 matrix WRITE32_REG(VPU, VPP_WRAP_OSD1_MATRIX_PRE_OFFSET0_1, ((m[0] & 0xfff) << 16) | (m[1] & 0xfff)); WRITE32_REG(VPU, VPP_WRAP_OSD1_MATRIX_PRE_OFFSET2, m[2] & 0xfff); WRITE32_REG(VPU, VPP_WRAP_OSD1_MATRIX_COEF00_01, ((m[3] & 0x1fff) << 16) | (m[4] & 0x1fff)); WRITE32_REG(VPU, VPP_WRAP_OSD1_MATRIX_COEF02_10, ((m[5] & 0x1fff) << 16) | (m[6] & 0x1fff)); WRITE32_REG(VPU, VPP_WRAP_OSD1_MATRIX_COEF11_12, ((m[7] & 0x1fff) << 16) | (m[8] & 0x1fff)); WRITE32_REG(VPU, VPP_WRAP_OSD1_MATRIX_COEF20_21, ((m[9] & 0x1fff) << 16) | (m[10] & 0x1fff)); WRITE32_REG(VPU, VPP_WRAP_OSD1_MATRIX_COEF22, m[11] & 0x1fff); WRITE32_REG(VPU, VPP_WRAP_OSD1_MATRIX_OFFSET0_1, ((m[18] & 0xfff) << 16) | (m[19] & 0xfff)); WRITE32_REG(VPU, VPP_WRAP_OSD1_MATRIX_OFFSET2, m[20] & 0xfff); SET_BIT32(VPU, VPP_WRAP_OSD1_MATRIX_EN_CTRL, 1, 0, 1); // VPP WRAP OSD2 matrix WRITE32_REG(VPU, VPP_WRAP_OSD2_MATRIX_PRE_OFFSET0_1, ((m[0] & 0xfff) << 16) | (m[1] & 0xfff)); WRITE32_REG(VPU, VPP_WRAP_OSD2_MATRIX_PRE_OFFSET2, m[2] & 0xfff); WRITE32_REG(VPU, VPP_WRAP_OSD2_MATRIX_COEF00_01, ((m[3] & 0x1fff) << 16) | (m[4] & 0x1fff)); WRITE32_REG(VPU, VPP_WRAP_OSD2_MATRIX_COEF02_10, ((m[5] & 0x1fff) << 16) | (m[6] & 0x1fff)); WRITE32_REG(VPU, VPP_WRAP_OSD2_MATRIX_COEF11_12, ((m[7] & 0x1fff) << 16) | (m[8] & 0x1fff)); WRITE32_REG(VPU, VPP_WRAP_OSD2_MATRIX_COEF20_21, ((m[9] & 0x1fff) << 16) | (m[10] & 0x1fff)); WRITE32_REG(VPU, VPP_WRAP_OSD2_MATRIX_COEF22, m[11] & 0x1fff); WRITE32_REG(VPU, VPP_WRAP_OSD2_MATRIX_OFFSET0_1, ((m[18] & 0xfff) << 16) | (m[19] & 0xfff)); WRITE32_REG(VPU, VPP_WRAP_OSD2_MATRIX_OFFSET2, m[20] & 0xfff); SET_BIT32(VPU, VPP_WRAP_OSD2_MATRIX_EN_CTRL, 1, 0, 1); // VPP WRAP OSD3 matrix WRITE32_REG(VPU, VPP_WRAP_OSD3_MATRIX_PRE_OFFSET0_1, ((m[0] & 0xfff) << 16) | (m[1] & 0xfff)); WRITE32_REG(VPU, VPP_WRAP_OSD3_MATRIX_PRE_OFFSET2, m[2] & 0xfff); WRITE32_REG(VPU, VPP_WRAP_OSD3_MATRIX_COEF00_01, ((m[3] & 0x1fff) << 16) | (m[4] & 0x1fff)); WRITE32_REG(VPU, VPP_WRAP_OSD3_MATRIX_COEF02_10, ((m[5] & 0x1fff) << 16) | (m[6] & 0x1fff)); WRITE32_REG(VPU, VPP_WRAP_OSD3_MATRIX_COEF11_12, ((m[7] & 0x1fff) << 16) | (m[8] & 0x1fff)); WRITE32_REG(VPU, VPP_WRAP_OSD3_MATRIX_COEF20_21, ((m[9] & 0x1fff) << 16) | (m[10] & 0x1fff)); WRITE32_REG(VPU, VPP_WRAP_OSD3_MATRIX_COEF22, m[11] & 0x1fff); WRITE32_REG(VPU, VPP_WRAP_OSD3_MATRIX_OFFSET0_1, ((m[18] & 0xfff) << 16) | (m[19] & 0xfff)); WRITE32_REG(VPU, VPP_WRAP_OSD3_MATRIX_OFFSET2, m[20] & 0xfff); SET_BIT32(VPU, VPP_WRAP_OSD3_MATRIX_EN_CTRL, 1, 0, 1); WRITE32_REG(VPU, DOLBY_PATH_CTRL, 0xf); // POST2 matrix: YUV limit -> RGB default is 12bit m = YUV709l_to_RGB709_coeff12; // VPP WRAP POST2 matrix WRITE32_REG(VPU, VPP_POST2_MATRIX_PRE_OFFSET0_1, (((m[0] >> 2) & 0xfff) << 16) | ((m[1] >> 2) & 0xfff)); WRITE32_REG(VPU, VPP_POST2_MATRIX_PRE_OFFSET2, (m[2] >> 2) & 0xfff); WRITE32_REG(VPU, VPP_POST2_MATRIX_COEF00_01, (((m[3] >> 2) & 0x1fff) << 16) | ((m[4] >> 2) & 0x1fff)); WRITE32_REG(VPU, VPP_POST2_MATRIX_COEF02_10, (((m[5] >> 2) & 0x1fff) << 16) | ((m[6] >> 2) & 0x1fff)); WRITE32_REG(VPU, VPP_POST2_MATRIX_COEF11_12, (((m[7] >> 2) & 0x1fff) << 16) | ((m[8] >> 2) & 0x1fff)); WRITE32_REG(VPU, VPP_POST2_MATRIX_COEF20_21, (((m[9] >> 2) & 0x1fff) << 16) | ((m[10] >> 2) & 0x1fff)); WRITE32_REG(VPU, VPP_POST2_MATRIX_COEF22, (m[11] >> 2) & 0x1fff); WRITE32_REG(VPU, VPP_POST2_MATRIX_OFFSET0_1, (((m[18] >> 2) & 0xfff) << 16) | ((m[19] >> 2) & 0xfff)); WRITE32_REG(VPU, VPP_POST2_MATRIX_OFFSET2, (m[20] >> 2) & 0xfff); SET_BIT32(VPU, VPP_POST2_MATRIX_EN_CTRL, 1, 0, 1); SET_BIT32(VPU, VPP_MATRIX_CTRL, 1, 0, 1); SET_BIT32(VPU, VPP_MATRIX_CTRL, 0, 8, 3); // 709L to RGB WRITE32_REG(VPU, VPP_MATRIX_PRE_OFFSET0_1, 0x0FC00E00); WRITE32_REG(VPU, VPP_MATRIX_PRE_OFFSET2, 0x00000E00); // ycbcr limit range, 709 to RGB // -16 1.164 0 1.793 0 // -128 1.164 -0.213 -0.534 0 // -128 1.164 2.115 0 0 WRITE32_REG(VPU, VPP_MATRIX_COEF00_01, 0x04A80000); WRITE32_REG(VPU, VPP_MATRIX_COEF02_10, 0x072C04A8); WRITE32_REG(VPU, VPP_MATRIX_COEF11_12, 0x1F261DDD); WRITE32_REG(VPU, VPP_MATRIX_COEF20_21, 0x04A80876); WRITE32_REG(VPU, VPP_MATRIX_COEF22, 0x0); WRITE32_REG(VPU, VPP_MATRIX_OFFSET0_1, 0x0); WRITE32_REG(VPU, VPP_MATRIX_OFFSET2, 0x0); SET_BIT32(VPU, VPP_MATRIX_CLIP, 0, 5, 3); } void Vpu::ConfigureClock() { ZX_DEBUG_ASSERT(initialized_); // vpu clock WRITE32_REG(HHI, HHI_VPU_CLK_CNTL, ((kVpuMux << 9) | (kVpuDiv << 0))); SET_BIT32(HHI, HHI_VPU_CLK_CNTL, 1, 8, 1); // vpu clkb // bit 0 is set since kVpuClkFrequency > clkB max frequency (350MHz) WRITE32_REG(HHI, HHI_VPU_CLKB_CNTL, ((1 << 8) | (1 << 0))); // vapb clk // turn on ge2d clock since kVpuClkFrequency > 250MHz WRITE32_REG(HHI, HHI_VAPBCLK_CNTL, (1 << 30) | (0 << 9) | (1 << 0)); SET_BIT32(HHI, HHI_VAPBCLK_CNTL, 1, 8, 1); SET_BIT32(HHI, HHI_VID_CLK_CNTL2, 0, 0, 8); // dmc_arb_config WRITE32_REG(VPU, VPU_RDARB_MODE_L1C1, 0x0); WRITE32_REG(VPU, VPU_RDARB_MODE_L1C2, 0x10000); WRITE32_REG(VPU, VPU_RDARB_MODE_L2C1, 0x900000); WRITE32_REG(VPU, VPU_WRARB_MODE_L2C1, 0x20000); } void Vpu::PowerOn() { ZX_DEBUG_ASSERT(initialized_); SET_BIT32(AOBUS, AOBUS_GEN_PWR_SLEEP0, 0, 8, 1); // [8] power on // power up memories for (int i = 0; i < 32; i+=2) { SET_BIT32(HHI, HHI_VPU_MEM_PD_REG0, 0, i, 2); zx_nanosleep(zx_deadline_after(ZX_USEC(5))); } for (int i = 0; i < 32; i+=2) { SET_BIT32(HHI, HHI_VPU_MEM_PD_REG1, 0, i, 2); zx_nanosleep(zx_deadline_after(ZX_USEC(5))); } SET_BIT32(HHI, HHI_VPU_MEM_PD_REG2, 0, 0, 2); zx_nanosleep(zx_deadline_after(ZX_USEC(5))); for (int i = 4; i < 18; i+=2) { SET_BIT32(HHI, HHI_VPU_MEM_PD_REG2, 0, i, 2); zx_nanosleep(zx_deadline_after(ZX_USEC(5))); } SET_BIT32(HHI, HHI_VPU_MEM_PD_REG2, 0, 30, 2); zx_nanosleep(zx_deadline_after(ZX_USEC(5))); for (int i = 8; i < 16; i++) { SET_BIT32(HHI, HHI_MEM_PD_REG0, 0, i, 1); zx_nanosleep(zx_deadline_after(ZX_USEC(5))); } zx_nanosleep(zx_deadline_after(ZX_USEC(20))); // Reset VIU + VENC // Reset VENCI + VENCP + VADC + VENCL // Reset HDMI-APB + HDMI-SYS + HDMI-TX + HDMI-CEC CLEAR_MASK32(CBUS, RESET0_LEVEL, ((1<<5) | (1<<10) | (1<<19) | (1<<13))); CLEAR_MASK32(CBUS, RESET1_LEVEL, (1<<5)); CLEAR_MASK32(CBUS, RESET2_LEVEL, (1<<15)); CLEAR_MASK32(CBUS, RESET4_LEVEL, ((1<<6) | (1<<7) | (1<<13) | (1<<5) | (1<<9) | (1<<4) | (1<<12))); CLEAR_MASK32(CBUS, RESET7_LEVEL, (1<<7)); // Remove VPU_HDMI ISO SET_BIT32(AOBUS, AOBUS_GEN_PWR_SLEEP0, 0, 9, 1); // [9] VPU_HDMI // release Reset SET_MASK32(CBUS, RESET0_LEVEL, ((1 << 5) | (1<<10) | (1<<19) | (1<<13))); SET_MASK32(CBUS, RESET1_LEVEL, (1<<5)); SET_MASK32(CBUS, RESET2_LEVEL, (1<<15)); SET_MASK32(CBUS, RESET4_LEVEL, ((1<<6) | (1<<7) | (1<<13) | (1<<5) | (1<<9) | (1<<4) | (1<<12))); SET_MASK32(CBUS, RESET7_LEVEL, (1<<7)); ConfigureClock(); } void Vpu::PowerOff() { ZX_DEBUG_ASSERT(initialized_); // Power down VPU_HDMI // Enable Isolation SET_BIT32(AOBUS, AOBUS_GEN_PWR_SLEEP0, 1, 9, 1); // ISO zx_nanosleep(zx_deadline_after(ZX_USEC(20))); // power down memories for (int i = 0; i < 32; i+=2) { SET_BIT32(HHI, HHI_VPU_MEM_PD_REG0, 0x3, i, 2); zx_nanosleep(zx_deadline_after(ZX_USEC(5))); } for (int i = 0; i < 32; i+=2) { SET_BIT32(HHI, HHI_VPU_MEM_PD_REG1, 0x3, i, 2); zx_nanosleep(zx_deadline_after(ZX_USEC(5))); } SET_BIT32(HHI, HHI_VPU_MEM_PD_REG2, 0x3, 0, 2); zx_nanosleep(zx_deadline_after(ZX_USEC(5))); for (int i = 4; i < 18; i+=2) { SET_BIT32(HHI, HHI_VPU_MEM_PD_REG2, 0x3, i, 2); zx_nanosleep(zx_deadline_after(ZX_USEC(5))); } SET_BIT32(HHI, HHI_VPU_MEM_PD_REG2, 0x3, 30, 2); zx_nanosleep(zx_deadline_after(ZX_USEC(5))); for (int i = 8; i < 16; i++) { SET_BIT32(HHI, HHI_MEM_PD_REG0, 0x1, i, 1); zx_nanosleep(zx_deadline_after(ZX_USEC(5))); } zx_nanosleep(zx_deadline_after(ZX_USEC(20))); // Power down VPU domain SET_BIT32(AOBUS, AOBUS_GEN_PWR_SLEEP0, 1, 8, 1); // PDN SET_BIT32(HHI, HHI_VAPBCLK_CNTL, 0, 8, 1); SET_BIT32(HHI, HHI_VPU_CLK_CNTL, 0, 8, 1); } } // namespace astro_display