blob: 3a2d7ff1798719fc7146704d56693d8c0f58da3f [file] [log] [blame]
// 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.
#ifndef SRC_GRAPHICS_DISPLAY_DRIVERS_AMLOGIC_DISPLAY_OSD_H_
#define SRC_GRAPHICS_DISPLAY_DRIVERS_AMLOGIC_DISPLAY_OSD_H_
#include <fuchsia/hardware/display/controller/cpp/banjo.h>
#include <lib/device-protocol/pdev.h>
#include <lib/inspect/cpp/inspect.h>
#include <lib/mmio/mmio.h>
#include <lib/zircon-internal/thread_annotations.h>
#include <lib/zx/bti.h>
#include <lib/zx/handle.h>
#include <lib/zx/interrupt.h>
#include <lib/zx/pmt.h>
#include <threads.h>
#include <zircon/compiler.h>
#include <zircon/types.h>
#include <cstdint>
#include <optional>
#include <fbl/auto_lock.h>
#include <fbl/condition_variable.h>
#include <fbl/mutex.h>
#include "common.h"
namespace amlogic_display {
struct RdmaTable {
uint32_t reg;
uint32_t val;
};
/*
* This is the RDMA table index. Each index points to a specific VPU register.
* RDMA engine will be programmed to update all those registers at vsync time.
* Since all the fields will be updated at vsync time, we need to make sure all
* the fields are updated with a valid value when FlipOnVsync is called.
*/
enum {
IDX_BLK0_CFG_W0,
IDX_CTRL_STAT,
IDX_CTRL_STAT2,
IDX_MATRIX_COEF00_01,
IDX_MATRIX_COEF02_10,
IDX_MATRIX_COEF11_12,
IDX_MATRIX_COEF20_21,
IDX_MATRIX_COEF22,
IDX_MATRIX_OFFSET0_1,
IDX_MATRIX_OFFSET2,
IDX_MATRIX_PRE_OFFSET0_1,
IDX_MATRIX_PRE_OFFSET2,
IDX_MATRIX_EN_CTRL,
IDX_GAMMA_EN,
IDX_BLK2_CFG_W4,
IDX_MALI_UNPACK_CTRL,
IDX_PATH_MISC_CTRL,
IDX_AFBC_HEAD_BUF_ADDR_LOW,
IDX_AFBC_HEAD_BUF_ADDR_HIGH,
IDX_AFBC_SURFACE_CFG,
// This should be the last element to make sure the entire config
// was written
IDX_RDMA_CFG_STAMP_HIGH,
IDX_RDMA_CFG_STAMP_LOW,
IDX_MAX,
};
static_assert(IDX_RDMA_CFG_STAMP_HIGH == (IDX_MAX - 2), "Invalid RDMA Index Table");
static_assert(IDX_RDMA_CFG_STAMP_LOW == (IDX_MAX - 1), "Invalid RDMA Index Table");
// Table size for non-AFBC RDMA
constexpr size_t kTableSize = (IDX_MAX * sizeof(RdmaTable));
// Single element table for AFBC RDMA
constexpr size_t kAfbcTableSize = sizeof(RdmaTable);
// Non-AFBC RDMA Region size
constexpr size_t kRdmaRegionSize = ZX_PAGE_SIZE;
// AFBC RDMA Region Size
constexpr size_t kAfbcRdmaRegionSize = ZX_PAGE_SIZE;
// Arbitrarily limit table size to maximum 16
constexpr uint32_t kNumberOfTables = std::min(16ul, (kRdmaRegionSize / (kTableSize)));
// We should have space for at least 3 tables. If RDMA table has grown too large and cannot
// fit more than 3 tables within a PAGE_SIZE, we need to either:
// - Re-evaluate why RDMA table has grown so large
// - Create a larger RDMA table allocation
static_assert(kNumberOfTables >= 3, "RDMA table is too large");
// This value indicates an available entry into the RDMA table
constexpr uint64_t kRdmaTableReady = UINT64_MAX - 1;
// An entry is marked as unavailable temporarily when there are unapplied configs. This will ensure
// new configs are added to end of table since RDMA requires physical contiguous entries
constexpr uint64_t kRdmaTableUnavailable = UINT64_MAX;
// Use RDMA Channel used
constexpr uint8_t kRdmaChannel = 0;
// RDMA Channel 7 will be dedicated to AFBC Trigger
constexpr uint8_t kAfbcRdmaChannel = 7;
enum class GammaChannel {
kRed,
kGreen,
kBlue,
};
struct RdmaChannelContainer {
zx_paddr_t phys_offset; // offset into physical address
uint8_t* virt_offset; // offset into virtual address (vmar buf)
};
/*
* RDMA Operation Design (non-AFBC):
* Allocate kRdmaRegionSize of physical contiguous memory. This region will include
* kNumberOfTables of RDMA Tables.
* RDMA Tables will get populated with <reg><val> pairs. The last element will be a unique
* stamp for a given configuration. The stamp is used to verify how far the RDMA channel was able
* to write at vsync time.
* _______________
* |<reg><val> |
* |<reg><val> |
* |... |
* |<Config Stamp> |
* |_______________|
* |<reg><val> |
* |<reg><val> |
* |... |
* |<Config Stamp> |
* |_______________|
* .
* .
* .
* |<reg><val> |
* |<reg><val> |
* |... |
* |<Config Stamp> |
* |_______________|
* |<reg><val> |
* |<reg><val> |
* |... |
* |<Config Stamp> |
* |_______________|
*
* The physical and virtual addresses of the above tables are stored in rdma_chnl_container_
*
* Each table contains a specific configuration to be applied at Vsync time. RDMA is programmed
* to read from [start_index_used_ end_index_used_] inclusive. If more than one configuration is
* applied within a vsync period, the new configuration will get added at the
* next sequential index and RDMA end_index_used_ will get updated.
*
* rdma_usage_table_ is used to keep track of tables being used by RDMA. rdma_usage_table_ may
* contain three possible values:
* kRdmaTableReady: This index may be used by RDMA
* kRdmaTableUnavailable: This index is unavailble
* <config stamp>: This index is includes a valid config
*
* When RDMA completes, RdmaThread (which processes RDMA IRQs) checks how far the RDMA was able to
* write by comparing the "Config Stamp" in a scratch register to rdma_usage_table_. If RDMA did
* not apply all the configs, it will re-schedule a new RDMA transaction.
* RdmaThread will also signal the Vsync thread and provide the most recent applied configuration
* (latest_applied_config_)
*/
class Osd {
public:
Osd(bool supports_afbc, uint32_t fb_width, uint32_t fb_height, uint32_t display_width,
uint32_t display_height, inspect::Node* parent_node)
: supports_afbc_(supports_afbc),
fb_width_(fb_width),
fb_height_(fb_height),
display_width_(display_width),
display_height_(display_height),
inspect_node_(parent_node->CreateChild("osd")),
rdma_allocation_failures_(inspect_node_.CreateUint("rdma_allocation_failures", 0)) {}
zx_status_t Init(ddk::PDev& pdev);
void HwInit();
void Disable();
void Enable();
// This function will apply configuration when VSYNC interrupt occurs using RDMA
void FlipOnVsync(uint8_t idx, const display_config_t* config);
void Dump();
void Release();
void DumpRdmaState() __TA_REQUIRES(rdma_lock_);
// This function converts a float into Signed fixed point 3.10 format
// [12][11:10][9:0] = [sign][integer][fraction]
static uint32_t FloatToFixed3_10(float f);
// This function converts a float into Signed fixed point 2.10 format
// [11][10][9:0] = [sign][integer][fraction]
static uint32_t FloatToFixed2_10(float f);
static constexpr size_t kGammaTableSize = 256;
void SetMinimumRgb(uint8_t minimum_rgb);
// This function is used by vsync thread to determine the latest config applied
uint64_t GetLastImageApplied();
private:
void DefaultSetup();
// this function sets up scaling based on framebuffer and actual display
// dimensions. The scaling IP and registers and undocumented.
void EnableScaling(bool enable);
void StopRdma();
zx_status_t SetupRdma();
void ResetRdmaTable();
void SetRdmaTableValue(uint32_t table_index, uint32_t idx, uint32_t val);
void FlushRdmaTable(uint32_t table_index);
void SetAfbcRdmaTableValue(uint32_t val) const;
void FlushAfbcRdmaTable() const;
int RdmaThread() __TA_EXCLUDES(rdma_lock_);
void EnableGamma();
void DisableGamma();
zx_status_t ConfigAfbc();
zx_status_t SetGamma(GammaChannel channel, const float* data);
void SetColorCorrection(uint32_t rdma_table_idx, const display_config_t* config);
zx_status_t WaitForGammaAddressReady();
zx_status_t WaitForGammaWriteReady();
void WaitForRdmaIdle() __TA_REQUIRES(rdma_lock_);
int GetNextAvailableRdmaTableIndex();
std::optional<ddk::MmioBuffer> vpu_mmio_;
zx::bti bti_;
// RDMA IRQ handle and thread
zx::interrupt rdma_irq_;
thrd_t rdma_thread_;
fbl::Mutex rdma_lock_;
fbl::ConditionVariable rdma_active_cnd_ TA_GUARDED(rdma_lock_);
uint64_t rdma_usage_table_[kNumberOfTables] TA_GUARDED(rdma_lock_);
size_t start_index_used_ TA_GUARDED(rdma_lock_) = 0;
size_t end_index_used_ TA_GUARDED(rdma_lock_) = 0;
bool rdma_active_ TA_GUARDED(rdma_lock_) = false;
uint64_t latest_applied_config_ TA_GUARDED(rdma_lock_) = 0;
RdmaChannelContainer rdma_chnl_container_[kNumberOfTables];
// use a single vmo for all channels
zx::vmo rdma_vmo_;
zx::pmt rdma_pmt_;
zx_paddr_t rdma_phys_;
uint8_t* rdma_vbuf_;
// Container that holds AFBC specific trigger register
RdmaChannelContainer afbc_rdma_chnl_container_;
zx::vmo afbc_rdma_vmo_;
zx_handle_t afbc_rdma_pmt_;
zx_paddr_t afbc_rdma_phys_;
uint8_t* afbc_rdma_vbuf_;
const bool supports_afbc_;
// Framebuffer dimension
uint32_t fb_width_;
uint32_t fb_height_;
// Actual display dimension
uint32_t display_width_;
uint32_t display_height_;
// This flag is set when the driver enables gamma correction.
// If this flag is not set, we should not disable gamma in the absence
// of a gamma table since that might have been provided by earlier boot stages.
bool osd_enabled_gamma_ = false;
bool initialized_ = false;
inspect::Node inspect_node_;
inspect::UintProperty rdma_allocation_failures_;
};
} // namespace amlogic_display
#endif // SRC_GRAPHICS_DISPLAY_DRIVERS_AMLOGIC_DISPLAY_OSD_H_