blob: 7e27fd5336d86ce1e7b84bc95adc3ccfe0f3b484 [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 "astro-display.h"
#define NS_TO_LANEBYTE(x) ((x + lanebytetime - 1) / (lanebytetime))
zx_status_t mipi_dsi_phy_cfg_load(astro_display_t* display, uint32_t bitrate) {
if (display == NULL) {
DISP_ERROR("Uninitialized memory detected!\n");
return ZX_ERR_INVALID_ARGS;
}
// 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.
uint32_t ui = (1 * 1000 * 1000 * 100) / (bitrate / 1000);
// Calculate values will be rounded by the lanebyteclk
uint32_t lanebytetime = ui * 8;
// lp_tesc:TX Excape Clock Division factor (from linebyteclk). Round up to units of ui
display->dsi_phy_cfg.lp_tesc = NS_TO_LANEBYTE(DPHY_TIME_LP_TESC) & 0xff;
// lp_lpx: Transmit length of any LP state period
display->dsi_phy_cfg.lp_lpx = NS_TO_LANEBYTE(DPHY_TIME_LP_LPX) & 0xff;
// lp_ta_sure
display->dsi_phy_cfg.lp_ta_sure = NS_TO_LANEBYTE(DPHY_TIME_LP_TA_SURE) & 0xff;
// lp_ta_go
display->dsi_phy_cfg.lp_ta_go = NS_TO_LANEBYTE(DPHY_TIME_LP_TA_GO) & 0xff;
// lp_ta_get
display->dsi_phy_cfg.lp_ta_get = NS_TO_LANEBYTE(DPHY_TIME_LP_TA_GET) & 0xff;
// hs_exit
display->dsi_phy_cfg.hs_exit = NS_TO_LANEBYTE(DPHY_TIME_HS_EXIT) & 0xff;
// clk-_prepare
display->dsi_phy_cfg.clk_prepare = NS_TO_LANEBYTE(DPHY_TIME_CLK_PREPARE) & 0xff;
// clk_zero
display->dsi_phy_cfg.clk_zero = NS_TO_LANEBYTE(DPHY_TIME_CLK_ZERO(ui)) & 0xff;
// clk_pre
display->dsi_phy_cfg.clk_pre = NS_TO_LANEBYTE(DPHY_TIME_CLK_PRE(ui)) & 0xff;
// init
display->dsi_phy_cfg.init = NS_TO_LANEBYTE(DPHY_TIME_INIT) & 0xff;
// wakeup
display->dsi_phy_cfg.wakeup = NS_TO_LANEBYTE(DPHY_TIME_WAKEUP) & 0xff;
// clk_trail
display->dsi_phy_cfg.clk_trail = NS_TO_LANEBYTE(DPHY_TIME_CLK_TRAIL) & 0xff;
// clk_post
display->dsi_phy_cfg.clk_post = NS_TO_LANEBYTE(DPHY_TIME_CLK_POST(ui)) & 0xff;
// hs_trail
display->dsi_phy_cfg.hs_trail = NS_TO_LANEBYTE(DPHY_TIME_HS_TRAIL(ui)) & 0xff;
// hs_prepare
display->dsi_phy_cfg.hs_prepare = NS_TO_LANEBYTE(DPHY_TIME_HS_PREPARE(ui)) & 0xff;
// hs_zero
display->dsi_phy_cfg.hs_zero = NS_TO_LANEBYTE(DPHY_TIME_HS_ZERO(ui)) & 0xff;
// Ensure both clk-trail and hs-trail do not exceed Teot
uint32_t t_req_max = NS_TO_LANEBYTE(DPHY_TIME_EOT(ui)) & 0xff;
if ((display->dsi_phy_cfg.clk_trail > t_req_max) ||
(display->dsi_phy_cfg.hs_trail > t_req_max)) {
DISP_ERROR("Invalid clk-trail and/or hs-trail exceed Teot!\n");
DISP_ERROR("clk-trail = 0x%02x, hs-trail = 0x%02x, Teot = 0x%02x\n",
display->dsi_phy_cfg.clk_trail, display->dsi_phy_cfg.hs_trail, t_req_max );
return ZX_ERR_OUT_OF_RANGE;
}
DISP_SPEW( "lp_tesc = 0x%02x\n"
"lp_lpx = 0x%02x\n"
"lp_ta_sure = 0x%02x\n"
"lp_ta_go = 0x%02x\n"
"lp_ta_get = 0x%02x\n"
"hs_exit = 0x%02x\n"
"hs_trail = 0x%02x\n"
"hs_zero = 0x%02x\n"
"hs_prepare = 0x%02x\n"
"clk_trail = 0x%02x\n"
"clk_post = 0x%02x\n"
"clk_zero = 0x%02x\n"
"clk_prepare = 0x%02x\n"
"clk_pre = 0x%02x\n"
"init = 0x%02x\n"
"wakeup = 0x%02x\n\n",
display->dsi_phy_cfg.lp_tesc,
display->dsi_phy_cfg.lp_lpx,
display->dsi_phy_cfg.lp_ta_sure,
display->dsi_phy_cfg.lp_ta_go,
display->dsi_phy_cfg.lp_ta_get,
display->dsi_phy_cfg.hs_exit,
display->dsi_phy_cfg.hs_trail,
display->dsi_phy_cfg.hs_zero,
display->dsi_phy_cfg.hs_prepare,
display->dsi_phy_cfg.clk_trail,
display->dsi_phy_cfg.clk_post,
display->dsi_phy_cfg.clk_zero,
display->dsi_phy_cfg.clk_prepare,
display->dsi_phy_cfg.clk_pre,
display->dsi_phy_cfg.init,
display->dsi_phy_cfg.wakeup);
return ZX_OK;
}
static void aml_dsi_phy_init(astro_display_t* display, uint32_t lane_num)
{
// enable phy clock.
WRITE32_REG(DSI_PHY, MIPI_DSI_PHY_CTRL, PHY_CTRL_TXDDRCLK_EN |
PHY_CTRL_DDRCLKPATH_EN | PHY_CTRL_CLK_DIV_COUNTER | PHY_CTRL_CLK_DIV_EN |
PHY_CTRL_BYTECLK_EN);
// Toggle PHY CTRL RST
SET_BIT32(DSI_PHY, MIPI_DSI_PHY_CTRL, 1, PHY_CTRL_RST_START, PHY_CTRL_RST_BITS);
SET_BIT32(DSI_PHY, MIPI_DSI_PHY_CTRL, 0, PHY_CTRL_RST_START, PHY_CTRL_RST_BITS);
WRITE32_REG(DSI_PHY, MIPI_DSI_CLK_TIM,
(display->dsi_phy_cfg.clk_trail | (display->dsi_phy_cfg.clk_post << 8) |
(display->dsi_phy_cfg.clk_zero << 16) |
(display->dsi_phy_cfg.clk_prepare << 24)));
WRITE32_REG(DSI_PHY, MIPI_DSI_CLK_TIM1, display->dsi_phy_cfg.clk_pre);
WRITE32_REG(DSI_PHY, MIPI_DSI_HS_TIM,
(display->dsi_phy_cfg.hs_exit | (display->dsi_phy_cfg.hs_trail << 8) |
(display->dsi_phy_cfg.hs_zero << 16) |
(display->dsi_phy_cfg.hs_prepare << 24)));
WRITE32_REG(DSI_PHY, MIPI_DSI_LP_TIM,
(display->dsi_phy_cfg.lp_lpx | (display->dsi_phy_cfg.lp_ta_sure << 8) |
(display->dsi_phy_cfg.lp_ta_go << 16) | (display->dsi_phy_cfg.lp_ta_get << 24)));
WRITE32_REG(DSI_PHY, MIPI_DSI_ANA_UP_TIM, ANA_UP_TIME);
WRITE32_REG(DSI_PHY, MIPI_DSI_INIT_TIM, display->dsi_phy_cfg.init);
WRITE32_REG(DSI_PHY, MIPI_DSI_WAKEUP_TIM, display->dsi_phy_cfg.wakeup);
WRITE32_REG(DSI_PHY, MIPI_DSI_LPOK_TIM, LPOK_TIME);
WRITE32_REG(DSI_PHY, MIPI_DSI_ULPS_CHECK, ULPS_CHECK_TIME);
WRITE32_REG(DSI_PHY, MIPI_DSI_LP_WCHDOG, LP_WCHDOG_TIME);
WRITE32_REG(DSI_PHY, MIPI_DSI_TURN_WCHDOG, TURN_WCHDOG_TIME);
WRITE32_REG(DSI_PHY, MIPI_DSI_CHAN_CTRL, 0);
}
// This function checks two things in order to decide whether the PHY is
// ready or not. LOCK Bit and StopStateClk bit. According to spec, once these
// are set, PHY has completed initialization
static zx_status_t aml_dsi_waitfor_phy_ready(astro_display_t* display)
{
int timeout = DPHY_TIMEOUT;
while ((GET_BIT32(MIPI_DSI, DW_DSI_PHY_STATUS, PHY_STATUS_PHY_LOCK, 1) == 0) &&
timeout--) {
usleep(6);
}
if (timeout <= 0) {
DISP_ERROR("Timeout! D-PHY did not lock\n");
return ZX_ERR_TIMED_OUT;
}
timeout = DPHY_TIMEOUT;
while ((GET_BIT32(MIPI_DSI, DW_DSI_PHY_STATUS, PHY_STATUS_PHY_STOPSTATECLKLANE, 1) == 0) &&
timeout--) {
usleep(6);
}
if (timeout <= 0) {
DISP_ERROR("Timeout! D-PHY StopStateClk not set\n");
return ZX_ERR_TIMED_OUT;
}
return ZX_OK;
}
zx_status_t mipi_dsi_phy_init(astro_display_t* display)
{
if (display == NULL) {
DISP_ERROR("Uninitialized memory detected!\n");
return ZX_ERR_INVALID_ARGS;
}
// Power up DSI
WRITE32_REG(MIPI_DSI, DW_DSI_PWR_UP, PWR_UP_ON);
// 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: Find out why 0x74 was selected
WRITE32_REG(MIPI_DSI, DW_DSI_PHY_TST_CTRL1, 0x00010044);
WRITE32_REG(MIPI_DSI, DW_DSI_PHY_TST_CTRL0, 0x2);
WRITE32_REG(MIPI_DSI, DW_DSI_PHY_TST_CTRL0, 0x0);
WRITE32_REG(MIPI_DSI, DW_DSI_PHY_TST_CTRL1, 0x00000074);
WRITE32_REG(MIPI_DSI, DW_DSI_PHY_TST_CTRL0, 0x2);
WRITE32_REG(MIPI_DSI, DW_DSI_PHY_TST_CTRL0, 0x0);
// Power up D-PHY
WRITE32_REG(MIPI_DSI, DW_DSI_PHY_RSTZ, PHY_RSTZ_PWR_UP);
// Setup PHY Timing parameters
aml_dsi_phy_init(display, display->disp_setting.lane_num);
// Wait for PHY to be read
zx_status_t status;
if ((status = aml_dsi_waitfor_phy_ready(display)) != ZX_OK) {
// no need to print additional info.
return status;
}
// Trigger a sync active for esc_clk
SET_BIT32(DSI_PHY, MIPI_DSI_PHY_CTRL, 1, 1, 1);
// Startup transfer, default lpclk
WRITE32_REG(MIPI_DSI, DW_DSI_LPCLK_CTRL, (0x1 << LPCLK_CTRL_AUTOCLKLANE_CTRL) |
(0x1 << LPCLK_CTRL_TXREQUESTCLKHS));
return ZX_OK;
}