blob: 1a08c8d19233f9a4bfe95dbf63c6c1d281403c4f [file] [log] [blame]
// Copyright 2023 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.
#ifndef SRC_GRAPHICS_DISPLAY_DRIVERS_AMLOGIC_DISPLAY_CLOCK_REGS_H_
#define SRC_GRAPHICS_DISPLAY_DRIVERS_AMLOGIC_DISPLAY_CLOCK_REGS_H_
#include <lib/stdcompat/span.h>
#include <zircon/assert.h>
#include <cstdint>
#include <hwreg/bitfields.h>
#include "src/graphics/display/drivers/amlogic-display/fixed-point-util.h"
namespace amlogic_display {
// # Amlogic Clock Subsystem
//
// On Amlogic SoCs, the clock subsystem consists of **phased-lock loops (PLLs)**
// generating clock signals using oscillators, and **clock trees** which are
// exclusively made up of digital logic (such as muxes and frequency dividers).
//
// ## Video Clock Tree
//
// The **video clock tree** is a set of muxes and frequency dividers providing
// clocks for encoders, HDMI / DSI transmitter, display timing controllers,
// and video digital-analog converters.
//
// The video clock tree takes the following clock sources:
//
// - "vid_pll"
// Video "PLL", which is actually an output signal of the HDMI clock tree
// taking the HDMI PLL as its source.
// * A311D Datasheet, Section 8.7.1.3 HDMI Clock Tree, Page 113
// * S905D2 Datasheet, Section 6.6.2.3 HDMI Clock Tree, Page 97
// * S905D3 Datasheet, Section 6.7.2.3 HDMI Clock Tree, Page 97
// - "gp0_pll"
// General-purpose PLL 0.
// * A311D Datasheet, Section 8.7.2.3 GP0 PLL, Page 117
// * S905D2 Datasheet, Section 6.6.3.3 GP0 PLL, Page 101
// * S905D3 Datasheet, Section 6.7.3.2 GP0 PLL, Page 100
// - "hifi_pll"
// HiFi PLL.
// * A311D Datasheet, Section 8.7.2.5 HIFI PLL, Page 118
// * S905D2 Datasheet, Section 6.6.3.5 HIFI PLL, Page 102
// * S905D3 Datasheet, Section 6.7.3.3 HIFI PLL, Page 101
// - "mp1_clk"
// Also known as "MPLL1", "MPLL_DDS_CLK1". The fixed-frequency PLL (MPLL,
// also known as FIX_PLL) is divided by a programmable frequency divider,
// providing a clock with frequency of up to 500MHz.
// References for all MPLL / FIX_PLL outputs:
// * A311D Datasheet, Section 8.7.2.5 MPLL (Fixed PLL), Page 119
// * S905D2 Datasheet, Section 6.6.3.6 MPLL (Fixed PLL), Page 103
// * S905D3 Datasheet, Section 6.7.3.7 MPLL Page 104
// - "fclk_div3"
// Also known as "MPLL_CLK_OUT_DIV3". The fixed-frequency MPLL is divided by a
// fixed-value frequency divider, providing a 666MHz fixed-frequency clock.
// - "fclk_div4"
// Also known as "MPLL_CLK_OUT_DIV4". The fixed-frequency MPLL is divided by a
// fixed-value frequency divider, providing a 500MHz fixed-frequency clock.
// - "fclk_div5"
// Also known as "MPLL_CLK_OUT_DIV5". The fixed-frequency MPLL is divided by a
// fixed-value frequency divider, providing a 400MHz fixed-frequency clock.
// - "fclk_div7"
// Also known as "MPLL_CLK_OUT_DIV7". The fixed-frequency MPLL is divided by a
// fixed-value frequency divider, providing a 285.7MHz fixed-frequency clock.
//
// It provides the following clock signals:
// - "cts_tcon" / "tcon_clko" (Timing controller)
// - "lcd_an_clk_ph2" (LCD Analog clock for PHY2)
// - "lcd_an_clk_ph3" (LCD Analog clock for PHY3)
// - "cts_enci_clk" (ENCI (Interlaced Encoder) clock)
// - "cts_encl_clk" (ENCL (LVDS Encoder) clock)
// - "cts_encp_clk" (ENCP (Progressive Encoder) clock)
// - "hdmi_tx_pixel" (HDMI Transmitter pixel clock)
// - "cts_vdac_clk" (Video digital-analog converter clock)
//
// The following sections of the Amlogic datasheets are good for understanding
// the PLLs and the dividers that make up the clock tree sources:
// - A311D
// * Section 8.7.1 "Clock" > "Overview" (pages 108-109) has Table 8-9 "A311D
// PLLs" and Figure 8-6 "Clock Connections";
// * Section 8.7.2 "Frequency Calculation and Setting" (pages 115-121) has
// per-PLL subsections showing diagrams.
// * Section 8.7.1.3 "HDMI Clock Tree" (pages 112-113) has the clock tree
// diagram (Figure 8-12).
// - S905D2
// * Section 6.6.1 "Clock" > "Overview" (page 91) has Table 6-5 "A311D
// PLLs".
// * Section 6.6.2 "Clock Trees" (pages 91-92) has Figure 6-5 "Clock
// Connections";
// * Section 6.6.3 "Frequency Calculation and Setting" (pages 99-106) has
// per-PLL subsections showing diagrams.
// * Section 6.6.2.3 "HDMI Clock Tree" (pages 96-97) has the clock tree
// diagram (Figure 6-11).
// - S905D3
// * Section 6.7.1 "Clock" > "Overview" (page 90) has Table 6-5 "A311D
// PLLs".
// * Section 6.7.2 "Clock Trees" (pages 91-92) has Figure 6-5 "Clock
// Connections";
// * Section 6.7.3 "Frequency Calculation and Setting" (pages 99-106) has
// per-PLL subsections showing diagrams.
// * Section 6.7.2.3 "HDMI Clock Tree" (pages 96-97) has the clock tree
// diagram (Figure 6-11).
//
// The detailed diagram of the video clock tree is shown in the following
// section of the Amlogic datasheets:
//
// A311D Datasheet, Figure 8-13 "Video Clock Tree", Section 8.7.1.4 EE Clock
// Tree, Page 114.
// S905D2 Datasheet, Figure 6-12 "Video Clock Tree", Section 6.6.2.4 EE Clock
// Tree, Page 98.
// S905D3 Datasheet, Figure 6-13 "Video Clock Tree", Section 6.7.2.4 EE Clock
// Tree, Page 99.
// Video clock tree has two muxes for input signals, named video clock 1 / mux 1
// (VID_CLK) and video clock 2 / mux 2 (VIID_CLK / V2).
//
// Each video clock mux is followed by a programmable divisor (/N0 for mux 1
// and /N2 for mux 2) and then multiple fixed divisors (/2, /4, /6 and /12).
// Each encoder / HDMI transmitter / video digital-to-analog converter (DAC)
// clock signal has its own mux, to select a clock from those provided by the
// above divisors.
//
// The output of video clock 1 is also used to generate the timing
// controller signal and LCD analog clocks.
enum class VideoClock {
kVideoClock1 = 1,
kVideoClock2 = 2,
};
// Selection of video clock muxes.
//
// The mux value <-> clock source mapping is shown in the following diagram
// of the Amlogic datasheets:
// A311D Datasheet, Figure 8-13 "Video Clock Tree", Section 8.7.1.4 EE Clock
// Tree, Page 114.
// S905D2 Datasheet, Figure 6-12 "Video Clock Tree", Section 6.6.2.4 EE Clock
// Tree, Page 98.
// S905D3 Datasheet, Figure 6-13 "Video Clock Tree", Section 6.7.2.4 EE Clock
// Tree, Page 99.
enum class VideoClockMuxSource : uint32_t {
kVideoPll = 0, // vid_pll
kGeneralPurpose0Pll = 1, // gp0_pll
kHifiPll = 2, // hifi_pll
kMpll1 = 3, // mp1_clk
kFixed666Mhz = 4, // fclk_div3
kFixed500Mhz = 5, // fclk_div4
kFixed400Mhz = 6, // fclk_div5
kFixed285_7Mhz = 7, // fclk_div7
};
// Selection of video clock and dividers for encoder clock muxes.
//
// The mux value <-> clock source mapping is shown in the following diagram
// of the Amlogic datasheets:
// A311D Datasheet, Figure 8-13 "Video Clock Tree", Section 8.7.1.4 EE Clock
// Tree, Page 114.
// S905D2 Datasheet, Figure 6-12 "Video Clock Tree", Section 6.6.2.4 EE Clock
// Tree, Page 98.
// S905D3 Datasheet, Figure 6-13 "Video Clock Tree", Section 6.7.2.4 EE Clock
// Tree, Page 99.
enum class EncoderClockSource : uint32_t {
// "VideoClock1" is first divided by the programmable divider "/N0" before
// being divided by the fixed divider. So the actual frequency is
// (Selected Video Clock 1 input) / (N0) / (1, 2, 4, 16, or 12)
kVideoClock1 = 0,
kVideoClock1Div2 = 1,
kVideoClock1Div4 = 2,
kVideoClock1Div6 = 3,
kVideoClock1Div12 = 4,
// "VideoClock2" is first divided by the programmable divider "/N2" before
// being divided by the fixed divider. So the actual frequency is
// (Selected Video Clock 2 input) / (N2) / (1, 2, 4, 16, or 12)
kVideoClock2 = 8,
kVideoClock2Div2 = 9,
kVideoClock2Div4 = 10,
kVideoClock2Div6 = 11,
kVideoClock2Div12 = 12,
};
// HHI_VIID_CLK_DIV
//
// A311D Datasheet, Section 8.7.6 Register Descriptions, Page 146.
// S905D2 Datasheet, Section 6.6.6 Register Descriptions, Page 126.
// S905D3 Datasheet, Section 6.7.6 Register Descriptions, Page 131.
class VideoClock2Divider : public hwreg::RegisterBase<VideoClock2Divider, uint32_t> {
public:
static constexpr int kMinDivider2 = 1;
static constexpr int kMaxDivider2 = 256;
static_assert(kMaxDivider2 == 1 << (7 - 0 + 1));
static hwreg::RegisterAddr<VideoClock2Divider> Get() { return {0x4a * sizeof(uint32_t)}; }
DEF_ENUM_FIELD(EncoderClockSource, 31, 28, video_dac_clock_selection);
// Bits 27-24 and 23-20 document the DAC1 and DAC2 clock selections in A311D,
// S905D2 and S905D3 documentations. These clocks don't exist in the clock
// trees and are not used by this driver, so we don't define these bits.
// Iff true, overrides the Video DAC clock source selection in
// "video_dac_clock_selection" field to "adc_pll_clk_b2".
DEF_BIT(19, video_dac_clock_selects_adc_pll_clock_b2);
DEF_RSVDZ_BIT(18);
// Iff true, resets the divider (/N2) for video clock 2.
DEF_BIT(17, divider_reset);
// Iff true, enables the divider (/N2) for video clock 2.
DEF_BIT(16, divider_enabled);
DEF_ENUM_FIELD(EncoderClockSource, 15, 12, encl_clock_selection);
// Bits 14-8 are defined as unused in the datasheets, which contradicts the
// definition of bits 15-12 in the same table. Experiments on VIM3 (A311D),
// Astro (S905D2) and Nelson (S905D3) show that only bits 11-8 are unused.
DEF_RSVDZ_FIELD(11, 8);
// Also known as "/N2" in the Video Clock Tree diagram.
//
// Prefer `Divider2()` and `SetDivider2()` to accessing the field directly.
DEF_FIELD(7, 0, divider2_minus_one);
VideoClock2Divider& SetDivider2(int divider2) {
ZX_DEBUG_ASSERT(divider2 >= kMinDivider2);
ZX_DEBUG_ASSERT(divider2 <= kMaxDivider2);
return set_divider2_minus_one(divider2 - 1);
}
int Divider2() const { return divider2_minus_one() + 1; }
};
// HHI_VIID_CLK_CNTL
//
// A311D Datasheet, Section 8.7.6 Register Descriptions, Page 146.
// S905D2 Datasheet, Section 6.6.6 Register Descriptions, Page 126-127.
// S905D3 Datasheet, Section 6.7.6 Register Descriptions, Page 132.
class VideoClock2Control : public hwreg::RegisterBase<VideoClock2Control, uint32_t> {
public:
static hwreg::RegisterAddr<VideoClock2Control> Get() { return {0x4b * sizeof(uint32_t)}; }
DEF_RSVDZ_FIELD(31, 20);
// If false, the input and output signals for the video clock 2 divider are
// gated.
//
// The input signal may be also gated by the `divider_enabled` bit of the
// `VideoClock2Divider` register.
DEF_BIT(19, clock_enabled);
DEF_ENUM_FIELD(VideoClockMuxSource, 18, 16, mux_source);
// This is a "level triggered" signal. Drivers reset the clock dividers by
// first setting the bit to 1, sleeping for 10 us (empirical value from VIM3
// using Amlogic A311D chip) and then setting the bit to 0.
DEF_BIT(15, soft_reset);
DEF_RSVDZ_FIELD(12, 5);
DEF_BIT(4, div12_enabled);
DEF_BIT(3, div6_enabled);
DEF_BIT(2, div4_enabled);
DEF_BIT(1, div2_enabled);
DEF_BIT(0, div1_enabled);
};
// HHI_VID_CLK_DIV
//
// A311D Datasheet, Section 8.7.6 Register Descriptions, Page 150.
// S905D2 Datasheet, Section 6.6.6 Register Descriptions, Page 136.
// S905D3 Datasheet, Section 6.7.6 Register Descriptions, Page 129.
class VideoClock1Divider : public hwreg::RegisterBase<VideoClock1Divider, uint32_t> {
public:
static constexpr int kMinDivider1 = 1;
static constexpr int kMaxDivider1 = 256;
static_assert(kMaxDivider1 == 1 << (15 - 8 + 1));
static constexpr int kMinDivider0 = 1;
static constexpr int kMaxDivider0 = 256;
static_assert(kMaxDivider0 == 1 << (7 - 0 + 1));
static hwreg::RegisterAddr<VideoClock1Divider> Get() { return {0x59 * sizeof(uint32_t)}; }
DEF_ENUM_FIELD(EncoderClockSource, 31, 28, enci_clock_selection);
DEF_ENUM_FIELD(EncoderClockSource, 27, 24, encp_clock_selection);
DEF_ENUM_FIELD(EncoderClockSource, 23, 20, enct_clock_selection);
DEF_RSVDZ_FIELD(19, 18);
// Iff true, resets the dividers (/N0 and /N1) for video clock 1.
DEF_BIT(17, dividers_reset);
// Iff true, enables the dividers (/N0 and /N1) for video clock 1.
// Divider /N0 / /N1 works iff `dividers_enabled` and `divider0/1_enabled`
// field in `VideoClock1Control` register are both true.
DEF_BIT(16, dividers_enabled);
// Also known as "/N1" in the Video Clock Tree diagram.
//
// Prefer `Divider1()` and `SetDivider1()` to accessing the field directly.
DEF_FIELD(15, 8, divider1_minus_one);
VideoClock1Divider& SetDivider1(int divider1) {
ZX_DEBUG_ASSERT(divider1 >= kMinDivider1);
ZX_DEBUG_ASSERT(divider1 <= kMaxDivider1);
return set_divider1_minus_one(divider1 - 1);
}
int Divider1() const { return divider1_minus_one() + 1; }
// Also known as "/N0" in the Video Clock Tree diagram.
//
// Prefer `Divider0()` and `SetDivider0()` to accessing the field directly.
DEF_FIELD(7, 0, divider0_minus_one);
VideoClock1Divider& SetDivider0(int divider0) {
ZX_DEBUG_ASSERT(divider0 >= kMinDivider0);
ZX_DEBUG_ASSERT(divider0 <= kMaxDivider0);
return set_divider0_minus_one(divider0 - 1);
}
int Divider0() const { return divider0_minus_one() + 1; }
};
// HHI_VID_CLK_CNTL
//
// A311D Datasheet, Section 8.7.6 Register Descriptions, Page 151.
// S905D2 Datasheet, Section 6.6.6 Register Descriptions, Page 137.
// S905D3 Datasheet, Section 6.7.6 Register Descriptions, Page 129-130.
class VideoClock1Control : public hwreg::RegisterBase<VideoClock1Control, uint32_t> {
public:
enum class LcdAnalogClockSelection : uint32_t {
kVideoClock1Div6 = 0,
kVideoClock1Div12 = 1,
};
static hwreg::RegisterAddr<VideoClock1Control> Get() { return {0x5f * sizeof(uint32_t)}; }
// Bits 31-21 control the clock generation module for timing controller clock
// (cts_tcon). The subfield definition is not in the register description
// table but is mentioned in the clock tree diagram.
//
// A311D Datasheet, Section 8.7.1 Clock Trees, Page 114.
// S905D2 Datasheet, Section 6.6.2 Clock Trees, Page 98.
// S905D3 Datasheet, Section 6.7.2 Clock Trees, Page 99.
DEF_FIELD(31, 21, timing_controller_clock_control);
// If false, the output signal of divider /N1 is gated.
//
// Divider /N1 works iff `divider1_enabled` and the `dividers_enabled`
// field in `VideoClock1Divider` register are both true.
DEF_BIT(20, divider1_enabled);
// If false, the output signal of divider /N0 is gated.
//
// Divider /N0 works iff `divider0_enabled` and the `dividers_enabled`
// field in `VideoClock1Divider` register are both true.
DEF_BIT(19, divider0_enabled);
DEF_ENUM_FIELD(VideoClockMuxSource, 18, 16, mux_source);
// This is a "level triggered" signal. Drivers reset the clock dividers by
// first setting the bit to 1, sleeping for 10 us (empirical value from VIM3
// using A311D chip) and then setting the bit to 0.
DEF_BIT(15, soft_reset);
// Enables the mux for the clock "lcd_an_clk_ph2" and "lcd_an_clk_ph3".
DEF_BIT(14, lcd_analog_clock_mux_enabled);
// "Video Clock Tree" diagrams use the bit 11 on register 0x1a
// (HHI_GP1_PLL_CNTL2) which doesn't match the register definitions on the
// same datasheet. Experiments on VIM3 (Amlogic A311D) shows that this bit
// is the correct bit to select input source for LCD analog clocks.
DEF_ENUM_FIELD(LcdAnalogClockSelection, 13, 13, lcd_analog_clock_selection);
DEF_RSVDZ_FIELD(12, 5);
DEF_BIT(4, div12_enabled);
DEF_BIT(3, div6_enabled);
DEF_BIT(2, div4_enabled);
DEF_BIT(1, div2_enabled);
DEF_BIT(0, div1_enabled);
};
// HHI_VID_CLK_CNTL2
//
// A311D Datasheet, Section 8.7.6 Register Descriptions, Page 152.
// S905D2 Datasheet, Section 6.6.6 Register Descriptions, Page 137.
// S905D3 Datasheet, Section 6.7.6 Register Descriptions, Page 130.
class VideoClockOutputControl : public hwreg::RegisterBase<VideoClockOutputControl, uint32_t> {
public:
static hwreg::RegisterAddr<VideoClockOutputControl> Get() { return {0x65 * sizeof(uint32_t)}; }
DEF_RSVDZ_FIELD(15, 9);
DEF_BIT(8, analog_tv_demodulator_video_dac_clock_enabled);
DEF_BIT(7, lcd_analog_clock_phy2_enabled);
DEF_BIT(6, lcd_analog_clock_phy3_enabled);
DEF_BIT(5, hdmi_tx_pixel_clock_enabled);
DEF_BIT(4, video_dac_clock_enabled);
DEF_BIT(3, encoder_lvds_enabled);
DEF_BIT(2, encoder_progressive_enabled);
DEF_BIT(1, encoder_tv_enabled);
DEF_BIT(0, encoder_interlaced_enabled);
};
// HHI_HDMI_CLK_CNTL - Configures "cts_hdmitx_sys_clk" and
// "cts_hdmitx_pixel_clk".
//
// A311D Datasheet, Section 8.7.6 Register Descriptions, Page 157.
// S905D2 Datasheet, Section 6.6.6 Register Descriptions, Page 142.
// S905D3 Datasheet, Section 6.7.6 Register Descriptions, Page 133.
class HdmiClockControl : public hwreg::RegisterBase<HdmiClockControl, uint32_t> {
public:
// Selection of video clock muxes.
//
// The mux value <-> clock source mapping is shown in the following diagram
// of the Amlogic datasheets:
// A311D Datasheet, Figure 8-13 "Video Clock Tree", Section 8.7.1.4 EE Clock
// Tree, Page 114.
// S905D2 Datasheet, Figure 6-12 "Video Clock Tree", Section 6.6.2.4 EE Clock
// Tree, Page 98.
// S905D3 Datasheet, Figure 6-13 "Video Clock Tree", Section 6.7.2.4 EE Clock
// Tree, Page 99.
enum class HdmiTxPixelClockSource : uint32_t {
// "VideoClock1" is divided by the programmable divider "/N0" before
// being divided by the fixed divider. So the actual frequency is
// (Selected Video Clock 1 input) / (N0) / (1, 2, 4, 16, or 12)
kVideoClock1 = 0,
kVideoClock1Div2 = 1,
kVideoClock1Div4 = 2,
kVideoClock1Div6 = 3,
kVideoClock1Div12 = 4,
// "VideoClock2" is divided by the programmable divider "/N2" before
// being divided by the fixed divider. So the actual frequency is
// (Selected Video Clock 2 input) / (N2) / (1, 2, 4, 16, or 12)
kVideoClock2 = 8,
kVideoClock2Div2 = 9,
kVideoClock2Div4 = 10,
kVideoClock2Div6 = 11,
kVideoClock2Div12 = 12,
// "cts_tcon" clock provided by the video clock tree.
//
// This value is not documented in S905D3 datasheets. However, experiments
// on a Nelson device (Amlogic S905D3) show that the value is the same as
// other devices.
kTimingControllerClock = 15,
};
enum class HdmiTxSystemClockSource : uint32_t {
kExternalOscillator24Mhz = 0, // xtal
kFixed500Mhz = 1, // fclk_div4
kFixed666Mhz = 2, // fclk_div3
kFixed400Mhz = 3, // fclk_div5
};
static constexpr int kMinHdmiTxSystemClockDivider = 1;
static constexpr int kMaxHdmiTxSystemClockDivider = 128;
static_assert(kMaxHdmiTxSystemClockDivider == 1 << (6 - 0 + 1));
static hwreg::RegisterAddr<HdmiClockControl> Get() { return {0x73 * sizeof(uint32_t)}; }
DEF_RSVDZ_FIELD(31, 20);
DEF_ENUM_FIELD(HdmiTxPixelClockSource, 19, 16, hdmi_tx_pixel_clock_selection);
DEF_RSVDZ_FIELD(15, 11);
DEF_ENUM_FIELD(HdmiTxSystemClockSource, 10, 9, hdmi_tx_system_clock_selection);
DEF_BIT(8, hdmi_tx_system_clock_enabled);
DEF_RSVDZ_BIT(7);
// Prefer `HdmiTxSystemClockDivider()` and `SetHdmiTxSystemClockDivider()` to
// accessing the field directly.
DEF_FIELD(6, 0, hdmi_tx_system_clock_divider_minus_one);
HdmiClockControl& SetHdmiTxSystemClockDivider(int divider) {
ZX_DEBUG_ASSERT(divider >= kMinHdmiTxSystemClockDivider);
ZX_DEBUG_ASSERT(divider <= kMaxHdmiTxSystemClockDivider);
return set_hdmi_tx_system_clock_divider_minus_one(divider - 1);
}
int HdmiTxSystemClockDivider() const { return hdmi_tx_system_clock_divider_minus_one() + 1; }
};
// ## EE (Everything Else) Clock Tree
//
// EE Clock Tree consists of clocks in the EE power domain, which includes
// (but not limited to) codecs, Mali GPUs, PWM controllers, Video Processing
// Unit (VPU) and video signal transmitters.
//
// Video Clock Tree is technically part of the EE Clock Tree, with a more
// complicated muxing and frequency divider logic.
//
// Details of sources and frequency dividers for each clock is available at:
// A311D Datasheet, Section 8.7.1.4 "EE Clock Tree", Page 113.
// S905D2 Datasheet, Section 6.7.1.4 "EE Clock Tree", Page 97.
// S905D3 Datasheet, Section 6.7.2.4 "EE Clock Tree", Page 98.
//
// Below we only list all the registers configuring clocks used for display.
//
// ### Branched clock inputs
//
// Some of the clocks have a final mux with two identical branches to facilitate
// fast clock transitions without frequency glitches. The unused branch can be
// configured for the new clock speed, and then the final mux is switched over
// for a quick transition. The branches have to be symmetrical for this to be
// easy to use.
//
// Similar dynamic muxes are also documented in other clock trees. The A53
// clock tree on S905D2, the A53/A73 clock tree on A311D, and the A55 clock
// tree on S905D3 all have similar designs documented.
//
// A311D Datasheet, Section 8.7.1.1 "A53/A73 Clock Tree", Pages 109-111.
// S905D2 Datasheet, Section 6.7.1.1 "A53 Clock Tree", Pages 92-94.
// S905D3 Datasheet, Section 6.7.2.1 "A55 Clock Tree", Pages 92-94.
//
// ### Frequency Dividers
//
// The control register fields store "division ratio - 1" for each frequency
// divider.
//
// This is not documented in A311D / S905D2 / S905D3 datasheets, but experiments
// on VIM3 (using A311D), Astro (using S905D2) and Nelson (using S905D3) and
// Amlogic-provided code has verified this. Besides, datasheets of new
// generation chips (for example, A311D2) have mentioned that, "if you want
// div8, set to 7".
//
// A311D2 Datasheet, Table 7-153 "CLKCTRL_CPU_CLKC_CTRL", Page 199.
// HHI_VPU_CLKC_CNTL - Configures the "cts_vpu_clkc" clock signal.
//
// The circuit has two branches, and a final mux that chooses between one of
// them. Each branch has an input mux connected to several clock sources,
// followed by a frequency divider.
//
// The mapping between register fields and branches is not available in the
// register description table, but in the EE Clock Tree table only.
//
// A311D Datasheet, Section 8.7.1.4 "EE Clock Tree", row "cts_vpu_clkc",
// Page 113; Section 8.7.6 Register Descriptions, Page 155.
// S905D2 Datasheet, Section 6.6.6 Register Descriptions, Page 132;
// Section 6.7.1.4 "EE Clock Tree", row "cts_vpu_clkc", Page 97.
// S905D3 Datasheet, Section 6.7.6 Register Descriptions, Page 141.
// Section 6.7.2.4 "EE Clock Tree", row "cts_vpu_clkc", Page 98.
class VpuClockCControl : public hwreg::RegisterBase<VpuClockCControl, uint32_t> {
public:
enum class FinalMuxSource : uint32_t {
kBranch0 = 0,
kBranch1 = 1,
};
enum class ClockSource : uint32_t {
kFixed500Mhz = 0, // fclk_div4
kFixed666Mhz = 1, // fclk_div3
kFixed400Mhz = 2, // fclk_div5
kFixed285_7Mhz = 3, // fclk_div7
kMpll1 = 4, // mpll1
kVideoPll = 5, // vid_pll
kMpll2 = 6, // mpll2
kGeneralPurpose0Pll = 7, // gp0_pll
};
static constexpr int kMinBranchMuxDivider = 1;
static constexpr int kMaxBranchMuxDivider = 128;
static hwreg::RegisterAddr<VpuClockCControl> Get() { return {0x6d * sizeof(uint32_t)}; }
DEF_ENUM_FIELD(FinalMuxSource, 31, 31, final_mux_selection);
DEF_RSVDZ_FIELD(30, 29);
DEF_ENUM_FIELD(ClockSource, 27, 25, branch1_mux_source);
DEF_BIT(24, branch1_mux_enabled);
DEF_RSVDZ_BIT(23);
// Prefer `Branch1MuxDivider()` and `SetBranch1MuxDivider()` to accessing the field
// directly.
DEF_FIELD(22, 16, branch1_mux_divider_minus_one);
static_assert(kMaxBranchMuxDivider == 1 << (22 - 16 + 1));
// This field is undocumented on Amlogic datasheets.
// Amlogic-provided code directly writes 0 to this field regardless of its
// original value, so we can believe that zero is a safe setting for this
// field.
DEF_RSVDZ_FIELD(15, 12);
DEF_ENUM_FIELD(ClockSource, 11, 9, branch0_mux_source);
DEF_BIT(8, branch0_mux_enabled);
DEF_RSVDZ_BIT(7);
// Prefer `Branch0MuxDivider()` and `SetBranch0MuxDivider()` to accessing the field
// directly.
DEF_FIELD(6, 0, branch0_mux_divider_minus_one);
static_assert(kMaxBranchMuxDivider == 1 << (6 - 0 + 1));
VpuClockCControl& SetBranch1MuxDivider(int divider) {
ZX_DEBUG_ASSERT(divider >= kMinBranchMuxDivider);
ZX_DEBUG_ASSERT(divider <= kMaxBranchMuxDivider);
return set_branch1_mux_divider_minus_one(divider - 1);
}
int Branch1MuxDivider() const { return branch1_mux_divider_minus_one() + 1; }
VpuClockCControl& SetBranch0MuxDivider(int divider) {
ZX_DEBUG_ASSERT(divider >= kMinBranchMuxDivider);
ZX_DEBUG_ASSERT(divider <= kMaxBranchMuxDivider);
return set_branch0_mux_divider_minus_one(divider - 1);
}
int Branch0MuxDivider() const { return branch0_mux_divider_minus_one() + 1; }
};
// HHI_VPU_CLK_CNTL - Configures the "cts_vpu_clk" clock signal.
//
// The circuit has two branches, and a final mux that chooses between one of
// them. Each branch has an input mux connected to several clock sources,
// followed by a frequency divider.
//
// The mapping between register fields and branches is not available in the
// register description table, but in the EE Clock Tree table only.
//
// A311D Datasheet, Section 8.7.1.4 "EE Clock Tree", row "cts_vpu_clk",
// Page 113; Section 8.7.6 Register Descriptions, Page 156.
// S905D2 Datasheet, Section 6.7.1.4 "EE Clock Tree", row "cts_vpu_clk",
// Page 97; Section 6.6.6 Register Descriptions, Page 132.
// S905D3 Datasheet, Section 6.7.2.4 "EE Clock Tree", row "cts_vpu_clk",
// Page 98; Section 6.7.6 Register Descriptions, Page 142.
class VpuClockControl : public hwreg::RegisterBase<VpuClockControl, uint32_t> {
public:
enum class FinalMuxSource : uint32_t {
kBranch0 = 0,
kBranch1 = 1,
};
enum class ClockSource : uint32_t {
kFixed666Mhz = 0, // fclk_div3
kFixed500Mhz = 1, // fclk_div4
kFixed400Mhz = 2, // fclk_div5
kFixed285_7Mhz = 3, // fclk_div7
kMpll1 = 4, // mpll1
kVideoPll = 5, // vid_pll
kHifiPll = 6, // hifi_pll
kGeneralPurpose0Pll = 7, // gp0_pll
};
static constexpr int kMinBranchMuxDivider = 1;
static constexpr int kMaxBranchMuxDivider = 128;
static hwreg::RegisterAddr<VpuClockControl> Get() { return {0x6f * sizeof(uint32_t)}; }
DEF_ENUM_FIELD(FinalMuxSource, 31, 31, final_mux_selection);
DEF_RSVDZ_FIELD(30, 29);
DEF_ENUM_FIELD(ClockSource, 27, 25, branch1_mux_source);
DEF_BIT(24, branch1_mux_enabled);
DEF_RSVDZ_BIT(23);
// Prefer `Branch1MuxDivider()` and `SetBranch1MuxDivider()` to accessing the field
// directly.
DEF_FIELD(22, 16, branch1_mux_divider_minus_one);
static_assert(kMaxBranchMuxDivider == 1 << (22 - 16 + 1));
// This field is undocumented on Amlogic datasheets.
// Amlogic-provided code directly writes 0 to this field regardless of its
// original value, so we can believe that zero is a safe setting for this
// field.
DEF_RSVDZ_FIELD(15, 12);
DEF_ENUM_FIELD(ClockSource, 11, 9, branch0_mux_source);
DEF_BIT(8, branch0_mux_enabled);
DEF_RSVDZ_BIT(7);
// Prefer `Branch0MuxDivider()` and `SetBranch0MuxDivider()` to accessing the field
// directly.
DEF_FIELD(6, 0, branch0_mux_divider_minus_one);
static_assert(kMaxBranchMuxDivider == 1 << (6 - 0 + 1));
VpuClockControl& SetBranch1MuxDivider(int divider) {
ZX_DEBUG_ASSERT(divider >= kMinBranchMuxDivider);
ZX_DEBUG_ASSERT(divider <= kMaxBranchMuxDivider);
return set_branch1_mux_divider_minus_one(divider - 1);
}
int Branch1MuxDivider() const { return branch1_mux_divider_minus_one() + 1; }
VpuClockControl& SetBranch0MuxDivider(int divider) {
ZX_DEBUG_ASSERT(divider >= kMinBranchMuxDivider);
ZX_DEBUG_ASSERT(divider <= kMaxBranchMuxDivider);
return set_branch0_mux_divider_minus_one(divider - 1);
}
int Branch0MuxDivider() const { return branch0_mux_divider_minus_one() + 1; }
};
// HHI_VAPBCLK_CNTL - Configures the "cts_vapbclk" and "cts_ge2d_clk" clock
// signal.
//
// The circuit has two branches, and a final mux that chooses between one of
// them. Each branch has an input mux connected to several clock sources,
// followed by a frequency divider.
//
// The mapping between register fields and branches are available in both the
// register description table, and the EE Clock Tree table.
//
// A311D Datasheet, Section 8.7.1.4 "EE Clock Tree", row "cts_vapbclk" and
// "cts_ge2d_clk", Page 113; Section 8.7.6 Register Descriptions, Page 164.
// S905D2 Datasheet, Section 6.7.1.4 "EE Clock Tree", row "cts_vapbclk" and
// "cts_ge2d_clk", Page 97; Section 6.6.6 Register Descriptions, Page 141.
// S905D3 Datasheet, Section 6.7.2.4 "EE Clock Tree", row "cts_vapbclk" and
// "cts_ge2d_clk", Page 98; Section 6.7.6 Register Descriptions, Page 136.
class VideoAdvancedPeripheralBusClockControl
: public hwreg::RegisterBase<VideoAdvancedPeripheralBusClockControl, uint32_t> {
public:
enum class FinalMuxSource : uint32_t {
kBranch0 = 0,
kBranch1 = 1,
};
enum class ClockSource : uint32_t {
kFixed500Mhz = 0, // fclk_div4
kFixed666Mhz = 1, // fclk_div3
kFixed400Mhz = 2, // fclk_div5
kFixed285_7Mhz = 3, // fclk_div7
kMpll1 = 4, // mpll1
kVideoPll = 5, // vid_pll
kMpll2 = 6, // mpll2
kFixed800Mhz = 7, // fclk_div2p5
};
static constexpr int kMinBranchMuxDivider = 1;
static constexpr int kMaxBranchMuxDivider = 128;
static hwreg::RegisterAddr<VideoAdvancedPeripheralBusClockControl> Get() {
return {0x7d * sizeof(uint32_t)};
}
DEF_ENUM_FIELD(FinalMuxSource, 31, 31, final_mux_selection);
// If false, the "cts_ge2d_clk" signal is gated. "cts_ge2d_clk" takes
// "cts_vapbclk" output as its clock source and has no frequency dividers.
//
// This bit is named "enable" in A311D, S905D2 and S905D3 datasheet register
// descriptions, but the "EE clock table" shows that it gates the
// "cts_ge2d_clk" signal. Besides, A311D2 datasheet also documents it as
// ""cts_ge2d_clk" enable" in the register descriptions.
//
// A311D2 Datasheet, Section 7.6.5 Register Descriptions, Page 200.
DEF_BIT(30, ge2d_clock_enabled);
DEF_RSVDZ_FIELD(29, 28);
DEF_ENUM_FIELD(ClockSource, 27, 25, branch1_mux_source);
DEF_BIT(24, branch1_mux_enabled);
DEF_RSVDZ_BIT(23);
// Prefer `Branch1MuxDivider()` and `SetBranch1MuxDivider()` to accessing the field
// directly.
DEF_FIELD(22, 16, branch1_mux_divider_minus_one);
static_assert(kMaxBranchMuxDivider == 1 << (22 - 16 + 1));
DEF_RSVDZ_FIELD(15, 12);
DEF_ENUM_FIELD(ClockSource, 11, 9, branch0_mux_source);
DEF_BIT(8, branch0_mux_enabled);
DEF_RSVDZ_BIT(7);
// Prefer `Branch0MuxDivider()` and `SetBranch0MuxDivider()` to accessing the field
// directly.
DEF_FIELD(6, 0, branch0_mux_divider_minus_one);
static_assert(kMaxBranchMuxDivider == 1 << (6 - 0 + 1));
VideoAdvancedPeripheralBusClockControl& SetBranch1MuxDivider(int divider) {
ZX_DEBUG_ASSERT(divider >= kMinBranchMuxDivider);
ZX_DEBUG_ASSERT(divider <= kMaxBranchMuxDivider);
return set_branch1_mux_divider_minus_one(divider - 1);
}
int Branch1MuxDivider() const { return branch1_mux_divider_minus_one() + 1; }
VideoAdvancedPeripheralBusClockControl& SetBranch0MuxDivider(int divider) {
ZX_DEBUG_ASSERT(divider >= kMinBranchMuxDivider);
ZX_DEBUG_ASSERT(divider <= kMaxBranchMuxDivider);
return set_branch0_mux_divider_minus_one(divider - 1);
}
int Branch0MuxDivider() const { return branch0_mux_divider_minus_one() + 1; }
};
// HHI_VPU_CLKB_CNTL - Configures the "cts_vpu_clkb" and "cts_vpu_clkb_tmp"
// clock signals.
//
// The VPU Clock B (cts_vpu_clkb) first selects its source from a mux with
// VPU clock and 500, 400, 285.7 MHz fixed clocks, and then gets divided by
// divider 1 and divider 2.
//
// The datasheets describe it as two clock signals: the target clock signal
// "cts_vpu_clkb", and a temporary clock signal ("cts_vpu_clkb_tmp").
//
// "cts_vpu_clkb_tmp" takes inputs from PLLs, and "cts_vpu_clkb" takes inputs
// from only "cts_vpu_clkb_tmp", and clock has its own divider. Since the
// temporary clock signal is not used anywhere else, this is equivalent to our
// two-divider model described above.
//
// A311D Datasheet, Section 8.7.1.4 "EE Clock Tree", row "cts_vpu_clkb" and
// "cts_vpu_clkb_tmp", Page 113; Section 8.7.6 Register Descriptions,
// Page 164.
// S905D2 Datasheet, Section 6.7.1.4 "EE Clock Tree", row "cts_vpu_clkb" and
// "cts_vpu_clkb_tmp", Page 97; Section 6.6.6 Register Descriptions, Page 141.
// S905D3 Datasheet, Section 6.7.2.4 "EE Clock Tree", row "cts_vpu_clkb" and
// "cts_vpu_clkb_tmp", Page 98; Section 6.7.6 Register Descriptions, Page 136.
class VpuClockBControl : public hwreg::RegisterBase<VpuClockBControl, uint32_t> {
public:
// In the S905D3 datasheet, the mapping between selection values and clock
// sources are not mentioned in the register description table, but only in
// the EE Clock tree table, which matches the rest of the definitions.
enum class ClockSource : uint32_t {
kVpuClock = 0, // cts_vpu_clk
kFixed500Mhz = 1, // fclk_div4
kFixed400Mhz = 2, // fclk_div5
kFixed285_7Mhz = 3, // fclk_div7
};
static constexpr int kMinDivider1 = 1;
static constexpr int kMaxDivider1 = 16;
static constexpr int kMinDivider2 = 1;
static constexpr int kMaxDivider2 = 256;
static hwreg::RegisterAddr<VpuClockBControl> Get() { return {0x83 * sizeof(uint32_t)}; }
DEF_RSVDZ_FIELD(31, 25);
// The clock is enabled only when both `divider1_enabled` and
// `divider2_enabled` are true.
DEF_BIT(24, divider1_enabled);
DEF_ENUM_FIELD(ClockSource, 21, 20, clock_source);
// Prefer `Divider1()` and `SetDivider1()` to accessing the field directly.
DEF_FIELD(19, 16, divider1_minus_one);
static_assert(kMaxDivider1 == 1 << (19 - 16 + 1));
// Iff true, latches the register write until the next vpu_clkb_pulse signal.
DEF_BIT(9, effective_after_vpu_clkb_pulse);
// The clock is enabled only when both `divider1_enabled` and
// `divider2_enabled` are true.
DEF_BIT(8, divider2_enabled);
// Prefer `Divider2()` and `SetDivider2()` to accessing the field directly.
DEF_FIELD(7, 0, divider2_minus_one);
static_assert(kMaxDivider2 == 1 << (7 - 0 + 1));
VpuClockBControl& SetDivider1(int divider1) {
ZX_DEBUG_ASSERT(divider1 >= kMinDivider1);
ZX_DEBUG_ASSERT(divider1 <= kMaxDivider1);
return set_divider1_minus_one(divider1 - 1);
}
int Divider1() const { return divider1_minus_one() + 1; }
VpuClockBControl& SetDivider2(int divider2) {
ZX_DEBUG_ASSERT(divider2 >= kMinDivider2);
ZX_DEBUG_ASSERT(divider2 <= kMaxDivider2);
return set_divider2_minus_one(divider2 - 1);
}
int Divider2() const { return divider2_minus_one() + 1; }
};
// HHI_VDIN_MEAS_CLK_CNTL - Configures the "cts_vdin_meas_clk" and
// "cts_dsi_meas_clk" clock signals.
//
// A311D Datasheet, Section 8.7.1.4 Clock Tree, Page 113; Section 8.7.6
// Register Descriptions, Page 164.
// S905D2 Datasheet, Section 6.7.1.4 Clock Tree, Page 97; Section 6.6.6
// Register Descriptions, Page 151.
// S905D3 Datasheet, Section 6.7.2.4 Clock Tree, Page 98; Section 6.7.6
// Register Descriptions, Page 140.
class VideoInputMeasureClockControl
: public hwreg::RegisterBase<VideoInputMeasureClockControl, uint32_t> {
public:
enum class ClockSource : uint32_t {
kExternalOscillator24Mhz = 0, // xtal
kFixed500Mhz = 1, // fclk_div4
kFixed666Mhz = 2, // fclk_div3
kFixed400Mhz = 3, // fclk_div5
kVideoPll = 4, // vid_pll
kGeneralPurpose0Pll = 5, // gp0_pll
kFixed1000Mhz = 6, // fclk_div2
kFixed285_7Mhz = 7, // fclk_div7
};
static constexpr int kMinDsiMeasureClockDivider = 1;
static constexpr int kMaxDsiMeasureClockDivider = 128;
static constexpr int kMinVideoInputMeasureClockDivider = 1;
static constexpr int kMaxVideoInputMeasureClockDivider = 128;
static hwreg::RegisterAddr<VideoInputMeasureClockControl> Get() {
return {0x94 * sizeof(uint32_t)};
}
DEF_RSVDZ_FIELD(31, 24);
// In the S905D3 and S905D2 datasheets, bits 31-12 are documented as "unused"
// in the register-level documentation but bits 23-12 (DSI measure clock
// control) are mentioned in the EE clock tree table.
//
// Clock-measurement-based experiments on Astro (using S905D2) and Nelson
// (using S905D3) show that the clock input value 0-7 have the definition
// above on these devices.
DEF_ENUM_FIELD(ClockSource, 23, 21, dsi_measure_clock_selection);
DEF_BIT(20, dsi_measure_clock_enabled);
// Prefer `DsiMeasureClockDivider()` and `SetDsiMeasureClockDivider()` to
// accessing the field directly.
DEF_FIELD(18, 12, dsi_measure_clock_divider_minus_one);
static_assert(kMaxDsiMeasureClockDivider == 1 << (18 - 12 + 1));
// In the S905D2 and A311D datasheets, the register description table listed
// clock sources 0-7, while the EE clock tree table only listed clock source
// 0-3.
//
// Clock-measurement-based experiments on Astro (using S905D2) and VIM3
// (using A311D) show that the clock input value 0-7 have the definition
// above on these devices.
DEF_ENUM_FIELD(ClockSource, 11, 9, video_input_measure_clock_selection);
DEF_BIT(8, video_input_measure_clock_enabled);
// Prefer `VideoInputMeasureClockDivider()` and
// `SetVideoInputMeasureClockDivider()` to accessing the field directly.
DEF_FIELD(6, 0, video_input_measure_clock_divider_minus_one);
static_assert(kMaxVideoInputMeasureClockDivider == 1 << (6 - 0 + 1));
VideoInputMeasureClockControl& SetDsiMeasureClockDivider(int divider) {
ZX_DEBUG_ASSERT(divider >= kMinDsiMeasureClockDivider);
ZX_DEBUG_ASSERT(divider <= kMaxDsiMeasureClockDivider);
return set_dsi_measure_clock_divider_minus_one(divider - 1);
}
int DsiMeasureClockDivider() const { return dsi_measure_clock_divider_minus_one() + 1; }
VideoInputMeasureClockControl& SetVideoInputMeasureClockDivider(int divider) {
ZX_DEBUG_ASSERT(divider >= kMinVideoInputMeasureClockDivider);
ZX_DEBUG_ASSERT(divider <= kMaxVideoInputMeasureClockDivider);
return set_video_input_measure_clock_divider_minus_one(divider - 1);
}
int VideoInputMeasureClockDivider() const {
return video_input_measure_clock_divider_minus_one() + 1;
}
};
// HHI_MIPIDSI_PHY_CLK_CNTL - Configures the "mipi_dsi_phy_clk" (also known as
// "cts_dsi_phy_clk") clock signal.
//
// This register is not documented in S905D3 datasheets but mentioned in S905D3
// EE clock tree table.
//
// A311D Datasheet, Section 8.7.1.4 Clock Tree, Page 113; Section 8.7.6
// Register Descriptions, Page 164.
// S905D2 Datasheet, Section 6.7.1.4 Clock Tree, Page 97; Section 6.6.6
// Register Descriptions, Page 151.
// S905D3 Datasheet, Section 6.7.2.4 Clock Tree, Page 98.
class MipiDsiPhyClockControl : public hwreg::RegisterBase<MipiDsiPhyClockControl, uint32_t> {
public:
enum class ClockSource : uint32_t {
kVideoPll = 0, // vid_pll
kGeneralPurpose0Pll = 1, // gp0_pll
kHifiPll = 2, // hifi_pll
kMpll1 = 3, // mpll1
kFixed1000Mhz = 4, // fclk_div2
kFixed800Mhz = 5, // fclk_div2p5
kFixed666Mhz = 6, // fclk_div3
kFixed285_7Mhz = 7, // fclk_div7
};
static constexpr int kMinDivider = 1;
static constexpr int kMaxDivider = 128;
static_assert(kMaxDivider == 1 << (6 - 0 + 1));
static hwreg::RegisterAddr<MipiDsiPhyClockControl> Get() { return {0x95 * sizeof(uint32_t)}; }
DEF_ENUM_FIELD(ClockSource, 14, 12, clock_source);
DEF_BIT(8, enabled);
// Prefer `Divider()` and `SetDivider()` to accessing the field directly.
DEF_FIELD(6, 0, divider_minus_one);
MipiDsiPhyClockControl& SetDivider(int divider) {
ZX_DEBUG_ASSERT(divider >= kMinDivider);
ZX_DEBUG_ASSERT(divider <= kMaxDivider);
return set_divider_minus_one(divider - 1);
}
int Divider() const { return divider_minus_one() + 1; }
};
// ## HDMI Clock Tree
//
// The **HDMI Clock Tree** takes the HDMI PLL as its clock input and produces
// the "vid_pll_clk" clock signal to be used by the EE (Everything Else) clock
// tree, which includes the VPU (display engine). It uses a pattern repeater as
// a frequency divider circuit, which is controlled by the
// `HdmiClockTreeControl` register.
// Values for the `pattern_generator_mode_selection` field in `HdmiClockTreeControl`.
enum class HdmiClockTreePatternGeneratorModeSource : uint32_t {
// Source 0: Repeating the lower 12 bits of the provided pattern.
kRepeated12BitPattern = 0,
// Source 1: Repeating the lower 14 bits of the provided pattern.
kRepeated14BitPattern = 1,
// Source 2: Repeating the lower 15 bits of the provided pattern.
kRepeated15BitPattern = 2,
// Source 3: Repeating a fixed 25-bit pattern:
// (MSB) 111 000 111 000 111 000 1111 000 (LSB)
kFixed25BitPattern = 3,
};
// HHI_VID_PLL_CLK_DIV - Configures the "vid_pll_clk" clock signal.
//
// The HDMI clock tree has a pattern repeater that can repeat a given (or
// fixed) bit pattern, one bit at each clock cycle, which effectively acts as a
// frequency divider of the input clock signal. This register controls the
// behavior of the pattern generator.
//
// A311D Datasheet, Section 8.7.1.3 "HDMI Clock Tree", Page 112-113; Section
// 8.7.6 Register Descriptions, Page 153.
// S905D2 Datasheet, Section 6.6.2.3 "HDMI Clock Tree", Page 96-97; Section
// 6.6.6 Register Descriptions, Page 138-139.
// S905D3 Datasheet, Section 6.7.2.3 "HDMI Clock Tree", Page 96-97; Section
// 6.7.6 Register Descriptions, Page 131.
class HdmiClockTreeControl : public hwreg::RegisterBase<HdmiClockTreeControl, uint32_t> {
public:
static hwreg::RegisterAddr<HdmiClockTreeControl> Get() { return {0x68 * sizeof(uint32_t)}; }
// Bits 31-24 are reserved.
DEF_RSVDZ_FIELD(23, 20);
// If false, the output clock vid_pll_clk is gated.
DEF_BIT(19, clock_output_enabled);
// If true, the output clock matches the HDMI PLL clock.
//
// When this bit is true, the pattern repeater configuration does not
// influence the output signal.
//
// `Pattern()`, `PatternSize()` and `SetFrequencyDividerRatio()` helpers are
// preferred over direct field manipulations.
DEF_BIT(18, bypass_pattern_generators);
// `Pattern()`, `PatternSize()` and `SetFrequencyDividerRatio()` helpers are
// preferred over direct field manipulations.
DEF_ENUM_FIELD(HdmiClockTreePatternGeneratorModeSource, 17, 16, pattern_generator_mode_selection);
// If false, the `preset_pattern` field is ignored when the register is
// written.
DEF_BIT(15, preset_pattern_update_enabled);
// The bits output by the pattern generator, when not in fixed pattern mode.
//
// For example, to get a clock signal at 1/5 of the HDMI PLL frequency (with a
// 60/40 duty cycle), use 15-bit repeater (source 2) and set the pattern to
// 0b111'00'111'00'111'00 to generate the following pattern (assuming the bits
// are emitted from the least significant bit to the most significant bit):
//
// (output) 0 0 1 1 1 0 0 1 1 1 0 0 1 1 1
// 1 ______ ______ ______
// 0 ____ ____ ____
//
// The pattern must fulfill the following constraints:
// - The pattern's least significant bit must be zero.
// - If PatternSize() is non-zero, the bit `PatternSize() - 1` is the most
// significant bit set. In testing, this means the pattern will be at least
// `1 << PatternSize()` and less than `1 << (PatternSize() + 1)`.
// - The number of 1 -> 0 bit transitions (when reading from the most
// significant bit to the least significant bit) in `Pattern()` equals
// `PatternSize() / (divider_ratio - 1.0)` for divider ratios greater than
// one.
// - The maximum length of a consecutive sequence of ones or zeros will
// differ by at most 1 from the minimum length of a consecutive sequence
// of ones or zeros.
//
// `Pattern()`, `PatternSize()` and `SetFrequencyDividerRatio()` helpers are
// preferred over direct field manipulations.
DEF_FIELD(14, 0, pattern_generator_state);
// The generated signal's period (cycle size), in bits.
// Returns 0 if the pattern generator is bypassed.
int PatternSize() const;
// The pattern repeated by the pattern generator.
// Returns 0 if the pattern generator is bypassed.
uint32_t Pattern() const;
// Frequency division ratios supported by the pattern generator.
static constexpr uint32_t kSupportedFrequencyDividerRatiosArray[] = {
ToU28_4(1.0), ToU28_4(2.0), ToU28_4(2.5), ToU28_4(3.0), ToU28_4(3.5),
ToU28_4(3.75), ToU28_4(4.0), ToU28_4(5.0), ToU28_4(6.0), ToU28_4(6.25),
ToU28_4(7.0), ToU28_4(7.5), ToU28_4(12.0), ToU28_4(14.0), ToU28_4(15.0),
};
static constexpr cpp20::span<const uint32_t> kSupportedFrequencyDividerRatios =
kSupportedFrequencyDividerRatiosArray;
// Sets the pattern generator so that it works as a frequency divider with a
// division ratio of `division_ratio_u28_4`.
//
// `division_ratio_u28_4` is a U28.4 format fixed-point fraction with 28
// integer bits and 4 fractional bits.
//
// `division_ratio_u28_4` must be one of the values in
// `kSupportedFrequencyDividerRatios`.
HdmiClockTreeControl& SetFrequencyDividerRatio(uint32_t divider_ratio_u28_4);
};
} // namespace amlogic_display
#endif // SRC_GRAPHICS_DISPLAY_DRIVERS_AMLOGIC_DISPLAY_CLOCK_REGS_H_