//
// Copyright 2018 Ettus Research, a National Instruments Company
//
// SPDX-License-Identifier: GPL-3.0-or-later
//

#include "e31x_radio_control_impl.hpp"
#include "e31x_regs.hpp"
#include <uhd/rfnoc/registry.hpp>

using namespace uhd;
using namespace uhd::usrp;
using namespace uhd::rfnoc;

e31x_radio_control_impl::e31x_radio_control_impl(make_args_ptr make_args)
    : e3xx_radio_control_impl(std::move(make_args))
{
    // Swap front ends for E310
    _fe_swap = true;
    _init_mpm();
}

e31x_radio_control_impl::~e31x_radio_control_impl()
{
    RFNOC_LOG_TRACE("e31x_radio_control_impl::dtor()");
}

/******************************************************************************
 * API Calls
 *****************************************************************************/
uint32_t e31x_radio_control_impl::get_tx_switches(const size_t chan, const double freq)
{
    RFNOC_LOG_TRACE("Update all TX freq related switches. f=" << freq << " Hz, ");

    size_t fe_chan = _fe_swap ? (chan ? 0 : 1) : chan;

    auto tx_sw1    = TX_SW1_LB_2750; // SW1 = 0
    auto vctxrx_sw = VCTXRX_SW_OFF;
    auto tx_bias   = (fe_chan == 0) ? TX1_BIAS_LB_ON : TX2_BIAS_LB_ON;

    const auto band = e3xx_radio_control_impl::map_freq_to_tx_band(freq);
    switch (band) {
        case tx_band::LB_80:
            tx_sw1    = TX_SW1_LB_80;
            vctxrx_sw = (fe_chan == 0) ? VCTXRX1_SW_TX_LB : VCTXRX2_SW_TX_LB;
            break;
        case tx_band::LB_160:
            tx_sw1    = TX_SW1_LB_160;
            vctxrx_sw = (fe_chan == 0) ? VCTXRX1_SW_TX_LB : VCTXRX2_SW_TX_LB;
            break;
        case tx_band::LB_225:
            tx_sw1    = TX_SW1_LB_225;
            vctxrx_sw = (fe_chan == 0) ? VCTXRX1_SW_TX_LB : VCTXRX2_SW_TX_LB;
            break;
        case tx_band::LB_400:
            tx_sw1    = TX_SW1_LB_400;
            vctxrx_sw = (fe_chan == 0) ? VCTXRX1_SW_TX_LB : VCTXRX2_SW_TX_LB;
            break;
        case tx_band::LB_575:
            tx_sw1    = TX_SW1_LB_575;
            vctxrx_sw = (fe_chan == 0) ? VCTXRX1_SW_TX_LB : VCTXRX2_SW_TX_LB;
            break;
        case tx_band::LB_1000:
            tx_sw1    = TX_SW1_LB_1000;
            vctxrx_sw = (fe_chan == 0) ? VCTXRX1_SW_TX_LB : VCTXRX2_SW_TX_LB;
            break;
        case tx_band::LB_1700:
            tx_sw1    = TX_SW1_LB_1700;
            vctxrx_sw = (fe_chan == 0) ? VCTXRX1_SW_TX_LB : VCTXRX2_SW_TX_LB;
            break;
        case tx_band::LB_2750:
            tx_sw1    = TX_SW1_LB_2750;
            vctxrx_sw = (fe_chan == 0) ? VCTXRX1_SW_TX_LB : VCTXRX2_SW_TX_LB;
            break;
        case tx_band::HB:
            tx_sw1    = TX_SW1_LB_80;
            vctxrx_sw = VCTXRX_SW_TX_HB;
            tx_bias   = (fe_chan == 0) ? TX1_BIAS_HB_ON : TX2_BIAS_HB_ON;
            break;
        case tx_band::INVALID_BAND:
            RFNOC_LOG_ERROR("Cannot map TX frequency to band: " << freq);
            UHD_THROW_INVALID_CODE_PATH();
            break;
    }

    RFNOC_LOG_TRACE("TX band = " << int(band) << "TX SW1 = " << tx_sw1
                                 << "TX VCTXRX_SW = " << vctxrx_sw
                                 << "TX_BIAS = " << tx_bias);

    auto tx_regs = 0 | vctxrx_sw << VCTXRX_SW_SHIFT | tx_bias << TX_BIAS_SHIFT
                   | tx_sw1 << TX_SW1_SHIFT;
    return tx_regs;
}

uint32_t e31x_radio_control_impl::get_rx_switches(
    const size_t chan, const double freq, const std::string& ant)
{
    RFNOC_LOG_TRACE("Update all E310 RX freq related switches. f=" << freq << " Hz, ");

    size_t fe_chan = _fe_swap ? (chan ? 0 : 1) : chan;

    // Default to OFF
    auto rx_sw1    = RX_SW1_OFF;
    auto rx_swc    = RX_SWC_OFF;
    auto rx_swb    = RX_SWB_OFF;
    auto vctxrx_sw = VCTXRX_SW_OFF;
    auto vcrx_sw   = (ant == "TX/RX") ? VCRX_TXRX_SW_LB : VCRX_RX_SW_LB;
    if (ant == "TX/RX") {
        vctxrx_sw = (fe_chan == 0) ? VCTXRX1_SW_RX : VCTXRX2_SW_RX;
    }

    const auto band = e3xx_radio_control_impl::map_freq_to_rx_band(freq);

    switch (band) {
        case rx_band::LB_B2:
            rx_sw1 = (fe_chan == 0) ? RX1_SW1_LB_B2 : RX2_SW1_LB_B2;
            rx_swc = (fe_chan == 0) ? RX1_SWC_LB_B2 : RX2_SWC_LB_B2;
            rx_swb = RX_SWB_OFF;
            break;
        case rx_band::LB_B3:
            rx_sw1 = (fe_chan == 0) ? RX1_SW1_LB_B3 : RX2_SW1_LB_B3;
            rx_swc = (fe_chan == 0) ? RX1_SWC_LB_B3 : RX2_SWC_LB_B3;
            rx_swb = RX_SWB_OFF;
            break;
        case rx_band::LB_B4:
            rx_sw1 = (fe_chan == 0) ? RX1_SW1_LB_B4 : RX2_SW1_LB_B4;
            rx_swc = (fe_chan == 0) ? RX1_SWC_LB_B4 : RX2_SWC_LB_B4;
            rx_swb = RX_SWB_OFF;
            break;
        case rx_band::LB_B5:
            rx_sw1 = (fe_chan == 0) ? RX1_SW1_LB_B5 : RX2_SW1_LB_B5;
            rx_swc = RX_SWC_OFF;
            rx_swb = (fe_chan == 0) ? RX1_SWB_LB_B5 : RX2_SWB_LB_B5;
            break;
        case rx_band::LB_B6:
            rx_sw1 = (fe_chan == 0) ? RX1_SW1_LB_B6 : RX2_SW1_LB_B6;
            rx_swc = RX_SWC_OFF;
            rx_swb = (fe_chan == 0) ? RX1_SWB_LB_B6 : RX2_SWB_LB_B6;
            break;
        case rx_band::LB_B7:
            rx_sw1 = (fe_chan == 0) ? RX1_SW1_LB_B7 : RX2_SW1_LB_B7;
            rx_swc = RX_SWC_OFF;
            rx_swb = (fe_chan == 0) ? RX1_SWB_LB_B7 : RX2_SWB_LB_B7;
            break;
        case rx_band::HB:
            rx_sw1  = RX_SW1_OFF;
            rx_swc  = RX_SWC_OFF;
            rx_swb  = RX_SWB_OFF;
            vcrx_sw = (ant == "TX/RX") ? VCRX_TXRX_SW_HB : VCRX_RX_SW_HB;
            break;
        case rx_band::INVALID_BAND:
            RFNOC_LOG_ERROR("Cannot map RX frequency to band: " << freq);
            UHD_THROW_INVALID_CODE_PATH();
            break;
    }
    RFNOC_LOG_TRACE("RX SW1=" << rx_sw1 << " RX SWC=" << rx_swc << " RX SWB=" << rx_swb
                              << " RX VCRX_SW=" << vcrx_sw
                              << " RX VCTXRX_SW=" << vctxrx_sw);

    auto rx_regs = 0 | vcrx_sw << VCRX_SW_SHIFT | vctxrx_sw << VCTXRX_SW_SHIFT
                   | rx_swc << RX_SWC_SHIFT | rx_swb << RX_SWB_SHIFT
                   | rx_sw1 << RX_SW1_SHIFT;
    return rx_regs;
}

uint32_t e31x_radio_control_impl::get_idle_switches()
{
    uint32_t idle_regs = VCRX_SW_OFF << VCRX_SW_SHIFT | VCTXRX_SW_OFF << VCTXRX_SW_SHIFT
                         | TX_BIAS_OFF << TX_BIAS_SHIFT | RX_SWC_OFF << RX_SWC_SHIFT
                         | RX_SWB_OFF << RX_SWB_SHIFT | RX_SW1_OFF << RX_SW1_SHIFT
                         | TX_SW1_LB_2750 << TX_SW1_SHIFT;
    return idle_regs;
}

uint32_t e31x_radio_control_impl::get_idle_led()
{
    return 0;
}

uint32_t e31x_radio_control_impl::get_rx_led()
{
    return 1 << LED_RX_RX_SHIFT;
}

uint32_t e31x_radio_control_impl::get_tx_led()
{
    return 1 << LED_TXRX_TX_SHIFT;
}

uint32_t e31x_radio_control_impl::get_txrx_led()
{
    return 1 << LED_TXRX_RX_SHIFT;
}

UHD_RFNOC_BLOCK_REGISTER_FOR_DEVICE_DIRECT(
    e31x_radio_control, RADIO_BLOCK, E310, "Radio", true, "radio_clk", "bus_clk")