/* Copyright (c) 2019 Mellanox Technologies. */
 
 #include "dr_types.h"
+#include "dr_ste.h"
 
 enum dr_action_domain {
        DR_ACTION_DOMAIN_NIC_INGRESS,
                        [DR_ACTION_TYP_CTR]             = DR_ACTION_STATE_NON_TERM,
                        [DR_ACTION_TYP_TNL_L2_TO_L2]    = DR_ACTION_STATE_DECAP,
                        [DR_ACTION_TYP_TNL_L3_TO_L2]    = DR_ACTION_STATE_DECAP,
+                       [DR_ACTION_TYP_L2_TO_TNL_L2]    = DR_ACTION_STATE_ENCAP,
+                       [DR_ACTION_TYP_L2_TO_TNL_L3]    = DR_ACTION_STATE_ENCAP,
                        [DR_ACTION_TYP_MODIFY_HDR]      = DR_ACTION_STATE_MODIFY_HDR,
                        [DR_ACTION_TYP_POP_VLAN]        = DR_ACTION_STATE_MODIFY_VLAN,
                },
                        [DR_ACTION_TYP_FT]              = DR_ACTION_STATE_TERM,
                        [DR_ACTION_TYP_TAG]             = DR_ACTION_STATE_DECAP,
                        [DR_ACTION_TYP_CTR]             = DR_ACTION_STATE_DECAP,
+                       [DR_ACTION_TYP_L2_TO_TNL_L2]    = DR_ACTION_STATE_ENCAP,
+                       [DR_ACTION_TYP_L2_TO_TNL_L3]    = DR_ACTION_STATE_ENCAP,
                        [DR_ACTION_TYP_MODIFY_HDR]      = DR_ACTION_STATE_MODIFY_HDR,
                        [DR_ACTION_TYP_POP_VLAN]        = DR_ACTION_STATE_MODIFY_VLAN,
                },
+               [DR_ACTION_STATE_ENCAP] = {
+                       [DR_ACTION_TYP_DROP]            = DR_ACTION_STATE_TERM,
+                       [DR_ACTION_TYP_QP]              = DR_ACTION_STATE_TERM,
+                       [DR_ACTION_TYP_FT]              = DR_ACTION_STATE_TERM,
+                       [DR_ACTION_TYP_TAG]             = DR_ACTION_STATE_ENCAP,
+                       [DR_ACTION_TYP_CTR]             = DR_ACTION_STATE_ENCAP,
+               },
                [DR_ACTION_STATE_MODIFY_HDR] = {
                        [DR_ACTION_TYP_DROP]            = DR_ACTION_STATE_TERM,
                        [DR_ACTION_TYP_QP]              = DR_ACTION_STATE_TERM,
                        [DR_ACTION_TYP_FT]              = DR_ACTION_STATE_TERM,
                        [DR_ACTION_TYP_TAG]             = DR_ACTION_STATE_MODIFY_HDR,
                        [DR_ACTION_TYP_CTR]             = DR_ACTION_STATE_MODIFY_HDR,
+                       [DR_ACTION_TYP_L2_TO_TNL_L2]    = DR_ACTION_STATE_ENCAP,
+                       [DR_ACTION_TYP_L2_TO_TNL_L3]    = DR_ACTION_STATE_ENCAP,
                },
                [DR_ACTION_STATE_MODIFY_VLAN] = {
                        [DR_ACTION_TYP_DROP]            = DR_ACTION_STATE_TERM,
                        [DR_ACTION_TYP_CTR]             = DR_ACTION_STATE_MODIFY_VLAN,
                        [DR_ACTION_TYP_POP_VLAN]        = DR_ACTION_STATE_MODIFY_VLAN,
                        [DR_ACTION_TYP_MODIFY_HDR]      = DR_ACTION_STATE_MODIFY_HDR,
+                       [DR_ACTION_TYP_L2_TO_TNL_L2]    = DR_ACTION_STATE_ENCAP,
+                       [DR_ACTION_TYP_L2_TO_TNL_L3]    = DR_ACTION_STATE_ENCAP,
                },
                [DR_ACTION_STATE_NON_TERM] = {
                        [DR_ACTION_TYP_DROP]            = DR_ACTION_STATE_TERM,
                        [DR_ACTION_TYP_CTR]             = DR_ACTION_STATE_NON_TERM,
                        [DR_ACTION_TYP_TNL_L2_TO_L2]    = DR_ACTION_STATE_DECAP,
                        [DR_ACTION_TYP_TNL_L3_TO_L2]    = DR_ACTION_STATE_DECAP,
+                       [DR_ACTION_TYP_L2_TO_TNL_L2]    = DR_ACTION_STATE_ENCAP,
+                       [DR_ACTION_TYP_L2_TO_TNL_L3]    = DR_ACTION_STATE_ENCAP,
                        [DR_ACTION_TYP_MODIFY_HDR]      = DR_ACTION_STATE_MODIFY_HDR,
                        [DR_ACTION_TYP_POP_VLAN]        = DR_ACTION_STATE_MODIFY_VLAN,
                },
                        [DR_ACTION_TYP_CTR]             = DR_ACTION_STATE_NON_TERM,
                        [DR_ACTION_TYP_TNL_L2_TO_L2]    = DR_ACTION_STATE_DECAP,
                        [DR_ACTION_TYP_TNL_L3_TO_L2]    = DR_ACTION_STATE_DECAP,
+                       [DR_ACTION_TYP_L2_TO_TNL_L2]    = DR_ACTION_STATE_ENCAP,
+                       [DR_ACTION_TYP_L2_TO_TNL_L3]    = DR_ACTION_STATE_ENCAP,
                        [DR_ACTION_TYP_MODIFY_HDR]      = DR_ACTION_STATE_MODIFY_HDR,
                        [DR_ACTION_TYP_POP_VLAN]        = DR_ACTION_STATE_MODIFY_VLAN,
                        [DR_ACTION_TYP_VPORT]           = DR_ACTION_STATE_TERM,
                        [DR_ACTION_TYP_MODIFY_HDR]      = DR_ACTION_STATE_MODIFY_HDR,
                        [DR_ACTION_TYP_POP_VLAN]        = DR_ACTION_STATE_MODIFY_VLAN,
                        [DR_ACTION_TYP_VPORT]           = DR_ACTION_STATE_TERM,
+                       [DR_ACTION_TYP_L2_TO_TNL_L2]    = DR_ACTION_STATE_ENCAP,
+                       [DR_ACTION_TYP_L2_TO_TNL_L3]    = DR_ACTION_STATE_ENCAP,
+               },
+               [DR_ACTION_STATE_ENCAP] = {
+                       [DR_ACTION_TYP_DROP]            = DR_ACTION_STATE_TERM,
+                       [DR_ACTION_TYP_QP]              = DR_ACTION_STATE_TERM,
+                       [DR_ACTION_TYP_FT]              = DR_ACTION_STATE_TERM,
+                       [DR_ACTION_TYP_VPORT]           = DR_ACTION_STATE_TERM,
+                       [DR_ACTION_TYP_CTR]             = DR_ACTION_STATE_ENCAP,
                },
                [DR_ACTION_STATE_MODIFY_HDR] = {
                        [DR_ACTION_TYP_DROP]            = DR_ACTION_STATE_TERM,
                        [DR_ACTION_TYP_FT]              = DR_ACTION_STATE_TERM,
                        [DR_ACTION_TYP_CTR]             = DR_ACTION_STATE_MODIFY_HDR,
                        [DR_ACTION_TYP_VPORT]           = DR_ACTION_STATE_TERM,
+                       [DR_ACTION_TYP_L2_TO_TNL_L2]    = DR_ACTION_STATE_ENCAP,
+                       [DR_ACTION_TYP_L2_TO_TNL_L3]    = DR_ACTION_STATE_ENCAP,
                },
                [DR_ACTION_STATE_MODIFY_VLAN] = {
                        [DR_ACTION_TYP_DROP]            = DR_ACTION_STATE_TERM,
                        [DR_ACTION_TYP_CTR]             = DR_ACTION_STATE_MODIFY_VLAN,
                        [DR_ACTION_TYP_VPORT]           = DR_ACTION_STATE_TERM,
                        [DR_ACTION_TYP_MODIFY_HDR]      = DR_ACTION_STATE_MODIFY_HDR,
+                       [DR_ACTION_TYP_L2_TO_TNL_L2]    = DR_ACTION_STATE_ENCAP,
+                       [DR_ACTION_TYP_L2_TO_TNL_L3]    = DR_ACTION_STATE_ENCAP,
                },
                [DR_ACTION_STATE_NON_TERM] = {
                        [DR_ACTION_TYP_DROP]            = DR_ACTION_STATE_TERM,
                        [DR_ACTION_TYP_CTR]             = DR_ACTION_STATE_NON_TERM,
                        [DR_ACTION_TYP_TNL_L2_TO_L2]    = DR_ACTION_STATE_DECAP,
                        [DR_ACTION_TYP_TNL_L3_TO_L2]    = DR_ACTION_STATE_DECAP,
+                       [DR_ACTION_TYP_L2_TO_TNL_L2]    = DR_ACTION_STATE_ENCAP,
+                       [DR_ACTION_TYP_L2_TO_TNL_L3]    = DR_ACTION_STATE_ENCAP,
                        [DR_ACTION_TYP_MODIFY_HDR]      = DR_ACTION_STATE_MODIFY_HDR,
                        [DR_ACTION_TYP_POP_VLAN]        = DR_ACTION_STATE_MODIFY_VLAN,
                        [DR_ACTION_TYP_VPORT]           = DR_ACTION_STATE_TERM,
                        break;
                case DR_ACTION_TYP_L2_TO_TNL_L2:
                case DR_ACTION_TYP_L2_TO_TNL_L3:
+                       if (rx_rule &&
+                           !(dmn->ste_ctx->actions_caps & DR_STE_CTX_ACTION_CAP_RX_ENCAP)) {
+                               mlx5dr_info(dmn, "Device doesn't support Encap on RX\n");
+                               goto out_invalid_arg;
+                       }
                        attr.reformat_size = action->reformat->reformat_size;
                        attr.reformat_id = action->reformat->reformat_id;
                        break;
 
        MLX5_SET(ste_match_bwc_v1, hw_ste_p, reparse, 1);
 }
 
-static void dr_ste_v1_set_tx_encap(u8 *hw_ste_p, u8 *d_action,
-                                  u32 reformat_id, int size)
+static void dr_ste_v1_set_encap(u8 *hw_ste_p, u8 *d_action,
+                               u32 reformat_id, int size)
 {
        MLX5_SET(ste_double_action_insert_with_ptr_v1, d_action, action_id,
                 DR_STE_V1_ACTION_ID_INSERT_POINTER);
        dr_ste_v1_set_reparse(hw_ste_p);
 }
 
-static void dr_ste_v1_set_tx_encap_l3(u8 *hw_ste_p,
-                                     u8 *frst_s_action,
-                                     u8 *scnd_d_action,
-                                     u32 reformat_id,
-                                     int size)
+static void dr_ste_v1_set_encap_l3(u8 *hw_ste_p,
+                                  u8 *frst_s_action,
+                                  u8 *scnd_d_action,
+                                  u32 reformat_id,
+                                  int size)
 {
        /* Remove L2 headers */
        MLX5_SET(ste_single_action_remove_header_v1, frst_s_action, action_id,
                        action_sz = DR_STE_ACTION_TRIPLE_SZ;
                        allow_encap = true;
                }
-               dr_ste_v1_set_tx_encap(last_ste, action,
-                                      attr->reformat_id,
-                                      attr->reformat_size);
+               dr_ste_v1_set_encap(last_ste, action,
+                                   attr->reformat_id,
+                                   attr->reformat_size);
                action_sz -= DR_STE_ACTION_DOUBLE_SZ;
                action += DR_STE_ACTION_DOUBLE_SZ;
        } else if (action_type_set[DR_ACTION_TYP_L2_TO_TNL_L3]) {
                action_sz = DR_STE_ACTION_TRIPLE_SZ;
                d_action = action + DR_STE_ACTION_SINGLE_SZ;
 
-               dr_ste_v1_set_tx_encap_l3(last_ste,
-                                         action, d_action,
-                                         attr->reformat_id,
-                                         attr->reformat_size);
+               dr_ste_v1_set_encap_l3(last_ste,
+                                      action, d_action,
+                                      attr->reformat_id,
+                                      attr->reformat_size);
                action_sz -= DR_STE_ACTION_TRIPLE_SZ;
                action += DR_STE_ACTION_TRIPLE_SZ;
        }
                dr_ste_v1_set_counter_id(last_ste, attr->ctr_id);
        }
 
+       if (action_type_set[DR_ACTION_TYP_L2_TO_TNL_L2]) {
+               if (action_sz < DR_STE_ACTION_DOUBLE_SZ) {
+                       dr_ste_v1_arr_init_next_match(&last_ste, added_stes, attr->gvmi);
+                       action = MLX5_ADDR_OF(ste_mask_and_match_v1, last_ste, action);
+                       action_sz = DR_STE_ACTION_TRIPLE_SZ;
+               }
+               dr_ste_v1_set_encap(last_ste, action,
+                                   attr->reformat_id,
+                                   attr->reformat_size);
+               action_sz -= DR_STE_ACTION_DOUBLE_SZ;
+               action += DR_STE_ACTION_DOUBLE_SZ;
+               allow_modify_hdr = false;
+       } else if (action_type_set[DR_ACTION_TYP_L2_TO_TNL_L3]) {
+               u8 *d_action;
+
+               if (action_sz < DR_STE_ACTION_TRIPLE_SZ) {
+                       dr_ste_v1_arr_init_next_match(&last_ste, added_stes, attr->gvmi);
+                       action = MLX5_ADDR_OF(ste_mask_and_match_v1, last_ste, action);
+                       action_sz = DR_STE_ACTION_TRIPLE_SZ;
+               }
+
+               d_action = action + DR_STE_ACTION_SINGLE_SZ;
+
+               dr_ste_v1_set_encap_l3(last_ste,
+                                      action, d_action,
+                                      attr->reformat_id,
+                                      attr->reformat_size);
+               action_sz -= DR_STE_ACTION_TRIPLE_SZ;
+               allow_modify_hdr = false;
+       }
+
        dr_ste_v1_set_hit_gvmi(last_ste, attr->hit_gvmi);
        dr_ste_v1_set_hit_addr(last_ste, attr->final_icm_addr, 1);
 }
        .set_byte_mask                  = &dr_ste_v1_set_byte_mask,
        .get_byte_mask                  = &dr_ste_v1_get_byte_mask,
        /* Actions */
+       .actions_caps                   = DR_STE_CTX_ACTION_CAP_RX_ENCAP,
        .set_actions_rx                 = &dr_ste_v1_set_actions_rx,
        .set_actions_tx                 = &dr_ste_v1_set_actions_tx,
        .modify_field_arr_sz            = ARRAY_SIZE(dr_ste_v1_action_modify_field_arr),