text: /*
   * Copyright (c) 2017 comsuisse AG
   *
   * SPDX-License-Identifier: Apache-2.0
   */
  
  
  #include <zephyr.h>
  #include <ztest.h>
  #include <drivers/i2s.h>
  
  #define I2S_DEV_NAME_RX "I2S_0"
  #ifdef CONFIG_I2S_TEST_SEPARATE_DEVICES
  #define I2S_DEV_NAME_TX "I2S_1"
  #else
  #define I2S_DEV_NAME_TX "I2S_0"
  #endif
  
  #define NUM_BLOCKS 20
  #define SAMPLE_NO 64
  
  /* The data_l represent a sine wave */
  static int16_t data_l[SAMPLE_NO] = {
  	  3211,   6392,   9511,  12539,  15446,  18204,  20787,  23169,
  	 25329,  27244,  28897,  30272,  31356,  32137,  32609,  32767,
  	 32609,  32137,  31356,  30272,  28897,  27244,  25329,  23169,
  	 20787,  18204,  15446,  12539,   9511,   6392,   3211,      0,
  	 -3212,  -6393,  -9512, -12540, -15447, -18205, -20788, -23170,
  	-25330, -27245, -28898, -30273, -31357, -32138, -32610, -32767,
  	-32610, -32138, -31357, -30273, -28898, -27245, -25330, -23170,
  	-20788, -18205, -15447, -12540,  -9512,  -6393,  -3212,     -1,
  };
  
  /* The data_r represent a sine wave shifted by 90 deg to data_l sine wave */
  static int16_t data_r[SAMPLE_NO] = {
  	 32609,  32137,  31356,  30272,  28897,  27244,  25329,  23169,
  	 20787,  18204,  15446,  12539,   9511,   6392,   3211,      0,
  	 -3212,  -6393,  -9512, -12540, -15447, -18205, -20788, -23170,
  	-25330, -27245, -28898, -30273, -31357, -32138, -32610, -32767,
  	-32610, -32138, -31357, -30273, -28898, -27245, -25330, -23170,
  	-20788, -18205, -15447, -12540,  -9512,  -6393,  -3212,     -1,
  	  3211,   6392,   9511,  12539,  15446,  18204,  20787,  23169,
  	 25329,  27244,  28897,  30272,  31356,  32137,  32609,  32767,
  };
  
  #define BLOCK_SIZE (2 * sizeof(data_l))
  
  /*
   * NUM_BLOCKS is the number of blocks used by the test. Some of the drivers,
   * e.g. i2s_mcux_flexcomm, permanently keep ownership of a few RX buffers. Add a few more
   * RX blocks to satisfy this requirement
   */
  K_MEM_SLAB_DEFINE(rx_0_mem_slab, BLOCK_SIZE, NUM_BLOCKS + 2, 32);
  K_MEM_SLAB_DEFINE(tx_0_mem_slab, BLOCK_SIZE, NUM_BLOCKS, 32);
  
  static void fill_buf(int16_t *tx_block, int att)
  {
  	for (int i = 0; i < SAMPLE_NO; i++) {
  		tx_block[2 * i] = data_l[i] >> att;
  		tx_block[2 * i + 1] = data_r[i] >> att;
  	}
  }
  
  static int verify_buf(int16_t *rx_block, int att)
  {
  	for (int i = 0; i < SAMPLE_NO; i++) {
  		if (rx_block[2 * i] != data_l[i] >> att) {
  			TC_PRINT("Error: att %d: data_l mismatch at position "
  				 "%d, expected %d, actual %d\n",
  				 att, i, data_l[i] >> att, rx_block[2 * i]);
  			return -TC_FAIL;
  		}
  		if (rx_block[2 * i + 1] != data_r[i] >> att) {
  			TC_PRINT("Error: att %d: data_r mismatch at position "
  				 "%d, expected %d, actual %d\n",
  				 att, i, data_r[i] >> att, rx_block[2 * i + 1]);
  			return -TC_FAIL;
  		}
  	}
  
  	return TC_PASS;
  }
  
  #define TIMEOUT          2000
  #define FRAME_CLK_FREQ   44000
  
  /** Configure I2S TX transfer. */
  void test_i2s_tx_transfer_configure(void)
  {
  	const struct device *dev_i2s;
  	struct i2s_config i2s_cfg;
  	int ret;
  
  	dev_i2s = device_get_binding(I2S_DEV_NAME_TX);
  	zassert_not_null(dev_i2s, "device " I2S_DEV_NAME_TX " not found");
  
  	/* Configure */
  
  	i2s_cfg.word_size = 16U;
  	i2s_cfg.channels = 2U;
  	i2s_cfg.format = I2S_FMT_DATA_FORMAT_I2S;
  	/* Configure the Transmit port as Master */
  	i2s_cfg.options = I2S_OPT_FRAME_CLK_MASTER | I2S_OPT_BIT_CLK_MASTER;
  	i2s_cfg.frame_clk_freq = FRAME_CLK_FREQ;
  	i2s_cfg.block_size = BLOCK_SIZE;
  	i2s_cfg.mem_slab = &tx_0_mem_slab;
  	i2s_cfg.timeout = TIMEOUT;
  	i2s_cfg.options = I2S_OPT_LOOPBACK;
  
  	ret = i2s_configure(dev_i2s, I2S_DIR_TX, &i2s_cfg);
  	zassert_equal(ret, 0, "Failed to configure I2S TX stream");
  }
  
  /** Configure I2S RX transfer. */
  void test_i2s_rx_transfer_configure(void)
  {
  	const struct device *dev_i2s;
  	struct i2s_config i2s_cfg;
  	int ret;
  
  	dev_i2s = device_get_binding(I2S_DEV_NAME_RX);
  	zassert_not_null(dev_i2s, "device " I2S_DEV_NAME_RX " not found");
  
  	/* Configure */
  
  	i2s_cfg.word_size = 16U;
  	i2s_cfg.channels = 2U;
  	i2s_cfg.format = I2S_FMT_DATA_FORMAT_I2S;
  	/* Configure the Receive port as Slave */
  	i2s_cfg.options = I2S_OPT_FRAME_CLK_SLAVE | I2S_OPT_BIT_CLK_SLAVE;
  	i2s_cfg.frame_clk_freq = FRAME_CLK_FREQ;
  	i2s_cfg.block_size = BLOCK_SIZE;
  	i2s_cfg.mem_slab = &rx_0_mem_slab;
  	i2s_cfg.timeout = TIMEOUT;
  	i2s_cfg.options = I2S_OPT_LOOPBACK;
  
  	ret = i2s_configure(dev_i2s, I2S_DIR_RX, &i2s_cfg);
  	zassert_equal(ret, 0, "Failed to configure I2S RX stream");
  }
  
  /** @brief Short I2S transfer.
   *
   * - TX stream START trigger starts transmission.
   * - RX stream START trigger starts reception.
   * - sending / receiving a short sequence of data returns success.
   * - TX stream DRAIN trigger empties the transmit queue.
   * - RX stream STOP trigger stops reception.
   */
  void test_i2s_transfer_short(void)
  {
  	const struct device *dev_i2s_rx;
  	const struct device *dev_i2s_tx;
  	void *rx_block[3];
  	void *tx_block;
  	size_t rx_size;
  	int ret;
  
  	dev_i2s_rx = device_get_binding(I2S_DEV_NAME_RX);
  	zassert_not_null(dev_i2s_rx, "device " I2S_DEV_NAME_RX " not found");
  
  	dev_i2s_tx = device_get_binding(I2S_DEV_NAME_TX);
  	zassert_not_null(dev_i2s_tx, "device " I2S_DEV_NAME_TX " not found");
  
  	/* Prefill TX queue */
  	for (int i = 0; i < 3; i++) {
  		ret = k_mem_slab_alloc(&tx_0_mem_slab, &tx_block, K_FOREVER);
  		zassert_equal(ret, 0, NULL);
  		fill_buf((uint16_t *)tx_block, i);
  
  		ret = i2s_write(dev_i2s_tx, tx_block, BLOCK_SIZE);
  		zassert_equal(ret, 0, NULL);
  
  		TC_PRINT("%d->OK\n", i);
  	}
  
  	/* Start reception */
  	ret = i2s_trigger(dev_i2s_rx, I2S_DIR_RX, I2S_TRIGGER_START);
  	zassert_equal(ret, 0, "RX START trigger failed");
  
  	/* Start transmission */
  	ret = i2s_trigger(dev_i2s_tx, I2S_DIR_TX, I2S_TRIGGER_START);
  	zassert_equal(ret, 0, "TX START trigger failed");
  
  	/* All data written, drain TX queue and stop the transmission */
  	ret = i2s_trigger(dev_i2s_tx, I2S_DIR_TX, I2S_TRIGGER_DRAIN);
  	zassert_equal(ret, 0, "TX DRAIN trigger failed");
  
  	ret = i2s_read(dev_i2s_rx, &rx_block[0], &rx_size);
  	zassert_equal(ret, 0, NULL);
  	zassert_equal(rx_size, BLOCK_SIZE, NULL);
  
  	ret = i2s_read(dev_i2s_rx, &rx_block[1], &rx_size);
  	zassert_equal(ret, 0, NULL);
  	zassert_equal(rx_size, BLOCK_SIZE, NULL);
  
  	/* All but one data block read, stop reception */
  	ret = i2s_trigger(dev_i2s_rx, I2S_DIR_RX, I2S_TRIGGER_STOP);
  	zassert_equal(ret, 0, "RX STOP trigger failed");
  
  	ret = i2s_read(dev_i2s_rx, &rx_block[2], &rx_size);
  	zassert_equal(ret, 0, NULL);
  	zassert_equal(rx_size, BLOCK_SIZE, NULL);
  
  	/* Verify received data */
  	ret = verify_buf((uint16_t *)rx_block[0], 0);
  	zassert_equal(ret, 0, NULL);
  	k_mem_slab_free(&rx_0_mem_slab, &rx_block[0]);
  	TC_PRINT("%d<-OK\n", 1);
  
  	ret = verify_buf((uint16_t *)rx_block[1], 1);
  	zassert_equal(ret, 0, NULL);
  	k_mem_slab_free(&rx_0_mem_slab, &rx_block[1]);
  	TC_PRINT("%d<-OK\n", 2);
  
  	ret = verify_buf((uint16_t *)rx_block[2], 2);
  	zassert_equal(ret, 0, NULL);
  	k_mem_slab_free(&rx_0_mem_slab, &rx_block[2]);
  	TC_PRINT("%d<-OK\n", 3);
  }
  
  /** @brief Long I2S transfer.
   *
   * - TX stream START trigger starts transmission.
   * - RX stream START trigger starts reception.
   * - sending / receiving a long sequence of data returns success.
   * - TX stream DRAIN trigger empties the transmit queue.
   * - RX stream STOP trigger stops reception.
   */
  void test_i2s_transfer_long(void)
  {
  	const struct device *dev_i2s_rx;
  	const struct device *dev_i2s_tx;
  	void *rx_block[NUM_BLOCKS];
  	void *tx_block[NUM_BLOCKS];
  	size_t rx_size;
  	int tx_idx;
  	int rx_idx = 0;
  	int num_verified;
  	int ret;
  
  	dev_i2s_rx = device_get_binding(I2S_DEV_NAME_RX);
  	zassert_not_null(dev_i2s_rx, "device " I2S_DEV_NAME_RX " not found");
  
  	dev_i2s_tx = device_get_binding(I2S_DEV_NAME_TX);
  	zassert_not_null(dev_i2s_tx, "device " I2S_DEV_NAME_TX " not found");
  
  	/* Prepare TX data blocks */
  	for (tx_idx = 0; tx_idx < NUM_BLOCKS; tx_idx++) {
  		ret = k_mem_slab_alloc(&tx_0_mem_slab, &tx_block[tx_idx],
  				       K_FOREVER);
  		zassert_equal(ret, 0, NULL);
  		fill_buf((uint16_t *)tx_block[tx_idx], tx_idx % 3);
  	}
  
  	tx_idx = 0;
  
  	/* Prefill TX queue */
  	ret = i2s_write(dev_i2s_tx, tx_block[tx_idx++], BLOCK_SIZE);
  	zassert_equal(ret, 0, NULL);
  
  	ret = i2s_write(dev_i2s_tx, tx_block[tx_idx++], BLOCK_SIZE);
  	zassert_equal(ret, 0, NULL);
  
  	/* Start reception */
  	ret = i2s_trigger(dev_i2s_rx, I2S_DIR_RX, I2S_TRIGGER_START);
  	zassert_equal(ret, 0, "RX START trigger failed");
  
  	/* Start transmission */
  	ret = i2s_trigger(dev_i2s_tx, I2S_DIR_TX, I2S_TRIGGER_START);
  	zassert_equal(ret, 0, "TX START trigger failed");
  
  	for (; tx_idx < NUM_BLOCKS; ) {
  		ret = i2s_write(dev_i2s_tx, tx_block[tx_idx++], BLOCK_SIZE);
  		zassert_equal(ret, 0, NULL);
  
  		ret = i2s_read(dev_i2s_rx, &rx_block[rx_idx++], &rx_size);
  		zassert_equal(ret, 0, NULL);
  		zassert_equal(rx_size, BLOCK_SIZE, NULL);
  	}
  
  	/* All data written, flush TX queue and stop the transmission */
  	ret = i2s_trigger(dev_i2s_tx, I2S_DIR_TX, I2S_TRIGGER_DRAIN);
  	zassert_equal(ret, 0, "TX DRAIN trigger failed");
  
  	ret = i2s_read(dev_i2s_rx, &rx_block[rx_idx++], &rx_size);
  	zassert_equal(ret, 0, NULL);
  	zassert_equal(rx_size, BLOCK_SIZE, NULL);
  
  	/* All but one data block read, stop reception */
  	ret = i2s_trigger(dev_i2s_rx, I2S_DIR_RX, I2S_TRIGGER_STOP);
  	zassert_equal(ret, 0, "RX STOP trigger failed");
  
  	ret = i2s_read(dev_i2s_rx, &rx_block[rx_idx++], &rx_size);
  	zassert_equal(ret, 0, NULL);
  	zassert_equal(rx_size, BLOCK_SIZE, NULL);
  
  	TC_PRINT("%d TX blocks sent\n", tx_idx);
  	TC_PRINT("%d RX blocks received\n", rx_idx);
  
  	/* Verify received data */
  	num_verified = 0;
  	for (rx_idx = 0; rx_idx < NUM_BLOCKS; rx_idx++) {
  		ret = verify_buf((uint16_t *)rx_block[rx_idx], rx_idx % 3);
  		if (ret != 0) {
  			TC_PRINT("%d RX block invalid\n", rx_idx);
  		} else {
  			num_verified++;
  		}
  		k_mem_slab_free(&rx_0_mem_slab, &rx_block[rx_idx]);
  	}
  	zassert_equal(num_verified, NUM_BLOCKS, "Invalid RX blocks received");
  }
  