ipc: wcd-dsp-glink: move wdsp ipc to rpmsg

Add support for wdsp communication over rpmsg to enable
inter process communication between application processor
and codec DSP.

Change-Id: I6679e9d8b104bbd41e19bbf788a41129dc97da88
Signed-off-by: Vidyakumar Athota <vathota@codeaurora.org>
diff --git a/ipc/wcd-dsp-glink.c b/ipc/wcd-dsp-glink.c
index 400fd06..04df026 100644
--- a/ipc/wcd-dsp-glink.c
+++ b/ipc/wcd-dsp-glink.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2016-2017, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2016-2018, The Linux Foundation. All rights reserved.
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License version 2 and
@@ -13,34 +13,33 @@
 #include <linux/init.h>
 #include <linux/kernel.h>
 #include <linux/module.h>
-#include <linux/mutex.h>
+#include <linux/spinlock.h>
 #include <linux/errno.h>
 #include <linux/fs.h>
 #include <linux/uaccess.h>
 #include <linux/slab.h>
-#include <linux/list.h>
 #include <linux/cdev.h>
 #include <linux/platform_device.h>
+#include <linux/of_device.h>
 #include <linux/vmalloc.h>
-#include <soc/qcom/glink.h>
+#include <linux/rpmsg.h>
 #include "sound/wcd-dsp-glink.h"
 
 #define WDSP_GLINK_DRIVER_NAME "wcd-dsp-glink"
 #define WDSP_MAX_WRITE_SIZE (256 * 1024)
 #define WDSP_MAX_READ_SIZE (4 * 1024)
-#define WDSP_MAX_NO_OF_INTENTS (20)
-#define WDSP_MAX_NO_OF_CHANNELS (10)
 #define WDSP_WRITE_PKT_SIZE (sizeof(struct wdsp_write_pkt))
-#define WDSP_REG_PKT_SIZE (sizeof(struct wdsp_reg_pkt))
 #define WDSP_CMD_PKT_SIZE (sizeof(struct wdsp_cmd_pkt))
-#define WDSP_CH_CFG_SIZE (sizeof(struct wdsp_glink_ch_cfg))
 
 #define MINOR_NUMBER_COUNT 1
-#define WDSP_EDGE "wdsp"
 #define RESP_QUEUE_SIZE 3
-#define QOS_PKT_SIZE 1024
 #define TIMEOUT_MS 1000
 
+enum wdsp_ch_state {
+	WDSP_CH_DISCONNECTED,
+	WDSP_CH_CONNECTED,
+};
+
 struct wdsp_glink_dev {
 	struct class *cls;
 	struct device *dev;
@@ -48,7 +47,7 @@
 	dev_t dev_num;
 };
 
-struct wdsp_glink_rsp_que {
+struct wdsp_rsp_que {
 	/* Size of valid data in buffer */
 	u32 buf_size;
 
@@ -56,687 +55,230 @@
 	u8 buf[WDSP_MAX_READ_SIZE];
 };
 
-struct wdsp_glink_tx_buf {
+struct wdsp_ch {
+	struct wdsp_glink_priv *wpriv;
+	/* rpmsg handle */
+	void *handle;
+	/* Channel states like connect, disconnect */
+	int ch_state;
+	char ch_name[RPMSG_NAME_SIZE];
+	spinlock_t ch_lock;
+};
+
+struct wdsp_tx_buf {
 	struct work_struct tx_work;
-	struct work_struct free_tx_work;
 
 	/* Glink channel information */
-	struct wdsp_glink_ch *ch;
+	struct wdsp_ch *ch;
 
 	/* Tx buffer to send to glink */
 	u8 buf[0];
 };
 
-struct wdsp_glink_ch {
-	struct wdsp_glink_priv *wpriv;
-
-	/* Glink channel handle */
-	void *handle;
-
-	/* Channel states like connect, disconnect */
-	int channel_state;
-	struct mutex mutex;
-
-	/* To free up the channel memory */
-	bool free_mem;
-
-	/* Glink local channel open work */
-	struct work_struct lcl_ch_open_wrk;
-
-	/* Glink local channel close work */
-	struct work_struct lcl_ch_cls_wrk;
-
-	/* Wait for ch connect state before sending any command */
-	wait_queue_head_t ch_connect_wait;
-
-	/*
-	 * Glink channel configuration. This has to be the last
-	 * member of the strucuture as it has variable size
-	 */
-	struct wdsp_glink_ch_cfg ch_cfg;
-};
-
-struct wdsp_glink_state {
-	/* Glink link state information */
-	enum glink_link_state link_state;
-	void *handle;
-};
-
 struct wdsp_glink_priv {
 	/* Respone buffer related */
 	u8 rsp_cnt;
-	struct wdsp_glink_rsp_que rsp[RESP_QUEUE_SIZE];
+	struct wdsp_rsp_que rsp[RESP_QUEUE_SIZE];
 	struct completion rsp_complete;
-	struct mutex rsp_mutex;
+	spinlock_t rsp_lock;
 
 	/* Glink channel related */
-	struct mutex glink_mutex;
-	struct wdsp_glink_state glink_state;
-	struct wdsp_glink_ch **ch;
-	u8 no_of_channels;
-	struct work_struct ch_open_cls_wrk;
+	int no_of_channels;
+	struct wdsp_ch **ch;
 	struct workqueue_struct *work_queue;
+	/* Wait for all channels state before sending any command */
+	wait_queue_head_t ch_state_wait;
 
-	wait_queue_head_t link_state_wait;
-
+	struct wdsp_glink_dev *wdev;
 	struct device *dev;
 };
+static struct wdsp_glink_priv *wpriv;
 
-static int wdsp_glink_close_ch(struct wdsp_glink_ch *ch);
-static int wdsp_glink_open_ch(struct wdsp_glink_ch *ch);
-
-/*
- * wdsp_glink_free_tx_buf_work - Work function to free tx pkt
- * work:      Work structure
- */
-static void wdsp_glink_free_tx_buf_work(struct work_struct *work)
-{
-	struct wdsp_glink_tx_buf *tx_buf;
-
-	tx_buf = container_of(work, struct wdsp_glink_tx_buf,
-			      free_tx_work);
-	vfree(tx_buf);
-}
-
-/*
- * wdsp_glink_free_tx_buf - Function to free tx buffer
- * priv:        Pointer to the channel
- * pkt_priv:    Pointer to the tx buffer
- */
-static void wdsp_glink_free_tx_buf(const void *priv, const void *pkt_priv)
-{
-	struct wdsp_glink_tx_buf *tx_buf = (struct wdsp_glink_tx_buf *)pkt_priv;
-	struct wdsp_glink_priv *wpriv;
-	struct wdsp_glink_ch *ch;
-
-	if (!priv) {
-		pr_err("%s: Invalid priv\n", __func__);
-		return;
-	}
-	if (!tx_buf) {
-		pr_err("%s: Invalid tx_buf\n", __func__);
-		return;
-	}
-
-	ch = (struct wdsp_glink_ch *)priv;
-	wpriv = ch->wpriv;
-	/* Work queue to free tx pkt */
-	INIT_WORK(&tx_buf->free_tx_work, wdsp_glink_free_tx_buf_work);
-	queue_work(wpriv->work_queue, &tx_buf->free_tx_work);
-}
-
-/*
- * wdsp_glink_notify_rx - Glink notify rx callback for responses
- * handle:      Opaque Channel handle returned by GLink
- * priv:        Private pointer to the channel
- * pkt_priv:    Private pointer to the packet
- * ptr:         Pointer to the Rx data
- * size:        Size of the Rx data
- */
-static void wdsp_glink_notify_rx(void *handle, const void *priv,
-				 const void *pkt_priv, const void *ptr,
-				 size_t size)
-{
-	u8 *rx_buf;
-	u8 rsp_cnt;
-	struct wdsp_glink_ch *ch;
-	struct wdsp_glink_priv *wpriv;
-
-	if (!ptr || !priv) {
-		pr_err("%s: Invalid parameters\n", __func__);
-		return;
-	}
-
-	ch = (struct wdsp_glink_ch *)priv;
-	wpriv = ch->wpriv;
-	rx_buf = (u8 *)ptr;
-	if (size > WDSP_MAX_READ_SIZE) {
-		dev_err(wpriv->dev, "%s: Size %zd is greater than allowed %d\n",
-			__func__, size, WDSP_MAX_READ_SIZE);
-		size = WDSP_MAX_READ_SIZE;
-	}
-
-	mutex_lock(&wpriv->rsp_mutex);
-	rsp_cnt = wpriv->rsp_cnt;
-	if (rsp_cnt >= RESP_QUEUE_SIZE) {
-		dev_err(wpriv->dev, "%s: Resp Queue is Full\n", __func__);
-		rsp_cnt = 0;
-	}
-	dev_dbg(wpriv->dev, "%s: copy into buffer %d\n", __func__, rsp_cnt);
-
-	memcpy(wpriv->rsp[rsp_cnt].buf, rx_buf, size);
-	wpriv->rsp[rsp_cnt].buf_size = size;
-	wpriv->rsp_cnt = ++rsp_cnt;
-	mutex_unlock(&wpriv->rsp_mutex);
-
-	glink_rx_done(handle, ptr, true);
-	complete(&wpriv->rsp_complete);
-}
-
-/*
- * wdsp_glink_notify_tx_done - Glink notify tx done callback to
- * free tx buffer
- * handle:      Opaque Channel handle returned by GLink
- * priv:        Private pointer to the channel
- * pkt_priv:    Private pointer to the packet
- * ptr:         Pointer to the Tx data
- */
-static void wdsp_glink_notify_tx_done(void *handle, const void *priv,
-				      const void *pkt_priv, const void *ptr)
-{
-	wdsp_glink_free_tx_buf(priv, pkt_priv);
-}
-/*
- * wdsp_glink_notify_tx_abort - Glink notify tx abort callback to
- * free tx buffer
- * handle:      Opaque Channel handle returned by GLink
- * priv:        Private pointer to the channel
- * pkt_priv:    Private pointer to the packet
- */
-static void wdsp_glink_notify_tx_abort(void *handle, const void *priv,
-				       const void *pkt_priv)
-{
-	wdsp_glink_free_tx_buf(priv, pkt_priv);
-}
-
-/*
- * wdsp_glink_notify_rx_intent_req - Glink notify rx intent request callback
- * to queue buffer to receive from remote client
- * handle:      Opaque channel handle returned by GLink
- * priv:        Private pointer to the channel
- * req_size:    Size of intent to be queued
- */
-static bool wdsp_glink_notify_rx_intent_req(void *handle, const void *priv,
-					    size_t req_size)
-{
-	struct wdsp_glink_priv *wpriv;
-	struct wdsp_glink_ch *ch;
-	int rc = 0;
-	bool ret = false;
-
-	if (!priv) {
-		pr_err("%s: Invalid priv\n", __func__);
-		goto done;
-	}
-	if (req_size > WDSP_MAX_READ_SIZE) {
-		pr_err("%s: Invalid req_size %zd\n", __func__, req_size);
-		goto done;
-	}
-
-	ch = (struct wdsp_glink_ch *)priv;
-	wpriv = ch->wpriv;
-
-	dev_dbg(wpriv->dev, "%s: intent size %zd requested for ch name %s",
-		 __func__, req_size, ch->ch_cfg.name);
-
-	mutex_lock(&ch->mutex);
-	rc = glink_queue_rx_intent(ch->handle, ch, req_size);
-	if (rc < 0) {
-		dev_err(wpriv->dev, "%s: Failed to queue rx intent, rc = %d\n",
-			__func__, rc);
-		mutex_unlock(&ch->mutex);
-		goto done;
-	}
-	mutex_unlock(&ch->mutex);
-	ret = true;
-
-done:
-	return ret;
-}
-
-/*
- * wdsp_glink_lcl_ch_open_wrk - Work function to open channel again
- * when local disconnect event happens
- * work:      Work structure
- */
-static void wdsp_glink_lcl_ch_open_wrk(struct work_struct *work)
-{
-	struct wdsp_glink_ch *ch;
-
-	ch = container_of(work, struct wdsp_glink_ch,
-			  lcl_ch_open_wrk);
-
-	wdsp_glink_open_ch(ch);
-}
-
-/*
- * wdsp_glink_lcl_ch_cls_wrk - Work function to close channel locally
- * when remote disconnect event happens
- * work:      Work structure
- */
-static void wdsp_glink_lcl_ch_cls_wrk(struct work_struct *work)
-{
-	struct wdsp_glink_ch *ch;
-
-	ch = container_of(work, struct wdsp_glink_ch,
-			  lcl_ch_cls_wrk);
-
-	wdsp_glink_close_ch(ch);
-}
-
-/*
- * wdsp_glink_notify_state - Glink channel state information event callback
- * handle:      Opaque Channel handle returned by GLink
- * priv:        Private pointer to the channel
- * event:       channel state event
- */
-static void wdsp_glink_notify_state(void *handle, const void *priv,
-				    unsigned int event)
-{
-	struct wdsp_glink_priv *wpriv;
-	struct wdsp_glink_ch *ch;
-	int i, ret = 0;
-
-	if (!priv) {
-		pr_err("%s: Invalid priv\n", __func__);
-		return;
-	}
-
-	ch = (struct wdsp_glink_ch *)priv;
-	wpriv = ch->wpriv;
-
-	mutex_lock(&ch->mutex);
-	ch->channel_state = event;
-	if (event == GLINK_CONNECTED) {
-		dev_dbg(wpriv->dev, "%s: glink channel: %s connected\n",
-			__func__, ch->ch_cfg.name);
-
-		for (i = 0; i < ch->ch_cfg.no_of_intents; i++) {
-			dev_dbg(wpriv->dev, "%s: intent_size = %d\n", __func__,
-				ch->ch_cfg.intents_size[i]);
-			ret = glink_queue_rx_intent(ch->handle, ch,
-						    ch->ch_cfg.intents_size[i]);
-			if (ret < 0)
-				dev_warn(wpriv->dev, "%s: Failed to queue intent %d of size %d\n",
-					 __func__, i,
-					 ch->ch_cfg.intents_size[i]);
-		}
-
-		ret = glink_qos_latency(ch->handle, ch->ch_cfg.latency_in_us,
-					QOS_PKT_SIZE);
-		if (ret < 0)
-			dev_warn(wpriv->dev, "%s: Failed to request qos %d for ch %s\n",
-				__func__, ch->ch_cfg.latency_in_us,
-				ch->ch_cfg.name);
-
-		wake_up(&ch->ch_connect_wait);
-		mutex_unlock(&ch->mutex);
-	} else if (event == GLINK_LOCAL_DISCONNECTED) {
-		/*
-		 * Don't use dev_dbg here as dev may not be valid if channel
-		 * closed from driver close.
-		 */
-		pr_debug("%s: channel: %s disconnected locally\n",
-			 __func__, ch->ch_cfg.name);
-		mutex_unlock(&ch->mutex);
-
-		if (ch->free_mem) {
-			kfree(ch);
-			ch = NULL;
-		}
-	} else if (event == GLINK_REMOTE_DISCONNECTED) {
-		dev_dbg(wpriv->dev, "%s: remote channel: %s disconnected remotely\n",
-			 __func__, ch->ch_cfg.name);
-		mutex_unlock(&ch->mutex);
-		/*
-		 * If remote disconnect happens, local side also has
-		 * to close the channel as per glink design in a
-		 * separate work_queue.
-		 */
-		queue_work(wpriv->work_queue, &ch->lcl_ch_cls_wrk);
-	}
-}
-
-/*
- * wdsp_glink_close_ch - Internal function to close glink channel
- * ch:       Glink Channel structure.
- */
-static int wdsp_glink_close_ch(struct wdsp_glink_ch *ch)
-{
-	struct wdsp_glink_priv *wpriv = ch->wpriv;
-	int ret = 0;
-
-	mutex_lock(&wpriv->glink_mutex);
-	if (ch->handle) {
-		ret = glink_close(ch->handle);
-		if (ret < 0) {
-			dev_err(wpriv->dev, "%s: glink_close is failed, ret = %d\n",
-				 __func__, ret);
-		} else {
-			ch->handle = NULL;
-			dev_dbg(wpriv->dev, "%s: ch %s is closed\n", __func__,
-				ch->ch_cfg.name);
-		}
-	} else {
-		dev_dbg(wpriv->dev, "%s: ch %s is already closed\n", __func__,
-			ch->ch_cfg.name);
-	}
-	mutex_unlock(&wpriv->glink_mutex);
-
-
-	return ret;
-}
-
-/*
- * wdsp_glink_open_ch - Internal function to open glink channel
- * ch:       Glink Channel structure.
- */
-static int wdsp_glink_open_ch(struct wdsp_glink_ch *ch)
-{
-	struct wdsp_glink_priv *wpriv = ch->wpriv;
-	struct glink_open_config open_cfg;
-	int ret = 0;
-
-	mutex_lock(&wpriv->glink_mutex);
-	if (!ch->handle) {
-		memset(&open_cfg, 0, sizeof(open_cfg));
-		open_cfg.options = GLINK_OPT_INITIAL_XPORT;
-		open_cfg.edge = WDSP_EDGE;
-		open_cfg.notify_rx = wdsp_glink_notify_rx;
-		open_cfg.notify_tx_done = wdsp_glink_notify_tx_done;
-		open_cfg.notify_tx_abort = wdsp_glink_notify_tx_abort;
-		open_cfg.notify_state = wdsp_glink_notify_state;
-		open_cfg.notify_rx_intent_req = wdsp_glink_notify_rx_intent_req;
-		open_cfg.priv = ch;
-		open_cfg.name = ch->ch_cfg.name;
-
-		dev_dbg(wpriv->dev, "%s: ch->ch_cfg.name = %s, latency_in_us = %d, intents = %d\n",
-			__func__, ch->ch_cfg.name, ch->ch_cfg.latency_in_us,
-			ch->ch_cfg.no_of_intents);
-
-		ch->handle = glink_open(&open_cfg);
-		if (IS_ERR_OR_NULL(ch->handle)) {
-			dev_err(wpriv->dev, "%s: glink_open failed for ch %s\n",
-				__func__, ch->ch_cfg.name);
-			ch->handle = NULL;
-			ret = -EINVAL;
-		}
-	} else {
-		dev_err(wpriv->dev, "%s: ch %s is already opened\n", __func__,
-			ch->ch_cfg.name);
-	}
-	mutex_unlock(&wpriv->glink_mutex);
-
-	return ret;
-}
-
-/*
- * wdsp_glink_close_all_ch - Internal function to close all glink channels
- * wpriv:       Wdsp_glink private structure
- */
-static void wdsp_glink_close_all_ch(struct wdsp_glink_priv *wpriv)
+static struct wdsp_ch *wdsp_get_ch(char *ch_name)
 {
 	int i;
 
-	for (i = 0; i < wpriv->no_of_channels; i++)
-		if (wpriv->ch && wpriv->ch[i])
-			wdsp_glink_close_ch(wpriv->ch[i]);
-}
-
-/*
- * wdsp_glink_open_all_ch - Internal function to open all glink channels
- * wpriv:       Wdsp_glink private structure
- */
-static int wdsp_glink_open_all_ch(struct wdsp_glink_priv *wpriv)
-{
-	int ret = 0, i, j;
-
 	for (i = 0; i < wpriv->no_of_channels; i++) {
-		if (wpriv->ch && wpriv->ch[i]) {
-			ret = wdsp_glink_open_ch(wpriv->ch[i]);
-			if (ret < 0)
-				goto err_open;
-		}
+		if (!strcmp(ch_name, wpriv->ch[i]->ch_name))
+			return wpriv->ch[i];
 	}
-	goto done;
-
-err_open:
-	for (j = 0; j < i; j++)
-		if (wpriv->ch[i])
-			wdsp_glink_close_ch(wpriv->ch[j]);
-
-done:
-	return ret;
+	return NULL;
 }
 
 /*
- * wdsp_glink_ch_open_wq - Work function to open glink channels
- * work:      Work structure
+ * wdsp_rpmsg_callback - Rpmsg callback for responses
+ * rpdev:     Rpmsg device structure
+ * data:      Pointer to the Rx data
+ * len:       Size of the Rx data
+ * priv:      Private pointer to the channel
+ * addr:      Address variable
+ * Returns 0 on success and an appropriate error value on failure
  */
-static void wdsp_glink_ch_open_cls_wrk(struct work_struct *work)
+static int wdsp_rpmsg_callback(struct rpmsg_device *rpdev, void *data,
+				int len, void *priv, u32 addr__unused)
 {
+	struct wdsp_ch *ch = dev_get_drvdata(&rpdev->dev);
 	struct wdsp_glink_priv *wpriv;
+	unsigned long flags;
+	u8 *rx_buf;
+	u8 rsp_cnt = 0;
 
-	wpriv = container_of(work, struct wdsp_glink_priv,
-			     ch_open_cls_wrk);
-
-	if (wpriv->glink_state.link_state == GLINK_LINK_STATE_DOWN) {
-		dev_info(wpriv->dev, "%s: GLINK_LINK_STATE_DOWN\n",
-			 __func__);
-
-		wdsp_glink_close_all_ch(wpriv);
-	} else if (wpriv->glink_state.link_state == GLINK_LINK_STATE_UP) {
-		dev_info(wpriv->dev, "%s: GLINK_LINK_STATE_UP\n",
-			 __func__);
-
-		wdsp_glink_open_all_ch(wpriv);
+	if (!ch || !data) {
+		pr_err("%s: Invalid ch or data\n", __func__);
+		return -EINVAL;
 	}
+
+	wpriv = ch->wpriv;
+	rx_buf = (u8 *)data;
+	if (len > WDSP_MAX_READ_SIZE) {
+		dev_info_ratelimited(wpriv->dev, "%s: Size %d is greater than allowed %d\n",
+			__func__, len, WDSP_MAX_READ_SIZE);
+		len = WDSP_MAX_READ_SIZE;
+	}
+	dev_dbg_ratelimited(wpriv->dev, "%s: copy into buffer %d\n", __func__,
+			    wpriv->rsp_cnt);
+
+	if (wpriv->rsp_cnt >= RESP_QUEUE_SIZE) {
+		dev_info_ratelimited(wpriv->dev, "%s: Resp Queue is Full\n",
+				      __func__);
+		rsp_cnt = 0;
+	}
+	spin_lock_irqsave(&wpriv->rsp_lock, flags);
+	rsp_cnt = wpriv->rsp_cnt;
+	memcpy(wpriv->rsp[rsp_cnt].buf, rx_buf, len);
+	wpriv->rsp[rsp_cnt].buf_size = len;
+	wpriv->rsp_cnt = ++rsp_cnt;
+	spin_unlock_irqrestore(&wpriv->rsp_lock, flags);
+
+	complete(&wpriv->rsp_complete);
+
+	return 0;
 }
 
 /*
- * wdsp_glink_link_state_cb - Glink link state callback to inform
- * about link states
- * cb_info:     Glink link state callback information structure
- * priv:        Private structure of link state passed while register
+ * wdsp_rpmsg_probe - Rpmsg channel probe function
+ * rpdev:     Rpmsg device structure
+ * Returns 0 on success and an appropriate error value on failure
  */
-static void wdsp_glink_link_state_cb(struct glink_link_state_cb_info *cb_info,
-				     void *priv)
+static int wdsp_rpmsg_probe(struct rpmsg_device *rpdev)
 {
-	struct wdsp_glink_priv *wpriv;
+	struct wdsp_ch *ch;
 
-	if (!cb_info || !priv) {
-		pr_err("%s: Invalid parameters\n", __func__);
+	ch = wdsp_get_ch(rpdev->id.name);
+	if (!ch) {
+		dev_err(&rpdev->dev, "%s, Invalid Channel [%s]\n",
+			__func__, rpdev->id.name);
+		return -EINVAL;
+	}
+
+	dev_dbg(&rpdev->dev, "%s: Channel[%s] state[Up]\n",
+		 __func__, rpdev->id.name);
+
+	spin_lock(&ch->ch_lock);
+	ch->handle = rpdev;
+	ch->ch_state = WDSP_CH_CONNECTED;
+	spin_unlock(&ch->ch_lock);
+	dev_set_drvdata(&rpdev->dev, ch);
+	wake_up(&wpriv->ch_state_wait);
+
+	return 0;
+}
+
+/*
+ * wdsp_rpmsg_remove - Rpmsg channel remove function
+ * rpdev:     Rpmsg device structure
+ */
+static void wdsp_rpmsg_remove(struct rpmsg_device *rpdev)
+{
+	struct wdsp_ch *ch = dev_get_drvdata(&rpdev->dev);
+
+	if (!ch) {
+		dev_err(&rpdev->dev, "%s: Invalid ch\n", __func__);
 		return;
 	}
 
-	wpriv = (struct wdsp_glink_priv *)priv;
-
-	mutex_lock(&wpriv->glink_mutex);
-	wpriv->glink_state.link_state = cb_info->link_state;
-	wake_up(&wpriv->link_state_wait);
-	mutex_unlock(&wpriv->glink_mutex);
-
-	queue_work(wpriv->work_queue, &wpriv->ch_open_cls_wrk);
+	dev_dbg(&rpdev->dev, "%s: Channel[%s] state[Down]\n",
+		 __func__, rpdev->id.name);
+	spin_lock(&ch->ch_lock);
+	ch->handle = NULL;
+	ch->ch_state = WDSP_CH_DISCONNECTED;
+	spin_unlock(&ch->ch_lock);
+	dev_set_drvdata(&rpdev->dev, NULL);
 }
 
-/*
- * wdsp_glink_ch_info_init- Internal function to allocate channel memory
- * and register with glink
- * wpriv:     Wdsp_glink private structure.
- * pkt:       Glink registration packet contains glink channel information.
- * pkt_size:  Size of the pkt.
- */
-static int wdsp_glink_ch_info_init(struct wdsp_glink_priv *wpriv,
-				   struct wdsp_reg_pkt *pkt, size_t pkt_size)
+static bool wdsp_is_ch_connected(struct wdsp_glink_priv *wpriv)
 {
-	int ret = 0, i, j;
-	struct glink_link_info link_info;
-	struct wdsp_glink_ch_cfg *ch_cfg;
-	struct wdsp_glink_ch **ch;
-	u8 no_of_channels;
-	u8 *payload;
-	u32 ch_size, ch_cfg_size;
-	size_t size = WDSP_WRITE_PKT_SIZE + WDSP_REG_PKT_SIZE;
+	int i;
 
-	mutex_lock(&wpriv->glink_mutex);
-	if (wpriv->ch) {
-		dev_err_ratelimited(wpriv->dev, "%s: glink ch memory is already allocated\n",
-			 __func__);
-		ret = -EINVAL;
-		goto done;
-	}
-	payload = (u8 *)pkt->payload;
-	no_of_channels = pkt->no_of_channels;
-
-	if (no_of_channels > WDSP_MAX_NO_OF_CHANNELS) {
-		dev_err_ratelimited(wpriv->dev, "%s: no_of_channels: %d but max allowed are %d\n",
-			__func__, no_of_channels, WDSP_MAX_NO_OF_CHANNELS);
-		ret = -EINVAL;
-		goto done;
-	}
-	ch = kcalloc(no_of_channels, sizeof(struct wdsp_glink_ch *),
-		     GFP_ATOMIC);
-	if (!ch) {
-		ret = -ENOMEM;
-		goto done;
-	}
-	wpriv->ch = ch;
-	wpriv->no_of_channels = no_of_channels;
-
-	for (i = 0; i < no_of_channels; i++) {
-		ch_cfg = (struct wdsp_glink_ch_cfg *)payload;
-
-		size += WDSP_CH_CFG_SIZE;
-		if (size > pkt_size) {
-			dev_err_ratelimited(wpriv->dev, "%s: Invalid size = %zd, pkt_size = %zd\n",
-				__func__, size, pkt_size);
-			ret = -EINVAL;
-			goto err_ch_mem;
+	for (i = 0; i < wpriv->no_of_channels; i++) {
+		spin_lock(&wpriv->ch[i]->ch_lock);
+		if (wpriv->ch[i]->ch_state != WDSP_CH_CONNECTED) {
+			spin_unlock(&wpriv->ch[i]->ch_lock);
+			return false;
 		}
-		if (ch_cfg->no_of_intents > WDSP_MAX_NO_OF_INTENTS) {
-			dev_err_ratelimited(wpriv->dev, "%s: Invalid no_of_intents = %d\n",
-				__func__, ch_cfg->no_of_intents);
-			ret = -EINVAL;
-			goto err_ch_mem;
-		}
-		size += (sizeof(u32) * ch_cfg->no_of_intents);
-		if (size > pkt_size) {
-			dev_err_ratelimited(wpriv->dev, "%s: Invalid size = %zd, pkt_size = %zd\n",
-				__func__, size, pkt_size);
-			ret = -EINVAL;
-			goto err_ch_mem;
-		}
-
-		ch_cfg_size = sizeof(struct wdsp_glink_ch_cfg) +
-					(sizeof(u32) * ch_cfg->no_of_intents);
-		ch_size = sizeof(struct wdsp_glink_ch) +
-					(sizeof(u32) * ch_cfg->no_of_intents);
-
-		dev_dbg(wpriv->dev, "%s: channels: %d ch_cfg_size: %d, size: %zd, pkt_size: %zd",
-			 __func__, no_of_channels, ch_cfg_size, size, pkt_size);
-
-		ch[i] = kzalloc(ch_size, GFP_KERNEL);
-		if (!ch[i]) {
-			ret = -ENOMEM;
-			goto err_ch_mem;
-		}
-		ch[i]->channel_state = GLINK_LOCAL_DISCONNECTED;
-		memcpy(&ch[i]->ch_cfg, payload, ch_cfg_size);
-		payload += ch_cfg_size;
-
-		/* check ch name is valid string or not */
-		for (j = 0; j < WDSP_CH_NAME_MAX_LEN; j++) {
-			if (ch[i]->ch_cfg.name[j] == '\0')
-				break;
-		}
-
-		if (j == WDSP_CH_NAME_MAX_LEN) {
-			dev_err_ratelimited(wpriv->dev, "%s: Wrong channel name\n",
-				__func__);
-			kfree(ch[i]);
-			ch[i] = NULL;
-			ret = -EINVAL;
-			goto err_ch_mem;
-		}
-
-		mutex_init(&ch[i]->mutex);
-		ch[i]->wpriv = wpriv;
-		INIT_WORK(&ch[i]->lcl_ch_open_wrk, wdsp_glink_lcl_ch_open_wrk);
-		INIT_WORK(&ch[i]->lcl_ch_cls_wrk, wdsp_glink_lcl_ch_cls_wrk);
-		init_waitqueue_head(&ch[i]->ch_connect_wait);
+		spin_unlock(&wpriv->ch[i]->ch_lock);
 	}
+	return true;
+}
 
-	INIT_WORK(&wpriv->ch_open_cls_wrk, wdsp_glink_ch_open_cls_wrk);
+static int wdsp_wait_for_all_ch_connect(struct wdsp_glink_priv *wpriv)
+{
+	int ret;
 
-	/* Register glink link_state notification */
-	link_info.glink_link_state_notif_cb = wdsp_glink_link_state_cb;
-	link_info.transport = NULL;
-	link_info.edge = WDSP_EDGE;
-
-	wpriv->glink_state.link_state = GLINK_LINK_STATE_DOWN;
-	wpriv->glink_state.handle = glink_register_link_state_cb(&link_info,
-								 wpriv);
-	if (!wpriv->glink_state.handle) {
-		dev_err(wpriv->dev, "%s: Unable to register wdsp link state\n",
-			__func__);
-		ret = -EINVAL;
-		goto err_ch_mem;
+	ret = wait_event_timeout(wpriv->ch_state_wait,
+				wdsp_is_ch_connected(wpriv),
+				msecs_to_jiffies(TIMEOUT_MS));
+	if (!ret) {
+		dev_err_ratelimited(wpriv->dev, "%s: All channels are not connected\n",
+				    __func__);
+		ret = -ETIMEDOUT;
+		goto err;
 	}
-	goto done;
+	ret = 0;
 
-err_ch_mem:
-	for (j = 0; j < i; j++) {
-		mutex_destroy(&ch[j]->mutex);
-		kfree(wpriv->ch[j]);
-		wpriv->ch[j] = NULL;
-	}
-	kfree(wpriv->ch);
-	wpriv->ch = NULL;
-	wpriv->no_of_channels = 0;
-
-done:
-	mutex_unlock(&wpriv->glink_mutex);
+err:
 	return ret;
 }
 
 /*
- * wdsp_glink_tx_buf_work - Work queue function to send tx buffer to glink
+ * wdsp_tx_buf_work - Work queue function to send tx buffer to glink
  * work:     Work structure
  */
-static void wdsp_glink_tx_buf_work(struct work_struct *work)
+static void wdsp_tx_buf_work(struct work_struct *work)
 {
 	struct wdsp_glink_priv *wpriv;
-	struct wdsp_glink_ch *ch;
-	struct wdsp_glink_tx_buf *tx_buf;
+	struct wdsp_ch *ch;
+	struct wdsp_tx_buf *tx_buf;
 	struct wdsp_write_pkt *wpkt;
 	struct wdsp_cmd_pkt *cpkt;
 	int ret = 0;
+	struct rpmsg_device *rpdev = NULL;
 
-	tx_buf = container_of(work, struct wdsp_glink_tx_buf,
+	tx_buf = container_of(work, struct wdsp_tx_buf,
 			      tx_work);
 	ch = tx_buf->ch;
 	wpriv = ch->wpriv;
 	wpkt = (struct wdsp_write_pkt *)tx_buf->buf;
 	cpkt = (struct wdsp_cmd_pkt *)wpkt->payload;
+
 	dev_dbg(wpriv->dev, "%s: ch name = %s, payload size = %d\n",
 		__func__, cpkt->ch_name, cpkt->payload_size);
 
-	mutex_lock(&tx_buf->ch->mutex);
-	if (ch->channel_state == GLINK_CONNECTED) {
-		mutex_unlock(&tx_buf->ch->mutex);
-		ret = glink_tx(ch->handle, tx_buf,
-			       cpkt->payload, cpkt->payload_size,
-			       GLINK_TX_REQ_INTENT);
-		if (ret < 0) {
-			dev_err(wpriv->dev, "%s: glink tx failed, ret = %d\n",
+	spin_lock(&ch->ch_lock);
+	rpdev = ch->handle;
+	if (rpdev || ch->ch_state == WDSP_CH_CONNECTED) {
+		spin_unlock(&ch->ch_lock);
+		ret = rpmsg_send(rpdev->ept, cpkt->payload,
+				 cpkt->payload_size);
+		if (ret < 0)
+			dev_err(wpriv->dev, "%s: rpmsg send failed, ret = %d\n",
 				__func__, ret);
-			/*
-			 * If glink_tx() is failed then free tx_buf here as
-			 * there won't be any tx_done notification to
-			 * free the buffer.
-			 */
-			vfree(tx_buf);
-		}
 	} else {
-		mutex_unlock(&tx_buf->ch->mutex);
+		spin_unlock(&ch->ch_lock);
 		dev_err(wpriv->dev, "%s: channel %s is not in connected state\n",
-			__func__, ch->ch_cfg.name);
-		/*
-		 * Free tx_buf here as there won't be any tx_done
-		 * notification in this case also.
-		 */
-		vfree(tx_buf);
+			__func__, ch->ch_name);
 	}
+	vfree(tx_buf);
 }
 
 /*
@@ -745,19 +287,20 @@
  * buf:     Pointer to the userspace buffer
  * count:   Number bytes to read from the file
  * ppos:    Pointer to the position into the file
+ * Returns 0 on success and an appropriate error value on failure
  */
 static ssize_t wdsp_glink_read(struct file *file, char __user *buf,
 			       size_t count, loff_t *ppos)
 {
 	int ret = 0, ret1 = 0;
-	struct wdsp_glink_rsp_que *rsp;
+	struct wdsp_rsp_que *read_rsp = NULL;
 	struct wdsp_glink_priv *wpriv;
+	unsigned long flags;
 
 	wpriv = (struct wdsp_glink_priv *)file->private_data;
 	if (!wpriv) {
 		pr_err("%s: Invalid private data\n", __func__);
-		ret = -EINVAL;
-		goto done;
+		return -EINVAL;
 	}
 
 	if (count > WDSP_MAX_READ_SIZE) {
@@ -766,35 +309,42 @@
 		count = WDSP_MAX_READ_SIZE;
 	}
 	/*
-	 * Complete signal has given from glink rx notification callback
+	 * Complete signal has given from gwdsp_rpmsg_callback()
 	 * or from flush API. Also use interruptible wait_for_completion API
 	 * to allow the system to go in suspend.
 	 */
 	ret = wait_for_completion_interruptible(&wpriv->rsp_complete);
-	if (ret)
-		goto done;
+	if (ret < 0)
+		return ret;
 
-	mutex_lock(&wpriv->rsp_mutex);
+	read_rsp = kzalloc(sizeof(struct wdsp_rsp_que), GFP_KERNEL);
+	if (!read_rsp)
+		return -ENOMEM;
+
+	spin_lock_irqsave(&wpriv->rsp_lock, flags);
 	if (wpriv->rsp_cnt) {
 		wpriv->rsp_cnt--;
 		dev_dbg(wpriv->dev, "%s: read from buffer %d\n",
 			__func__, wpriv->rsp_cnt);
 
-		rsp = &wpriv->rsp[wpriv->rsp_cnt];
-		if (count < rsp->buf_size) {
-			ret1 = copy_to_user(buf, &rsp->buf, count);
+		memcpy(read_rsp, &wpriv->rsp[wpriv->rsp_cnt],
+			sizeof(struct wdsp_rsp_que));
+		spin_unlock_irqrestore(&wpriv->rsp_lock, flags);
+
+		if (count < read_rsp->buf_size) {
+			ret1 = copy_to_user(buf, read_rsp->buf, count);
 			/* Return the number of bytes copied */
 			ret = count;
 		} else {
-			ret1 = copy_to_user(buf, &rsp->buf, rsp->buf_size);
+			ret1 = copy_to_user(buf, read_rsp->buf,
+					    read_rsp->buf_size);
 			/* Return the number of bytes copied */
-			ret = rsp->buf_size;
+			ret = read_rsp->buf_size;
 		}
 
 		if (ret1) {
-			mutex_unlock(&wpriv->rsp_mutex);
 			dev_err_ratelimited(wpriv->dev, "%s: copy_to_user failed %d\n",
-				__func__, ret);
+					    __func__, ret);
 			ret = -EFAULT;
 			goto done;
 		}
@@ -805,11 +355,12 @@
 		 */
 		dev_dbg(wpriv->dev, "%s: resp count = %d\n", __func__,
 			wpriv->rsp_cnt);
+		spin_unlock_irqrestore(&wpriv->rsp_lock, flags);
 		ret = -EINVAL;
 	}
-	mutex_unlock(&wpriv->rsp_mutex);
 
 done:
+	kfree(read_rsp);
 	return ret;
 }
 
@@ -819,6 +370,7 @@
  * buf:     Pointer to the userspace buffer
  * count:   Number bytes to read from the file
  * ppos:    Pointer to the position into the file
+ * Returns 0 on success and an appropriate error value on failure
  */
 static ssize_t wdsp_glink_write(struct file *file, const char __user *buf,
 				size_t count, loff_t *ppos)
@@ -826,7 +378,7 @@
 	int ret = 0, i, tx_buf_size;
 	struct wdsp_write_pkt *wpkt;
 	struct wdsp_cmd_pkt *cpkt;
-	struct wdsp_glink_tx_buf *tx_buf;
+	struct wdsp_tx_buf *tx_buf;
 	struct wdsp_glink_priv *wpriv;
 	size_t pkt_max_size;
 
@@ -847,7 +399,7 @@
 
 	dev_dbg(wpriv->dev, "%s: count = %zd\n", __func__, count);
 
-	tx_buf_size = count + sizeof(struct wdsp_glink_tx_buf);
+	tx_buf_size = count + sizeof(struct wdsp_tx_buf);
 	tx_buf = vzalloc(tx_buf_size);
 	if (!tx_buf) {
 		ret = -ENOMEM;
@@ -865,33 +417,14 @@
 	wpkt = (struct wdsp_write_pkt *)tx_buf->buf;
 	switch (wpkt->pkt_type) {
 	case WDSP_REG_PKT:
-		if (count < (WDSP_WRITE_PKT_SIZE + WDSP_REG_PKT_SIZE +
-			     WDSP_CH_CFG_SIZE)) {
-			dev_err_ratelimited(wpriv->dev, "%s: Invalid reg pkt size = %zd\n",
-				__func__, count);
-			ret = -EINVAL;
-			goto free_buf;
-		}
-		ret = wdsp_glink_ch_info_init(wpriv,
-					(struct wdsp_reg_pkt *)wpkt->payload,
-					count);
-		if (ret < 0)
-			dev_err_ratelimited(wpriv->dev, "%s: glink register failed, ret = %d\n",
-				__func__, ret);
+		/* Keep this case to support backward compatibility */
 		vfree(tx_buf);
 		break;
 	case WDSP_READY_PKT:
-		ret = wait_event_timeout(wpriv->link_state_wait,
-					 (wpriv->glink_state.link_state ==
-							GLINK_LINK_STATE_UP),
-					 msecs_to_jiffies(TIMEOUT_MS));
-		if (!ret) {
-			dev_err_ratelimited(wpriv->dev, "%s: Link state wait timeout\n",
-				__func__);
-			ret = -ETIMEDOUT;
-			goto free_buf;
-		}
-		ret = 0;
+		ret = wdsp_wait_for_all_ch_connect(wpriv);
+		if (ret < 0)
+			dev_err_ratelimited(wpriv->dev, "%s: Channels not in connected state\n",
+					    __func__);
 		vfree(tx_buf);
 		break;
 	case WDSP_CMD_PKT:
@@ -901,16 +434,6 @@
 			ret = -EINVAL;
 			goto free_buf;
 		}
-		mutex_lock(&wpriv->glink_mutex);
-		if (wpriv->glink_state.link_state == GLINK_LINK_STATE_DOWN) {
-			mutex_unlock(&wpriv->glink_mutex);
-			dev_err_ratelimited(wpriv->dev, "%s: Link state is Down\n",
-				__func__);
-
-			ret = -ENETRESET;
-			goto free_buf;
-		}
-		mutex_unlock(&wpriv->glink_mutex);
 		cpkt = (struct wdsp_cmd_pkt *)wpkt->payload;
 		pkt_max_size =  sizeof(struct wdsp_write_pkt) +
 					sizeof(struct wdsp_cmd_pkt) +
@@ -922,15 +445,13 @@
 			goto free_buf;
 		}
 		for (i = 0; i < wpriv->no_of_channels; i++) {
-			if (wpriv->ch && wpriv->ch[i] &&
-				(!strcmp(cpkt->ch_name,
-						wpriv->ch[i]->ch_cfg.name))) {
+			if (!strcmp(cpkt->ch_name, wpriv->ch[i]->ch_name)) {
 				tx_buf->ch = wpriv->ch[i];
 				break;
 			}
 		}
 		if (!tx_buf->ch) {
-			dev_err_ratelimited(wpriv->dev, "%s: Failed to get glink channel\n",
+			dev_err_ratelimited(wpriv->dev, "%s: Failed to get channel\n",
 				__func__);
 			ret = -EINVAL;
 			goto free_buf;
@@ -938,20 +459,17 @@
 		dev_dbg(wpriv->dev, "%s: requested ch_name: %s, pkt_size: %zd\n",
 			__func__, cpkt->ch_name, pkt_max_size);
 
-		ret = wait_event_timeout(tx_buf->ch->ch_connect_wait,
-					 (tx_buf->ch->channel_state ==
-							GLINK_CONNECTED),
-					 msecs_to_jiffies(TIMEOUT_MS));
-		if (!ret) {
-			dev_err_ratelimited(wpriv->dev, "%s: glink channel %s is not in connected state %d\n",
-				__func__, tx_buf->ch->ch_cfg.name,
-				tx_buf->ch->channel_state);
-			ret = -ETIMEDOUT;
+		spin_lock(&tx_buf->ch->ch_lock);
+		if (tx_buf->ch->ch_state != WDSP_CH_CONNECTED) {
+			spin_unlock(&tx_buf->ch->ch_lock);
+			ret = -ENETRESET;
+			dev_err_ratelimited(wpriv->dev, "%s: Channels are not in connected state\n",
+						__func__);
 			goto free_buf;
 		}
-		ret = 0;
+		spin_unlock(&tx_buf->ch->ch_lock);
 
-		INIT_WORK(&tx_buf->tx_work, wdsp_glink_tx_buf_work);
+		INIT_WORK(&tx_buf->tx_work, wdsp_tx_buf_work);
 		queue_work(wpriv->work_queue, &tx_buf->tx_work);
 		break;
 	default:
@@ -974,54 +492,22 @@
  * wdsp_glink_open - Open API to initialize private data
  * inode:   Pointer to the inode structure
  * file:    Pointer to the file structure
+ * Returns 0 on success and an appropriate error value on failure
  */
 static int wdsp_glink_open(struct inode *inode, struct file *file)
 {
-	int ret = 0;
-	struct wdsp_glink_priv *wpriv;
-	struct wdsp_glink_dev *wdev;
 
-	if (!inode->i_cdev) {
-		pr_err("%s: cdev is NULL\n", __func__);
-		ret = -EINVAL;
-		goto done;
-	}
-	wdev = container_of(inode->i_cdev, struct wdsp_glink_dev, cdev);
-
-	wpriv = kzalloc(sizeof(struct wdsp_glink_priv), GFP_KERNEL);
-	if (!wpriv) {
-		ret = -ENOMEM;
-		goto done;
-	}
-	wpriv->dev = wdev->dev;
-	wpriv->work_queue = create_singlethread_workqueue("wdsp_glink_wq");
-	if (!wpriv->work_queue) {
-		dev_err(wpriv->dev, "%s: Error creating wdsp_glink_wq\n",
-			__func__);
-		ret = -EINVAL;
-		goto err_wq;
-	}
-
-	wpriv->glink_state.link_state = GLINK_LINK_STATE_DOWN;
-	init_completion(&wpriv->rsp_complete);
-	init_waitqueue_head(&wpriv->link_state_wait);
-	mutex_init(&wpriv->rsp_mutex);
-	mutex_init(&wpriv->glink_mutex);
+	pr_debug("%s: wpriv = %pK\n", __func__, wpriv);
 	file->private_data = wpriv;
 
-	goto done;
-
-err_wq:
-	kfree(wpriv);
-
-done:
-	return ret;
+	return 0;
 }
 
 /*
  * wdsp_glink_flush - Flush API to unblock read.
  * file:    Pointer to the file structure
  * id:      Lock owner ID
+ * Returns 0 on success and an appropriate error value on failure
  */
 static int wdsp_glink_flush(struct file *file, fl_owner_t id)
 {
@@ -1047,56 +533,115 @@
  *
  * inode:   Pointer to the inode structure
  * file:    Pointer to the file structure
+ * Returns 0 on success and an appropriate error value on failure
  */
 static int wdsp_glink_release(struct inode *inode, struct file *file)
 {
-	int i, ret = 0;
-	struct wdsp_glink_priv *wpriv;
-
-	wpriv = (struct wdsp_glink_priv *)file->private_data;
-	if (!wpriv) {
-		pr_err("%s: Invalid private data\n", __func__);
-		ret = -EINVAL;
-		goto done;
-	}
-
-	if (wpriv->glink_state.handle)
-		glink_unregister_link_state_cb(wpriv->glink_state.handle);
-
-	flush_workqueue(wpriv->work_queue);
-	destroy_workqueue(wpriv->work_queue);
-
-	/*
-	 * Clean up glink channel memory in channel state
-	 * callback only if close channels are called from here.
-	 */
-	if (wpriv->ch) {
-		for (i = 0; i < wpriv->no_of_channels; i++) {
-			if (wpriv->ch[i]) {
-				wpriv->ch[i]->free_mem = true;
-				/*
-				 * Channel handle NULL means channel is already
-				 * closed. Free the channel memory here itself.
-				 */
-				if (!wpriv->ch[i]->handle) {
-					kfree(wpriv->ch[i]);
-					wpriv->ch[i] = NULL;
-				} else {
-					wdsp_glink_close_ch(wpriv->ch[i]);
-				}
-			}
-		}
-
-		kfree(wpriv->ch);
-		wpriv->ch = NULL;
-	}
-
-	mutex_destroy(&wpriv->glink_mutex);
-	mutex_destroy(&wpriv->rsp_mutex);
-	kfree(wpriv);
+	pr_debug("%s: file->private_data = %pK\n", __func__,
+		 file->private_data);
 	file->private_data = NULL;
 
-done:
+	return 0;
+}
+
+static struct rpmsg_driver wdsp_rpmsg_driver = {
+	.probe = wdsp_rpmsg_probe,
+	.remove = wdsp_rpmsg_remove,
+	.callback = wdsp_rpmsg_callback,
+	/* Update this dynamically before register_rpmsg() */
+	.id_table = NULL,
+	.drv = {
+		.name = "wdsp_rpmsg",
+	},
+};
+
+static int wdsp_register_rpmsg(struct platform_device *pdev,
+				struct wdsp_glink_dev *wdev)
+{
+	int ret = 0;
+	int i, no_of_channels;
+	struct rpmsg_device_id *wdsp_rpmsg_id_table, *id_table;
+	const char *ch_name = NULL;
+
+	wpriv = devm_kzalloc(&pdev->dev,
+			     sizeof(struct wdsp_glink_priv), GFP_KERNEL);
+	if (!wpriv)
+		return -ENOMEM;
+
+	no_of_channels = of_property_count_strings(pdev->dev.of_node,
+						   "qcom,wdsp_channles");
+	if (no_of_channels < 0) {
+		dev_err(&pdev->dev, "%s: channel name parse error %d\n",
+			__func__, no_of_channels);
+		return -EINVAL;
+	}
+
+	wpriv->ch = devm_kzalloc(&pdev->dev,
+			(sizeof(struct wdsp_glink_priv *) * no_of_channels),
+			GFP_KERNEL);
+	if (!wpriv->ch)
+		return -ENOMEM;
+
+	for (i = 0; i < no_of_channels; i++) {
+		ret = of_property_read_string_index(pdev->dev.of_node,
+						   "qcom,wdsp-channels", i,
+						   &ch_name);
+		if (ret) {
+			dev_err(&pdev->dev, "%s: channel name parse error %d\n",
+				__func__, ret);
+			return -EINVAL;
+		}
+		wpriv->ch[i] = devm_kzalloc(&pdev->dev,
+					    sizeof(struct wdsp_glink_priv),
+					    GFP_KERNEL);
+		if (!wpriv->ch[i])
+			return -ENOMEM;
+
+		strlcpy(wpriv->ch[i]->ch_name, ch_name, RPMSG_NAME_SIZE);
+		wpriv->ch[i]->wpriv = wpriv;
+		spin_lock_init(&wpriv->ch[i]->ch_lock);
+	}
+	init_waitqueue_head(&wpriv->ch_state_wait);
+	init_completion(&wpriv->rsp_complete);
+	spin_lock_init(&wpriv->rsp_lock);
+
+	wpriv->wdev = wdev;
+	wpriv->dev = wdev->dev;
+	wpriv->work_queue = create_singlethread_workqueue("wdsp_glink_wq");
+	if (!wpriv->work_queue) {
+		dev_err(&pdev->dev, "%s: Error creating wdsp_glink_wq\n",
+			__func__);
+		return -EINVAL;
+	}
+
+	wdsp_rpmsg_id_table = devm_kzalloc(&pdev->dev,
+					   (sizeof(struct rpmsg_device_id) *
+							(no_of_channels + 1)),
+					   GFP_KERNEL);
+	if (!wdsp_rpmsg_id_table) {
+		ret = -ENOMEM;
+		goto err;
+	}
+
+	wpriv->no_of_channels = no_of_channels;
+	id_table = wdsp_rpmsg_id_table;
+	for (i = 0; i < no_of_channels; i++) {
+		strlcpy(id_table->name, wpriv->ch[i]->ch_name,
+			RPMSG_NAME_SIZE);
+		id_table++;
+	}
+	wdsp_rpmsg_driver.id_table = wdsp_rpmsg_id_table;
+	ret = register_rpmsg_driver(&wdsp_rpmsg_driver);
+	if (ret < 0) {
+		dev_err(&pdev->dev, "%s: Rpmsg driver register failed, err = %d\n",
+			__func__, ret);
+		goto err;
+	}
+
+	return 0;
+
+err:
+	destroy_workqueue(wpriv->work_queue);
 	return ret;
 }
 
@@ -1109,10 +654,6 @@
 	.release =              wdsp_glink_release,
 };
 
-/*
- * wdsp_glink_probe - Driver probe to expose char device
- * pdev:    Pointer to device tree data.
- */
 static int wdsp_glink_probe(struct platform_device *pdev)
 {
 	int ret;
@@ -1156,7 +697,15 @@
 			__func__, ret);
 		goto err_cdev_add;
 	}
-	platform_set_drvdata(pdev, wdev);
+
+	ret = wdsp_register_rpmsg(pdev, wdev);
+	if (ret < 0) {
+		dev_err(&pdev->dev, "%s: Failed to register with rpmsg, err = %d\n",
+			__func__, ret);
+		goto err_cdev_add;
+	}
+	platform_set_drvdata(pdev, wpriv);
+
 	goto done;
 
 err_cdev_add:
@@ -1169,28 +718,25 @@
 	unregister_chrdev_region(0, MINOR_NUMBER_COUNT);
 
 err_chrdev:
-	devm_kfree(&pdev->dev, wdev);
-
 done:
 	return ret;
 }
 
-/*
- * wdsp_glink_remove - Driver remove to handle cleanup
- * pdev:     Pointer to device tree data.
- */
 static int wdsp_glink_remove(struct platform_device *pdev)
 {
-	struct wdsp_glink_dev *wdev = platform_get_drvdata(pdev);
+	struct wdsp_glink_priv *wpriv = platform_get_drvdata(pdev);
 
-	if (wdev) {
-		cdev_del(&wdev->cdev);
-		device_destroy(wdev->cls, wdev->dev_num);
-		class_destroy(wdev->cls);
-		unregister_chrdev_region(0, MINOR_NUMBER_COUNT);
-		devm_kfree(&pdev->dev, wdev);
-	} else {
-		dev_err(&pdev->dev, "%s: Invalid device data\n", __func__);
+	unregister_rpmsg_driver(&wdsp_rpmsg_driver);
+
+	if (wpriv) {
+		flush_workqueue(wpriv->work_queue);
+		destroy_workqueue(wpriv->work_queue);
+		if (wpriv->wdev) {
+			cdev_del(&wpriv->wdev->cdev);
+			device_destroy(wpriv->wdev->cls, wpriv->wdev->dev_num);
+			class_destroy(wpriv->wdev->cls);
+			unregister_chrdev_region(0, MINOR_NUMBER_COUNT);
+		}
 	}
 
 	return 0;