diff --git a/doc/guides/nics/cpfl.rst b/doc/guides/nics/cpfl.rst
index 9b7a99c894..591bd496e6 100644
--- a/doc/guides/nics/cpfl.rst
+++ b/doc/guides/nics/cpfl.rst
@@ -213,6 +213,16 @@ low level hardware resources.
       flow create X ingress group M pattern eth dst is 00:01:00:00:03:14 / ipv4 src is 192.168.0.1 \
       dst is 192.168.0.2 / tcp / end actions port_representor port_id Y / end
 
+#. Create one flow for TDI engine to forward ETH-IPV4-TCP from I/O port to a local(CPF's) vport. Flow should
+   be created on vport X. Group M should be table id. Prog name N should be action id. Prog arguments
+   port_representor Y means forward packet to local vport Y::
+
+   .. code-block:: console
+
+      flow create X ingress group M pattern prog key is 0x00 / prog key is 0x000100000314 / prog key
+      is 0x001122334455 / prog key is 0xC0A80001 / prog key is 0xC0A80002 / prog key is 0x1451 / prog key
+      is 0x157C / end actions prog name N arguments port_representor Y  end / end
+
 #. Send a matched packet, and it should be displayed on PMD::
 
    .. code-block:: console
diff --git a/doc/guides/nics/features/cpfl.ini b/doc/guides/nics/features/cpfl.ini
index 4eadaca6e7..85b8011a54 100644
--- a/doc/guides/nics/features/cpfl.ini
+++ b/doc/guides/nics/features/cpfl.ini
@@ -33,6 +33,7 @@ tcp                  = Y
 udp                  = Y
 vlan                 = Y
 vxlan                = Y
+flex                 = Y
 
 [rte_flow actions]
 count                = Y
diff --git a/drivers/net/cpfl/cpfl_ethdev.h b/drivers/net/cpfl/cpfl_ethdev.h
index e580f80f2f..7dfa4a0183 100644
--- a/drivers/net/cpfl/cpfl_ethdev.h
+++ b/drivers/net/cpfl/cpfl_ethdev.h
@@ -185,10 +185,18 @@ struct cpfl_repr {
 	bool func_up; /* If the represented function is up */
 };
 
+struct cpfl_tdi_table_node;
+TAILQ_HEAD(cpfl_tdi_table_list, cpfl_tdi_table_node);
+
+struct cpfl_tdi_action_node;
+TAILQ_HEAD(cpfl_tdi_action_list, cpfl_tdi_action_node);
+
 struct cpfl_flow_parser {
 	struct cpfl_flow_js_parser *fixed_parser;
 	struct cpfl_tdi_program *p4_parser;
 	bool is_p4_parser;
+	struct cpfl_tdi_table_list tdi_table_list;
+	struct cpfl_tdi_action_list tdi_action_list;
 };
 
 struct cpfl_metadata_chunk {
diff --git a/drivers/net/cpfl/cpfl_flow.c b/drivers/net/cpfl/cpfl_flow.c
index 1c4131da2c..15c7cc6d8b 100644
--- a/drivers/net/cpfl/cpfl_flow.c
+++ b/drivers/net/cpfl/cpfl_flow.c
@@ -6,6 +6,7 @@
 
 #include "cpfl_flow.h"
 #include "cpfl_flow_parser.h"
+#include "cpfl_tdi.h"
 #include "cpfl_tdi_parser.h"
 
 TAILQ_HEAD(cpfl_flow_engine_list, cpfl_flow_engine);
@@ -338,8 +339,10 @@ cpfl_flow_uninit(struct cpfl_adapter_ext *ad)
 	if (ad->flow_parser.fixed_parser)
 		cpfl_parser_destroy(ad->flow_parser.fixed_parser);
 
-	if (ad->flow_parser.p4_parser)
+	if (ad->flow_parser.p4_parser) {
+		cpfl_tdi_free_table_list(&ad->flow_parser);
 		cpfl_tdi_program_destroy(ad->flow_parser.p4_parser);
+	}
 
 	cpfl_flow_engine_uninit(ad);
 }
diff --git a/drivers/net/cpfl/cpfl_flow.h b/drivers/net/cpfl/cpfl_flow.h
index 1bde847763..1de9c25b17 100644
--- a/drivers/net/cpfl/cpfl_flow.h
+++ b/drivers/net/cpfl/cpfl_flow.h
@@ -15,6 +15,7 @@ extern const struct rte_flow_ops cpfl_flow_ops;
 enum cpfl_flow_engine_type {
 	CPFL_FLOW_ENGINE_NONE = 0,
 	CPFL_FLOW_ENGINE_FXP,
+	CPFL_FLOW_ENGINE_TDI,
 };
 
 typedef int (*engine_init_t)(struct cpfl_adapter_ext *ad);
diff --git a/drivers/net/cpfl/cpfl_flow_engine_fxp.c b/drivers/net/cpfl/cpfl_flow_engine_fxp.c
index f269ff97e1..6a5e7ed770 100644
--- a/drivers/net/cpfl/cpfl_flow_engine_fxp.c
+++ b/drivers/net/cpfl/cpfl_flow_engine_fxp.c
@@ -27,23 +27,11 @@
 #include "cpfl_fxp_rule.h"
 #include "cpfl_flow_parser.h"
 
-#define CPFL_COOKIE_DEF		0x1000
-#define CPFL_MOD_COOKIE_DEF	0x1237561
 #define CPFL_PREC_DEF		1
 #define CPFL_PREC_SET		5
 #define CPFL_TYPE_ID		3
 #define CPFL_OFFSET		0x0a
-#define CPFL_HOST_ID_DEF	0
 #define CPFL_PF_NUM_DEF		0
-#define CPFL_PORT_NUM_DEF	0
-#define CPFL_RESP_REQ_DEF	2
-#define CPFL_PIN_TO_CACHE_DEF	0
-#define CPFL_CLEAR_MIRROR_1ST_STATE_DEF	0
-#define CPFL_FIXED_FETCH_DEF	0
-#define CPFL_PTI_DEF		0
-#define CPFL_MOD_OBJ_SIZE_DEF	0
-#define CPFL_PIN_MOD_CONTENT_DEF	0
-
 #define CPFL_MAX_MOD_CONTENT_INDEX	256
 #define CPFL_MAX_MR_ACTION_NUM	8
 
diff --git a/drivers/net/cpfl/cpfl_flow_parser.c b/drivers/net/cpfl/cpfl_flow_parser.c
index e7f8a8a6cc..a3572e32d5 100644
--- a/drivers/net/cpfl/cpfl_flow_parser.c
+++ b/drivers/net/cpfl/cpfl_flow_parser.c
@@ -6,6 +6,7 @@
 
 #include "cpfl_flow_parser.h"
 #include "cpfl_tdi_parser.h"
+#include "cpfl_tdi.h"
 
 static enum rte_flow_item_type
 cpfl_get_item_type_by_str(const char *type)
@@ -976,6 +977,13 @@ cpfl_parser_create(struct cpfl_flow_parser *flow_parser, const char *filename)
 		flow_parser->p4_parser = prog;
 		flow_parser->fixed_parser = NULL;
 		flow_parser->is_p4_parser = true;
+
+		ret = cpfl_tdi_build(flow_parser);
+		if (ret != 0) {
+			PMD_INIT_LOG(ERR, "Failed to build tdi program %s", filename);
+			rte_free(prog);
+			return -EINVAL;
+		}
 	} else {
 		PMD_DRV_LOG(NOTICE, "flow parser mode is fixed function mode.");
 		parser = rte_zmalloc("flow_parser", sizeof(struct cpfl_flow_js_parser), 0);
diff --git a/drivers/net/cpfl/cpfl_fxp_rule.h b/drivers/net/cpfl/cpfl_fxp_rule.h
index ed757b80b1..f089155353 100644
--- a/drivers/net/cpfl/cpfl_fxp_rule.h
+++ b/drivers/net/cpfl/cpfl_fxp_rule.h
@@ -9,6 +9,18 @@
 
 #define CPFL_MAX_KEY_LEN 128
 #define CPFL_MAX_RULE_ACTIONS 32
+#define CPFL_RESP_REQ_DEF	2
+#define CPFL_PIN_TO_CACHE_DEF	0
+#define CPFL_CLEAR_MIRROR_1ST_STATE_DEF	0
+#define CPFL_FIXED_FETCH_DEF	0
+#define CPFL_PTI_DEF		0
+#define CPFL_MOD_OBJ_SIZE_DEF	0
+#define CPFL_PIN_MOD_CONTENT_DEF	0
+#define CPFL_HOST_ID_DEF	0
+#define CPFL_PORT_NUM_DEF	0
+#define CPFL_VSI_DEF	0
+#define CPFL_COOKIE_DEF		0x1000
+#define CPFL_MOD_COOKIE_DEF	0x1237561
 
 struct cpfl_sem_rule_info {
 	uint16_t prof_id;
diff --git a/drivers/net/cpfl/cpfl_tdi.c b/drivers/net/cpfl/cpfl_tdi.c
new file mode 100644
index 0000000000..b7dd3e4764
--- /dev/null
+++ b/drivers/net/cpfl/cpfl_tdi.c
@@ -0,0 +1,1244 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(c) 2023 Intel Corporation
+ */
+#include <asm-generic/errno-base.h>
+#include <rte_hash_crc.h>
+#include <rte_tailq.h>
+#include <stdint.h>
+#include <string.h>
+
+#include "cpfl_actions.h"
+#include "cpfl_flow.h"
+#include "cpfl_fxp_rule.h"
+#include "cpfl_tdi.h"
+#include "cpfl_tdi_parser.h"
+#include "rte_common.h"
+#include "rte_flow.h"
+
+uint64_t cpfl_tdi_rule_cookie = CPFL_COOKIE_DEF;
+
+/*help function to do left shift on a byte array */
+static void
+cpfl_tdi_shift_left(uint8_t *buf, int length, uint32_t shift_amount)
+{
+	uint32_t i;
+	int j;
+	uint32_t carry = 0;
+
+	if (shift_amount == 0)
+		return;
+
+	for (i = 0; i < shift_amount; i += 8) {
+		for (j = 0; j < length; j++) {
+			uint32_t temp = (buf[j] << (shift_amount - i)) | carry;
+
+			carry = (temp >> 8) & 0xff;
+			buf[j] = temp & 0xff;
+		}
+	}
+}
+
+/* help function to init a mask array with bit_width */
+static void
+cpfl_tdi_init_msk_buf(uint8_t *buf, int length, uint32_t bit_width)
+{
+	uint32_t i;
+
+	memset(buf, 0, length);
+	for (i = 0; i < bit_width; i++) {
+		cpfl_tdi_shift_left(buf, length, 1);
+		buf[0] += 1;
+	}
+}
+
+/* help function to OR a byte array with value and mask */
+static void
+cpfl_tdi_or_buf(uint8_t *buf, int length, uint8_t *values, uint8_t *mask)
+{
+	int i;
+
+	for (i = 0; i < length; i++)
+		buf[i] = (values[i] & mask[i]) | (buf[i] & ~mask[i]);
+}
+
+static uint32_t
+cpfl_tdi_to_action_code(struct cpfl_tdi_hw_action *ha, uint8_t *val)
+{
+	switch (ha->action_code) {
+	case CPFL_TDI_ACTION_CODE_SET1A_24b:
+		switch (ha->index) {
+		case 0: /* mod addr */
+			return cpfl_act_mod_addr(ha->prec, (uint32_t)*val).data;
+		case 8: /* todo */
+			break;
+		default:
+			PMD_DRV_LOG(WARNING, "Unsupported SET1A_24b index %d", ha->index);
+			break;
+		}
+		break;
+	case CPFL_TDI_ACTION_CODE_SET1_16b:
+		switch (ha->index) {
+		case 2: /* set vsi */
+			return cpfl_act_fwd_vsi(0, ha->prec, CPFL_PE_LAN, (uint16_t)*val).data;
+		default:
+			PMD_DRV_LOG(WARNING, "Unsupported SET1_16b index %d", ha->index);
+			break;
+		}
+		break;
+	case CPFL_TDI_ACTION_CODE_SET1B_24b: /* set metadata */
+		switch (ha->setmd_action_code) {
+		case CPFL_TDI_SETMD_ACTION_CODE_SET_16b:
+			return cpfl_act_set_md16(ha->index, ha->prec, ha->type_id, ha->offset,
+						 (uint16_t)*val)
+			    .data;
+		case CPFL_TDI_SETMD_ACTION_CODE_SET_32b_AUX: /* todo */
+			return cpfl_act_fwd_vsi(0, ha->prec, CPFL_PE_LAN, (uint16_t)*val).data;
+		default:
+			PMD_DRV_LOG(WARNING, "Unsupported SET1b_24b setmd code %d",
+				    ha->setmd_action_code);
+			break;
+		}
+		break;
+	default:
+		PMD_DRV_LOG(WARNING, "Unsupported action code %d", ha->action_code);
+		break;
+	}
+
+	return 0;
+}
+
+static void
+cpfl_tdi_pack_sem_entry(struct cpfl_tdi_rule_info *rinfo,
+			struct cpfl_tdi_ma_hardware_block *hb,
+			enum cpfl_tdi_table_entry_op op,
+			struct idpf_dma_mem *dma,
+			struct idpf_ctlq_msg *msg)
+{
+	union cpfl_rule_cfg_pkt_record *blob;
+	struct cpfl_rule_cfg_data cfg = {0};
+	uint16_t cfg_ctrl;
+	enum cpfl_ctlq_rule_cfg_opc opc = 0;
+	const struct cpfl_tdi_table_key_obj *key = &rinfo->kobj;
+	const struct cpfl_tdi_action_obj *action = &rinfo->aobj;
+
+	blob = (void *)dma->va;
+	memset(blob, 0, sizeof(*blob));
+
+	cfg_ctrl = CPFL_GET_MEV_SEM_RULE_CFG_CTRL(hb->profile[0], hb->sem.sub_profile, 0, 0);
+
+	switch (op) {
+	case CPFL_TDI_TABLE_ENTRY_OP_ADD:
+		cpfl_prep_sem_rule_blob(key->buf, key->buf_len, action->buf, action->buf_len,
+					cfg_ctrl, blob);
+		opc = cpfl_ctlq_sem_add_rule;
+		break;
+	case CPFL_TDI_TABLE_ENTRY_OP_DEL:
+		cpfl_prep_sem_rule_blob(key->buf, key->buf_len, NULL, 0, cfg_ctrl, blob);
+		opc = cpfl_ctlq_sem_del_rule;
+		break;
+	case CPFL_TDI_TABLE_ENTRY_OP_QRY:
+		cpfl_prep_sem_rule_blob(key->buf, key->buf_len, NULL, 0, cfg_ctrl, blob);
+		opc = cpfl_ctlq_sem_query_rule;
+		break;
+	default:
+		PMD_DRV_LOG(ERR, "Unknown ops, this is a bug.");
+		break;
+	}
+
+	cpfl_fill_rule_cfg_data_common(opc,
+				       rinfo->cookie,
+				       rinfo->vsi,
+				       rinfo->port_num,
+				       rinfo->host_id,
+				       0, /* time_sel */
+				       0, /* time_sel_val */
+				       0, /* cache_wr_thru */
+				       rinfo->resp_req,
+				       sizeof(union cpfl_rule_cfg_pkt_record),
+				       dma,
+				       &cfg.common);
+
+	cpfl_prep_rule_desc(&cfg, msg);
+}
+
+static void
+cpfl_tdi_pack_mod_entry(struct cpfl_tdi_rule_info *rinfo,
+			enum cpfl_tdi_table_entry_op op,
+			struct idpf_dma_mem *dma,
+			struct idpf_ctlq_msg *msg)
+{
+	union cpfl_rule_cfg_pkt_record *blob;
+	struct cpfl_rule_cfg_data cfg = {0};
+	uint32_t mod_index;
+	enum cpfl_ctlq_rule_cfg_opc opc = 0;
+	const struct cpfl_tdi_table_key_obj *key = &rinfo->kobj;
+	const struct cpfl_tdi_action_obj *action = &rinfo->aobj;
+
+	blob = (void *)dma->va;
+	memset(blob, 0, sizeof(*blob));
+
+	mod_index = *(const uint32_t *)&key->buf[0];
+
+	switch (op) {
+	case CPFL_TDI_TABLE_ENTRY_OP_ADD:
+		cpfl_fill_rule_mod_content(CPFL_MOD_OBJ_SIZE_DEF, CPFL_PIN_MOD_CONTENT_DEF,
+					   mod_index, &cfg.ext.mod_content);
+
+		rte_memcpy(blob->mod_blob, action->buf, action->buf_len);
+		opc = cpfl_ctlq_mod_add_update_rule;
+		break;
+	case CPFL_TDI_TABLE_ENTRY_OP_QRY:
+		opc = cpfl_ctlq_mod_query_rule;
+		break;
+	default:
+		break;
+	}
+
+	cpfl_fill_rule_cfg_data_common(opc,
+				       CPFL_MOD_COOKIE_DEF,
+				       0, /* vsi_id not used for mod */
+				       CPFL_PORT_NUM_DEF,
+				       0,
+				       0,	    /* time_sel */
+				       0,			    /* time_sel_val */
+				       0,			    /* cache_wr_thru */
+				       CPFL_RESP_REQ_DEF,
+				       sizeof(union cpfl_rule_cfg_pkt_record),
+				       dma,
+				       &cfg.common);
+
+	cpfl_prep_rule_desc(&cfg, msg);
+}
+
+static int
+cpfl_tdi_rule_process(struct cpfl_itf *itf,
+		      struct idpf_ctlq_info *tx_cq,
+		      struct idpf_ctlq_info *rx_cq,
+		      struct cpfl_tdi_rule_info *rinfo,
+		      int rule_num,
+		      enum cpfl_tdi_table_entry_op op)
+{
+	const struct cpfl_tdi_table_key_obj *kobj;
+	struct idpf_hw *hw = &itf->adapter->base.hw;
+	struct cpfl_tdi_ma_hardware_block *hb;
+	const struct cpfl_tdi_table *table;
+	int ret = 0;
+
+	if (rule_num == 0)
+		return 0;
+
+	kobj = &rinfo->kobj;
+
+	table = kobj->tnode->table;
+	if (table->match_attributes.hardware_block_num == 0) {
+		PMD_DRV_LOG(ERR, "No valid hardware block be specified");
+		return -EINVAL;
+	}
+	hb = &table->match_attributes.hardware_blocks[0];
+	switch (hb->hw_block) {
+	case CPFL_TDI_HW_BLOCK_SEM:
+		cpfl_tdi_pack_sem_entry(rinfo, hb, op, &itf->dma[0], &itf->msg[0]);
+		break;
+	case CPFL_TDI_HW_BLOCK_MOD:
+		if (op == CPFL_TDI_TABLE_ENTRY_OP_DEL)
+			/* do nothing */
+			return 0;
+		cpfl_tdi_pack_mod_entry(rinfo, op, &itf->dma[0], &itf->msg[0]);
+		break;
+	default:
+		PMD_DRV_LOG(ERR, "Unsupported hardware block %d", hb->hw_block);
+		return -EINVAL;
+	}
+
+	ret = cpfl_send_ctlq_msg(hw, tx_cq, 1, itf->msg);
+	if (ret != 0) {
+		PMD_DRV_LOG(ERR, "Failed to send control message");
+		return -EINVAL;
+	}
+
+	ret = cpfl_receive_ctlq_msg(hw, rx_cq, 1, itf->msg);
+	if (ret) {
+		PMD_INIT_LOG(ERR, "Failed to update rule");
+		return -EINVAL;
+	}
+	return 0;
+}
+
+static int
+cpfl_tdi_fxp_rule_create(struct rte_eth_dev *dev,
+			 struct rte_flow *flow,
+			 void *meta,
+			 struct rte_flow_error *error)
+{
+	struct cpfl_itf *itf = CPFL_DEV_TO_ITF(dev);
+	struct cpfl_adapter_ext *ad = itf->adapter;
+	int ret;
+	uint32_t cpq_id = 0;
+	struct cpfl_vport *vport;
+	struct cpfl_repr *repr;
+	struct cpfl_tdi_rule_info *rinfo = meta;
+
+	if (!rinfo)
+		goto err;
+
+	if (itf->type == CPFL_ITF_TYPE_VPORT) {
+		vport = (struct cpfl_vport *)itf;
+		/* Every vport has one pair control queues configured to handle message.
+		 * Even index is tx queue and odd index is rx queue.
+		 */
+		cpq_id = vport->base.devarg_id * 2;
+	} else if (itf->type == CPFL_ITF_TYPE_REPRESENTOR) {
+		repr = (struct cpfl_repr *)itf;
+		cpq_id = ((repr->repr_id.pf_id + repr->repr_id.vf_id) & (CPFL_TX_CFGQ_NUM - 1)) * 2;
+	} else {
+		rte_flow_error_set(error, EINVAL, RTE_FLOW_ERROR_TYPE_HANDLE, NULL,
+				   "fail to find correct control queue");
+		return -rte_errno;
+	}
+
+	ret = cpfl_tdi_rule_process(itf, ad->ctlqp[cpq_id], ad->ctlqp[cpq_id + 1], rinfo, 1,
+				    CPFL_TDI_TABLE_ENTRY_OP_ADD);
+	if (ret)
+		goto err;
+
+	flow->rule = rinfo;
+
+	return 0;
+
+err:
+	return rte_flow_error_set(error, EINVAL, RTE_FLOW_ERROR_TYPE_HANDLE, NULL,
+				  "cpfl filter create flow fail");
+}
+
+static int
+cpfl_tdi_fxp_rule_destroy(struct rte_eth_dev *dev,
+			  struct rte_flow *flow,
+			  struct rte_flow_error *error)
+{
+	struct cpfl_itf *itf = CPFL_DEV_TO_ITF(dev);
+	struct cpfl_adapter_ext *ad = itf->adapter;
+	struct cpfl_vport *vport;
+	struct cpfl_repr *repr;
+	struct cpfl_tdi_rule_info *rinfo = (struct cpfl_tdi_rule_info *)flow->rule;
+	int ret = 0;
+	uint32_t cpq_id = 0;
+
+	if (itf->type == CPFL_ITF_TYPE_VPORT) {
+		vport = (struct cpfl_vport *)itf;
+		cpq_id = vport->base.devarg_id * 2;
+	} else if (itf->type == CPFL_ITF_TYPE_REPRESENTOR) {
+		repr = (struct cpfl_repr *)itf;
+		cpq_id = ((repr->repr_id.pf_id + repr->repr_id.vf_id) & (CPFL_TX_CFGQ_NUM - 1)) * 2;
+	} else {
+		rte_flow_error_set(error, EINVAL, RTE_FLOW_ERROR_TYPE_HANDLE, NULL,
+				   "fail to find correct control queue");
+		ret = -rte_errno;
+		goto err;
+	}
+
+	ret = cpfl_tdi_rule_process(itf, ad->ctlqp[cpq_id], ad->ctlqp[cpq_id + 1], rinfo, 1,
+				    CPFL_TDI_TABLE_ENTRY_OP_DEL);
+	if (ret)
+		goto err;
+
+err:
+	rte_free(rinfo);
+	flow->rule = NULL;
+	return ret;
+}
+
+void
+cpfl_tdi_free_table_list(struct cpfl_flow_parser *flow_parser)
+{
+	struct cpfl_tdi_table_node *node;
+
+	while ((node = TAILQ_FIRST(&flow_parser->tdi_table_list))) {
+		TAILQ_REMOVE(&flow_parser->tdi_table_list, node, next);
+		rte_free(node);
+	}
+}
+
+static int
+cpfl_tdi_build_table_list(struct cpfl_flow_parser *flow_parser)
+{
+	struct cpfl_tdi_program *prog = flow_parser->p4_parser;
+	int i;
+
+	TAILQ_INIT(&flow_parser->tdi_table_list);
+
+	for (i = 0; i < prog->table_num; i++) {
+		struct cpfl_tdi_table *table = &prog->tables[i];
+		struct cpfl_tdi_table_node *node;
+
+		node = rte_zmalloc(NULL, sizeof(struct cpfl_tdi_table_node), 0);
+		if (node == NULL)
+			return -ENOMEM;
+
+		node->table = table;
+		TAILQ_INSERT_TAIL(&flow_parser->tdi_table_list, node, next);
+	}
+
+	return 0;
+}
+
+static void
+cpfl_tdi_build_hw_action_buf(struct cpfl_tdi_action_node *node)
+{
+	int i, j;
+
+	for (i = 0; i < node->format->hw_action_num; i++) {
+		struct cpfl_tdi_hw_action *ha = &node->format->hw_actions_list[i];
+		struct cpfl_tdi_hw_action_parameter *hap = &ha->parameters[0];
+		uint32_t msk = UINT32_MAX;
+		uint32_t action_code = 0;
+		uint16_t offset;
+		uint16_t size;
+
+		switch (ha->action_code) {
+		case CPFL_TDI_ACTION_CODE_SET10_1b:
+		case CPFL_TDI_ACTION_CODE_SET1_16b:
+		case CPFL_TDI_ACTION_CODE_SET1A_24b:
+		case CPFL_TDI_ACTION_CODE_SET1B_24b:
+			offset = node->buf_len;
+			size = 4;
+			node->buf_len += 4; /* 32 bit action encode */
+			break;
+		default:
+			continue;
+		}
+
+		if (ha->parameter_num == 0) {
+			switch (ha->action_code) {
+			case CPFL_TDI_ACTION_CODE_SET10_1b:
+				switch (ha->index) {
+				case 0: /* drop */
+					action_code = CPFL_ACT_MAKE_1B(ha->prec,
+								       CPFL_ACT_1B_OP_DROP,
+								       ha->value & ha->mask);
+					break;
+				default:
+					continue;
+				}
+				break;
+			case CPFL_TDI_ACTION_CODE_SET1A_24b:
+				switch (ha->index) {
+				case 9: /* mod profile */
+					action_code =
+					    cpfl_act_mod_profile(ha->prec, ha->mod_profile, 0, 0, 0,
+								 CPFL_ACT_MOD_PROFILE_PREFETCH_256B)
+						.data;
+					break;
+				case 8: /* queue */
+					/* todo */
+					break;
+				default:
+					break;
+				}
+				break;
+			case CPFL_TDI_ACTION_CODE_SET1B_24b: /* set metadata */
+				switch (ha->setmd_action_code) {
+				case CPFL_TDI_SETMD_ACTION_CODE_SET_8b:
+					action_code =
+					    cpfl_act_set_md8(ha->index, ha->prec, ha->type_id,
+							     ha->offset, ha->value, ha->mask)
+						.data;
+					break;
+				default:
+					break;
+				}
+				break;
+			default:
+				continue;
+			}
+
+			rte_memcpy(&node->init_buf[offset], &action_code, 4);
+			rte_memcpy(&node->query_msk[offset], &msk, 4);
+			continue;
+		} else {
+			uint32_t code_msk = 0;
+			uint32_t dummy = 0;
+			uint32_t action_code = cpfl_tdi_to_action_code(ha, (void *)&dummy);
+
+			switch (ha->action_code) {
+			case CPFL_TDI_ACTION_CODE_SET10_1b:
+				code_msk = ~CPFL_ACT_1B_VAL_M;
+				break;
+			case CPFL_TDI_ACTION_CODE_SET1_16b:
+				code_msk = ~CPFL_ACT_16B_VAL_M;
+				break;
+			case CPFL_TDI_ACTION_CODE_SET1A_24b:
+				code_msk = ~CPFL_ACT_24B_A_VAL_M;
+				break;
+			case CPFL_TDI_ACTION_CODE_SET1B_24b: /* set metadata */
+				code_msk = ~CPFL_ACT_24B_B_VAL_M;
+				break;
+			default:
+				continue;
+			}
+
+			rte_memcpy(&node->init_buf[offset], &action_code, 4);
+			rte_memcpy(&node->query_msk[offset], &code_msk, 4);
+		}
+
+		/* only check the first parameter */
+		for (j = 0; j < node->format->immediate_field_num; j++) {
+			struct cpfl_tdi_immediate_field *imf = &node->format->immediate_fields[j];
+
+			if (imf->param_handle == hap->param_handle) {
+				node->params[j].id = imf->param_handle;
+				node->params[j].offset = offset;
+				node->params[j].size = size;
+			}
+		}
+	}
+}
+
+static void
+cpfl_tdi_build_mod_content_format_buf(struct cpfl_tdi_action_node *node)
+{
+	int i, j;
+	uint8_t val_buf[CPFL_TDI_VALUE_SIZE_MAX] = {0};
+	uint8_t msk_buf[CPFL_TDI_VALUE_SIZE_MAX] = {0};
+
+	for (i = 0; i < node->format->mod_content_format.mod_field_num; i++) {
+		struct cpfl_tdi_mod_field *mf = &node->format->mod_content_format.mod_fields[i];
+		uint16_t size = (uint16_t)((mf->start_bit_offset + mf->bit_width) >> 3);
+
+		node->buf_len += size;
+
+		if (mf->type == CPFL_TDI_MOD_FIELD_TYPE_CONSTANT) {
+			rte_memcpy(val_buf, mf->value, size);
+			cpfl_tdi_shift_left(val_buf, size, mf->start_bit_offset);
+			cpfl_tdi_init_msk_buf(msk_buf, size, mf->bit_width);
+			cpfl_tdi_shift_left(msk_buf, size, mf->start_bit_offset);
+			cpfl_tdi_or_buf(&node->init_buf[mf->byte_array_index], size, val_buf,
+					msk_buf);
+			continue;
+		}
+
+		for (j = 0; j < node->format->immediate_field_num; j++) {
+			struct cpfl_tdi_immediate_field *imf = &node->format->immediate_fields[j];
+
+			if (imf->param_handle == mf->param_handle) {
+				node->params[j].id = imf->param_handle;
+				node->params[j].offset = mf->byte_array_index;
+				node->params[j].size = size;
+				break;
+			}
+		}
+	}
+}
+
+static void
+cpfl_tdi_build_action_params(struct cpfl_tdi_action_node *node)
+{
+	node->buf_len = 0;
+	/* build mod content layout */
+	if (node->format->mod_content_format.mod_field_num > 0) {
+		cpfl_tdi_build_mod_content_format_buf(node);
+		/* build action buffer layout */
+	} else if (node->format->hw_action_num > 0) {
+		cpfl_tdi_build_hw_action_buf(node);
+	}
+}
+
+static void
+cpfl_tdi_free_action_list(struct cpfl_flow_parser *flow_parser)
+{
+	struct cpfl_tdi_action_node *action;
+
+	while ((action = TAILQ_FIRST(&flow_parser->tdi_action_list))) {
+		TAILQ_REMOVE(&flow_parser->tdi_action_list, action, next);
+		rte_free(action);
+	}
+}
+
+static int
+cpfl_tdi_build_action_list(struct cpfl_flow_parser *flow_parser)
+{
+#define _HASH_TABLE_NAME_SIZE 32
+#define _HASH_TABLE_ENTRY_SIZE 1024
+	struct cpfl_tdi_program *prog = flow_parser->p4_parser;
+	char hname[_HASH_TABLE_NAME_SIZE];
+	struct rte_hash *ht;
+	int ret = 0;
+	int i, j, k;
+
+	snprintf(hname, _HASH_TABLE_NAME_SIZE, "cpfl_tdi_action_hash");
+
+	struct rte_hash_parameters params = {
+	    .name = hname,
+	    .entries = _HASH_TABLE_ENTRY_SIZE,
+	    .key_len = sizeof(uint32_t),
+	    .hash_func = rte_hash_crc,
+	    .hash_func_init_val = 0,
+	    .socket_id = SOCKET_ID_ANY,
+	    .extra_flag = 0,
+	};
+
+	ht = rte_hash_create(&params);
+
+	if (ht == NULL) {
+		PMD_INIT_LOG(ERR, "Failed to create hash table %s", hname);
+		return -EINVAL;
+	}
+
+	TAILQ_INIT(&flow_parser->tdi_action_list);
+
+	for (i = 0; i < prog->table_num; i++) {
+		struct cpfl_tdi_table *table = &prog->tables[i];
+
+		for (j = 0; j < table->action_num; j++) {
+			struct cpfl_tdi_action *action = &table->actions[j];
+			uint32_t handle = action->handle;
+			struct cpfl_tdi_action_node *node;
+
+			/* skip if already exist */
+			if (rte_hash_lookup(ht, &handle) >= 0)
+				continue;
+
+			node = rte_zmalloc(NULL, sizeof(struct cpfl_tdi_action_node), 0);
+			if (node == NULL) {
+				ret = -ENOMEM;
+				goto err;
+			}
+
+			node->action = action;
+			ret = rte_hash_add_key_data(ht, &handle, node);
+			if (ret != 0)
+				goto err;
+
+			TAILQ_INSERT_TAIL(&flow_parser->tdi_action_list, node, next);
+		}
+
+		for (j = 0; j < table->match_attributes.hardware_block_num; j++) {
+			struct cpfl_tdi_ma_hardware_block *hb =
+			    &table->match_attributes.hardware_blocks[j];
+
+			for (k = 0; k < hb->action_format_num; k++) {
+				struct cpfl_tdi_action_format *format = &hb->action_format[k];
+				uint32_t handle = format->action_handle;
+				struct cpfl_tdi_action_node *node = NULL;
+
+				if (rte_hash_lookup_data(ht, &handle, (void **)&node) >= 0) {
+					node->hw_block_type = hb->hw_block;
+					if (node->format == NULL) {
+						node->format = format;
+						cpfl_tdi_build_action_params(node);
+					}
+				}
+			}
+		}
+	}
+
+	rte_hash_free(ht);
+
+	return 0;
+
+err:
+
+	rte_hash_free(ht);
+	cpfl_tdi_free_action_list(flow_parser);
+	return ret;
+}
+
+static int
+cpfl_tdi_table_info_get(struct rte_eth_dev *dev,
+			uint32_t table_id,
+			struct cpfl_tdi_table_node **table_node)
+{
+	struct cpfl_itf *itf = CPFL_DEV_TO_ITF(dev);
+	struct cpfl_adapter_ext *adapter = itf->adapter;
+	struct cpfl_tdi_table_node *node;
+	void *temp;
+	int i;
+
+	RTE_TAILQ_FOREACH_SAFE(node, &adapter->flow_parser.tdi_table_list, next, temp)
+	{
+		const struct cpfl_tdi_table *table = node->table;
+
+		if (table->handle != table_id)
+			continue;
+
+		if (table->match_key_field_num > CPFL_TDI_KEY_FIELD_NUM_MAX) {
+			PMD_DRV_LOG(ERR, "Too many fields (%d) in tdi table %s",
+				    table->match_key_field_num, table->name);
+			goto err;
+		}
+
+		if (table->action_num > CPFL_TDI_ACTION_SPEC_NUM_MAX) {
+			PMD_DRV_LOG(ERR, "Too many action types (%d) in tdi table %s",
+				    table->action_num, table->name);
+			goto err;
+		}
+
+		/* match_key_field first */
+		if (table->match_key_format_num == 0) {
+			node->buf_len = 0;
+			for (i = 0; i < table->match_key_field_num; i++) {
+				struct cpfl_tdi_match_key_field *field =
+				    &table->match_key_fields[i];
+				uint32_t size = (uint16_t)(field->bit_width >> 3);
+
+				node->params[i].id = field->index;
+				node->params[i].offset =
+				    node->buf_len; /* equal with field->position */
+				node->params[i].size = size;
+				node->buf_len += size;
+			}
+		} else {
+			for (i = 0; i < table->match_key_format_num; i++) {
+				struct cpfl_tdi_match_key_format *format =
+				    &table->match_key_format[i];
+
+				node->buf_len =
+				    format->byte_array_index + (uint16_t)(format->bit_width >> 3);
+				node->params[i].id = format->match_key_handle;
+				node->params[i].offset = format->byte_array_index;
+				node->params[i].size = (uint16_t)(format->bit_width >> 3);
+			}
+		}
+		*table_node = node;
+
+		return 0;
+	}
+
+err:
+	return -EINVAL;
+}
+
+static int
+cpfl_tdi_table_key_node_init(struct rte_eth_dev *dev __rte_unused,
+			     struct cpfl_tdi_table_node *node,
+			     struct cpfl_tdi_table_key_obj *kobj)
+{
+	kobj->tnode = node;
+	kobj->buf_len = node->buf_len;
+	kobj->sem.pin_to_cache = CPFL_PIN_TO_CACHE_DEF;
+	kobj->sem.fixed_fetch = CPFL_FIXED_FETCH_DEF;
+	return 0;
+}
+
+static int
+cpfl_tdi_table_key_create(struct rte_eth_dev *dev,
+			  uint32_t table_id,
+			  struct cpfl_tdi_table_node **table_node,
+			  struct cpfl_tdi_table_key_obj *kobj)
+{
+	int ret;
+
+	if (!kobj)
+		return -EINVAL;
+
+	ret = cpfl_tdi_table_info_get(dev, table_id, table_node);
+	if (ret != 0)
+		return -EINVAL;
+
+	ret = cpfl_tdi_table_key_node_init(dev, *table_node, kobj);
+	if (ret != 0)
+		return -EINVAL;
+
+	return 0;
+}
+
+static int
+cpfl_tdi_table_key_field_info_get(__rte_unused struct rte_eth_dev *dev,
+				  struct cpfl_tdi_table_node *node,
+				  uint32_t field_id,
+				  struct cpfl_tdi_table_key_field_info **key_field_info)
+{
+	const struct cpfl_tdi_table *table = node->table;
+	int i, j;
+
+	for (i = 0; i < table->match_key_field_num; i++) {
+		struct cpfl_tdi_match_key_field *field = &table->match_key_fields[i];
+		struct cpfl_tdi_table_key_field_info *tkfinfo;
+
+		if (field->index != field_id)
+			continue;
+
+		tkfinfo = rte_zmalloc(NULL, sizeof(struct cpfl_tdi_table_key_field_info), 0);
+		if (tkfinfo == NULL)
+			return -ENOMEM;
+
+		tkfinfo->field = field;
+		tkfinfo->param = node->params[i];
+		*key_field_info = tkfinfo;
+
+		/* adjust byte width */
+		for (j = 0; j < table->match_key_format_num; j++) {
+			struct cpfl_tdi_match_key_format *format = &table->match_key_format[j];
+
+			if (format->match_key_handle != field_id)
+				continue;
+			tkfinfo->format = format;
+			return 0;
+		}
+
+		return 0;
+	}
+
+	return -EINVAL;
+}
+
+static int
+_cpfl_tdi_table_key_field_set(struct rte_eth_dev *dev __rte_unused,
+			      struct cpfl_tdi_table_key_obj *kobj,
+			      struct cpfl_tdi_table_key_field_info *tkfinfo,
+			      const uint8_t *value,
+			      uint16_t size)
+{
+	struct cpfl_tdi_param_info *pi = &tkfinfo->param;
+	uint8_t *target = &kobj->buf[pi->offset];
+
+	rte_memcpy(target, value, size);
+
+	/* Need to fix as this will overwrite. */
+	if (tkfinfo->format != NULL)
+		cpfl_tdi_shift_left(target, pi->size, tkfinfo->format->start_bit_offset);
+
+	return 0;
+}
+
+static int
+cpfl_tdi_table_key_field_set(struct rte_eth_dev *dev,
+			     struct cpfl_tdi_table_node *table_node,
+			     struct cpfl_tdi_table_key_obj *kobj,
+			     uint32_t field_id,
+			     const uint8_t *value,
+			     uint16_t size)
+{
+	struct cpfl_tdi_table_key_field_info *key_field_info;
+	int ret;
+
+	if (!kobj || !value)
+		return -EINVAL;
+
+	ret = cpfl_tdi_table_key_field_info_get(dev, table_node, field_id, &key_field_info);
+	if (ret != 0)
+		return -EINVAL;
+
+	if (key_field_info->field->match_type != CPFL_TDI_MATCH_TYPE_EXACT)
+		return -EINVAL;
+
+	ret = _cpfl_tdi_table_key_field_set(dev, kobj, key_field_info, value, size);
+	if (ret != 0)
+		return -EINVAL;
+
+	return 0;
+}
+
+static int
+cpfl_tdi_action_spec_info_get(struct rte_eth_dev *dev,
+			      uint32_t spec_id,
+			      struct cpfl_tdi_action_node **action_node)
+{
+	struct cpfl_itf *itf = CPFL_DEV_TO_ITF(dev);
+	struct cpfl_adapter_ext *adapter = itf->adapter;
+	struct cpfl_tdi_action_node *node;
+	void *temp;
+
+	RTE_TAILQ_FOREACH_SAFE(node, &adapter->flow_parser.tdi_action_list, next, temp)
+	{
+		if (node->action->handle != spec_id)
+			continue;
+		*action_node = node;
+
+		return 0;
+	}
+
+	return -EINVAL;
+}
+
+static int
+cpfl_tdi_action_obj_init(struct rte_eth_dev *dev __rte_unused,
+			 struct cpfl_tdi_table_node *tnode,
+			 struct cpfl_tdi_action_node *anode,
+			 struct cpfl_tdi_action_obj *aobj)
+{
+	aobj->table = tnode->table;
+	aobj->node = anode;
+	aobj->buf_len = anode->buf_len;
+
+	rte_memcpy(aobj->buf, anode->init_buf, anode->buf_len);
+
+	return 0;
+}
+
+static inline bool __rte_unused
+verify_action(struct cpfl_tdi_table_node *table_node, uint32_t spec_id)
+{
+	int i;
+	const struct cpfl_tdi_table *table = table_node->table;
+
+	for (i = 0; i < table->action_num; i++)
+		if (spec_id == table->actions[i].handle)
+			return true;
+
+	return false;
+}
+
+static int
+cpfl_tdi_action_node_get_by_spec_id(struct rte_eth_dev *dev,
+				    uint32_t table_id __rte_unused,
+				    uint32_t spec_id,
+				    struct cpfl_tdi_action_node **action_node)
+{
+	return cpfl_tdi_action_spec_info_get(dev, spec_id, action_node);
+}
+
+static int
+cpfl_tdi_action_spec_field_info_get(struct rte_eth_dev *dev __rte_unused,
+				    struct cpfl_tdi_action_node *anode,
+				    uint32_t field_id,
+				    struct cpfl_tdi_action_spec_field_info **info)
+{
+	int ret = -EINVAL;
+	int i, j;
+
+	if (anode->format == NULL)
+		goto err;
+
+	for (i = 0; i < anode->format->immediate_field_num; i++) {
+		struct cpfl_tdi_immediate_field *field = &anode->format->immediate_fields[i];
+		struct cpfl_tdi_action_spec_field_info *asfinfo;
+
+		if (field->param_handle != field_id)
+			continue;
+
+		asfinfo = rte_malloc(NULL, sizeof(struct cpfl_tdi_action_spec_field_info), 0);
+		if (asfinfo == NULL) {
+			ret = -ENOMEM;
+			goto err;
+		}
+
+		asfinfo->field = field;
+		asfinfo->param = anode->params[i];
+
+		for (j = 0; j < anode->format->mod_content_format.mod_field_num; j++) {
+			struct cpfl_tdi_mod_field *mod_field =
+			    &anode->format->mod_content_format.mod_fields[j];
+
+			if (mod_field->type != CPFL_TDI_MOD_FIELD_TYPE_PARAMETER)
+				continue;
+
+			if (mod_field->param_handle != field_id)
+				continue;
+
+			asfinfo->mod_field = mod_field;
+		}
+
+		for (i = 0; i < anode->format->hw_action_num; i++) {
+			struct cpfl_tdi_hw_action *hw_action = &anode->format->hw_actions_list[i];
+
+			if (hw_action->parameter_num == 0)
+				continue;
+
+			if (hw_action->parameters[0].param_handle != field_id)
+				continue;
+
+			asfinfo->hw_action = hw_action;
+		}
+
+		*info = asfinfo;
+
+		return 0;
+	}
+	PMD_DRV_LOG(WARNING, "No immediate_field_num!!!");
+err:
+	return ret;
+}
+
+static int
+_cpfl_tdi_action_field_set(struct rte_eth_dev *dev __rte_unused,
+			   struct cpfl_tdi_action_obj *aobj,
+			   struct cpfl_tdi_action_spec_field_info *asfinfo,
+			   const uint8_t *value,
+			   uint16_t size)
+{
+	struct cpfl_tdi_action_node *node = aobj->node;
+	struct cpfl_tdi_param_info *pi = &asfinfo->param;
+	uint8_t val_buf[CPFL_TDI_VALUE_SIZE_MAX] = {0};
+	uint8_t msk_buf[CPFL_TDI_VALUE_SIZE_MAX] = {0};
+
+	rte_memcpy(val_buf, value, size);
+
+	if (node->format->mod_content_format.mod_field_num > 0) {
+		struct cpfl_tdi_mod_field *mf = asfinfo->mod_field;
+
+		cpfl_tdi_shift_left(val_buf, pi->size, mf->start_bit_offset);
+		cpfl_tdi_init_msk_buf(msk_buf, pi->size, mf->bit_width);
+		cpfl_tdi_shift_left(msk_buf, pi->size, mf->start_bit_offset);
+		cpfl_tdi_or_buf(&aobj->buf[pi->offset], pi->size, val_buf, msk_buf);
+	} else {
+		struct cpfl_tdi_hw_action *ha = asfinfo->hw_action;
+		uint32_t action_code = cpfl_tdi_to_action_code(ha, val_buf);
+
+		rte_memcpy(&aobj->buf[pi->offset], &action_code, 4);
+	}
+
+	return 0;
+}
+
+static int
+cpfl_tdi_action_field_set(struct rte_eth_dev *dev,
+			  struct cpfl_tdi_action_obj *aobj,
+			  struct cpfl_tdi_action_node *action_node,
+			  uint32_t field_id,
+			  const struct rte_flow_action_prog_argument *arg)
+{
+	struct cpfl_tdi_action_spec_field_info *asfinfo;
+	enum rte_flow_action_type action_type;
+	/* used when action is PORT_REPRESENTOR type */
+	struct cpfl_itf *dst_itf;
+	uint16_t dev_id; /* vsi id */
+	uint8_t value;
+	bool is_vsi;
+	int ret;
+
+	if (!aobj || !arg)
+		return -EINVAL;
+
+	ret = cpfl_tdi_action_spec_field_info_get(dev, action_node, field_id, &asfinfo);
+	if (ret != 0)
+		return -EINVAL;
+
+	if (!strcmp(arg->name, "port_representor"))
+		action_type = RTE_FLOW_ACTION_TYPE_PORT_REPRESENTOR;
+	else if (!strcmp(arg->name, "represented_port"))
+		action_type = RTE_FLOW_ACTION_TYPE_REPRESENTED_PORT;
+	else
+		return _cpfl_tdi_action_field_set(dev, aobj, asfinfo, arg->value, arg->size);
+
+	if (arg->size != 1)
+		return -EINVAL;
+
+	dst_itf = cpfl_get_itf_by_port_id(arg->value[0]);
+	if (!dst_itf)
+		goto err;
+
+	is_vsi = (action_type == RTE_FLOW_ACTION_TYPE_PORT_REPRESENTOR ||
+		  dst_itf->type == CPFL_ITF_TYPE_REPRESENTOR);
+	if (is_vsi)
+		dev_id = cpfl_get_vsi_id(dst_itf);
+	else
+		dev_id = cpfl_get_port_id(dst_itf);
+
+	if (dev_id == CPFL_INVALID_HW_ID)
+		goto err;
+
+	value = (uint8_t)dev_id;
+
+	return _cpfl_tdi_action_field_set(dev, aobj, asfinfo, &value, arg->size);
+err:
+	PMD_DRV_LOG(ERR, "Can not get dev id.");
+	return -EINVAL;
+
+	return 0;
+}
+
+int
+cpfl_tdi_build(struct cpfl_flow_parser *flow_parser)
+{
+	int ret;
+
+	ret = cpfl_tdi_build_table_list(flow_parser);
+	if (ret != 0) {
+		PMD_INIT_LOG(ERR, "Failed to build tdi table list");
+		return ret;
+	}
+
+	ret = cpfl_tdi_build_action_list(flow_parser);
+	if (ret != 0) {
+		PMD_INIT_LOG(ERR, "Failed to build tdi action list");
+		cpfl_tdi_free_table_list(flow_parser);
+		return ret;
+	}
+
+	return 0;
+}
+
+static bool
+cpfl_flow_items_all_is_flex(const struct rte_flow_item pattern[])
+{
+	int i;
+
+	for (i = 0; pattern[i].type != RTE_FLOW_ITEM_TYPE_END; i++) {
+		if (pattern[i].type != RTE_FLOW_ITEM_TYPE_FLEX)
+			return false;
+	}
+	return true;
+}
+
+static void
+cpfl_fill_rinfo_default_value(struct cpfl_tdi_rule_info *rinfo)
+{
+	if (cpfl_tdi_rule_cookie == ~0llu)
+		cpfl_tdi_rule_cookie = CPFL_COOKIE_DEF;
+	rinfo->cookie = cpfl_tdi_rule_cookie++;
+	rinfo->host_id = CPFL_HOST_ID_DEF;
+	rinfo->port_num = CPFL_PORT_NUM_DEF;
+	rinfo->resp_req = CPFL_RESP_REQ_DEF;
+	rinfo->vsi = CPFL_VSI_DEF;
+	rinfo->clear_mirror_1st_state = CPFL_CLEAR_MIRROR_1ST_STATE_DEF;
+}
+
+static int
+cpfl_tdi_parse_pattern(struct rte_eth_dev *dev,
+		       const struct rte_flow_item pattern[],
+		       uint32_t table_id,
+		       struct cpfl_tdi_table_node **table_node,
+		       struct cpfl_tdi_table_key_obj *kobj)
+{
+	const struct rte_flow_item_flex *flex;
+	int i, ret;
+
+	/* Create the key */
+	ret = cpfl_tdi_table_key_create(dev, table_id, table_node, kobj);
+	if (ret) {
+		PMD_INIT_LOG(ERR, "Failed to create table key obj.");
+		return -EINVAL;
+	}
+
+	for (i = 0; pattern[i].type != RTE_FLOW_ITEM_TYPE_END; i++) {
+		if (pattern[i].type != RTE_FLOW_ITEM_TYPE_FLEX) {
+			PMD_INIT_LOG(ERR, "All pattern type should be RTE_FLOW_ITEM_TYPE_FLEX.");
+			return -EINVAL;
+		}
+		flex = pattern[i].spec;
+		/* Set the key fields */
+		ret = cpfl_tdi_table_key_field_set(dev, *table_node, kobj, i, flex->pattern,
+						   flex->length);
+		if (ret) {
+			PMD_INIT_LOG(ERR, "Failed to set table key field.");
+			return -EINVAL;
+		}
+	}
+	return 0;
+}
+
+static int
+cpfl_tdi_parse_action(struct rte_eth_dev *dev,
+		      const struct rte_flow_action actions[],
+		      uint32_t table_id,
+		      struct cpfl_tdi_table_node *table_node,
+		      struct cpfl_tdi_action_node **action_node,
+		      struct cpfl_tdi_action_obj *aobj)
+{
+	const struct rte_flow_action_prog *prog;
+	const struct rte_flow_action_prog_argument *arg;
+	uint32_t action_spec_id;
+	int i, ret;
+	uint32_t j;
+
+	for (i = 0; actions[i].type != RTE_FLOW_ACTION_TYPE_END; i++) {
+		if (actions[i].type != RTE_FLOW_ACTION_TYPE_PROG)
+			continue;
+
+		prog = actions[i].conf;
+		action_spec_id = atoi(prog->name);
+		/* Get action node */
+		ret =
+		    cpfl_tdi_action_node_get_by_spec_id(dev, table_id, action_spec_id, action_node);
+		if (ret) {
+			PMD_INIT_LOG(ERR, "Failed to get action node.");
+			return -EINVAL;
+		}
+
+		ret = cpfl_tdi_action_obj_init(dev, table_node, *action_node, aobj);
+		if (ret != 0) {
+			PMD_INIT_LOG(ERR, "Failed to init action obj.");
+			return -EINVAL;
+		}
+
+		for (j = 0; j < prog->args_num; j++) {
+			arg = &prog->args[j];
+			/* Set the action fields */
+			ret = cpfl_tdi_action_field_set(dev, aobj, *action_node, j, arg);
+			if (ret) {
+				PMD_INIT_LOG(ERR, "Failed to set action field.");
+				return -EINVAL;
+			}
+		}
+	}
+	return 0;
+}
+
+static int
+cpfl_tdi_parse_pattern_action(struct rte_eth_dev *dev,
+			      const struct rte_flow_attr *attr,
+			      const struct rte_flow_item pattern[],
+			      const struct rte_flow_action actions[],
+			      void **meta)
+{
+	struct cpfl_itf *itf = CPFL_DEV_TO_ITF(dev);
+	struct cpfl_adapter_ext *adapter = itf->adapter;
+	int ret;
+	struct cpfl_tdi_rule_info *rinfo;
+	struct cpfl_tdi_table_node *table_node;
+	struct cpfl_tdi_action_node *action_node;
+	struct cpfl_tdi_table_key_obj *kobj;
+	struct cpfl_tdi_action_obj *aobj;
+	uint32_t table_id = attr->group;
+
+	if (!adapter->flow_parser.is_p4_parser || !cpfl_flow_items_all_is_flex(pattern))
+		return -EINVAL;
+
+	rinfo = rte_zmalloc(NULL, sizeof(struct cpfl_tdi_rule_info), 0);
+	if (!rinfo)
+		return -ENOMEM;
+
+	kobj = &rinfo->kobj;
+	aobj = &rinfo->aobj;
+	memset(kobj, 0, sizeof(struct cpfl_tdi_table_key_obj));
+	memset(aobj, 0, sizeof(struct cpfl_tdi_action_obj));
+
+	ret = cpfl_tdi_parse_pattern(dev, pattern, table_id, &table_node, kobj);
+	if (ret) {
+		PMD_DRV_LOG(ERR, "Invalid pattern");
+		rte_free(rinfo);
+		return -EINVAL;
+	}
+
+	ret = cpfl_tdi_parse_action(dev, actions, table_id, table_node, &action_node, aobj);
+	if (ret) {
+		PMD_DRV_LOG(ERR, "Invalid action");
+		rte_free(rinfo);
+		return -EINVAL;
+	}
+
+	cpfl_fill_rinfo_default_value(rinfo);
+	if (!meta)
+		rte_free(rinfo);
+	else
+		*meta = rinfo;
+
+	return 0;
+}
+
+static int
+cpfl_tdi_fxp_init(struct cpfl_adapter_ext *ad __rte_unused)
+{
+	return 0;
+}
+
+static void
+cpfl_tdi_fxp_uninit(struct cpfl_adapter_ext *ad __rte_unused)
+{
+}
+
+static struct cpfl_flow_engine cpfl_tdi_engine = {
+	.type = CPFL_FLOW_ENGINE_TDI,
+	.init = cpfl_tdi_fxp_init,
+	.uninit = cpfl_tdi_fxp_uninit,
+	.create = cpfl_tdi_fxp_rule_create,
+	.destroy = cpfl_tdi_fxp_rule_destroy,
+	.parse_pattern_action = cpfl_tdi_parse_pattern_action,
+};
+
+RTE_INIT(cpfl_sw_engine_init)
+{
+	struct cpfl_flow_engine *engine = &cpfl_tdi_engine;
+
+	cpfl_flow_engine_register(engine);
+}
diff --git a/drivers/net/cpfl/cpfl_tdi.h b/drivers/net/cpfl/cpfl_tdi.h
new file mode 100644
index 0000000000..c7b8c38657
--- /dev/null
+++ b/drivers/net/cpfl/cpfl_tdi.h
@@ -0,0 +1,123 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(c) 2023 Intel Corporation
+ */
+#ifndef _CPFL_TDI_H_
+#define _CPFL_TDI_H_
+
+#include "cpfl_ethdev.h"
+#include "cpfl_fxp_rule.h"
+#include "cpfl_tdi_parser.h"
+
+#define CPFL_TDI_KEY_FIELD_NUM_MAX 256	/* Max number of key field in a table. */
+#define CPFL_TDI_ACTION_SPEC_NUM_MAX 64 /* Max number of action spec in a table. */
+#define CPFL_TDI_ACTION_PARAMETER_NUM_MAX 16
+#define CPFL_TDI_ACTION_BUF_SIZE_MAX 256
+#define CPFL_TDI_TABLE_KEY_FIELD_MAX 32
+#define CPFL_TDI_MAX_TABLE_KEY_SIZE 128
+
+/**
+ *
+ * Table entry operation type.
+ */
+
+enum cpfl_tdi_table_entry_op {
+	CPFL_TDI_TABLE_ENTRY_OP_ADD, /* Add an entry */
+	CPFL_TDI_TABLE_ENTRY_OP_DEL, /* Delete an entry */
+	CPFL_TDI_TABLE_ENTRY_OP_QRY, /* Query an entry */
+};
+
+/**
+ * Table key match type.
+ *
+ * To specify the key match type of a table.
+ */
+enum cpfl_tdi_table_key_match_type {
+	CPFL_TDI_TABLE_KEY_MATCH_TYPE_EXACT,	/**< Exact match. */
+	CPFL_TDI_TABLE_KEY_MATCH_TYPE_WILDCARD, /**< Wildcard match. */
+	CPFL_TDI_TABLE_KEY_MATCH_TYPE_RANGE,	/**< Range match. */
+	CPFL_TDI_TABLE_KEY_MATCH_TYPE_LPM,	/**< longest prefix match. */
+};
+
+struct cpfl_tdi_param_info {
+	uint32_t id;
+	uint16_t offset;
+	uint16_t size;
+};
+
+struct cpfl_tdi_action_spec_field_info {
+	struct cpfl_tdi_immediate_field *field;
+	struct cpfl_tdi_param_info param;
+	struct cpfl_tdi_mod_field *mod_field;
+	struct cpfl_tdi_hw_action *hw_action;
+};
+
+struct cpfl_tdi_table_key_field_info {
+	struct cpfl_tdi_match_key_field *field;
+	struct cpfl_tdi_match_key_format *format;
+	struct cpfl_tdi_param_info param;
+};
+
+struct cpfl_tdi_action_node {
+	TAILQ_ENTRY(cpfl_tdi_action_node) next;
+	enum cpfl_tdi_hw_block hw_block_type;
+	const struct cpfl_tdi_action *action;
+	const struct cpfl_tdi_action_format *format;
+	uint32_t buf_len;
+	struct cpfl_tdi_param_info params[CPFL_TDI_ACTION_PARAMETER_NUM_MAX];
+	uint8_t init_buf[CPFL_TDI_ACTION_BUF_SIZE_MAX];
+	uint8_t query_msk[CPFL_TDI_ACTION_BUF_SIZE_MAX];
+};
+
+struct cpfl_tdi_table_node {
+	TAILQ_ENTRY(cpfl_tdi_table_node) next;
+	const struct cpfl_tdi_table *table;
+	struct cpfl_tdi_action_node **actions;
+	uint16_t buf_len;
+	struct cpfl_tdi_param_info params[CPFL_TDI_TABLE_KEY_FIELD_MAX];
+};
+
+struct cpfl_tdi_table_key_obj {
+	uint16_t buf_len;
+	uint8_t buf[CPFL_TDI_MAX_TABLE_KEY_SIZE];
+	const struct cpfl_tdi_table_node *tnode;
+	union {
+		struct {
+			uint16_t prof_id;
+			uint8_t sub_prof_id;
+			uint8_t pin_to_cache;
+			uint8_t fixed_fetch;
+		} sem;
+		struct {
+			uint32_t mod_index;
+			uint8_t pin_mod_content;
+			uint8_t mod_obj_size;
+		} mod;
+	};
+};
+
+struct cpfl_tdi_action_obj {
+	const struct cpfl_tdi_table *table;
+	struct cpfl_tdi_action_node *node;
+	uint16_t buf_len;
+	uint8_t buf[CPFL_TDI_ACTION_BUF_SIZE_MAX];
+};
+
+TAILQ_HEAD(tdi_table_key_obj_list, tdi_table_key_obj);
+TAILQ_HEAD(tdi_action_obj_list, tdi_action_obj);
+
+struct cpfl_tdi_rule_info {
+	enum cpfl_rule_type type;
+	struct cpfl_tdi_table_key_obj kobj;
+	struct cpfl_tdi_action_obj aobj;
+	uint64_t cookie;
+	uint8_t host_id;
+	uint8_t port_num;
+	uint8_t resp_req;
+	/* vsi is used for lem and lpm rules */
+	uint16_t vsi;
+	uint8_t clear_mirror_1st_state;
+};
+
+void cpfl_tdi_free_table_list(struct cpfl_flow_parser *flow_parser);
+int cpfl_tdi_build(struct cpfl_flow_parser *flow_parser);
+#endif
diff --git a/drivers/net/cpfl/meson.build b/drivers/net/cpfl/meson.build
index f948033f1f..a2e5c9d25b 100644
--- a/drivers/net/cpfl/meson.build
+++ b/drivers/net/cpfl/meson.build
@@ -48,6 +48,7 @@ if dpdk_conf.has('RTE_HAS_JANSSON')
             'cpfl_flow_parser.c',
             'cpfl_fxp_rule.c',
             'cpfl_tdi_parser.c',
+            'cpfl_tdi.c',
     )
     ext_deps += jansson_dep
 endif
