| // 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_VIDEO_INPUT_REGS_H_ |
| #define SRC_GRAPHICS_DISPLAY_DRIVERS_AMLOGIC_DISPLAY_VIDEO_INPUT_REGS_H_ |
| |
| #include <lib/driver/logging/cpp/logger.h> |
| #include <zircon/assert.h> |
| |
| #include <cstdint> |
| |
| #include <hwreg/bitfields.h> |
| |
| namespace amlogic_display { |
| |
| // There are two video input modules (VDIN) in Amlogic display engine that |
| // receive video from external input (e.g. BT.656) or internal input (e.g. |
| // internal VIU loopback), and write the data back to the DDR memory. |
| enum class VideoInputModuleId { |
| kVideoInputModule0 = 0, |
| kVideoInputModule1 = 1, |
| }; |
| |
| // VDIN0_COM_CTRL0, VDIN1_COM_CTRL0. |
| // |
| // A311D Datasheet, Section 10.2.3.42 VDIN, Pages 1086-1087, 1108-1109. |
| // S905D2 Datasheet, Section 7.2.3.41 VDIN, Pages 777, 801. |
| // S905D3 Datasheet, Section 8.2.3.42 VDIN, Pages 713-714, 736-737. |
| class VideoInputCommandControl : public hwreg::RegisterBase<VideoInputCommandControl, uint32_t> { |
| public: |
| enum class ComponentInput : uint32_t { |
| kComponentInput0 = 0b00, |
| kComponentInput1 = 0b01, |
| kComponentInput2 = 0b10, |
| }; |
| |
| // Values for the `input_source_selection` field. |
| enum class InputSource : uint32_t { |
| kNoInput = 0, |
| kMpegFromDram = 1, |
| kBt656 = 2, |
| kReservedForComponent = 3, |
| kReservedForTvDecoder = 4, |
| kReservedForHdmiRx = 5, |
| kDigitalVideoInput = 6, |
| |
| // Source selected by `WritebackMuxControl`. |
| // |
| // This source is documented as "Internal loopback from VIU" on A311D, |
| // S905D2, and T931. Experiments on VIM3 (A311D), Astro (S905D2) and |
| // Sherlock (T931) confirmed that the behavior actually matches S905D3. |
| kWritebackMux0 = 7, |
| |
| kReservedForMipiCsi2 = 8, |
| |
| // Source selected by `WritebackMuxControl`. |
| // |
| // This source is documented as "Reserved (ISP)" on A311D, S905D2, and T931. |
| // Experiments on VIM3 (A311D), Astro (S905D2) and Sherlock (T931) confirmed |
| // that the behavior actually matches S905D3. |
| kWritebackMux1 = 9, |
| |
| kSecondBt656 = 10, |
| }; |
| |
| static hwreg::RegisterAddr<VideoInputCommandControl> Get(VideoInputModuleId video_input) { |
| switch (video_input) { |
| case VideoInputModuleId::kVideoInputModule0: |
| return {0x1202 * sizeof(uint32_t)}; |
| case VideoInputModuleId::kVideoInputModule1: |
| return {0x1302 * sizeof(uint32_t)}; |
| } |
| ZX_DEBUG_ASSERT_MSG(false, "Invalid video input module ID: %d", static_cast<int>(video_input)); |
| } |
| |
| DEF_BIT(31, bypass_mpeg_noise_reduction); |
| DEF_BIT(30, mpeg_field_info); |
| |
| // Trigger a go_field (Vsync) pulse on the video input module when true is |
| // written. |
| DEF_BIT(29, trigger_go_field_pulse); |
| |
| // Trigger a go_line (Hsync) pulse on the video input module when true is |
| // written. |
| DEF_BIT(28, trigger_go_line_pulse); |
| |
| DEF_BIT(27, mpeg_go_field_input_signal_enabled); |
| |
| // Not documented for this register; for fields of the same name in other |
| // registers (VD1_IF0_GEN_REG, DI_IF0_GEN_REG, etc.), `hold_lines` is the |
| // number of lines to hold after go_field pulse and before the module is |
| // enabled. |
| DEF_FIELD(26, 20, hold_lines); |
| |
| // Whether the `go_field` pulse is delayed for the video input module. |
| DEF_BIT(19, go_field_pulse_delayed); |
| |
| // Number of lines that `go_field` pulse is delayed, if |
| // `go_field_pulse_delayed` is true. |
| DEF_FIELD(18, 12, go_field_pulse_delay_lines); |
| |
| // Seems unused for internal loopback mode. |
| DEF_ENUM_FIELD(ComponentInput, 11, 10, component2_output_selection); |
| DEF_ENUM_FIELD(ComponentInput, 9, 8, component1_output_selection); |
| DEF_ENUM_FIELD(ComponentInput, 7, 6, component0_output_selection); |
| |
| // Indicates whether the video input is cropped using a window specified by |
| // `VDIN0/1_WIN_H_START_END` and `VDIN0/1_WIN_V_START_END` registers. |
| DEF_BIT(5, video_input_cropped); |
| |
| DEF_BIT(4, video_input_enabled); |
| |
| // If the input source doesn't equal to any value specified in `InputSource`, |
| // no input is provided to the video input module. |
| DEF_ENUM_FIELD(InputSource, 3, 0, input_source_selection); |
| |
| VideoInputCommandControl& SetInputSource(InputSource input_source) { |
| switch (input_source) { |
| case InputSource::kNoInput: |
| case InputSource::kMpegFromDram: |
| case InputSource::kBt656: |
| case InputSource::kReservedForComponent: |
| case InputSource::kReservedForTvDecoder: |
| case InputSource::kReservedForHdmiRx: |
| case InputSource::kDigitalVideoInput: |
| case InputSource::kWritebackMux0: |
| case InputSource::kReservedForMipiCsi2: |
| case InputSource::kWritebackMux1: |
| case InputSource::kSecondBt656: |
| return set_input_source_selection(input_source); |
| } |
| ZX_DEBUG_ASSERT_MSG(false, "Unsupported input source: %" PRIu32, |
| static_cast<uint32_t>(input_source)); |
| return set_input_source_selection(InputSource::kNoInput); |
| } |
| |
| InputSource GetInputSource() const { |
| InputSource input_source = input_source_selection(); |
| switch (input_source) { |
| case InputSource::kNoInput: |
| case InputSource::kMpegFromDram: |
| case InputSource::kBt656: |
| case InputSource::kReservedForComponent: |
| case InputSource::kReservedForTvDecoder: |
| case InputSource::kReservedForHdmiRx: |
| case InputSource::kDigitalVideoInput: |
| case InputSource::kWritebackMux0: |
| case InputSource::kReservedForMipiCsi2: |
| case InputSource::kWritebackMux1: |
| case InputSource::kSecondBt656: |
| return input_source; |
| } |
| return InputSource::kNoInput; |
| } |
| }; |
| |
| // VDIN0_COM_STATUS0, VDIN1_COM_STATUS0 |
| // |
| // This register is read-only. |
| // |
| // A311D Datasheet, Section 10.2.3.42 VDIN, Pages 1087, 1109 |
| // S905D2 Datasheet, Section 7.2.3.41 VDIN, Pages 778, 802 |
| // S905D3 Datasheet, Section 8.2.3.41 VDIN, Pages 714, 737 |
| class VideoInputCommandStatus0 : public hwreg::RegisterBase<VideoInputCommandStatus0, uint32_t> { |
| public: |
| static hwreg::RegisterAddr<VideoInputCommandStatus0> Get(VideoInputModuleId video_input) { |
| switch (video_input) { |
| case VideoInputModuleId::kVideoInputModule0: |
| return {0x1205 * sizeof(uint32_t)}; |
| case VideoInputModuleId::kVideoInputModule1: |
| return {0x1305 * sizeof(uint32_t)}; |
| } |
| ZX_DEBUG_ASSERT_MSG(false, "Invalid video input module ID: %d", static_cast<int>(video_input)); |
| } |
| |
| // Bits 17-3` are defined differently for VDIN0_COM_STATUS0 and |
| // VDIN0_COM_STATUS0, in all the datasheets mentioned above. |
| // |
| // VDIN0_COM_STATUS0 uses bit 17 as `vid_wr_pending_ddr_wrrsp`, bit 16 |
| // as `curr_pic_sec`, and bit 15 as `curr_pic_sec_sav`, bits 14-3 as |
| // `lfifo_buf_cnt`. |
| // |
| // VDIN1_COM_STATUS0 uses bits 12-3 as `lfifo_buf_cnt` and bits 17-13 |
| // reserved. |
| // |
| // Since these fields are not currently used by the driver, we are omitting |
| // these fields as reserved. Future drivers may need to fork the register |
| // definitions or create helper functions to access the fields. |
| |
| // Indicates that the write of raw pixels from input source to RAM is done. |
| // |
| // Cleared by `clear_direct_write_done` bit in the `VideoInputWriteControl` |
| // register. |
| DEF_BIT(2, direct_write_done); |
| |
| // Indicates that the write of noise-reduced (NR) pixels from input source to |
| // RAM is done. |
| // |
| // Cleared by `clear_noise_reduced_write_done` bit in the |
| // `VideoInputWriteControl` register. |
| DEF_BIT(1, noise_reduced_write_done); |
| |
| // Current field for interlaced input. |
| // |
| // For interlaced inputs, 0 means top field, 1 means bottom field. |
| // This is not documented in Amlogic datasheets but appears in drivers. |
| // |
| // Unused for progressive inputs. |
| DEF_BIT(0, current_field); |
| }; |
| |
| // There are multiple video input channels (VDIs, possibly shorthand for Video |
| // Data Input) available for video input modules (VDINs), numbered from VDI1 to |
| // VDI9. Each VDIN has one asynchronous FIFO (ASFIFO) for each VDI to receive |
| // pixels from. |
| // |
| // For each VDIN, there are four control registers (ASFIFO_CTRL0/1/2/3) to |
| // configure the way the VDIN reads pixels from a channel by setting the |
| // corresponding FIFO behaviors. The layout of the ASFIFO configuration fields |
| // is the same across all channels. |
| // |
| // Currently this driver only uses VDI 6 and VDI 8; control fields / registers |
| // for all the other VDI channels are not defined here. |
| // |
| // A311D Datasheet, Section 10.2.3.42 VDIN, Pages 1088-1090, 1106, 1110-1112, |
| // 1128. |
| // S905D2 Datasheet, Section 7.2.3.41 VDIN, Pages 779-781, 798-799, 802-805, |
| // 822. |
| // S905D3 Datasheet, Section 8.2.3.42 VDIN, Pages 715-717, 733, 738-740, 755. |
| |
| // VDIN0_ASFIFO_CTRL2, VDIN1_ASFIFO_CTRL2 |
| // |
| // A311D Datasheet, Section 10.2.3.42 VDIN, Pages 1090, 1112. |
| // S905D2 Datasheet, Section 7.2.3.41 VDIN, Pages 781, 805. |
| // S905D3 Datasheet, Section 8.2.3.42 VDIN, Pages 717, 740. |
| class VideoInputChannelFifoControl2 |
| : public hwreg::RegisterBase<VideoInputChannelFifoControl2, uint32_t> { |
| public: |
| // Bits 25 and 23-20 provide additional configuration on decimation. |
| // These bits are not defined because this driver currently does not support |
| // decimation. |
| |
| // True iff input decimation subsampling is enabled. |
| DEF_BIT(24, decimation_data_enabled); |
| |
| // Only 1 / (decimation_ratio_minus_one + 1) of the pixels will be sampled. |
| // Setting this field to zero effectively disables decimation. |
| DEF_FIELD(19, 16, decimation_ratio_minus_one); |
| |
| // Bits 7-0 configure VDI 5. These bits are not defined because this driver |
| // currently does not support decimation. |
| |
| static hwreg::RegisterAddr<VideoInputChannelFifoControl2> Get(VideoInputModuleId video_input) { |
| switch (video_input) { |
| case VideoInputModuleId::kVideoInputModule0: |
| return {0x120f * sizeof(uint32_t)}; |
| case VideoInputModuleId::kVideoInputModule1: |
| return {0x130f * sizeof(uint32_t)}; |
| } |
| ZX_DEBUG_ASSERT_MSG(false, "Invalid video input module ID: %d", static_cast<int>(video_input)); |
| } |
| }; |
| |
| // VDIN0_ASFIFO_CTRL3, VDIN1_ASFIFO_CTRL3 |
| // |
| // A311D Datasheet, Section 10.2.3.42 VDIN, Pages 1106, 1128. |
| // S905D2 Datasheet, Section 7.2.3.41 VDIN, Pages 798-799, 822. |
| // S905D3 Datasheet, Section 8.2.3.42 VDIN, Pages 733, 755. |
| class VideoInputChannelFifoControl3 |
| : public hwreg::RegisterBase<VideoInputChannelFifoControl3, uint32_t> { |
| public: |
| static hwreg::RegisterAddr<VideoInputChannelFifoControl3> Get(VideoInputModuleId video_input) { |
| switch (video_input) { |
| case VideoInputModuleId::kVideoInputModule0: |
| return {0x126f * sizeof(uint32_t)}; |
| case VideoInputModuleId::kVideoInputModule1: |
| return {0x136f * sizeof(uint32_t)}; |
| } |
| ZX_DEBUG_ASSERT_MSG(false, "Invalid video input module ID: %d", static_cast<int>(video_input)); |
| } |
| |
| // Bits 31-24 configure VDI 9. |
| // Currently this driver doesn't use VDI channel 9, so we don't define these |
| // bits. |
| |
| // Bits 23-16 configure VDI 8. These bits are not documented in any |
| // datasheet. Experiments on a VIM3 (using Amlogic A311D) show that these |
| // bits correspond to VDI 8 / ASFIFO 8. |
| |
| // Enable data transmission on channel 8. |
| DEF_BIT(23, channel8_data_enabled); |
| |
| // Enable go_field (Vsync) signals on channel 8. |
| DEF_BIT(22, channel8_go_field_signal_enabled); |
| |
| // Enable go_line (Hsync) signals for channel 8. |
| DEF_BIT(21, channel8_go_line_signal_enabled); |
| |
| // True iff the input video on channel 8 has negative polarity Vsync signals. |
| DEF_BIT(20, channel8_input_vsync_is_negative); |
| |
| // True iff the input video on channel 6 has negative polarity Hsync signals. |
| DEF_BIT(19, channel8_input_hsync_is_negative); |
| |
| // If true, the ASFIFO will be reset on each Vsync signal. |
| DEF_BIT(18, channel8_async_fifo_software_reset_on_vsync); |
| |
| // Clears (and acknowledges) the `vdi8_fifo_overflow` bit in the |
| // `VDIN0/1_COM_STATUS2` register. |
| DEF_BIT(17, channel8_clear_fifo_overflow_bit); |
| |
| // Resets the async FIFO. |
| // |
| // This bit is a "level signal" bit. Drivers reset the FIFO by first setting |
| // it to 1, and then to 0. |
| DEF_BIT(16, channel8_async_fifo_software_reset); |
| |
| // Bits 15-9 configure VDI 7. |
| // Currently this driver doesn't use VDI channel 7, so we don't define these |
| // bits. |
| |
| // Enable data transmission on channel 6. |
| DEF_BIT(7, channel6_data_enabled); |
| |
| // Enable go_field (Vsync) signals on channel 6. |
| DEF_BIT(6, channel6_go_field_signal_enabled); |
| |
| // Enable go_line (Hsync) signals on channel 6. |
| DEF_BIT(5, channel6_go_line_signal_enabled); |
| |
| // True iff the input video on channel 6 has negative polarity Vsync signals. |
| DEF_BIT(4, channel6_input_vsync_is_negative); |
| |
| // True iff the input video on channel 6 has negative polarity Hsync signals. |
| DEF_BIT(3, channel6_input_hsync_is_negative); |
| |
| // If true, the channel ASFIFO will be reset on each Vsync signal. |
| DEF_BIT(2, channel6_async_fifo_software_reset_on_vsync); |
| |
| // Clears (and acknowledges) the `vdi6_fifo_overflow` bit in the |
| // `VDIN0/1_COM_STATUS2` register. |
| DEF_BIT(1, channel6_clear_fifo_overflow_bit); |
| |
| // Resets the async FIFO. |
| // |
| // This bit is a "level signal" bit. Drivers reset the FIFO by first setting |
| // it to 1, and then to 0. |
| DEF_BIT(0, channel6_async_fifo_software_reset); |
| }; |
| |
| // Selects the clock or data source for a writeback mux. |
| // |
| // Fields of this type must transition through `kDisabled` when being updated. |
| enum class WritebackMuxSource : uint32_t { |
| // Disable the input path. A required intermediate step for changing input |
| // sources. |
| kDisabled = 0b00000, |
| // VIU ENCI domain. |
| kEncoderInterlaced = 0b00001, |
| // VIU ENCP domain. |
| kEncoderProgressive = 0b00010, |
| // VIU ENCT domain. |
| kEncoderTvPanel = 0b00100, |
| // Also known as "VIU writeback domain 1" in S905D3 datasheets. |
| kViuWriteback0 = 0b01000, |
| // Also known as "VIU writeback domain 2" in S905D3 datasheets. |
| kViuWriteback1 = 0b10000, |
| }; |
| |
| // VPU_VIU_VDIN_IF_MUX_CTRL |
| // |
| // Each VDIN (Video Input Module) can receive image data from one of the data |
| // sources available on the SoC, selected via the `VideoInputCommandControl` |
| // register. Two of the data sources, `kWritebackMux0` and `kWritebackMux1`, |
| // are actually multiplexers connected to multiple sources. |
| // |
| // This register configures the multiplexers. Each multiplexer has clock and |
| // data outputs ("paths" in the datasheets), which are configured separately. |
| // |
| // Many configurations are invalid. For example, a multiplexer's clock and data |
| // outputs must be connected to the same source. To reduce the likelihood of |
| // errors, this register's fields should be accessed exclusively via the |
| // higher-level helper methods defined after the fields. |
| // |
| // S905D3 Datasheet, Section 8.2.3.1 "VPU Registers", Page 314. |
| // |
| // This register is not documented in the datasheets for S905D2, A311D and T931. |
| // However, experiments on VIM3 (Amlogic A311D), Astro (Amlogic S905D2) and |
| // Sherlock (Amlogic T931) show that the register exists and has the same layout |
| // and functionality as that in S905D3. |
| class WritebackMuxControl : public hwreg::RegisterBase<WritebackMuxControl, uint32_t> { |
| public: |
| static hwreg::RegisterAddr<WritebackMuxControl> Get() { return {0x2783 * sizeof(uint32_t)}; } |
| |
| // Selects the data path from VIU/Encoder to writeback mux 1. |
| // |
| // This field is effective when a `VideoInputCommandControl` register selects |
| // the `kWritebackMux1` input ("VDIN0/1 source input 9" in the datasheet). |
| // |
| // It's preferred to use `GetMux1Selection()` and `SetMux1Selection()` helper |
| // functions to change the data path. For direct register manipulations, |
| // `mux1_data_selection` and `mux1_clock_selection` must be equal, otherwise |
| // the behavior is undefined. |
| DEF_ENUM_FIELD(WritebackMuxSource, 28, 24, mux1_data_selection); |
| |
| // Selects the clock path from VIU/Encoder to writeback mux 1. |
| // |
| // This field is effective when a `VideoInputCommandControl` register selects |
| // the `kWritebackMux1` input ("VDIN0/1 source input 9" in the datasheet). |
| // |
| // It's preferred to use `GetMux1Selection()` and `SetMux1Selection()` helper |
| // functions to change the data path. For direct register manipulations, |
| // `mux1_data_selection` and `mux1_clock_selection` must be equal, otherwise |
| // the behavior is undefined. |
| DEF_ENUM_FIELD(WritebackMuxSource, 20, 16, mux1_clock_selection); |
| |
| // Selects the data path from VIU/Encoder to writeback mux 0. |
| // |
| // This field is effective when a `VideoInputCommandControl` register selects |
| // the `kWritebackMux0` input ("VDIN0/1 source input 7" in the datasheet). |
| // |
| // It's preferred to use `GetMux0Selection()` and `SetMux0Selection()` helper |
| // functions to change the data path. For direct register manipulations, |
| // `mux0_data_selection` and `mux0_clock_selection` must be equal, otherwise |
| // the behavior is undefined. |
| DEF_ENUM_FIELD(WritebackMuxSource, 12, 8, mux0_data_selection); |
| |
| // Selects the clock path from VIU/Encoder to writeback mux 0. |
| // |
| // This field is effective when a `VideoInputCommandControl` register selects |
| // the `kWritebackMux0` input ("VDIN0/1 source input 7" in the datasheet). |
| // |
| // It's preferred to use `GetMux0Selection()` and `SetMux0Selection()` helper |
| // functions to change the data path. For direct register manipulations, |
| // `mux0_data_selection` and `mux0_clock_selection` must be equal, otherwise |
| // the behavior is WritebackMuxSelection. |
| DEF_ENUM_FIELD(WritebackMuxSource, 4, 0, mux0_clock_selection); |
| |
| // The clock/data source selected for writeback mux 1. |
| WritebackMuxSource GetMux1Selection() const { |
| WritebackMuxSource clock = mux1_clock_selection(); |
| WritebackMuxSource data = mux1_data_selection(); |
| if (clock != data) { |
| fdf::warn("Writeback mux1 clock selection {} != data selection {}", |
| static_cast<uint32_t>(clock), static_cast<uint32_t>(data)); |
| } |
| return clock; |
| } |
| |
| // Set the data/clock source for writeback mux 1. |
| WritebackMuxControl& SetMux1Selection(WritebackMuxSource mux_selection) { |
| ZX_ASSERT(GetMux1Selection() == WritebackMuxSource::kDisabled || |
| mux_selection == WritebackMuxSource::kDisabled); |
| switch (mux_selection) { |
| case WritebackMuxSource::kDisabled: |
| case WritebackMuxSource::kEncoderInterlaced: |
| case WritebackMuxSource::kEncoderProgressive: |
| case WritebackMuxSource::kEncoderTvPanel: |
| case WritebackMuxSource::kViuWriteback0: |
| case WritebackMuxSource::kViuWriteback1: |
| set_mux1_clock_selection(mux_selection); |
| set_mux1_data_selection(mux_selection); |
| return *this; |
| } |
| ZX_DEBUG_ASSERT_MSG(false, "Invalid mux selection %" PRIu32, |
| static_cast<uint32_t>(mux_selection)); |
| return *this; |
| } |
| |
| // The clock/data source selected for writeback mux 0. |
| WritebackMuxSource GetMux0Selection() const { |
| WritebackMuxSource clock = mux0_clock_selection(); |
| WritebackMuxSource data = mux0_data_selection(); |
| if (clock != data) { |
| fdf::warn("Writeback mux0 clock selection {} != data selection {}", |
| static_cast<uint32_t>(clock), static_cast<uint32_t>(data)); |
| } |
| return clock; |
| } |
| |
| // Set the data/clock source for writeback mux 0. |
| WritebackMuxControl& SetMux0Selection(WritebackMuxSource mux_selection) { |
| ZX_ASSERT(GetMux0Selection() == WritebackMuxSource::kDisabled || |
| mux_selection == WritebackMuxSource::kDisabled); |
| switch (mux_selection) { |
| case WritebackMuxSource::kDisabled: |
| case WritebackMuxSource::kEncoderInterlaced: |
| case WritebackMuxSource::kEncoderProgressive: |
| case WritebackMuxSource::kEncoderTvPanel: |
| case WritebackMuxSource::kViuWriteback0: |
| case WritebackMuxSource::kViuWriteback1: |
| set_mux0_clock_selection(mux_selection); |
| set_mux0_data_selection(mux_selection); |
| return *this; |
| } |
| ZX_DEBUG_ASSERT_MSG(false, "Invalid mux selection %" PRIu32, |
| static_cast<uint32_t>(mux_selection)); |
| return *this; |
| } |
| }; |
| |
| // VDIN0_LFIFO_CTRL, VDIN1_LFIFO_CTRL. |
| // |
| // A311D Datasheet, Section 10.2.3.42 VDIN, Pages 1092, 1114. |
| // S905D2 Datasheet, Section 7.2.3.41 VDIN, Pages 783, 807. |
| // S905D3 Datasheet, Section 8.2.3.42 VDIN, Pages 719, 742. |
| class VideoInputLinearFifoControl |
| : public hwreg::RegisterBase<VideoInputLinearFifoControl, uint32_t> { |
| public: |
| static hwreg::RegisterAddr<VideoInputLinearFifoControl> Get(VideoInputModuleId video_input) { |
| switch (video_input) { |
| case VideoInputModuleId::kVideoInputModule0: |
| return {0x121a * sizeof(uint32_t)}; |
| case VideoInputModuleId::kVideoInputModule1: |
| return {0x131a * sizeof(uint32_t)}; |
| } |
| ZX_DEBUG_ASSERT_MSG(false, "Invalid video input module ID: %d", static_cast<int>(video_input)); |
| } |
| |
| DEF_FIELD(11, 0, linear_fifo_buffer_size_pixels); |
| }; |
| |
| // VDIN0_INTF_WIDTHM1, VDIN1_INTF_WIDTHM1. |
| // |
| // A311D Datasheet, Section 10.2.3.42 VDIN, Pages 1092, 1114. |
| // S905D2 Datasheet, Section 7.2.3.41 VDIN, Pages 783, 807. |
| // S905D3 Datasheet, Section 8.2.3.42 VDIN, Pages 720, 742. |
| class VideoInputInterfaceWidth : public hwreg::RegisterBase<VideoInputInterfaceWidth, uint32_t> { |
| public: |
| static hwreg::RegisterAddr<VideoInputInterfaceWidth> Get(VideoInputModuleId video_input) { |
| switch (video_input) { |
| case VideoInputModuleId::kVideoInputModule0: |
| return {0x121c * sizeof(uint32_t)}; |
| case VideoInputModuleId::kVideoInputModule1: |
| return {0x131c * sizeof(uint32_t)}; |
| } |
| ZX_DEBUG_ASSERT_MSG(false, "Invalid video input module ID: %d", static_cast<int>(video_input)); |
| } |
| |
| // Bit 26 and bit 25 provide additional control over the memory interface |
| // write behavior. They are not used by the driver for capture, so we don't |
| // define them. |
| |
| // Width minus one of the input video after up/downscaling but before |
| // cropping (window function), in pixels. |
| // |
| // It's preferred to use width() and set_width() helpers instead. |
| DEF_FIELD(12, 0, width_minus_one_px); |
| |
| int width_px() const { |
| // The value `width_minus_one_px() + 1` falls within the `int` range |
| // because `width_minus_one_px()` has only 13 bits. |
| return static_cast<int>(width_minus_one_px()) + 1; |
| } |
| |
| // Sets the input image width to `width_px` pixels. |
| // |
| // `width_px` must be positive and not greater than 2^13 (8192). |
| VideoInputInterfaceWidth& set_width_px(int width_px) { |
| ZX_DEBUG_ASSERT(width_px > 0); |
| ZX_DEBUG_ASSERT(width_px <= (1 << 13)); |
| // `width - 1` always fits in the register field because it is guaranteed |
| // to be non-negative and is less than 2^13. |
| return set_width_minus_one_px(static_cast<uint32_t>(width_px - 1)); |
| } |
| }; |
| |
| // VDIN0_WR_H_START_END, VDIN1_WR_H_START_END. |
| // |
| // A311D Datasheet, Section 10.2.3.42 VDIN, Pages 1094, 1116. |
| // S905D2 Datasheet, Section 7.2.3.41 VDIN, Pages 785, 809. |
| // S905D3 Datasheet, Section 8.2.3.42 VDIN, Pages 721, 743. |
| // Display Write Back App-Note, Amlogic (Google internal), Page 4. |
| class VideoInputWriteRangeHorizontal |
| : public hwreg::RegisterBase<VideoInputWriteRangeHorizontal, uint32_t> { |
| public: |
| enum class Endianness : uint8_t { |
| // In big-endian mode, the memory interface writes 8-bit YUV444 capture |
| // images in the following byte order (without 64-bit pair swaps): |
| // Y5 V4 U4 Y4 V3 U3 Y3 V2 U2 Y2 V1 U1 Y1 V0 U0 Y0 |
| // U10 Y10 V9 U9 Y9 V8 U8 Y8 V7 U7 Y7 V6 U6 Y6 V5 U5 |
| // V15 U15 Y15 V14 U14 Y14 V13 U13 Y13 V12 U12 Y12 V11 U11 Y11 V10 ... |
| kBig = 0, |
| // In little-endian mode, the memory interface writes 8-bit YUV444 capture |
| // images in the following byte order (without 64-bit pair swaps): |
| // V0 U0 Y0 V1 U1 Y1 V2 U2 Y2 V3 U3 Y3 V4 U4 Y4 V5 |
| // U5 Y5 V6 U6 Y6 V7 U7 Y7 V8 U8 Y8 V9 U9 Y9 V10 U10 |
| // Y10 V11 U11 Y11 V12 U12 Y12 V13 U13 Y13 V14 U14 Y14 V15 U15 Y15 |
| kLittle = 1, |
| }; |
| |
| static hwreg::RegisterAddr<VideoInputWriteRangeHorizontal> Get(VideoInputModuleId video_input) { |
| switch (video_input) { |
| case VideoInputModuleId::kVideoInputModule0: |
| return {0x1221 * sizeof(uint32_t)}; |
| case VideoInputModuleId::kVideoInputModule1: |
| return {0x1321 * sizeof(uint32_t)}; |
| } |
| ZX_DEBUG_ASSERT_MSG(false, "Invalid video input module ID: %d", static_cast<int>(video_input)); |
| } |
| |
| // Byte ordering ("endianness") for every 128-bit (16-byte) chunk. |
| // |
| // This bit is not documented in any datasheet. Experiments on Khadas |
| // VIM3 (A311D), Astro (S905D2) and Nelson (S905D3) all confirm that |
| // this bit is available and functions as described above for both VDINs. |
| DEF_ENUM_FIELD(Endianness, 30, 30, endianness_128_bit); |
| |
| // If true, the captured contents are flipped horizontally. |
| // |
| // This bit is documented for VDIN0 but not for VDIN1 in Amlogic datasheets. |
| // Both "Display Write Back App-Note" and experiments on Khadas VIM3 |
| // (A311D) confirm that this bit is available and functions as described |
| // above for both VDINs. |
| DEF_BIT(29, horizontally_flipped); |
| |
| // The leftmost column (inclusive) to write on the write image buffer, in |
| // pixels. |
| // |
| // Must be less than or equal to `right_px_inclusive`. |
| // |
| // The Amlogic datasheets specify that this field uses bits 28-16 for VDIN0 |
| // bits 27-16 for VDIN1. However, both the "Display Write Back App-Note" |
| // and experiments on Khadas VIM3 (A311D) confirm that it actually uses |
| // bits 28-16 for both VDINs. |
| // |
| // It's preferred to use the `SetHorizontalRange()` helper method. |
| DEF_FIELD(28, 16, left_px_inclusive); |
| |
| // The rightmost column (inclusive) to write on the write image buffer, in |
| // pixels. |
| // |
| // Must be greater than or equal to `left_px_inclusive`. |
| // |
| // The Amlogic datasheets specify that this field uses bits 12-0 for VDIN0 |
| // bits 11-0 for VDIN1. However, both the "Display Write Back App-Note" |
| // and experiments on Khadas VIM3 (A311D) confirm that it actually uses |
| // bits 12-0 for both VDINs. |
| // |
| // It's preferred to use the `SetHorizontalRange()` helper method. |
| DEF_FIELD(12, 0, right_px_inclusive); |
| |
| // Sets the horizontal range of the write image buffer to |
| // `[left_px_inclusive, right_px_inclusive]`. |
| // |
| // `left_px_inclusive` must be less than or equal to `right_px_inclusive`. |
| // |
| // Both `left_px_inclusive` and `right_px_inclusive` must be non-negative and |
| // less than 2^13 (8192). |
| VideoInputWriteRangeHorizontal& SetHorizontalRange(int left_px_inclusive, |
| int right_px_inclusive) { |
| ZX_DEBUG_ASSERT(left_px_inclusive >= 0); |
| ZX_DEBUG_ASSERT(left_px_inclusive < (1 << 13)); |
| ZX_DEBUG_ASSERT(right_px_inclusive >= 0); |
| ZX_DEBUG_ASSERT(right_px_inclusive < (1 << 13)); |
| ZX_DEBUG_ASSERT(left_px_inclusive <= right_px_inclusive); |
| |
| return set_left_px_inclusive(left_px_inclusive).set_right_px_inclusive(right_px_inclusive); |
| } |
| }; |
| |
| // VDIN0_WR_V_START_END, VDIN1_WR_V_START_END. |
| // |
| // A311D Datasheet, Section 10.2.3.42 VDIN, Pages 1094, 1116. |
| // S905D2 Datasheet, Section 7.2.3.41 VDIN, Pages 785, 809. |
| // S905D3 Datasheet, Section 8.2.3.42 VDIN, Pages 721, 743. |
| class VideoInputWriteRangeVertical |
| : public hwreg::RegisterBase<VideoInputWriteRangeVertical, uint32_t> { |
| public: |
| static hwreg::RegisterAddr<VideoInputWriteRangeVertical> Get(VideoInputModuleId video_input) { |
| switch (video_input) { |
| case VideoInputModuleId::kVideoInputModule0: |
| return {0x1222 * sizeof(uint32_t)}; |
| case VideoInputModuleId::kVideoInputModule1: |
| return {0x1322 * sizeof(uint32_t)}; |
| } |
| ZX_DEBUG_ASSERT_MSG(false, "Invalid video input module ID: %d", static_cast<int>(video_input)); |
| } |
| |
| // If true, the captured contents are flipped vertically. |
| // |
| // This bit is documented for VDIN0 but not documented for VDIN1 in any |
| // Amlogic datasheets. Both "Display Write Back App-Note" and experiments on |
| // Khadas VIM3 (A311D) show that both video input modules have this bit |
| // available. |
| DEF_BIT(29, vertically_flipped); |
| |
| // The top row (inclusive) to write on the write image buffer, in lines. |
| // |
| // Must be less than or equal to `bottom_inclusive`. |
| // |
| // The Amlogic datasheets specify that this field uses bits 28-16 for VDIN0 |
| // bits 27-16 for VDIN1. However, both the "Display Write Back App-Note" |
| // and experiments on Khadas VIM3 (A311D) confirm that it actually uses |
| // bits 28-16 for both VDINs. |
| // |
| // It's preferred to use the `SetVerticalRange()` helper method. |
| DEF_FIELD(28, 16, top_line_inclusive); |
| |
| // The bottom row (inclusive) to write on the write image buffer, in lines. |
| // |
| // Must be greater than or equal to `top_inclusive`. |
| // |
| // The Amlogic datasheets specify that this field uses bits 12-0 for VDIN0 |
| // bits 11-0 for VDIN1. However, both the "Display Write Back App-Note" |
| // and experiments on Khadas VIM3 (A311D) confirm that it actually uses |
| // bits 12-0 for both VDINs. |
| // |
| // It's preferred to use the `SetVerticalRange()` helper method. |
| DEF_FIELD(12, 0, bottom_line_inclusive); |
| |
| // Set the vertical range of the write image buffer to `[top_line_inclusive, |
| // bottom_line_inclusive]`. |
| // |
| // `top_line_inclusive` must be less than or equal to `bottom_line_inclusive`. |
| // |
| // Both `top_line_inclusive` and `bottom_line_inclusive` must be non-negative |
| // and less than 2^13 (8192). |
| VideoInputWriteRangeVertical& SetVerticalRange(int top_line_inclusive, |
| int bottom_line_inclusive) { |
| ZX_DEBUG_ASSERT(top_line_inclusive >= 0); |
| ZX_DEBUG_ASSERT(top_line_inclusive < (1 << 13)); |
| ZX_DEBUG_ASSERT(bottom_line_inclusive >= 0); |
| ZX_DEBUG_ASSERT(bottom_line_inclusive < (1 << 13)); |
| ZX_DEBUG_ASSERT(top_line_inclusive <= bottom_line_inclusive); |
| |
| return set_top_line_inclusive(top_line_inclusive) |
| .set_bottom_line_inclusive(bottom_line_inclusive); |
| } |
| }; |
| |
| // VDIN0_WR_CTRL, VDIN1_WR_CTRL. |
| // |
| // A311D Datasheet, Section 10.2.3.42 VDIN, Pages 1093, 1115-1116. |
| // S905D2 Datasheet, Section 7.2.3.41 VDIN, Pages 784-785, 808-809. |
| // S905D3 Datasheet, Section 8.2.3.42 VDIN, Pages 720-721, 743. |
| // Private correspondence with Amlogic (Re: display loopback mode), June 27, |
| // 2019. |
| class VideoInputWriteControl : public hwreg::RegisterBase<VideoInputWriteControl, uint32_t> { |
| public: |
| enum class ChromaHorizontalSubsamplingMode : uint8_t { |
| // Only the chroma channels of even pixels in a row are sampled. |
| // The image is downsampled to YUV 4:2:x format. |
| kEvenPixelsOnly = 0, |
| // Only the chroma channels of odd pixels in a row are sampled. |
| // The image is downsampled to YUV 4:2:x format. |
| kOddPixelsOnly = 1, |
| // The average value of chroma channel of each pixel pair in a row are |
| // sampled. |
| // The image is downsampled to YUV 4:2:x format. |
| kAveragedPixels = 2, |
| // The Chroma channels of all pixels are used; no subsampling is performed. |
| // The image is downsampled to YUV 4:4:x format. |
| // |
| // To use this subsampling mode, `write_format` must be `kSemiPlanar`. |
| kAllPixels = 3, |
| }; |
| |
| enum class LineEndIndicator : uint8_t { |
| // Uses EOL (End of Line) as the line-end indication. The precise |
| // definition of "EOL" is unclear from the datasheets. |
| kEol = 0, |
| // Uses the line width, as specified in the `VideoInputInterfaceWidth` |
| // register, to determine line endings. |
| kWidth = 1, |
| }; |
| |
| enum class ChromaChannelLayout : uint8_t { |
| // First Cb (U), then Cr (V). Used in the NV12 chroma subplane. |
| kCbCr = 0, |
| // First Cr (V), then Cb (U). Used in the NV21 chroma subplane. |
| kCrCb = 1, |
| }; |
| |
| enum class ChromaVerticalSubsamplingMode : uint8_t { |
| // Only the chroma channels of pixels in even rows will be sampled. |
| // The image will be downsampled to YUV 4:x:0 format. |
| kEvenRowsOnly = 0, |
| // Only the chroma channels of pixels in odd rows will be sampled. |
| // The image will be downsampled to YUV 4:x:0 format. |
| kOddRowsOnly = 1, |
| kReserved = 2, |
| // Chroma channels of all rows will be used; no subsampling is performed. |
| // The image will be downsampled to YUV 4:x:x format. |
| kAllRows = 3, |
| }; |
| |
| enum class YuvSamplingStorageFormat : uint8_t { |
| // Image is in a YUV 4:2:2 packed (single-planar) format, stored in the Luma |
| // canvas. |
| // The detailed macropixel encoding is unclear. |
| kYuv422Packed = 0, |
| // Image is in a YUV 4:4:4 packed (single-planar) format, stored in the Luma |
| // canvas. |
| // |
| // Note: RGB888 format can be considered as a special YUV 4:4:4 packed |
| // format where the R/G/B channels correspond to Y/Cb/Cr channels |
| // respectively. |
| kYuv444Packed = 1, |
| // Image is in a YUV semi-planar format. The Luma plane is stored in the |
| // Luma canvas and the Chroma plane is stored in the Chroma canvas. |
| kSemiPlanar = 2, |
| // Image is in a YUV 4:2:2 10-bit "fully packed" (single-planar) format, |
| // stored in the Luma canvas. |
| // The detailed macropixel encoding is unclear. |
| kYuv422FullyPackedMode = 3, |
| }; |
| |
| static hwreg::RegisterAddr<VideoInputWriteControl> Get(VideoInputModuleId video_input) { |
| switch (video_input) { |
| case VideoInputModuleId::kVideoInputModule0: |
| return {0x1220 * sizeof(uint32_t)}; |
| case VideoInputModuleId::kVideoInputModule1: |
| return {0x1320 * sizeof(uint32_t)}; |
| } |
| ZX_DEBUG_ASSERT_MSG(false, "Invalid video input module ID: %d", static_cast<int>(video_input)); |
| } |
| |
| // Effective iff `yuv_sampling_storage_format` is `kYuv422Packed` or |
| // `kSemiPlanar`. |
| DEF_ENUM_FIELD(ChromaHorizontalSubsamplingMode, 31, 30, chroma_horizontal_subsampling_mode); |
| |
| // True iff the clock gate for the memory interface is disabled, which means |
| // that the memory interface uses a free running clock (i.e. a clock that is |
| // not interrupted nor gated). |
| DEF_BIT(29, memory_interface_clock_gate_disabled); |
| |
| // If true, the counter field for memory interface responses is cleared. |
| // The register it clears is unknown from the datasheets. |
| DEF_BIT(28, memory_interface_response_counter_cleared); |
| |
| // Selects the line end indicator in the memory write interface. |
| DEF_ENUM_FIELD(LineEndIndicator, 27, 27, line_end_indicator_selection); |
| |
| // True iff the frame is reset on each Vsync. |
| DEF_BIT(23, frame_reset_on_vsync); |
| |
| // True iff a software reset is performed on the line FIFO on each Vsync. |
| DEF_BIT(22, line_fifo_reset_on_vsync); |
| |
| // If true, the `direct_write_done` bit of the `VideoInputCommandStatus0` |
| // register is cleared. |
| DEF_BIT(21, direct_write_done_cleared); |
| |
| // If true, the `noise_reduced_write_done` bit of the |
| // `VideoInputCommandStatus0` register is cleared. |
| DEF_BIT(20, noise_reduced_write_done_cleared); |
| |
| // True iff the data is reordered by swapping consecutive pairs of 64-bit |
| // chunks. |
| DEF_BIT(19, consecutive_64bit_pairs_reordered); |
| |
| // The layout of channels in each macropixel of the chroma subplane. |
| // |
| // Effective iff `yuv_sampling_storage_format` is `kSemiPlanar`. |
| DEF_ENUM_FIELD(ChromaChannelLayout, 18, 18, chroma_channel_layout); |
| |
| // Effective iff `yuv_sampling_storage_format` is `kSemiPlanar`. |
| DEF_ENUM_FIELD(ChromaVerticalSubsamplingMode, 17, 16, chroma_vertical_subsampling_mode); |
| |
| // YUV sampling and storage format of the image to write. |
| DEF_ENUM_FIELD(YuvSamplingStorageFormat, 13, 12, yuv_sampling_storage_format); |
| |
| // True iff the value of `luma_canvas_address` is double-buffered. In that |
| // case, the new value written to the register field is applied on each Vsync. |
| DEF_BIT(11, luma_canvas_address_double_buffered); |
| |
| // If true, unpauses the write request. |
| // |
| // This bit is not in any datasheet but was mentioned in private |
| // correspondence with Amlogic. Experiments on Khadas VIM3 (A311D), |
| // Astro (S905D2) and Nelson (S905D3) all show that this bit is available |
| // and functions as described above. |
| DEF_BIT(10, write_request_unpaused); |
| |
| // True iff the next memory write request is of higher priority. |
| DEF_BIT(9, write_request_urgent); |
| |
| // True iff the memory write interface allows memory write requests. |
| DEF_BIT(8, write_request_enabled); |
| |
| // The address to the Luma canvas for the memory interface to write to. |
| DEF_FIELD(7, 0, luma_canvas_address); |
| }; |
| |
| // VDIN_MISC_CTRL |
| // |
| // This register is not documented in A311D or S905D2 datasheets, but it |
| // appears in S905D3 datasheets and private correspondence with Amlogic. |
| // |
| // Experiments on Khadas VIM3 board (A311D) and Astro (S905D2) show that |
| // the bits defined in this class are available and have the same function |
| // as S905D3. |
| // |
| // S905D3 Datasheet, Section 8.2.3.1 "VPU Registers", Page 314. |
| // Private correspondence with Amlogic (Re: display loopback mode), June 27, |
| // 2019. |
| class VideoInputMiscellaneousControl |
| : public hwreg::RegisterBase<VideoInputMiscellaneousControl, uint32_t> { |
| public: |
| static hwreg::RegisterAddr<VideoInputMiscellaneousControl> Get() { |
| return {0x2782 * sizeof(uint32_t)}; |
| } |
| |
| DEF_RSVDZ_FIELD(31, 26); |
| |
| // Bits 25-6, as defined in the datasheets, provide additional control over |
| // the VDIN output behavior. They are not defined because the the driver |
| // doesn't use them. |
| |
| // If true, the memory interface of video input module 1 (VDIN1) is reset. |
| // |
| // Experiments on Khadas VIM3 (A311D) show that the memory interface is reset |
| // when the bit is held true for 1us. |
| DEF_BIT(4, video_input_module1_memory_interface_reset); |
| |
| // If true, the memory interface of video input module 0 (VDIN0) is reset. |
| // |
| // Experiments on Khadas VIM3 (A311D) show that the memory interface is |
| // reset when the bit is held true for 1us. |
| DEF_BIT(3, video_input_module0_memory_interface_reset); |
| |
| // If true, the video input module 1 (VDIN1) is reset. |
| // |
| // Experiments on Khadas VIM3 (A311D) shows that the memory interface is |
| // reset when the bit is held true for 1us. |
| DEF_BIT(1, video_input_module1_reset); |
| |
| // If true, the video input module 0 (VDIN0) is reset. |
| // |
| // Experiments on Khadas VIM3 (A311D) show that the video input module is |
| // reset when the bit is held true for 1us. |
| DEF_BIT(0, video_input_module0_reset); |
| }; |
| |
| } // namespace amlogic_display |
| |
| #endif // SRC_GRAPHICS_DISPLAY_DRIVERS_AMLOGIC_DISPLAY_VIDEO_INPUT_REGS_H_ |