text: /*----------------------------------------------------------------------------/
    Lovyan GFX - Graphics library for embedded devices.
  
  Original Source:
   https://github.com/lovyan03/LovyanGFX/
  
  Licence:
   [FreeBSD](https://github.com/lovyan03/LovyanGFX/blob/master/license.txt)
  
  Author:
   [lovyan03](https://twitter.com/lovyan03)
  
  Contributors:
   [ciniml](https://github.com/ciniml)
   [mongonta0716](https://github.com/mongonta0716)
   [tobozo](https://github.com/tobozo)
  /----------------------------------------------------------------------------*/
  #if defined (__SAMD21__) || defined(__SAMD21G18A__) || defined(__SAMD21J18A__) || defined(__SAMD21E17A__) || defined(__SAMD21E18A__)
  
  #include "Bus_SPI.hpp"
  
  #include "../../misc/pixelcopy.hpp"
  
  namespace lgfx
  {
   inline namespace v1
   {
  //----------------------------------------------------------------------------
  
    uint32_t Bus_SPI::FreqToClockDiv(uint32_t freq)
    {
      uint32_t div = std::min<uint32_t>(255, _cfg.sercom_clkfreq / (1+(freq<<1)));
      return div;
    }
  
    void Bus_SPI::setFreqDiv(uint32_t div)
    {
      auto *spi = &_sercom->SPI;
      while (spi->SYNCBUSY.reg);
      spi->CTRLA.bit.ENABLE = 0;
      while (spi->SYNCBUSY.reg);
      spi->BAUD.reg = div;
      spi->CTRLA.bit.ENABLE = 1;
      _need_wait = false;
      while (spi->SYNCBUSY.reg);
    }
  
    void Bus_SPI::config(const config_t& config)
    {
      _cfg = config;
  
      _need_wait = false;
      _sercom = reinterpret_cast<Sercom*>(samd21::getSercomData(_cfg.sercom_index)->sercomPtr);
      _last_apb_freq = -1;
      _mask_reg_dc = 0;
      uint32_t port = 0;
      if (_cfg.pin_dc >= 0)
      {
        _mask_reg_dc = (1ul << (_cfg.pin_dc & (samd21::PIN_MASK)));
        port = _cfg.pin_dc >> samd21::PORT_SHIFT;
      }
  
      _gpio_reg_dc_h = &PORT->Group[port].OUTSET.reg;
      _gpio_reg_dc_l = &PORT->Group[port].OUTCLR.reg;
  
      _clkdiv_read  = FreqToClockDiv(_cfg.freq_read);
      _clkdiv_write = FreqToClockDiv(_cfg.freq_write);
    }
  
    bool Bus_SPI::init(void)
    {
      dc_control(true);
      pinMode(_cfg.pin_dc, pin_mode_t::output);
  
      if (lgfx::spi::init(_cfg.sercom_index, _cfg.pin_sclk, _cfg.pin_miso, _cfg.pin_mosi).has_error())
      {
        return false;
      }
  
      return true;
    }
  
    void Bus_SPI::release(void)
    {
      lgfx::spi::release(_cfg.sercom_index);
    }
  
    void Bus_SPI::beginTransaction(void)
    {
      spi::beginTransaction(_cfg.sercom_index, _cfg.freq_write, _cfg.spi_mode);
      _need_wait = false;
    }
  
    void Bus_SPI::endTransaction(void)
    {
      dc_control(true);
      spi::endTransaction(_cfg.sercom_index);
    }
  
    void Bus_SPI::beginRead(void)
    {
      wait_spi();
      _need_wait = false;
      spi::endTransaction(_cfg.sercom_index);
      spi::beginTransaction(_cfg.sercom_index, _cfg.freq_read, _cfg.spi_mode);
    }
  
    void Bus_SPI::endRead(void)
    {
      spi::endTransaction(_cfg.sercom_index);
      spi::beginTransaction(_cfg.sercom_index, _cfg.freq_write, _cfg.spi_mode);
    }
  
    void Bus_SPI::wait(void)
    {
      wait_spi();
    }
  
    bool Bus_SPI::busy(void) const
    {
      return _need_wait && (_sercom->SPI.INTFLAG.bit.TXC == 0);
    }
  
    bool Bus_SPI::writeCommand(uint32_t data, uint_fast8_t bit_length)
    {
      bit_length >>= 3;
      auto *spi = &_sercom->SPI;
  
      auto gpio_reg_dc = _gpio_reg_dc_l;
      auto mask_reg_dc = _mask_reg_dc;
      wait_spi();
      *gpio_reg_dc = mask_reg_dc;
      spi->DATA.reg = data;
      _need_wait = true;
      while (--bit_length)
      {
        data >>= 8;
        while (spi->INTFLAG.bit.DRE == 0);
        spi->DATA.reg = data;
      }
      return true;
    }
  
    void Bus_SPI::writeData(uint32_t data, uint_fast8_t bit_length)
    {
      bit_length >>= 3;
      auto *spi = &_sercom->SPI;
  
      auto gpio_reg_dc = _gpio_reg_dc_h;
      auto mask_reg_dc = _mask_reg_dc;
      wait_spi();
      *gpio_reg_dc = mask_reg_dc;
      spi->DATA.reg = data;
      _need_wait = true;
      while (--bit_length)
      {
        data >>= 8;
        while (spi->INTFLAG.bit.DRE == 0);
        spi->DATA.reg = data;
      }
    }
  
    void Bus_SPI::writeDataRepeat(uint32_t data, uint_fast8_t bit_length, uint32_t length)
    {
      size_t bits = bit_length & ~7;
      auto *spi = &_sercom->SPI;
      dc_control(true);
      _need_wait = true;
      size_t i = 0;
      for (;;)
      {
        size_t tmp = data >> i;
        i += 8;
        while (spi->INTFLAG.bit.DRE == 0);
        spi->DATA.reg = tmp;
        if (i != bits) continue;
        i = 0;
        if (0 == --length) return;
      }
    }
  
    void Bus_SPI::writePixels(pixelcopy_t* param, uint32_t length)
    {
      const uint8_t bytes = param->dst_bits >> 3;
      auto *spi = &_sercom->SPI;
      uint32_t data;
      param->fp_copy((uint8_t*)&data, 0, 1, param);
      dc_control(true);
      spi->DATA.reg = data;
      _need_wait = true;
      uint32_t i = 0;
      while (++i != bytes)
      {
        while (spi->INTFLAG.bit.DRE == 0);
        spi->DATA.reg = data >> (i << 3);
      }
      if (--length)
      {
        do
        {
          param->fp_copy((uint8_t*)&data, 0, 1, param);
          i = 0;
          do
          {
            uint32_t tmp = data >> (i << 3);
            while (spi->INTFLAG.bit.DRE == 0);
            spi->DATA.reg = tmp;
          } while (++i != bytes);
        } while (--length);
      }
    }
  
    void Bus_SPI::writeBytes(const uint8_t* data, uint32_t length, bool dc, bool use_dma)
    {
      auto *spi = &_sercom->SPI;
      dc_control(dc);
      spi->DATA.reg = *data;
      _need_wait = true;
      while (--length)
      {
        uint32_t tmp = *++data;
        while (spi->INTFLAG.bit.DRE == 0);
        spi->DATA.reg = tmp;
      }
    }
  
    uint32_t Bus_SPI::readData(uint_fast8_t bit_length)
    {
      auto *spi = &_sercom->SPI;
      uint32_t res = 0;
      bit_length >>= 3;
      if (!bit_length) return res;
      int idx = 0;
      do
      {
        spi->DATA.reg = 0;
        while (spi->INTFLAG.bit.RXC == 0);
        res |= (spi->DATA.reg & 0xFF) << idx;
        idx += 8;
      } while (--bit_length);
      return res;
    }
  
    bool Bus_SPI::readBytes(uint8_t* dst, uint32_t length, bool use_dma)
    {
      auto *spi = &_sercom->SPI;
      spi->DATA.reg = 0;
      while (--length)
      {
        while (spi->INTFLAG.bit.RXC == 0);
        uint_fast8_t tmp = spi->DATA.reg;
        spi->DATA.reg = 0;
        *dst++ = tmp;
      }
      while (spi->INTFLAG.bit.RXC == 0);
      dst[0] = spi->DATA.reg;
      return true;
    }
  
    void Bus_SPI::readPixels(void* dst, pixelcopy_t* param, uint32_t length)
    {
      uint32_t bytes = param->src_bits >> 3;
      uint32_t dstindex = 0;
      uint32_t len = 4;
      uint8_t buf[24];
      param->src_data = buf;
      do {
        if (len > length) len = length;
        readBytes((uint8_t*)buf, len * bytes, true);
        param->src_x = 0;
        dstindex = param->fp_copy(dst, dstindex, dstindex + len, param);
        length -= len;
      } while (length);
    }
  
  //----------------------------------------------------------------------------
   }
  }
  
  #endif
  