| // Copyright 2021 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 "aml-hdmi-host.h" |
| |
| #include <fbl/alloc_checker.h> |
| |
| #include "cbus-regs.h" |
| #include "hhi-regs.h" |
| #include "vpu-regs.h" |
| |
| namespace amlogic_display { |
| |
| namespace { |
| |
| struct reg_val_pair { |
| uint32_t reg; |
| uint32_t val; |
| }; |
| |
| static const struct reg_val_pair ENC_LUT_GEN[] = { |
| {VPU_ENCP_VIDEO_EN, 0}, {VPU_ENCI_VIDEO_EN, 0}, |
| {VPU_ENCP_VIDEO_MODE, 0x4040}, {VPU_ENCP_VIDEO_MODE_ADV, 0x18}, |
| {VPU_VPU_VIU_VENC_MUX_CTRL, 0xA}, {VPU_ENCP_VIDEO_VSO_BEGIN, 16}, |
| {VPU_ENCP_VIDEO_VSO_END, 32}, {VPU_ENCI_VIDEO_EN, 0}, |
| {VPU_ENCP_VIDEO_EN, 1}, {0xFFFFFFFF, 0}, |
| }; |
| |
| void TranslateDisplayMode(fidl::AnyAllocator& allocator, const display_mode_t& in_mode, |
| const ColorParam& in_color, DisplayMode* out_mode) { |
| // Serves to translate between banjo struct display_mode_t and fidl struct DisplayMode |
| fuchsia_hardware_hdmi::wire::StandardDisplayMode mode{ |
| .pixel_clock_10khz = in_mode.pixel_clock_10khz, |
| .h_addressable = in_mode.h_addressable, |
| .h_front_porch = in_mode.h_front_porch, |
| .h_sync_pulse = in_mode.h_sync_pulse, |
| .h_blanking = in_mode.h_blanking, |
| .v_addressable = in_mode.v_addressable, |
| .v_front_porch = in_mode.v_front_porch, |
| .v_sync_pulse = in_mode.v_sync_pulse, |
| .v_blanking = in_mode.v_blanking, |
| .flags = in_mode.flags, |
| }; |
| out_mode->set_mode(allocator, mode); |
| |
| ColorParam color{ |
| .input_color_format = in_color.input_color_format, |
| .output_color_format = in_color.output_color_format, |
| .color_depth = in_color.color_depth, |
| }; |
| out_mode->set_color(allocator, color); |
| } |
| |
| } // namespace |
| |
| zx_status_t AmlHdmiHost::Init() { |
| // Map registers |
| auto status = pdev_.MapMmio(MMIO_VPU, &vpu_mmio_); |
| if (status != ZX_OK) { |
| DISP_ERROR("Could not map VPU mmio\n"); |
| return status; |
| } |
| |
| status = pdev_.MapMmio(MMIO_HHI, &hhi_mmio_); |
| if (status != ZX_OK) { |
| DISP_ERROR("Could not map HHI mmio\n"); |
| return status; |
| } |
| |
| status = pdev_.MapMmio(MMIO_CBUS, &cbus_mmio_); |
| if (status != ZX_OK) { |
| DISP_ERROR("Could not map CBUS mmio\n"); |
| return status; |
| } |
| |
| fbl::AllocChecker ac; |
| hdmitx_ = fbl::make_unique_checked<amlogic_display::AmlHdmitx>(&ac, pdev_); |
| if (!ac.check()) { |
| DISP_ERROR("Could not create AmlHdmitx object\n"); |
| return ZX_ERR_NO_MEMORY; |
| } |
| |
| status = hdmitx_->Init(); |
| if (status != ZX_OK) { |
| DISP_ERROR("Could not initialize HDMITX\n"); |
| return status; |
| } |
| |
| return HostOn(); |
| } |
| |
| zx_status_t AmlHdmiHost::HostOn() { |
| /* Step 1: Initialize various clocks related to the HDMI Interface*/ |
| SET_BIT32(CBUS, PAD_PULL_UP_EN_REG3, 0, 0, 2); |
| SET_BIT32(CBUS, PAD_PULL_UP_REG3, 0, 0, 2); |
| SET_BIT32(CBUS, P_PREG_PAD_GPIO3_EN_N, 3, 0, 2); |
| SET_BIT32(CBUS, PERIPHS_PIN_MUX_B, 0x11, 0, 8); |
| |
| // enable clocks |
| HhiHdmiClkCntlReg::Get() |
| .ReadFrom(&(*hhi_mmio_)) |
| .set_clk_div(0) |
| .set_clk_en(1) |
| .set_clk_sel(0) |
| .WriteTo(&(*hhi_mmio_)); |
| |
| // enable clk81 (needed for HDMI module and a bunch of other modules) |
| HhiGclkMpeg2Reg::Get().ReadFrom(&(*hhi_mmio_)).set_clk81_en(1).WriteTo(&(*hhi_mmio_)); |
| |
| // power up HDMI Memory (bits 15:8) |
| HhiMemPdReg0::Get().ReadFrom(&(*hhi_mmio_)).set_hdmi(0).WriteTo(&(*hhi_mmio_)); |
| |
| return hdmitx_->InitHw(); |
| } |
| |
| void AmlHdmiHost::HostOff() { |
| /* Close HDMITX PHY */ |
| WRITE32_REG(HHI, HHI_HDMI_PHY_CNTL0, 0); |
| WRITE32_REG(HHI, HHI_HDMI_PHY_CNTL3, 0); |
| /* Disable HPLL */ |
| WRITE32_REG(HHI, HHI_HDMI_PLL_CNTL0, 0); |
| |
| return hdmitx_->ShutDown(); |
| } |
| |
| zx_status_t AmlHdmiHost::ModeSet(const display_mode_t& mode) { |
| for (size_t i = 0; ENC_LUT_GEN[i].reg != 0xFFFFFFFF; i++) { |
| WRITE32_REG(VPU, ENC_LUT_GEN[i].reg, ENC_LUT_GEN[i].val); |
| } |
| |
| WRITE32_REG( |
| VPU, VPU_ENCP_VIDEO_MAX_PXCNT, |
| (p_.timings.venc_pixel_repeat) ? ((p_.timings.htotal << 1) - 1) : (p_.timings.htotal - 1)); |
| WRITE32_REG(VPU, VPU_ENCP_VIDEO_MAX_LNCNT, p_.timings.vtotal - 1); |
| |
| if (p_.timings.venc_pixel_repeat) { |
| SET_BIT32(VPU, VPU_ENCP_VIDEO_MODE_ADV, 1, 0, 1); |
| } |
| |
| // Configure Encoder with detailed timing info (based on resolution) |
| ConfigEncoder(); |
| |
| // Configure VDAC |
| WRITE32_REG(HHI, HHI_VDAC_CNTL0_G12A, 0); |
| WRITE32_REG(HHI, HHI_VDAC_CNTL1_G12A, 8); // set Cdac_pwd [whatever that is] |
| |
| fidl::FidlAllocator<2048> allocator; |
| DisplayMode translated_mode(allocator); |
| TranslateDisplayMode(allocator, mode, color_, &translated_mode); |
| auto status = hdmitx_->InitInterface(translated_mode); |
| if (status != ZX_OK) { |
| DISP_ERROR("Unable to initialize interface %d\n", status); |
| return status; |
| } |
| |
| // Setup HDMI related registers in VPU |
| // not really needed since we are not converting from 420/422. but set anyways |
| VpuHdmiFmtCtrlReg::Get() |
| .FromValue(0) |
| .set_cntl_chroma_dnsmp(2) |
| .set_cntl_hdmi_dith_en(0) |
| .set_rounding_enable(1) |
| .WriteTo(&(*vpu_mmio_)); |
| |
| // setup some magic registers |
| VpuHdmiDithCntlReg::Get() |
| .ReadFrom(&(*vpu_mmio_)) |
| .set_cntl_hdmi_dith_en(1) |
| .set_hsync_invert(0) |
| .set_vsync_invert(0) |
| .WriteTo(&(*vpu_mmio_)); |
| |
| // reset vpu bridge |
| uint32_t wr_rate = VpuHdmiSettingReg::Get().ReadFrom(&(*vpu_mmio_)).wr_rate(); |
| WRITE32_REG(VPU, VPU_ENCP_VIDEO_EN, 0); |
| VpuHdmiSettingReg::Get() |
| .ReadFrom(&(*vpu_mmio_)) |
| .set_src_sel(0) |
| .set_wr_rate(0) |
| .WriteTo(&(*vpu_mmio_)); |
| usleep(1); |
| WRITE32_REG(VPU, VPU_ENCP_VIDEO_EN, 1); |
| usleep(1); |
| VpuHdmiSettingReg::Get().ReadFrom(&(*vpu_mmio_)).set_wr_rate(wr_rate).WriteTo(&(*vpu_mmio_)); |
| usleep(1); |
| VpuHdmiSettingReg::Get().ReadFrom(&(*vpu_mmio_)).set_src_sel(2).WriteTo(&(*vpu_mmio_)); |
| |
| // setup hdmi phy |
| ConfigPhy(); |
| |
| DISP_INFO("done!!\n"); |
| return ZX_OK; |
| } |
| |
| zx_status_t AmlHdmiHost::GetVic(const display_mode_t* disp_timing) { |
| display_mode_t mode; |
| memcpy(&mode, disp_timing, sizeof(display_mode_t)); |
| hdmi_param p; |
| return GetVic(&mode, &p); |
| } |
| |
| zx_status_t AmlHdmiHost::GetVic(display_mode_t* disp_timing) { return GetVic(disp_timing, &p_); } |
| |
| zx_status_t AmlHdmiHost::GetVic(display_mode_t* disp_timing, hdmi_param* p) { |
| if (disp_timing->v_addressable == 2160) { |
| DISP_INFO("4K Monitor Detected.\n"); |
| |
| if ((disp_timing->pixel_clock_10khz * 10) == 533250) { |
| // FIXME: 4K with reduced blanking (533.25MHz) does not work |
| DISP_INFO("4K @ 30Hz\n"); |
| disp_timing->flags &= ~MODE_FLAG_INTERLACED; |
| disp_timing->pixel_clock_10khz = 29700; |
| disp_timing->h_addressable = 3840; |
| disp_timing->h_blanking = 560; |
| disp_timing->h_front_porch = 176; |
| disp_timing->h_sync_pulse = 88; |
| disp_timing->flags |= MODE_FLAG_HSYNC_POSITIVE; |
| disp_timing->v_addressable = 2160; |
| disp_timing->v_blanking = 90; |
| disp_timing->v_front_porch = 8; |
| disp_timing->v_sync_pulse = 10; |
| disp_timing->flags |= MODE_FLAG_VSYNC_POSITIVE; |
| } |
| } |
| |
| // Monitor has its own preferred timings. Use that |
| p->timings.interlace_mode = disp_timing->flags & MODE_FLAG_INTERLACED; |
| p->timings.pfreq = (disp_timing->pixel_clock_10khz * 10); // KHz |
| // TODO: pixel repetition is 0 for most progressive. We don't support interlaced |
| p->timings.pixel_repeat = 0; |
| p->timings.hactive = disp_timing->h_addressable; |
| p->timings.hblank = disp_timing->h_blanking; |
| p->timings.hfront = disp_timing->h_front_porch; |
| p->timings.hsync = disp_timing->h_sync_pulse; |
| p->timings.htotal = (p->timings.hactive) + (p->timings.hblank); |
| p->timings.hback = (p->timings.hblank) - (p->timings.hfront + p->timings.hsync); |
| p->timings.hpol = disp_timing->flags & MODE_FLAG_HSYNC_POSITIVE; |
| |
| p->timings.vactive = disp_timing->v_addressable; |
| p->timings.vblank0 = disp_timing->v_blanking; |
| p->timings.vfront = disp_timing->v_front_porch; |
| p->timings.vsync = disp_timing->v_sync_pulse; |
| p->timings.vtotal = (p->timings.vactive) + (p->timings.vblank0); |
| p->timings.vback = (p->timings.vblank0) - (p->timings.vfront + p->timings.vsync); |
| p->timings.vpol = disp_timing->flags & MODE_FLAG_VSYNC_POSITIVE; |
| |
| // FIXE: VENC Repeat is undocumented. It seems to be only needed for the following |
| // resolutions: 1280x720p60, 1280x720p50, 720x480p60, 720x480i60, 720x576p50, 720x576i50 |
| // For now, we will simply not support this feature. |
| p->timings.venc_pixel_repeat = 0; |
| // Let's make sure we support what we've got so far |
| if (p->timings.interlace_mode) { |
| return ZX_ERR_NOT_SUPPORTED; |
| } |
| |
| if (p->timings.pfreq > 500000) { |
| p->phy_mode = 1; |
| } else if (p->timings.pfreq > 200000) { |
| p->phy_mode = 2; |
| } else if (p->timings.pfreq > 100000) { |
| p->phy_mode = 3; |
| } else { |
| p->phy_mode = 4; |
| } |
| |
| // TODO: We probably need a more sophisticated method for calculating |
| // clocks. This will do for now. |
| p->pll_p_24b.viu_channel = 1; |
| p->pll_p_24b.viu_type = VIU_ENCP; |
| p->pll_p_24b.vid_pll_div = VID_PLL_DIV_5; |
| p->pll_p_24b.vid_clk_div = 2; |
| p->pll_p_24b.hdmi_tx_pixel_div = 1; |
| p->pll_p_24b.encp_div = 1; |
| p->pll_p_24b.od1 = 1; |
| p->pll_p_24b.od2 = 1; |
| p->pll_p_24b.od3 = 1; |
| |
| p->pll_p_24b.hpll_clk_out = (p->timings.pfreq * 10); |
| while (p->pll_p_24b.hpll_clk_out < 2900000) { |
| if (p->pll_p_24b.od1 < 4) { |
| p->pll_p_24b.od1 *= 2; |
| p->pll_p_24b.hpll_clk_out *= 2; |
| } else if (p->pll_p_24b.od2 < 4) { |
| p->pll_p_24b.od2 *= 2; |
| p->pll_p_24b.hpll_clk_out *= 2; |
| } else if (p->pll_p_24b.od3 < 4) { |
| p->pll_p_24b.od3 *= 2; |
| p->pll_p_24b.hpll_clk_out *= 2; |
| } else { |
| return ZX_ERR_OUT_OF_RANGE; |
| } |
| } |
| if (p->pll_p_24b.hpll_clk_out > 6000000) { |
| DISP_ERROR("Something went wrong in clock calculation (pll_out = %d)\n", |
| p->pll_p_24b.hpll_clk_out); |
| return ZX_ERR_OUT_OF_RANGE; |
| } |
| |
| return ZX_OK; |
| } |
| |
| void AmlHdmiHost::ConfigEncoder() { |
| uint32_t h_begin, h_end; |
| uint32_t v_begin, v_end; |
| uint32_t hs_begin, hs_end; |
| uint32_t vs_begin, vs_end; |
| uint32_t vsync_adjust = 0; |
| uint32_t active_lines, total_lines; |
| uint32_t venc_total_pixels, venc_active_pixels, venc_fp, venc_hsync; |
| auto* p = &p_; |
| |
| active_lines = (p->timings.vactive / (1 + p->timings.interlace_mode)); |
| total_lines = (active_lines + p->timings.vblank0) + |
| ((active_lines + p->timings.vblank1) * p->timings.interlace_mode); |
| |
| venc_total_pixels = |
| (p->timings.htotal / (p->timings.pixel_repeat + 1)) * (p->timings.venc_pixel_repeat + 1); |
| |
| venc_active_pixels = |
| (p->timings.hactive / (p->timings.pixel_repeat + 1)) * (p->timings.venc_pixel_repeat + 1); |
| |
| venc_fp = |
| (p->timings.hfront / (p->timings.pixel_repeat + 1)) * (p->timings.venc_pixel_repeat + 1); |
| |
| venc_hsync = |
| (p->timings.hsync / (p->timings.pixel_repeat + 1)) * (p->timings.venc_pixel_repeat + 1); |
| |
| SET_BIT32(VPU, VPU_ENCP_VIDEO_MODE, 1, 14, 1); // DE Signal polarity |
| WRITE32_REG(VPU, VPU_ENCP_VIDEO_HAVON_BEGIN, p->timings.hsync + p->timings.hback); |
| WRITE32_REG(VPU, VPU_ENCP_VIDEO_HAVON_END, |
| p->timings.hsync + p->timings.hback + p->timings.hactive - 1); |
| |
| WRITE32_REG(VPU, VPU_ENCP_VIDEO_VAVON_BLINE, p->timings.vsync + p->timings.vback); |
| WRITE32_REG(VPU, VPU_ENCP_VIDEO_VAVON_ELINE, |
| p->timings.vsync + p->timings.vback + p->timings.vactive - 1); |
| |
| WRITE32_REG(VPU, VPU_ENCP_VIDEO_HSO_BEGIN, 0); |
| WRITE32_REG(VPU, VPU_ENCP_VIDEO_HSO_END, p->timings.hsync); |
| |
| WRITE32_REG(VPU, VPU_ENCP_VIDEO_VSO_BLINE, 0); |
| WRITE32_REG(VPU, VPU_ENCP_VIDEO_VSO_ELINE, p->timings.vsync); |
| |
| // Below calculations assume no pixel repeat and progressive mode. |
| // HActive Start/End |
| h_begin = p->timings.hsync + p->timings.hback + 2; // 2 is the HDMI Latency |
| |
| h_begin = h_begin % venc_total_pixels; // wrap around if needed |
| h_end = h_begin + venc_active_pixels; |
| h_end = h_end % venc_total_pixels; // wrap around if needed |
| WRITE32_REG(VPU, VPU_ENCP_DE_H_BEGIN, h_begin); |
| WRITE32_REG(VPU, VPU_ENCP_DE_H_END, h_end); |
| |
| // VActive Start/End |
| v_begin = p->timings.vsync + p->timings.vback; |
| v_end = v_begin + active_lines; |
| WRITE32_REG(VPU, VPU_ENCP_DE_V_BEGIN_EVEN, v_begin); |
| WRITE32_REG(VPU, VPU_ENCP_DE_V_END_EVEN, v_end); |
| |
| if (p->timings.interlace_mode) { |
| // TODO: Add support for interlace mode |
| // We should not even get here |
| DISP_ERROR("Interlace mode not supported\n"); |
| } |
| |
| // HSync Timings |
| hs_begin = h_end + venc_fp; |
| if (hs_begin >= venc_total_pixels) { |
| hs_begin -= venc_total_pixels; |
| vsync_adjust = 1; |
| } |
| |
| hs_end = hs_begin + venc_hsync; |
| hs_end = hs_end % venc_total_pixels; |
| WRITE32_REG(VPU, VPU_ENCP_DVI_HSO_BEGIN, hs_begin); |
| WRITE32_REG(VPU, VPU_ENCP_DVI_HSO_END, hs_end); |
| |
| // VSync Timings |
| if (v_begin >= (p->timings.vback + p->timings.vsync + (1 - vsync_adjust))) { |
| vs_begin = v_begin - p->timings.vback - p->timings.vsync - (1 - vsync_adjust); |
| } else { |
| vs_begin = |
| p->timings.vtotal + v_begin - p->timings.vback - p->timings.vsync - (1 - vsync_adjust); |
| } |
| vs_end = vs_begin + p->timings.vsync; |
| vs_end = vs_end % total_lines; |
| |
| WRITE32_REG(VPU, VPU_ENCP_DVI_VSO_BLINE_EVN, vs_begin); |
| WRITE32_REG(VPU, VPU_ENCP_DVI_VSO_ELINE_EVN, vs_end); |
| WRITE32_REG(VPU, VPU_ENCP_DVI_VSO_BEGIN_EVN, hs_begin); |
| WRITE32_REG(VPU, VPU_ENCP_DVI_VSO_END_EVN, hs_begin); |
| |
| WRITE32_REG(VPU, VPU_HDMI_SETTING, 0); |
| // hsync, vsync active high. output CbYCr (GRB) |
| // TODO: output desired format is hardcoded here to CbYCr (GRB) |
| WRITE32_REG(VPU, VPU_HDMI_SETTING, (p->timings.hpol << 2) | (p->timings.vpol << 3) | (4 << 5)); |
| |
| if (p->timings.venc_pixel_repeat) { |
| SET_BIT32(VPU, VPU_HDMI_SETTING, 1, 8, 1); |
| } |
| |
| // Select ENCP data to HDMI |
| VpuHdmiSettingReg::Get().ReadFrom(&(*vpu_mmio_)).set_src_sel(2).WriteTo(&(*vpu_mmio_)); |
| |
| DISP_INFO("done\n"); |
| } |
| |
| void AmlHdmiHost::ConfigPhy() { |
| auto* p = &p_; |
| |
| HhiHdmiPhyCntl0Reg::Get().FromValue(0).WriteTo(&(*hhi_mmio_)); |
| HhiHdmiPhyCntl1Reg::Get() |
| .ReadFrom(&(*hhi_mmio_)) |
| .set_hdmi_tx_phy_soft_reset(0) |
| .set_hdmi_tx_phy_clk_en(0) |
| .set_hdmi_fifo_enable(0) |
| .set_hdmi_fifo_wr_enable(0) |
| .set_msb_lsb_swap(0) |
| .set_bit_invert(0) |
| .set_ch0_swap(0) |
| .set_ch1_swap(1) |
| .set_ch2_swap(2) |
| .set_ch3_swap(3) |
| .set_new_prbs_en(0) |
| .set_new_prbs_sel(0) |
| .set_new_prbs_prbsmode(0) |
| .set_new_prbs_mode(0) |
| .WriteTo(&(*hhi_mmio_)); |
| |
| HhiHdmiPhyCntl1Reg::Get() |
| .ReadFrom(&(*hhi_mmio_)) |
| .set_hdmi_tx_phy_soft_reset(1) |
| .set_hdmi_tx_phy_clk_en(1) |
| .set_hdmi_fifo_enable(1) |
| .set_hdmi_fifo_wr_enable(1) |
| .WriteTo(&(*hhi_mmio_)); |
| usleep(2); |
| HhiHdmiPhyCntl1Reg::Get() |
| .ReadFrom(&(*hhi_mmio_)) |
| .set_hdmi_tx_phy_soft_reset(0) |
| .set_hdmi_tx_phy_clk_en(1) |
| .set_hdmi_fifo_enable(1) |
| .set_hdmi_fifo_wr_enable(1) |
| .WriteTo(&(*hhi_mmio_)); |
| usleep(2); |
| HhiHdmiPhyCntl1Reg::Get() |
| .ReadFrom(&(*hhi_mmio_)) |
| .set_hdmi_tx_phy_soft_reset(1) |
| .set_hdmi_tx_phy_clk_en(1) |
| .set_hdmi_fifo_enable(1) |
| .set_hdmi_fifo_wr_enable(1) |
| .WriteTo(&(*hhi_mmio_)); |
| usleep(2); |
| HhiHdmiPhyCntl1Reg::Get() |
| .ReadFrom(&(*hhi_mmio_)) |
| .set_hdmi_tx_phy_soft_reset(0) |
| .set_hdmi_tx_phy_clk_en(1) |
| .set_hdmi_fifo_enable(1) |
| .set_hdmi_fifo_wr_enable(1) |
| .WriteTo(&(*hhi_mmio_)); |
| usleep(2); |
| |
| switch (p->phy_mode) { |
| case 1: /* 5.94Gbps, 3.7125Gbsp */ |
| HhiHdmiPhyCntl0Reg::Get().FromValue(0).set_hdmi_ctl1(0x37eb).set_hdmi_ctl2(0x65c4).WriteTo( |
| &(*hhi_mmio_)); |
| HhiHdmiPhyCntl3Reg::Get().FromValue(0x2ab0ff3b).WriteTo(&(*hhi_mmio_)); |
| HhiHdmiPhyCntl5Reg::Get().FromValue(0x0000080b).WriteTo(&(*hhi_mmio_)); |
| break; |
| case 2: /* 2.97Gbps */ |
| HhiHdmiPhyCntl0Reg::Get().FromValue(0).set_hdmi_ctl1(0x33eb).set_hdmi_ctl2(0x6262).WriteTo( |
| &(*hhi_mmio_)); |
| HhiHdmiPhyCntl3Reg::Get().FromValue(0x2ab0ff3b).WriteTo(&(*hhi_mmio_)); |
| HhiHdmiPhyCntl5Reg::Get().FromValue(0x00000003).WriteTo(&(*hhi_mmio_)); |
| break; |
| default: /* 1.485Gbps, and below */ |
| HhiHdmiPhyCntl0Reg::Get().FromValue(0).set_hdmi_ctl1(0x33eb).set_hdmi_ctl2(0x4242).WriteTo( |
| &(*hhi_mmio_)); |
| HhiHdmiPhyCntl3Reg::Get().FromValue(0x2ab0ff3b).WriteTo(&(*hhi_mmio_)); |
| HhiHdmiPhyCntl5Reg::Get().FromValue(0x00000003).WriteTo(&(*hhi_mmio_)); |
| break; |
| } |
| usleep(20); |
| DISP_INFO("done!\n"); |
| } |
| |
| } // namespace amlogic_display |