text: /***************************************************************************//**
   * @file
   * @brief Liquid Crystal Display (LCD) 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_lcd.h"
  #if defined(LCD_COUNT) && (LCD_COUNT > 0)
  #include "em_assert.h"
  #include "em_bus.h"
  
  /***************************************************************************//**
   * @addtogroup emlib
   * @{
   ******************************************************************************/
  
  /***************************************************************************//**
   * @addtogroup LCD
   * @brief Liquid Crystal Display (LCD) Peripheral API
   * @details
   *  This module contains functions to control the LDC peripheral of Silicon
   *  Labs 32-bit MCUs and SoCs. The LCD driver can drive up to 8x36 segmented
   *  LCD directly. The animation feature makes it possible to have active
   *  animations without the CPU intervention.
   * @{
   ******************************************************************************/
  
  /***************************************************************************//**
   * @brief
   *   Initialize the Liquid Crystal Display (LCD) controller.
   *
   * @details
   *   Configures the LCD controller. You must enable
   *   it afterwards, potentially configuring Frame Control and interrupts first
   *   according to requirements.
   *
   * @param[in] lcdInit
   *   A pointer to the initialization structure which configures the LCD controller.
   *
   ******************************************************************************/
  void LCD_Init(const LCD_Init_TypeDef *lcdInit)
  {
    uint32_t dispCtrl = LCD->DISPCTRL;
  
    EFM_ASSERT(lcdInit != (void *) 0);
  
    /* Disable the controller before reconfiguration. */
    LCD_Enable(false);
  
    /* Make sure the other bit fields don't get affected (i.e., voltage boost). */
    dispCtrl &= ~(0
  #if defined(LCD_DISPCTRL_MUXE)
                  | _LCD_DISPCTRL_MUXE_MASK
  #endif
                  | _LCD_DISPCTRL_MUX_MASK
                  | _LCD_DISPCTRL_BIAS_MASK
                  | _LCD_DISPCTRL_WAVE_MASK
  #if defined(_LCD_DISPCTRL_VLCDSEL_MASK)
                  | _LCD_DISPCTRL_VLCDSEL_MASK
  #endif
  #if defined(_LCD_DISPCTRL_CONCONF_MASK)
                  | _LCD_DISPCTRL_CONCONF_MASK
  #endif
  #if defined(_LCD_DISPCTRL_MODE_MASK)
                  | _LCD_DISPCTRL_MODE_MASK
  #endif
  #if defined(_LCD_DISPCTRL_CHGRDST_MASK)
                  | _LCD_DISPCTRL_CHGRDST_MASK
  #endif
                  );
  
    /* Configure the controller according to the initialization structure. */
    dispCtrl |= lcdInit->mux; /* Also configures MUXE. */
    dispCtrl |= lcdInit->bias;
    dispCtrl |= lcdInit->wave;
  #if defined(_SILICON_LABS_32B_SERIES_0)
    dispCtrl |= lcdInit->vlcd;
    dispCtrl |= lcdInit->contrast;
  #endif
  #if defined(_SILICON_LABS_32B_SERIES_1)
    dispCtrl |= lcdInit->mode;
    dispCtrl |= (lcdInit->chargeRedistribution);
  #endif
  
    /* Update the display controller. */
    LCD->DISPCTRL = dispCtrl;
  
  #if defined(_SILICON_LABS_32B_SERIES_1)
    LCD->FRAMERATE = lcdInit->frameRateDivider;
    LCD_ContrastSet(lcdInit->contrastLevel);
  #endif
  
    /* Enable the controller if needed. */
    if (lcdInit->enable) {
      LCD_Enable(true);
    }
  }
  
  #if defined(_SILICON_LABS_32B_SERIES_0)
  /***************************************************************************//**
   * @brief
   *   Select a source for VLCD.
   *
   * @param[in] vlcd
   *   Select a source for the VLCD voltage.
   ******************************************************************************/
  void LCD_VLCDSelect(LCD_VLCDSel_TypeDef vlcd)
  {
    uint32_t dispctrl = LCD->DISPCTRL;
  
    /* Select VEXT or VDD */
    dispctrl &= ~_LCD_DISPCTRL_VLCDSEL_MASK;
    switch (vlcd) {
      case lcdVLCDSelVExtBoost:
        dispctrl |= LCD_DISPCTRL_VLCDSEL_VEXTBOOST;
        break;
      case lcdVLCDSelVDD:
        dispctrl |= LCD_DISPCTRL_VLCDSEL_VDD;
        break;
      default:
        break;
    }
  
    LCD->DISPCTRL = dispctrl;
  }
  #endif
  
  /***************************************************************************//**
   * @brief
   *   Configure Update Control.
   *
   * @param[in] ud
   *   Configures the LCD update method.
   ******************************************************************************/
  void LCD_UpdateCtrl(LCD_UpdateCtrl_TypeDef ud)
  {
    LCD->CTRL = (LCD->CTRL & ~_LCD_CTRL_UDCTRL_MASK) | ud;
  }
  
  /***************************************************************************//**
   * @brief
   *   Initialize the LCD Frame Counter.
   *
   * @param[in] fcInit
   *   A pointer to the Frame Counter initialization structure.
   ******************************************************************************/
  void LCD_FrameCountInit(const LCD_FrameCountInit_TypeDef *fcInit)
  {
    uint32_t bactrl = LCD->BACTRL;
  
    EFM_ASSERT(fcInit != (void *) 0);
  
    /* Verify that the FC Top Counter is within limits. */
    EFM_ASSERT(fcInit->top < 64);
  
    /* Reconfigure the frame count configuration. */
    bactrl &= ~(_LCD_BACTRL_FCTOP_MASK
                | _LCD_BACTRL_FCPRESC_MASK);
    bactrl |= (fcInit->top << _LCD_BACTRL_FCTOP_SHIFT);
    bactrl |= fcInit->prescale;
  
    /* Set the Blink and Animation Control Register. */
    LCD->BACTRL = bactrl;
  
    LCD_FrameCountEnable(fcInit->enable);
  }
  
  /***************************************************************************//**
   * @brief
   *   Configure the LCD controller Animation feature.
   *
   * @param[in] animInit
   *   A pointer to the LCD Animation initialization structure.
   ******************************************************************************/
  void LCD_AnimInit(const LCD_AnimInit_TypeDef *animInit)
  {
    uint32_t bactrl = LCD->BACTRL;
  
    EFM_ASSERT(animInit != (void *) 0);
  
    /* Set Animation Register Values. */
    LCD->AREGA = animInit->AReg;
    LCD->AREGB = animInit->BReg;
  
    /* Configure the Animation Shift and Logic. */
    bactrl &= ~(_LCD_BACTRL_AREGASC_MASK
                | _LCD_BACTRL_AREGBSC_MASK
                | _LCD_BACTRL_ALOGSEL_MASK);
  
    bactrl |= (animInit->AShift << _LCD_BACTRL_AREGASC_SHIFT);
    bactrl |= (animInit->BShift << _LCD_BACTRL_AREGBSC_SHIFT);
    bactrl |= animInit->animLogic;
  
  #if defined(LCD_BACTRL_ALOC)
    bactrl &= ~(_LCD_BACTRL_ALOC_MASK);
  
    if (animInit->startSeg == 0) {
      bactrl |= LCD_BACTRL_ALOC_SEG0TO7;
    } else if (animInit->startSeg == 8) {
      bactrl |= LCD_BACTRL_ALOC_SEG8TO15;
    }
  #endif
  
    /* Reconfigure. */
    LCD->BACTRL = bactrl;
  
    /* Enable. */
    LCD_AnimEnable(animInit->enable);
  }
  
  /***************************************************************************//**
   * @brief
   *   Enables updating this range of LCD segment lines.
   *
   * @param[in] segmentRange
   *   A range of 4 LCD segment lines to enable or disable for all enabled COM
   *   lines.
   *
   * @param[in] enable
   *   Boolean true to enable segment updates, false to disable updates.
   ******************************************************************************/
  #if defined(_SILICON_LABS_32B_SERIES_0)
  void LCD_SegmentRangeEnable(LCD_SegmentRange_TypeDef segmentRange, bool enable)
  {
    if (enable) {
      LCD->SEGEN |= segmentRange;
    } else {
      LCD->SEGEN &= ~((uint32_t)segmentRange);
    }
  }
  #endif
  
  /***************************************************************************//**
   * @brief
   *   Turn on or clear a segment.
   *
   * @note
   *    For the Gecko Family, the maximum configuration is (COM-lines x Segment-Lines) 4x40.
   *    For the Tiny Gecko Family, the maximum configuration is 8x20 or 4x24.
   *    For the Giant Gecko Family, the maximum configuration is 8x36 or 4x40.
   *
   * @param[in] com
   *   A COM line to change.
   *
   * @param[in] bit
   *   A bit index indicating which field to change.
   *
   * @param[in] enable
   *   True will set segment, false will clear segment.
   ******************************************************************************/
  void LCD_SegmentSet(int com, int bit, bool enable)
  {
  #if defined(_LCD_SEGD7L_MASK)
    /* Tiny Gecko and Giant Gecko Families support up to 8 COM lines. */
    EFM_ASSERT(com < 8);
  #else
    /* Gecko Family supports up to 4 COM lines. */
    EFM_ASSERT(com < 4);
  #endif
  
  #if defined(_LCD_SEGD0H_MASK)
    EFM_ASSERT(bit < 40);
  #else
    /* Tiny Gecko Family supports only "low" segment registers. */
    EFM_ASSERT(bit < 32);
  #endif
  
    /* Use a bitband access for atomic bit set/clear of the segment. */
    switch (com) {
      case 0:
        if (bit < 32) {
          BUS_RegBitWrite(&(LCD->SEGD0L), bit, enable);
        }
  #if defined(_LCD_SEGD0H_MASK)
        else {
          bit -= 32;
          BUS_RegBitWrite(&(LCD->SEGD0H), bit, enable);
        }
  #endif
        break;
      case 1:
        if (bit < 32) {
          BUS_RegBitWrite(&(LCD->SEGD1L), bit, enable);
        }
  #if defined(_LCD_SEGD1H_MASK)
        else {
          bit -= 32;
          BUS_RegBitWrite(&(LCD->SEGD1H), bit, enable);
        }
  #endif
        break;
      case 2:
        if (bit < 32) {
          BUS_RegBitWrite(&(LCD->SEGD2L), bit, enable);
        }
  #if defined(_LCD_SEGD2H_MASK)
        else {
          bit -= 32;
          BUS_RegBitWrite(&(LCD->SEGD2H), bit, enable);
        }
  #endif
        break;
      case 3:
        if (bit < 32) {
          BUS_RegBitWrite(&(LCD->SEGD3L), bit, enable);
        }
  #if defined(_LCD_SEGD3H_MASK)
        else {
          bit -= 32;
          BUS_RegBitWrite(&(LCD->SEGD3H), bit, enable);
        }
  #endif
        break;
  #if defined(_LCD_SEGD4L_MASK)
      case 4:
        if (bit < 32) {
          BUS_RegBitWrite(&(LCD->SEGD4L), bit, enable);
        }
  #if defined(_LCD_SEGD4H_MASK)
        else {
          bit -= 32;
          BUS_RegBitWrite(&(LCD->SEGD4H), bit, enable);
        }
  #endif
        break;
  #endif
  #if defined(_LCD_SEGD5L_MASK)
      case 5:
        if (bit < 32) {
          BUS_RegBitWrite(&(LCD->SEGD5L), bit, enable);
        }
  #if defined(_LCD_SEGD5H_MASK)
        else {
          bit -= 32;
          BUS_RegBitWrite(&(LCD->SEGD5H), bit, enable);
        }
  #endif
        break;
  #endif
      case 6:
  #if defined(_LCD_SEGD6L_MASK)
        if (bit < 32) {
          BUS_RegBitWrite(&(LCD->SEGD6L), bit, enable);
        }
  #if defined(_LCD_SEGD6H_MASK)
        else {
          bit -= 32;
          BUS_RegBitWrite(&(LCD->SEGD6H), bit, enable);
        }
  #endif
        break;
  #endif
  #if defined(_LCD_SEGD7L_MASK)
      case 7:
        if (bit < 32) {
          BUS_RegBitWrite(&(LCD->SEGD7L), bit, enable);
        }
  #if defined(_LCD_SEGD7H_MASK)
        else {
          bit -= 32;
          BUS_RegBitWrite(&(LCD->SEGD7H), bit, enable);
        }
  #endif
        break;
  #endif
  
      default:
        EFM_ASSERT(0);
        break;
    }
  }
  
  /***************************************************************************//**
   * @brief
   *   Update 0-31 lowest segments on a given COM-line in one operation
   *   according to the bit mask.
   *
   * @param[in] com
   *   Indicates a COM line to update.
   *
   * @param[in] mask
   *   A bit mask for segments 0-31.
   *
   * @param[in] bits
   *   A bit pattern for segments 0-31.
   ******************************************************************************/
  void LCD_SegmentSetLow(int com, uint32_t mask, uint32_t bits)
  {
    uint32_t segData;
  
    /* A maximum number of com lines. */
  #if defined(_LCD_SEGD7L_MASK)
    EFM_ASSERT(com < 8);
  #else
    /* Gecko Family supports up to 4 COM lines. */
    EFM_ASSERT(com < 4);
  #endif
  
    switch (com) {
      case 0:
        segData     = LCD->SEGD0L;
        segData    &= ~(mask);
        segData    |= (mask & bits);
        LCD->SEGD0L = segData;
        break;
      case 1:
        segData     = LCD->SEGD1L;
        segData    &= ~(mask);
        segData    |= (mask & bits);
        LCD->SEGD1L = segData;
        break;
      case 2:
        segData     = LCD->SEGD2L;
        segData    &= ~(mask);
        segData    |= (mask & bits);
        LCD->SEGD2L = segData;
        break;
      case 3:
        segData     = LCD->SEGD3L;
        segData    &= ~(mask);
        segData    |= (mask & bits);
        LCD->SEGD3L = segData;
        break;
  #if defined(_LCD_SEGD4L_MASK)
      case 4:
        segData     = LCD->SEGD4L;
        segData    &= ~(mask);
        segData    |= (mask & bits);
        LCD->SEGD4L = segData;
        break;
  #endif
  #if defined(_LCD_SEGD5L_MASK)
      case 5:
        segData     = LCD->SEGD5L;
        segData    &= ~(mask);
        segData    |= (mask & bits);
        LCD->SEGD5L = segData;
        break;
  #endif
  #if defined(_LCD_SEGD6L_MASK)
      case 6:
        segData     = LCD->SEGD6L;
        segData    &= ~(mask);
        segData    |= (mask & bits);
        LCD->SEGD6L = segData;
        break;
  #endif
  #if defined(_LCD_SEGD7L_MASK)
      case 7:
        segData     = LCD->SEGD7L;
        segData    &= ~(mask);
        segData    |= (mask & bits);
        LCD->SEGD7L = segData;
        break;
  #endif
      default:
        EFM_ASSERT(0);
        break;
    }
  }
  
  #if defined(_LCD_SEGD0H_MASK)
  /***************************************************************************//**
   * @brief
   *   Update the high (32-39) segments on a given COM-line in one operation.
   *
   * @param[in] com
   *   Indicates a COM line to update.
   *
   * @param[in] mask
   *   A bit mask for segments 32-39.
   *
   * @param[in] bits
   *   A bit pattern for segments 32-39.
   ******************************************************************************/
  void LCD_SegmentSetHigh(int com, uint32_t mask, uint32_t bits)
  {
    uint32_t segData;
  
  #if defined(_LCD_SEGD7H_MASK)
    EFM_ASSERT(com < 8);
  #else
    EFM_ASSERT(com < 4);
  #endif
  
    /* A maximum number of com lines. */
    switch (com) {
      case 0:
        segData     = LCD->SEGD0H;
        segData    &= ~(mask);
        segData    |= (mask & bits);
        LCD->SEGD0H = segData;
        break;
      case 1:
        segData     = LCD->SEGD1H;
        segData    &= ~(mask);
        segData    |= (mask & bits);
        LCD->SEGD1H = segData;
        break;
      case 2:
        segData     = LCD->SEGD2H;
        segData    &= ~(mask);
        segData    |= (mask & bits);
        LCD->SEGD2H = segData;
        break;
      case 3:
        segData     = LCD->SEGD3H;
        segData    &= ~(mask);
        segData    |= (mask & bits);
        LCD->SEGD3H = segData;
        break;
  #if defined(_LCD_SEGD4H_MASK)
      case 4:
        segData     = LCD->SEGD4H;
        segData    &= ~(mask);
        segData    |= (mask & bits);
        LCD->SEGD4H = segData;
        break;
  #endif
  #if defined(_LCD_SEGD5H_MASK)
      case 5:
        segData     = LCD->SEGD5H;
        segData    &= ~(mask);
        segData    |= (mask & bits);
        LCD->SEGD5H = segData;
        break;
  #endif
  #if defined(_LCD_SEGD6H_MASK)
      case 6:
        segData     = LCD->SEGD6H;
        segData    &= ~(mask);
        segData    |= (mask & bits);
        LCD->SEGD6H = segData;
        break;
  #endif
  #if defined(_LCD_SEGD7H_MASK)
      case 7:
        segData     = LCD->SEGD7H;
        segData    &= ~(mask);
        segData    |= (mask & bits);
        LCD->SEGD7H = segData;
        break;
  #endif
      default:
        break;
    }
  }
  #endif
  
  #if defined(_SILICON_LABS_32B_SERIES_0)
  /***************************************************************************//**
   * @brief
   *   Configure the contrast level on the LCD panel.
   *
   * @param[in] level
   *   The contrast level in range 0-31.
   ******************************************************************************/
  void LCD_ContrastSet(int level)
  {
    EFM_ASSERT(level < 32);
  
    LCD->DISPCTRL = (LCD->DISPCTRL & ~_LCD_DISPCTRL_CONLEV_MASK)
                    | (level << _LCD_DISPCTRL_CONLEV_SHIFT);
  }
  #endif
  
  #if defined(_SILICON_LABS_32B_SERIES_1)
  /***************************************************************************//**
   * @brief
   *   Configure the contrast level on the LCD panel.
   *
   * @param[in] level
   *   The contrast level in range 0-63.
   ******************************************************************************/
  void LCD_ContrastSet(int level)
  {
    EFM_ASSERT(level < 64);
  
    LCD->DISPCTRL = (LCD->DISPCTRL & ~_LCD_DISPCTRL_CONTRAST_MASK)
                    | (level << _LCD_DISPCTRL_CONTRAST_SHIFT);
  }
  #endif
  
  /***************************************************************************//**
   * @brief
   *   Configure the bias level on the LCD panel.
   *
   * @param[in] bias
   *   The bias level.
   ******************************************************************************/
  void LCD_BiasSet(LCD_Bias_TypeDef bias)
  {
    LCD->DISPCTRL = (LCD->DISPCTRL & ~_LCD_DISPCTRL_BIAS_MASK) | bias;
  }
  
  #if defined(_SILICON_LABS_32B_SERIES_0)
  /***************************************************************************//**
   * @brief
   *   Configure voltage booster
   *
   * The resulting voltage level is described in each part number's data sheet
   *
   * @param[in] vboost
   *   Voltage boost level
   ******************************************************************************/
  void LCD_VBoostSet(LCD_VBoostLevel_TypeDef vboost)
  {
    /* Reconfigure Voltage Boost */
    LCD->DISPCTRL = (LCD->DISPCTRL & ~_LCD_DISPCTRL_VBLEV_MASK) | vboost;
  }
  #endif
  
  #if defined(LCD_CTRL_DSC)
  /***************************************************************************//**
   * @brief
   *   Configure the bias level for a specific segment line for Direct Segment Control.
   *
   * @note
   *   When DSC is active, each configuration takes up 4 bits in the Segment
   *   Registers (SEGD0L/SEGD1H) which defines the bias level.
   *   For optimal use of this feature, the entire SEGD-registers should be set
   *   at once in an optimized routine. Therefore, this function
   *   shows how to correctly configure the bias levels and should be used
   *   with care.
   *
   * @param[in] segmentLine
   *   A segment line number.
   *
   * @param[in] biasLevel
   *   The bias configuration level, 0-4. This value must be within the constraints
   *   defined by the LCD_DISPCTRL bias settings. For more information,
   *   see the applicable Reference Manual and data sheet.
   ******************************************************************************/
  void LCD_BiasSegmentSet(int segmentLine, int biasLevel)
  {
    int               biasRegister;
    int               bitShift;
    volatile uint32_t *segmentRegister;
  
  #if !defined(_LCD_SEGD0H_MASK)
    EFM_ASSERT(segmentLine < 20);
  
    /* A bias configuration for 8 segment lines per SEGDnL register. */
    biasRegister = segmentLine / 8;
    bitShift     = (segmentLine % 8) * 4;
  
    switch (biasRegister) {
      case 0:
        segmentRegister = &LCD->SEGD0L;
        break;
      case 1:
        segmentRegister = &LCD->SEGD1L;
        break;
      case 2:
        segmentRegister = &LCD->SEGD2L;
        break;
      case 3:
        segmentRegister = &LCD->SEGD3L;
        break;
      default:
        segmentRegister = (uint32_t *)0x00000000;
        EFM_ASSERT(0);
        break;
    }
  #else
    EFM_ASSERT(segmentLine < 40);
  
    /* A bias configuration for 10 segment lines per SEGDn L+H registers. */
    biasRegister = segmentLine / 10;
    bitShift     = (segmentLine % 10) * 4;
  
    switch (biasRegister) {
      case 0:
        if (bitShift < 32) {
          segmentRegister = &LCD->SEGD0L;
        } else {
          segmentRegister = &LCD->SEGD0H;
          bitShift       -= 32;
        }
        break;
      case 1:
        if (bitShift < 32) {
          segmentRegister = &LCD->SEGD1L;
        } else {
          segmentRegister = &LCD->SEGD1H;
          bitShift       -= 32;
        }
        break;
      case 2:
        if (bitShift < 32) {
          segmentRegister = &LCD->SEGD2L;
        } else {
          segmentRegister = &LCD->SEGD1H;
          bitShift       -= 32;
        }
        break;
      case 3:
        if (bitShift < 32) {
          segmentRegister = &LCD->SEGD3L;
        } else {
          segmentRegister = &LCD->SEGD3H;
          bitShift       -= 32;
        }
        break;
      default:
        segmentRegister = (uint32_t *)0x00000000;
        EFM_ASSERT(0);
        break;
    }
  #endif
  
    /* Configure a new bias setting. */
    *segmentRegister = (*segmentRegister & ~(0xF << bitShift)) | (biasLevel << bitShift);
  }
  #endif
  
  #if defined(LCD_CTRL_DSC)
  /***************************************************************************//**
   * @brief
   *   Configure the bias level for a specific segment line.
   *
   * @note
   *   When DSC is active, each configuration takes up 4 bits in the Segment
   *   Registers (SEGD4L/SEGD4H) which defines bias level.
   *   For optimal use of this feature, the entire SEGD-registers should be set
   *   at once in a optimized routine. Therefore, this function
   *   shows how to correctly configure the bias levels and should be used
   *   with care.
   *
   * @param[in] comLine
   *   A COM line number, 0-7.
   *
   * @param[in] biasLevel
   *   The bias configuration level, 0-4. This value must be within the constraints
   *   defined by the LCD_DISPCTRL bias settings.
   *   For more information, see the appropriate Reference Manual and data sheet.
   ******************************************************************************/
  void LCD_BiasComSet(int comLine, int biasLevel)
  {
    int bitShift;
    EFM_ASSERT(comLine < 8);
  
    bitShift    = comLine * 4;
    LCD->SEGD4L = (LCD->SEGD4L & ~(0xF << bitShift)) | (biasLevel << bitShift);
  }
  #endif
  
  #if defined(_SILICON_LABS_32B_SERIES_1)
  /***************************************************************************//**
   * @brief
   *   Configure the mode for the LCD panel.
   *
   * @param[in] mode
   *   A mode.
   ******************************************************************************/
  void LCD_ModeSet(LCD_Mode_Typedef mode)
  {
    LCD->DISPCTRL = (LCD->DISPCTRL & ~_LCD_DISPCTRL_MODE_MASK) | mode;
  }
  
  /***************************************************************************//**
   * @brief
   *   Configure the charge redistribution cycles for the LCD panel.
   *
   * @param[in] chgrDst
   *   Charge redistribution cycles, range 0-4.
   ******************************************************************************/
  void LCD_ChargeRedistributionCyclesSet(uint8_t cycles)
  {
    EFM_ASSERT(cycles <= 4);
  
    LCD->DISPCTRL = (LCD->DISPCTRL & ~_LCD_DISPCTRL_CHGRDST_MASK)
                    | (cycles << _LCD_DISPCTRL_CHGRDST_SHIFT);
  }
  #endif
  
  /** @} (end addtogroup LCD) */
  /** @} (end addtogroup emlib) */
  
  #endif /* defined(LCD_COUNT) && (LCD_COUNT > 0) */
  