/* SPDX-License-Identifier: BSD-3-Clause
 * Copyright(C) 2019 Marvell International Ltd.
 */

#include <rte_vect.h>

#include "otx2_ethdev.h"

#define NIX_XMIT_FC_OR_RETURN(txq, pkts) do {				\
	/* Cached value is low, Update the fc_cache_pkts */		\
	if (unlikely((txq)->fc_cache_pkts < (pkts))) {			\
		/* Multiply with sqe_per_sqb to express in pkts */	\
		(txq)->fc_cache_pkts =					\
			((txq)->nb_sqb_bufs_adj - *(txq)->fc_mem) <<    \
				(txq)->sqes_per_sqb_log2;		\
		/* Check it again for the room */			\
		if (unlikely((txq)->fc_cache_pkts < (pkts)))		\
			return 0;					\
	}								\
} while (0)


static __rte_always_inline uint16_t
nix_xmit_pkts(void *tx_queue, struct rte_mbuf **tx_pkts,
	      uint16_t pkts, uint64_t *cmd, const uint16_t flags)
{
	struct otx2_eth_txq *txq = tx_queue; uint16_t i;
	const rte_iova_t io_addr = txq->io_addr;
	void *lmt_addr = txq->lmt_addr;
	uint64_t lso_tun_fmt;

	NIX_XMIT_FC_OR_RETURN(txq, pkts);

	otx2_lmt_mov(cmd, &txq->cmd[0], otx2_nix_tx_ext_subs(flags));

	/* Perform header writes before barrier for TSO */
	if (flags & NIX_TX_OFFLOAD_TSO_F) {
		lso_tun_fmt = txq->lso_tun_fmt;
		for (i = 0; i < pkts; i++)
			otx2_nix_xmit_prepare_tso(tx_pkts[i], flags);
	}

	/* Lets commit any changes in the packet here as no further changes
	 * to the packet will be done unless no fast free is enabled.
	 */
	if (!(flags & NIX_TX_OFFLOAD_MBUF_NOFF_F))
		rte_io_wmb();

	for (i = 0; i < pkts; i++) {
		otx2_nix_xmit_prepare(tx_pkts[i], cmd, flags, lso_tun_fmt);
		/* Passing no of segdw as 4: HDR + EXT + SG + SMEM */
		otx2_nix_xmit_prepare_tstamp(cmd, &txq->cmd[0],
					     tx_pkts[i]->ol_flags, 4, flags);
		otx2_nix_xmit_one(cmd, lmt_addr, io_addr, flags);
	}

	/* Reduce the cached count */
	txq->fc_cache_pkts -= pkts;

	return pkts;
}

static __rte_always_inline uint16_t
nix_xmit_pkts_mseg(void *tx_queue, struct rte_mbuf **tx_pkts,
		   uint16_t pkts, uint64_t *cmd, const uint16_t flags)
{
	struct otx2_eth_txq *txq = tx_queue; uint64_t i;
	const rte_iova_t io_addr = txq->io_addr;
	void *lmt_addr = txq->lmt_addr;
	uint64_t lso_tun_fmt;
	uint16_t segdw;

	NIX_XMIT_FC_OR_RETURN(txq, pkts);

	otx2_lmt_mov(cmd, &txq->cmd[0], otx2_nix_tx_ext_subs(flags));

	/* Perform header writes before barrier for TSO */
	if (flags & NIX_TX_OFFLOAD_TSO_F) {
		lso_tun_fmt = txq->lso_tun_fmt;
		for (i = 0; i < pkts; i++)
			otx2_nix_xmit_prepare_tso(tx_pkts[i], flags);
	}

	/* Lets commit any changes in the packet here as no further changes
	 * to the packet will be done unless no fast free is enabled.
	 */
	if (!(flags & NIX_TX_OFFLOAD_MBUF_NOFF_F))
		rte_io_wmb();

	for (i = 0; i < pkts; i++) {
		otx2_nix_xmit_prepare(tx_pkts[i], cmd, flags, lso_tun_fmt);
		segdw = otx2_nix_prepare_mseg(tx_pkts[i], cmd, flags);
		otx2_nix_xmit_prepare_tstamp(cmd, &txq->cmd[0],
					     tx_pkts[i]->ol_flags, segdw,
					     flags);
		otx2_nix_xmit_mseg_one(cmd, lmt_addr, io_addr, segdw);
	}

	/* Reduce the cached count */
	txq->fc_cache_pkts -= pkts;

	return pkts;
}

#if defined(RTE_ARCH_ARM64)

#define NIX_DESCS_PER_LOOP	4
static __rte_always_inline uint16_t
nix_xmit_pkts_vector(void *tx_queue, struct rte_mbuf **tx_pkts,
		     uint16_t pkts, uint64_t *cmd, const uint16_t flags)
{
	uint64x2_t dataoff_iova0, dataoff_iova1, dataoff_iova2, dataoff_iova3;
	uint64x2_t len_olflags0, len_olflags1, len_olflags2, len_olflags3;
	uint64_t *mbuf0, *mbuf1, *mbuf2, *mbuf3;
	uint64x2_t senddesc01_w0, senddesc23_w0;
	uint64x2_t senddesc01_w1, senddesc23_w1;
	uint64x2_t sgdesc01_w0, sgdesc23_w0;
	uint64x2_t sgdesc01_w1, sgdesc23_w1;
	struct otx2_eth_txq *txq = tx_queue;
	uint64_t *lmt_addr = txq->lmt_addr;
	rte_iova_t io_addr = txq->io_addr;
	uint64x2_t ltypes01, ltypes23;
	uint64x2_t xtmp128, ytmp128;
	uint64x2_t xmask01, xmask23;
	uint64x2_t cmd00, cmd01;
	uint64x2_t cmd10, cmd11;
	uint64x2_t cmd20, cmd21;
	uint64x2_t cmd30, cmd31;
	uint64_t lmt_status, i;
	uint16_t pkts_left;

	NIX_XMIT_FC_OR_RETURN(txq, pkts);

	pkts_left = pkts & (NIX_DESCS_PER_LOOP - 1);
	pkts = RTE_ALIGN_FLOOR(pkts, NIX_DESCS_PER_LOOP);

	/* Reduce the cached count */
	txq->fc_cache_pkts -= pkts;

	/* Lets commit any changes in the packet here as no further changes
	 * to the packet will be done unless no fast free is enabled.
	 */
	if (!(flags & NIX_TX_OFFLOAD_MBUF_NOFF_F))
		rte_io_wmb();

	senddesc01_w0 = vld1q_dup_u64(&txq->cmd[0]);
	senddesc23_w0 = senddesc01_w0;
	senddesc01_w1 = vdupq_n_u64(0);
	senddesc23_w1 = senddesc01_w1;
	sgdesc01_w0 = vld1q_dup_u64(&txq->cmd[2]);
	sgdesc23_w0 = sgdesc01_w0;

	for (i = 0; i < pkts; i += NIX_DESCS_PER_LOOP) {
		/* Clear lower 32bit of SEND_HDR_W0 and SEND_SG_W0 */
		senddesc01_w0 = vbicq_u64(senddesc01_w0,
					  vdupq_n_u64(0xFFFFFFFF));
		sgdesc01_w0 = vbicq_u64(sgdesc01_w0,
					vdupq_n_u64(0xFFFFFFFF));

		senddesc23_w0 = senddesc01_w0;
		sgdesc23_w0 = sgdesc01_w0;

		/* Move mbufs to iova */
		mbuf0 = (uint64_t *)tx_pkts[0];
		mbuf1 = (uint64_t *)tx_pkts[1];
		mbuf2 = (uint64_t *)tx_pkts[2];
		mbuf3 = (uint64_t *)tx_pkts[3];

		mbuf0 = (uint64_t *)((uintptr_t)mbuf0 +
				     offsetof(struct rte_mbuf, buf_iova));
		mbuf1 = (uint64_t *)((uintptr_t)mbuf1 +
				     offsetof(struct rte_mbuf, buf_iova));
		mbuf2 = (uint64_t *)((uintptr_t)mbuf2 +
				     offsetof(struct rte_mbuf, buf_iova));
		mbuf3 = (uint64_t *)((uintptr_t)mbuf3 +
				     offsetof(struct rte_mbuf, buf_iova));
		/*
		 * Get mbuf's, olflags, iova, pktlen, dataoff
		 * dataoff_iovaX.D[0] = iova,
		 * dataoff_iovaX.D[1](15:0) = mbuf->dataoff
		 * len_olflagsX.D[0] = ol_flags,
		 * len_olflagsX.D[1](63:32) = mbuf->pkt_len
		 */
		dataoff_iova0  = vld1q_u64(mbuf0);
		len_olflags0 = vld1q_u64(mbuf0 + 2);
		dataoff_iova1  = vld1q_u64(mbuf1);
		len_olflags1 = vld1q_u64(mbuf1 + 2);
		dataoff_iova2  = vld1q_u64(mbuf2);
		len_olflags2 = vld1q_u64(mbuf2 + 2);
		dataoff_iova3  = vld1q_u64(mbuf3);
		len_olflags3 = vld1q_u64(mbuf3 + 2);

		if (flags & NIX_TX_OFFLOAD_MBUF_NOFF_F) {
			struct rte_mbuf *mbuf;
			/* Set don't free bit if reference count > 1 */
			xmask01 = vdupq_n_u64(0);
			xmask23 = xmask01;

			mbuf = (struct rte_mbuf *)((uintptr_t)mbuf0 -
				offsetof(struct rte_mbuf, buf_iova));

			if (otx2_nix_prefree_seg(mbuf))
				vsetq_lane_u64(0x80000, xmask01, 0);
			else
				RTE_MEMPOOL_CHECK_COOKIES(mbuf->pool,
							(void **)&mbuf,
							1, 0);

			mbuf = (struct rte_mbuf *)((uintptr_t)mbuf1 -
				offsetof(struct rte_mbuf, buf_iova));
			if (otx2_nix_prefree_seg(mbuf))
				vsetq_lane_u64(0x80000, xmask01, 1);
			else
				RTE_MEMPOOL_CHECK_COOKIES(mbuf->pool,
							(void **)&mbuf,
							1, 0);

			mbuf = (struct rte_mbuf *)((uintptr_t)mbuf2 -
				offsetof(struct rte_mbuf, buf_iova));
			if (otx2_nix_prefree_seg(mbuf))
				vsetq_lane_u64(0x80000, xmask23, 0);
			else
				RTE_MEMPOOL_CHECK_COOKIES(mbuf->pool,
							(void **)&mbuf,
							1, 0);

			mbuf = (struct rte_mbuf *)((uintptr_t)mbuf3 -
				offsetof(struct rte_mbuf, buf_iova));
			if (otx2_nix_prefree_seg(mbuf))
				vsetq_lane_u64(0x80000, xmask23, 1);
			else
				RTE_MEMPOOL_CHECK_COOKIES(mbuf->pool,
							(void **)&mbuf,
							1, 0);
			senddesc01_w0 = vorrq_u64(senddesc01_w0, xmask01);
			senddesc23_w0 = vorrq_u64(senddesc23_w0, xmask23);
			/* Ensuring mbuf fields which got updated in
			 * otx2_nix_prefree_seg are written before LMTST.
			 */
			rte_io_wmb();
		} else {
			struct rte_mbuf *mbuf;
			/* Mark mempool object as "put" since
			 * it is freed by NIX
			 */
			mbuf = (struct rte_mbuf *)((uintptr_t)mbuf0 -
				offsetof(struct rte_mbuf, buf_iova));
			RTE_MEMPOOL_CHECK_COOKIES(mbuf->pool, (void **)&mbuf,
						1, 0);

			mbuf = (struct rte_mbuf *)((uintptr_t)mbuf1 -
				offsetof(struct rte_mbuf, buf_iova));
			RTE_MEMPOOL_CHECK_COOKIES(mbuf->pool, (void **)&mbuf,
						1, 0);

			mbuf = (struct rte_mbuf *)((uintptr_t)mbuf2 -
				offsetof(struct rte_mbuf, buf_iova));
			RTE_MEMPOOL_CHECK_COOKIES(mbuf->pool, (void **)&mbuf,
						1, 0);

			mbuf = (struct rte_mbuf *)((uintptr_t)mbuf3 -
				offsetof(struct rte_mbuf, buf_iova));
			RTE_MEMPOOL_CHECK_COOKIES(mbuf->pool, (void **)&mbuf,
						1, 0);
			RTE_SET_USED(mbuf);
		}

		/* Move mbufs to point pool */
		mbuf0 = (uint64_t *)((uintptr_t)mbuf0 +
			 offsetof(struct rte_mbuf, pool) -
			 offsetof(struct rte_mbuf, buf_iova));
		mbuf1 = (uint64_t *)((uintptr_t)mbuf1 +
			 offsetof(struct rte_mbuf, pool) -
			 offsetof(struct rte_mbuf, buf_iova));
		mbuf2 = (uint64_t *)((uintptr_t)mbuf2 +
			 offsetof(struct rte_mbuf, pool) -
			 offsetof(struct rte_mbuf, buf_iova));
		mbuf3 = (uint64_t *)((uintptr_t)mbuf3 +
			 offsetof(struct rte_mbuf, pool) -
			 offsetof(struct rte_mbuf, buf_iova));

		if (flags &
		    (NIX_TX_OFFLOAD_OL3_OL4_CSUM_F |
		     NIX_TX_OFFLOAD_L3_L4_CSUM_F)) {
			/* Get tx_offload for ol2, ol3, l2, l3 lengths */
			/*
			 * E(8):OL2_LEN(7):OL3_LEN(9):E(24):L3_LEN(9):L2_LEN(7)
			 * E(8):OL2_LEN(7):OL3_LEN(9):E(24):L3_LEN(9):L2_LEN(7)
			 */

			asm volatile ("LD1 {%[a].D}[0],[%[in]]\n\t" :
				      [a]"+w"(senddesc01_w1) :
				      [in]"r"(mbuf0 + 2) : "memory");

			asm volatile ("LD1 {%[a].D}[1],[%[in]]\n\t" :
				      [a]"+w"(senddesc01_w1) :
				      [in]"r"(mbuf1 + 2) : "memory");

			asm volatile ("LD1 {%[b].D}[0],[%[in]]\n\t" :
				      [b]"+w"(senddesc23_w1) :
				      [in]"r"(mbuf2 + 2) : "memory");

			asm volatile ("LD1 {%[b].D}[1],[%[in]]\n\t" :
				      [b]"+w"(senddesc23_w1) :
				      [in]"r"(mbuf3 + 2) : "memory");

			/* Get pool pointer alone */
			mbuf0 = (uint64_t *)*mbuf0;
			mbuf1 = (uint64_t *)*mbuf1;
			mbuf2 = (uint64_t *)*mbuf2;
			mbuf3 = (uint64_t *)*mbuf3;
		} else {
			/* Get pool pointer alone */
			mbuf0 = (uint64_t *)*mbuf0;
			mbuf1 = (uint64_t *)*mbuf1;
			mbuf2 = (uint64_t *)*mbuf2;
			mbuf3 = (uint64_t *)*mbuf3;
		}

		const uint8x16_t shuf_mask2 = {
			0x4, 0x5, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
			0xc, 0xd, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
		};
		xtmp128 = vzip2q_u64(len_olflags0, len_olflags1);
		ytmp128 = vzip2q_u64(len_olflags2, len_olflags3);

		/* Clear dataoff_iovaX.D[1] bits other than dataoff(15:0) */
		const uint64x2_t and_mask0 = {
			0xFFFFFFFFFFFFFFFF,
			0x000000000000FFFF,
		};

		dataoff_iova0 = vandq_u64(dataoff_iova0, and_mask0);
		dataoff_iova1 = vandq_u64(dataoff_iova1, and_mask0);
		dataoff_iova2 = vandq_u64(dataoff_iova2, and_mask0);
		dataoff_iova3 = vandq_u64(dataoff_iova3, and_mask0);

		/*
		 * Pick only 16 bits of pktlen preset at bits 63:32
		 * and place them at bits 15:0.
		 */
		xtmp128 = vqtbl1q_u8(xtmp128, shuf_mask2);
		ytmp128 = vqtbl1q_u8(ytmp128, shuf_mask2);

		/* Add pairwise to get dataoff + iova in sgdesc_w1 */
		sgdesc01_w1 = vpaddq_u64(dataoff_iova0, dataoff_iova1);
		sgdesc23_w1 = vpaddq_u64(dataoff_iova2, dataoff_iova3);

		/* Orr both sgdesc_w0 and senddesc_w0 with 16 bits of
		 * pktlen at 15:0 position.
		 */
		sgdesc01_w0 = vorrq_u64(sgdesc01_w0, xtmp128);
		sgdesc23_w0 = vorrq_u64(sgdesc23_w0, ytmp128);
		senddesc01_w0 = vorrq_u64(senddesc01_w0, xtmp128);
		senddesc23_w0 = vorrq_u64(senddesc23_w0, ytmp128);

		if ((flags & NIX_TX_OFFLOAD_L3_L4_CSUM_F) &&
		    !(flags & NIX_TX_OFFLOAD_OL3_OL4_CSUM_F)) {
			/*
			 * Lookup table to translate ol_flags to
			 * il3/il4 types. But we still use ol3/ol4 types in
			 * senddesc_w1 as only one header processing is enabled.
			 */
			const uint8x16_t tbl = {
				/* [0-15] = il4type:il3type */
				0x04, /* none (IPv6 assumed) */
				0x14, /* RTE_MBUF_F_TX_TCP_CKSUM (IPv6 assumed) */
				0x24, /* RTE_MBUF_F_TX_SCTP_CKSUM (IPv6 assumed) */
				0x34, /* RTE_MBUF_F_TX_UDP_CKSUM (IPv6 assumed) */
				0x03, /* RTE_MBUF_F_TX_IP_CKSUM */
				0x13, /* RTE_MBUF_F_TX_IP_CKSUM | RTE_MBUF_F_TX_TCP_CKSUM */
				0x23, /* RTE_MBUF_F_TX_IP_CKSUM | RTE_MBUF_F_TX_SCTP_CKSUM */
				0x33, /* RTE_MBUF_F_TX_IP_CKSUM | RTE_MBUF_F_TX_UDP_CKSUM */
				0x02, /* RTE_MBUF_F_TX_IPV4  */
				0x12, /* RTE_MBUF_F_TX_IPV4 | RTE_MBUF_F_TX_TCP_CKSUM */
				0x22, /* RTE_MBUF_F_TX_IPV4 | RTE_MBUF_F_TX_SCTP_CKSUM */
				0x32, /* RTE_MBUF_F_TX_IPV4 | RTE_MBUF_F_TX_UDP_CKSUM */
				0x03, /* RTE_MBUF_F_TX_IPV4 | RTE_MBUF_F_TX_IP_CKSUM */
				0x13, /* RTE_MBUF_F_TX_IPV4 | RTE_MBUF_F_TX_IP_CKSUM |
				       * RTE_MBUF_F_TX_TCP_CKSUM
				       */
				0x23, /* RTE_MBUF_F_TX_IPV4 | RTE_MBUF_F_TX_IP_CKSUM |
				       * RTE_MBUF_F_TX_SCTP_CKSUM
				       */
				0x33, /* RTE_MBUF_F_TX_IPV4 | RTE_MBUF_F_TX_IP_CKSUM |
				       * RTE_MBUF_F_TX_UDP_CKSUM
				       */
			};

			/* Extract olflags to translate to iltypes */
			xtmp128 = vzip1q_u64(len_olflags0, len_olflags1);
			ytmp128 = vzip1q_u64(len_olflags2, len_olflags3);

			/*
			 * E(47):L3_LEN(9):L2_LEN(7+z)
			 * E(47):L3_LEN(9):L2_LEN(7+z)
			 */
			senddesc01_w1 = vshlq_n_u64(senddesc01_w1, 1);
			senddesc23_w1 = vshlq_n_u64(senddesc23_w1, 1);

			/* Move OLFLAGS bits 55:52 to 51:48
			 * with zeros preprended on the byte and rest
			 * don't care
			 */
			xtmp128 = vshrq_n_u8(xtmp128, 4);
			ytmp128 = vshrq_n_u8(ytmp128, 4);
			/*
			 * E(48):L3_LEN(8):L2_LEN(z+7)
			 * E(48):L3_LEN(8):L2_LEN(z+7)
			 */
			const int8x16_t tshft3 = {
				-1, 0, 8, 8, 8,	8, 8, 8,
				-1, 0, 8, 8, 8,	8, 8, 8,
			};

			senddesc01_w1 = vshlq_u8(senddesc01_w1, tshft3);
			senddesc23_w1 = vshlq_u8(senddesc23_w1, tshft3);

			/* Do the lookup */
			ltypes01 = vqtbl1q_u8(tbl, xtmp128);
			ltypes23 = vqtbl1q_u8(tbl, ytmp128);

			/* Just use ld1q to retrieve aura
			 * when we don't need tx_offload
			 */
			mbuf0 = (uint64_t *)((uintptr_t)mbuf0 +
					offsetof(struct rte_mempool, pool_id));
			mbuf1 = (uint64_t *)((uintptr_t)mbuf1 +
					offsetof(struct rte_mempool, pool_id));
			mbuf2 = (uint64_t *)((uintptr_t)mbuf2 +
					offsetof(struct rte_mempool, pool_id));
			mbuf3 = (uint64_t *)((uintptr_t)mbuf3 +
					offsetof(struct rte_mempool, pool_id));

			/* Pick only relevant fields i.e Bit 48:55 of iltype
			 * and place it in ol3/ol4type of senddesc_w1
			 */
			const uint8x16_t shuf_mask0 = {
				0xFF, 0xFF, 0xFF, 0xFF,	0x6, 0xFF, 0xFF, 0xFF,
				0xFF, 0xFF, 0xFF, 0xFF, 0xE, 0xFF, 0xFF, 0xFF,
			};

			ltypes01 = vqtbl1q_u8(ltypes01, shuf_mask0);
			ltypes23 = vqtbl1q_u8(ltypes23, shuf_mask0);

			/* Prepare ol4ptr, ol3ptr from ol3len, ol2len.
			 * a [E(32):E(16):OL3(8):OL2(8)]
			 * a = a + (a << 8)
			 * a [E(32):E(16):(OL3+OL2):OL2]
			 * => E(32):E(16)::OL4PTR(8):OL3PTR(8)
			 */
			senddesc01_w1 = vaddq_u8(senddesc01_w1,
						 vshlq_n_u16(senddesc01_w1, 8));
			senddesc23_w1 = vaddq_u8(senddesc23_w1,
						 vshlq_n_u16(senddesc23_w1, 8));

			/* Create first half of 4W cmd for 4 mbufs (sgdesc) */
			cmd01 = vzip1q_u64(sgdesc01_w0, sgdesc01_w1);
			cmd11 = vzip2q_u64(sgdesc01_w0, sgdesc01_w1);
			cmd21 = vzip1q_u64(sgdesc23_w0, sgdesc23_w1);
			cmd31 = vzip2q_u64(sgdesc23_w0, sgdesc23_w1);

			xmask01 = vdupq_n_u64(0);
			xmask23 = xmask01;
			asm volatile ("LD1 {%[a].H}[0],[%[in]]\n\t" :
				[a]"+w"(xmask01) : [in]"r"(mbuf0) : "memory");

			asm volatile ("LD1 {%[a].H}[4],[%[in]]\n\t" :
				 [a]"+w"(xmask01) : [in]"r"(mbuf1) : "memory");

			asm volatile ("LD1 {%[b].H}[0],[%[in]]\n\t" :
				 [b]"+w"(xmask23) : [in]"r"(mbuf2) : "memory");

			asm volatile ("LD1 {%[b].H}[4],[%[in]]\n\t" :
				 [b]"+w"(xmask23) : [in]"r"(mbuf3) : "memory");
			xmask01 = vshlq_n_u64(xmask01, 20);
			xmask23 = vshlq_n_u64(xmask23, 20);

			senddesc01_w0 = vorrq_u64(senddesc01_w0, xmask01);
			senddesc23_w0 = vorrq_u64(senddesc23_w0, xmask23);
			/* Move ltypes to senddesc*_w1 */
			senddesc01_w1 = vorrq_u64(senddesc01_w1, ltypes01);
			senddesc23_w1 = vorrq_u64(senddesc23_w1, ltypes23);

			/* Create first half of 4W cmd for 4 mbufs (sendhdr) */
			cmd00 = vzip1q_u64(senddesc01_w0, senddesc01_w1);
			cmd10 = vzip2q_u64(senddesc01_w0, senddesc01_w1);
			cmd20 = vzip1q_u64(senddesc23_w0, senddesc23_w1);
			cmd30 = vzip2q_u64(senddesc23_w0, senddesc23_w1);

		} else if (!(flags & NIX_TX_OFFLOAD_L3_L4_CSUM_F) &&
			   (flags & NIX_TX_OFFLOAD_OL3_OL4_CSUM_F)) {
			/*
			 * Lookup table to translate ol_flags to
			 * ol3/ol4 types.
			 */

			const uint8x16_t tbl = {
				/* [0-15] = ol4type:ol3type */
				0x00, /* none */
				0x03, /* OUTER_IP_CKSUM */
				0x02, /* OUTER_IPV4 */
				0x03, /* OUTER_IPV4 | OUTER_IP_CKSUM */
				0x04, /* OUTER_IPV6 */
				0x00, /* OUTER_IPV6 | OUTER_IP_CKSUM */
				0x00, /* OUTER_IPV6 | OUTER_IPV4 */
				0x00, /* OUTER_IPV6 | OUTER_IPV4 |
				       * OUTER_IP_CKSUM
				       */
				0x00, /* OUTER_UDP_CKSUM */
				0x33, /* OUTER_UDP_CKSUM | OUTER_IP_CKSUM */
				0x32, /* OUTER_UDP_CKSUM | OUTER_IPV4 */
				0x33, /* OUTER_UDP_CKSUM | OUTER_IPV4 |
				       * OUTER_IP_CKSUM
				       */
				0x34, /* OUTER_UDP_CKSUM | OUTER_IPV6 */
				0x00, /* OUTER_UDP_CKSUM | OUTER_IPV6 |
				       * OUTER_IP_CKSUM
				       */
				0x00, /* OUTER_UDP_CKSUM | OUTER_IPV6 |
				       * OUTER_IPV4
				       */
				0x00, /* OUTER_UDP_CKSUM | OUTER_IPV6 |
				       * OUTER_IPV4 | OUTER_IP_CKSUM
				       */
			};

			/* Extract olflags to translate to iltypes */
			xtmp128 = vzip1q_u64(len_olflags0, len_olflags1);
			ytmp128 = vzip1q_u64(len_olflags2, len_olflags3);

			/*
			 * E(47):OL3_LEN(9):OL2_LEN(7+z)
			 * E(47):OL3_LEN(9):OL2_LEN(7+z)
			 */
			const uint8x16_t shuf_mask5 = {
				0x6, 0x5, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
				0xE, 0xD, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
			};
			senddesc01_w1 = vqtbl1q_u8(senddesc01_w1, shuf_mask5);
			senddesc23_w1 = vqtbl1q_u8(senddesc23_w1, shuf_mask5);

			/* Extract outer ol flags only */
			const uint64x2_t o_cksum_mask = {
				0x1C00020000000000,
				0x1C00020000000000,
			};

			xtmp128 = vandq_u64(xtmp128, o_cksum_mask);
			ytmp128 = vandq_u64(ytmp128, o_cksum_mask);

			/* Extract OUTER_UDP_CKSUM bit 41 and
			 * move it to bit 61
			 */

			xtmp128 = xtmp128 | vshlq_n_u64(xtmp128, 20);
			ytmp128 = ytmp128 | vshlq_n_u64(ytmp128, 20);

			/* Shift oltype by 2 to start nibble from BIT(56)
			 * instead of BIT(58)
			 */
			xtmp128 = vshrq_n_u8(xtmp128, 2);
			ytmp128 = vshrq_n_u8(ytmp128, 2);
			/*
			 * E(48):L3_LEN(8):L2_LEN(z+7)
			 * E(48):L3_LEN(8):L2_LEN(z+7)
			 */
			const int8x16_t tshft3 = {
				-1, 0, 8, 8, 8, 8, 8, 8,
				-1, 0, 8, 8, 8, 8, 8, 8,
			};

			senddesc01_w1 = vshlq_u8(senddesc01_w1, tshft3);
			senddesc23_w1 = vshlq_u8(senddesc23_w1, tshft3);

			/* Do the lookup */
			ltypes01 = vqtbl1q_u8(tbl, xtmp128);
			ltypes23 = vqtbl1q_u8(tbl, ytmp128);

			/* Just use ld1q to retrieve aura
			 * when we don't need tx_offload
			 */
			mbuf0 = (uint64_t *)((uintptr_t)mbuf0 +
					offsetof(struct rte_mempool, pool_id));
			mbuf1 = (uint64_t *)((uintptr_t)mbuf1 +
					offsetof(struct rte_mempool, pool_id));
			mbuf2 = (uint64_t *)((uintptr_t)mbuf2 +
					offsetof(struct rte_mempool, pool_id));
			mbuf3 = (uint64_t *)((uintptr_t)mbuf3 +
					offsetof(struct rte_mempool, pool_id));

			/* Pick only relevant fields i.e Bit 56:63 of oltype
			 * and place it in ol3/ol4type of senddesc_w1
			 */
			const uint8x16_t shuf_mask0 = {
				0xFF, 0xFF, 0xFF, 0xFF,	0x7, 0xFF, 0xFF, 0xFF,
				0xFF, 0xFF, 0xFF, 0xFF, 0xF, 0xFF, 0xFF, 0xFF,
			};

			ltypes01 = vqtbl1q_u8(ltypes01, shuf_mask0);
			ltypes23 = vqtbl1q_u8(ltypes23, shuf_mask0);

			/* Prepare ol4ptr, ol3ptr from ol3len, ol2len.
			 * a [E(32):E(16):OL3(8):OL2(8)]
			 * a = a + (a << 8)
			 * a [E(32):E(16):(OL3+OL2):OL2]
			 * => E(32):E(16)::OL4PTR(8):OL3PTR(8)
			 */
			senddesc01_w1 = vaddq_u8(senddesc01_w1,
						 vshlq_n_u16(senddesc01_w1, 8));
			senddesc23_w1 = vaddq_u8(senddesc23_w1,
						 vshlq_n_u16(senddesc23_w1, 8));

			/* Create second half of 4W cmd for 4 mbufs (sgdesc) */
			cmd01 = vzip1q_u64(sgdesc01_w0, sgdesc01_w1);
			cmd11 = vzip2q_u64(sgdesc01_w0, sgdesc01_w1);
			cmd21 = vzip1q_u64(sgdesc23_w0, sgdesc23_w1);
			cmd31 = vzip2q_u64(sgdesc23_w0, sgdesc23_w1);

			xmask01 = vdupq_n_u64(0);
			xmask23 = xmask01;
			asm volatile ("LD1 {%[a].H}[0],[%[in]]\n\t" :
				 [a]"+w"(xmask01) : [in]"r"(mbuf0) : "memory");

			asm volatile ("LD1 {%[a].H}[4],[%[in]]\n\t" :
				 [a]"+w"(xmask01) : [in]"r"(mbuf1) : "memory");

			asm volatile ("LD1 {%[b].H}[0],[%[in]]\n\t" :
				 [b]"+w"(xmask23) : [in]"r"(mbuf2) : "memory");

			asm volatile ("LD1 {%[b].H}[4],[%[in]]\n\t" :
				 [b]"+w"(xmask23) : [in]"r"(mbuf3) : "memory");
			xmask01 = vshlq_n_u64(xmask01, 20);
			xmask23 = vshlq_n_u64(xmask23, 20);

			senddesc01_w0 = vorrq_u64(senddesc01_w0, xmask01);
			senddesc23_w0 = vorrq_u64(senddesc23_w0, xmask23);
			/* Move ltypes to senddesc*_w1 */
			senddesc01_w1 = vorrq_u64(senddesc01_w1, ltypes01);
			senddesc23_w1 = vorrq_u64(senddesc23_w1, ltypes23);

			/* Create first half of 4W cmd for 4 mbufs (sendhdr) */
			cmd00 = vzip1q_u64(senddesc01_w0, senddesc01_w1);
			cmd10 = vzip2q_u64(senddesc01_w0, senddesc01_w1);
			cmd20 = vzip1q_u64(senddesc23_w0, senddesc23_w1);
			cmd30 = vzip2q_u64(senddesc23_w0, senddesc23_w1);

		} else if ((flags & NIX_TX_OFFLOAD_L3_L4_CSUM_F) &&
			   (flags & NIX_TX_OFFLOAD_OL3_OL4_CSUM_F)) {
			/* Lookup table to translate ol_flags to
			 * ol4type, ol3type, il4type, il3type of senddesc_w1
			 */
			const uint8x16x2_t tbl = {
			{
				{
					/* [0-15] = il4type:il3type */
					0x04, /* none (IPv6) */
					0x14, /* RTE_MBUF_F_TX_TCP_CKSUM (IPv6) */
					0x24, /* RTE_MBUF_F_TX_SCTP_CKSUM (IPv6) */
					0x34, /* RTE_MBUF_F_TX_UDP_CKSUM (IPv6) */
					0x03, /* RTE_MBUF_F_TX_IP_CKSUM */
					0x13, /* RTE_MBUF_F_TX_IP_CKSUM |
					       * RTE_MBUF_F_TX_TCP_CKSUM
					       */
					0x23, /* RTE_MBUF_F_TX_IP_CKSUM |
					       * RTE_MBUF_F_TX_SCTP_CKSUM
					       */
					0x33, /* RTE_MBUF_F_TX_IP_CKSUM |
					       * RTE_MBUF_F_TX_UDP_CKSUM
					       */
					0x02, /* RTE_MBUF_F_TX_IPV4 */
					0x12, /* RTE_MBUF_F_TX_IPV4 |
					       * RTE_MBUF_F_TX_TCP_CKSUM
					       */
					0x22, /* RTE_MBUF_F_TX_IPV4 |
					       * RTE_MBUF_F_TX_SCTP_CKSUM
					       */
					0x32, /* RTE_MBUF_F_TX_IPV4 |
					       * RTE_MBUF_F_TX_UDP_CKSUM
					       */
					0x03, /* RTE_MBUF_F_TX_IPV4 |
					       * RTE_MBUF_F_TX_IP_CKSUM
					       */
					0x13, /* RTE_MBUF_F_TX_IPV4 | RTE_MBUF_F_TX_IP_CKSUM |
					       * RTE_MBUF_F_TX_TCP_CKSUM
					       */
					0x23, /* RTE_MBUF_F_TX_IPV4 | RTE_MBUF_F_TX_IP_CKSUM |
					       * RTE_MBUF_F_TX_SCTP_CKSUM
					       */
					0x33, /* RTE_MBUF_F_TX_IPV4 | RTE_MBUF_F_TX_IP_CKSUM |
					       * RTE_MBUF_F_TX_UDP_CKSUM
					       */
				},

				{
					/* [16-31] = ol4type:ol3type */
					0x00, /* none */
					0x03, /* OUTER_IP_CKSUM */
					0x02, /* OUTER_IPV4 */
					0x03, /* OUTER_IPV4 | OUTER_IP_CKSUM */
					0x04, /* OUTER_IPV6 */
					0x00, /* OUTER_IPV6 | OUTER_IP_CKSUM */
					0x00, /* OUTER_IPV6 | OUTER_IPV4 */
					0x00, /* OUTER_IPV6 | OUTER_IPV4 |
					       * OUTER_IP_CKSUM
					       */
					0x00, /* OUTER_UDP_CKSUM */
					0x33, /* OUTER_UDP_CKSUM |
					       * OUTER_IP_CKSUM
					       */
					0x32, /* OUTER_UDP_CKSUM |
					       * OUTER_IPV4
					       */
					0x33, /* OUTER_UDP_CKSUM |
					       * OUTER_IPV4 | OUTER_IP_CKSUM
					       */
					0x34, /* OUTER_UDP_CKSUM |
					       * OUTER_IPV6
					       */
					0x00, /* OUTER_UDP_CKSUM | OUTER_IPV6 |
					       * OUTER_IP_CKSUM
					       */
					0x00, /* OUTER_UDP_CKSUM | OUTER_IPV6 |
					       * OUTER_IPV4
					       */
					0x00, /* OUTER_UDP_CKSUM | OUTER_IPV6 |
					       * OUTER_IPV4 | OUTER_IP_CKSUM
					       */
				},
			}
			};

			/* Extract olflags to translate to oltype & iltype */
			xtmp128 = vzip1q_u64(len_olflags0, len_olflags1);
			ytmp128 = vzip1q_u64(len_olflags2, len_olflags3);

			/*
			 * E(8):OL2_LN(7):OL3_LN(9):E(23):L3_LN(9):L2_LN(7+z)
			 * E(8):OL2_LN(7):OL3_LN(9):E(23):L3_LN(9):L2_LN(7+z)
			 */
			const uint32x4_t tshft_4 = {
				1, 0,
				1, 0,
			};
			senddesc01_w1 = vshlq_u32(senddesc01_w1, tshft_4);
			senddesc23_w1 = vshlq_u32(senddesc23_w1, tshft_4);

			/*
			 * E(32):L3_LEN(8):L2_LEN(7+Z):OL3_LEN(8):OL2_LEN(7+Z)
			 * E(32):L3_LEN(8):L2_LEN(7+Z):OL3_LEN(8):OL2_LEN(7+Z)
			 */
			const uint8x16_t shuf_mask5 = {
				0x6, 0x5, 0x0, 0x1, 0xFF, 0xFF, 0xFF, 0xFF,
				0xE, 0xD, 0x8, 0x9, 0xFF, 0xFF,	0xFF, 0xFF,
			};
			senddesc01_w1 = vqtbl1q_u8(senddesc01_w1, shuf_mask5);
			senddesc23_w1 = vqtbl1q_u8(senddesc23_w1, shuf_mask5);

			/* Extract outer and inner header ol_flags */
			const uint64x2_t oi_cksum_mask = {
				0x1CF0020000000000,
				0x1CF0020000000000,
			};

			xtmp128 = vandq_u64(xtmp128, oi_cksum_mask);
			ytmp128 = vandq_u64(ytmp128, oi_cksum_mask);

			/* Extract OUTER_UDP_CKSUM bit 41 and
			 * move it to bit 61
			 */

			xtmp128 = xtmp128 | vshlq_n_u64(xtmp128, 20);
			ytmp128 = ytmp128 | vshlq_n_u64(ytmp128, 20);

			/* Shift right oltype by 2 and iltype by 4
			 * to start oltype nibble from BIT(58)
			 * instead of BIT(56) and iltype nibble from BIT(48)
			 * instead of BIT(52).
			 */
			const int8x16_t tshft5 = {
				8, 8, 8, 8, 8, 8, -4, -2,
				8, 8, 8, 8, 8, 8, -4, -2,
			};

			xtmp128 = vshlq_u8(xtmp128, tshft5);
			ytmp128 = vshlq_u8(ytmp128, tshft5);
			/*
			 * E(32):L3_LEN(8):L2_LEN(8):OL3_LEN(8):OL2_LEN(8)
			 * E(32):L3_LEN(8):L2_LEN(8):OL3_LEN(8):OL2_LEN(8)
			 */
			const int8x16_t tshft3 = {
				-1, 0, -1, 0, 0, 0, 0, 0,
				-1, 0, -1, 0, 0, 0, 0, 0,
			};

			senddesc01_w1 = vshlq_u8(senddesc01_w1, tshft3);
			senddesc23_w1 = vshlq_u8(senddesc23_w1, tshft3);

			/* Mark Bit(4) of oltype */
			const uint64x2_t oi_cksum_mask2 = {
				0x1000000000000000,
				0x1000000000000000,
			};

			xtmp128 = vorrq_u64(xtmp128, oi_cksum_mask2);
			ytmp128 = vorrq_u64(ytmp128, oi_cksum_mask2);

			/* Do the lookup */
			ltypes01 = vqtbl2q_u8(tbl, xtmp128);
			ltypes23 = vqtbl2q_u8(tbl, ytmp128);

			/* Just use ld1q to retrieve aura
			 * when we don't need tx_offload
			 */
			mbuf0 = (uint64_t *)((uintptr_t)mbuf0 +
					offsetof(struct rte_mempool, pool_id));
			mbuf1 = (uint64_t *)((uintptr_t)mbuf1 +
					offsetof(struct rte_mempool, pool_id));
			mbuf2 = (uint64_t *)((uintptr_t)mbuf2 +
					offsetof(struct rte_mempool, pool_id));
			mbuf3 = (uint64_t *)((uintptr_t)mbuf3 +
					offsetof(struct rte_mempool, pool_id));

			/* Pick only relevant fields i.e Bit 48:55 of iltype and
			 * Bit 56:63 of oltype and place it in corresponding
			 * place in senddesc_w1.
			 */
			const uint8x16_t shuf_mask0 = {
				0xFF, 0xFF, 0xFF, 0xFF, 0x7, 0x6, 0xFF, 0xFF,
				0xFF, 0xFF, 0xFF, 0xFF,	0xF, 0xE, 0xFF, 0xFF,
			};

			ltypes01 = vqtbl1q_u8(ltypes01, shuf_mask0);
			ltypes23 = vqtbl1q_u8(ltypes23, shuf_mask0);

			/* Prepare l4ptr, l3ptr, ol4ptr, ol3ptr from
			 * l3len, l2len, ol3len, ol2len.
			 * a [E(32):L3(8):L2(8):OL3(8):OL2(8)]
			 * a = a + (a << 8)
			 * a [E:(L3+L2):(L2+OL3):(OL3+OL2):OL2]
			 * a = a + (a << 16)
			 * a [E:(L3+L2+OL3+OL2):(L2+OL3+OL2):(OL3+OL2):OL2]
			 * => E(32):IL4PTR(8):IL3PTR(8):OL4PTR(8):OL3PTR(8)
			 */
			senddesc01_w1 = vaddq_u8(senddesc01_w1,
						 vshlq_n_u32(senddesc01_w1, 8));
			senddesc23_w1 = vaddq_u8(senddesc23_w1,
						 vshlq_n_u32(senddesc23_w1, 8));

			/* Create second half of 4W cmd for 4 mbufs (sgdesc) */
			cmd01 = vzip1q_u64(sgdesc01_w0, sgdesc01_w1);
			cmd11 = vzip2q_u64(sgdesc01_w0, sgdesc01_w1);
			cmd21 = vzip1q_u64(sgdesc23_w0, sgdesc23_w1);
			cmd31 = vzip2q_u64(sgdesc23_w0, sgdesc23_w1);

			/* Continue preparing l4ptr, l3ptr, ol4ptr, ol3ptr */
			senddesc01_w1 = vaddq_u8(senddesc01_w1,
						vshlq_n_u32(senddesc01_w1, 16));
			senddesc23_w1 = vaddq_u8(senddesc23_w1,
						vshlq_n_u32(senddesc23_w1, 16));

			xmask01 = vdupq_n_u64(0);
			xmask23 = xmask01;
			asm volatile ("LD1 {%[a].H}[0],[%[in]]\n\t" :
				 [a]"+w"(xmask01) : [in]"r"(mbuf0) : "memory");

			asm volatile ("LD1 {%[a].H}[4],[%[in]]\n\t" :
				 [a]"+w"(xmask01) : [in]"r"(mbuf1) : "memory");

			asm volatile ("LD1 {%[b].H}[0],[%[in]]\n\t" :
				 [b]"+w"(xmask23) : [in]"r"(mbuf2) : "memory");

			asm volatile ("LD1 {%[b].H}[4],[%[in]]\n\t" :
				 [b]"+w"(xmask23) : [in]"r"(mbuf3) : "memory");
			xmask01 = vshlq_n_u64(xmask01, 20);
			xmask23 = vshlq_n_u64(xmask23, 20);

			senddesc01_w0 = vorrq_u64(senddesc01_w0, xmask01);
			senddesc23_w0 = vorrq_u64(senddesc23_w0, xmask23);
			/* Move ltypes to senddesc*_w1 */
			senddesc01_w1 = vorrq_u64(senddesc01_w1, ltypes01);
			senddesc23_w1 = vorrq_u64(senddesc23_w1, ltypes23);

			/* Create first half of 4W cmd for 4 mbufs (sendhdr) */
			cmd00 = vzip1q_u64(senddesc01_w0, senddesc01_w1);
			cmd10 = vzip2q_u64(senddesc01_w0, senddesc01_w1);
			cmd20 = vzip1q_u64(senddesc23_w0, senddesc23_w1);
			cmd30 = vzip2q_u64(senddesc23_w0, senddesc23_w1);
		} else {
			/* Just use ld1q to retrieve aura
			 * when we don't need tx_offload
			 */
			mbuf0 = (uint64_t *)((uintptr_t)mbuf0 +
					offsetof(struct rte_mempool, pool_id));
			mbuf1 = (uint64_t *)((uintptr_t)mbuf1 +
					offsetof(struct rte_mempool, pool_id));
			mbuf2 = (uint64_t *)((uintptr_t)mbuf2 +
					offsetof(struct rte_mempool, pool_id));
			mbuf3 = (uint64_t *)((uintptr_t)mbuf3 +
					offsetof(struct rte_mempool, pool_id));
			xmask01 = vdupq_n_u64(0);
			xmask23 = xmask01;
			asm volatile ("LD1 {%[a].H}[0],[%[in]]\n\t" :
				 [a]"+w"(xmask01) : [in]"r"(mbuf0) : "memory");

			asm volatile ("LD1 {%[a].H}[4],[%[in]]\n\t" :
				 [a]"+w"(xmask01) : [in]"r"(mbuf1) : "memory");

			asm volatile ("LD1 {%[b].H}[0],[%[in]]\n\t" :
				 [b]"+w"(xmask23) : [in]"r"(mbuf2) : "memory");

			asm volatile ("LD1 {%[b].H}[4],[%[in]]\n\t" :
				 [b]"+w"(xmask23) : [in]"r"(mbuf3) : "memory");
			xmask01 = vshlq_n_u64(xmask01, 20);
			xmask23 = vshlq_n_u64(xmask23, 20);

			senddesc01_w0 = vorrq_u64(senddesc01_w0, xmask01);
			senddesc23_w0 = vorrq_u64(senddesc23_w0, xmask23);

			/* Create 4W cmd for 4 mbufs (sendhdr, sgdesc) */
			cmd00 = vzip1q_u64(senddesc01_w0, senddesc01_w1);
			cmd01 = vzip1q_u64(sgdesc01_w0, sgdesc01_w1);
			cmd10 = vzip2q_u64(senddesc01_w0, senddesc01_w1);
			cmd11 = vzip2q_u64(sgdesc01_w0, sgdesc01_w1);
			cmd20 = vzip1q_u64(senddesc23_w0, senddesc23_w1);
			cmd21 = vzip1q_u64(sgdesc23_w0, sgdesc23_w1);
			cmd30 = vzip2q_u64(senddesc23_w0, senddesc23_w1);
			cmd31 = vzip2q_u64(sgdesc23_w0, sgdesc23_w1);
		}

		do {
			vst1q_u64(lmt_addr, cmd00);
			vst1q_u64(lmt_addr + 2, cmd01);
			vst1q_u64(lmt_addr + 4, cmd10);
			vst1q_u64(lmt_addr + 6, cmd11);
			vst1q_u64(lmt_addr + 8, cmd20);
			vst1q_u64(lmt_addr + 10, cmd21);
			vst1q_u64(lmt_addr + 12, cmd30);
			vst1q_u64(lmt_addr + 14, cmd31);
			lmt_status = otx2_lmt_submit(io_addr);

		} while (lmt_status == 0);
		tx_pkts = tx_pkts + NIX_DESCS_PER_LOOP;
	}

	if (unlikely(pkts_left))
		pkts += nix_xmit_pkts(tx_queue, tx_pkts, pkts_left, cmd, flags);

	return pkts;
}

#else
static __rte_always_inline uint16_t
nix_xmit_pkts_vector(void *tx_queue, struct rte_mbuf **tx_pkts,
		     uint16_t pkts, uint64_t *cmd, const uint16_t flags)
{
	RTE_SET_USED(tx_queue);
	RTE_SET_USED(tx_pkts);
	RTE_SET_USED(pkts);
	RTE_SET_USED(cmd);
	RTE_SET_USED(flags);
	return 0;
}
#endif

#define T(name, f6, f5, f4, f3, f2, f1, f0, sz, flags)			\
static uint16_t __rte_noinline	__rte_hot					\
otx2_nix_xmit_pkts_ ## name(void *tx_queue,				\
			struct rte_mbuf **tx_pkts, uint16_t pkts)	\
{									\
	uint64_t cmd[sz];						\
									\
	/* For TSO inner checksum is a must */				\
	if (((flags) & NIX_TX_OFFLOAD_TSO_F) &&				\
	    !((flags) & NIX_TX_OFFLOAD_L3_L4_CSUM_F))			\
		return 0;						\
	return nix_xmit_pkts(tx_queue, tx_pkts, pkts, cmd, flags);	\
}

NIX_TX_FASTPATH_MODES
#undef T

#define T(name, f6, f5, f4, f3, f2, f1, f0, sz, flags)			\
static uint16_t __rte_noinline	__rte_hot					\
otx2_nix_xmit_pkts_mseg_ ## name(void *tx_queue,			\
			struct rte_mbuf **tx_pkts, uint16_t pkts)	\
{									\
	uint64_t cmd[(sz) + NIX_TX_MSEG_SG_DWORDS - 2];			\
									\
	/* For TSO inner checksum is a must */				\
	if (((flags) & NIX_TX_OFFLOAD_TSO_F) &&				\
	    !((flags) & NIX_TX_OFFLOAD_L3_L4_CSUM_F))			\
		return 0;						\
	return nix_xmit_pkts_mseg(tx_queue, tx_pkts, pkts, cmd,		\
				  (flags) | NIX_TX_MULTI_SEG_F);	\
}

NIX_TX_FASTPATH_MODES
#undef T

#define T(name, f6, f5, f4, f3, f2, f1, f0, sz, flags)			\
static uint16_t __rte_noinline	__rte_hot					\
otx2_nix_xmit_pkts_vec_ ## name(void *tx_queue,				\
			struct rte_mbuf **tx_pkts, uint16_t pkts)	\
{									\
	uint64_t cmd[sz];						\
									\
	/* VLAN, TSTMP, TSO is not supported by vec */			\
	if ((flags) & NIX_TX_OFFLOAD_VLAN_QINQ_F ||			\
	    (flags) & NIX_TX_OFFLOAD_TSTAMP_F ||			\
	    (flags) & NIX_TX_OFFLOAD_TSO_F)				\
		return 0;						\
	return nix_xmit_pkts_vector(tx_queue, tx_pkts, pkts, cmd, (flags)); \
}

NIX_TX_FASTPATH_MODES
#undef T

static inline void
pick_tx_func(struct rte_eth_dev *eth_dev,
	     const eth_tx_burst_t tx_burst[2][2][2][2][2][2][2])
{
	struct otx2_eth_dev *dev = otx2_eth_pmd_priv(eth_dev);

	/* [SEC] [TSTMP] [NOFF] [VLAN] [OL3_OL4_CSUM] [IL3_IL4_CSUM] */
	eth_dev->tx_pkt_burst = tx_burst
		[!!(dev->tx_offload_flags & NIX_TX_OFFLOAD_SECURITY_F)]
		[!!(dev->tx_offload_flags & NIX_TX_OFFLOAD_TSO_F)]
		[!!(dev->tx_offload_flags & NIX_TX_OFFLOAD_TSTAMP_F)]
		[!!(dev->tx_offload_flags & NIX_TX_OFFLOAD_MBUF_NOFF_F)]
		[!!(dev->tx_offload_flags & NIX_TX_OFFLOAD_VLAN_QINQ_F)]
		[!!(dev->tx_offload_flags & NIX_TX_OFFLOAD_OL3_OL4_CSUM_F)]
		[!!(dev->tx_offload_flags & NIX_TX_OFFLOAD_L3_L4_CSUM_F)];
}

void
otx2_eth_set_tx_function(struct rte_eth_dev *eth_dev)
{
	struct otx2_eth_dev *dev = otx2_eth_pmd_priv(eth_dev);

	const eth_tx_burst_t nix_eth_tx_burst[2][2][2][2][2][2][2] = {
#define T(name, f6, f5, f4, f3, f2, f1, f0, sz, flags)			\
	[f6][f5][f4][f3][f2][f1][f0] =  otx2_nix_xmit_pkts_ ## name,

NIX_TX_FASTPATH_MODES
#undef T
	};

	const eth_tx_burst_t nix_eth_tx_burst_mseg[2][2][2][2][2][2][2] = {
#define T(name, f6, f5, f4, f3, f2, f1, f0, sz, flags)			\
	[f6][f5][f4][f3][f2][f1][f0] =  otx2_nix_xmit_pkts_mseg_ ## name,

NIX_TX_FASTPATH_MODES
#undef T
	};

	const eth_tx_burst_t nix_eth_tx_vec_burst[2][2][2][2][2][2][2] = {
#define T(name, f6, f5, f4, f3, f2, f1, f0, sz, flags)			\
	[f6][f5][f4][f3][f2][f1][f0] =  otx2_nix_xmit_pkts_vec_ ## name,

NIX_TX_FASTPATH_MODES
#undef T
	};

	if (dev->scalar_ena ||
	    (dev->tx_offload_flags &
	     (NIX_TX_OFFLOAD_VLAN_QINQ_F | NIX_TX_OFFLOAD_TSTAMP_F |
	      NIX_TX_OFFLOAD_TSO_F)))
		pick_tx_func(eth_dev, nix_eth_tx_burst);
	else
		pick_tx_func(eth_dev, nix_eth_tx_vec_burst);

	if (dev->tx_offloads & RTE_ETH_TX_OFFLOAD_MULTI_SEGS)
		pick_tx_func(eth_dev, nix_eth_tx_burst_mseg);

	rte_mb();
}
