mirror of
https://github.com/openmv/openmv.git
synced 2025-11-04 14:49:50 +08:00
489 lines
13 KiB
C
489 lines
13 KiB
C
/* Copyright (C) 2024 Alif Semiconductor - All Rights Reserved.
|
|
* Use, distribution and modification of this code is permitted under the
|
|
* terms stated in the Alif Semiconductor Software License Agreement
|
|
*
|
|
* You should have received a copy of the Alif Semiconductor Software
|
|
* License Agreement with this file. If not, please write to:
|
|
* contact@alifsemi.com, or visit: https://alifsemi.com/license
|
|
*
|
|
*/
|
|
|
|
/******************************************************************************
|
|
* @file DPHY_DSI.c
|
|
* @author Prasanna Ravi and Chandra Bhushan Singh
|
|
* @email prasanna.ravi@alifsemi.com and chandrabhushan.singh@alifsemi.com
|
|
* @version V1.0.0
|
|
* @date 14-May-2024
|
|
* @brief Driver for MIPI DPHY DSI.
|
|
* @bug None.
|
|
* @Note None.
|
|
******************************************************************************/
|
|
#include <stdint.h>
|
|
#include "Driver_Common.h"
|
|
#include "RTE_Components.h"
|
|
#include CMSIS_device_header
|
|
#include "RTE_Device.h"
|
|
#include "DPHY_Test_and_Control_Interface.h"
|
|
#include "DPHY_Private.h"
|
|
#include "dphy.h"
|
|
#include "sys_ctrl_dphy.h"
|
|
#include "dsi.h"
|
|
#include "sys_ctrl_dsi.h"
|
|
#include "DPHY_DSI.h"
|
|
|
|
/*DPHY initialize status global variables*/
|
|
static volatile uint32_t dsi_init_status = 0;
|
|
|
|
/*hsfreqrange and osc_freq_target range*/
|
|
extern const DPHY_FREQ_RANGE frequency_range[];
|
|
|
|
/*vco_cntrl range*/
|
|
static const DPHY_PLL_VCO_CTRL vco_ctrl_range[] =
|
|
{
|
|
{ 1170, 0x03 }, { 975, 0x07 }, { 853.125, 0x08 }, { 706.875, 0x08 },
|
|
{ 585, 0x0B }, { 487.5, 0x0F }, { 426.56, 0x10 }, { 353.4, 0x10 },
|
|
{ 292.5, 0x13 }, { 243.75, 0x17 }, { 213.3, 0x18 }, { 176.72, 0x18 },
|
|
{ 146.25, 0x1B }, { 121.88, 0x1F }, { 106.64, 0x20 }, { 88.36, 0x20 },
|
|
{ 73.13, 0x23}, { 60.93, 0x27 }, { 53.32, 0x28 }, { 44.18, 0x28 },
|
|
{ 40, 0x2B}
|
|
};
|
|
|
|
/*Output division factor range*/
|
|
static const DPHY_PLL_OUTPUT_DIVISION_FACTOR pll_p_factor[] =
|
|
{
|
|
{ 1000, 2 }, { 500, 4 }, { 250, 8 }, { 125, 16 }, { 62.5, 32 }, { 40, 64 }
|
|
};
|
|
|
|
/**
|
|
\fn static void MIPI_DSI_DPHY_Shutdown (uint8_t state)
|
|
\brief PHY shutdown line control callback function.
|
|
\param[in] state ENABLE/DISABLE the line.
|
|
*/
|
|
static void MIPI_DSI_DPHY_Shutdown (uint8_t state)
|
|
{
|
|
if(state == ENABLE)
|
|
{
|
|
dsi_phy_shutdown_enable((DSI_Type *)DSI_BASE);
|
|
}
|
|
else
|
|
{
|
|
dsi_phy_shutdown_disable((DSI_Type *)DSI_BASE);
|
|
}
|
|
}
|
|
|
|
/**
|
|
\fn static void MIPI_DSI_DPHY_Rst (uint8_t state)
|
|
\brief PHY reset line control callback function.
|
|
\param[in] state ENABLE/DISABLE the line.
|
|
*/
|
|
static void MIPI_DSI_DPHY_Rst (uint8_t state)
|
|
{
|
|
if(state == ENABLE)
|
|
{
|
|
dsi_phy_reset_enable((DSI_Type *)DSI_BASE);
|
|
}
|
|
else
|
|
{
|
|
dsi_phy_reset_disable((DSI_Type *)DSI_BASE);
|
|
}
|
|
}
|
|
|
|
/**
|
|
\fn static void MIPI_DSI_DPHY_Enableclk (uint8_t state)
|
|
\brief PHY enable clock line control callback function.
|
|
\param[in] state ENABLE/DISABLE the line.
|
|
*/
|
|
static void MIPI_DSI_DPHY_Enableclk (uint8_t state)
|
|
{
|
|
if(state == ENABLE)
|
|
{
|
|
dsi_phy_enable_clock((DSI_Type *)DSI_BASE);
|
|
}
|
|
else
|
|
{
|
|
dsi_phy_disable_clock((DSI_Type *)DSI_BASE);
|
|
}
|
|
}
|
|
|
|
/**
|
|
\fn static void MIPI_DSI_DPHY_Testclr (uint8_t state)
|
|
\brief PHY testclr line control callback function.
|
|
\param[in] state ENABLE/DISABLE the line.
|
|
*/
|
|
static void MIPI_DSI_DPHY_Testclr (uint8_t state)
|
|
{
|
|
if(state == ENABLE)
|
|
{
|
|
dsi_phy_testclr_enable((DSI_Type *)DSI_BASE);
|
|
}
|
|
else
|
|
{
|
|
dsi_phy_testclr_disable((DSI_Type *)DSI_BASE);
|
|
}
|
|
|
|
}
|
|
|
|
/**
|
|
\fn static DSI_PLL_STATUS MIPI_DSI_DPHY_PLL_Lock (void)
|
|
\brief PHY testclr line control callback function.
|
|
\return return status of the PLL lock.
|
|
*/
|
|
static DSI_PLL_STATUS MIPI_DSI_DPHY_PLL_Lock (void)
|
|
{
|
|
return dsi_get_phy_lock_status((DSI_Type *)DSI_BASE);
|
|
}
|
|
|
|
/**
|
|
\fn static uint8_t MIPI_DSI_DPHY_Stopstate (void)
|
|
\brief status of stopstate from PHY
|
|
\return return status of stopstate.
|
|
*/
|
|
static DPHY_STOPSTATE MIPI_DSI_DPHY_Stopstate (void)
|
|
{
|
|
uint8_t ret = 0;
|
|
|
|
if(dsi_get_lane_stopstate_status((DSI_Type *)DSI_BASE, DSI_LANE_CLOCK) == DSI_LANE_STOPSTATE_ON)
|
|
{
|
|
ret |= DPHY_STOPSTATE_CLOCK;
|
|
}
|
|
|
|
if(dsi_get_lane_stopstate_status((DSI_Type *)DSI_BASE, DSI_LANE_0) == DSI_LANE_STOPSTATE_ON)
|
|
{
|
|
ret |= DPHY_STOPSTATE_LANE0;
|
|
}
|
|
|
|
if(dsi_get_lane_stopstate_status((DSI_Type *)DSI_BASE, DSI_LANE_1) == DSI_LANE_STOPSTATE_ON)
|
|
{
|
|
ret |= DPHY_STOPSTATE_LANE1;
|
|
}
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
/**
|
|
\fn uint8_t DPHY_DSI_Read_Mask (uint16_t address,
|
|
uint8_t pos,
|
|
uint8_t width)
|
|
\brief Read Mask DSI DPHY registers.
|
|
\param[in] address is register index.
|
|
\param[in] pos is start bit position.
|
|
\param[in] width is number bits to read.
|
|
\return return received data from DPHY register.
|
|
*/
|
|
uint8_t DPHY_DSI_Read_Mask (uint16_t address,
|
|
uint8_t pos,
|
|
uint8_t width)
|
|
{
|
|
return (MIPI_DPHY_Read(address, DPHY_MODE_CFG_DSI) >> pos) & ((1 << width) - 1);
|
|
}
|
|
|
|
|
|
/**
|
|
\fn void DPHY_DSI_Write_Mask (uint16_t address,
|
|
uint8_t data,
|
|
uint8_t pos,
|
|
uint8_t width)
|
|
\brief write Mask DSI DPHY registers.
|
|
\param[in] address is register index
|
|
\param[in] data is value to be write to the DPHY register.
|
|
\param[in] pos is start bit position.
|
|
\param[in] width is number bits to write.
|
|
*/
|
|
void DPHY_DSI_Write_Mask (uint16_t address,
|
|
uint8_t data,
|
|
uint8_t pos,
|
|
uint8_t width)
|
|
{
|
|
uint8_t reg_data = 0;
|
|
uint8_t mask = (1U << width) - 1;
|
|
|
|
reg_data = MIPI_DPHY_Read(address, DPHY_MODE_CFG_DSI);
|
|
reg_data &= ~(mask << pos);
|
|
reg_data |= (data & mask) << pos;
|
|
MIPI_DPHY_Write(address,reg_data, DPHY_MODE_CFG_DSI);
|
|
}
|
|
|
|
/**
|
|
\fn void DPHY_PowerEnable (void)
|
|
\brief Enable DPHY Interface Power.
|
|
*/
|
|
static void DPHY_PowerEnable (void)
|
|
{
|
|
enable_dphy_pll_reference_clock();
|
|
|
|
enable_txdphy_configure_clock();
|
|
|
|
enable_dsi_periph_clk();
|
|
}
|
|
|
|
/**
|
|
\fn void DPHY_PowerDisable (void)
|
|
\brief Disable DPHY Interface Power.
|
|
*/
|
|
static void DPHY_PowerDisable (void)
|
|
{
|
|
|
|
disable_dsi_periph_clk();
|
|
|
|
disable_txdphy_configure_clock();
|
|
|
|
disable_dphy_pll_reference_clock();
|
|
}
|
|
|
|
/**
|
|
\fn int32_t DPHY_ConfigurePLL(uint32_t clock_frequency)
|
|
\brief configuring MIPI TX DPHY PLL.
|
|
\param[in] clock_frequency DPHY clock frequency.
|
|
\return \ref execution_status
|
|
*/
|
|
static int32_t DPHY_ConfigurePLL(uint32_t clock_frequency)
|
|
{
|
|
float frequency_in_mhz = clock_frequency/1000000.0f;
|
|
uint32_t pll_m = 0;
|
|
uint8_t pll_p = 0;
|
|
uint8_t vco_ctrl = 0;
|
|
uint8_t range = 0;
|
|
pll_config_t pll_config;
|
|
|
|
uint8_t pll_n = RTE_MIPI_DSI_PLL_INPUT_DIV_FACTOR_N;
|
|
|
|
if(((DPHY_FCLKIN_MHZ/pll_n) > 24) || ((DPHY_FCLKIN_MHZ/pll_n) < 8))
|
|
{
|
|
return ARM_DRIVER_ERROR_PARAMETER;
|
|
}
|
|
|
|
for( range = 0; (range < ARRAY_SIZE(vco_ctrl_range) - 1) &&
|
|
((frequency_in_mhz) < vco_ctrl_range[range].frequency_mhz);
|
|
++range);
|
|
|
|
vco_ctrl = vco_ctrl_range[range].vco_ctrl;
|
|
|
|
for( range = 0; (range < ARRAY_SIZE(pll_p_factor) - 1) &&
|
|
((frequency_in_mhz) <= pll_p_factor[range].frequency_mhz);
|
|
++range);
|
|
|
|
pll_p = pll_p_factor[range].p;
|
|
|
|
pll_m = (uint32_t)((frequency_in_mhz * pll_n * pll_p * 2) / DPHY_FCLKIN_MHZ);
|
|
|
|
set_dphy_pll_clksel(DPHY_PLL_CLKSEL_CLOCK_GENERAT);
|
|
|
|
enable_dphy_pll_shadow_clear();
|
|
|
|
sys_busy_loop_us(1);
|
|
|
|
disable_dphy_pll_shadow_clear();
|
|
|
|
pll_config.pll_gmp_ctrl = DPHY_GMP_CNTRL;
|
|
pll_config.pll_m = pll_m;
|
|
pll_config.pll_n = (pll_n - 1);
|
|
pll_config.pll_cpbias_ctrl = DPHY_CPBIAS_CNTRL;
|
|
pll_config.pll_int_ctrl = DPHY_INT_CNTRL;
|
|
pll_config.pll_prop_ctrl = DPHY_PROP_CNTRL;
|
|
pll_config.pll_vco_ctrl = vco_ctrl;
|
|
|
|
set_dphy_pll_configuration(&pll_config);
|
|
|
|
sys_busy_loop_us(1);
|
|
|
|
enable_dphy_updatepll();
|
|
|
|
sys_busy_loop_us(1);
|
|
|
|
disable_dphy_updatepll();
|
|
|
|
DPHY_DSI_Write_Mask(dphy4txtester_DIG_RDWR_TX_PLL_17, 0x1, 7, 1);
|
|
|
|
DPHY_DSI_Write_Mask(dphy4txtester_DIG_RDWR_TX_PLL_17, 0x1, 6, 1);
|
|
|
|
return ARM_DRIVER_OK;
|
|
}
|
|
|
|
/**
|
|
\fn int32_t DPHY_MasterSetup (uint32_t clock_frequency, uint8_t n_lanes)
|
|
\brief MIPI DPHY Tx startup sequence.
|
|
\param[in] clock_frequency DPHY clock frequency.
|
|
\param[in] n_lanes number of lanes.
|
|
\return \ref execution_status
|
|
*/
|
|
static int32_t DPHY_MasterSetup (uint32_t clock_frequency, uint8_t n_lanes)
|
|
{
|
|
uint32_t bitrate_mbps = (clock_frequency * 2)/1000000;
|
|
uint8_t hsfreqrange = 0;
|
|
uint8_t cfgclkfreqrange = 0;
|
|
uint8_t range = 0;
|
|
uint8_t stopstate_check = 0;
|
|
uint32_t lp_count = 0;
|
|
|
|
if(bitrate_mbps < 80 || bitrate_mbps > 2500)
|
|
{
|
|
return ARM_DRIVER_ERROR;
|
|
}
|
|
|
|
for(range = 0; (bitrate_mbps > frequency_range[range].bitrate_in_mbps);
|
|
++range);
|
|
|
|
hsfreqrange = frequency_range[range].hsfreqrange;
|
|
|
|
dsi_set_active_lanes((DSI_Type *)DSI_BASE, n_lanes - 1);
|
|
|
|
MIPI_DSI_DPHY_Rst(DISABLE);
|
|
|
|
MIPI_DSI_DPHY_Shutdown(DISABLE);
|
|
|
|
set_tx_dphy_txrx(DPHY_MODE_MASTER);
|
|
|
|
set_tx_dphy_testport_select(DPHY_TESTPORT_SELECT_RX);
|
|
MIPI_DSI_DPHY_Testclr(ENABLE);
|
|
set_tx_dphy_testport_select(DPHY_TESTPORT_SELECT_TX);
|
|
MIPI_DSI_DPHY_Testclr(ENABLE);
|
|
|
|
sys_busy_loop_us(1);
|
|
|
|
set_tx_dphy_testport_select(DPHY_TESTPORT_SELECT_RX);
|
|
MIPI_DSI_DPHY_Testclr(DISABLE);
|
|
set_tx_dphy_testport_select(DPHY_TESTPORT_SELECT_TX);
|
|
MIPI_DSI_DPHY_Testclr(DISABLE);
|
|
|
|
set_tx_dphy_hsfreqrange(hsfreqrange);
|
|
|
|
DPHY_DSI_Write_Mask(dphy4txtester_DIG_RDWR_TX_PLL_13, 0x3, 0, 2);
|
|
|
|
DPHY_DSI_Write_Mask(dphy4txtester_DIG_RDWR_TX_CB_1, 0x2, 0, 2);
|
|
|
|
DPHY_DSI_Write_Mask(dphy4txtester_DIG_RDWR_TX_CB_0, 0x2, 5, 2);
|
|
|
|
if(bitrate_mbps < 450)
|
|
DPHY_DSI_Write_Mask(dphy4txtester_DIG_RDWR_TX_CB_2, 0x1, 4, 1);
|
|
|
|
DPHY_DSI_Write_Mask(dphy4txtester_DIG_RDWR_TX_CLK_TERMLOWCAP, 0x2, 0, 2);
|
|
|
|
if(bitrate_mbps <= 1000)
|
|
{
|
|
DPHY_DSI_Write_Mask(dphy4txtester_DIG_RDWR_TX_SLEW_5,
|
|
(uint8_t)DPHY_LESS_THEN_1GBPS_SR_OSC_FREQ_TARGET,
|
|
0, 8);
|
|
DPHY_DSI_Write_Mask(dphy4txtester_DIG_RDWR_TX_SLEW_6,
|
|
(uint8_t)(DPHY_LESS_THEN_1GBPS_SR_OSC_FREQ_TARGET >> 8),
|
|
0, 4);
|
|
DPHY_DSI_Write_Mask(dphy4txtester_DIG_RDWR_TX_SLEW_7, 0x1, 4, 1);
|
|
DPHY_DSI_Write_Mask(dphy4txtester_DIG_RDWR_TX_SLEW_7, 0x1, 0, 1);
|
|
}
|
|
else if ((bitrate_mbps > 1000) && (bitrate_mbps <= 1500))
|
|
{
|
|
DPHY_DSI_Write_Mask(dphy4txtester_DIG_RDWR_TX_SLEW_5,
|
|
(uint8_t)DPHY_MORE_THEN_1GBPS_SR_OSC_FREQ_TARGET,
|
|
0, 8);
|
|
DPHY_DSI_Write_Mask(dphy4txtester_DIG_RDWR_TX_SLEW_6,
|
|
(uint8_t)(DPHY_MORE_THEN_1GBPS_SR_OSC_FREQ_TARGET >> 8),
|
|
0, 4);
|
|
}
|
|
|
|
cfgclkfreqrange = (DPHY_FCFG_CLOCK_MHZ - 17) * 4;
|
|
|
|
set_tx_dphy_cfgclkfreqrange(cfgclkfreqrange);
|
|
|
|
if(DPHY_ConfigurePLL(clock_frequency) != ARM_DRIVER_OK)
|
|
{
|
|
return ARM_DRIVER_ERROR_PARAMETER;
|
|
}
|
|
|
|
unset_tx_dphy_basedir((1U << n_lanes) - 1);
|
|
|
|
unset_tx_dphy_forcerxmode((1U << n_lanes) - 1);
|
|
|
|
sys_busy_loop_us(1);
|
|
|
|
MIPI_DSI_DPHY_Enableclk(ENABLE);
|
|
|
|
sys_busy_loop_us(1);
|
|
|
|
MIPI_DSI_DPHY_Shutdown(ENABLE);
|
|
|
|
sys_busy_loop_us(1);
|
|
|
|
MIPI_DSI_DPHY_Rst(ENABLE);
|
|
|
|
while(MIPI_DSI_DPHY_PLL_Lock() != (DSI_PLL_STATUS) DPHY_PLL_STATUS_PLL_LOCK)
|
|
{
|
|
if(lp_count++ < 1000000)
|
|
{
|
|
sys_busy_loop_us(1);
|
|
}
|
|
else
|
|
{
|
|
return ARM_DRIVER_ERROR;
|
|
}
|
|
}
|
|
|
|
stopstate_check = DPHY_STOPSTATE_CLOCK | (n_lanes == 1 ? (DPHY_STOPSTATE_LANE0) :
|
|
(DPHY_STOPSTATE_LANE0) | (DPHY_STOPSTATE_LANE1) );
|
|
|
|
lp_count = 0;
|
|
while(MIPI_DSI_DPHY_Stopstate() != stopstate_check)
|
|
{
|
|
if(lp_count++ < 1000000)
|
|
{
|
|
sys_busy_loop_us(1);
|
|
}
|
|
else
|
|
{
|
|
return ARM_DRIVER_ERROR;
|
|
}
|
|
}
|
|
|
|
return ARM_DRIVER_OK;
|
|
}
|
|
|
|
/**
|
|
\fn int32_t DSI_DPHY_Initialize (uint32_t frequency, uint8_t n_lanes)
|
|
\brief Initialize MIPI DSI DPHY Interface.
|
|
\param[in] frequency to configure DPHY PLL.
|
|
\param[in] n_lanes number of lanes.
|
|
\return \ref execution_status
|
|
*/
|
|
int32_t DSI_DPHY_Initialize (uint32_t frequency, uint8_t n_lanes)
|
|
{
|
|
int32_t ret = ARM_DRIVER_OK;
|
|
|
|
if(dsi_init_status == DPHY_INIT_STATUS_INITIALIZED)
|
|
{
|
|
return ARM_DRIVER_OK;
|
|
}
|
|
|
|
DPHY_PowerEnable();
|
|
|
|
ret = DPHY_MasterSetup(frequency, n_lanes);
|
|
if(ret != ARM_DRIVER_OK)
|
|
{
|
|
return ret;
|
|
}
|
|
|
|
dsi_init_status = DPHY_INIT_STATUS_INITIALIZED;
|
|
|
|
return ret;
|
|
}
|
|
|
|
/**
|
|
\fn int32_t DSI_DPHY_Uninitialize (void)
|
|
\brief Uninitialize MIPI DSI DPHY Interface.
|
|
\return \ref execution_status
|
|
*/
|
|
int32_t DSI_DPHY_Uninitialize (void)
|
|
{
|
|
if(dsi_init_status == DPHY_INIT_STATUS_UNINITIALIZED)
|
|
{
|
|
return ARM_DRIVER_OK;
|
|
}
|
|
|
|
MIPI_DSI_DPHY_Rst(DISABLE);
|
|
MIPI_DSI_DPHY_Shutdown(DISABLE);
|
|
MIPI_DSI_DPHY_Enableclk(DISABLE);
|
|
DPHY_PowerDisable();
|
|
|
|
dsi_init_status = DPHY_INIT_STATUS_UNINITIALIZED;
|
|
|
|
return ARM_DRIVER_OK;
|
|
}
|