| // 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 "src/graphics/display/drivers/amlogic-display/clock.h" |
| |
| #include <fidl/fuchsia.hardware.platform.device/cpp/wire.h> |
| #include <lib/mmio/mmio-buffer.h> |
| #include <lib/zx/result.h> |
| #include <zircon/assert.h> |
| |
| #include <cstdint> |
| |
| #include <fbl/alloc_checker.h> |
| |
| #include "src/graphics/display/drivers/amlogic-display/board-resources.h" |
| #include "src/graphics/display/drivers/amlogic-display/clock-regs.h" |
| #include "src/graphics/display/drivers/amlogic-display/common.h" |
| #include "src/graphics/display/drivers/amlogic-display/dsi.h" |
| #include "src/graphics/display/drivers/amlogic-display/fixed-point-util.h" |
| #include "src/graphics/display/drivers/amlogic-display/hhi-regs.h" |
| #include "src/graphics/display/drivers/amlogic-display/vpu-regs.h" |
| #include "src/graphics/display/lib/api-types-cpp/display-timing.h" |
| #include "src/graphics/display/lib/driver-framework-migration-utils/logging/zxlogf.h" |
| |
| namespace amlogic_display { |
| |
| Clock::Clock(fdf::MmioBuffer vpu_mmio, fdf::MmioBuffer hhi_mmio, bool clock_enabled) |
| : vpu_mmio_(std::move(vpu_mmio)), |
| hhi_mmio_(std::move(hhi_mmio)), |
| clock_enabled_(clock_enabled) {} |
| |
| // static |
| LcdTiming Clock::CalculateLcdTiming(const display::DisplayTiming& d) { |
| LcdTiming out; |
| // Calculate and store DataEnable horizontal and vertical start/stop times |
| const uint32_t de_hstart = d.horizontal_total_px() - d.horizontal_active_px - 1; |
| const uint32_t de_vstart = d.vertical_total_lines() - d.vertical_active_lines; |
| out.vid_pixel_on = de_hstart; |
| out.vid_line_on = de_vstart; |
| |
| // Calculate and Store HSync horizontal and vertical start/stop times |
| const uint32_t hstart = (de_hstart + d.horizontal_total_px() - d.horizontal_back_porch_px - |
| d.horizontal_sync_width_px) % |
| d.horizontal_total_px(); |
| const uint32_t hend = |
| (de_hstart + d.horizontal_total_px() - d.horizontal_back_porch_px) % d.horizontal_total_px(); |
| out.hs_hs_addr = hstart; |
| out.hs_he_addr = hend; |
| |
| // Calculate and Store VSync horizontal and vertical start/stop times |
| out.vs_hs_addr = (hstart + d.horizontal_total_px()) % d.horizontal_total_px(); |
| out.vs_he_addr = out.vs_hs_addr; |
| const uint32_t vstart = (de_vstart + d.vertical_total_lines() - d.vertical_back_porch_lines - |
| d.vertical_sync_width_lines) % |
| d.vertical_total_lines(); |
| const uint32_t vend = (de_vstart + d.vertical_total_lines() - d.vertical_back_porch_lines) % |
| d.vertical_total_lines(); |
| out.vs_vs_addr = vstart; |
| out.vs_ve_addr = vend; |
| return out; |
| } |
| |
| zx::result<> Clock::WaitForHdmiPllToLock() { |
| uint32_t pll_lock; |
| |
| constexpr int kMaxPllLockAttempt = 3; |
| for (int lock_attempts = 0; lock_attempts < kMaxPllLockAttempt; lock_attempts++) { |
| zxlogf(TRACE, "Waiting for PLL Lock: (%d/3).", lock_attempts + 1); |
| |
| // The configurations used in retries are from Amlogic-provided code which |
| // is undocumented. |
| if (lock_attempts == 1) { |
| hhi_mmio_.Write32( |
| SetFieldValue32(hhi_mmio_.Read32(HHI_HDMI_PLL_CNTL3), /*field_begin_bit=*/31, |
| /*field_size_bits=*/1, /*field_value=*/1), |
| HHI_HDMI_PLL_CNTL3); |
| } else if (lock_attempts == 2) { |
| hhi_mmio_.Write32(0x55540000, HHI_HDMI_PLL_CNTL6); // more magic |
| } |
| |
| int retries = 1000; |
| while ((pll_lock = GetFieldValue32(hhi_mmio_.Read32(HHI_HDMI_PLL_CNTL0), |
| /*field_begin_bit=*/LCD_PLL_LOCK_HPLL_G12A, |
| /*field_size_bits=*/1)) != 1 && |
| retries--) { |
| zx_nanosleep(zx_deadline_after(ZX_USEC(50))); |
| } |
| if (pll_lock) { |
| return zx::ok(); |
| } |
| } |
| |
| zxlogf(ERROR, "Failed to lock HDMI PLL after %d attempts.", kMaxPllLockAttempt); |
| return zx::error(ZX_ERR_UNAVAILABLE); |
| } |
| |
| // static |
| zx::result<HdmiPllConfigForMipiDsi> Clock::GenerateHPLL( |
| int64_t pixel_clock_frequency_hz, int64_t maximum_per_data_lane_bit_per_second) { |
| HdmiPllConfigForMipiDsi pll_cfg; |
| // Requested Pixel clock |
| if (pixel_clock_frequency_hz > kMaxPixelClockFrequencyHz) { |
| zxlogf(ERROR, "Pixel clock out of range (%" PRId64 " Hz)", pixel_clock_frequency_hz); |
| return zx::error(ZX_ERR_OUT_OF_RANGE); |
| } |
| |
| constexpr uint32_t kMinClockFactor = 1u; |
| constexpr uint32_t kMaxClockFactor = 255u; |
| |
| // If clock factor is not specified in display panel configuration, the driver |
| // will find the first valid clock factor in between kMinClockFactor and |
| // kMaxClockFactor (both inclusive). |
| uint32_t clock_factor_min = kMinClockFactor; |
| uint32_t clock_factor_max = kMaxClockFactor; |
| |
| for (uint32_t clock_factor = clock_factor_min; clock_factor <= clock_factor_max; clock_factor++) { |
| pll_cfg.clock_factor = clock_factor; |
| |
| // Desired PLL Frequency based on pixel clock needed |
| const int64_t requested_pll_frequency_hz = pixel_clock_frequency_hz * pll_cfg.clock_factor; |
| |
| // Make sure all clocks are within range |
| // If these values are not within range, we will not have a valid display |
| const int64_t dphy_data_lane_bit_rate_max_hz = maximum_per_data_lane_bit_per_second; |
| const int64_t dphy_data_lane_bit_rate_min_hz = |
| dphy_data_lane_bit_rate_max_hz - pixel_clock_frequency_hz; |
| if ((requested_pll_frequency_hz < dphy_data_lane_bit_rate_min_hz) || |
| (requested_pll_frequency_hz > dphy_data_lane_bit_rate_max_hz)) { |
| zxlogf(TRACE, "Calculated clocks out of range for xd = %u, skipped", clock_factor); |
| continue; |
| } |
| |
| // Now that we have valid frequency ranges, let's calculated all the PLL-related |
| // multipliers/dividers |
| // [fin] * [m/n] = [voltage_controlled_oscillator_output_frequency_hz] |
| // [voltage_controlled_oscillator_output_frequency_khz] / [output_divider1] |
| // / [output_divider2] / [output_divider3] |
| // = requested_pll_frequency |
| // |
| // The PLL output is used for the MIPI D-PHY clock lane; its frequency |
| // must be equal to the MIPI D-PHY data lane bit rate. |
| int32_t output_divider3 = (1 << (MAX_OD_SEL - 1)); |
| while (output_divider3 != 0) { |
| const int64_t output_divider3_input_frequency_hz = |
| requested_pll_frequency_hz * output_divider3; |
| int output_divider2 = output_divider3; |
| while (output_divider2 != 0) { |
| const int64_t output_divider2_input_frequency_hz = |
| output_divider3_input_frequency_hz * output_divider2; |
| int32_t output_divider1 = output_divider2; |
| while (output_divider1 != 0) { |
| const int64_t output_divider1_input_frequency_hz = |
| output_divider2_input_frequency_hz * output_divider1; |
| const int64_t voltage_controlled_oscillator_output_frequency_hz = |
| output_divider1_input_frequency_hz; |
| |
| if ((voltage_controlled_oscillator_output_frequency_hz >= |
| kMinVoltageControlledOscillatorFrequencyHz) && |
| (voltage_controlled_oscillator_output_frequency_hz <= |
| kMaxVoltageControlledOscillatorFrequencyHz)) { |
| // within range! |
| pll_cfg.output_divider1_selection = output_divider1 >> 1; |
| pll_cfg.output_divider2_selection = output_divider2 >> 1; |
| pll_cfg.output_divider3_selection = output_divider3 >> 1; |
| pll_cfg.pll_frequency_hz = requested_pll_frequency_hz; |
| zxlogf(TRACE, "od1=%d, od2=%d, od3=%d", (output_divider1 >> 1), (output_divider2 >> 1), |
| (output_divider3 >> 1)); |
| zxlogf(TRACE, "pll_fvco=%" PRId64, voltage_controlled_oscillator_output_frequency_hz); |
| pll_cfg.pll_voltage_controlled_oscillator_output_frequency_hz = |
| voltage_controlled_oscillator_output_frequency_hz; |
| |
| // For simplicity, assume pll_divider = 1. |
| pll_cfg.pll_divider = 1; |
| |
| // Calculate pll_multiplier such that |
| // kExternalOscillatorFrequencyHz x pll_multiplier = |
| // voltage_controlled_oscillator_output_frequency_hz |
| pll_cfg.pll_multiplier_integer = static_cast<int32_t>( |
| voltage_controlled_oscillator_output_frequency_hz / kExternalOscillatorFrequencyHz); |
| pll_cfg.pll_multiplier_fraction = (voltage_controlled_oscillator_output_frequency_hz % |
| kExternalOscillatorFrequencyHz) * |
| PLL_FRAC_RANGE / kExternalOscillatorFrequencyHz; |
| |
| zxlogf(TRACE, "m=%d, n=%d, frac=0x%x", pll_cfg.pll_multiplier_integer, |
| pll_cfg.pll_divider, pll_cfg.pll_multiplier_fraction); |
| pll_cfg.dphy_data_lane_bits_per_second = pll_cfg.pll_frequency_hz; // Hz |
| |
| return zx::ok(std::move(pll_cfg)); |
| } |
| output_divider1 >>= 1; |
| } |
| output_divider2 >>= 1; |
| } |
| output_divider3 >>= 1; |
| } |
| } |
| |
| zxlogf(ERROR, "Could not generate correct PLL values for: "); |
| zxlogf(ERROR, " pixel_clock_frequency_hz = %" PRId64, pixel_clock_frequency_hz); |
| zxlogf(ERROR, " max_per_data_lane_bit_rate_hz = %" PRId64, maximum_per_data_lane_bit_per_second); |
| return zx::error(ZX_ERR_INTERNAL); |
| } |
| |
| void Clock::Disable() { |
| if (!clock_enabled_) { |
| return; |
| } |
| vpu_mmio_.Write32(0, ENCL_VIDEO_EN); |
| |
| VideoClockOutputControl::Get() |
| .ReadFrom(&hhi_mmio_) |
| .set_encoder_lvds_enabled(false) |
| .WriteTo(&hhi_mmio_); |
| VideoClock2Control::Get() |
| .ReadFrom(&hhi_mmio_) |
| .set_div1_enabled(false) |
| .set_div2_enabled(false) |
| .set_div4_enabled(false) |
| .set_div6_enabled(false) |
| .set_div12_enabled(false) |
| .WriteTo(&hhi_mmio_); |
| VideoClock2Control::Get().ReadFrom(&hhi_mmio_).set_clock_enabled(false).WriteTo(&hhi_mmio_); |
| |
| // disable pll |
| hhi_mmio_.Write32(SetFieldValue32(hhi_mmio_.Read32(HHI_HDMI_PLL_CNTL0), |
| /*field_begin_bit=*/LCD_PLL_EN_HPLL_G12A, |
| /*field_size_bits=*/1, /*field_value=*/0), |
| HHI_HDMI_PLL_CNTL0); |
| clock_enabled_ = false; |
| } |
| |
| zx::result<> Clock::Enable(const PanelConfig& panel_config) { |
| if (clock_enabled_) { |
| return zx::ok(); |
| } |
| |
| // Populate internal LCD timing structure based on predefined tables |
| lcd_timing_ = CalculateLcdTiming(panel_config.display_timing); |
| zx::result<HdmiPllConfigForMipiDsi> pll_result = |
| GenerateHPLL(panel_config.display_timing.pixel_clock_frequency_hz, |
| panel_config.maximum_per_data_lane_bit_per_second()); |
| if (pll_result.is_error()) { |
| zxlogf(ERROR, "Failed to generate HDMI PLL and Video clock tree configuration: %s", |
| pll_result.status_string()); |
| return pll_result.take_error(); |
| } |
| pll_cfg_ = std::move(pll_result).value(); |
| |
| uint32_t regVal; |
| bool useFrac = pll_cfg_.pll_multiplier_fraction != 0; |
| |
| regVal = ((1 << LCD_PLL_EN_HPLL_G12A) | (1 << LCD_PLL_OUT_GATE_CTRL_G12A) | // clk out gate |
| (pll_cfg_.pll_divider << LCD_PLL_N_HPLL_G12A) | |
| (pll_cfg_.pll_multiplier_integer << LCD_PLL_M_HPLL_G12A) | |
| (pll_cfg_.output_divider1_selection << LCD_PLL_OD1_HPLL_G12A) | |
| (pll_cfg_.output_divider2_selection << LCD_PLL_OD2_HPLL_G12A) | |
| (pll_cfg_.output_divider3_selection << LCD_PLL_OD3_HPLL_G12A) | |
| (useFrac ? (1 << 27) : (0 << 27))); |
| hhi_mmio_.Write32(regVal, HHI_HDMI_PLL_CNTL0); |
| |
| hhi_mmio_.Write32(pll_cfg_.pll_multiplier_fraction, HHI_HDMI_PLL_CNTL1); |
| hhi_mmio_.Write32(0x00, HHI_HDMI_PLL_CNTL2); |
| // Magic numbers from U-Boot. |
| hhi_mmio_.Write32(useFrac ? 0x6a285c00 : 0x48681c00, HHI_HDMI_PLL_CNTL3); |
| hhi_mmio_.Write32(useFrac ? 0x65771290 : 0x33771290, HHI_HDMI_PLL_CNTL4); |
| hhi_mmio_.Write32(0x39272000, HHI_HDMI_PLL_CNTL5); |
| hhi_mmio_.Write32(useFrac ? 0x56540000 : 0x56540000, HHI_HDMI_PLL_CNTL6); |
| |
| // reset dpll |
| hhi_mmio_.Write32(SetFieldValue32(hhi_mmio_.Read32(HHI_HDMI_PLL_CNTL0), |
| /*field_begin_bit=*/LCD_PLL_RST_HPLL_G12A, |
| /*field_size_bits=*/1, /*field_value=*/1), |
| HHI_HDMI_PLL_CNTL0); |
| zx_nanosleep(zx_deadline_after(ZX_USEC(100))); |
| // release from reset |
| hhi_mmio_.Write32(SetFieldValue32(hhi_mmio_.Read32(HHI_HDMI_PLL_CNTL0), |
| /*field_begin_bit=*/LCD_PLL_RST_HPLL_G12A, |
| /*field_size_bits=*/1, /*field_value=*/0), |
| HHI_HDMI_PLL_CNTL0); |
| |
| zx_nanosleep(zx_deadline_after(ZX_USEC(50))); |
| zx::result<> wait_for_pll_lock_result = WaitForHdmiPllToLock(); |
| if (!wait_for_pll_lock_result.is_ok()) { |
| zxlogf(ERROR, "Failed to lock HDMI PLL: %s", wait_for_pll_lock_result.status_string()); |
| return wait_for_pll_lock_result.take_error(); |
| } |
| |
| // Disable Video Clock mux 2 since we are changing its input selection. |
| VideoClock2Control::Get().ReadFrom(&hhi_mmio_).set_clock_enabled(false).WriteTo(&hhi_mmio_); |
| zx_nanosleep(zx_deadline_after(ZX_USEC(5))); |
| |
| // Sets the HDMI clock tree frequency division ratio to 1. |
| |
| // Disable the HDMI clock tree output |
| HdmiClockTreeControl hdmi_clock_tree_control = HdmiClockTreeControl::Get().ReadFrom(&hhi_mmio_); |
| hdmi_clock_tree_control.set_clock_output_enabled(false).WriteTo(&hhi_mmio_); |
| |
| hdmi_clock_tree_control.set_preset_pattern_update_enabled(false).WriteTo(&hhi_mmio_); |
| hdmi_clock_tree_control.SetFrequencyDividerRatio(ToU28_4(1.0)).WriteTo(&hhi_mmio_); |
| |
| // Enable the final output clock |
| hdmi_clock_tree_control.set_clock_output_enabled(true).WriteTo(&hhi_mmio_); |
| |
| // Enable DSI measure clocks. |
| VideoInputMeasureClockControl::Get() |
| .ReadFrom(&hhi_mmio_) |
| .set_dsi_measure_clock_selection( |
| VideoInputMeasureClockControl::ClockSource::kExternalOscillator24Mhz) |
| .WriteTo(&hhi_mmio_); |
| VideoInputMeasureClockControl::Get() |
| .ReadFrom(&hhi_mmio_) |
| .SetDsiMeasureClockDivider(1) |
| .WriteTo(&hhi_mmio_); |
| VideoInputMeasureClockControl::Get() |
| .ReadFrom(&hhi_mmio_) |
| .set_dsi_measure_clock_enabled(true) |
| .WriteTo(&hhi_mmio_); |
| |
| // Use Video PLL (vid_pll) as MIPI_DSY PHY clock source. |
| MipiDsiPhyClockControl::Get() |
| .ReadFrom(&hhi_mmio_) |
| .set_clock_source(MipiDsiPhyClockControl::ClockSource::kVideoPll) |
| .WriteTo(&hhi_mmio_); |
| // Enable MIPI-DSY PHY clock. |
| MipiDsiPhyClockControl::Get().ReadFrom(&hhi_mmio_).set_enabled(true).WriteTo(&hhi_mmio_); |
| // Set divider to 1. |
| // TODO(https://fxbug.dev/42082049): This should occur before enabling the clock. |
| MipiDsiPhyClockControl::Get().ReadFrom(&hhi_mmio_).SetDivider(1).WriteTo(&hhi_mmio_); |
| |
| // Set the Video clock 2 divider. |
| VideoClock2Divider::Get() |
| .ReadFrom(&hhi_mmio_) |
| .SetDivider2(pll_cfg_.clock_factor) |
| .WriteTo(&hhi_mmio_); |
| zx_nanosleep(zx_deadline_after(ZX_USEC(5))); |
| |
| VideoClock2Control::Get() |
| .ReadFrom(&hhi_mmio_) |
| .set_mux_source(VideoClockMuxSource::kVideoPll) |
| .WriteTo(&hhi_mmio_); |
| VideoClock2Control::Get().ReadFrom(&hhi_mmio_).set_clock_enabled(true).WriteTo(&hhi_mmio_); |
| zx_nanosleep(zx_deadline_after(ZX_USEC(2))); |
| |
| // Select video clock 2 for ENCL clock. |
| VideoClock2Divider::Get() |
| .ReadFrom(&hhi_mmio_) |
| .set_encl_clock_selection(EncoderClockSource::kVideoClock2) |
| .WriteTo(&hhi_mmio_); |
| // Enable video clock 2 divider. |
| VideoClock2Divider::Get().ReadFrom(&hhi_mmio_).set_divider_enabled(true).WriteTo(&hhi_mmio_); |
| zx_nanosleep(zx_deadline_after(ZX_USEC(5))); |
| |
| VideoClock2Control::Get().ReadFrom(&hhi_mmio_).set_div1_enabled(true).WriteTo(&hhi_mmio_); |
| VideoClock2Control::Get().ReadFrom(&hhi_mmio_).set_soft_reset(true).WriteTo(&hhi_mmio_); |
| zx_nanosleep(zx_deadline_after(ZX_USEC(10))); |
| VideoClock2Control::Get().ReadFrom(&hhi_mmio_).set_soft_reset(false).WriteTo(&hhi_mmio_); |
| zx_nanosleep(zx_deadline_after(ZX_USEC(5))); |
| |
| // Enable ENCL clock output. |
| VideoClockOutputControl::Get() |
| .ReadFrom(&hhi_mmio_) |
| .set_encoder_lvds_enabled(true) |
| .WriteTo(&hhi_mmio_); |
| |
| zx_nanosleep(zx_deadline_after(ZX_MSEC(10))); |
| |
| vpu_mmio_.Write32(0, ENCL_VIDEO_EN); |
| |
| // Connect both VIUs (Video Input Units) to the LCD Encoder. |
| VideoInputUnitEncoderMuxControl::Get() |
| .ReadFrom(&vpu_mmio_) |
| .set_vsync_shared_by_viu_blocks(false) |
| .set_viu1_encoder_selection(VideoInputUnitEncoderMuxControl::Encoder::kLcd) |
| .set_viu2_encoder_selection(VideoInputUnitEncoderMuxControl::Encoder::kLcd) |
| .WriteTo(&vpu_mmio_); |
| |
| // Undocumented registers below |
| vpu_mmio_.Write32(0x8000, ENCL_VIDEO_MODE); // bit[15] shadown en |
| vpu_mmio_.Write32(0x0418, ENCL_VIDEO_MODE_ADV); // Sampling rate: 1 |
| |
| // bypass filter -- Undocumented registers |
| const display::DisplayTiming& display_timing = panel_config.display_timing; |
| vpu_mmio_.Write32(0x1000, ENCL_VIDEO_FILT_CTRL); |
| vpu_mmio_.Write32(display_timing.horizontal_total_px() - 1, ENCL_VIDEO_MAX_PXCNT); |
| vpu_mmio_.Write32(display_timing.vertical_total_lines() - 1, ENCL_VIDEO_MAX_LNCNT); |
| vpu_mmio_.Write32(lcd_timing_.vid_pixel_on, ENCL_VIDEO_HAVON_BEGIN); |
| vpu_mmio_.Write32(display_timing.horizontal_active_px - 1 + lcd_timing_.vid_pixel_on, |
| ENCL_VIDEO_HAVON_END); |
| vpu_mmio_.Write32(lcd_timing_.vid_line_on, ENCL_VIDEO_VAVON_BLINE); |
| vpu_mmio_.Write32(display_timing.vertical_active_lines - 1 + lcd_timing_.vid_line_on, |
| ENCL_VIDEO_VAVON_ELINE); |
| vpu_mmio_.Write32(lcd_timing_.hs_hs_addr, ENCL_VIDEO_HSO_BEGIN); |
| vpu_mmio_.Write32(lcd_timing_.hs_he_addr, ENCL_VIDEO_HSO_END); |
| vpu_mmio_.Write32(lcd_timing_.vs_hs_addr, ENCL_VIDEO_VSO_BEGIN); |
| vpu_mmio_.Write32(lcd_timing_.vs_he_addr, ENCL_VIDEO_VSO_END); |
| vpu_mmio_.Write32(lcd_timing_.vs_vs_addr, ENCL_VIDEO_VSO_BLINE); |
| vpu_mmio_.Write32(lcd_timing_.vs_ve_addr, ENCL_VIDEO_VSO_ELINE); |
| vpu_mmio_.Write32(3, ENCL_VIDEO_RGBIN_CTRL); |
| vpu_mmio_.Write32(1, ENCL_VIDEO_EN); |
| |
| vpu_mmio_.Write32(0, L_RGB_BASE_ADDR); |
| vpu_mmio_.Write32(0x400, L_RGB_COEFF_ADDR); |
| vpu_mmio_.Write32(0x400, L_DITH_CNTL_ADDR); |
| |
| // The driver behavior here deviates from the Amlogic-provided code. |
| // |
| // The Amlogic-provided code sets up the timing controller (TCON) within the |
| // LCD Encoder to generate Display Enable (DE), Horizontal Sync (HSYNC) and |
| // Vertical Sync (VSYNC) timing signals. |
| // |
| // These signals are useful for LVDS or TTL LCD interfaces, but not for the |
| // MIPI-DSI interface. The MIPI-DSI Host Controller IP block always sets the |
| // output timings using the values from its own control registers, and it |
| // doesn't use the outputs from the timing controller. |
| // |
| // Therefore, this driver doesn't set the timing controller registers and |
| // keeps the timing controller disabled. This was tested on Nelson (S905D3) |
| // and Khadas VIM3 (A311D) boards. |
| |
| vpu_mmio_.Write32(vpu_mmio_.Read32(VPP_MISC) & ~(VPP_OUT_SATURATE), VPP_MISC); |
| |
| // Ready to be used |
| clock_enabled_ = true; |
| return zx::ok(); |
| } |
| |
| void Clock::SetVideoOn(bool on) { vpu_mmio_.Write32(on, ENCL_VIDEO_EN); } |
| |
| // static |
| zx::result<std::unique_ptr<Clock>> Clock::Create( |
| fidl::UnownedClientEnd<fuchsia_hardware_platform_device::Device> platform_device, |
| bool already_enabled) { |
| ZX_DEBUG_ASSERT(platform_device.is_valid()); |
| |
| zx::result<fdf::MmioBuffer> vpu_mmio_result = MapMmio(MmioResourceIndex::kVpu, platform_device); |
| if (vpu_mmio_result.is_error()) { |
| return vpu_mmio_result.take_error(); |
| } |
| fdf::MmioBuffer vpu_mmio = std::move(vpu_mmio_result).value(); |
| |
| zx::result<fdf::MmioBuffer> hhi_mmio_result = MapMmio(MmioResourceIndex::kHhi, platform_device); |
| if (hhi_mmio_result.is_error()) { |
| return hhi_mmio_result.take_error(); |
| } |
| fdf::MmioBuffer hhi_mmio = std::move(hhi_mmio_result).value(); |
| |
| fbl::AllocChecker alloc_checker; |
| auto clock = |
| fbl::make_unique_checked<Clock>(&alloc_checker, std::move(vpu_mmio), std::move(hhi_mmio), |
| /*clock_enabled=*/already_enabled); |
| if (!alloc_checker.check()) { |
| zxlogf(ERROR, "Failed to allocate memory for Clock"); |
| return zx::error(ZX_ERR_NO_MEMORY); |
| } |
| |
| return zx::ok(std::move(clock)); |
| } |
| |
| } // namespace amlogic_display |