Post Go back to editing

Switching RX / TX channels in 1Rx1Tx mode

Category: Software
Software Version: 2023_r2

I'm working with AntSDR E200 board, which is basically ADALM-PLUTO clone with VCTXCO and AD9361 (https://github.com/MicroPhase/antsdr-fw/blob/master/schematic/ANT-E200_Public.pdf); I'm implementing some changes in HDL, I need 61.44Msps and since AD9361 is connected in CMOS mode, I have to stay with 1Rx1Tx to ensure correct sample rate.

At the same time I want to be able to switch input to other channel, so I can use two different RF input paths / antennas. I reviewed driver code and I noticed there is devicetree setting to use channel 2 in 1Rx1Tx mode - adi,1rx-1tx-mode-use-rx-num , however this setting is static and channel is set up when driver is initialized. I was able to develop a patch, which allows me to dynamically change the channel with IIO attribute. I had also to:
- call ad9361_tracking_control after channel switch is performed
- modify part of code, which does AGC setup

I performed some tests with RX and it looks everything works just fine - I didn't take any measurements though.

Can you take a look at my patch (attached)? Is there anything else I should add to ensure the channel switch works as expected?

diff --git a/drivers/iio/adc/ad9361.c b/drivers/iio/adc/ad9361.c
index a752a6453212..34e2b231b61b 100644
--- a/drivers/iio/adc/ad9361.c
+++ b/drivers/iio/adc/ad9361.c
@@ -5034,7 +5034,7 @@ static int ad9361_setup(struct ad9361_rf_phy *phy)
 			clamp_t(u32, pd->rx1tx1_mode_use_rx_num, RX_1, RX_2);
 
 		ad9361_en_dis_tx(phy, TX_1 | TX_2, pd->rx1tx1_mode_use_tx_num);
-		ad9361_en_dis_rx(phy, TX_1 | TX_2, pd->rx1tx1_mode_use_rx_num);
+		ad9361_en_dis_rx(phy, RX_1 | RX_2, pd->rx1tx1_mode_use_rx_num);
 	} else {
 		ad9361_en_dis_tx(phy, TX_1 | TX_2, TX_1 | TX_2);
 		ad9361_en_dis_rx(phy, RX_1 | RX_2, RX_1 | RX_2);
@@ -7401,6 +7401,7 @@ enum lo_ext_info {
 	LOEXT_SAVE,
 	LOEXT_EXTERNAL,
 	LOEXT_PD,
+	LOEXT_RXTX_NUM,
 };
 
 static ssize_t ad9361_phy_lo_write(struct iio_dev *indio_dev,
@@ -7410,6 +7411,8 @@ static ssize_t ad9361_phy_lo_write(struct iio_dev *indio_dev,
 {
 	struct ad9361_rf_phy *phy = iio_priv(indio_dev);
 	struct ad9361_rf_phy_state *st = phy->state;
+	struct ad9361_phy_platform_data *pd = phy->pdata;
+	u32 rxtx_num;
 	u64 readin;
 	bool res;
 	int ret = 0;
@@ -7535,6 +7538,38 @@ static ssize_t ad9361_phy_lo_write(struct iio_dev *indio_dev,
 		}
 
 		break;
+	case LOEXT_RXTX_NUM:
+		if (pd->rx2tx2) {
+			ret = -EINVAL;
+			break;
+		}
+		switch (chan->channel) {
+		case 0:
+			rxtx_num = clamp_t(u32, readin, RX_1, RX_2);
+			ret = ad9361_en_dis_rx(phy, RX_1 | RX_2, rxtx_num);
+			if (ret < 0)
+				break;
+
+			pd->rx1tx1_mode_use_rx_num = rxtx_num;
+			break;
+		case 1:
+			rxtx_num = clamp_t(u32, readin, TX_1, TX_2);
+			ret = ad9361_en_dis_tx(phy, TX_1 | TX_2, rxtx_num);
+			if (ret < 0)
+				break;
+
+			pd->rx1tx1_mode_use_tx_num = rxtx_num;
+            break;
+		default:
+			ret = -EINVAL;
+		}
+		if (ret < 0)
+			break;
+
+		ret = ad9361_tracking_control(phy, st->bbdc_track_en,
+                         st->rfdc_track_en,
+                         st->quad_track_en);
+		break;
 	}
 	mutex_unlock(&phy->lock);
 
@@ -7548,6 +7583,7 @@ static ssize_t ad9361_phy_lo_read(struct iio_dev *indio_dev,
 {
 	struct ad9361_rf_phy *phy = iio_priv(indio_dev);
 	struct ad9361_rf_phy_state *st = phy->state;
+	struct ad9361_phy_platform_data *pd = phy->pdata;
 	u64 val = 0;
 	size_t len;
 	int ret = 0;
@@ -7612,6 +7648,18 @@ static ssize_t ad9361_phy_lo_read(struct iio_dev *indio_dev,
 		mutex_unlock(&phy->lock);
 		return len;
 		}
+	case LOEXT_RXTX_NUM:
+		switch (chan->channel) {
+		case 0:
+			val = pd->rx1tx1_mode_use_rx_num;
+			break;
+		case 1:
+			val = pd->rx1tx1_mode_use_tx_num;
+			break;
+		default:
+			ret = -EINVAL;
+		}
+		break;
 	default:
 		ret = 0;
 
@@ -7647,6 +7695,7 @@ static const struct iio_chan_spec_ext_info ad9361_phy_ext_info[] = {
 	_AD9361_EXT_LO_INFO("fastlock_save", LOEXT_SAVE),
 	_AD9361_EXT_LO_INFO("external", LOEXT_EXTERNAL),
 	_AD9361_EXT_LO_INFO("powerdown", LOEXT_PD),
+	_AD9361_EXT_LO_INFO("rxtx_num", LOEXT_RXTX_NUM),
 	{ },
 };
 
@@ -7656,12 +7705,14 @@ static int ad9361_set_agc_mode(struct iio_dev *indio_dev,
 	struct ad9361_rf_phy *phy = iio_priv(indio_dev);
 	struct ad9361_rf_phy_state *st = phy->state;
 	struct rf_gain_ctrl gc = {0};
+	int ant;
 
-	if (st->agc_mode[chan->channel] == mode)
+	ant = ad9361_1rx1tx_channel_map(phy, false, chan->channel + 1);
+	if (st->agc_mode[ant - 1] == mode)
 		return 0;
 
-	gc.ant = ad9361_1rx1tx_channel_map(phy, false, chan->channel + 1);
-	gc.mode = st->agc_mode[chan->channel] = mode;
+	gc.ant = ant;
+	gc.mode = st->agc_mode[ant - 1] = mode;
 
 	return ad9361_set_gain_ctrl_mode(phy, &gc);
 }
@@ -7671,8 +7722,10 @@ static int ad9361_get_agc_mode(struct iio_dev *indio_dev,
 {
 	struct ad9361_rf_phy *phy = iio_priv(indio_dev);
 	struct ad9361_rf_phy_state *st = phy->state;
+	int ant;
 
-	return st->agc_mode[chan->channel];
+	ant = ad9361_1rx1tx_channel_map(phy, false, chan->channel + 1);
+	return st->agc_mode[ant - 1];
 }
 
 static const char * const ad9361_agc_modes[] =

  • ADI North America will be on winter shutdown starting December 24, 2024; perhaps another community member can assist you until our return on January 2, 2025.
  • I performed a bit more testing and I've noticed there is one change missing in the patch. Final version attached.

    diff --git a/drivers/iio/adc/ad9361.c b/drivers/iio/adc/ad9361.c
    index a752a6453212..21d896164a0a 100644
    --- a/drivers/iio/adc/ad9361.c
    +++ b/drivers/iio/adc/ad9361.c
    @@ -5034,7 +5034,7 @@ static int ad9361_setup(struct ad9361_rf_phy *phy)
     			clamp_t(u32, pd->rx1tx1_mode_use_rx_num, RX_1, RX_2);
     
     		ad9361_en_dis_tx(phy, TX_1 | TX_2, pd->rx1tx1_mode_use_tx_num);
    -		ad9361_en_dis_rx(phy, TX_1 | TX_2, pd->rx1tx1_mode_use_rx_num);
    +		ad9361_en_dis_rx(phy, RX_1 | RX_2, pd->rx1tx1_mode_use_rx_num);
     	} else {
     		ad9361_en_dis_tx(phy, TX_1 | TX_2, TX_1 | TX_2);
     		ad9361_en_dis_rx(phy, RX_1 | RX_2, RX_1 | RX_2);
    @@ -7401,6 +7401,7 @@ enum lo_ext_info {
     	LOEXT_SAVE,
     	LOEXT_EXTERNAL,
     	LOEXT_PD,
    +	LOEXT_RXTX_NUM,
     };
     
     static ssize_t ad9361_phy_lo_write(struct iio_dev *indio_dev,
    @@ -7410,6 +7411,8 @@ static ssize_t ad9361_phy_lo_write(struct iio_dev *indio_dev,
     {
     	struct ad9361_rf_phy *phy = iio_priv(indio_dev);
     	struct ad9361_rf_phy_state *st = phy->state;
    +	struct ad9361_phy_platform_data *pd = phy->pdata;
    +	u32 rxtx_num;
     	u64 readin;
     	bool res;
     	int ret = 0;
    @@ -7426,6 +7429,7 @@ static ssize_t ad9361_phy_lo_write(struct iio_dev *indio_dev,
     		case LOEXT_STORE:
     		case LOEXT_RECALL:
     		case LOEXT_SAVE:
    +		case LOEXT_RXTX_NUM:
     			ret = kstrtoull(buf, 10, &readin);
     			if (ret)
     				return ret;
    @@ -7535,6 +7539,38 @@ static ssize_t ad9361_phy_lo_write(struct iio_dev *indio_dev,
     		}
     
     		break;
    +	case LOEXT_RXTX_NUM:
    +		if (pd->rx2tx2) {
    +			ret = -EINVAL;
    +			break;
    +		}
    +		switch (chan->channel) {
    +		case 0:
    +			rxtx_num = clamp_t(u32, readin, RX_1, RX_2);
    +			ret = ad9361_en_dis_rx(phy, RX_1 | RX_2, rxtx_num);
    +			if (ret < 0)
    +				break;
    +
    +			pd->rx1tx1_mode_use_rx_num = rxtx_num;
    +			break;
    +		case 1:
    +			rxtx_num = clamp_t(u32, readin, TX_1, TX_2);
    +			ret = ad9361_en_dis_tx(phy, TX_1 | TX_2, rxtx_num);
    +			if (ret < 0)
    +				break;
    +
    +			pd->rx1tx1_mode_use_tx_num = rxtx_num;
    +			break;
    +		default:
    +			ret = -EINVAL;
    +		}
    +		if (ret < 0)
    +			break;
    +
    +		ret = ad9361_tracking_control(phy, st->bbdc_track_en,
    +                         st->rfdc_track_en,
    +                         st->quad_track_en);
    +		break;
     	}
     	mutex_unlock(&phy->lock);
     
    @@ -7548,6 +7584,7 @@ static ssize_t ad9361_phy_lo_read(struct iio_dev *indio_dev,
     {
     	struct ad9361_rf_phy *phy = iio_priv(indio_dev);
     	struct ad9361_rf_phy_state *st = phy->state;
    +	struct ad9361_phy_platform_data *pd = phy->pdata;
     	u64 val = 0;
     	size_t len;
     	int ret = 0;
    @@ -7612,6 +7649,18 @@ static ssize_t ad9361_phy_lo_read(struct iio_dev *indio_dev,
     		mutex_unlock(&phy->lock);
     		return len;
     		}
    +	case LOEXT_RXTX_NUM:
    +		switch (chan->channel) {
    +		case 0:
    +			val = pd->rx1tx1_mode_use_rx_num;
    +			break;
    +		case 1:
    +			val = pd->rx1tx1_mode_use_tx_num;
    +			break;
    +		default:
    +			ret = -EINVAL;
    +		}
    +		break;
     	default:
     		ret = 0;
     
    @@ -7647,6 +7696,7 @@ static const struct iio_chan_spec_ext_info ad9361_phy_ext_info[] = {
     	_AD9361_EXT_LO_INFO("fastlock_save", LOEXT_SAVE),
     	_AD9361_EXT_LO_INFO("external", LOEXT_EXTERNAL),
     	_AD9361_EXT_LO_INFO("powerdown", LOEXT_PD),
    +	_AD9361_EXT_LO_INFO("rxtx_num", LOEXT_RXTX_NUM),
     	{ },
     };
     
    @@ -7656,12 +7706,14 @@ static int ad9361_set_agc_mode(struct iio_dev *indio_dev,
     	struct ad9361_rf_phy *phy = iio_priv(indio_dev);
     	struct ad9361_rf_phy_state *st = phy->state;
     	struct rf_gain_ctrl gc = {0};
    +	int ant;
     
    -	if (st->agc_mode[chan->channel] == mode)
    +	ant = ad9361_1rx1tx_channel_map(phy, false, chan->channel + 1);
    +	if (st->agc_mode[ant - 1] == mode)
     		return 0;
     
    -	gc.ant = ad9361_1rx1tx_channel_map(phy, false, chan->channel + 1);
    -	gc.mode = st->agc_mode[chan->channel] = mode;
    +	gc.ant = ant;
    +	gc.mode = st->agc_mode[ant - 1] = mode;
     
     	return ad9361_set_gain_ctrl_mode(phy, &gc);
     }
    @@ -7671,8 +7723,10 @@ static int ad9361_get_agc_mode(struct iio_dev *indio_dev,
     {
     	struct ad9361_rf_phy *phy = iio_priv(indio_dev);
     	struct ad9361_rf_phy_state *st = phy->state;
    +	int ant;
     
    -	return st->agc_mode[chan->channel];
    +	ant = ad9361_1rx1tx_channel_map(phy, false, chan->channel + 1);
    +	return st->agc_mode[ant - 1];
     }
     
     static const char * const ad9361_agc_modes[] =