text: /*
   * Copyright 2019-2020 NXP
   * All rights reserved.
   *
   * SPDX-License-Identifier: BSD-3-Clause
   */
  
  #include "fsl_ldb.h"
  
  /*******************************************************************************
   * Definitions
   ******************************************************************************/
  
  /* Component ID definition, used by tools. */
  #ifndef FSL_COMPONENT_ID
  #define FSL_COMPONENT_ID "platform.drivers.ldb_combo_phy"
  #endif
  
  #define LDB_CH_COUNT 2U
  
  #define LDB_DPI_24BIT 5U
  
  #define LDB_PM_CTRL_REG_CH_MODE_MASK(ch) ((uint32_t)MIPI_DSI_LVDS_COMBO_CSR_PM_CTRL_CH0_MODE_MASK << ((ch)*2U))
  #define LDB_PM_CTRL_REG_CH_DATA_WIDTH_MASK(ch) \
      ((uint32_t)MIPI_DSI_LVDS_COMBO_CSR_PM_CTRL_CH0_DATA_WIDTH_MASK << ((ch)*2U))
  #define LDB_PM_CTRL_REG_CH_BIT_MAPPING_MASK(ch) \
      ((uint32_t)MIPI_DSI_LVDS_COMBO_CSR_PM_CTRL_CH0_BIT_MAPPING_MASK << ((ch)*2U))
  #define LDB_PM_CTRL_REG_DI_VS_POLARITY_MASK(di) ((uint32_t)MIPI_DSI_LVDS_COMBO_CSR_PM_CTRL_DI0_VS_POLARITY_MASK << (di))
  
  #define LDB_PM_CTRL_REG_CH_MASK(ch)                                              \
      (LDB_PM_CTRL_REG_CH_MODE_MASK(ch) | LDB_PM_CTRL_REG_CH_DATA_WIDTH_MASK(ch) | \
       LDB_PM_CTRL_REG_CH_BIT_MAPPING_MASK(ch))
  
  #define LDB_PM_CTRL_REG_DI_MASK(di) (LDB_PM_CTRL_REG_DI_VS_POLARITY_MASK(di))
  
  #define LDB_SS_CTRL_CH_VSYNC_POL_MASK(ch) ((uint32_t)MIPI_DSI_LVDS_COMBO_CSR_SS_CRTL_CH0_VSYNC_POL_MASK << ((ch)*2U))
  #define LDB_SS_CTRL_CH_HSYNC_POL_MASK(ch) ((uint32_t)MIPI_DSI_LVDS_COMBO_CSR_SS_CRTL_CH0_HSYNC_POL_MASK << ((ch)*2U))
  
  /*******************************************************************************
   * Variables
   ******************************************************************************/
  
  /*! @brief Pointers to LDB PHY control blocks. */
  static MIPI_DSI_HOST_Type *const s_ldbPhyBases[] = MIPI_DSI_HOST_BASE_PTRS;
  /*! @brief Pointers to ldb bases for each instance. */
  static LDB_Type *const s_ldbBases[] = MIPI_DSI_LVDS_COMBO_CSR_BASE_PTRS;
  
  #if !(defined(FSL_SDK_DISABLE_DRIVER_CLOCK_CONTROL) && FSL_SDK_DISABLE_DRIVER_CLOCK_CONTROL)
  /*! @brief Pointers to LDB clocks for each instance. */
  static const clock_ip_name_t s_ldbClocks[] = LDB_CLOCKS;
  #endif /* FSL_SDK_DISABLE_DRIVER_CLOCK_CONTROL */
  
  /*******************************************************************************
   * Prototypes
   ******************************************************************************/
  /*!
   * @brief Get the LDB instance from peripheral base address.
   *
   * @param base LDB peripheral base address.
   * @return LDB instance.
   */
  static uint32_t LDB_GetInstance(LDB_Type *base);
  
  /*!
   * @brief Get the register value based on pixel map.
   *
   * @param outputBus Pixel map.
   * @param ch Channel.
   * @return Register value.
   */
  static uint32_t LDB_GetPixelMapConfig(ldb_output_bus_t outputBus, uint8_t ch);
  
  /*!
   * @brief Get the register value based on pixel map.
   *
   * @param outputBus Pixel map.
   * @param ch Channel.
   * @return Register value.
   */
  static MIPI_DSI_HOST_Type *LDB_GetPhyControlBlock(LDB_Type *base);
  
  /*******************************************************************************
   * Code
   ******************************************************************************/
  static uint32_t LDB_GetInstance(LDB_Type *base)
  {
      uint32_t instance;
  
      /* Find the instance index from base address mappings. */
      for (instance = 0U; instance < ARRAY_SIZE(s_ldbBases); instance++)
      {
          if (s_ldbBases[instance] == base)
          {
              break;
          }
      }
  
      assert(instance < ARRAY_SIZE(s_ldbBases));
  
      return instance;
  }
  
  static uint32_t LDB_GetPixelMapConfig(ldb_output_bus_t outputBus, uint8_t ch)
  {
      return (uint32_t)outputBus << (ch * 2U);
  }
  
  static MIPI_DSI_HOST_Type *LDB_GetPhyControlBlock(LDB_Type *base)
  {
      return s_ldbPhyBases[LDB_GetInstance(base)];
  }
  
  /*!
   * brief Initializes the LDB module.
   *
   * param base LDB peripheral base address.
   */
  void LDB_Init(LDB_Type *base)
  {
  #if !(defined(FSL_SDK_DISABLE_DRIVER_CLOCK_CONTROL) && FSL_SDK_DISABLE_DRIVER_CLOCK_CONTROL)
      (void)CLOCK_EnableClock(s_ldbClocks[LDB_GetInstance(base)]);
  #endif /* FSL_SDK_DISABLE_DRIVER_CLOCK_CONTROL */
  
      base->SS_CRTL = 0U;
      base->PM_CTRL = 0U;
  
      base->LVDS_PHY_CTRL =
          MIPI_DSI_LVDS_COMBO_CSR_LVDS_PHY_CTRL_LVDS_EN(0) /* Disable. */
          | MIPI_DSI_LVDS_COMBO_CSR_LVDS_PHY_CTRL_RFB(1)   /* Clock edge. 0: Falling edge. 1: Rising edge. */
          | MIPI_DSI_LVDS_COMBO_CSR_LVDS_PHY_CTRL_CA(4)    /* Output current. */
          | MIPI_DSI_LVDS_COMBO_CSR_LVDS_PHY_CTRL_CCM(4);  /* voltage control . */
  
      base->PXL2DPI_CTRL = LDB_DPI_24BIT;
      base->ULPS_CTRL    = MIPI_DSI_LVDS_COMBO_CSR_ULPS_CTRL_TX_ULPS_MASK; /* Set to lowpower mode. */
  }
  
  /*!
   * brief De-initializes the LDB module.
   *
   * param base LDB peripheral base address.
   */
  void LDB_Deinit(LDB_Type *base)
  {
      base->LVDS_PHY_CTRL = 0U;
  
  #if !(defined(FSL_SDK_DISABLE_DRIVER_CLOCK_CONTROL) && FSL_SDK_DISABLE_DRIVER_CLOCK_CONTROL)
      (void)CLOCK_DisableClock(s_ldbClocks[LDB_GetInstance(base)]);
  #endif /* FSL_SDK_DISABLE_DRIVER_CLOCK_CONTROL */
  }
  
  /*!
   * brief Initializes the LDB channel.
   *
   * param base LDB peripheral base address.
   * param channel Channel index.
   * param config Pointer to the configuration.
   * return Return kStatus_Success if success.
   */
  status_t LDB_InitChannel(LDB_Type *base, uint8_t channel, const ldb_channel_config_t *config)
  {
      assert(channel < LDB_CH_COUNT);
  
      uint32_t reg;
      uint32_t reg_m;
      uint32_t pixelClock_MHz;
  
      MIPI_DSI_HOST_Type *phy = LDB_GetPhyControlBlock(base);
  
      reg = base->SS_CRTL & ~(LDB_SS_CTRL_CH_VSYNC_POL_MASK(channel) | LDB_SS_CTRL_CH_HSYNC_POL_MASK(channel));
  
      if (0U != (config->inputFlag & (uint32_t)kLDB_InputVsyncActiveHigh))
      {
          reg |= LDB_SS_CTRL_CH_VSYNC_POL_MASK(channel);
      }
  
      if (0U != (config->inputFlag & (uint32_t)kLDB_InputHsyncActiveHigh))
      {
          reg |= LDB_SS_CTRL_CH_HSYNC_POL_MASK(channel);
      }
  
      base->SS_CRTL = reg;
  
      /* CH0 to DI0, CH1 to DI1 */
      reg = base->PM_CTRL & ~(LDB_PM_CTRL_REG_DI_MASK(channel) | LDB_PM_CTRL_REG_CH_MASK(channel));
  
      reg |= LDB_GetPixelMapConfig(config->outputBus, channel);
  
      if (0U != (config->inputFlag & (uint32_t)kLDB_InputVsyncActiveHigh))
      {
          reg |= LDB_PM_CTRL_REG_DI_VS_POLARITY_MASK(channel);
      }
  
      if (channel == 0U)
      {
          reg |= (1UL << MIPI_DSI_LVDS_COMBO_CSR_PM_CTRL_CH0_MODE_SHIFT);
      }
      else
      {
          reg |= (3UL << MIPI_DSI_LVDS_COMBO_CSR_PM_CTRL_CH1_MODE_SHIFT);
      }
  
      reg |= MIPI_DSI_LVDS_COMBO_CSR_PM_CTRL_CH_SEL(channel);
  
      base->PM_CTRL = reg;
  
      reg = (base->LVDS_PHY_CTRL & ~MIPI_DSI_LVDS_COMBO_CSR_LVDS_PHY_CTRL_RFB_MASK);
  
      /*
       * DPHY_CO is configured based on pixel clock and NB.
       *
       * if NB = 1:
       *         input pixel clock           REG_M
       *            25M ~ 32M                  2
       *            33M ~ 64M                  1
       *            65M ~ 165M                 0
       *
       * if NB = 0:
       *         input pixel clock           REG_M
       *            25M ~ 45M                  2
       *            46M ~ 92M                  1
       *            93M ~ 165M                 0
       *
       * Current driver only supports NB=0 (7bit).
       */
      pixelClock_MHz = config->pixelClock_Hz / 1000000U;
      if (pixelClock_MHz <= 45U)
      {
          reg_m = 2U;
      }
      else if (pixelClock_MHz <= 92U)
      {
          reg_m = 1U;
      }
      else
      {
          reg_m = 0U;
      }
  
      if (0U != ((uint32_t)kLDB_InputDataLatchOnRisingEdge & config->inputFlag))
      {
          reg |= MIPI_DSI_LVDS_COMBO_CSR_LVDS_PHY_CTRL_RFB_MASK;
      }
  
      base->LVDS_PHY_CTRL = reg;
      phy->DPHY_PD_PLL    = 0U;
      phy->DPHY_PD_TX     = 0U;
      phy->DPHY_CO        = reg_m;
      base->ULPS_CTRL     = 0U;
      base->LVDS_PHY_CTRL = reg | MIPI_DSI_LVDS_COMBO_CSR_LVDS_PHY_CTRL_LVDS_EN_MASK;
  
      return kStatus_Success;
  }
  
  /*!
   * brief De-initializes the LDB channel.
   *
   * param base LDB peripheral base address.
   * param channel Channel index.
   */
  void LDB_DeinitChannel(LDB_Type *base, uint8_t channel)
  {
      assert(channel < LDB_CH_COUNT);
  
      MIPI_DSI_HOST_Type *phy = LDB_GetPhyControlBlock(base);
  
      base->LVDS_PHY_CTRL &= ~MIPI_DSI_LVDS_COMBO_CSR_LVDS_PHY_CTRL_LVDS_EN_MASK;
      base->ULPS_CTRL  = MIPI_DSI_LVDS_COMBO_CSR_ULPS_CTRL_TX_ULPS_MASK;
      phy->DPHY_PD_TX  = MIPI_DSI_HOST_DPHY_PD_TX_dphy_pd_tx_MASK;
      phy->DPHY_PD_PLL = MIPI_DSI_HOST_DPHY_PD_PLL_dphy_pd_pll_MASK;
  }
  