blob: c7aa20dc685980e5644524f1e1dfcbf7fe5e291c [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.
#include "src/graphics/display/drivers/amlogic-display/mipi-phy.h"
#include <fidl/fuchsia.hardware.platform.device/cpp/wire.h>
#include <lib/mmio/mmio-buffer.h>
#include <lib/zx/result.h>
#include <cstdint>
#include <memory>
#include <fbl/algorithm.h>
#include <fbl/alloc_checker.h>
#include "src/graphics/display/drivers/amlogic-display/board-resources.h"
#include "src/graphics/display/drivers/amlogic-display/common.h"
#include "src/graphics/display/drivers/amlogic-display/dsi.h"
#include "src/graphics/display/lib/designware-dsi/dsi-host-controller.h"
#include "src/graphics/display/lib/driver-framework-migration-utils/logging/zxlogf.h"
namespace amlogic_display {
template <typename T>
constexpr inline uint8_t NsToLaneByte(T x, int64_t lanebytetime) {
return (static_cast<uint8_t>((x + lanebytetime - 1) / lanebytetime) & 0xFF);
}
constexpr int64_t kUnit = int64_t{1} * 1000 * 1000 * 100;
zx::result<> MipiPhy::PhyCfgLoad(int64_t bitrate) {
// According to MIPI -PHY Spec, we need to define Unit Interval (UI).
// This UI is defined as the time it takes to send a bit (i.e. bitrate)
// The x100 is to ensure the ui is not rounded too much (i.e. 2.56 --> 256)
// However, since we have introduced x100, we need to make sure we include x100
// to all the PHY timings that are in ns units.
const int64_t ui = kUnit / (bitrate / 1000);
// Calculate values will be rounded by the lanebyteclk
const int64_t lanebytetime = ui * 8;
// lp_tesc:TX Escape Clock Division factor (from linebyteclk). Round up to units of ui
dsi_phy_cfg_.lp_tesc = NsToLaneByte(DPHY_TIME_LP_TESC, lanebytetime);
// lp_lpx: Transmit length of any LP state period
dsi_phy_cfg_.lp_lpx = NsToLaneByte(DPHY_TIME_LP_LPX, lanebytetime);
// lp_ta_sure
dsi_phy_cfg_.lp_ta_sure = NsToLaneByte(DPHY_TIME_LP_TA_SURE, lanebytetime);
// lp_ta_go
dsi_phy_cfg_.lp_ta_go = NsToLaneByte(DPHY_TIME_LP_TA_GO, lanebytetime);
// lp_ta_get
dsi_phy_cfg_.lp_ta_get = NsToLaneByte(DPHY_TIME_LP_TA_GET, lanebytetime);
// hs_exit
dsi_phy_cfg_.hs_exit = NsToLaneByte(DPHY_TIME_HS_EXIT, lanebytetime);
// clk-_prepare
dsi_phy_cfg_.clk_prepare = NsToLaneByte(DPHY_TIME_CLK_PREPARE, lanebytetime);
// clk_zero
dsi_phy_cfg_.clk_zero = NsToLaneByte(DPHY_TIME_CLK_ZERO(ui), lanebytetime);
// clk_pre
dsi_phy_cfg_.clk_pre = NsToLaneByte(DPHY_TIME_CLK_PRE(ui), lanebytetime);
// init
dsi_phy_cfg_.init = NsToLaneByte(DPHY_TIME_INIT, lanebytetime);
// wakeup
dsi_phy_cfg_.wakeup = NsToLaneByte(DPHY_TIME_WAKEUP, lanebytetime);
// clk_trail
dsi_phy_cfg_.clk_trail = NsToLaneByte(DPHY_TIME_CLK_TRAIL, lanebytetime);
// clk_post
dsi_phy_cfg_.clk_post = NsToLaneByte(DPHY_TIME_CLK_POST(ui), lanebytetime);
// hs_trail
dsi_phy_cfg_.hs_trail = NsToLaneByte(DPHY_TIME_HS_TRAIL(ui), lanebytetime);
// hs_prepare
dsi_phy_cfg_.hs_prepare = NsToLaneByte(DPHY_TIME_HS_PREPARE(ui), lanebytetime);
// hs_zero
dsi_phy_cfg_.hs_zero = NsToLaneByte(DPHY_TIME_HS_ZERO(ui), lanebytetime);
// Ensure both clk-trail and hs-trail do not exceed Teot (End of Transmission Time)
const uint32_t time_req_max = NsToLaneByte(DPHY_TIME_EOT(ui), lanebytetime);
if ((dsi_phy_cfg_.clk_trail > time_req_max) || (dsi_phy_cfg_.hs_trail > time_req_max)) {
zxlogf(ERROR, "Invalid clk-trail and/or hs-trail exceed Teot!");
zxlogf(ERROR, "clk-trail = 0x%02x, hs-trail = 0x%02x, Teot = 0x%02x", dsi_phy_cfg_.clk_trail,
dsi_phy_cfg_.hs_trail, time_req_max);
return zx::error(ZX_ERR_OUT_OF_RANGE);
}
zxlogf(TRACE,
"lp_tesc = 0x%02x"
"lp_lpx = 0x%02x"
"lp_ta_sure = 0x%02x"
"lp_ta_go = 0x%02x"
"lp_ta_get = 0x%02x"
"hs_exit = 0x%02x"
"hs_trail = 0x%02x"
"hs_zero = 0x%02x"
"hs_prepare = 0x%02x"
"clk_trail = 0x%02x"
"clk_post = 0x%02x"
"clk_zero = 0x%02x"
"clk_prepare = 0x%02x"
"clk_pre = 0x%02x"
"init = 0x%02x"
"wakeup = 0x%02x",
dsi_phy_cfg_.lp_tesc, dsi_phy_cfg_.lp_lpx, dsi_phy_cfg_.lp_ta_sure, dsi_phy_cfg_.lp_ta_go,
dsi_phy_cfg_.lp_ta_get, dsi_phy_cfg_.hs_exit, dsi_phy_cfg_.hs_trail, dsi_phy_cfg_.hs_zero,
dsi_phy_cfg_.hs_prepare, dsi_phy_cfg_.clk_trail, dsi_phy_cfg_.clk_post,
dsi_phy_cfg_.clk_zero, dsi_phy_cfg_.clk_prepare, dsi_phy_cfg_.clk_pre, dsi_phy_cfg_.init,
dsi_phy_cfg_.wakeup);
return zx::ok();
}
void MipiPhy::PhyInit() {
// Enable phy clock.
dsi_phy_mmio_.Write32(PHY_CTRL_TXDDRCLK_EN | PHY_CTRL_DDRCLKPATH_EN | PHY_CTRL_CLK_DIV_COUNTER |
PHY_CTRL_CLK_DIV_EN | PHY_CTRL_BYTECLK_EN,
MIPI_DSI_PHY_CTRL);
// Toggle PHY CTRL RST
dsi_phy_mmio_.Write32(SetFieldValue32(dsi_phy_mmio_.Read32(MIPI_DSI_PHY_CTRL),
/*field_begin_bit=*/PHY_CTRL_RST_START,
/*field_size_bits=*/PHY_CTRL_RST_BITS, /*field_value=*/1),
MIPI_DSI_PHY_CTRL);
dsi_phy_mmio_.Write32(SetFieldValue32(dsi_phy_mmio_.Read32(MIPI_DSI_PHY_CTRL),
/*field_begin_bit=*/PHY_CTRL_RST_START,
/*field_size_bits=*/PHY_CTRL_RST_BITS, /*field_value=*/0),
MIPI_DSI_PHY_CTRL);
dsi_phy_mmio_.Write32((dsi_phy_cfg_.clk_trail | (dsi_phy_cfg_.clk_post << 8) |
(dsi_phy_cfg_.clk_zero << 16) | (dsi_phy_cfg_.clk_prepare << 24)),
MIPI_DSI_CLK_TIM);
dsi_phy_mmio_.Write32(dsi_phy_cfg_.clk_pre, MIPI_DSI_CLK_TIM1);
dsi_phy_mmio_.Write32((dsi_phy_cfg_.hs_exit | (dsi_phy_cfg_.hs_trail << 8) |
(dsi_phy_cfg_.hs_zero << 16) | (dsi_phy_cfg_.hs_prepare << 24)),
MIPI_DSI_HS_TIM);
dsi_phy_mmio_.Write32((dsi_phy_cfg_.lp_lpx | (dsi_phy_cfg_.lp_ta_sure << 8) |
(dsi_phy_cfg_.lp_ta_go << 16) | (dsi_phy_cfg_.lp_ta_get << 24)),
MIPI_DSI_LP_TIM);
dsi_phy_mmio_.Write32(ANA_UP_TIME, MIPI_DSI_ANA_UP_TIM);
dsi_phy_mmio_.Write32(dsi_phy_cfg_.init, MIPI_DSI_INIT_TIM);
dsi_phy_mmio_.Write32(dsi_phy_cfg_.wakeup, MIPI_DSI_WAKEUP_TIM);
dsi_phy_mmio_.Write32(LPOK_TIME, MIPI_DSI_LPOK_TIM);
dsi_phy_mmio_.Write32(ULPS_CHECK_TIME, MIPI_DSI_ULPS_CHECK);
dsi_phy_mmio_.Write32(LP_WCHDOG_TIME, MIPI_DSI_LP_WCHDOG);
dsi_phy_mmio_.Write32(TURN_WCHDOG_TIME, MIPI_DSI_TURN_WCHDOG);
dsi_phy_mmio_.Write32(0, MIPI_DSI_CHAN_CTRL);
}
void MipiPhy::Shutdown() {
if (!phy_enabled_) {
return;
}
// Power down DSI
designware_dsi_host_controller_.PowerDown();
dsi_phy_mmio_.Write32(0x1f, MIPI_DSI_CHAN_CTRL);
dsi_phy_mmio_.Write32(
SetFieldValue32(dsi_phy_mmio_.Read32(MIPI_DSI_PHY_CTRL), /*field_begin_bit=*/7,
/*field_size_bits=*/1, /*field_value=*/0),
MIPI_DSI_PHY_CTRL);
phy_enabled_ = false;
}
zx::result<> MipiPhy::Startup() {
if (phy_enabled_) {
return zx::ok();
}
// Power up DSI
designware_dsi_host_controller_.PowerUp();
// Setup Parameters of DPHY
// Below we are sending test code 0x44 with parameter 0x74. This means
// we are setting up the phy to operate in 1050-1099 Mbps mode
// TODO(payamm): Find out why 0x74 was selected
designware_dsi_host_controller_.PhySendCode(0x00010044, 0x00000074);
// Power up D-PHY
designware_dsi_host_controller_.PhyPowerUp();
// Setup PHY Timing parameters
PhyInit();
// Wait for PHY to be read
zx_status_t status = designware_dsi_host_controller_.PhyWaitForReady();
if (status != ZX_OK) {
// no need to print additional info.
return zx::error(status);
}
// Trigger a sync active for esc_clk
dsi_phy_mmio_.Write32(
SetFieldValue32(dsi_phy_mmio_.Read32(MIPI_DSI_PHY_CTRL), /*field_begin_bit=*/1,
/*field_size_bits=*/1, /*field_value=*/1),
MIPI_DSI_PHY_CTRL);
phy_enabled_ = true;
return zx::ok();
}
// static
zx::result<std::unique_ptr<MipiPhy>> MipiPhy::Create(
fidl::UnownedClientEnd<fuchsia_hardware_platform_device::Device> platform_device,
designware_dsi::DsiHostController* designware_dsi_host_controller, bool enabled) {
ZX_DEBUG_ASSERT(platform_device.is_valid());
zx::result<fdf::MmioBuffer> d_phy_mmio_result =
MapMmio(MmioResourceIndex::kDsiPhy, platform_device);
if (d_phy_mmio_result.is_error()) {
return d_phy_mmio_result.take_error();
}
fdf::MmioBuffer d_phy_mmio = std::move(d_phy_mmio_result).value();
fbl::AllocChecker alloc_checker;
auto mipi_phy = fbl::make_unique_checked<MipiPhy>(&alloc_checker, std::move(d_phy_mmio),
designware_dsi_host_controller, enabled);
if (!alloc_checker.check()) {
zxlogf(ERROR, "Failed to allocate memory for Lcd");
return zx::error(ZX_ERR_NO_MEMORY);
}
return zx::ok(std::move(mipi_phy));
}
MipiPhy::MipiPhy(fdf::MmioBuffer d_phy_mmio,
designware_dsi::DsiHostController* designware_dsi_host_controller, bool enabled)
: dsi_phy_mmio_(std::move(d_phy_mmio)),
designware_dsi_host_controller_(*designware_dsi_host_controller),
phy_enabled_(enabled) {
ZX_DEBUG_ASSERT(designware_dsi_host_controller != nullptr);
}
void MipiPhy::Dump() {
zxlogf(INFO, "%s: DUMPING PHY REGS", __func__);
zxlogf(INFO, "MIPI_DSI_PHY_CTRL = 0x%x", dsi_phy_mmio_.Read32(MIPI_DSI_PHY_CTRL));
zxlogf(INFO, "MIPI_DSI_CHAN_CTRL = 0x%x", dsi_phy_mmio_.Read32(MIPI_DSI_CHAN_CTRL));
zxlogf(INFO, "MIPI_DSI_CHAN_STS = 0x%x", dsi_phy_mmio_.Read32(MIPI_DSI_CHAN_STS));
zxlogf(INFO, "MIPI_DSI_CLK_TIM = 0x%x", dsi_phy_mmio_.Read32(MIPI_DSI_CLK_TIM));
zxlogf(INFO, "MIPI_DSI_HS_TIM = 0x%x", dsi_phy_mmio_.Read32(MIPI_DSI_HS_TIM));
zxlogf(INFO, "MIPI_DSI_LP_TIM = 0x%x", dsi_phy_mmio_.Read32(MIPI_DSI_LP_TIM));
zxlogf(INFO, "MIPI_DSI_ANA_UP_TIM = 0x%x", dsi_phy_mmio_.Read32(MIPI_DSI_ANA_UP_TIM));
zxlogf(INFO, "MIPI_DSI_INIT_TIM = 0x%x", dsi_phy_mmio_.Read32(MIPI_DSI_INIT_TIM));
zxlogf(INFO, "MIPI_DSI_WAKEUP_TIM = 0x%x", dsi_phy_mmio_.Read32(MIPI_DSI_WAKEUP_TIM));
zxlogf(INFO, "MIPI_DSI_LPOK_TIM = 0x%x", dsi_phy_mmio_.Read32(MIPI_DSI_LPOK_TIM));
zxlogf(INFO, "MIPI_DSI_LP_WCHDOG = 0x%x", dsi_phy_mmio_.Read32(MIPI_DSI_LP_WCHDOG));
zxlogf(INFO, "MIPI_DSI_ANA_CTRL = 0x%x", dsi_phy_mmio_.Read32(MIPI_DSI_ANA_CTRL));
zxlogf(INFO, "MIPI_DSI_CLK_TIM1 = 0x%x", dsi_phy_mmio_.Read32(MIPI_DSI_CLK_TIM1));
zxlogf(INFO, "MIPI_DSI_TURN_WCHDOG = 0x%x", dsi_phy_mmio_.Read32(MIPI_DSI_TURN_WCHDOG));
zxlogf(INFO, "MIPI_DSI_ULPS_CHECK = 0x%x", dsi_phy_mmio_.Read32(MIPI_DSI_ULPS_CHECK));
zxlogf(INFO, "MIPI_DSI_TEST_CTRL0 = 0x%x", dsi_phy_mmio_.Read32(MIPI_DSI_TEST_CTRL0));
zxlogf(INFO, "MIPI_DSI_TEST_CTRL1 = 0x%x", dsi_phy_mmio_.Read32(MIPI_DSI_TEST_CTRL1));
zxlogf(INFO, "");
zxlogf(INFO, "#############################");
zxlogf(INFO, "Dumping dsi_phy_cfg structure:");
zxlogf(INFO, "#############################");
zxlogf(INFO, "lp_tesc = 0x%x (%u)", dsi_phy_cfg_.lp_tesc, dsi_phy_cfg_.lp_tesc);
zxlogf(INFO, "lp_lpx = 0x%x (%u)", dsi_phy_cfg_.lp_lpx, dsi_phy_cfg_.lp_lpx);
zxlogf(INFO, "lp_ta_sure = 0x%x (%u)", dsi_phy_cfg_.lp_ta_sure, dsi_phy_cfg_.lp_ta_sure);
zxlogf(INFO, "lp_ta_go = 0x%x (%u)", dsi_phy_cfg_.lp_ta_go, dsi_phy_cfg_.lp_ta_go);
zxlogf(INFO, "lp_ta_get = 0x%x (%u)", dsi_phy_cfg_.lp_ta_get, dsi_phy_cfg_.lp_ta_get);
zxlogf(INFO, "hs_exit = 0x%x (%u)", dsi_phy_cfg_.hs_exit, dsi_phy_cfg_.hs_exit);
zxlogf(INFO, "hs_trail = 0x%x (%u)", dsi_phy_cfg_.hs_trail, dsi_phy_cfg_.hs_trail);
zxlogf(INFO, "hs_zero = 0x%x (%u)", dsi_phy_cfg_.hs_zero, dsi_phy_cfg_.hs_zero);
zxlogf(INFO, "hs_prepare = 0x%x (%u)", dsi_phy_cfg_.hs_prepare, dsi_phy_cfg_.hs_prepare);
zxlogf(INFO, "clk_trail = 0x%x (%u)", dsi_phy_cfg_.clk_trail, dsi_phy_cfg_.clk_trail);
zxlogf(INFO, "clk_post = 0x%x (%u)", dsi_phy_cfg_.clk_post, dsi_phy_cfg_.clk_post);
zxlogf(INFO, "clk_zero = 0x%x (%u)", dsi_phy_cfg_.clk_zero, dsi_phy_cfg_.clk_zero);
zxlogf(INFO, "clk_prepare = 0x%x (%u)", dsi_phy_cfg_.clk_prepare, dsi_phy_cfg_.clk_prepare);
zxlogf(INFO, "clk_pre = 0x%x (%u)", dsi_phy_cfg_.clk_pre, dsi_phy_cfg_.clk_pre);
zxlogf(INFO, "init = 0x%x (%u)", dsi_phy_cfg_.init, dsi_phy_cfg_.init);
zxlogf(INFO, "wakeup = 0x%x (%u)", dsi_phy_cfg_.wakeup, dsi_phy_cfg_.wakeup);
}
} // namespace amlogic_display