From 2f7c68cb55ecb7331f2381deb497c27155f32faf Mon Sep 17 00:00:00 2001
From: hc <hc@nodka.com>
Date: Wed, 03 Jan 2024 09:43:39 +0000
Subject: [PATCH] update kernel to 5.10.198

---
 kernel/sound/soc/rockchip/rockchip_dlp.c | 1323 ++++++++++++++++++++++++--------------------------------
 1 files changed, 569 insertions(+), 754 deletions(-)

diff --git a/kernel/sound/soc/rockchip/rockchip_dlp.c b/kernel/sound/soc/rockchip/rockchip_dlp.c
index f954a32..f0d1f41 100644
--- a/kernel/sound/soc/rockchip/rockchip_dlp.c
+++ b/kernel/sound/soc/rockchip/rockchip_dlp.c
@@ -1,4 +1,4 @@
-// SPDX-License-Identifier: GPL-2.0
+// SPDX-License-Identifier: GPL-2.0-or-later
 /*
  * Rockchip DLP (Digital Loopback) Driver
  *
@@ -11,28 +11,15 @@
 #include <linux/module.h>
 #include <linux/init.h>
 #include <linux/dmaengine.h>
+#include <linux/dma-mapping.h>
 #include <linux/slab.h>
 #include <sound/pcm.h>
 #include <sound/pcm_params.h>
 #include <sound/soc.h>
-#include <linux/dma-mapping.h>
-#include <linux/of.h>
 
-#include <sound/dmaengine_pcm.h>
 #include "rockchip_dlp.h"
 
-#ifdef DLP_DBG
-#define dlp_info(args...)		pr_info(args)
-#else
-#define dlp_info(args...)		no_printk(args)
-#endif
-
-#define SND_DMAENGINE_DLP_DRV_NAME	"snd_dmaengine_dlp"
 #define PBUF_CNT			2
-
-static unsigned int prealloc_buffer_size_kbytes = 512;
-module_param(prealloc_buffer_size_kbytes, uint, 0444);
-MODULE_PARM_DESC(prealloc_buffer_size_kbytes, "Preallocate DMA buffer size (KB).");
 
 /* MUST: dlp_text should be match to enum dlp_mode */
 static const char *const dlp_text[] = {
@@ -55,74 +42,43 @@
 	"16CH: 8 Mics + 8 Loopbacks",
 };
 
-enum dlp_mode {
-	DLP_MODE_DISABLED,
-	DLP_MODE_2CH_1LP_1MIC,		/* replace cap-ch-0   with play-ch-0 */
-	DLP_MODE_2CH_1MIC_1LP,		/* replace cap-ch-1   with play-ch-1 */
-	DLP_MODE_2CH_1MIC_1LP_MIX,	/* replace cap-ch-1   with play-ch-all-mix */
-	DLP_MODE_2CH_2LP,		/* replace cap-ch-0~1 with play-ch-0~1 */
-	DLP_MODE_4CH_2MIC_2LP,		/* replace cap-ch-2~3 with play-ch-0~1 */
-	DLP_MODE_4CH_2MIC_1LP_MIX,	/* replace cap-ch-3   with play-ch-all-mix */
-	DLP_MODE_4CH_4LP,		/* replace cap-ch-0~3 with play-ch-0~3 */
-	DLP_MODE_6CH_4MIC_2LP,		/* replace cap-ch-4~5 with play-ch-0~1 */
-	DLP_MODE_6CH_4MIC_1LP_MIX,	/* replace cap-ch-4   with play-ch-all-mix */
-	DLP_MODE_6CH_6LP,		/* replace cap-ch-0~5 with play-ch-0~5 */
-	DLP_MODE_8CH_6MIC_2LP,		/* replace cap-ch-6~7 with play-ch-0~1 */
-	DLP_MODE_8CH_6MIC_1LP_MIX,	/* replace cap-ch-6   with play-ch-all-mix */
-	DLP_MODE_8CH_8LP,		/* replace cap-ch-0~7 with play-ch-0~7 */
-	DLP_MODE_10CH_8MIC_2LP,		/* replace cap-ch-8~9 with play-ch-0~1 */
-	DLP_MODE_10CH_8MIC_1LP_MIX,	/* replace cap-ch-8   with play-ch-all-mix */
-	DLP_MODE_16CH_8MIC_8LP,		/* replace cap-ch-8~f with play-ch-8~f */
-};
-
-struct dmaengine_dlp_runtime_data;
-struct dmaengine_dlp {
-	struct device *dev;
-	struct dma_chan *chan[SNDRV_PCM_STREAM_LAST + 1];
-	const struct snd_dlp_config *config;
-	struct snd_soc_component component;
-	struct list_head ref_list;
-	enum dlp_mode mode;
-	struct dmaengine_dlp_runtime_data *pref;
-	spinlock_t lock;
-	spinlock_t pref_lock;
-};
-
-struct dmaengine_dlp_runtime_data {
-	struct dmaengine_dlp *parent;
-	struct dmaengine_dlp_runtime_data *ref;
-	struct dma_chan *dma_chan;
-	struct kref refcount;
-	struct list_head node;
-	dma_cookie_t cookie;
-
-	char *buf;
-	snd_pcm_uframes_t buf_sz;
-	snd_pcm_uframes_t period_sz;
-	snd_pcm_uframes_t hw_ptr;
-	snd_pcm_sframes_t hw_ptr_delta; /* play-ptr - cap-ptr */
-	unsigned long period_elapsed;
-	unsigned int frame_bytes;
-	unsigned int channels;
-	unsigned int buf_ofs;
-	int stream;
-};
-
-static inline void dlp_activate(struct dmaengine_dlp *dlp)
+static inline void drd_buf_free(struct dlp_runtime_data *drd)
 {
-	spin_lock(&dlp->lock);
-	dlp->component.active++;
-	spin_unlock(&dlp->lock);
+	if (drd && drd->buf) {
+		dev_dbg(drd->parent->dev, "%s: stream[%d]: 0x%px\n",
+			__func__, drd->stream, drd->buf);
+		kvfree(drd->buf);
+		drd->buf = NULL;
+	}
 }
 
-static inline void dlp_deactivate(struct dmaengine_dlp *dlp)
+static inline int drd_buf_alloc(struct dlp_runtime_data *drd, int size)
 {
-	spin_lock(&dlp->lock);
-	dlp->component.active--;
-	spin_unlock(&dlp->lock);
+	if (drd) {
+		if (snd_BUG_ON(drd->buf))
+			return -EINVAL;
+
+		drd->buf = kvzalloc(size, GFP_KERNEL);
+		if (!drd->buf)
+			return -ENOMEM;
+		dev_dbg(drd->parent->dev, "%s: stream[%d]: 0x%px\n",
+			__func__, drd->stream, drd->buf);
+	}
+
+	return 0;
 }
 
-static inline bool dlp_mode_channels_match(struct dmaengine_dlp *dlp,
+static inline void dlp_activate(struct dlp *dlp)
+{
+	atomic_inc(&dlp->active);
+}
+
+static inline void dlp_deactivate(struct dlp *dlp)
+{
+	atomic_dec(&dlp->active);
+}
+
+static inline bool dlp_mode_channels_match(struct dlp *dlp,
 					   int ch, int *expected)
 {
 	*expected = 0;
@@ -163,183 +119,135 @@
 	}
 }
 
-static inline ssize_t dlp_channels_to_bytes(struct dmaengine_dlp_runtime_data *prtd,
-					    int channels)
-{
-	return (prtd->frame_bytes / prtd->channels) * channels;
-}
-
-static inline ssize_t dlp_frames_to_bytes(struct dmaengine_dlp_runtime_data *prtd,
-					  snd_pcm_sframes_t size)
-{
-	return size * prtd->frame_bytes;
-}
-
-static inline snd_pcm_sframes_t dlp_bytes_to_frames(struct dmaengine_dlp_runtime_data *prtd,
-						    ssize_t size)
-{
-	return size / prtd->frame_bytes;
-}
-
-static inline struct dmaengine_dlp *soc_component_to_dlp(struct snd_soc_component *p)
-{
-	return container_of(p, struct dmaengine_dlp, component);
-}
-
-static inline struct dmaengine_dlp_runtime_data *substream_to_prtd(
-	const struct snd_pcm_substream *substream)
-{
-	if (!substream->runtime)
-		return NULL;
-
-	return substream->runtime->private_data;
-}
-
-static struct dma_chan *snd_dmaengine_dlp_get_chan(struct snd_pcm_substream *substream)
-{
-	struct dmaengine_dlp_runtime_data *prtd = substream_to_prtd(substream);
-
-	return prtd->dma_chan;
-}
-
-static struct device *dmaengine_dma_dev(struct dmaengine_dlp *dlp,
-	struct snd_pcm_substream *substream)
-{
-	if (!dlp->chan[substream->stream])
-		return NULL;
-
-	return dlp->chan[substream->stream]->device->dev;
-}
-
-static int dlp_get_offset_size(struct dmaengine_dlp_runtime_data *prtd,
+static int dlp_get_offset_size(struct dlp_runtime_data *drd,
 			       enum dlp_mode mode, int *ofs, int *size, bool *mix)
 {
-	bool is_playback = prtd->stream == SNDRV_PCM_STREAM_PLAYBACK;
+	bool is_playback = drd->stream == SNDRV_PCM_STREAM_PLAYBACK;
 	int ret = 0;
 
 	switch (mode) {
 	case DLP_MODE_2CH_1LP_1MIC:
 		*ofs = 0;
-		*size = dlp_channels_to_bytes(prtd, 1);
+		*size = dlp_channels_to_bytes(drd, 1);
 		break;
 	case DLP_MODE_2CH_1MIC_1LP:
-		*ofs = dlp_channels_to_bytes(prtd, 1);
-		*size = dlp_channels_to_bytes(prtd, 1);
+		*ofs = dlp_channels_to_bytes(drd, 1);
+		*size = dlp_channels_to_bytes(drd, 1);
 		break;
 	case DLP_MODE_2CH_1MIC_1LP_MIX:
 		if (is_playback) {
 			*ofs = 0;
-			*size = dlp_frames_to_bytes(prtd, 1);
+			*size = dlp_frames_to_bytes(drd, 1);
 			if (mix)
 				*mix = true;
 		} else {
-			*ofs = dlp_channels_to_bytes(prtd, 1);
-			*size = dlp_channels_to_bytes(prtd, 1);
+			*ofs = dlp_channels_to_bytes(drd, 1);
+			*size = dlp_channels_to_bytes(drd, 1);
 		}
 		break;
 	case DLP_MODE_2CH_2LP:
 		*ofs = 0;
-		*size = dlp_channels_to_bytes(prtd, 2);
+		*size = dlp_channels_to_bytes(drd, 2);
 		break;
 	case DLP_MODE_4CH_2MIC_2LP:
 		if (is_playback) {
 			*ofs = 0;
-			*size = dlp_channels_to_bytes(prtd, 2);
+			*size = dlp_channels_to_bytes(drd, 2);
 		} else {
-			*ofs = dlp_channels_to_bytes(prtd, 2);
-			*size = dlp_channels_to_bytes(prtd, 2);
+			*ofs = dlp_channels_to_bytes(drd, 2);
+			*size = dlp_channels_to_bytes(drd, 2);
 		}
 		break;
 	case DLP_MODE_4CH_2MIC_1LP_MIX:
 		if (is_playback) {
 			*ofs = 0;
-			*size = dlp_frames_to_bytes(prtd, 1);
+			*size = dlp_frames_to_bytes(drd, 1);
 			if (mix)
 				*mix = true;
 		} else {
-			*ofs = dlp_channels_to_bytes(prtd, 2);
-			*size = dlp_channels_to_bytes(prtd, 1);
+			*ofs = dlp_channels_to_bytes(drd, 2);
+			*size = dlp_channels_to_bytes(drd, 1);
 		}
 		break;
 	case DLP_MODE_4CH_4LP:
 		*ofs = 0;
-		*size = dlp_channels_to_bytes(prtd, 4);
+		*size = dlp_channels_to_bytes(drd, 4);
 		break;
 	case DLP_MODE_6CH_4MIC_2LP:
 		if (is_playback) {
 			*ofs = 0;
-			*size = dlp_channels_to_bytes(prtd, 2);
+			*size = dlp_channels_to_bytes(drd, 2);
 		} else {
-			*ofs = dlp_channels_to_bytes(prtd, 4);
-			*size = dlp_channels_to_bytes(prtd, 2);
+			*ofs = dlp_channels_to_bytes(drd, 4);
+			*size = dlp_channels_to_bytes(drd, 2);
 		}
 		break;
 	case DLP_MODE_6CH_4MIC_1LP_MIX:
 		if (is_playback) {
 			*ofs = 0;
-			*size = dlp_frames_to_bytes(prtd, 1);
+			*size = dlp_frames_to_bytes(drd, 1);
 			if (mix)
 				*mix = true;
 		} else {
-			*ofs = dlp_channels_to_bytes(prtd, 4);
-			*size = dlp_channels_to_bytes(prtd, 1);
+			*ofs = dlp_channels_to_bytes(drd, 4);
+			*size = dlp_channels_to_bytes(drd, 1);
 		}
 		break;
 	case DLP_MODE_6CH_6LP:
 		*ofs = 0;
-		*size = dlp_channels_to_bytes(prtd, 6);
+		*size = dlp_channels_to_bytes(drd, 6);
 		break;
 	case DLP_MODE_8CH_6MIC_2LP:
 		if (is_playback) {
 			*ofs = 0;
-			*size = dlp_channels_to_bytes(prtd, 2);
+			*size = dlp_channels_to_bytes(drd, 2);
 		} else {
-			*ofs = dlp_channels_to_bytes(prtd, 6);
-			*size = dlp_channels_to_bytes(prtd, 2);
+			*ofs = dlp_channels_to_bytes(drd, 6);
+			*size = dlp_channels_to_bytes(drd, 2);
 		}
 		break;
 	case DLP_MODE_8CH_6MIC_1LP_MIX:
 		if (is_playback) {
 			*ofs = 0;
-			*size = dlp_frames_to_bytes(prtd, 1);
+			*size = dlp_frames_to_bytes(drd, 1);
 			if (mix)
 				*mix = true;
 		} else {
-			*ofs = dlp_channels_to_bytes(prtd, 6);
-			*size = dlp_channels_to_bytes(prtd, 1);
+			*ofs = dlp_channels_to_bytes(drd, 6);
+			*size = dlp_channels_to_bytes(drd, 1);
 		}
 		break;
 	case DLP_MODE_8CH_8LP:
 		*ofs = 0;
-		*size = dlp_channels_to_bytes(prtd, 8);
+		*size = dlp_channels_to_bytes(drd, 8);
 		break;
 	case DLP_MODE_10CH_8MIC_2LP:
 		if (is_playback) {
 			*ofs = 0;
-			*size = dlp_channels_to_bytes(prtd, 2);
+			*size = dlp_channels_to_bytes(drd, 2);
 		} else {
-			*ofs = dlp_channels_to_bytes(prtd, 8);
-			*size = dlp_channels_to_bytes(prtd, 2);
+			*ofs = dlp_channels_to_bytes(drd, 8);
+			*size = dlp_channels_to_bytes(drd, 2);
 		}
 		break;
 	case DLP_MODE_10CH_8MIC_1LP_MIX:
 		if (is_playback) {
 			*ofs = 0;
-			*size = dlp_frames_to_bytes(prtd, 1);
+			*size = dlp_frames_to_bytes(drd, 1);
 			if (mix)
 				*mix = true;
 		} else {
-			*ofs = dlp_channels_to_bytes(prtd, 8);
-			*size = dlp_channels_to_bytes(prtd, 1);
+			*ofs = dlp_channels_to_bytes(drd, 8);
+			*size = dlp_channels_to_bytes(drd, 1);
 		}
 		break;
 	case DLP_MODE_16CH_8MIC_8LP:
 		if (is_playback) {
 			*ofs = 0;
-			*size = dlp_channels_to_bytes(prtd, 8);
+			*size = dlp_channels_to_bytes(drd, 8);
 		} else {
-			*ofs = dlp_channels_to_bytes(prtd, 8);
-			*size = dlp_channels_to_bytes(prtd, 8);
+			*ofs = dlp_channels_to_bytes(drd, 8);
+			*size = dlp_channels_to_bytes(drd, 8);
 		}
 		break;
 	default:
@@ -353,22 +261,22 @@
 	return ret;
 }
 
-static int dlp_mix_frame_buffer(struct dmaengine_dlp_runtime_data *prtd, void *buf)
+static int dlp_mix_frame_buffer(struct dlp_runtime_data *drd, void *buf)
 {
-	int sample_bytes = dlp_channels_to_bytes(prtd, 1);
+	int sample_bytes = dlp_channels_to_bytes(drd, 1);
 	int16_t *p16 = (int16_t *)buf, v16 = 0;
 	int32_t *p32 = (int32_t *)buf, v32 = 0;
 	int i = 0;
 
 	switch (sample_bytes) {
 	case 2:
-		for (i = 0; i < prtd->channels; i++)
-			v16 += (p16[i] / prtd->channels);
+		for (i = 0; i < drd->channels; i++)
+			v16 += (p16[i] / drd->channels);
 		p16[0] = v16;
 		break;
 	case 4:
-		for (i = 0; i < prtd->channels; i++)
-			v32 += (p32[i] / prtd->channels);
+		for (i = 0; i < drd->channels; i++)
+			v32 += (p32[i] / drd->channels);
 		p32[0] = v32;
 		break;
 	default:
@@ -378,17 +286,209 @@
 	return 0;
 }
 
-static int dmaengine_dlp_hw_params(struct snd_soc_component *component,
-				   struct snd_pcm_substream *substream,
-				   struct snd_pcm_hw_params *params)
+static inline int drd_init_from(struct dlp_runtime_data *drd, struct dlp_runtime_data *src)
 {
-	struct dmaengine_dlp *dlp = soc_component_to_dlp(component);
-	struct dmaengine_dlp_runtime_data *prtd = substream_to_prtd(substream);
-	struct dma_chan *chan = snd_dmaengine_dlp_get_chan(substream);
-	struct dma_slave_config slave_config;
+	memset(drd, 0x0, sizeof(*drd));
+
+	drd->parent = src->parent;
+	drd->buf_sz = src->buf_sz;
+	drd->period_sz = src->period_sz;
+	drd->frame_bytes = src->frame_bytes;
+	drd->channels = src->channels;
+	drd->stream = src->stream;
+
+	INIT_LIST_HEAD(&drd->node);
+	kref_init(&drd->refcount);
+
+	dev_dbg(drd->parent->dev, "%s: drd: 0x%px\n", __func__, drd);
+
+	return 0;
+}
+
+static void drd_avl_list_add(struct dlp *dlp, struct dlp_runtime_data *drd)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&dlp->lock, flags);
+	list_add(&drd->node, &dlp->drd_avl_list);
+	dlp->drd_avl_count++;
+	spin_unlock_irqrestore(&dlp->lock, flags);
+}
+
+static struct dlp_runtime_data *drd_avl_list_get(struct dlp *dlp)
+{
+	struct dlp_runtime_data *drd = NULL;
+	unsigned long flags;
+
+	spin_lock_irqsave(&dlp->lock, flags);
+	if (!list_empty(&dlp->drd_avl_list)) {
+		drd = list_first_entry(&dlp->drd_avl_list, struct dlp_runtime_data, node);
+		list_del(&drd->node);
+		dlp->drd_avl_count--;
+	}
+	spin_unlock_irqrestore(&dlp->lock, flags);
+
+	return drd;
+}
+
+static void drd_release(struct kref *ref)
+{
+	struct dlp_runtime_data *drd =
+		container_of(ref, struct dlp_runtime_data, refcount);
+
+	dev_dbg(drd->parent->dev, "%s: drd: 0x%px\n", __func__, drd);
+
+	drd_buf_free(drd);
+	/* move to available list */
+	drd_avl_list_add(drd->parent, drd);
+}
+
+static inline struct dlp_runtime_data *drd_get(struct dlp_runtime_data *drd)
+{
+	if (!drd)
+		return NULL;
+
+	return kref_get_unless_zero(&drd->refcount) ? drd : NULL;
+}
+
+static inline void drd_put(struct dlp_runtime_data *drd)
+{
+	if (!drd)
+		return;
+
+	kref_put(&drd->refcount, drd_release);
+}
+
+static void drd_rdy_list_add(struct dlp *dlp, struct dlp_runtime_data *drd)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&dlp->lock, flags);
+	list_add(&drd->node, &dlp->drd_rdy_list);
+	spin_unlock_irqrestore(&dlp->lock, flags);
+}
+
+static struct dlp_runtime_data *drd_rdy_list_get(struct dlp *dlp)
+{
+	struct dlp_runtime_data *drd = NULL;
+	unsigned long flags;
+
+	spin_lock_irqsave(&dlp->lock, flags);
+	if (!list_empty(&dlp->drd_rdy_list)) {
+		/* the newest one */
+		drd = list_first_entry(&dlp->drd_rdy_list, struct dlp_runtime_data, node);
+		list_del(&drd->node);
+	}
+	spin_unlock_irqrestore(&dlp->lock, flags);
+
+	return drd;
+}
+
+static bool drd_rdy_list_found(struct dlp *dlp, struct dlp_runtime_data *drd)
+{
+	struct dlp_runtime_data *_drd;
+	unsigned long flags;
+	bool found = false;
+
+	if (!drd)
+		return false;
+
+	spin_lock_irqsave(&dlp->lock, flags);
+	list_for_each_entry(_drd, &dlp->drd_rdy_list, node) {
+		if (_drd == drd) {
+			found = true;
+			break;
+		}
+	}
+	spin_unlock_irqrestore(&dlp->lock, flags);
+
+	return found;
+}
+
+static void drd_rdy_list_free(struct dlp *dlp)
+{
+	struct list_head drd_list;
+	struct dlp_runtime_data *drd;
+	unsigned long flags;
+
+	spin_lock_irqsave(&dlp->lock, flags);
+	list_replace_init(&dlp->drd_rdy_list, &drd_list);
+	spin_unlock_irqrestore(&dlp->lock, flags);
+
+	while (!list_empty(&drd_list)) {
+		drd = list_first_entry(&drd_list, struct dlp_runtime_data, node);
+		list_del(&drd->node);
+		drd_put(drd);
+	}
+}
+
+static void drd_ref_list_add(struct dlp *dlp, struct dlp_runtime_data *drd)
+{
+	unsigned long flags;
+
+	/* push valid playback into ref list */
+	spin_lock_irqsave(&dlp->lock, flags);
+	list_add_tail(&drd->node, &dlp->drd_ref_list);
+	spin_unlock_irqrestore(&dlp->lock, flags);
+}
+
+static struct dlp_runtime_data *drd_ref_list_first(struct dlp *dlp)
+{
+	struct dlp_runtime_data *drd = NULL;
+	unsigned long flags;
+
+	spin_lock_irqsave(&dlp->lock, flags);
+	if (!list_empty(&dlp->drd_ref_list))
+		drd = list_first_entry(&dlp->drd_ref_list, struct dlp_runtime_data, node);
+	spin_unlock_irqrestore(&dlp->lock, flags);
+
+	return drd;
+}
+
+static struct dlp_runtime_data *drd_ref_list_del(struct dlp *dlp,
+						 struct dlp_runtime_data *drd)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&dlp->lock, flags);
+	list_del(&drd->node);
+	spin_unlock_irqrestore(&dlp->lock, flags);
+
+	return drd;
+}
+
+static void drd_ref_list_free(struct dlp *dlp)
+{
+	struct list_head drd_list;
+	struct dlp_runtime_data *drd;
+	unsigned long flags;
+
+	spin_lock_irqsave(&dlp->lock, flags);
+	list_replace_init(&dlp->drd_ref_list, &drd_list);
+	spin_unlock_irqrestore(&dlp->lock, flags);
+
+	while (!list_empty(&drd_list)) {
+		drd = list_first_entry(&drd_list, struct dlp_runtime_data, node);
+		list_del(&drd->node);
+
+		if (!atomic_read(&drd->stop))
+			drd_rdy_list_add(dlp, drd);
+		else
+			drd_put(drd);
+	}
+}
+
+int dlp_hw_params(struct snd_soc_component *component,
+		  struct snd_pcm_substream *substream,
+		  struct snd_pcm_hw_params *params)
+{
+	struct dlp *dlp = soc_component_to_dlp(component);
+	struct dlp_runtime_data *drd = substream_to_drd(substream);
 	bool is_playback = substream->stream == SNDRV_PCM_STREAM_PLAYBACK;
 	int ch_req = params_channels(params), ch_exp = 0;
-	int ret;
+
+	if (unlikely(!dlp || !drd))
+		return -EINVAL;
 
 	/* mode should match to channels */
 	if (!is_playback && !dlp_mode_channels_match(dlp, ch_req, &ch_exp)) {
@@ -398,292 +498,150 @@
 		return -EINVAL;
 	}
 
-	memset(&slave_config, 0, sizeof(slave_config));
-
-	ret = snd_dmaengine_pcm_prepare_slave_config(substream, params, &slave_config);
-	if (ret)
-		return ret;
-
-	ret = dmaengine_slave_config(chan, &slave_config);
-	if (ret)
-		return ret;
-
-	prtd->frame_bytes = snd_pcm_format_size(params_format(params),
-						params_channels(params));
-	prtd->period_sz = params_period_size(params);
-	prtd->buf_sz = params_buffer_size(params);
-	prtd->channels = params_channels(params);
+	drd->frame_bytes = snd_pcm_format_size(params_format(params),
+					       params_channels(params));
+	drd->period_sz = params_period_size(params);
+	drd->buf_sz = params_buffer_size(params);
+	drd->channels = params_channels(params);
 
 	if (is_playback)
-		prtd->buf_sz *= PBUF_CNT;
+		drd->buf_sz *= PBUF_CNT;
 
 	return 0;
 }
+EXPORT_SYMBOL_GPL(dlp_hw_params);
 
-static int
-dmaengine_pcm_set_runtime_hwparams(struct snd_soc_component *component,
-				   struct snd_pcm_substream *substream)
+int dlp_open(struct dlp *dlp, struct dlp_runtime_data *drd,
+	     struct snd_pcm_substream *substream)
 {
-	struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
-	struct dmaengine_dlp *dlp = soc_component_to_dlp(component);
-	struct device *dma_dev = dmaengine_dma_dev(dlp, substream);
-	struct dma_chan *chan = dlp->chan[substream->stream];
-	struct snd_dmaengine_dai_dma_data *dma_data;
-	struct snd_pcm_hardware hw;
-
-	if (rtd->num_cpus > 1) {
-		dev_err(rtd->dev,
-			"%s doesn't support Multi CPU yet\n", __func__);
+	if (unlikely(!dlp || !drd))
 		return -EINVAL;
-	}
 
-	dma_data = snd_soc_dai_get_dma_data(asoc_rtd_to_cpu(rtd, 0), substream);
+	drd->parent = dlp;
+	drd->stream = substream->stream;
 
-	memset(&hw, 0, sizeof(hw));
-	hw.info = SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_MMAP_VALID |
-			SNDRV_PCM_INFO_INTERLEAVED;
-	hw.periods_min = 2;
-	hw.periods_max = UINT_MAX;
-	hw.period_bytes_min = 256;
-	hw.period_bytes_max = dma_get_max_seg_size(dma_dev);
-	hw.buffer_bytes_max = SIZE_MAX;
-	hw.fifo_size = dma_data->fifo_size;
-
-	/**
-	 * FIXME: Remove the return value check to align with the code
-	 * before adding snd_dmaengine_pcm_refine_runtime_hwparams
-	 * function.
-	 */
-	snd_dmaengine_pcm_refine_runtime_hwparams(substream,
-						  dma_data,
-						  &hw,
-						  chan);
-
-	return snd_soc_set_runtime_hwparams(substream, &hw);
-}
-
-static int dmaengine_dlp_open(struct snd_soc_component *component,
-			      struct snd_pcm_substream *substream)
-{
-	struct dmaengine_dlp *dlp = soc_component_to_dlp(component);
-	struct dma_chan *chan = dlp->chan[substream->stream];
-	struct dmaengine_dlp_runtime_data *prtd;
-	int ret;
-
-	if (!chan)
-		return -ENXIO;
-
-	ret = dmaengine_pcm_set_runtime_hwparams(component, substream);
-	if (ret)
-		return ret;
-
-	ret = snd_pcm_hw_constraint_integer(substream->runtime,
-					    SNDRV_PCM_HW_PARAM_PERIODS);
-	if (ret < 0)
-		return ret;
-
-	prtd = kzalloc(sizeof(*prtd), GFP_KERNEL);
-	if (!prtd)
-		return -ENOMEM;
-
-	dlp_info("PRTD-CREATE: 0x%px (%s)\n",
-		 prtd, substream->stream ? "C" : "P");
-
-	kref_init(&prtd->refcount);
-	prtd->parent = dlp;
-	prtd->stream = substream->stream;
-	prtd->dma_chan = chan;
-
-	substream->runtime->private_data = prtd;
+	substream->runtime->private_data = drd;
 
 	dlp_activate(dlp);
 
 	return 0;
 }
+EXPORT_SYMBOL_GPL(dlp_open);
 
-static void dmaengine_free_prtd(struct kref *ref)
+int dlp_close(struct dlp *dlp, struct dlp_runtime_data *drd,
+	      struct snd_pcm_substream *substream)
 {
-	struct dmaengine_dlp_runtime_data *prtd =
-		container_of(ref, struct dmaengine_dlp_runtime_data, refcount);
-
-	dlp_info("PRTD-FREE: 0x%px\n", prtd);
-
-	kfree(prtd->buf);
-	kfree(prtd);
-}
-
-static void free_ref_list(struct snd_soc_component *component)
-{
-	struct dmaengine_dlp *dlp = soc_component_to_dlp(component);
-	struct dmaengine_dlp_runtime_data *prtd, *_pt;
-
-	spin_lock(&dlp->lock);
-	list_for_each_entry_safe(prtd, _pt, &dlp->ref_list, node) {
-		list_del(&prtd->node);
-		kref_put(&prtd->refcount, dmaengine_free_prtd);
-	}
-	spin_unlock(&dlp->lock);
-}
-
-static int dmaengine_dlp_close(struct snd_soc_component *component,
-			       struct snd_pcm_substream *substream)
-{
-	struct dmaengine_dlp *dlp = soc_component_to_dlp(component);
-	struct dmaengine_dlp_runtime_data *prtd = substream_to_prtd(substream);
-
-	dmaengine_synchronize(prtd->dma_chan);
+	if (unlikely(!dlp || !drd))
+		return -EINVAL;
 
 	/*
-	 * kref put should be after hw_ptr updated when stop,
-	 * ops->trigger: SNDRV_PCM_TRIGGER_STOP -> ops->close
-	 * obviously, it is!
+	 * In case: open -> hw_params -> prepare -> close flow
+	 * should check and free all.
 	 */
-	kref_put(&prtd->refcount, dmaengine_free_prtd);
+	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+		drd_put(dlp->drd_pb_shadow);
+		dlp->drd_pb_shadow = NULL;
+	} else {
+		drd_buf_free(drd);
+	}
 
 	dlp_deactivate(dlp);
 
 	return 0;
 }
+EXPORT_SYMBOL_GPL(dlp_close);
 
-static snd_pcm_uframes_t dmaengine_dlp_pointer(
-	struct snd_soc_component *component,
-	struct snd_pcm_substream *substream)
+void dlp_dma_complete(struct dlp *dlp, struct dlp_runtime_data *drd)
 {
-	struct dmaengine_dlp_runtime_data *prtd = substream_to_prtd(substream);
-	struct dma_tx_state state;
-	unsigned int buf_size;
-	unsigned int pos = 0;
-
-	dmaengine_tx_status(prtd->dma_chan, prtd->cookie, &state);
-	buf_size = snd_pcm_lib_buffer_bytes(substream);
-	if (state.residue > 0 && state.residue <= buf_size)
-		pos = buf_size - state.residue;
-
-	return dlp_bytes_to_frames(prtd, pos);
-}
-
-static void dmaengine_dlp_dma_complete(void *arg)
-{
-	struct snd_pcm_substream *substream = arg;
-	struct dmaengine_dlp_runtime_data *prtd = substream_to_prtd(substream);
-	struct dmaengine_dlp *dlp = prtd->parent;
-
-	if (!substream->runtime)
+	if (unlikely(!dlp || !drd))
 		return;
 
-	spin_lock(&dlp->lock);
-	prtd->period_elapsed++;
-	prtd->hw_ptr = prtd->period_elapsed * prtd->period_sz;
-	spin_unlock(&dlp->lock);
-	snd_pcm_period_elapsed(substream);
+	atomic64_inc(&drd->period_elapsed);
 }
+EXPORT_SYMBOL_GPL(dlp_dma_complete);
 
-static int dmaengine_dlp_prepare_and_submit(struct snd_pcm_substream *substream)
+int dlp_start(struct snd_soc_component *component,
+	      struct snd_pcm_substream *substream,
+	      struct device *dev,
+	      dma_pointer_f dma_pointer)
 {
-	struct dmaengine_dlp_runtime_data *prtd = substream_to_prtd(substream);
-	struct dma_chan *chan = prtd->dma_chan;
-	struct dma_async_tx_descriptor *desc;
-	enum dma_transfer_direction direction;
-	unsigned long flags = DMA_CTRL_ACK;
-
-	direction = snd_pcm_substream_to_dma_direction(substream);
-
-	if (!substream->runtime->no_period_wakeup)
-		flags |= DMA_PREP_INTERRUPT;
-
-	desc = dmaengine_prep_dma_cyclic(chan,
-		substream->runtime->dma_addr,
-		snd_pcm_lib_buffer_bytes(substream),
-		snd_pcm_lib_period_bytes(substream), direction, flags);
-
-	if (!desc)
-		return -ENOMEM;
-
-	desc->callback = dmaengine_dlp_dma_complete;
-	desc->callback_param = substream;
-	prtd->cookie = dmaengine_submit(desc);
-
-	return 0;
-}
-
-static int dmaengine_dlp_setup(struct snd_soc_component *component,
-			       struct snd_pcm_substream *substream)
-{
-	struct dmaengine_dlp *dlp = soc_component_to_dlp(component);
+	struct dlp *dlp = soc_component_to_dlp(component);
 	int bstream = SNDRV_PCM_STREAM_LAST - substream->stream;
 	struct snd_pcm_str *bro = &substream->pcm->streams[bstream];
 	struct snd_pcm_substream *bsubstream = bro->substream;
-	struct dmaengine_dlp_runtime_data *prtd = substream_to_prtd(substream);
-	struct dmaengine_dlp_runtime_data *brtd = substream_to_prtd(bsubstream);
-	struct dmaengine_dlp_runtime_data *pref = dlp->pref;
+	struct dlp_runtime_data *adrd = substream_to_drd(substream);
+	struct dlp_runtime_data *bdrd = substream_to_drd(bsubstream);
+	struct dlp_runtime_data *drd_ref;
 	bool is_playback = substream->stream == SNDRV_PCM_STREAM_PLAYBACK;
-	snd_pcm_uframes_t a = 0, b = 0, fifo_a = 0, fifo_b = 0;
+	uint64_t a = 0, b = 0;
+	snd_pcm_uframes_t fifo_a = 0, fifo_b = 0;
 	snd_pcm_sframes_t delta = 0;
+
+	if (unlikely(!dlp || !adrd || !dma_pointer))
+		return -EINVAL;
 
 	if (dlp->mode == DLP_MODE_DISABLED)
 		return -EINVAL;
 
-	fifo_a = dlp->config->get_fifo_count(dlp->dev, substream->stream);
-	a = dmaengine_dlp_pointer(component, substream);
+	fifo_a = dlp->config->get_fifo_count(dev, substream);
+	a = dma_pointer(component, substream) % adrd->period_sz;
 
 	if (bsubstream->runtime && snd_pcm_running(bsubstream)) {
-		fifo_b = dlp->config->get_fifo_count(dlp->dev, bstream);
-		b = dmaengine_dlp_pointer(component, bsubstream);
+		if (unlikely(!bdrd))
+			return -EINVAL;
 
-		spin_lock(&dlp->lock);
-		if (!pref) {
-			spin_unlock(&dlp->lock);
+		fifo_b = dlp->config->get_fifo_count(dev, bsubstream);
+		b = dma_pointer(component, bsubstream) % bdrd->period_sz;
+
+		drd_ref = drd_rdy_list_get(dlp);
+		if (unlikely(!drd_ref)) {
+			dev_err(dev, "Failed to get rdy drd\n");
 			return -EINVAL;
 		}
 
-		a = (prtd->period_elapsed * prtd->period_sz) + (a % prtd->period_sz);
-		b = (brtd->period_elapsed * brtd->period_sz) + (b % brtd->period_sz);
+		a += (atomic64_read(&adrd->period_elapsed) * adrd->period_sz);
+		b += (atomic64_read(&bdrd->period_elapsed) * bdrd->period_sz);
 
-		fifo_a = dlp_bytes_to_frames(prtd, fifo_a * 4);
-		fifo_b = dlp_bytes_to_frames(brtd, fifo_b * 4);
+		fifo_a = dlp_bytes_to_frames(adrd, fifo_a * 4);
+		fifo_b = dlp_bytes_to_frames(bdrd, fifo_b * 4);
 
 		delta = is_playback ? (a - fifo_a) - (b + fifo_b) : (b - fifo_b) - (a + fifo_a);
 
-		pref->hw_ptr_delta = delta;
-		kref_get(&pref->refcount);
-		/* push valid playback into ref list */
-		list_add_tail(&pref->node, &dlp->ref_list);
+		drd_ref->hw_ptr_delta = delta;
 
-		spin_unlock(&dlp->lock);
+		drd_ref_list_add(dlp, drd_ref);
 	}
 
 	if (is_playback)
-		dlp_info("START-P: DMA-P: %lu, DMA-C: %lu, FIFO-P: %lu, FIFO-C: %lu, DELTA: %ld\n",
-			 a, b, fifo_a, fifo_b, delta);
+		dev_dbg(dev, "START-P: DMA-P: %llu, DMA-C: %llu, FIFO-P: %lu, FIFO-C: %lu, DELTA: %ld\n",
+			a, b, fifo_a, fifo_b, delta);
 	else
-		dlp_info("START-C: DMA-P: %lu, DMA-C: %lu, FIFO-P: %lu, FIFO-C: %lu, DELTA: %ld\n",
-			 b, a, fifo_b, fifo_a, delta);
+		dev_dbg(dev, "START-C: DMA-P: %llu, DMA-C: %llu, FIFO-P: %lu, FIFO-C: %lu, DELTA: %ld\n",
+			b, a, fifo_b, fifo_a, delta);
 
 	return 0;
 }
+EXPORT_SYMBOL_GPL(dlp_start);
 
-static void dmaengine_dlp_release(struct snd_soc_component *component,
-				  struct snd_pcm_substream *substream)
+void dlp_stop(struct snd_soc_component *component,
+	      struct snd_pcm_substream *substream,
+	      dma_pointer_f dma_pointer)
 {
-	struct dmaengine_dlp *dlp = soc_component_to_dlp(component);
-	struct dmaengine_dlp_runtime_data *prtd = substream_to_prtd(substream);
-	struct dmaengine_dlp_runtime_data *pref = dlp->pref;
+	struct dlp *dlp = soc_component_to_dlp(component);
+	struct dlp_runtime_data *drd = substream_to_drd(substream);
 	struct snd_pcm_runtime *runtime = substream->runtime;
-	snd_pcm_uframes_t appl_ptr, hw_ptr;
+	uint64_t appl_ptr, hw_ptr;
+
+	if (unlikely(!dlp || !drd || !runtime || !dma_pointer))
+		return;
 
 	if (dlp->mode == DLP_MODE_DISABLED)
 		return;
 
 	/* any data in FIFOs will be gone ,so don't care */
 	appl_ptr = READ_ONCE(runtime->control->appl_ptr);
-	hw_ptr = dmaengine_dlp_pointer(component, substream);
-	spin_lock(&dlp->lock);
-	hw_ptr = (prtd->period_elapsed * prtd->period_sz) + (hw_ptr % prtd->period_sz);
-	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
-		pref->hw_ptr = min(hw_ptr, appl_ptr);
-	prtd->period_elapsed = 0;
-	prtd->hw_ptr = 0;
-	spin_unlock(&dlp->lock);
+	hw_ptr = dma_pointer(component, substream) % drd->period_sz;
+	hw_ptr += (atomic64_read(&drd->period_elapsed) * drd->period_sz);
 
 	/*
 	 * playback:
@@ -694,187 +652,98 @@
 	 * anyway, we should use the smaller one, obviously, it's hw_ptr.
 	 */
 	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
-		spin_lock(&dlp->pref_lock);
-		kref_put(&pref->refcount, dmaengine_free_prtd);
-		dlp->pref = NULL;
-		spin_unlock(&dlp->pref_lock);
-		dlp_info("STOP-P: applptr: %lu, hwptr: %lu\n", appl_ptr, hw_ptr);
+		if (dlp->drd_pb_shadow) {
+			dlp->drd_pb_shadow->hw_ptr = min(hw_ptr, appl_ptr);
+			atomic_set(&dlp->drd_pb_shadow->stop, 1);
+		}
+		drd_rdy_list_free(dlp);
 	} else {
 		/* free residue playback ref list for capture when stop */
-		free_ref_list(component);
-		dlp_info("STOP-C: applptr: %lu, hwptr: %lu\n", appl_ptr, hw_ptr);
-	}
-}
-
-static int dmaengine_dlp_trigger(struct snd_soc_component *component,
-				 struct snd_pcm_substream *substream, int cmd)
-{
-	struct dmaengine_dlp_runtime_data *prtd = substream_to_prtd(substream);
-	struct snd_pcm_runtime *runtime = substream->runtime;
-	int ret;
-
-	switch (cmd) {
-	case SNDRV_PCM_TRIGGER_START:
-		ret = dmaengine_dlp_prepare_and_submit(substream);
-		if (ret)
-			return ret;
-		dma_async_issue_pending(prtd->dma_chan);
-		dmaengine_dlp_setup(component, substream);
-		break;
-	case SNDRV_PCM_TRIGGER_RESUME:
-	case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
-		dmaengine_resume(prtd->dma_chan);
-		break;
-	case SNDRV_PCM_TRIGGER_SUSPEND:
-		if (runtime->info & SNDRV_PCM_INFO_PAUSE) {
-			dmaengine_pause(prtd->dma_chan);
-		} else {
-			dmaengine_dlp_release(component, substream);
-			dmaengine_terminate_async(prtd->dma_chan);
-		}
-		break;
-	case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
-		dmaengine_pause(prtd->dma_chan);
-		break;
-	case SNDRV_PCM_TRIGGER_STOP:
-		dmaengine_dlp_release(component, substream);
-		dmaengine_terminate_async(prtd->dma_chan);
-		break;
-	default:
-		return -EINVAL;
+		drd_ref_list_free(dlp);
 	}
 
-	return 0;
+	atomic64_set(&drd->period_elapsed, 0);
+
+	dev_dbg(dlp->dev, "STOP-%s: applptr: %llu, hwptr: %llu\n",
+		substream->stream ? "C" : "P", appl_ptr, hw_ptr);
 }
-
-static int dmaengine_dlp_new(struct snd_soc_component *component,
-			     struct snd_soc_pcm_runtime *rtd)
-{
-	struct dmaengine_dlp *dlp = soc_component_to_dlp(component);
-	struct snd_pcm_substream *substream;
-	size_t prealloc_buffer_size;
-	size_t max_buffer_size;
-	unsigned int i;
-
-	prealloc_buffer_size = prealloc_buffer_size_kbytes * 1024;
-	max_buffer_size = SIZE_MAX;
-
-	for_each_pcm_streams(i) {
-		substream = rtd->pcm->streams[i].substream;
-		if (!substream)
-			continue;
-
-		if (!dlp->chan[i]) {
-			dev_err(component->dev,
-				"Missing dma channel for stream: %d\n", i);
-			return -EINVAL;
-		}
-
-		snd_pcm_set_managed_buffer(substream,
-				SNDRV_DMA_TYPE_DEV_IRAM,
-				dmaengine_dma_dev(dlp, substream),
-				prealloc_buffer_size,
-				max_buffer_size);
-
-		if (rtd->pcm->streams[i].pcm->name[0] == '\0') {
-			strscpy_pad(rtd->pcm->streams[i].pcm->name,
-				    rtd->pcm->streams[i].pcm->id,
-				    sizeof(rtd->pcm->streams[i].pcm->name));
-		}
-	}
-
-	return 0;
-}
-
-static struct dmaengine_dlp_runtime_data *get_ref(struct snd_soc_component *component)
-{
-	struct dmaengine_dlp *dlp = soc_component_to_dlp(component);
-	struct dmaengine_dlp_runtime_data *pref = NULL;
-
-	spin_lock(&dlp->lock);
-	if (!list_empty(&dlp->ref_list)) {
-		pref = list_first_entry(&dlp->ref_list, struct dmaengine_dlp_runtime_data, node);
-		list_del(&pref->node);
-	}
-	spin_unlock(&dlp->lock);
-
-	return pref;
-}
+EXPORT_SYMBOL_GPL(dlp_stop);
 
 static int process_capture(struct snd_soc_component *component,
 			   struct snd_pcm_substream *substream,
 			   unsigned long hwoff,
 			   void __user *buf, unsigned long bytes)
 {
-	struct dmaengine_dlp *dlp = soc_component_to_dlp(component);
+	struct dlp *dlp = soc_component_to_dlp(component);
 	struct snd_pcm_runtime *runtime = substream->runtime;
-	struct dmaengine_dlp_runtime_data *prtd = substream_to_prtd(substream);
-	struct dmaengine_dlp_runtime_data *pref = NULL;
-	void *dma_ptr = runtime->dma_area + hwoff;
-	snd_pcm_sframes_t frames = dlp_bytes_to_frames(prtd, bytes);
+	struct dlp_runtime_data *drd = substream_to_drd(substream);
+	struct dlp_runtime_data *drd_ref = NULL;
+	snd_pcm_sframes_t frames = 0;
 	snd_pcm_sframes_t frames_consumed = 0, frames_residue = 0, frames_tmp = 0;
 	snd_pcm_sframes_t ofs = 0;
 	snd_pcm_uframes_t appl_ptr;
-	char *cbuf = prtd->buf, *pbuf = NULL;
 	int ofs_cap, ofs_play, size_cap, size_play;
 	int i = 0, j = 0, ret = 0;
 	bool free_ref = false, mix = false;
+	char *cbuf = NULL, *pbuf = NULL;
+	void *dma_ptr;
+
+	if (unlikely(!drd || !runtime || !buf))
+		return -EINVAL;
+
+	frames = dlp_bytes_to_frames(drd, bytes);
+	dma_ptr = runtime->dma_area + hwoff;
+	cbuf = drd->buf;
 
 	appl_ptr = READ_ONCE(runtime->control->appl_ptr);
 
 	memcpy(cbuf, dma_ptr, bytes);
 #ifdef DLP_DBG
 	/* DBG: mark STUB in ch-REC for trace each read */
-	memset(cbuf, 0x22, dlp_channels_to_bytes(prtd, 1));
+	memset(cbuf, 0x22, dlp_channels_to_bytes(drd, 1));
 #endif
-	ret = dlp_get_offset_size(prtd, dlp->mode, &ofs_cap, &size_cap, NULL);
+	ret = dlp_get_offset_size(drd, dlp->mode, &ofs_cap, &size_cap, NULL);
 	if (ret) {
-		dlp_info("fail to get dlp cap offset\n");
+		dev_err(dlp->dev, "Failed to get dlp cap offset\n");
 		return -EINVAL;
 	}
 
 	/* clear channel-LP_CHN */
 	for (i = 0; i < frames; i++) {
-		cbuf = prtd->buf + dlp_frames_to_bytes(prtd, i) + ofs_cap;
+		cbuf = drd->buf + dlp_frames_to_bytes(drd, i) + ofs_cap;
 		memset(cbuf, 0x0, size_cap);
 	}
 
 start:
-	if (!prtd->ref)
-		prtd->ref = get_ref(component);
-	pref = prtd->ref;
-
-	/* do nothing if play stop */
-	if (!pref)
+	drd_ref = drd_get(drd_ref_list_first(dlp));
+	if (!drd_ref)
 		return 0;
 
-	ret = dlp_get_offset_size(pref, dlp->mode, &ofs_play, &size_play, &mix);
+	ret = dlp_get_offset_size(drd_ref, dlp->mode, &ofs_play, &size_play, &mix);
 	if (ret) {
-		dlp_info("fail to get dlp play offset\n");
-		return 0;
+		dev_err(dlp->dev, "Failed to get dlp play offset\n");
+		goto _drd_put;
 	}
 
-	ofs = appl_ptr + pref->hw_ptr_delta;
+	ofs = appl_ptr + drd_ref->hw_ptr_delta;
 
 	/*
-	 * if playback stop, kref_put ref, and we can check this to
-	 * know if playback stopped, then free prtd->ref if data consumed.
-	 *
+	 * if playback stop, process the data tail and then
+	 * free drd_ref if data consumed.
 	 */
-	if (kref_read(&pref->refcount) == 1) {
-		if (ofs >= pref->hw_ptr) {
-			kref_put(&pref->refcount, dmaengine_free_prtd);
-			prtd->ref = NULL;
-			return 0;
-		} else if ((ofs + frames) > pref->hw_ptr) {
-			dlp_info("applptr: %8lu, ofs': %7ld, refhwptr: %lu, frames: %lu (*)\n",
-				 appl_ptr, ofs, pref->hw_ptr, frames);
+	if (atomic_read(&drd_ref->stop)) {
+		if (ofs >= drd_ref->hw_ptr) {
+			drd_put(drd_ref_list_del(dlp, drd_ref));
+			goto _drd_put;
+		} else if ((ofs + frames) > drd_ref->hw_ptr) {
+			dev_dbg(dlp->dev, "applptr: %8lu, ofs': %7ld, refhwptr: %lld, frames: %ld (*)\n",
+				appl_ptr, ofs, drd_ref->hw_ptr, frames);
 			/*
 			 * should ignore the data that after play stop
 			 * and care about if the next ref start in the
 			 * same window
 			 */
-			frames_tmp = pref->hw_ptr - ofs;
+			frames_tmp = drd_ref->hw_ptr - ofs;
 			frames_residue = frames - frames_tmp;
 			frames = frames_tmp;
 			free_ref = true;
@@ -891,27 +760,29 @@
 	 *
 	 */
 	if ((ofs + frames) <= 0)
-		return 0;
+		goto _drd_put;
 
 	/* skip if ofs < 0 and fixup ofs */
 	j = 0;
 	if (ofs < 0) {
-		dlp_info("applptr: %8lu, ofs: %8ld, frames: %lu (*)\n",
-			 appl_ptr, ofs, frames);
+		dev_dbg(dlp->dev, "applptr: %8lu, ofs: %8ld, frames: %ld (*)\n",
+			appl_ptr, ofs, frames);
 		j = -ofs;
 		frames += ofs;
 		ofs = 0;
+		appl_ptr += j;
 	}
 
-	ofs %= pref->buf_sz;
+	ofs %= drd_ref->buf_sz;
 
-	dlp_info("applptr: %8lu, ofs: %8ld, frames: %lu\n", appl_ptr, ofs, frames);
+	dev_dbg(dlp->dev, "applptr: %8lu, ofs: %8ld, frames: %5ld, refc: %u\n",
+		appl_ptr, ofs, frames, kref_read(&drd_ref->refcount));
 
 	for (i = 0; i < frames; i++, j++) {
-		cbuf = prtd->buf + dlp_frames_to_bytes(prtd, j + frames_consumed) + ofs_cap;
-		pbuf = pref->buf + dlp_frames_to_bytes(pref, ((i + ofs) % pref->buf_sz)) + ofs_play;
+		cbuf = drd->buf + dlp_frames_to_bytes(drd, j + frames_consumed) + ofs_cap;
+		pbuf = drd_ref->buf + dlp_frames_to_bytes(drd_ref, ((i + ofs) % drd_ref->buf_sz)) + ofs_play;
 		if (mix)
-			dlp_mix_frame_buffer(pref, pbuf);
+			dlp_mix_frame_buffer(drd_ref, pbuf);
 		memcpy(cbuf, pbuf, size_cap);
 	}
 
@@ -919,8 +790,9 @@
 	frames_consumed += frames;
 
 	if (free_ref) {
-		kref_put(&pref->refcount, dmaengine_free_prtd);
-		prtd->ref = NULL;
+		drd_put(drd_ref_list_del(dlp, drd_ref));
+		drd_put(drd_ref);
+		drd_ref = NULL;
 		free_ref = false;
 		if (frames_residue) {
 			frames = frames_residue;
@@ -928,6 +800,10 @@
 			goto start;
 		}
 	}
+
+_drd_put:
+	drd_put(drd_ref);
+	drd_ref = NULL;
 
 	return 0;
 }
@@ -937,40 +813,37 @@
 			    unsigned long hwoff,
 			    void __user *buf, unsigned long bytes)
 {
-	struct dmaengine_dlp *dlp = soc_component_to_dlp(component);
-	struct dmaengine_dlp_runtime_data *pref;
+	struct dlp *dlp = soc_component_to_dlp(component);
+	struct dlp_runtime_data *drd;
 	char *pbuf;
 	int ret = 0;
 
-	spin_lock(&dlp->pref_lock);
-	pref = dlp->pref;
-	if (!pref) {
-		ret = -EFAULT;
-		goto err_unlock;
-	}
+	drd = drd_get(dlp->drd_pb_shadow);
+	if (!drd)
+		return 0;
 
-	pbuf = pref->buf + pref->buf_ofs;
+	pbuf = drd->buf + drd->buf_ofs;
 
 	if (copy_from_user(pbuf, buf, bytes)) {
 		ret = -EFAULT;
-		goto err_unlock;
+		goto err_put;
 	}
 
-	pref->buf_ofs += bytes;
-	pref->buf_ofs %= dlp_frames_to_bytes(pref, pref->buf_sz);
+	drd->buf_ofs += bytes;
+	drd->buf_ofs %= dlp_frames_to_bytes(drd, drd->buf_sz);
 
-err_unlock:
-	spin_unlock(&dlp->pref_lock);
+err_put:
+	drd_put(drd);
 
 	return ret;
 }
 
-static int dmaengine_dlp_process(struct snd_soc_component *component,
-				 struct snd_pcm_substream *substream,
-				 unsigned long hwoff,
-				 void __user *buf, unsigned long bytes)
+static int dlp_process(struct snd_soc_component *component,
+		       struct snd_pcm_substream *substream,
+		       unsigned long hwoff,
+		       void __user *buf, unsigned long bytes)
 {
-	struct dmaengine_dlp *dlp = soc_component_to_dlp(component);
+	struct dlp *dlp = soc_component_to_dlp(component);
 	int ret = 0;
 
 	if (dlp->mode == DLP_MODE_DISABLED)
@@ -984,25 +857,30 @@
 	return ret;
 }
 
-static int dmaengine_dlp_copy_user(struct snd_soc_component *component,
-				   struct snd_pcm_substream *substream,
-				   int channel, unsigned long hwoff,
-				   void __user *buf, unsigned long bytes)
+int dlp_copy_user(struct snd_soc_component *component,
+		  struct snd_pcm_substream *substream,
+		  int channel, unsigned long hwoff,
+		  void __user *buf, unsigned long bytes)
 {
-	struct dmaengine_dlp_runtime_data *prtd = substream_to_prtd(substream);
+	struct dlp_runtime_data *drd = substream_to_drd(substream);
 	struct snd_pcm_runtime *runtime = substream->runtime;
 	bool is_playback = substream->stream == SNDRV_PCM_STREAM_PLAYBACK;
-	void *dma_ptr = runtime->dma_area + hwoff +
-			channel * (runtime->dma_bytes / runtime->channels);
+	void *dma_ptr;
 	int ret;
+
+	if (unlikely(!drd || !runtime || !buf))
+		return -EINVAL;
+
+	dma_ptr = runtime->dma_area + hwoff +
+		  channel * (runtime->dma_bytes / runtime->channels);
 
 	if (is_playback)
 		if (copy_from_user(dma_ptr, buf, bytes))
 			return -EFAULT;
 
-	ret = dmaengine_dlp_process(component, substream, hwoff, buf, bytes);
+	ret = dlp_process(component, substream, hwoff, buf, bytes);
 	if (!ret)
-		dma_ptr = prtd->buf;
+		dma_ptr = drd->buf;
 
 	if (!is_playback)
 		if (copy_to_user(buf, dma_ptr, bytes))
@@ -1010,30 +888,33 @@
 
 	return 0;
 }
+EXPORT_SYMBOL_GPL(dlp_copy_user);
 
 static SOC_ENUM_SINGLE_EXT_DECL(dlp_mode, dlp_text);
 
-static int dmaengine_dlp_mode_get(struct snd_kcontrol *kcontrol,
-				  struct snd_ctl_elem_value *ucontrol)
+static int dlp_mode_get(struct snd_kcontrol *kcontrol,
+			struct snd_ctl_elem_value *ucontrol)
 {
 	struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol);
-	struct dmaengine_dlp *dlp = soc_component_to_dlp(component);
+	struct dlp *dlp = soc_component_to_dlp(component);
 
 	ucontrol->value.enumerated.item[0] = dlp->mode;
 
 	return 0;
 }
 
-static int dmaengine_dlp_mode_put(struct snd_kcontrol *kcontrol,
-				  struct snd_ctl_elem_value *ucontrol)
+static int dlp_mode_put(struct snd_kcontrol *kcontrol,
+			struct snd_ctl_elem_value *ucontrol)
 {
 	struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol);
-	struct dmaengine_dlp *dlp = soc_component_to_dlp(component);
+	struct dlp *dlp = soc_component_to_dlp(component);
 	unsigned int mode = ucontrol->value.enumerated.item[0];
 
 	/* MUST: do not update mode while stream is running */
-	if (snd_soc_component_active(component))
+	if (atomic_read(&dlp->active)) {
+		dev_err(dlp->dev, "Should set this mode before pcm open\n");
 		return -EPERM;
+	}
 
 	if (mode == dlp->mode)
 		return 0;
@@ -1043,207 +924,141 @@
 	return 1;
 }
 
-static const struct snd_kcontrol_new dmaengine_dlp_controls[] = {
+static const struct snd_kcontrol_new dlp_controls[] = {
 	SOC_ENUM_EXT("Software Digital Loopback Mode", dlp_mode,
-		     dmaengine_dlp_mode_get,
-		     dmaengine_dlp_mode_put),
+		     dlp_mode_get, dlp_mode_put),
 };
 
-static int dmaengine_dlp_prepare(struct snd_soc_component *component,
-				  struct snd_pcm_substream *substream)
+int dlp_prepare(struct snd_soc_component *component,
+		struct snd_pcm_substream *substream)
 {
-	struct dmaengine_dlp *dlp = soc_component_to_dlp(component);
-	struct dmaengine_dlp_runtime_data *prtd = substream_to_prtd(substream);
-	struct dmaengine_dlp_runtime_data *pref = NULL;
-	int buf_bytes = dlp_frames_to_bytes(prtd, prtd->buf_sz);
-
-	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
-		pref = kmemdup(prtd, sizeof(*prtd), GFP_KERNEL);
-		if (!pref)
-			return -ENOMEM;
-
-		kref_init(&pref->refcount);
-		pref->buf_ofs = 0;
-		pref->buf = kzalloc(buf_bytes, GFP_KERNEL);
-		if (!pref->buf) {
-			kfree(pref);
-			return -ENOMEM;
-		}
-
-		spin_lock(&dlp->pref_lock);
-		dlp->pref = pref;
-		spin_unlock(&dlp->pref_lock);
-		dlp_info("PREF-CREATE: 0x%px\n", pref);
-	} else {
-		prtd->buf = kzalloc(buf_bytes, GFP_KERNEL);
-		if (!prtd->buf)
-			return -ENOMEM;
-	}
-
-	return 0;
-}
-static const struct snd_soc_component_driver dmaengine_dlp_component = {
-	.name		= SND_DMAENGINE_DLP_DRV_NAME,
-	.probe_order	= SND_SOC_COMP_ORDER_LATE,
-	.open		= dmaengine_dlp_open,
-	.close		= dmaengine_dlp_close,
-	.hw_params	= dmaengine_dlp_hw_params,
-	.prepare	= dmaengine_dlp_prepare,
-	.trigger	= dmaengine_dlp_trigger,
-	.pointer	= dmaengine_dlp_pointer,
-	.copy_user	= dmaengine_dlp_copy_user,
-	.pcm_construct	= dmaengine_dlp_new,
-	.controls	= dmaengine_dlp_controls,
-	.num_controls	= ARRAY_SIZE(dmaengine_dlp_controls),
-};
-
-static const char * const dmaengine_pcm_dma_channel_names[] = {
-	[SNDRV_PCM_STREAM_PLAYBACK] = "tx",
-	[SNDRV_PCM_STREAM_CAPTURE] = "rx",
-};
-
-static int dmaengine_pcm_request_chan_of(struct dmaengine_dlp *dlp,
-	struct device *dev, const struct snd_dmaengine_pcm_config *config)
-{
-	unsigned int i;
-	const char *name;
-	struct dma_chan *chan;
-
-	for_each_pcm_streams(i) {
-		name = dmaengine_pcm_dma_channel_names[i];
-		chan = dma_request_chan(dev, name);
-		if (IS_ERR(chan)) {
-			/*
-			 * Only report probe deferral errors, channels
-			 * might not be present for devices that
-			 * support only TX or only RX.
-			 */
-			if (PTR_ERR(chan) == -EPROBE_DEFER)
-				return -EPROBE_DEFER;
-			dlp->chan[i] = NULL;
-		} else {
-			dlp->chan[i] = chan;
-		}
-	}
-
-	return 0;
-}
-
-static void dmaengine_pcm_release_chan(struct dmaengine_dlp *dlp)
-{
-	unsigned int i;
-
-	for_each_pcm_streams(i) {
-		if (!dlp->chan[i])
-			continue;
-		dma_release_channel(dlp->chan[i]);
-	}
-}
-
-/**
- * snd_dmaengine_dlp_register - Register a dmaengine based DLP device
- * @dev: The parent device for the DLP device
- * @config: Platform specific DLP configuration
- */
-static int snd_dmaengine_dlp_register(struct device *dev,
-	const struct snd_dlp_config *config)
-{
-	const struct snd_soc_component_driver *driver;
-	struct dmaengine_dlp *dlp;
+	struct dlp *dlp = soc_component_to_dlp(component);
+	struct dlp_runtime_data *drd = substream_to_drd(substream);
+	struct dlp_runtime_data *drd_new = NULL;
+	int buf_bytes, last_buf_bytes;
 	int ret;
 
-	dlp = kzalloc(sizeof(*dlp), GFP_KERNEL);
-	if (!dlp)
-		return -ENOMEM;
+	if (unlikely(!dlp || !drd))
+		return -EINVAL;
+
+	if (dlp->mode == DLP_MODE_DISABLED)
+		return 0;
+
+	buf_bytes = dlp_frames_to_bytes(drd, drd->buf_sz);
+	last_buf_bytes = dlp_frames_to_bytes(drd, drd->last_buf_sz);
+
+	if (substream->runtime->status->state == SNDRV_PCM_STATE_XRUN)
+		dev_dbg(dlp->dev, "stream[%d]: prepare from XRUN\n",
+			substream->stream);
+
+	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+		dev_dbg(dlp->dev, "avl count: %d\n", dlp->drd_avl_count);
+		if (snd_BUG_ON(!dlp->drd_avl_count))
+			return -EINVAL;
+
+		/*
+		 * There might be multiple calls hw_params -> prepare
+		 * before start stream, so, should check buf size status
+		 * to determine whether to re-create buf or do nothing.
+		 */
+		if (drd_rdy_list_found(dlp, dlp->drd_pb_shadow)) {
+			if (buf_bytes == last_buf_bytes)
+				return 0;
+
+			drd_rdy_list_free(dlp);
+		}
+
+		/* release the old one, re-create for new params */
+		drd_put(dlp->drd_pb_shadow);
+		dlp->drd_pb_shadow = NULL;
+
+		drd_new = drd_avl_list_get(dlp);
+		if (!drd_new)
+			return -ENOMEM;
+
+		drd_init_from(drd_new, drd);
+
+		ret = drd_buf_alloc(drd_new, buf_bytes);
+		if (ret)
+			return -ENOMEM;
+
+		if (snd_BUG_ON(!drd_get(drd_new)))
+			return -EINVAL;
+
+		drd_rdy_list_add(dlp, drd_new);
+
+		dlp->drd_pb_shadow = drd_new;
+	} else {
+		/*
+		 * There might be multiple calls hw_params -> prepare
+		 * before start stream, so, should check buf size status
+		 * to determine whether to re-create buf or do nothing.
+		 */
+		if (drd->buf && buf_bytes == last_buf_bytes)
+			return 0;
+
+		drd_buf_free(drd);
+
+		ret = drd_buf_alloc(drd, buf_bytes);
+		if (ret)
+			return ret;
+	}
+
+	/* update last after all done success */
+	drd->last_buf_sz = drd->buf_sz;
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(dlp_prepare);
+
+int dlp_probe(struct snd_soc_component *component)
+{
+	snd_soc_add_component_controls(component, dlp_controls,
+				       ARRAY_SIZE(dlp_controls));
+	return 0;
+}
+EXPORT_SYMBOL_GPL(dlp_probe);
+
+int dlp_register(struct dlp *dlp, struct device *dev,
+		 const struct snd_soc_component_driver *driver,
+		 const struct snd_dlp_config *config)
+{
+	struct dlp_runtime_data *drd;
+	int ret = 0, i = 0;
+
+	if (unlikely(!dlp || !dev || !driver || !config))
+		return -EINVAL;
 
 	dlp->dev = dev;
 	dlp->config = config;
 
-	INIT_LIST_HEAD(&dlp->ref_list);
-	spin_lock_init(&dlp->lock);
-	spin_lock_init(&dlp->pref_lock);
-
 #ifdef CONFIG_DEBUG_FS
 	dlp->component.debugfs_prefix = "dma";
 #endif
-	ret = dmaengine_pcm_request_chan_of(dlp, dev, NULL);
-	if (ret)
-		goto err_free_dma;
+	INIT_LIST_HEAD(&dlp->drd_avl_list);
+	INIT_LIST_HEAD(&dlp->drd_rdy_list);
+	INIT_LIST_HEAD(&dlp->drd_ref_list);
 
-	driver = &dmaengine_dlp_component;
+	dlp->drd_avl_count = ARRAY_SIZE(dlp->drds);
+
+	for (i = 0; i < dlp->drd_avl_count; i++) {
+		drd = &dlp->drds[i];
+		list_add_tail(&drd->node, &dlp->drd_avl_list);
+	}
+
+	spin_lock_init(&dlp->lock);
+	atomic_set(&dlp->active, 0);
 
 	ret = snd_soc_component_initialize(&dlp->component, driver, dev);
 	if (ret)
-		goto err_free_dma;
+		return ret;
 
 	ret = snd_soc_add_component(&dlp->component, NULL, 0);
-	if (ret)
-		goto err_free_dma;
-
-	return 0;
-
-err_free_dma:
-	dmaengine_pcm_release_chan(dlp);
-	kfree(dlp);
-	return ret;
-}
-
-/**
- * snd_dmaengine_dlp_unregister - Removes a dmaengine based DLP device
- * @dev: Parent device the DLP was register with
- *
- * Removes a dmaengine based DLP device previously registered with
- * snd_dmaengine_dlp_register.
- */
-static void snd_dmaengine_dlp_unregister(struct device *dev)
-{
-	struct snd_soc_component *component;
-	struct dmaengine_dlp *dlp;
-
-	component = snd_soc_lookup_component(dev, SND_DMAENGINE_DLP_DRV_NAME);
-	if (!component)
-		return;
-
-	dlp = soc_component_to_dlp(component);
-
-	snd_soc_unregister_component_by_driver(dev, component->driver);
-	dmaengine_pcm_release_chan(dlp);
-	kfree(dlp);
-}
-
-static void devm_dmaengine_dlp_release(struct device *dev, void *res)
-{
-	snd_dmaengine_dlp_unregister(*(struct device **)res);
-}
-
-/**
- * devm_snd_dmaengine_dlp_register - resource managed dmaengine DLP registration
- * @dev: The parent device for the DLP device
- * @config: Platform specific DLP configuration
- *
- * Register a dmaengine based DLP device with automatic unregistration when the
- * device is unregistered.
- */
-int devm_snd_dmaengine_dlp_register(struct device *dev,
-	const struct snd_dlp_config *config)
-{
-	struct device **ptr;
-	int ret;
-
-	ptr = devres_alloc(devm_dmaengine_dlp_release, sizeof(*ptr), GFP_KERNEL);
-	if (!ptr)
-		return -ENOMEM;
-
-	ret = snd_dmaengine_dlp_register(dev, config);
-	if (ret == 0) {
-		*ptr = dev;
-		devres_add(dev, ptr);
-	} else {
-		devres_free(ptr);
-	}
 
 	return ret;
 }
-EXPORT_SYMBOL_GPL(devm_snd_dmaengine_dlp_register);
+EXPORT_SYMBOL_GPL(dlp_register);
 
+MODULE_DESCRIPTION("Rockchip Digital Loopback Core Driver");
+MODULE_AUTHOR("Sugar Zhang <sugar.zhang@rock-chips.com>");
 MODULE_LICENSE("GPL");

--
Gitblit v1.6.2