/***************************************************************************//**
 * @file
 * @brief Operational Amplifier (OPAMP) peripheral API
 *******************************************************************************
 * # License
 * <b>Copyright 2018 Silicon Laboratories Inc. www.silabs.com</b>
 *******************************************************************************
 *
 * SPDX-License-Identifier: Zlib
 *
 * The licensor of this software is Silicon Laboratories Inc.
 *
 * This software is provided 'as-is', without any express or implied
 * warranty. In no event will the authors be held liable for any damages
 * arising from the use of this software.
 *
 * Permission is granted to anyone to use this software for any purpose,
 * including commercial applications, and to alter it and redistribute it
 * freely, subject to the following restrictions:
 *
 * 1. The origin of this software must not be misrepresented; you must not
 *    claim that you wrote the original software. If you use this software
 *    in a product, an acknowledgment in the product documentation would be
 *    appreciated but is not required.
 * 2. Altered source versions must be plainly marked as such, and must not be
 *    misrepresented as being the original software.
 * 3. This notice may not be removed or altered from any source distribution.
 *
 ******************************************************************************/

#include "em_opamp.h"
#if ((defined(_SILICON_LABS_32B_SERIES_0) && defined(OPAMP_PRESENT) && (OPAMP_COUNT == 1)) \
  || (defined(_SILICON_LABS_32B_SERIES_1) && defined(VDAC_PRESENT)  && (VDAC_COUNT > 0)))

#include "em_system.h"
#include "sl_assert.h"

/* *INDENT-OFF* */
/***************************************************************************//**
 * @addtogroup opamp OPAMP - Operational Amplifier
 * @brief Operational Amplifier (OPAMP) peripheral API
 * @details
 *  This module contains functions to:
 *   @li OPAMP_Enable()       Configure and enable OPAMP.
 *   @li OPAMP_Disable()      Disable OPAMP.
 *
 * @if DOXYDOC_P1_DEVICE
 * All OPAMP functions assume that the DAC clock is running. If DAC is not
 * used, the clock can be turned off when OPAMPs are configured.
 * @elseif DOXYDOC_P2_DEVICE
 * All OPAMP functions assume that the VDAC clock is running. If VDAC is not
 * used, the clock can be turned off when the OPAMPs are configured.
 * @endif
 *
 * If the available gain values don't suit the application at hand, the resistor
 * ladders can be disabled and external gain programming resistors used.
 *
 * A number of predefined OPAMP setup macros are available for configuration
 * of the most common OPAMP topologies (see figures below).
 *
 * @note
 * <em>The terms POSPAD and NEGPAD in the figures are used to indicate that these
 * pads should be connected to a suitable signal ground.</em>
 *
 * \n<b>Unity gain voltage follower.</b>\n
 * @if DOXYDOC_P1_DEVICE
 * Use predefined macros @ref OPA_INIT_UNITY_GAIN and
 * @ref OPA_INIT_UNITY_GAIN_OPA2.
 * @elseif DOXYDOC_P2_DEVICE
 * Use predefined macro @ref OPA_INIT_UNITY_GAIN.
 * @endif
 * @verbatim

                       |\
            ___________|+\
                       |  \_______
                    ___|_ /    |
                   |   | /     |
                   |   |/      |
                   |___________|
   @endverbatim
 *
 * \n<b>Non-inverting amplifier.</b>\n
 * @if DOXYDOC_P1_DEVICE
 * Use predefined macros @ref OPA_INIT_NON_INVERTING and
 * @ref OPA_INIT_NON_INVERTING_OPA2.
 * @elseif DOXYDOC_P2_DEVICE
 * Use predefined macro @ref OPA_INIT_NON_INVERTING.
 * @endif
 * @verbatim

                       |\
            ___________|+\
                       |  \_______
                    ___|_ /    |
                   |   | /     |
                   |   |/      |
                   |_____R2____|
                   |
                   R1
                   |
                 NEGPAD @endverbatim
 *
 * \n<b>Inverting amplifier.</b>\n
 * @if DOXYDOC_P1_DEVICE
 * Use predefined macros @ref OPA_INIT_INVERTING and
 * @ref OPA_INIT_INVERTING_OPA2.
 * @elseif DOXYDOC_P2_DEVICE
 * Use predefined macro @ref OPA_INIT_INVERTING.
 * @endif
 * @verbatim

                    _____R2____
                   |           |
                   |   |\      |
            ____R1_|___|_\     |
                       |  \____|___
                    ___|  /
                   |   |+/
                   |   |/
                   |
                 POSPAD @endverbatim
 *
 * \n<b>Cascaded non-inverting amplifiers.</b>\n
 * Use predefined macros @ref OPA_INIT_CASCADED_NON_INVERTING_OPA0,
 * @ref OPA_INIT_CASCADED_NON_INVERTING_OPA1 and
 * @ref OPA_INIT_CASCADED_NON_INVERTING_OPA2.
 * @verbatim

                       |\                       |\                       |\
            ___________|+\ OPA0      ___________|+\ OPA1      ___________|+\ OPA2
                       |  \_________|           |  \_________|           |  \_______
                    ___|_ /    |             ___|_ /    |             ___|_ /    |
                   |   | /     |            |   | /     |            |   | /     |
                   |   |/      |            |   |/      |            |   |/      |
                   |_____R2____|            |_____R2____|            |_____R2____|
                   |                        |                        |
                   R1                       R1                       R1
                   |                        |                        |
                 NEGPAD                   NEGPAD                   NEGPAD @endverbatim
 *
 * \n<b>Cascaded inverting amplifiers.</b>\n
 * Use predefined macros @ref OPA_INIT_CASCADED_INVERTING_OPA0,
 * @ref OPA_INIT_CASCADED_INVERTING_OPA1 and
 * @ref OPA_INIT_CASCADED_INVERTING_OPA2.
 * @verbatim

                    _____R2____              _____R2____              _____R2____
                   |           |            |           |            |           |
                   |   |\      |            |   |\      |            |   |\      |
            ____R1_|___|_\     |     ____R1_|___|_\     |     ____R1_|___|_\     |
                       |  \____|____|           |  \____|___|            |  \____|__
                    ___|  /                  ___|  /                  ___|  /
                   |   |+/ OPA0             |   |+/ OPA1             |   |+/ OPA2
                   |   |/                   |   |/                   |   |/
                   |                        |                        |
                 POSPAD                   POSPAD                   POSPAD @endverbatim
 *
 * \n<b>Differential driver with two opamp's.</b>\n
 * Use predefined macros @ref OPA_INIT_DIFF_DRIVER_OPA0 and
 * @ref OPA_INIT_DIFF_DRIVER_OPA1.
 * @verbatim

                                     __________________________
                                    |                          +
                                    |        _____R2____
                       |\           |       |           |
            ___________|+\ OPA0     |       |   |\ OPA1 |
                       |  \_________|____R1_|___|_\     |      _
                    ___|_ /         |           |  \____|______
                   |   | /          |        ___|  /
                   |   |/           |       |   |+/
                   |________________|       |   |/
                                            |
                                          POSPAD @endverbatim
 *
 * \n<b>Differential receiver with three opamp's.</b>\n
 * Use predefined macros @ref OPA_INIT_DIFF_RECEIVER_OPA0,
 * @ref OPA_INIT_DIFF_RECEIVER_OPA1 and @ref OPA_INIT_DIFF_RECEIVER_OPA2.
 * @verbatim

                       |\
             __________|+\ OPA1
            _          |  \_________
                    ___|_ /    |    |        _____R2____
                   |   | /     |    |       |           |
                   |   |/      |    |       |   |\      |
                   |___________|    |____R1_|___|_\     |
                                                |  \____|___
                       |\            ____R1_ ___|  /
            +__________|+\ OPA0     |       |   |+/ OPA2
                       |  \_________|       |   |/
                    ___|_ /    |            R2
                   |   | /     |            |
                   |   |/      |          NEGPAD OPA0
                   |___________|
   @endverbatim
 *
 * @if DOXYDOC_P2_DEVICE
 * \n<b>Instrumentation amplifier.</b>\n
 * Use predefined macros @ref OPA_INIT_INSTR_AMP_OPA0 and
 * @ref OPA_INIT_INSTR_AMP_OPA1.
 * @verbatim

                       |\
             __________|+\ OPA1
                       |  \______________
                    ___|_ /     |
                   |   | /      |
                   |   |/       R2
                   |____________|
                                |
                                R1
                                |
                                R1
                    ____________|
                   |            |
                   |            R2
                   |   |\       |
                   |___|+\ OPA0 |
                       |  \_____|________
             __________|_ /
                       | /
                       |/

   @endverbatim
 * @endif
 *
 * @{
 ******************************************************************************/
/* *INDENT-ON* */

/*******************************************************************************
 **************************   GLOBAL FUNCTIONS   *******************************
 ******************************************************************************/

/***************************************************************************//**
 * @brief
 *   Disable an Operational Amplifier.
 *
 * @if DOXYDOC_P1_DEVICE
 * @param[in] dac
 *   A pointer to the DAC peripheral register block.
 * @elseif DOXYDOC_P2_DEVICE
 * @param[in] dac
 *   A pointer to the VDAC peripheral register block.
 * @endif
 *
 * @param[in] opa
 *   Selects an OPA, valid values are OPA0, OPA1, and OPA2.
 ******************************************************************************/
void OPAMP_Disable(
#if defined(_SILICON_LABS_32B_SERIES_0)
  DAC_TypeDef *dac,
#elif defined(_SILICON_LABS_32B_SERIES_1)
  VDAC_TypeDef *dac,
#endif
  OPAMP_TypeDef opa)
{
#if defined(_SILICON_LABS_32B_SERIES_0)
  EFM_ASSERT(DAC_REF_VALID(dac));
  EFM_ASSERT(DAC_OPA_VALID(opa));

  if (opa == OPA0) {
    dac->CH0CTRL &= ~DAC_CH0CTRL_EN;
    dac->OPACTRL &= ~DAC_OPACTRL_OPA0EN;
  } else if (opa == OPA1) {
    dac->CH1CTRL &= ~DAC_CH1CTRL_EN;
    dac->OPACTRL &= ~DAC_OPACTRL_OPA1EN;
  } else { /* OPA2 */
    dac->OPACTRL &= ~DAC_OPACTRL_OPA2EN;
  }

#elif defined(_SILICON_LABS_32B_SERIES_1)
  EFM_ASSERT(VDAC_REF_VALID(dac));
  EFM_ASSERT(VDAC_OPA_VALID(opa));

  if (opa == OPA0) {
#if defined(VDAC_STATUS_OPA0ENS)
    dac->CMD |= VDAC_CMD_OPA0DIS;
    while (dac->STATUS & VDAC_STATUS_OPA0ENS) {
    }
#endif
#if defined(VDAC_STATUS_OPA1ENS)
  } else if (opa == OPA1) {
    dac->CMD |= VDAC_CMD_OPA1DIS;
    while (dac->STATUS & VDAC_STATUS_OPA1ENS) {
    }
#endif
#if defined(VDAC_STATUS_OPA2ENS)
  } else if (opa == OPA2) {
    dac->CMD |= VDAC_CMD_OPA2DIS;
    while (dac->STATUS & VDAC_STATUS_OPA2ENS) {
    }
#endif
  } else { /* OPA3 */
#if defined(VDAC_STATUS_OPA3ENS)
    dac->CMD |= VDAC_CMD_OPA3DIS;
    while (dac->STATUS & VDAC_STATUS_OPA3ENS) {
    }
#endif
  }
#endif
}

/***************************************************************************//**
 * @brief
 *   Configure and enable an Operational Amplifier.
 *
 * @if DOXYDOC_P1_DEVICE
 * @note
 *   The value of the alternate output enable bit mask in the OPAMP_Init_TypeDef
 *   structure should consist of one or more of the
 *   DAC_OPA[opa#]MUX_OUTPEN_OUT[output#] flags
 *   (defined in \<part_name\>_dac.h) OR'ed together. @n @n
 *   For OPA0:
 *   @li DAC_OPA0MUX_OUTPEN_OUT0
 *   @li DAC_OPA0MUX_OUTPEN_OUT1
 *   @li DAC_OPA0MUX_OUTPEN_OUT2
 *   @li DAC_OPA0MUX_OUTPEN_OUT3
 *   @li DAC_OPA0MUX_OUTPEN_OUT4
 *
 *   For OPA1:
 *   @li DAC_OPA1MUX_OUTPEN_OUT0
 *   @li DAC_OPA1MUX_OUTPEN_OUT1
 *   @li DAC_OPA1MUX_OUTPEN_OUT2
 *   @li DAC_OPA1MUX_OUTPEN_OUT3
 *   @li DAC_OPA1MUX_OUTPEN_OUT4
 *
 *   For OPA2:
 *   @li DAC_OPA2MUX_OUTPEN_OUT0
 *   @li DAC_OPA2MUX_OUTPEN_OUT1
 *
 *   E.g: @n
 *   init.outPen = DAC_OPA0MUX_OUTPEN_OUT0 | DAC_OPA0MUX_OUTPEN_OUT4;
 *
 * @param[in] dac
 *   A pointer to the DAC peripheral register block.
 * @elseif DOXYDOC_P2_DEVICE
 * @note
 *   The value of the alternate output enable bit mask in the OPAMP_Init_TypeDef
 *   structure should consist of one or more of the
 *   VDAC_OPA_OUT_ALTOUTPADEN_OUT[output#] flags
 *   (defined in \<part_name\>_vdac.h) OR'ed together. @n @n
 *   @li VDAC_OPA_OUT_ALTOUTPADEN_OUT0
 *   @li VDAC_OPA_OUT_ALTOUTPADEN_OUT1
 *   @li VDAC_OPA_OUT_ALTOUTPADEN_OUT2
 *   @li VDAC_OPA_OUT_ALTOUTPADEN_OUT3
 *   @li VDAC_OPA_OUT_ALTOUTPADEN_OUT4
 *
 *   For example: @n
 *   init.outPen = VDAC_OPA_OUT_ALTOUTPADEN_OUT0 | VDAC_OPA_OUT_ALTOUTPADEN_OUT4;
 * @param[in] dac
 *   A pointer to the VDAC peripheral register block.
 * @endif
 *
 * @param[in] opa
 *   Selects an OPA, valid values are OPA0, OPA1, and OPA2.
 *
 * @param[in] init
 *   A pointer to a structure containing OPAMP initialization information.
 ******************************************************************************/
void OPAMP_Enable(
#if defined(_SILICON_LABS_32B_SERIES_0)
  DAC_TypeDef *dac,
#elif defined(_SILICON_LABS_32B_SERIES_1)
  VDAC_TypeDef *dac,
#endif
  OPAMP_TypeDef opa,
  const OPAMP_Init_TypeDef *init)
{
#if defined(_SILICON_LABS_32B_SERIES_0)
  uint32_t gain;

  EFM_ASSERT(DAC_REF_VALID(dac));
  EFM_ASSERT(DAC_OPA_VALID(opa));
  EFM_ASSERT(init->bias <= (_DAC_BIASPROG_BIASPROG_MASK
                            >> _DAC_BIASPROG_BIASPROG_SHIFT));

  if (opa == OPA0) {
    EFM_ASSERT((init->outPen & ~_DAC_OPA0MUX_OUTPEN_MASK) == 0);

    dac->BIASPROG = (dac->BIASPROG
                     & ~(_DAC_BIASPROG_BIASPROG_MASK
                         | DAC_BIASPROG_HALFBIAS))
                    | (init->bias     << _DAC_BIASPROG_BIASPROG_SHIFT)
                    | (init->halfBias ?   DAC_BIASPROG_HALFBIAS : 0);

    if (init->defaultOffset) {
      gain = dac->CAL & _DAC_CAL_GAIN_MASK;
      SYSTEM_GetCalibrationValue(&dac->CAL);
      dac->CAL = (dac->CAL & ~_DAC_CAL_GAIN_MASK) | gain;
    } else {
      EFM_ASSERT(init->offset <= (_DAC_CAL_CH0OFFSET_MASK
                                  >> _DAC_CAL_CH0OFFSET_SHIFT));

      dac->CAL = (dac->CAL & ~_DAC_CAL_CH0OFFSET_MASK)
                 | (init->offset << _DAC_CAL_CH0OFFSET_SHIFT);
    }

    dac->OPA0MUX  = (uint32_t)init->resSel
                    | (uint32_t)init->outMode
                    | init->outPen
                    | (uint32_t)init->resInMux
                    | (uint32_t)init->negSel
                    | (uint32_t)init->posSel
                    | (init->nextOut ? DAC_OPA0MUX_NEXTOUT : 0)
                    | (init->npEn    ? DAC_OPA0MUX_NPEN    : 0)
                    | (init->ppEn    ? DAC_OPA0MUX_PPEN    : 0);

    dac->CH0CTRL |= DAC_CH0CTRL_EN;
    dac->OPACTRL  = (dac->OPACTRL
                     & ~(DAC_OPACTRL_OPA0SHORT
                         | _DAC_OPACTRL_OPA0LPFDIS_MASK
                         |  DAC_OPACTRL_OPA0HCMDIS))
                    | (init->shortInputs ?  DAC_OPACTRL_OPA0SHORT : 0)
                    | (init->lpfPosPadDisable
                       ? DAC_OPACTRL_OPA0LPFDIS_PLPFDIS : 0)
                    | (init->lpfNegPadDisable
                       ? DAC_OPACTRL_OPA0LPFDIS_NLPFDIS : 0)
                    | (init->hcmDisable ? DAC_OPACTRL_OPA0HCMDIS : 0)
                    | DAC_OPACTRL_OPA0EN;
  } else if ( opa == OPA1 ) {
    EFM_ASSERT((init->outPen & ~_DAC_OPA1MUX_OUTPEN_MASK) == 0);

    dac->BIASPROG = (dac->BIASPROG
                     & ~(_DAC_BIASPROG_BIASPROG_MASK
                         | DAC_BIASPROG_HALFBIAS))
                    | (init->bias   << _DAC_BIASPROG_BIASPROG_SHIFT)
                    | (init->halfBias ? DAC_BIASPROG_HALFBIAS : 0);

    if (init->defaultOffset) {
      gain = dac->CAL & _DAC_CAL_GAIN_MASK;
      SYSTEM_GetCalibrationValue(&dac->CAL);
      dac->CAL = (dac->CAL & ~_DAC_CAL_GAIN_MASK) | gain;
    } else {
      EFM_ASSERT(init->offset <= (_DAC_CAL_CH1OFFSET_MASK
                                  >> _DAC_CAL_CH1OFFSET_SHIFT));

      dac->CAL = (dac->CAL & ~_DAC_CAL_CH1OFFSET_MASK)
                 | (init->offset << _DAC_CAL_CH1OFFSET_SHIFT);
    }

    dac->OPA1MUX  = (uint32_t)init->resSel
                    | (uint32_t)init->outMode
                    | init->outPen
                    | (uint32_t)init->resInMux
                    | (uint32_t)init->negSel
                    | (uint32_t)init->posSel
                    | (init->nextOut ? DAC_OPA1MUX_NEXTOUT : 0)
                    | (init->npEn    ? DAC_OPA1MUX_NPEN    : 0)
                    | (init->ppEn    ? DAC_OPA1MUX_PPEN    : 0);

    dac->CH1CTRL |= DAC_CH1CTRL_EN;
    dac->OPACTRL  = (dac->OPACTRL
                     & ~(DAC_OPACTRL_OPA1SHORT
                         | _DAC_OPACTRL_OPA1LPFDIS_MASK
                         | DAC_OPACTRL_OPA1HCMDIS))
                    | (init->shortInputs ? DAC_OPACTRL_OPA1SHORT : 0)
                    | (init->lpfPosPadDisable
                       ? DAC_OPACTRL_OPA1LPFDIS_PLPFDIS : 0)
                    | (init->lpfNegPadDisable
                       ? DAC_OPACTRL_OPA1LPFDIS_NLPFDIS : 0)
                    | (init->hcmDisable ? DAC_OPACTRL_OPA1HCMDIS : 0)
                    | DAC_OPACTRL_OPA1EN;
  } else { /* OPA2 */
    EFM_ASSERT((init->posSel == DAC_OPA2MUX_POSSEL_DISABLE)
               || (init->posSel == DAC_OPA2MUX_POSSEL_POSPAD)
               || (init->posSel == DAC_OPA2MUX_POSSEL_OPA1INP)
               || (init->posSel == DAC_OPA2MUX_POSSEL_OPATAP));

    EFM_ASSERT((init->outMode & ~DAC_OPA2MUX_OUTMODE) == 0);

    EFM_ASSERT((init->outPen & ~_DAC_OPA2MUX_OUTPEN_MASK) == 0);

    dac->BIASPROG = (dac->BIASPROG
                     & ~(_DAC_BIASPROG_OPA2BIASPROG_MASK
                         | DAC_BIASPROG_OPA2HALFBIAS))
                    | (init->bias << _DAC_BIASPROG_OPA2BIASPROG_SHIFT)
                    | (init->halfBias ? DAC_BIASPROG_OPA2HALFBIAS : 0);

    if (init->defaultOffset) {
      SYSTEM_GetCalibrationValue(&dac->OPAOFFSET);
    } else {
      EFM_ASSERT(init->offset <= (_DAC_OPAOFFSET_OPA2OFFSET_MASK
                                  >> _DAC_OPAOFFSET_OPA2OFFSET_SHIFT));
      dac->OPAOFFSET = (dac->OPAOFFSET & ~_DAC_OPAOFFSET_OPA2OFFSET_MASK)
                       | (init->offset << _DAC_OPAOFFSET_OPA2OFFSET_SHIFT);
    }

    dac->OPA2MUX  = (uint32_t)init->resSel
                    | (uint32_t)init->outMode
                    | init->outPen
                    | (uint32_t)init->resInMux
                    | (uint32_t)init->negSel
                    | (uint32_t)init->posSel
                    | (init->nextOut ? DAC_OPA2MUX_NEXTOUT : 0)
                    | (init->npEn    ? DAC_OPA2MUX_NPEN    : 0)
                    | (init->ppEn    ? DAC_OPA2MUX_PPEN    : 0);

    dac->OPACTRL  = (dac->OPACTRL
                     & ~(DAC_OPACTRL_OPA2SHORT
                         | _DAC_OPACTRL_OPA2LPFDIS_MASK
                         | DAC_OPACTRL_OPA2HCMDIS))
                    | (init->shortInputs ?  DAC_OPACTRL_OPA2SHORT : 0)
                    | (init->lpfPosPadDisable
                       ? DAC_OPACTRL_OPA2LPFDIS_PLPFDIS : 0)
                    | (init->lpfNegPadDisable
                       ? DAC_OPACTRL_OPA2LPFDIS_NLPFDIS : 0)
                    | (init->hcmDisable ? DAC_OPACTRL_OPA2HCMDIS : 0)
                    | DAC_OPACTRL_OPA2EN;
  }

#elif defined(_SILICON_LABS_32B_SERIES_1)
  uint32_t calData = 0;
  uint32_t warmupTime;

  EFM_ASSERT(VDAC_REF_VALID(dac));
  EFM_ASSERT(VDAC_OPA_VALID(opa));
  EFM_ASSERT(init->settleTime <= (_VDAC_OPA_TIMER_SETTLETIME_MASK
                                  >> _VDAC_OPA_TIMER_SETTLETIME_SHIFT));
  EFM_ASSERT(init->startupDly <= (_VDAC_OPA_TIMER_STARTUPDLY_MASK
                                  >> _VDAC_OPA_TIMER_STARTUPDLY_SHIFT));
  EFM_ASSERT((init->outPen & ~_VDAC_OPA_OUT_ALTOUTPADEN_MASK) == 0);
  EFM_ASSERT((init->drvStr == opaDrvStrLowerAccLowStr)
             || (init->drvStr == opaDrvStrLowAccLowStr)
             || (init->drvStr == opaDrvStrHighAccHighStr)
             || (init->drvStr == opaDrvStrHigherAccHighStr));

  /* Disable OPAMP before writing to registers. */
  OPAMP_Disable(dac, opa);

  /* Get the calibration value based on OPAMP, Drive Strength, and INCBW. */
  switch (opa) {
#if defined(VDAC_STATUS_OPA0ENS)
    case OPA0:
      switch (init->drvStr) {
        case opaDrvStrLowerAccLowStr:
          calData = (init->ugBwScale ? DEVINFO->OPA0CAL0 : DEVINFO->OPA0CAL4);
          break;
        case opaDrvStrLowAccLowStr:
          calData = (init->ugBwScale ? DEVINFO->OPA0CAL1 : DEVINFO->OPA0CAL5);
          break;
        case opaDrvStrHighAccHighStr:
          calData = (init->ugBwScale ? DEVINFO->OPA0CAL2 : DEVINFO->OPA0CAL6);
          break;
        case opaDrvStrHigherAccHighStr:
          calData = (init->ugBwScale ? DEVINFO->OPA0CAL3 : DEVINFO->OPA0CAL7);
          break;
      }
      break;
#endif

#if defined(VDAC_STATUS_OPA1ENS)
    case OPA1:
      switch (init->drvStr) {
        case opaDrvStrLowerAccLowStr:
          calData = (init->ugBwScale ? DEVINFO->OPA1CAL0 : DEVINFO->OPA1CAL4);
          break;
        case opaDrvStrLowAccLowStr:
          calData = (init->ugBwScale ? DEVINFO->OPA1CAL1 : DEVINFO->OPA1CAL5);
          break;
        case opaDrvStrHighAccHighStr:
          calData = (init->ugBwScale ? DEVINFO->OPA1CAL2 : DEVINFO->OPA1CAL6);
          break;
        case opaDrvStrHigherAccHighStr:
          calData = (init->ugBwScale ? DEVINFO->OPA1CAL3 : DEVINFO->OPA1CAL7);
          break;
      }
      break;
#endif

#if defined(VDAC_STATUS_OPA2ENS)
    case OPA2:
      switch (init->drvStr) {
        case opaDrvStrLowerAccLowStr:
          calData = (init->ugBwScale ? DEVINFO->OPA2CAL0 : DEVINFO->OPA2CAL4);
          break;
        case opaDrvStrLowAccLowStr:
          calData = (init->ugBwScale ? DEVINFO->OPA2CAL1 : DEVINFO->OPA2CAL5);
          break;
        case opaDrvStrHighAccHighStr:
          calData = (init->ugBwScale ? DEVINFO->OPA2CAL2 : DEVINFO->OPA2CAL6);
          break;
        case opaDrvStrHigherAccHighStr:
          calData = (init->ugBwScale ? DEVINFO->OPA2CAL3 : DEVINFO->OPA2CAL7);
          break;
      }
      break;
#endif

#if defined(VDAC_STATUS_OPA3ENS)
    case OPA3:
      switch (init->drvStr) {
        case opaDrvStrLowerAccLowStr:
          calData = (init->ugBwScale ? DEVINFO->OPA3CAL0 : DEVINFO->OPA3CAL4);
          break;
        case opaDrvStrLowAccLowStr:
          calData = (init->ugBwScale ? DEVINFO->OPA3CAL1 : DEVINFO->OPA3CAL5);
          break;
        case opaDrvStrHighAccHighStr:
          calData = (init->ugBwScale ? DEVINFO->OPA3CAL2 : DEVINFO->OPA3CAL6);
          break;
        case opaDrvStrHigherAccHighStr:
          calData = (init->ugBwScale ? DEVINFO->OPA3CAL3 : DEVINFO->OPA3CAL7);
          break;
      }
      break;
#endif
  }
  if (!init->defaultOffsetN) {
    EFM_ASSERT(init->offsetN <= (_VDAC_OPA_CAL_OFFSETN_MASK
                                 >> _VDAC_OPA_CAL_OFFSETN_SHIFT));
    calData = (calData & ~_VDAC_OPA_CAL_OFFSETN_MASK)
              | (init->offsetN << _VDAC_OPA_CAL_OFFSETN_SHIFT);
  }
  if (!init->defaultOffsetP) {
    EFM_ASSERT(init->offsetP <= (_VDAC_OPA_CAL_OFFSETP_MASK
                                 >> _VDAC_OPA_CAL_OFFSETP_SHIFT));
    calData = (calData & ~_VDAC_OPA_CAL_OFFSETP_MASK)
              | (init->offsetP << _VDAC_OPA_CAL_OFFSETP_SHIFT);
  }

  dac->OPA[opa].CAL = (calData &  _VDAC_OPA_CAL_MASK);

  dac->OPA[opa].MUX = (uint32_t)init->resSel
                      | (init->gain3xEn  ? VDAC_OPA_MUX_GAIN3X : 0)
                      | (uint32_t)init->resInMux
                      | (uint32_t)init->negSel
                      | (uint32_t)init->posSel;

  dac->OPA[opa].OUT = (uint32_t)init->outMode
                      | (uint32_t)init->outPen;

  switch (init->drvStr) {
    case opaDrvStrHigherAccHighStr:
      warmupTime = 6;
      break;

    case opaDrvStrHighAccHighStr:
      warmupTime = 8;
      break;

    case opaDrvStrLowAccLowStr:
      warmupTime = 85;
      break;

    case opaDrvStrLowerAccLowStr:
    default:
      warmupTime = 100;
      break;
  }

  dac->OPA[opa].TIMER = (uint32_t)(init->settleTime
                                   << _VDAC_OPA_TIMER_SETTLETIME_SHIFT)
                        | (uint32_t)(warmupTime
                                     << _VDAC_OPA_TIMER_WARMUPTIME_SHIFT)
                        | (uint32_t)(init->startupDly
                                     << _VDAC_OPA_TIMER_STARTUPDLY_SHIFT);

  dac->OPA[opa].CTRL  = (init->aportYMasterDisable
                         ? VDAC_OPA_CTRL_APORTYMASTERDIS : 0)
                        | (init->aportXMasterDisable
                           ? VDAC_OPA_CTRL_APORTXMASTERDIS : 0)
                        | (uint32_t)init->prsOutSel
                        | (uint32_t)init->prsSel
                        | (uint32_t)init->prsMode
                        | (init->prsEn ? VDAC_OPA_CTRL_PRSEN : 0)
                        | (init->halfDrvStr
                           ? VDAC_OPA_CTRL_OUTSCALE_HALF
                           : VDAC_OPA_CTRL_OUTSCALE_FULL)
                        | (init->hcmDisable ? VDAC_OPA_CTRL_HCMDIS : 0)
                        | (init->ugBwScale ? VDAC_OPA_CTRL_INCBW : 0)
                        | (uint32_t)init->drvStr;

  if (opa == OPA0) {
#if defined(VDAC_STATUS_OPA0ENS)
    dac->CMD |= VDAC_CMD_OPA0EN;
#endif
#if defined(VDAC_STATUS_OPA1ENS)
  } else if (opa == OPA1) {
    dac->CMD |= VDAC_CMD_OPA1EN;
#endif
#if defined(VDAC_STATUS_OPA2ENS)
  } else if (opa == OPA2) {
    dac->CMD |= VDAC_CMD_OPA2EN;
#endif
#if defined(VDAC_STATUS_OPA3ENS)
  } else { /* OPA3 */
    dac->CMD |= VDAC_CMD_OPA3EN;
#endif
  }

#endif
}

/** @} (end addtogroup opamp) */

#endif /* (defined(OPAMP_PRESENT) && (OPAMP_COUNT == 1)
       || defined(VDAC_PRESENT) && (VDAC_COUNT > 0) */
