qcacld-3.0: Support for DP RX Threads

Add support for DP RX Threads as a part of the FR. Multiple RX threads
can be enabled from the ini. The code is added in a new DP module
outside of the cmn project.

Change-Id: Ief6ee955f13c5e527986307371b8e45677cb9700
CRs-Fixed: 2256446
diff --git a/Kbuild b/Kbuild
index 05e4bf6..d5816ee 100755
--- a/Kbuild
+++ b/Kbuild
@@ -157,6 +157,10 @@
 HDD_OBJS += $(HDD_SRC_DIR)/wlan_hdd_rx_monitor.o
 endif
 
+ifeq ($(CONFIG_LITHIUM), y)
+CONFIG_WLAN_FEATURE_DP_RX_THREADS := y
+endif
+
 ifeq ($(CONFIG_WLAN_NUD_TRACKING), y)
 HDD_OBJS += $(HDD_SRC_DIR)/wlan_hdd_nud_tracking.o
 endif
@@ -1077,6 +1081,11 @@
 TXRX_OBJS +=     $(TXRX_DIR)/ol_tx_throttle.o
 endif
 
+############ TXRX 3.0 ############
+TXRX3.0_DIR :=     core/dp/txrx3.0
+TXRX3.0_INC :=     -I$(WLAN_ROOT)/$(TXRX3.0_DIR)
+TXRX3.0_OBJS := $(TXRX3.0_DIR)/dp_txrx.o \
+		$(TXRX3.0_DIR)/dp_rx_thread.o
 ifeq ($(CONFIG_LITHIUM), y)
 ############ DP 3.0 ############
 DP_INC := -I$(WLAN_COMMON_INC)/dp/inc \
@@ -1553,7 +1562,8 @@
 		$(REGULATORY_INC) \
 		$(HTC_INC) \
 		$(DFS_INC) \
-		$(WCFG_INC)
+		$(WCFG_INC) \
+		$(TXRX3.0_INC)
 
 INCS +=		$(HIF_INC) \
 		$(BMI_INC)
@@ -1721,6 +1731,10 @@
 OBJS +=		$(DP_OBJS)
 endif
 
+ifeq ($(CONFIG_WLAN_FEATURE_DP_RX_THREADS), y)
+OBJS += $(TXRX3.0_OBJS)
+endif
+
 ccflags-y += $(INCS)
 
 cppflags-y +=	-DANI_OS_TYPE_ANDROID=6 \
@@ -1773,6 +1787,7 @@
 cppflags-$(CONFIG_PLD_SDIO_CNSS_FLAG) += -DCONFIG_PLD_SDIO_CNSS
 cppflags-$(CONFIG_PLD_PCIE_CNSS_FLAG) += -DCONFIG_PLD_PCIE_CNSS
 cppflags-$(CONFIG_PLD_PCIE_INIT_FLAG) += -DCONFIG_PLD_PCIE_INIT
+cppflags-$(CONFIG_WLAN_FEATURE_DP_RX_THREADS) += -DFEATURE_WLAN_DP_RX_THREADS
 
 #Enable NL80211 test mode
 cppflags-$(CONFIG_NL80211_TESTMODE) += -DWLAN_NL80211_TESTMODE
diff --git a/core/cds/inc/cds_config.h b/core/cds/inc/cds_config.h
index 28c697e..7066c3b 100644
--- a/core/cds/inc/cds_config.h
+++ b/core/cds/inc/cds_config.h
@@ -91,6 +91,8 @@
  * @max_scan: Maximum number of parallel scans
  * @tx_flow_stop_queue_th: Threshold to stop queue in percentage
  * @tx_flow_start_queue_offset: Start queue offset in percentage
+ * @num_dp_rx_threads: number of dp rx threads to be configured
+ * @enable_dp_rx_threads: enable dp rx threads
  * @is_lpass_enabled: Indicate whether LPASS is enabled or not
  * @bool apf_packet_filter_enable; Indicate apf filter enabled or not
  * @tx_chain_mask_cck: Tx chain mask enabled or not
@@ -146,6 +148,8 @@
 	uint32_t tx_flow_stop_queue_th;
 	uint32_t tx_flow_start_queue_offset;
 #endif
+	uint8_t num_dp_rx_threads;
+	uint8_t enable_dp_rx_threads;
 #ifdef WLAN_FEATURE_LPSS
 	bool is_lpass_enabled;
 #endif
diff --git a/core/cds/src/cds_api.c b/core/cds/src/cds_api.c
index d923ddc..38345b6 100644
--- a/core/cds/src/cds_api.c
+++ b/core/cds/src/cds_api.c
@@ -58,7 +58,7 @@
 #include "target_type.h"
 #include "wlan_ocb_ucfg_api.h"
 #include "wlan_ipa_ucfg_api.h"
-
+#include "dp_txrx.h"
 #ifdef ENABLE_SMMU_S1_TRANSLATION
 #include "pld_common.h"
 #include <asm/dma-iommu.h>
@@ -728,6 +728,9 @@
 
 QDF_STATUS cds_dp_open(struct wlan_objmgr_psoc *psoc)
 {
+	QDF_STATUS qdf_status;
+	struct dp_txrx_config dp_config;
+
 	if (cdp_txrx_intr_attach(gp_cds_context->dp_soc)
 				!= QDF_STATUS_SUCCESS) {
 		cds_alert("Failed to attach interrupts");
@@ -746,6 +749,16 @@
 		goto intr_close;
 	}
 
+	dp_config.num_rx_threads = gp_cds_context->cds_cfg->num_dp_rx_threads;
+	dp_config.enable_rx_threads =
+		gp_cds_context->cds_cfg->enable_dp_rx_threads;
+	qdf_status = dp_txrx_init(cds_get_context(QDF_MODULE_ID_SOC),
+				  cds_get_context(QDF_MODULE_ID_TXRX),
+				  &dp_config);
+
+	if (!QDF_IS_STATUS_SUCCESS(qdf_status))
+		goto pdev_detach;
+
 	pmo_ucfg_psoc_set_txrx_handle(psoc, gp_cds_context->pdev_txrx_ctx);
 	ucfg_ocb_set_txrx_handle(psoc, gp_cds_context->pdev_txrx_ctx);
 
@@ -753,6 +766,9 @@
 
 	return 0;
 
+pdev_detach:
+	cdp_pdev_detach(gp_cds_context->dp_soc,
+			cds_get_context(QDF_MODULE_ID_TXRX), false);
 intr_close:
 	cdp_txrx_intr_detach(gp_cds_context->dp_soc);
 close:
@@ -1176,10 +1192,13 @@
 	void *ctx;
 
 	cdp_txrx_intr_detach(gp_cds_context->dp_soc);
-
 	ctx = cds_get_context(QDF_MODULE_ID_TXRX);
+
+	dp_txrx_deinit(cds_get_context(QDF_MODULE_ID_SOC));
+
 	cdp_pdev_detach(cds_get_context(QDF_MODULE_ID_SOC),
 		       (struct cdp_pdev *)ctx, 1);
+
 	cds_set_context(QDF_MODULE_ID_TXRX, NULL);
 	pmo_ucfg_psoc_set_txrx_handle(psoc, NULL);
 
diff --git a/core/dp/txrx3.0/dp_rx_thread.c b/core/dp/txrx3.0/dp_rx_thread.c
new file mode 100644
index 0000000..ab5b745
--- /dev/null
+++ b/core/dp/txrx3.0/dp_rx_thread.c
@@ -0,0 +1,639 @@
+/*
+ * Copyright (c) 2014-2018 The Linux Foundation. All rights reserved.
+ *
+ * Permission to use, copy, modify, and/or distribute this software for
+ * any purpose with or without fee is hereby granted, provided that the
+ * above copyright notice and this permission notice appear in all
+ * copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
+ * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
+ * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
+ * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
+ * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
+ * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+ * PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <dp_txrx.h>
+#include <cdp_txrx_cmn_struct.h>
+#include <cdp_txrx_peer_ops.h>
+#include <cds_sched.h>
+
+/* Timeout in ms to wait for a DP rx thread */
+#define DP_RX_THREAD_WAIT_TIMEOUT 200
+
+#define DP_RX_TM_DEBUG 0
+#if DP_RX_TM_DEBUG
+/**
+ * dp_rx_tm_walk_skb_list() - Walk skb list and print members
+ * @nbuf_list - nbuf list to print
+ *
+ * Returns: None
+ */
+static inline void dp_rx_tm_walk_skb_list(qdf_nbuf_t nbuf_list)
+{
+	qdf_nbuf_t nbuf;
+	int i = 0;
+
+	nbuf = nbuf_list;
+	while (nbuf) {
+		dp_debug("%d nbuf:%pk nbuf->next:%pK nbuf->data:%pk ", i,
+			 nbuf, qdf_nbuf_next(nbuf), qdf_nbuf_data(nbuf));
+		nbuf = qdf_nbuf_next(nbuf);
+		i++;
+	}
+}
+#else
+static inline void dp_rx_tm_walk_skb_list(qdf_nbuf_t nbuf_list)
+{ }
+#endif /* DP_RX_TM_DEBUG */
+
+/**
+ * dp_rx_tm_thread_dump_stats() - display stats for a rx_thread
+ * @rx_thread - rx_thread pointer for which the stats need to be
+ *            displayed
+ *
+ * Returns: None
+ */
+static void dp_rx_tm_thread_dump_stats(struct dp_rx_thread *rx_thread)
+{
+	uint8_t reo_ring_num;
+	uint32_t off = 0;
+	char nbuf_queued_string[100];
+	uint32_t total_queued = 0;
+	uint32_t temp = 0;
+
+	qdf_mem_set(nbuf_queued_string, 0, sizeof(nbuf_queued_string));
+
+	for (reo_ring_num = 0; reo_ring_num < DP_RX_TM_MAX_REO_RINGS;
+	     reo_ring_num++) {
+		temp = rx_thread->stats.nbuf_queued[reo_ring_num];
+		if (!temp)
+			continue;
+		total_queued += temp;
+		if (off >= sizeof(nbuf_queued_string))
+			continue;
+		off += qdf_scnprintf(&nbuf_queued_string[off],
+				     sizeof(nbuf_queued_string) - off,
+				     "reo[%u]:%u ", reo_ring_num, temp);
+	}
+	dp_info("thread:%u - qlen:%u queued:(total:%u %s) dequeued:%u stack:%u max_len:%u invalid(peer:%u vdev:%u others:%u)",
+		rx_thread->id,
+		qdf_nbuf_queue_head_qlen(&rx_thread->nbuf_queue),
+		total_queued,
+		nbuf_queued_string,
+		rx_thread->stats.nbuf_dequeued,
+		rx_thread->stats.nbuf_sent_to_stack,
+		rx_thread->stats.nbufq_max_len,
+		rx_thread->stats.dropped_invalid_peer,
+		rx_thread->stats.dropped_invalid_vdev,
+		rx_thread->stats.dropped_others);
+}
+
+QDF_STATUS dp_rx_tm_dump_stats(struct dp_rx_tm_handle *rx_tm_hdl)
+{
+	int i;
+
+	for (i = 0; i < DP_MAX_RX_THREADS; i++) {
+		if (!rx_tm_hdl->rx_thread[i])
+			continue;
+		dp_rx_tm_thread_dump_stats(rx_tm_hdl->rx_thread[i]);
+	}
+	return QDF_STATUS_SUCCESS;
+}
+
+/**
+ * dp_rx_tm_thread_enqueue() - enqueue nbuf list into rx_thread
+ * @rx_thread - rx_thread in which the nbuf needs to be queued
+ * @nbuf_list - list of packets to be queued into the thread
+ *
+ * Enqueue packet into rx_thread and wake it up. The function
+ * moves the next pointer of the nbuf_list into the ext list of
+ * the first nbuf for storage into the thread. Only the first
+ * nbuf is queued into the thread nbuf queue. The reverse is
+ * done at the time of dequeue.
+ *
+ * Returns: QDF_STATUS_SUCCESS on success or qdf error code on
+ * failure
+ */
+static QDF_STATUS dp_rx_tm_thread_enqueue(struct dp_rx_thread *rx_thread,
+					  qdf_nbuf_t nbuf_list)
+{
+	qdf_nbuf_t head_ptr, next_ptr_list;
+	uint32_t temp_qlen;
+	uint32_t num_elements_in_nbuf;
+	struct dp_rx_tm_handle_cmn *tm_handle_cmn;
+	uint8_t reo_ring_num = QDF_NBUF_CB_RX_CTX_ID(nbuf_list);
+	qdf_wait_queue_head_t *wait_q_ptr;
+
+	tm_handle_cmn = rx_thread->rtm_handle_cmn;
+
+	if (!tm_handle_cmn) {
+		dp_alert("tm_handle_cmn is null!");
+		QDF_BUG(0);
+		return QDF_STATUS_E_FAILURE;
+	}
+
+	wait_q_ptr = dp_rx_thread_get_wait_queue(tm_handle_cmn);
+
+	if (reo_ring_num >= DP_RX_TM_MAX_REO_RINGS) {
+		dp_alert("incorrect ring %u", reo_ring_num);
+		QDF_BUG(0);
+		return QDF_STATUS_E_FAILURE;
+	}
+
+	num_elements_in_nbuf = QDF_NBUF_CB_RX_NUM_ELEMENTS_IN_LIST(nbuf_list);
+
+	dp_rx_tm_walk_skb_list(nbuf_list);
+
+	head_ptr = nbuf_list;
+	next_ptr_list = head_ptr->next;
+
+	if (next_ptr_list) {
+		/* move ->next pointer to ext list */
+		qdf_nbuf_append_ext_list(head_ptr, next_ptr_list, 0);
+		dp_debug("appended next_ptr_list %pK to nbuf %pK ext list %pK",
+			 qdf_nbuf_next(nbuf_list), nbuf_list,
+			 qdf_nbuf_get_ext_list(nbuf_list));
+	}
+	qdf_nbuf_set_next(head_ptr, NULL);
+
+	qdf_nbuf_queue_head_enqueue_tail(&rx_thread->nbuf_queue, head_ptr);
+	temp_qlen = qdf_nbuf_queue_head_qlen(&rx_thread->nbuf_queue);
+
+	rx_thread->stats.nbuf_queued[reo_ring_num] += num_elements_in_nbuf;
+
+	if (temp_qlen > rx_thread->stats.nbufq_max_len)
+		rx_thread->stats.nbufq_max_len = temp_qlen;
+
+	qdf_set_bit(RX_POST_EVENT, &rx_thread->event_flag);
+	qdf_wake_up_interruptible(wait_q_ptr);
+
+	return QDF_STATUS_SUCCESS;
+}
+
+/**
+ * dp_rx_tm_thread_dequeue() - dequeue nbuf list from rx_thread
+ * @rx_thread - rx_thread from which the nbuf needs to be dequeued
+ *
+ * Returns: nbuf or nbuf_list dequeued from rx_thread
+ */
+static qdf_nbuf_t dp_rx_tm_thread_dequeue(struct dp_rx_thread *rx_thread)
+{
+	qdf_nbuf_t head, next_ptr_list, nbuf_list;
+
+	head = qdf_nbuf_queue_head_dequeue(&rx_thread->nbuf_queue);
+	nbuf_list = head;
+	if (head) {
+		/* move ext list to ->next pointer */
+		next_ptr_list = qdf_nbuf_get_ext_list(head);
+		qdf_nbuf_append_ext_list(head, NULL, 0);
+		qdf_nbuf_set_next(nbuf_list, next_ptr_list);
+		dp_rx_tm_walk_skb_list(nbuf_list);
+	}
+	return nbuf_list;
+}
+
+/**
+ * dp_rx_thread_process_nbufq() - process nbuf queue of a thread
+ * @rx_thread - rx_thread whose nbuf queue needs to be processed
+ *
+ * Returns: 0 on success, error code on failure
+ */
+static int dp_rx_thread_process_nbufq(struct dp_rx_thread *rx_thread)
+{
+	qdf_nbuf_t nbuf_list;
+	uint32_t peer_local_id;
+	void *peer;
+	struct cdp_vdev *vdev;
+	ol_txrx_rx_fp stack_fn;
+	ol_osif_vdev_handle osif_vdev;
+	ol_txrx_soc_handle soc;
+	uint32_t num_list_elements = 0;
+	struct cdp_pdev *pdev;
+
+	struct dp_txrx_handle_cmn *txrx_handle_cmn;
+
+	txrx_handle_cmn =
+		dp_rx_thread_get_txrx_handle(rx_thread->rtm_handle_cmn);
+
+	soc = dp_txrx_get_soc_from_ext_handle(txrx_handle_cmn);
+	pdev = dp_txrx_get_pdev_from_ext_handle(txrx_handle_cmn);
+
+	if (!soc || !pdev) {
+		dp_err("invalid soc or pdev!");
+		QDF_BUG(0);
+		return -EFAULT;
+	}
+
+	nbuf_list = dp_rx_tm_thread_dequeue(rx_thread);
+	while (nbuf_list) {
+		num_list_elements =
+			QDF_NBUF_CB_RX_NUM_ELEMENTS_IN_LIST(nbuf_list);
+		rx_thread->stats.nbuf_dequeued += num_list_elements;
+
+		peer_local_id = QDF_NBUF_CB_RX_PEER_LOCAL_ID(nbuf_list);
+		peer = cdp_peer_find_by_local_id(soc, pdev, peer_local_id);
+
+		if (!peer) {
+			rx_thread->stats.dropped_invalid_peer +=
+							num_list_elements;
+			dp_err("peer not found for local_id %u!",
+			       peer_local_id);
+			qdf_nbuf_list_free(nbuf_list);
+			continue;
+		}
+
+		vdev = cdp_peer_get_vdev(soc, peer);
+		if (!vdev) {
+			rx_thread->stats.dropped_invalid_vdev +=
+							num_list_elements;
+			dp_err("vdev not found for local_id %u!, pkt dropped",
+			       peer_local_id);
+			qdf_nbuf_list_free(nbuf_list);
+			continue;
+		}
+
+		cdp_get_os_rx_handles_from_vdev(soc, vdev, &stack_fn,
+						&osif_vdev);
+		if (!stack_fn || !osif_vdev) {
+			dp_alert("stack_fn or osif_vdev is null, pkt dropped!");
+			QDF_BUG(0);
+			rx_thread->stats.dropped_others +=
+							num_list_elements;
+			qdf_nbuf_list_free(nbuf_list);
+		}
+		stack_fn(osif_vdev, nbuf_list);
+		rx_thread->stats.nbuf_sent_to_stack += num_list_elements;
+
+		nbuf_list = dp_rx_tm_thread_dequeue(rx_thread);
+	}
+
+	return 0;
+}
+
+/**
+ * dp_rx_thread_sub_loop() - rx thread subloop
+ * @rx_thread - rx_thread to be processed
+ * @shutdown - pointer to shutdown variable
+ *
+ * The function handles shutdown and suspend events from other
+ * threads and processes nbuf queue of a rx thread. In case a
+ * shutdown event is received from some other wlan thread, the
+ * function sets the shutdown pointer to true and returns
+ *
+ * Returns: 0 on success, error code on failure
+ */
+static int dp_rx_thread_sub_loop(struct dp_rx_thread *rx_thread, bool *shutdown)
+{
+	while (true) {
+		if (qdf_atomic_test_and_clear_bit(RX_SHUTDOWN_EVENT,
+						  &rx_thread->event_flag)) {
+			if (qdf_atomic_test_and_clear_bit(RX_SUSPEND_EVENT,
+							  &rx_thread->event_flag)) {
+				qdf_event_set(&rx_thread->suspend_event);
+			}
+			dp_debug("shutting down (%s) id %d pid %d",
+				 qdf_get_current_comm(), rx_thread->id,
+				 qdf_get_current_pid());
+			*shutdown = true;
+			break;
+		}
+
+		dp_rx_thread_process_nbufq(rx_thread);
+
+		if (qdf_atomic_test_and_clear_bit(RX_SUSPEND_EVENT,
+						  &rx_thread->event_flag)) {
+			dp_debug("received suspend ind (%s) id %d pid %d",
+				 qdf_get_current_comm(), rx_thread->id,
+				 qdf_get_current_pid());
+			qdf_spin_lock(&rx_thread->lock);
+			qdf_event_reset(&rx_thread->resume_event);
+			qdf_event_set(&rx_thread->suspend_event);
+			qdf_spin_unlock(&rx_thread->lock);
+			dp_debug("waiting for resume (%s) id %d pid %d",
+				 qdf_get_current_comm(), rx_thread->id,
+				 qdf_get_current_pid());
+			qdf_wait_single_event(&rx_thread->resume_event, 0);
+		}
+		break;
+	}
+	return 0;
+}
+
+/**
+ * dp_rx_thread_loop() - main dp rx thread loop
+ * @arg: pointer to dp_rx_thread structure for the rx thread
+ *
+ * Return: thread exit code
+ */
+static int dp_rx_thread_loop(void *arg)
+{
+	struct dp_rx_thread *rx_thread = arg;
+	bool shutdown = false;
+	int status;
+	struct dp_rx_tm_handle_cmn *tm_handle_cmn;
+
+	tm_handle_cmn = rx_thread->rtm_handle_cmn;
+
+	if (!arg) {
+		dp_err("bad Args passed");
+		return 0;
+	}
+
+	qdf_set_user_nice(qdf_get_current_task(), -1);
+	qdf_set_wake_up_idle(true);
+
+	qdf_event_set(&rx_thread->start_event);
+	dp_info("starting rx_thread (%s) id %d pid %d", qdf_get_current_comm(),
+		rx_thread->id, qdf_get_current_pid());
+	while (!shutdown) {
+		/* This implements the execution model algorithm */
+		dp_debug("sleeping");
+		status =
+		    qdf_wait_queue_interruptible
+				(DP_RX_THREAD_GET_WAIT_QUEUE_OBJ(tm_handle_cmn),
+				 qdf_atomic_test_bit(RX_POST_EVENT,
+						     &rx_thread->event_flag) ||
+				 qdf_atomic_test_bit(RX_SUSPEND_EVENT,
+						     &rx_thread->event_flag));
+		dp_debug("woken up");
+
+		if (status == -ERESTARTSYS) {
+			dp_err("wait_event_interruptible returned -ERESTARTSYS");
+			QDF_DEBUG_PANIC();
+			break;
+		}
+		qdf_atomic_clear_bit(RX_POST_EVENT, &rx_thread->event_flag);
+		dp_rx_thread_sub_loop(rx_thread, &shutdown);
+	}
+
+	/* If we get here the scheduler thread must exit */
+	dp_info("exiting (%s) id %d pid %d", qdf_get_current_comm(),
+		rx_thread->id, qdf_get_current_pid());
+	qdf_event_set(&rx_thread->shutdown_event);
+	qdf_exit_thread(QDF_STATUS_SUCCESS);
+
+	return 0;
+}
+
+/*
+ * dp_rx_tm_thread_init() - Initialize dp_rx_thread structure and thread
+ *
+ * @rx_thread: dp_rx_thread structure to be initialized
+ * @id: id of the thread to be initialized
+ *
+ * Return: QDF_STATUS on success, QDF error code on failure
+ */
+static QDF_STATUS dp_rx_tm_thread_init(struct dp_rx_thread *rx_thread,
+				       uint8_t id)
+{
+	char thread_name[15];
+	QDF_STATUS qdf_status;
+
+	qdf_mem_set(thread_name, 0, sizeof(thread_name));
+
+	if (!rx_thread) {
+		dp_err("rx_thread is null!");
+		return QDF_STATUS_E_FAULT;
+	}
+	rx_thread->id = id;
+	rx_thread->event_flag = 0;
+	qdf_nbuf_queue_head_init(&rx_thread->nbuf_queue);
+	qdf_event_create(&rx_thread->start_event);
+	qdf_event_create(&rx_thread->suspend_event);
+	qdf_event_create(&rx_thread->resume_event);
+	qdf_event_create(&rx_thread->shutdown_event);
+	qdf_scnprintf(thread_name, sizeof(thread_name), "dp_rx_thread_%u", id);
+	dp_info("%s %u", thread_name, id);
+	rx_thread->task = qdf_create_thread(dp_rx_thread_loop,
+					    rx_thread, thread_name);
+	if (!rx_thread->task) {
+		dp_err("could not create dp_rx_thread %d", id);
+		return QDF_STATUS_E_FAILURE;
+	}
+
+	qdf_wake_up_process(rx_thread->task);
+	qdf_status = qdf_wait_single_event(&rx_thread->start_event, 0);
+
+	if (!QDF_IS_STATUS_SUCCESS(qdf_status)) {
+		dp_err("failed waiting for thread creation id %d", id);
+		return QDF_STATUS_E_FAILURE;
+	}
+	return QDF_STATUS_SUCCESS;
+}
+
+/*
+ * dp_rx_tm_thread_deinit() - De-Initialize dp_rx_thread structure and thread
+ * @rx_thread: dp_rx_thread structure to be de-initialized
+ * @id: id of the thread to be initialized
+ *
+ * Return: QDF_STATUS_SUCCESS
+ */
+static QDF_STATUS dp_rx_tm_thread_deinit(struct dp_rx_thread *rx_thread)
+{
+	qdf_event_destroy(&rx_thread->start_event);
+	qdf_event_destroy(&rx_thread->suspend_event);
+	qdf_event_destroy(&rx_thread->resume_event);
+	qdf_event_destroy(&rx_thread->shutdown_event);
+	return QDF_STATUS_SUCCESS;
+}
+
+QDF_STATUS dp_rx_tm_init(struct dp_rx_tm_handle *rx_tm_hdl,
+			 uint8_t num_dp_rx_threads)
+{
+	int i;
+	QDF_STATUS qdf_status = QDF_STATUS_SUCCESS;
+	/* ignoring num_dp_rx_threads for now */
+	qdf_init_waitqueue_head(&rx_tm_hdl->wait_q);
+
+	for (i = 0; i < DP_MAX_RX_THREADS; i++) {
+		rx_tm_hdl->rx_thread[i] =
+			(struct dp_rx_thread *)
+			qdf_mem_malloc(sizeof(struct dp_rx_thread));
+		if (qdf_unlikely(!rx_tm_hdl->rx_thread[i])) {
+			QDF_ASSERT(0);
+			qdf_status = QDF_STATUS_E_NOMEM;
+			dp_err("failed to allocate memory for dp_rx_thread");
+			goto ret;
+		}
+		rx_tm_hdl->rx_thread[i]->rtm_handle_cmn =
+				(struct dp_rx_tm_handle_cmn *)rx_tm_hdl;
+		qdf_status =
+			dp_rx_tm_thread_init(rx_tm_hdl->rx_thread[i], i);
+		if (!QDF_IS_STATUS_SUCCESS(qdf_status))
+			break;
+	}
+ret:
+	if (!QDF_IS_STATUS_SUCCESS(qdf_status))
+		dp_rx_tm_deinit(rx_tm_hdl);
+
+	return qdf_status;
+}
+
+/**
+ * dp_rx_tm_resume() - suspend DP RX threads
+ * @rx_tm_hdl: dp_rx_tm_handle containing the overall thread
+ *            infrastructure
+ *
+ * Return: QDF_STATUS_SUCCESS
+ */
+QDF_STATUS dp_rx_tm_suspend(struct dp_rx_tm_handle *rx_tm_hdl)
+{
+	int i;
+	QDF_STATUS qdf_status;
+	struct dp_rx_thread *rx_thread;
+
+	for (i = 0; i < DP_MAX_RX_THREADS; i++) {
+		if (!rx_tm_hdl->rx_thread[i])
+			continue;
+		qdf_set_bit(RX_SUSPEND_EVENT,
+			    &rx_tm_hdl->rx_thread[i]->event_flag);
+	}
+
+	qdf_wake_up_interruptible(&rx_tm_hdl->wait_q);
+
+	for (i = 0; i < DP_MAX_RX_THREADS; i++) {
+		rx_thread = rx_tm_hdl->rx_thread[i];
+		if (!rx_thread)
+			continue;
+		dp_debug("thread %d", i);
+		qdf_status = qdf_wait_single_event(&rx_thread->suspend_event,
+						   DP_RX_THREAD_WAIT_TIMEOUT);
+		if (QDF_IS_STATUS_SUCCESS(qdf_status))
+			dp_debug("thread:%d suspended", rx_thread->id);
+		else if (qdf_status == QDF_STATUS_E_TIMEOUT)
+			dp_err("thread:%d timed out waiting for suspend",
+			       rx_thread->id);
+		else
+			dp_err("thread:%d failed while waiting for suspend",
+			       rx_thread->id);
+	}
+	rx_tm_hdl->state = DP_RX_THREAD_SUSPENDED;
+
+	return QDF_STATUS_SUCCESS;
+}
+
+/**
+ * dp_rx_tm_resume() - resume DP RX threads
+ * @rx_tm_hdl: dp_rx_tm_handle containing the overall thread
+ *            infrastructure
+ *
+ * Return: QDF_STATUS_SUCCESS
+ */
+QDF_STATUS dp_rx_tm_resume(struct dp_rx_tm_handle *rx_tm_hdl)
+{
+	int i;
+
+	if (rx_tm_hdl->state != DP_RX_THREAD_SUSPENDED) {
+		dp_err("resume callback received without suspend");
+		return QDF_STATUS_E_FAULT;
+	}
+
+	for (i = 0; i < DP_MAX_RX_THREADS; i++) {
+		if (!rx_tm_hdl->rx_thread[i])
+			continue;
+		dp_debug("calling thread %d to resume", i);
+		qdf_event_set(&rx_tm_hdl->rx_thread[i]->resume_event);
+	}
+
+	return QDF_STATUS_SUCCESS;
+}
+
+/**
+ * dp_rx_tm_shutdown() - shutdown all DP RX threads
+ * @rx_tm_hdl: dp_rx_tm_handle containing the overall thread infrastructure
+ *
+ * Return: QDF_STATUS_SUCCESS
+ */
+static QDF_STATUS dp_rx_tm_shutdown(struct dp_rx_tm_handle *rx_tm_hdl)
+{
+	int i;
+
+	for (i = 0; i < DP_MAX_RX_THREADS; i++) {
+		if (!rx_tm_hdl->rx_thread[i])
+			continue;
+		qdf_set_bit(RX_SHUTDOWN_EVENT,
+			    &rx_tm_hdl->rx_thread[i]->event_flag);
+		qdf_set_bit(RX_POST_EVENT,
+			    &rx_tm_hdl->rx_thread[i]->event_flag);
+	}
+
+	qdf_wake_up_interruptible(&rx_tm_hdl->wait_q);
+
+	for (i = 0; i < DP_MAX_RX_THREADS; i++) {
+		if (!rx_tm_hdl->rx_thread[i])
+			continue;
+		dp_debug("waiting for shutdown of thread %d", i);
+		qdf_wait_single_event(&rx_tm_hdl->rx_thread[i]->shutdown_event,
+				      0);
+	}
+	return QDF_STATUS_SUCCESS;
+}
+
+/**
+ * dp_rx_tm_deinit() - de-initialize RX thread infrastructure
+ * @rx_tm_hdl: dp_rx_tm_handle containing the overall thread
+ *            infrastructure
+ *
+ * Return: QDF_STATUS_SUCCESS
+ */
+QDF_STATUS dp_rx_tm_deinit(struct dp_rx_tm_handle *rx_tm_hdl)
+{
+	int i = 0;
+
+	dp_rx_tm_shutdown(rx_tm_hdl);
+
+	for (i = 0; i < DP_MAX_RX_THREADS; i++) {
+		if (!rx_tm_hdl->rx_thread[i])
+			continue;
+		dp_rx_tm_thread_deinit(rx_tm_hdl->rx_thread[i]);
+		qdf_mem_free(rx_tm_hdl->rx_thread[i]);
+	}
+	return QDF_STATUS_SUCCESS;
+}
+
+/**
+ * dp_rx_tm_select_thread() - select a DP RX thread for a nbuf
+ * @rx_tm_hdl: dp_rx_tm_handle containing the overall thread
+ *            infrastructure
+ * @nbuf_list: list of nbufs to be enqueued in to the thread
+ *
+ * The function relies on the presence of QDF_NBUF_CB_RX_CTX_ID
+ * in the nbuf list. Depending on the RX_CTX (copy engine or reo
+ * ring) on which the packet was received, the function selects
+ * a corresponding rx_thread.
+ * The function uses a simplistic mapping -
+ *
+ * RX_THREAD = RX_CTX % number of RX threads in the system.
+ *
+ * This also means that if RX_CTX < # rx threads, more than one
+ * interrupt source may end up on the same rx_thread.
+ *
+ * Return: rx thread ID selected for the nbuf
+ */
+static uint8_t dp_rx_tm_select_thread(struct dp_rx_tm_handle *rx_tm_hdl,
+				      qdf_nbuf_t nbuf_list)
+{
+	uint8_t selected_rx_thread;
+	uint8_t reo_ring_num = QDF_NBUF_CB_RX_CTX_ID(nbuf_list);
+
+	selected_rx_thread = reo_ring_num % DP_MAX_RX_THREADS;
+
+	return selected_rx_thread;
+}
+
+QDF_STATUS dp_rx_tm_enqueue_pkt(struct dp_rx_tm_handle *rx_tm_hdl,
+				qdf_nbuf_t nbuf_list)
+{
+	uint8_t selected_thread_id;
+
+	selected_thread_id = dp_rx_tm_select_thread(rx_tm_hdl, nbuf_list);
+
+	dp_rx_tm_thread_enqueue(rx_tm_hdl->rx_thread[selected_thread_id],
+				nbuf_list);
+	return QDF_STATUS_SUCCESS;
+}
+
diff --git a/core/dp/txrx3.0/dp_rx_thread.h b/core/dp/txrx3.0/dp_rx_thread.h
new file mode 100644
index 0000000..048ba65
--- /dev/null
+++ b/core/dp/txrx3.0/dp_rx_thread.h
@@ -0,0 +1,211 @@
+/*
+ * Copyright (c) 2014-2018 The Linux Foundation. All rights reserved.
+ *
+ * Permission to use, copy, modify, and/or distribute this software for
+ * any purpose with or without fee is hereby granted, provided that the
+ * above copyright notice and this permission notice appear in all
+ * copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
+ * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
+ * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
+ * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
+ * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
+ * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+ * PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#if !defined(__DP_RX_THREAD__H)
+#define __DP_RX_THREAD_H
+
+#include <qdf_lock.h>
+#include <qdf_event.h>
+#include <qdf_threads.h>
+#include <wlan_objmgr_vdev_obj.h>
+/* Maximum number of REO rings supported (for stats tracking) */
+#define DP_RX_TM_MAX_REO_RINGS 4
+
+/* Number of DP RX threads supported */
+#define DP_MAX_RX_THREADS 3
+
+/*
+ * Macro to get to wait_queue structure. Needed since wait_q is an object.
+ * API qdf_wait_queue_interruptible needs the object be passed to it and not a
+ * pointer
+ */
+#define DP_RX_THREAD_GET_WAIT_QUEUE_OBJ(rx_tm_handle_cmn) \
+		(((struct dp_rx_tm_handle *)rx_tm_handle_cmn)->wait_q)
+/*
+ * struct dp_rx_tm_handle_cmn - Opaque handle for rx_threads to store
+ * rx_tm_handle. This handle will be common for all the threads.
+ * Individual threads should not be accessing
+ * elements from dp_rx_tm_handle. It should be via an API.
+ */
+struct dp_rx_tm_handle_cmn;
+
+/**
+ * struct dp_rx_thread_stats - structure holding stats for DP RX thread
+ * @nbuf_queued: packets queued into the thread per reo ring
+ * @nbuf_dequeued: packets de-queued from the thread
+ * @nbuf_sent_to_stack: packets sent to the stack. some dequeued packets may be
+ *			dropped due to no peer or vdev, hence this stat.
+ * @nbufq_max_len: maximum number of nbuf_lists queued for the thread
+ * @dropped_invalid_vdev: packets(nbuf_list) dropped due to no vdev
+ * @dropped_invalid_peer: packets(nbuf_list) dropped due to no peer
+ * @dropped_others: packets dropped due to other reasons
+
+ */
+struct dp_rx_thread_stats {
+	unsigned int nbuf_queued[DP_RX_TM_MAX_REO_RINGS];
+	unsigned int nbuf_dequeued;
+	unsigned int nbuf_sent_to_stack;
+	unsigned int nbufq_max_len;
+	unsigned int dropped_invalid_vdev;
+	unsigned int dropped_invalid_peer;
+	unsigned int dropped_others;
+};
+
+/**
+ * struct dp_rx_thread - structure holding variables for a single DP RX thread
+ * @task: task structure corresponding to the thread
+ * @start_event: handle of Event for DP Rx thread to signal startup
+ * @suspend_event: handle of Event for DP Rx thread to signal suspend
+ * @resume_event: handle of Event for DP Rx thread to signal resume
+ * @shutdown_event: handle of Event for DP Rx thread to signal shutdown
+ * @event_flag: event flag to post events to DP Rx thread
+ * @nbuf_queue:nbuf queue used to store RX packets
+ * @nbufq_len: length of the nbuf queue
+ * @aff_mask: cuurent affinity mask of the DP Rx thread
+ * @stats: per thread stats
+ * @id: id of the dp_rx_thread (0 or 1 or 2..DP_MAX_RX_THREADS - 1)
+ * @rtm_handle_cmn: abstract RX TM handle. This allows access to the dp_rx_tm
+ *		    structures via APIs.
+ */
+struct dp_rx_thread {
+	qdf_thread_t *task;
+	qdf_spinlock_t lock;
+	qdf_event_t start_event;
+	qdf_event_t suspend_event;
+	qdf_event_t resume_event;
+	qdf_event_t shutdown_event;
+	unsigned long event_flag;
+	qdf_nbuf_queue_head_t nbuf_queue;
+	unsigned long aff_mask;
+	struct dp_rx_thread_stats stats;
+	uint8_t id;
+	struct dp_rx_tm_handle_cmn *rtm_handle_cmn;
+};
+
+/**
+ * enum dp_rx_thread_state - enum to keep track of the state of the rx thread
+ * @DP_RX_THREAD_INVALID: initial invalid state
+ * @DP_RX_THREAD_INIT: state after being initialized
+ * @DP_RX_THREAD_RUNNING: rx thread is functional(NOT suspended, processing
+ *			  packets or waiting on a wait_queue)
+ * @DP_RX_THREAD_SUSPENDED: rx_thread operation is suspeded from cfg8011 suspend
+ */
+enum dp_rx_thread_state {
+	DP_RX_THREAD_INVALID,
+	DP_RX_THREAD_INIT,
+	DP_RX_THREAD_RUNNING,
+	DP_RX_THREAD_SUSPENDED
+};
+
+/**
+ * struct dp_rx_tm_handle - DP RX thread infrastructure handle
+ * @txrx_handle_cmn: opaque txrx handle to get to pdev and soc
+ * wait_q: wait_queue for the rx_threads to wait on and expect an event
+ * @state: state of the rx_threads. All of them should be in the same state.
+ * @rx_thread: array of pointers of type struct dp_rx_thread
+ */
+struct dp_rx_tm_handle {
+	struct dp_txrx_handle_cmn *txrx_handle_cmn;
+	qdf_wait_queue_head_t wait_q;
+	enum dp_rx_thread_state state;
+	struct dp_rx_thread *rx_thread[DP_MAX_RX_THREADS];
+};
+
+/**
+ * dp_rx_tm_init() - initialize DP Rx thread infrastructure
+ * @rx_tm_hdl: dp_rx_tm_handle containing the overall thread infrastructure
+ * @num_dp_rx_threads: number of DP Rx threads to be initialized
+ *
+ * Return: QDF_STATUS_SUCCESS
+ */
+QDF_STATUS dp_rx_tm_init(struct dp_rx_tm_handle *rx_tm_hdl,
+			 uint8_t num_dp_rx_threads);
+
+/**
+ * dp_rx_tm_deinit() - de-initialize DP Rx thread infrastructure
+ * @rx_tm_hdl: dp_rx_tm_handle containing the overall thread infrastructure
+ *
+ * Return: QDF_STATUS_SUCCESS on success, error qdf status on failure
+ */
+QDF_STATUS dp_rx_tm_deinit(struct dp_rx_tm_handle *rx_tm_hdl);
+
+/**
+ * dp_rx_tm_enqueue_pkt() - enqueue RX packet into RXTI
+ * @rx_tm_hdl: dp_rx_tm_handle containing the overall thread infrastructure
+ * @nbuf_list: single or a list of nbufs to be enqueued into RXTI
+ *
+ * Return: QDF_STATUS_SUCCESS
+ */
+QDF_STATUS dp_rx_tm_enqueue_pkt(struct dp_rx_tm_handle *rx_tm_hdl,
+				qdf_nbuf_t nbuf_list);
+
+/**
+ * dp_rx_tm_suspend() - suspend all threads in RXTI
+ * @rx_tm_handle: pointer to dp_rx_tm_handle object
+ *
+ * Return: QDF_STATUS_SUCCESS on success, error qdf status on failure
+ */
+QDF_STATUS dp_rx_tm_suspend(struct dp_rx_tm_handle *rx_tm_handle);
+
+/**
+ * dp_rx_tm_resume() - resume all threads in RXTI
+ * @rx_tm_handle: pointer to dp_rx_tm_handle object
+ *
+ * Return: QDF_STATUS_SUCCESS on success, error qdf status on failure
+ */
+QDF_STATUS dp_rx_tm_resume(struct dp_rx_tm_handle *rx_tm_handle);
+
+/**
+ * dp_rx_tm_dump_stats() - dump stats for all threads in RXTI
+ * @rx_tm_handle: pointer to dp_rx_tm_handle object
+ *
+ * Return: QDF_STATUS_SUCCESS on success, error qdf status on failure
+ */
+QDF_STATUS dp_rx_tm_dump_stats(struct dp_rx_tm_handle *rx_tm_handle);
+
+/**
+ * dp_rx_thread_get_txrx_handle() - get txrx handle from rx_tm_handle_cmn
+ * @rx_tm_handle_cmn: opaque pointer to dp_rx_tm_handle_cmn struct
+ *
+ * Return: pointer to dp_txrx_handle_cmn handle
+ */
+static inline struct dp_txrx_handle_cmn*
+dp_rx_thread_get_txrx_handle(struct dp_rx_tm_handle_cmn *rx_tm_handle_cmn)
+{
+	return (((struct dp_rx_tm_handle *)rx_tm_handle_cmn)->txrx_handle_cmn);
+}
+
+/**
+ * dp_rx_thread_get_wait_queue() - get wait_q from dp_rx_tm_handle
+ * @rx_tm_handle_cmn: opaque pointer to dp_rx_tm_handle_cmn struct
+ *
+ * The function is needed since dp_rx_thread does not have access to the real
+ * dp_rx_tm_handle structure, but only an opaque dp_rx_tm_handle_cmn handle
+ *
+ * Return: pointer to dp_txrx_handle_cmn handle
+ */
+static inline qdf_wait_queue_head_t*
+dp_rx_thread_get_wait_queue(struct dp_rx_tm_handle_cmn *rx_tm_handle_cmn)
+{
+	struct dp_rx_tm_handle *rx_tm_handle;
+
+	rx_tm_handle = (struct dp_rx_tm_handle *)rx_tm_handle_cmn;
+	return &rx_tm_handle->wait_q;
+}
+
+#endif /* __DP_RX_THREAD_H */
diff --git a/core/dp/txrx3.0/dp_txrx.c b/core/dp/txrx3.0/dp_txrx.c
new file mode 100644
index 0000000..2f8b6cc
--- /dev/null
+++ b/core/dp/txrx3.0/dp_txrx.c
@@ -0,0 +1,70 @@
+/*
+ * Copyright (c) 2017-2018 The Linux Foundation. All rights reserved.
+ *
+ * Permission to use, copy, modify, and/or distribute this software for
+ * any purpose with or without fee is hereby granted, provided that the
+ * above copyright notice and this permission notice appear in all
+ * copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
+ * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
+ * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
+ * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
+ * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
+ * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+ * PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <wlan_objmgr_pdev_obj.h>
+#include <dp_txrx.h>
+#include <cdp_txrx_cmn.h>
+
+QDF_STATUS dp_txrx_init(ol_txrx_soc_handle soc, struct cdp_pdev *pdev,
+			struct dp_txrx_config *config)
+{
+	struct dp_txrx_handle *dp_ext_hdl;
+	QDF_STATUS qdf_status = QDF_STATUS_SUCCESS;
+
+	dp_ext_hdl = qdf_mem_malloc(sizeof(*dp_ext_hdl));
+	if (!dp_ext_hdl) {
+		dp_err("failed to alloc dp_txrx_handle");
+		QDF_ASSERT(0);
+		return QDF_STATUS_E_NOMEM;
+	}
+
+	dp_debug("dp_txrx_handle allocated");
+	dp_ext_hdl->soc = soc;
+	dp_ext_hdl->pdev = pdev;
+	cdp_soc_set_dp_txrx_handle(soc, dp_ext_hdl);
+	qdf_mem_copy(&dp_ext_hdl->config, config, sizeof(*config));
+	dp_ext_hdl->rx_tm_hdl.txrx_handle_cmn =
+				dp_txrx_get_cmn_hdl_frm_ext_hdl(dp_ext_hdl);
+
+	if (dp_ext_hdl->config.enable_rx_threads) {
+		qdf_status = dp_rx_tm_init(&dp_ext_hdl->rx_tm_hdl,
+					   dp_ext_hdl->config.num_rx_threads);
+	}
+
+	return qdf_status;
+}
+
+QDF_STATUS dp_txrx_deinit(ol_txrx_soc_handle soc)
+{
+	struct dp_txrx_handle *dp_ext_hdl;
+
+	if (!soc)
+		return QDF_STATUS_E_INVAL;
+
+	dp_ext_hdl = cdp_soc_get_dp_txrx_handle(soc);
+	if (!dp_ext_hdl)
+		return QDF_STATUS_E_FAULT;
+
+	dp_rx_tm_deinit(&dp_ext_hdl->rx_tm_hdl);
+	qdf_mem_free(dp_ext_hdl);
+	dp_info("dp_txrx_handle_t de-allocated");
+
+	cdp_soc_set_dp_txrx_handle(soc, NULL);
+
+	return QDF_STATUS_SUCCESS;
+}
diff --git a/core/dp/txrx3.0/dp_txrx.h b/core/dp/txrx3.0/dp_txrx.h
new file mode 100644
index 0000000..da46293
--- /dev/null
+++ b/core/dp/txrx3.0/dp_txrx.h
@@ -0,0 +1,248 @@
+/*
+ * Copyright (c) 2017-2018 The Linux Foundation. All rights reserved.
+ *
+ * Permission to use, copy, modify, and/or distribute this software for
+ * any purpose with or without fee is hereby granted, provided that the
+ * above copyright notice and this permission notice appear in all
+ * copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
+ * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
+ * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
+ * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
+ * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
+ * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+ * PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifndef _DP_TXRX_H
+#define _DP_TXRX_H
+
+#include <wlan_objmgr_psoc_obj.h>
+#include <dp_rx_thread.h>
+#include <qdf_trace.h>
+#include <cdp_txrx_cmn_struct.h>
+#include <cdp_txrx_cmn.h>
+
+/**
+ * struct dp_txrx_config - dp txrx configuration passed to dp txrx modules
+ * @enable_dp_rx_threads: enable DP rx threads or not
+ * @num_rx_threads: number of DP RX threads
+ */
+struct dp_txrx_config {
+	bool enable_rx_threads;
+	uint8_t num_rx_threads;
+};
+
+struct dp_txrx_handle_cmn;
+/**
+ * struct dp_txrx_handle - main dp txrx container handle
+ * @soc: ol_txrx_soc_handle soc handle
+ * @rx_tm_hdl: rx thread infrastructure handle
+ */
+struct dp_txrx_handle {
+	ol_txrx_soc_handle soc;
+	struct cdp_pdev *pdev;
+	struct dp_rx_tm_handle rx_tm_hdl;
+	struct dp_txrx_config config;
+};
+
+#ifdef FEATURE_WLAN_DP_RX_THREADS
+/**
+ * dp_txrx_get_cmn_hdl_frm_ext_hdl() - conversion func ext_hdl->txrx_handle_cmn
+ * @dp_ext_hdl: pointer to dp_txrx_handle structure
+ *
+ * Return: typecasted pointer of type - struct dp_txrx_handle_cmn
+ */
+static inline struct dp_txrx_handle_cmn *
+dp_txrx_get_cmn_hdl_frm_ext_hdl(struct dp_txrx_handle *dp_ext_hdl)
+{
+	return (struct dp_txrx_handle_cmn *)dp_ext_hdl;
+}
+
+/**
+ * dp_txrx_get_ext_hdl_frm_cmn_hdl() - conversion func txrx_handle_cmn->ext_hdl
+ * @txrx_cmn_hdl: pointer to dp_txrx_handle_cmn structure
+ *
+ * Return: typecasted pointer of type - struct dp_txrx_handle
+ */
+static inline struct dp_txrx_handle *
+dp_txrx_get_ext_hdl_frm_cmn_hdl(struct dp_txrx_handle_cmn *txrx_cmn_hdl)
+{
+	return (struct dp_txrx_handle *)txrx_cmn_hdl;
+}
+
+static inline ol_txrx_soc_handle
+dp_txrx_get_soc_from_ext_handle(struct dp_txrx_handle_cmn *txrx_cmn_hdl)
+{
+	struct dp_txrx_handle *dp_ext_hdl;
+
+	dp_ext_hdl = dp_txrx_get_ext_hdl_frm_cmn_hdl(txrx_cmn_hdl);
+
+	return dp_ext_hdl->soc;
+}
+
+static inline struct cdp_pdev*
+dp_txrx_get_pdev_from_ext_handle(struct dp_txrx_handle_cmn *txrx_cmn_hdl)
+{
+	struct dp_txrx_handle *dp_ext_hdl;
+
+	dp_ext_hdl = dp_txrx_get_ext_hdl_frm_cmn_hdl(txrx_cmn_hdl);
+
+	return dp_ext_hdl->pdev;
+}
+
+/**
+ * dp_txrx_init() - initialize DP TXRX module
+ * @soc: ol_txrx_soc_handle
+ * @config: configuration for DP TXRX modules
+ *
+ * Return: QDF_STATUS_SUCCESS on success, error qdf status on failure
+ */
+QDF_STATUS dp_txrx_init(ol_txrx_soc_handle soc, struct cdp_pdev *pdev,
+			struct dp_txrx_config *config);
+
+/**
+ * dp_txrx_deinit() - de-initialize DP TXRX module
+ * @soc: ol_txrx_soc_handle
+ *
+ * Return: QDF_STATUS_SUCCESS on success, error qdf status on failure
+ */
+QDF_STATUS dp_txrx_deinit(ol_txrx_soc_handle soc);
+
+/**
+ * dp_txrx_resume() - resume all threads
+ * @soc: ol_txrx_soc_handle object
+ *
+ * Return: QDF_STATUS_SUCCESS on success, error qdf status on failure
+ */
+static inline QDF_STATUS dp_txrx_resume(ol_txrx_soc_handle soc)
+{
+	struct dp_txrx_handle *dp_ext_hdl;
+	QDF_STATUS qdf_status = QDF_STATUS_SUCCESS;
+
+	if (!soc) {
+		qdf_status = QDF_STATUS_E_INVAL;
+		goto ret;
+	}
+
+	dp_ext_hdl = cdp_soc_get_dp_txrx_handle(soc);
+	if (!dp_ext_hdl) {
+		qdf_status = QDF_STATUS_E_FAULT;
+		goto ret;
+	}
+
+	qdf_status = dp_rx_tm_resume(&dp_ext_hdl->rx_tm_hdl);
+ret:
+	return qdf_status;
+}
+
+/**
+ * dp_txrx_suspend() - suspend all threads
+ * @soc: ol_txrx_soc_handle object
+ *
+ * Return: QDF_STATUS_SUCCESS on success, error qdf status on failure
+ */
+static inline QDF_STATUS dp_txrx_suspend(ol_txrx_soc_handle soc)
+{
+	struct dp_txrx_handle *dp_ext_hdl;
+	QDF_STATUS qdf_status = QDF_STATUS_SUCCESS;
+
+	if (!soc) {
+		qdf_status = QDF_STATUS_E_INVAL;
+		goto ret;
+	}
+
+	dp_ext_hdl = cdp_soc_get_dp_txrx_handle(soc);
+	if (!dp_ext_hdl) {
+		qdf_status = QDF_STATUS_E_FAULT;
+		goto ret;
+	}
+
+	qdf_status = dp_rx_tm_suspend(&dp_ext_hdl->rx_tm_hdl);
+
+ret:
+	return qdf_status;
+}
+
+static inline
+QDF_STATUS dp_rx_enqueue_pkt(ol_txrx_soc_handle soc, qdf_nbuf_t nbuf_list)
+{
+	struct dp_txrx_handle *dp_ext_hdl;
+	QDF_STATUS qdf_status = QDF_STATUS_SUCCESS;
+
+	if (!soc || !nbuf_list) {
+		qdf_status = QDF_STATUS_E_INVAL;
+		dp_err("invalid input params soc %pK nbuf %pK"
+		       , soc, nbuf_list);
+		goto ret;
+	}
+
+	dp_ext_hdl = cdp_soc_get_dp_txrx_handle(soc);
+	if (!dp_ext_hdl) {
+		qdf_status = QDF_STATUS_E_FAULT;
+		goto ret;
+	}
+
+	qdf_status = dp_rx_tm_enqueue_pkt(&dp_ext_hdl->rx_tm_hdl, nbuf_list);
+ret:
+	return qdf_status;
+}
+
+static inline QDF_STATUS dp_txrx_dump_stats(ol_txrx_soc_handle soc)
+{
+	struct dp_txrx_handle *dp_ext_hdl;
+	QDF_STATUS qdf_status = QDF_STATUS_SUCCESS;
+
+	if (!soc) {
+		qdf_status = QDF_STATUS_E_INVAL;
+		dp_err("invalid input params soc %pK", soc);
+		goto ret;
+	}
+
+	dp_ext_hdl = cdp_soc_get_dp_txrx_handle(soc);
+	if (!dp_ext_hdl) {
+		qdf_status = QDF_STATUS_E_FAULT;
+		goto ret;
+	}
+
+	qdf_status = dp_rx_tm_dump_stats(&dp_ext_hdl->rx_tm_hdl);
+ret:
+	return qdf_status;
+}
+#else
+static inline
+QDF_STATUS dp_txrx_init(ol_txrx_soc_handle soc, struct cdp_pdev *pdev,
+			    struct dp_txrx_config *config)
+{
+	return QDF_STATUS_SUCCESS;
+}
+
+static inline QDF_STATUS dp_txrx_deinit(ol_txrx_soc_handle soc)
+{
+	return QDF_STATUS_SUCCESS;
+}
+
+static inline QDF_STATUS dp_txrx_resume(ol_txrx_soc_handle soc)
+{
+	return QDF_STATUS_SUCCESS;
+}
+
+static inline QDF_STATUS dp_txrx_suspend(ol_txrx_soc_handle soc)
+{
+	return QDF_STATUS_SUCCESS;
+}
+
+static inline
+QDF_STATUS dp_rx_enqueue_pkt(ol_txrx_soc_handle soc, qdf_nbuf_t nbuf_list)
+{
+	return QDF_STATUS_SUCCESS;
+}
+
+static inline QDF_STATUS dp_txrx_dump_stats(ol_txrx_soc_handle soc)
+{
+	return QDF_STATUS_SUCCESS;
+}
+#endif /* FEATURE_WLAN_DP_RX_THREADS */
+#endif /* _DP_TXRX_H */
diff --git a/core/hdd/inc/wlan_hdd_cfg.h b/core/hdd/inc/wlan_hdd_cfg.h
index 290f8ca..c8cfeb6 100644
--- a/core/hdd/inc/wlan_hdd_cfg.h
+++ b/core/hdd/inc/wlan_hdd_cfg.h
@@ -45,10 +45,11 @@
 #define FW_MODULE_LOG_LEVEL_STRING_LENGTH  (512)
 #define TX_SCHED_WRR_PARAM_STRING_LENGTH   (50)
 #define TX_SCHED_WRR_PARAMS_NUM            (5)
-#define CFG_ENABLE_RX_THREAD		(1 << 0)
-#define CFG_ENABLE_RPS			(1 << 1)
-#define CFG_ENABLE_NAPI			(1 << 2)
-#define CFG_ENABLE_DYNAMIC_RPS		(1 << 3)
+#define CFG_ENABLE_RX_THREAD		BIT(0)
+#define CFG_ENABLE_RPS			BIT(1)
+#define CFG_ENABLE_NAPI			BIT(2)
+#define CFG_ENABLE_DYNAMIC_RPS		BIT(3)
+#define CFG_ENABLE_DP_RX_THREADS	BIT(4)
 
 #ifdef DHCP_SERVER_OFFLOAD
 #define IPADDR_NUM_ENTRIES     (4)
@@ -9143,18 +9144,18 @@
 
 /*
  * <ini>
- * rx_mode - Control to decide rx mode
+ * rx_mode - Control to decide rx mode for packet procesing
  *
  * @Min: 0
  * @Max: (CFG_ENABLE_RX_THREAD | CFG_ENABLE_RPS | CFG_ENABLE_NAPI | \
- *        CFG_ENABLE_DYNAMIC_RPS)
- * @Default: MDM_PLATFORM   -  0
- *           HELIUMPLUS     -  CFG_ENABLE_NAPI
- *           Other cases    -  (CFG_ENABLE_RX_THREAD | CFG_ENABLE_NAPI)
- *
- * This ini is used to decide mode for the rx path
- *
- * Supported Feature: NAPI
+ *	 CFG_ENABLE_DYNAMIC_RPS)
+ * Some possible configurations:
+ * rx_mode=0 - Uses tasklets for bottom half
+ * CFG_ENABLE_NAPI (rx_mode=4) - Uses NAPI for bottom half
+ * CFG_ENABLE_RX_THREAD | CFG_ENABLE_NAPI (rx_mode=5) - NAPI for bottom half,
+ * rx_thread for stack. Single threaded.
+ * CFG_ENABLE_DP_RX_THREAD | CFG_ENABLE_NAPI (rx_mode=10) - NAPI for bottom
+ * half, dp_rx_thread for stack processing. Supports multiple rx threads.
  *
  * Usage: Internal
  *
@@ -9163,17 +9164,37 @@
 #define CFG_RX_MODE_NAME     "rx_mode"
 #define CFG_RX_MODE_MIN      (0)
 #define CFG_RX_MODE_MAX      (CFG_ENABLE_RX_THREAD | CFG_ENABLE_RPS | \
-				 CFG_ENABLE_NAPI | CFG_ENABLE_DYNAMIC_RPS)
+				 CFG_ENABLE_NAPI | CFG_ENABLE_DYNAMIC_RPS | \
+				 CFG_ENABLE_DP_RX_THREADS)
 #ifdef MDM_PLATFORM
 #define CFG_RX_MODE_DEFAULT  (0)
 #elif defined(HELIUMPLUS)
 #define CFG_RX_MODE_DEFAULT  CFG_ENABLE_NAPI
+#elif defined(QCA_WIFI_QCA6290_11AX)
+#define CFG_RX_MODE_DEFAULT  (CFG_ENABLE_DP_RX_THREADS | CFG_ENABLE_NAPI)
 #else
 #define CFG_RX_MODE_DEFAULT  (CFG_ENABLE_RX_THREAD | CFG_ENABLE_NAPI)
 #endif
 
 /*
  * <ini>
+ * num_dp_rx_threads - Control to set the number of dp rx threads
+ *
+ * @Min: 1
+ * @Max: 4
+ * @Default: 1
+ *
+ * Usage: Internal
+ *
+ * </ini>
+ */
+#define CFG_NUM_DP_RX_THREADS_NAME     "num_dp_rx_threads"
+#define CFG_NUM_DP_RX_THREADS_MIN      (1)
+#define CFG_NUM_DP_RX_THREADS_MAX      (4)
+#define CFG_NUM_DP_RX_THREADS_DEFAULT  (1)
+
+/*
+ * <ini>
  * ce_service_max_yield_time - Control to set ce service max yield time (in us)
  *
  * @Min: 500
@@ -14458,6 +14479,7 @@
 	uint8_t dot11p_mode;
 	bool etsi13_srd_chan_in_master_mode;
 	uint8_t rx_mode;
+	uint8_t num_dp_rx_threads;
 	uint32_t ce_service_max_yield_time;
 	uint8_t ce_service_max_rx_ind_flush;
 	uint32_t napi_cpu_affinity_mask;
diff --git a/core/hdd/inc/wlan_hdd_main.h b/core/hdd/inc/wlan_hdd_main.h
index c01554f..4aa3b74 100644
--- a/core/hdd/inc/wlan_hdd_main.h
+++ b/core/hdd/inc/wlan_hdd_main.h
@@ -1876,6 +1876,8 @@
 	bool rps;
 	bool dynamic_rps;
 	bool enable_rxthread;
+	/* support for DP RX threads */
+	bool enable_dp_rx_threads;
 	bool napi_enable;
 	bool stop_modules_in_progress;
 	bool start_modules_in_progress;
@@ -2548,6 +2550,7 @@
 void wlan_hdd_deinit_tx_rx_histogram(struct hdd_context *hdd_ctx);
 void wlan_hdd_display_tx_rx_histogram(struct hdd_context *hdd_ctx);
 void wlan_hdd_clear_tx_rx_histogram(struct hdd_context *hdd_ctx);
+
 void
 wlan_hdd_display_netif_queue_history(struct hdd_context *hdd_ctx,
 				     enum qdf_stats_verbosity_level verb_lvl);
diff --git a/core/hdd/inc/wlan_hdd_softap_tx_rx.h b/core/hdd/inc/wlan_hdd_softap_tx_rx.h
index 9a594f0..fa968bd 100644
--- a/core/hdd/inc/wlan_hdd_softap_tx_rx.h
+++ b/core/hdd/inc/wlan_hdd_softap_tx_rx.h
@@ -98,7 +98,7 @@
 
 /**
  * hdd_softap_rx_packet_cbk() - Receive packet handler
- * @context: pointer to HDD context
+ * @adapter_context: pointer to HDD adapter
  * @rx_buf: pointer to rx qdf_nbuf chain
  *
  * Receive callback registered with the Data Path.  The Data Path will
@@ -108,7 +108,7 @@
  * Return: QDF_STATUS_E_FAILURE if any errors encountered,
  *	   QDF_STATUS_SUCCESS otherwise
  */
-QDF_STATUS hdd_softap_rx_packet_cbk(void *context, qdf_nbuf_t rx_buf);
+QDF_STATUS hdd_softap_rx_packet_cbk(void *adapter_context, qdf_nbuf_t rx_buf);
 
 /**
  * hdd_softap_deregister_sta() - Deregister a STA with the Data Path
diff --git a/core/hdd/inc/wlan_hdd_tx_rx.h b/core/hdd/inc/wlan_hdd_tx_rx.h
index 2e633e6..8bd1318 100644
--- a/core/hdd/inc/wlan_hdd_tx_rx.h
+++ b/core/hdd/inc/wlan_hdd_tx_rx.h
@@ -33,6 +33,27 @@
 
 struct hdd_context;
 
+#define hdd_dp_alert(params...) QDF_TRACE_FATAL(QDF_MODULE_ID_HDD_DATA, params)
+#define hdd_dp_err(params...) QDF_TRACE_ERROR(QDF_MODULE_ID_HDD_DATA, params)
+#define hdd_dp_warn(params...) QDF_TRACE_WARN(QDF_MODULE_ID_HDD_DATA, params)
+#define hdd_dp_info(params...) QDF_TRACE_INFO(QDF_MODULE_ID_HDD_DATA, params)
+#define hdd_dp_debug(params...) QDF_TRACE_DEBUG(QDF_MODULE_ID_HDD_DATA, params)
+
+#define hdd_dp_alert_rl(params...) \
+			QDF_TRACE_FATAL_RL(QDF_MODULE_ID_HDD_DATA, params)
+#define hdd_dp_err_rl(params...) \
+			QDF_TRACE_ERROR_RL(QDF_MODULE_ID_HDD_DATA, params)
+#define hdd_dp_warn_rl(params...) \
+			QDF_TRACE_WARN_RL(QDF_MODULE_ID_HDD_DATA, params)
+#define hdd_dp_info_rl(params...) \
+			QDF_TRACE_INFO_RL(QDF_MODULE_ID_HDD_DATA, params)
+#define hdd_dp_debug_rl(params...) \
+			QDF_TRACE_DEBUG_RL(QDF_MODULE_ID_HDD_DATA, params)
+
+#define hdd_dp_enter() hdd_dp_debug("enter")
+#define hdd_dp_enter_dev(dev) hdd_dp_debug("enter(%s)", (dev)->name)
+#define hdd_dp_exit() hdd_dp_debug("exit")
+
 #define HDD_ETHERTYPE_802_1_X              0x888E
 #define HDD_ETHERTYPE_802_1_X_FRAME_OFFSET 12
 #ifdef FEATURE_WLAN_WAPI
@@ -55,7 +76,33 @@
 
 QDF_STATUS hdd_init_tx_rx(struct hdd_adapter *adapter);
 QDF_STATUS hdd_deinit_tx_rx(struct hdd_adapter *adapter);
-QDF_STATUS hdd_rx_packet_cbk(void *context, qdf_nbuf_t rxBuf);
+
+/**
+ * hdd_rx_packet_cbk() - Receive packet handler
+ * @adapter_context: pointer to HDD adapter context
+ * @rxBuf: pointer to rx qdf_nbuf
+ *
+ * Receive callback registered with data path.  DP will call this to notify
+ * the HDD when one or more packets were received for a registered
+ * STA.
+ *
+ * Return: QDF_STATUS_E_FAILURE if any errors encountered,
+ *	   QDF_STATUS_SUCCESS otherwise
+ */
+QDF_STATUS hdd_rx_packet_cbk(void *adapter_context, qdf_nbuf_t rxBuf);
+
+/**
+ * hdd_rx_pkt_thread_enqueue_cbk() - receive pkt handler to enqueue into thread
+ * @adapter: pointer to HDD adapter
+ * @rxBuf: pointer to rx qdf_nbuf
+ *
+ * Receive callback registered with DP layer which enqueues packets into dp rx
+ * thread
+ * Return: QDF_STATUS_E_FAILURE if any errors encountered,
+ *	   QDF_STATUS_SUCCESS otherwise
+ */
+QDF_STATUS hdd_rx_pkt_thread_enqueue_cbk(void *adapter_context,
+					 qdf_nbuf_t nbuf_list);
 
 /**
  * hdd_rx_ol_init() - Initialize Rx mode(LRO or GRO) method
diff --git a/core/hdd/src/wlan_hdd_assoc.c b/core/hdd/src/wlan_hdd_assoc.c
index 694f655..2327f42 100644
--- a/core/hdd/src/wlan_hdd_assoc.c
+++ b/core/hdd/src/wlan_hdd_assoc.c
@@ -2066,7 +2066,15 @@
 
 	/* Register the vdev transmit and receive functions */
 	qdf_mem_zero(&txrx_ops, sizeof(txrx_ops));
-	txrx_ops.rx.rx = hdd_rx_packet_cbk;
+
+	if (adapter->hdd_ctx->enable_dp_rx_threads) {
+		txrx_ops.rx.rx = hdd_rx_pkt_thread_enqueue_cbk;
+		txrx_ops.rx.rx_stack = hdd_rx_packet_cbk;
+	} else {
+		txrx_ops.rx.rx = hdd_rx_packet_cbk;
+		txrx_ops.rx.rx_stack = NULL;
+	}
+
 	txrx_ops.rx.stats_rx = hdd_tx_rx_collect_connectivity_stats_info;
 
 	adapter->txrx_vdev = (void *)cdp_get_vdev_from_vdev_id(soc,
diff --git a/core/hdd/src/wlan_hdd_cfg.c b/core/hdd/src/wlan_hdd_cfg.c
index b3c231d..d5cfea4 100644
--- a/core/hdd/src/wlan_hdd_cfg.c
+++ b/core/hdd/src/wlan_hdd_cfg.c
@@ -3887,6 +3887,13 @@
 		CFG_RX_MODE_MIN,
 		CFG_RX_MODE_MAX),
 
+	REG_VARIABLE(CFG_NUM_DP_RX_THREADS_NAME, WLAN_PARAM_Integer,
+		     struct hdd_config, num_dp_rx_threads,
+		     VAR_FLAGS_OPTIONAL | VAR_FLAGS_RANGE_CHECK_ASSUME_DEFAULT,
+		     CFG_NUM_DP_RX_THREADS_DEFAULT,
+		     CFG_NUM_DP_RX_THREADS_MIN,
+		     CFG_NUM_DP_RX_THREADS_MAX),
+
 	REG_VARIABLE(CFG_CE_SERVICE_MAX_YIELD_TIME_NAME, WLAN_PARAM_Integer,
 		struct hdd_config, ce_service_max_yield_time,
 		VAR_FLAGS_OPTIONAL | VAR_FLAGS_RANGE_CHECK_ASSUME_DEFAULT,
@@ -7383,6 +7390,8 @@
 
 	if (hdd_ctx->config->rx_mode & CFG_ENABLE_RX_THREAD)
 		hdd_ctx->enable_rxthread = true;
+	else if (hdd_ctx->config->rx_mode & CFG_ENABLE_DP_RX_THREADS)
+		hdd_ctx->enable_dp_rx_threads = true;
 
 	if (hdd_ctx->config->rx_mode & CFG_ENABLE_RPS)
 		hdd_ctx->rps = true;
@@ -7392,6 +7401,11 @@
 
 	if (hdd_ctx->config->rx_mode & CFG_ENABLE_DYNAMIC_RPS)
 		hdd_ctx->dynamic_rps = true;
+
+	hdd_info("rx_mode:%u dp_rx_threads:%u rx_thread:%u napi:%u rps:%u dynamic rps %u",
+		 hdd_ctx->config->rx_mode, hdd_ctx->enable_dp_rx_threads,
+		 hdd_ctx->enable_rxthread, hdd_ctx->napi_enable,
+		 hdd_ctx->rps, hdd_ctx->dynamic_rps);
 }
 
 /**
diff --git a/core/hdd/src/wlan_hdd_main.c b/core/hdd/src/wlan_hdd_main.c
index 733080c..39ab8a7 100644
--- a/core/hdd/src/wlan_hdd_main.c
+++ b/core/hdd/src/wlan_hdd_main.c
@@ -125,6 +125,7 @@
 #include "sme_power_save_api.h"
 #include "enet.h"
 #include <cdp_txrx_cmn_struct.h>
+#include <dp_txrx.h>
 #include "wlan_hdd_sysfs.h"
 #include "wlan_disa_ucfg_api.h"
 #include "wlan_disa_obj_mgmt_api.h"
@@ -7355,11 +7356,10 @@
  *
  * Returns: None
  */
-static inline
-void hdd_display_periodic_stats(struct hdd_context *hdd_ctx,
-				bool data_in_interval)
+static void hdd_display_periodic_stats(struct hdd_context *hdd_ctx,
+				       bool data_in_interval)
 {
-	static u32 counter;
+	static uint32_t counter;
 	static bool data_in_time_period;
 	ol_txrx_pdev_handle pdev;
 
@@ -7379,6 +7379,11 @@
 	if (counter * hdd_ctx->config->busBandwidthComputeInterval >=
 		hdd_ctx->config->periodic_stats_disp_time * 1000) {
 		if (data_in_time_period) {
+			wlan_hdd_display_txrx_stats(hdd_ctx);
+			dp_txrx_dump_stats(cds_get_context(QDF_MODULE_ID_SOC));
+			cdp_display_stats(cds_get_context(QDF_MODULE_ID_SOC),
+					  CDP_RX_RING_STATS,
+					  QDF_STATS_VERBOSITY_LEVEL_LOW);
 			cdp_display_stats(cds_get_context(QDF_MODULE_ID_SOC),
 					  CDP_TXRX_PATH_STATS,
 					  QDF_STATS_VERBOSITY_LEVEL_LOW);
@@ -9350,6 +9355,9 @@
 		hdd_ctx->config->TxFlowStopQueueThreshold;
 	cds_cfg->tx_flow_start_queue_offset =
 		hdd_ctx->config->TxFlowStartQueueOffset;
+	/* configuration for DP RX Threads */
+	cds_cfg->enable_dp_rx_threads = hdd_ctx->enable_dp_rx_threads;
+	cds_cfg->num_dp_rx_threads = hdd_ctx->config->num_dp_rx_threads;
 }
 #else
 static inline void hdd_txrx_populate_cds_config(struct cds_config_info
diff --git a/core/hdd/src/wlan_hdd_power.c b/core/hdd/src/wlan_hdd_power.c
index bfadba4..553b63e 100644
--- a/core/hdd/src/wlan_hdd_power.c
+++ b/core/hdd/src/wlan_hdd_power.c
@@ -74,10 +74,10 @@
 #include "cds_utils.h"
 #include "wlan_hdd_packet_filter_api.h"
 #include "wlan_cfg80211_scan.h"
+#include <dp_txrx.h>
 #include "wlan_ipa_ucfg_api.h"
 #include <wlan_cfg80211_mc_cp_stats.h>
 #include "wlan_p2p_ucfg_api.h"
-
 /* Preprocessor definitions and constants */
 #ifdef QCA_WIFI_NAPIER_EMULATION
 #define HDD_SSR_BRING_UP_TIME 3000000
@@ -85,6 +85,9 @@
 #define HDD_SSR_BRING_UP_TIME 30000
 #endif
 
+/* timeout in msec to wait for RX_THREAD to suspend */
+#define HDD_RXTHREAD_SUSPEND_TIMEOUT 200
+
 /* Type declarations */
 
 #ifdef FEATURE_WLAN_DIAG_SUPPORT
@@ -1232,12 +1235,11 @@
 		hdd_ctx->is_scheduler_suspended = false;
 		hdd_ctx->is_wiphy_suspended = false;
 	}
-#ifdef QCA_CONFIG_SMP
+
 	if (true == hdd_ctx->is_ol_rx_thread_suspended) {
 		complete(&cds_sched_context->ol_resume_rx_event);
 		hdd_ctx->is_ol_rx_thread_suspended = false;
 	}
-#endif
 
 	hdd_wlan_stop_modules(hdd_ctx, false);
 
@@ -1538,13 +1540,16 @@
 		scheduler_resume();
 		hdd_ctx->is_scheduler_suspended = false;
 	}
-#ifdef QCA_CONFIG_SMP
+
 	/* Resume tlshim Rx thread */
-	if (hdd_ctx->is_ol_rx_thread_suspended) {
+	if (hdd_ctx->enable_rxthread && hdd_ctx->is_ol_rx_thread_suspended) {
 		complete(&cds_sched_context->ol_resume_rx_event);
 		hdd_ctx->is_ol_rx_thread_suspended = false;
 	}
-#endif
+
+	if (hdd_ctx->enable_dp_rx_threads)
+		dp_txrx_resume(cds_get_context(QDF_MODULE_ID_SOC));
+
 
 	MTRACE(qdf_trace(QDF_MODULE_ID_HDD,
 			 TRACE_CODE_HDD_CFG80211_RESUME_WLAN,
@@ -1595,9 +1600,6 @@
 static int __wlan_hdd_cfg80211_suspend_wlan(struct wiphy *wiphy,
 				     struct cfg80211_wowlan *wow)
 {
-#ifdef QCA_CONFIG_SMP
-#define RX_TLSHIM_SUSPEND_TIMEOUT 200   /* msecs */
-#endif
 	struct hdd_context *hdd_ctx = wiphy_priv(wiphy);
 	p_cds_sched_context cds_sched_context = get_cds_sched_ctxt();
 	struct hdd_adapter *adapter;
@@ -1722,22 +1724,27 @@
 	}
 	hdd_ctx->is_scheduler_suspended = true;
 
-#ifdef QCA_CONFIG_SMP
-	/* Suspend tlshim rx thread */
-	set_bit(RX_SUSPEND_EVENT, &cds_sched_context->ol_rx_event_flag);
-	wake_up_interruptible(&cds_sched_context->ol_rx_wait_queue);
-	rc = wait_for_completion_timeout(&cds_sched_context->
-					 ol_suspend_rx_event,
-					 msecs_to_jiffies
-						 (RX_TLSHIM_SUSPEND_TIMEOUT));
-	if (!rc) {
-		clear_bit(RX_SUSPEND_EVENT,
-			  &cds_sched_context->ol_rx_event_flag);
-		hdd_err("Failed to stop tl_shim rx thread");
-		goto resume_all;
+	if (hdd_ctx->enable_rxthread) {
+		/* Suspend tlshim rx thread */
+		set_bit(RX_SUSPEND_EVENT, &cds_sched_context->ol_rx_event_flag);
+		wake_up_interruptible(&cds_sched_context->ol_rx_wait_queue);
+		rc = wait_for_completion_timeout(&cds_sched_context->
+						 ol_suspend_rx_event,
+						 msecs_to_jiffies
+						 (HDD_RXTHREAD_SUSPEND_TIMEOUT)
+						);
+		if (!rc) {
+			clear_bit(RX_SUSPEND_EVENT,
+				  &cds_sched_context->ol_rx_event_flag);
+			hdd_err("Failed to stop tl_shim rx thread");
+			goto resume_all;
+		}
+		hdd_ctx->is_ol_rx_thread_suspended = true;
 	}
-	hdd_ctx->is_ol_rx_thread_suspended = true;
-#endif
+
+	if (hdd_ctx->enable_dp_rx_threads)
+		dp_txrx_suspend(cds_get_context(QDF_MODULE_ID_SOC));
+
 	if (hdd_suspend_wlan() < 0)
 		goto resume_all;
 
@@ -1751,15 +1758,10 @@
 	hdd_exit();
 	return 0;
 
-#ifdef QCA_CONFIG_SMP
 resume_all:
-
 	scheduler_resume();
 	hdd_ctx->is_scheduler_suspended = false;
-#endif
-
 resume_tx:
-
 	hdd_resume_wlan();
 	return -ETIME;
 
diff --git a/core/hdd/src/wlan_hdd_softap_tx_rx.c b/core/hdd/src/wlan_hdd_softap_tx_rx.c
index fe8f78c..4dc9215 100644
--- a/core/hdd/src/wlan_hdd_softap_tx_rx.c
+++ b/core/hdd/src/wlan_hdd_softap_tx_rx.c
@@ -825,7 +825,7 @@
 	}
 }
 
-QDF_STATUS hdd_softap_rx_packet_cbk(void *context, qdf_nbuf_t rx_buf)
+QDF_STATUS hdd_softap_rx_packet_cbk(void *adapter_context, qdf_nbuf_t rx_buf)
 {
 	struct hdd_adapter *adapter = NULL;
 	int rxstat;
@@ -837,13 +837,13 @@
 	uint8_t staid;
 
 	/* Sanity check on inputs */
-	if (unlikely((NULL == context) || (NULL == rx_buf))) {
+	if (unlikely((!adapter_context) || (!rx_buf))) {
 		QDF_TRACE(QDF_MODULE_ID_HDD_SAP_DATA, QDF_TRACE_LEVEL_ERROR,
 			  "%s: Null params being passed", __func__);
 		return QDF_STATUS_E_FAILURE;
 	}
 
-	adapter = (struct hdd_adapter *)context;
+	adapter = (struct hdd_adapter *)adapter_context;
 	if (unlikely(WLAN_HDD_ADAPTER_MAGIC != adapter->magic)) {
 		QDF_TRACE(QDF_MODULE_ID_HDD_DATA, QDF_TRACE_LEVEL_ERROR,
 			  "Magic cookie(%x) for adapter sanity verification is invalid",
@@ -1036,8 +1036,17 @@
 
 	/* Register the vdev transmit and receive functions */
 	qdf_mem_zero(&txrx_ops, sizeof(txrx_ops));
-	txrx_ops.rx.rx = hdd_softap_rx_packet_cbk;
+
 	txrx_ops.tx.tx_comp = hdd_softap_notify_tx_compl_cbk;
+
+	if (adapter->hdd_ctx->enable_dp_rx_threads) {
+		txrx_ops.rx.rx = hdd_rx_pkt_thread_enqueue_cbk;
+		txrx_ops.rx.rx_stack = hdd_softap_rx_packet_cbk;
+	} else {
+		txrx_ops.rx.rx = hdd_softap_rx_packet_cbk;
+		txrx_ops.rx.rx_stack = NULL;
+	}
+
 	cdp_vdev_register(soc,
 		(struct cdp_vdev *)cdp_get_vdev_from_vdev_id(soc,
 		(struct cdp_pdev *)pdev, adapter->session_id),
diff --git a/core/hdd/src/wlan_hdd_stats.c b/core/hdd/src/wlan_hdd_stats.c
index caf892c..bc079b3 100644
--- a/core/hdd/src/wlan_hdd_stats.c
+++ b/core/hdd/src/wlan_hdd_stats.c
@@ -6017,3 +6017,42 @@
 	hdd_exit();
 	return 0;
 }
+
+void wlan_hdd_display_txrx_stats(struct hdd_context *hdd_ctx)
+{
+	struct hdd_adapter *adapter = NULL;
+	struct hdd_tx_rx_stats *stats;
+	int i = 0;
+	uint32_t total_rx_pkt, total_rx_dropped,
+		 total_rx_delv, total_rx_refused;
+
+	hdd_for_each_adapter(hdd_ctx, adapter) {
+		total_rx_pkt = 0;
+		total_rx_dropped = 0;
+		total_rx_delv = 0;
+		total_rx_refused = 0;
+		stats = &adapter->hdd_stats.tx_rx_stats;
+		hdd_info("adapter: %u", adapter->session_id);
+		for (; i < NUM_CPUS; i++) {
+			total_rx_pkt += stats->rx_packets[i];
+			total_rx_dropped += stats->rx_dropped[i];
+			total_rx_delv += stats->rx_delivered[i];
+			total_rx_refused += stats->rx_refused[i];
+		}
+
+		hdd_info("Total Transmit - called %u, dropped %u orphan %u",
+			 stats->tx_called, stats->tx_dropped,
+			 stats->tx_orphaned);
+
+		for (i = 0; i < NUM_CPUS; i++) {
+			if (stats->rx_packets[i] == 0)
+				continue;
+			hdd_info("Rx CPU[%d]: packets %u, dropped %u, delivered %u, refused %u",
+				 i, stats->rx_packets[i], stats->rx_dropped[i],
+				 stats->rx_delivered[i], stats->rx_refused[i]);
+		}
+		hdd_info("Total Receive - packets %u, dropped %u, delivered %u, refused %u",
+			 total_rx_pkt, total_rx_dropped, total_rx_delv,
+			 total_rx_refused);
+	}
+}
diff --git a/core/hdd/src/wlan_hdd_stats.h b/core/hdd/src/wlan_hdd_stats.h
index d59fc7f..4fc80c9 100644
--- a/core/hdd/src/wlan_hdd_stats.h
+++ b/core/hdd/src/wlan_hdd_stats.h
@@ -462,4 +462,14 @@
  * Return: QDF_STATUS_SUCCESS if adapter's statistics were updated
  */
 int wlan_hdd_request_station_stats(struct hdd_adapter *adapter);
+
+/**
+ * wlan_hdd_display_txrx_stats() - display HDD txrx stats summary
+ * @hdd_ctx: hdd context
+ *
+ * Display TXRX Stats for all adapters
+ *
+ * Return: none
+ */
+void wlan_hdd_display_txrx_stats(struct hdd_context *hdd_ctx);
 #endif /* end #if !defined(WLAN_HDD_STATS_H) */
diff --git a/core/hdd/src/wlan_hdd_tx_rx.c b/core/hdd/src/wlan_hdd_tx_rx.c
index c5c0f28..92c04ca 100644
--- a/core/hdd/src/wlan_hdd_tx_rx.c
+++ b/core/hdd/src/wlan_hdd_tx_rx.c
@@ -59,6 +59,7 @@
 #include "wma_api.h"
 
 #include "wlan_hdd_nud_tracking.h"
+#include "dp_txrx.h"
 
 #ifdef QCA_LL_TX_FLOW_CONTROL_V2
 /*
@@ -1818,19 +1819,17 @@
 }
 #endif
 
-/**
- * hdd_rx_packet_cbk() - Receive packet handler
- * @context: pointer to HDD context
- * @rxBuf: pointer to rx qdf_nbuf
- *
- * Receive callback registered with TL.  TL will call this to notify
- * the HDD when one or more packets were received for a registered
- * STA.
- *
- * Return: QDF_STATUS_E_FAILURE if any errors encountered,
- *	   QDF_STATUS_SUCCESS otherwise
- */
-QDF_STATUS hdd_rx_packet_cbk(void *context, qdf_nbuf_t rxBuf)
+QDF_STATUS hdd_rx_pkt_thread_enqueue_cbk(void *adapter,
+					 qdf_nbuf_t nbuf_list) {
+	if (unlikely((!adapter) || (!nbuf_list))) {
+		hdd_err("Null params being passed");
+		return QDF_STATUS_E_FAILURE;
+	}
+	return dp_rx_enqueue_pkt(cds_get_context(QDF_MODULE_ID_SOC), nbuf_list);
+}
+
+QDF_STATUS hdd_rx_packet_cbk(void *adapter_context,
+			     qdf_nbuf_t rxBuf)
 {
 	struct hdd_adapter *adapter = NULL;
 	struct hdd_context *hdd_ctx = NULL;
@@ -1846,13 +1845,13 @@
 	bool track_arp = false;
 
 	/* Sanity check on inputs */
-	if (unlikely((NULL == context) || (NULL == rxBuf))) {
+	if (unlikely((!adapter_context) || (!rxBuf))) {
 		QDF_TRACE(QDF_MODULE_ID_HDD_DATA, QDF_TRACE_LEVEL_ERROR,
 			  "%s: Null params being passed", __func__);
 		return QDF_STATUS_E_FAILURE;
 	}
 
-	adapter = (struct hdd_adapter *)context;
+	adapter = (struct hdd_adapter *)adapter_context;
 	if (unlikely(WLAN_HDD_ADAPTER_MAGIC != adapter->magic)) {
 		QDF_TRACE(QDF_MODULE_ID_HDD_DATA, QDF_TRACE_LEVEL_ERROR,
 			  "Magic cookie(%x) for adapter sanity verification is invalid",