xref: /OK3568_Linux_fs/kernel/sound/soc/rockchip/rockchip_dlp.c (revision 4882a59341e53eb6f0b4789bf948001014eff981)
1*4882a593Smuzhiyun // SPDX-License-Identifier: GPL-2.0
2*4882a593Smuzhiyun /*
3*4882a593Smuzhiyun  * Rockchip DLP (Digital Loopback) Driver
4*4882a593Smuzhiyun  *
5*4882a593Smuzhiyun  * Copyright (c) 2022 Rockchip Electronics Co. Ltd.
6*4882a593Smuzhiyun  * Author: Sugar Zhang <sugar.zhang@rock-chips.com>
7*4882a593Smuzhiyun  *
8*4882a593Smuzhiyun  */
9*4882a593Smuzhiyun 
10*4882a593Smuzhiyun #include <linux/kref.h>
11*4882a593Smuzhiyun #include <linux/module.h>
12*4882a593Smuzhiyun #include <linux/init.h>
13*4882a593Smuzhiyun #include <linux/dmaengine.h>
14*4882a593Smuzhiyun #include <linux/slab.h>
15*4882a593Smuzhiyun #include <sound/pcm.h>
16*4882a593Smuzhiyun #include <sound/pcm_params.h>
17*4882a593Smuzhiyun #include <sound/soc.h>
18*4882a593Smuzhiyun #include <linux/dma-mapping.h>
19*4882a593Smuzhiyun #include <linux/of.h>
20*4882a593Smuzhiyun 
21*4882a593Smuzhiyun #include <sound/dmaengine_pcm.h>
22*4882a593Smuzhiyun #include "rockchip_dlp.h"
23*4882a593Smuzhiyun 
24*4882a593Smuzhiyun #ifdef DLP_DBG
25*4882a593Smuzhiyun #define dlp_info(args...)		pr_info(args)
26*4882a593Smuzhiyun #else
27*4882a593Smuzhiyun #define dlp_info(args...)		no_printk(args)
28*4882a593Smuzhiyun #endif
29*4882a593Smuzhiyun 
30*4882a593Smuzhiyun #define SND_DMAENGINE_DLP_DRV_NAME	"snd_dmaengine_dlp"
31*4882a593Smuzhiyun #define PBUF_CNT			2
32*4882a593Smuzhiyun 
33*4882a593Smuzhiyun static unsigned int prealloc_buffer_size_kbytes = 512;
34*4882a593Smuzhiyun module_param(prealloc_buffer_size_kbytes, uint, 0444);
35*4882a593Smuzhiyun MODULE_PARM_DESC(prealloc_buffer_size_kbytes, "Preallocate DMA buffer size (KB).");
36*4882a593Smuzhiyun 
37*4882a593Smuzhiyun /* MUST: dlp_text should be match to enum dlp_mode */
38*4882a593Smuzhiyun static const char *const dlp_text[] = {
39*4882a593Smuzhiyun 	"Disabled",
40*4882a593Smuzhiyun 	"2CH: 1 Loopback + 1 Mic",
41*4882a593Smuzhiyun 	"2CH: 1 Mic + 1 Loopback",
42*4882a593Smuzhiyun 	"2CH: 1 Mic + 1 Loopback-mixed",
43*4882a593Smuzhiyun 	"2CH: 2 Loopbacks",
44*4882a593Smuzhiyun 	"4CH: 2 Mics + 2 Loopbacks",
45*4882a593Smuzhiyun 	"4CH: 2 Mics + 1 Loopback-mixed",
46*4882a593Smuzhiyun 	"4CH: 4 Loopbacks",
47*4882a593Smuzhiyun 	"6CH: 4 Mics + 2 Loopbacks",
48*4882a593Smuzhiyun 	"6CH: 4 Mics + 1 Loopback-mixed",
49*4882a593Smuzhiyun 	"6CH: 6 Loopbacks",
50*4882a593Smuzhiyun 	"8CH: 6 Mics + 2 Loopbacks",
51*4882a593Smuzhiyun 	"8CH: 6 Mics + 1 Loopback-mixed",
52*4882a593Smuzhiyun 	"8CH: 8 Loopbacks",
53*4882a593Smuzhiyun 	"10CH: 8 Mics + 2 Loopbacks",
54*4882a593Smuzhiyun 	"10CH: 8 Mics + 1 Loopback-mixed",
55*4882a593Smuzhiyun 	"16CH: 8 Mics + 8 Loopbacks",
56*4882a593Smuzhiyun };
57*4882a593Smuzhiyun 
58*4882a593Smuzhiyun enum dlp_mode {
59*4882a593Smuzhiyun 	DLP_MODE_DISABLED,
60*4882a593Smuzhiyun 	DLP_MODE_2CH_1LP_1MIC,		/* replace cap-ch-0   with play-ch-0 */
61*4882a593Smuzhiyun 	DLP_MODE_2CH_1MIC_1LP,		/* replace cap-ch-1   with play-ch-1 */
62*4882a593Smuzhiyun 	DLP_MODE_2CH_1MIC_1LP_MIX,	/* replace cap-ch-1   with play-ch-all-mix */
63*4882a593Smuzhiyun 	DLP_MODE_2CH_2LP,		/* replace cap-ch-0~1 with play-ch-0~1 */
64*4882a593Smuzhiyun 	DLP_MODE_4CH_2MIC_2LP,		/* replace cap-ch-2~3 with play-ch-0~1 */
65*4882a593Smuzhiyun 	DLP_MODE_4CH_2MIC_1LP_MIX,	/* replace cap-ch-3   with play-ch-all-mix */
66*4882a593Smuzhiyun 	DLP_MODE_4CH_4LP,		/* replace cap-ch-0~3 with play-ch-0~3 */
67*4882a593Smuzhiyun 	DLP_MODE_6CH_4MIC_2LP,		/* replace cap-ch-4~5 with play-ch-0~1 */
68*4882a593Smuzhiyun 	DLP_MODE_6CH_4MIC_1LP_MIX,	/* replace cap-ch-4   with play-ch-all-mix */
69*4882a593Smuzhiyun 	DLP_MODE_6CH_6LP,		/* replace cap-ch-0~5 with play-ch-0~5 */
70*4882a593Smuzhiyun 	DLP_MODE_8CH_6MIC_2LP,		/* replace cap-ch-6~7 with play-ch-0~1 */
71*4882a593Smuzhiyun 	DLP_MODE_8CH_6MIC_1LP_MIX,	/* replace cap-ch-6   with play-ch-all-mix */
72*4882a593Smuzhiyun 	DLP_MODE_8CH_8LP,		/* replace cap-ch-0~7 with play-ch-0~7 */
73*4882a593Smuzhiyun 	DLP_MODE_10CH_8MIC_2LP,		/* replace cap-ch-8~9 with play-ch-0~1 */
74*4882a593Smuzhiyun 	DLP_MODE_10CH_8MIC_1LP_MIX,	/* replace cap-ch-8   with play-ch-all-mix */
75*4882a593Smuzhiyun 	DLP_MODE_16CH_8MIC_8LP,		/* replace cap-ch-8~f with play-ch-8~f */
76*4882a593Smuzhiyun };
77*4882a593Smuzhiyun 
78*4882a593Smuzhiyun struct dmaengine_dlp_runtime_data;
79*4882a593Smuzhiyun struct dmaengine_dlp {
80*4882a593Smuzhiyun 	struct device *dev;
81*4882a593Smuzhiyun 	struct dma_chan *chan[SNDRV_PCM_STREAM_LAST + 1];
82*4882a593Smuzhiyun 	const struct snd_dlp_config *config;
83*4882a593Smuzhiyun 	struct snd_soc_component component;
84*4882a593Smuzhiyun 	struct list_head ref_list;
85*4882a593Smuzhiyun 	enum dlp_mode mode;
86*4882a593Smuzhiyun 	struct dmaengine_dlp_runtime_data *pref;
87*4882a593Smuzhiyun 	spinlock_t lock;
88*4882a593Smuzhiyun 	spinlock_t pref_lock;
89*4882a593Smuzhiyun };
90*4882a593Smuzhiyun 
91*4882a593Smuzhiyun struct dmaengine_dlp_runtime_data {
92*4882a593Smuzhiyun 	struct dmaengine_dlp *parent;
93*4882a593Smuzhiyun 	struct dmaengine_dlp_runtime_data *ref;
94*4882a593Smuzhiyun 	struct dma_chan *dma_chan;
95*4882a593Smuzhiyun 	struct kref refcount;
96*4882a593Smuzhiyun 	struct list_head node;
97*4882a593Smuzhiyun 	dma_cookie_t cookie;
98*4882a593Smuzhiyun 
99*4882a593Smuzhiyun 	char *buf;
100*4882a593Smuzhiyun 	snd_pcm_uframes_t buf_sz;
101*4882a593Smuzhiyun 	snd_pcm_uframes_t period_sz;
102*4882a593Smuzhiyun 	snd_pcm_uframes_t hw_ptr;
103*4882a593Smuzhiyun 	snd_pcm_sframes_t hw_ptr_delta; /* play-ptr - cap-ptr */
104*4882a593Smuzhiyun 	unsigned long period_elapsed;
105*4882a593Smuzhiyun 	unsigned int frame_bytes;
106*4882a593Smuzhiyun 	unsigned int channels;
107*4882a593Smuzhiyun 	unsigned int buf_ofs;
108*4882a593Smuzhiyun 	int stream;
109*4882a593Smuzhiyun };
110*4882a593Smuzhiyun 
dlp_activate(struct dmaengine_dlp * dlp)111*4882a593Smuzhiyun static inline void dlp_activate(struct dmaengine_dlp *dlp)
112*4882a593Smuzhiyun {
113*4882a593Smuzhiyun 	spin_lock(&dlp->lock);
114*4882a593Smuzhiyun 	dlp->component.active++;
115*4882a593Smuzhiyun 	spin_unlock(&dlp->lock);
116*4882a593Smuzhiyun }
117*4882a593Smuzhiyun 
dlp_deactivate(struct dmaengine_dlp * dlp)118*4882a593Smuzhiyun static inline void dlp_deactivate(struct dmaengine_dlp *dlp)
119*4882a593Smuzhiyun {
120*4882a593Smuzhiyun 	spin_lock(&dlp->lock);
121*4882a593Smuzhiyun 	dlp->component.active--;
122*4882a593Smuzhiyun 	spin_unlock(&dlp->lock);
123*4882a593Smuzhiyun }
124*4882a593Smuzhiyun 
dlp_mode_channels_match(struct dmaengine_dlp * dlp,int ch,int * expected)125*4882a593Smuzhiyun static inline bool dlp_mode_channels_match(struct dmaengine_dlp *dlp,
126*4882a593Smuzhiyun 					   int ch, int *expected)
127*4882a593Smuzhiyun {
128*4882a593Smuzhiyun 	*expected = 0;
129*4882a593Smuzhiyun 
130*4882a593Smuzhiyun 	switch (dlp->mode) {
131*4882a593Smuzhiyun 	case DLP_MODE_DISABLED:
132*4882a593Smuzhiyun 		return true;
133*4882a593Smuzhiyun 	case DLP_MODE_2CH_1LP_1MIC:
134*4882a593Smuzhiyun 	case DLP_MODE_2CH_1MIC_1LP:
135*4882a593Smuzhiyun 	case DLP_MODE_2CH_1MIC_1LP_MIX:
136*4882a593Smuzhiyun 	case DLP_MODE_2CH_2LP:
137*4882a593Smuzhiyun 		*expected = 2;
138*4882a593Smuzhiyun 		return (ch == 2);
139*4882a593Smuzhiyun 	case DLP_MODE_4CH_2MIC_2LP:
140*4882a593Smuzhiyun 	case DLP_MODE_4CH_2MIC_1LP_MIX:
141*4882a593Smuzhiyun 	case DLP_MODE_4CH_4LP:
142*4882a593Smuzhiyun 		*expected = 4;
143*4882a593Smuzhiyun 		return (ch == 4);
144*4882a593Smuzhiyun 	case DLP_MODE_6CH_4MIC_2LP:
145*4882a593Smuzhiyun 	case DLP_MODE_6CH_4MIC_1LP_MIX:
146*4882a593Smuzhiyun 	case DLP_MODE_6CH_6LP:
147*4882a593Smuzhiyun 		*expected = 6;
148*4882a593Smuzhiyun 		return (ch == 6);
149*4882a593Smuzhiyun 	case DLP_MODE_8CH_6MIC_2LP:
150*4882a593Smuzhiyun 	case DLP_MODE_8CH_6MIC_1LP_MIX:
151*4882a593Smuzhiyun 	case DLP_MODE_8CH_8LP:
152*4882a593Smuzhiyun 		*expected = 8;
153*4882a593Smuzhiyun 		return (ch == 8);
154*4882a593Smuzhiyun 	case DLP_MODE_10CH_8MIC_2LP:
155*4882a593Smuzhiyun 	case DLP_MODE_10CH_8MIC_1LP_MIX:
156*4882a593Smuzhiyun 		*expected = 10;
157*4882a593Smuzhiyun 		return (ch == 10);
158*4882a593Smuzhiyun 	case DLP_MODE_16CH_8MIC_8LP:
159*4882a593Smuzhiyun 		*expected = 16;
160*4882a593Smuzhiyun 		return (ch == 16);
161*4882a593Smuzhiyun 	default:
162*4882a593Smuzhiyun 		return false;
163*4882a593Smuzhiyun 	}
164*4882a593Smuzhiyun }
165*4882a593Smuzhiyun 
dlp_channels_to_bytes(struct dmaengine_dlp_runtime_data * prtd,int channels)166*4882a593Smuzhiyun static inline ssize_t dlp_channels_to_bytes(struct dmaengine_dlp_runtime_data *prtd,
167*4882a593Smuzhiyun 					    int channels)
168*4882a593Smuzhiyun {
169*4882a593Smuzhiyun 	return (prtd->frame_bytes / prtd->channels) * channels;
170*4882a593Smuzhiyun }
171*4882a593Smuzhiyun 
dlp_frames_to_bytes(struct dmaengine_dlp_runtime_data * prtd,snd_pcm_sframes_t size)172*4882a593Smuzhiyun static inline ssize_t dlp_frames_to_bytes(struct dmaengine_dlp_runtime_data *prtd,
173*4882a593Smuzhiyun 					  snd_pcm_sframes_t size)
174*4882a593Smuzhiyun {
175*4882a593Smuzhiyun 	return size * prtd->frame_bytes;
176*4882a593Smuzhiyun }
177*4882a593Smuzhiyun 
dlp_bytes_to_frames(struct dmaengine_dlp_runtime_data * prtd,ssize_t size)178*4882a593Smuzhiyun static inline snd_pcm_sframes_t dlp_bytes_to_frames(struct dmaengine_dlp_runtime_data *prtd,
179*4882a593Smuzhiyun 						    ssize_t size)
180*4882a593Smuzhiyun {
181*4882a593Smuzhiyun 	return size / prtd->frame_bytes;
182*4882a593Smuzhiyun }
183*4882a593Smuzhiyun 
soc_component_to_dlp(struct snd_soc_component * p)184*4882a593Smuzhiyun static inline struct dmaengine_dlp *soc_component_to_dlp(struct snd_soc_component *p)
185*4882a593Smuzhiyun {
186*4882a593Smuzhiyun 	return container_of(p, struct dmaengine_dlp, component);
187*4882a593Smuzhiyun }
188*4882a593Smuzhiyun 
substream_to_prtd(const struct snd_pcm_substream * substream)189*4882a593Smuzhiyun static inline struct dmaengine_dlp_runtime_data *substream_to_prtd(
190*4882a593Smuzhiyun 	const struct snd_pcm_substream *substream)
191*4882a593Smuzhiyun {
192*4882a593Smuzhiyun 	if (!substream->runtime)
193*4882a593Smuzhiyun 		return NULL;
194*4882a593Smuzhiyun 
195*4882a593Smuzhiyun 	return substream->runtime->private_data;
196*4882a593Smuzhiyun }
197*4882a593Smuzhiyun 
snd_dmaengine_dlp_get_chan(struct snd_pcm_substream * substream)198*4882a593Smuzhiyun static struct dma_chan *snd_dmaengine_dlp_get_chan(struct snd_pcm_substream *substream)
199*4882a593Smuzhiyun {
200*4882a593Smuzhiyun 	struct dmaengine_dlp_runtime_data *prtd = substream_to_prtd(substream);
201*4882a593Smuzhiyun 
202*4882a593Smuzhiyun 	return prtd->dma_chan;
203*4882a593Smuzhiyun }
204*4882a593Smuzhiyun 
dmaengine_dma_dev(struct dmaengine_dlp * dlp,struct snd_pcm_substream * substream)205*4882a593Smuzhiyun static struct device *dmaengine_dma_dev(struct dmaengine_dlp *dlp,
206*4882a593Smuzhiyun 	struct snd_pcm_substream *substream)
207*4882a593Smuzhiyun {
208*4882a593Smuzhiyun 	if (!dlp->chan[substream->stream])
209*4882a593Smuzhiyun 		return NULL;
210*4882a593Smuzhiyun 
211*4882a593Smuzhiyun 	return dlp->chan[substream->stream]->device->dev;
212*4882a593Smuzhiyun }
213*4882a593Smuzhiyun 
dlp_get_offset_size(struct dmaengine_dlp_runtime_data * prtd,enum dlp_mode mode,int * ofs,int * size,bool * mix)214*4882a593Smuzhiyun static int dlp_get_offset_size(struct dmaengine_dlp_runtime_data *prtd,
215*4882a593Smuzhiyun 			       enum dlp_mode mode, int *ofs, int *size, bool *mix)
216*4882a593Smuzhiyun {
217*4882a593Smuzhiyun 	bool is_playback = prtd->stream == SNDRV_PCM_STREAM_PLAYBACK;
218*4882a593Smuzhiyun 	int ret = 0;
219*4882a593Smuzhiyun 
220*4882a593Smuzhiyun 	switch (mode) {
221*4882a593Smuzhiyun 	case DLP_MODE_2CH_1LP_1MIC:
222*4882a593Smuzhiyun 		*ofs = 0;
223*4882a593Smuzhiyun 		*size = dlp_channels_to_bytes(prtd, 1);
224*4882a593Smuzhiyun 		break;
225*4882a593Smuzhiyun 	case DLP_MODE_2CH_1MIC_1LP:
226*4882a593Smuzhiyun 		*ofs = dlp_channels_to_bytes(prtd, 1);
227*4882a593Smuzhiyun 		*size = dlp_channels_to_bytes(prtd, 1);
228*4882a593Smuzhiyun 		break;
229*4882a593Smuzhiyun 	case DLP_MODE_2CH_1MIC_1LP_MIX:
230*4882a593Smuzhiyun 		if (is_playback) {
231*4882a593Smuzhiyun 			*ofs = 0;
232*4882a593Smuzhiyun 			*size = dlp_frames_to_bytes(prtd, 1);
233*4882a593Smuzhiyun 			if (mix)
234*4882a593Smuzhiyun 				*mix = true;
235*4882a593Smuzhiyun 		} else {
236*4882a593Smuzhiyun 			*ofs = dlp_channels_to_bytes(prtd, 1);
237*4882a593Smuzhiyun 			*size = dlp_channels_to_bytes(prtd, 1);
238*4882a593Smuzhiyun 		}
239*4882a593Smuzhiyun 		break;
240*4882a593Smuzhiyun 	case DLP_MODE_2CH_2LP:
241*4882a593Smuzhiyun 		*ofs = 0;
242*4882a593Smuzhiyun 		*size = dlp_channels_to_bytes(prtd, 2);
243*4882a593Smuzhiyun 		break;
244*4882a593Smuzhiyun 	case DLP_MODE_4CH_2MIC_2LP:
245*4882a593Smuzhiyun 		if (is_playback) {
246*4882a593Smuzhiyun 			*ofs = 0;
247*4882a593Smuzhiyun 			*size = dlp_channels_to_bytes(prtd, 2);
248*4882a593Smuzhiyun 		} else {
249*4882a593Smuzhiyun 			*ofs = dlp_channels_to_bytes(prtd, 2);
250*4882a593Smuzhiyun 			*size = dlp_channels_to_bytes(prtd, 2);
251*4882a593Smuzhiyun 		}
252*4882a593Smuzhiyun 		break;
253*4882a593Smuzhiyun 	case DLP_MODE_4CH_2MIC_1LP_MIX:
254*4882a593Smuzhiyun 		if (is_playback) {
255*4882a593Smuzhiyun 			*ofs = 0;
256*4882a593Smuzhiyun 			*size = dlp_frames_to_bytes(prtd, 1);
257*4882a593Smuzhiyun 			if (mix)
258*4882a593Smuzhiyun 				*mix = true;
259*4882a593Smuzhiyun 		} else {
260*4882a593Smuzhiyun 			*ofs = dlp_channels_to_bytes(prtd, 2);
261*4882a593Smuzhiyun 			*size = dlp_channels_to_bytes(prtd, 1);
262*4882a593Smuzhiyun 		}
263*4882a593Smuzhiyun 		break;
264*4882a593Smuzhiyun 	case DLP_MODE_4CH_4LP:
265*4882a593Smuzhiyun 		*ofs = 0;
266*4882a593Smuzhiyun 		*size = dlp_channels_to_bytes(prtd, 4);
267*4882a593Smuzhiyun 		break;
268*4882a593Smuzhiyun 	case DLP_MODE_6CH_4MIC_2LP:
269*4882a593Smuzhiyun 		if (is_playback) {
270*4882a593Smuzhiyun 			*ofs = 0;
271*4882a593Smuzhiyun 			*size = dlp_channels_to_bytes(prtd, 2);
272*4882a593Smuzhiyun 		} else {
273*4882a593Smuzhiyun 			*ofs = dlp_channels_to_bytes(prtd, 4);
274*4882a593Smuzhiyun 			*size = dlp_channels_to_bytes(prtd, 2);
275*4882a593Smuzhiyun 		}
276*4882a593Smuzhiyun 		break;
277*4882a593Smuzhiyun 	case DLP_MODE_6CH_4MIC_1LP_MIX:
278*4882a593Smuzhiyun 		if (is_playback) {
279*4882a593Smuzhiyun 			*ofs = 0;
280*4882a593Smuzhiyun 			*size = dlp_frames_to_bytes(prtd, 1);
281*4882a593Smuzhiyun 			if (mix)
282*4882a593Smuzhiyun 				*mix = true;
283*4882a593Smuzhiyun 		} else {
284*4882a593Smuzhiyun 			*ofs = dlp_channels_to_bytes(prtd, 4);
285*4882a593Smuzhiyun 			*size = dlp_channels_to_bytes(prtd, 1);
286*4882a593Smuzhiyun 		}
287*4882a593Smuzhiyun 		break;
288*4882a593Smuzhiyun 	case DLP_MODE_6CH_6LP:
289*4882a593Smuzhiyun 		*ofs = 0;
290*4882a593Smuzhiyun 		*size = dlp_channels_to_bytes(prtd, 6);
291*4882a593Smuzhiyun 		break;
292*4882a593Smuzhiyun 	case DLP_MODE_8CH_6MIC_2LP:
293*4882a593Smuzhiyun 		if (is_playback) {
294*4882a593Smuzhiyun 			*ofs = 0;
295*4882a593Smuzhiyun 			*size = dlp_channels_to_bytes(prtd, 2);
296*4882a593Smuzhiyun 		} else {
297*4882a593Smuzhiyun 			*ofs = dlp_channels_to_bytes(prtd, 6);
298*4882a593Smuzhiyun 			*size = dlp_channels_to_bytes(prtd, 2);
299*4882a593Smuzhiyun 		}
300*4882a593Smuzhiyun 		break;
301*4882a593Smuzhiyun 	case DLP_MODE_8CH_6MIC_1LP_MIX:
302*4882a593Smuzhiyun 		if (is_playback) {
303*4882a593Smuzhiyun 			*ofs = 0;
304*4882a593Smuzhiyun 			*size = dlp_frames_to_bytes(prtd, 1);
305*4882a593Smuzhiyun 			if (mix)
306*4882a593Smuzhiyun 				*mix = true;
307*4882a593Smuzhiyun 		} else {
308*4882a593Smuzhiyun 			*ofs = dlp_channels_to_bytes(prtd, 6);
309*4882a593Smuzhiyun 			*size = dlp_channels_to_bytes(prtd, 1);
310*4882a593Smuzhiyun 		}
311*4882a593Smuzhiyun 		break;
312*4882a593Smuzhiyun 	case DLP_MODE_8CH_8LP:
313*4882a593Smuzhiyun 		*ofs = 0;
314*4882a593Smuzhiyun 		*size = dlp_channels_to_bytes(prtd, 8);
315*4882a593Smuzhiyun 		break;
316*4882a593Smuzhiyun 	case DLP_MODE_10CH_8MIC_2LP:
317*4882a593Smuzhiyun 		if (is_playback) {
318*4882a593Smuzhiyun 			*ofs = 0;
319*4882a593Smuzhiyun 			*size = dlp_channels_to_bytes(prtd, 2);
320*4882a593Smuzhiyun 		} else {
321*4882a593Smuzhiyun 			*ofs = dlp_channels_to_bytes(prtd, 8);
322*4882a593Smuzhiyun 			*size = dlp_channels_to_bytes(prtd, 2);
323*4882a593Smuzhiyun 		}
324*4882a593Smuzhiyun 		break;
325*4882a593Smuzhiyun 	case DLP_MODE_10CH_8MIC_1LP_MIX:
326*4882a593Smuzhiyun 		if (is_playback) {
327*4882a593Smuzhiyun 			*ofs = 0;
328*4882a593Smuzhiyun 			*size = dlp_frames_to_bytes(prtd, 1);
329*4882a593Smuzhiyun 			if (mix)
330*4882a593Smuzhiyun 				*mix = true;
331*4882a593Smuzhiyun 		} else {
332*4882a593Smuzhiyun 			*ofs = dlp_channels_to_bytes(prtd, 8);
333*4882a593Smuzhiyun 			*size = dlp_channels_to_bytes(prtd, 1);
334*4882a593Smuzhiyun 		}
335*4882a593Smuzhiyun 		break;
336*4882a593Smuzhiyun 	case DLP_MODE_16CH_8MIC_8LP:
337*4882a593Smuzhiyun 		if (is_playback) {
338*4882a593Smuzhiyun 			*ofs = 0;
339*4882a593Smuzhiyun 			*size = dlp_channels_to_bytes(prtd, 8);
340*4882a593Smuzhiyun 		} else {
341*4882a593Smuzhiyun 			*ofs = dlp_channels_to_bytes(prtd, 8);
342*4882a593Smuzhiyun 			*size = dlp_channels_to_bytes(prtd, 8);
343*4882a593Smuzhiyun 		}
344*4882a593Smuzhiyun 		break;
345*4882a593Smuzhiyun 	default:
346*4882a593Smuzhiyun 		*ofs = 0;
347*4882a593Smuzhiyun 		*size = 0;
348*4882a593Smuzhiyun 		if (mix)
349*4882a593Smuzhiyun 			*mix = false;
350*4882a593Smuzhiyun 		ret = -EINVAL;
351*4882a593Smuzhiyun 	}
352*4882a593Smuzhiyun 
353*4882a593Smuzhiyun 	return ret;
354*4882a593Smuzhiyun }
355*4882a593Smuzhiyun 
dlp_mix_frame_buffer(struct dmaengine_dlp_runtime_data * prtd,void * buf)356*4882a593Smuzhiyun static int dlp_mix_frame_buffer(struct dmaengine_dlp_runtime_data *prtd, void *buf)
357*4882a593Smuzhiyun {
358*4882a593Smuzhiyun 	int sample_bytes = dlp_channels_to_bytes(prtd, 1);
359*4882a593Smuzhiyun 	int16_t *p16 = (int16_t *)buf, v16 = 0;
360*4882a593Smuzhiyun 	int32_t *p32 = (int32_t *)buf, v32 = 0;
361*4882a593Smuzhiyun 	int i = 0;
362*4882a593Smuzhiyun 
363*4882a593Smuzhiyun 	switch (sample_bytes) {
364*4882a593Smuzhiyun 	case 2:
365*4882a593Smuzhiyun 		for (i = 0; i < prtd->channels; i++)
366*4882a593Smuzhiyun 			v16 += (p16[i] / prtd->channels);
367*4882a593Smuzhiyun 		p16[0] = v16;
368*4882a593Smuzhiyun 		break;
369*4882a593Smuzhiyun 	case 4:
370*4882a593Smuzhiyun 		for (i = 0; i < prtd->channels; i++)
371*4882a593Smuzhiyun 			v32 += (p32[i] / prtd->channels);
372*4882a593Smuzhiyun 		p32[0] = v32;
373*4882a593Smuzhiyun 		break;
374*4882a593Smuzhiyun 	default:
375*4882a593Smuzhiyun 		return -EINVAL;
376*4882a593Smuzhiyun 	}
377*4882a593Smuzhiyun 
378*4882a593Smuzhiyun 	return 0;
379*4882a593Smuzhiyun }
380*4882a593Smuzhiyun 
dmaengine_dlp_hw_params(struct snd_soc_component * component,struct snd_pcm_substream * substream,struct snd_pcm_hw_params * params)381*4882a593Smuzhiyun static int dmaengine_dlp_hw_params(struct snd_soc_component *component,
382*4882a593Smuzhiyun 				   struct snd_pcm_substream *substream,
383*4882a593Smuzhiyun 				   struct snd_pcm_hw_params *params)
384*4882a593Smuzhiyun {
385*4882a593Smuzhiyun 	struct dmaengine_dlp *dlp = soc_component_to_dlp(component);
386*4882a593Smuzhiyun 	struct dmaengine_dlp_runtime_data *prtd = substream_to_prtd(substream);
387*4882a593Smuzhiyun 	struct dma_chan *chan = snd_dmaengine_dlp_get_chan(substream);
388*4882a593Smuzhiyun 	struct dma_slave_config slave_config;
389*4882a593Smuzhiyun 	bool is_playback = substream->stream == SNDRV_PCM_STREAM_PLAYBACK;
390*4882a593Smuzhiyun 	int ch_req = params_channels(params), ch_exp = 0;
391*4882a593Smuzhiyun 	int ret;
392*4882a593Smuzhiyun 
393*4882a593Smuzhiyun 	/* mode should match to channels */
394*4882a593Smuzhiyun 	if (!is_playback && !dlp_mode_channels_match(dlp, ch_req, &ch_exp)) {
395*4882a593Smuzhiyun 		dev_err(dlp->dev,
396*4882a593Smuzhiyun 			"capture %d ch, expected: %d ch for loopback mode-%d\n",
397*4882a593Smuzhiyun 			ch_req, ch_exp, dlp->mode);
398*4882a593Smuzhiyun 		return -EINVAL;
399*4882a593Smuzhiyun 	}
400*4882a593Smuzhiyun 
401*4882a593Smuzhiyun 	memset(&slave_config, 0, sizeof(slave_config));
402*4882a593Smuzhiyun 
403*4882a593Smuzhiyun 	ret = snd_dmaengine_pcm_prepare_slave_config(substream, params, &slave_config);
404*4882a593Smuzhiyun 	if (ret)
405*4882a593Smuzhiyun 		return ret;
406*4882a593Smuzhiyun 
407*4882a593Smuzhiyun 	ret = dmaengine_slave_config(chan, &slave_config);
408*4882a593Smuzhiyun 	if (ret)
409*4882a593Smuzhiyun 		return ret;
410*4882a593Smuzhiyun 
411*4882a593Smuzhiyun 	prtd->frame_bytes = snd_pcm_format_size(params_format(params),
412*4882a593Smuzhiyun 						params_channels(params));
413*4882a593Smuzhiyun 	prtd->period_sz = params_period_size(params);
414*4882a593Smuzhiyun 	prtd->buf_sz = params_buffer_size(params);
415*4882a593Smuzhiyun 	prtd->channels = params_channels(params);
416*4882a593Smuzhiyun 
417*4882a593Smuzhiyun 	if (is_playback)
418*4882a593Smuzhiyun 		prtd->buf_sz *= PBUF_CNT;
419*4882a593Smuzhiyun 
420*4882a593Smuzhiyun 	return 0;
421*4882a593Smuzhiyun }
422*4882a593Smuzhiyun 
423*4882a593Smuzhiyun static int
dmaengine_pcm_set_runtime_hwparams(struct snd_soc_component * component,struct snd_pcm_substream * substream)424*4882a593Smuzhiyun dmaengine_pcm_set_runtime_hwparams(struct snd_soc_component *component,
425*4882a593Smuzhiyun 				   struct snd_pcm_substream *substream)
426*4882a593Smuzhiyun {
427*4882a593Smuzhiyun 	struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
428*4882a593Smuzhiyun 	struct dmaengine_dlp *dlp = soc_component_to_dlp(component);
429*4882a593Smuzhiyun 	struct device *dma_dev = dmaengine_dma_dev(dlp, substream);
430*4882a593Smuzhiyun 	struct dma_chan *chan = dlp->chan[substream->stream];
431*4882a593Smuzhiyun 	struct snd_dmaengine_dai_dma_data *dma_data;
432*4882a593Smuzhiyun 	struct snd_pcm_hardware hw;
433*4882a593Smuzhiyun 
434*4882a593Smuzhiyun 	if (rtd->num_cpus > 1) {
435*4882a593Smuzhiyun 		dev_err(rtd->dev,
436*4882a593Smuzhiyun 			"%s doesn't support Multi CPU yet\n", __func__);
437*4882a593Smuzhiyun 		return -EINVAL;
438*4882a593Smuzhiyun 	}
439*4882a593Smuzhiyun 
440*4882a593Smuzhiyun 	dma_data = snd_soc_dai_get_dma_data(asoc_rtd_to_cpu(rtd, 0), substream);
441*4882a593Smuzhiyun 
442*4882a593Smuzhiyun 	memset(&hw, 0, sizeof(hw));
443*4882a593Smuzhiyun 	hw.info = SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_MMAP_VALID |
444*4882a593Smuzhiyun 			SNDRV_PCM_INFO_INTERLEAVED;
445*4882a593Smuzhiyun 	hw.periods_min = 2;
446*4882a593Smuzhiyun 	hw.periods_max = UINT_MAX;
447*4882a593Smuzhiyun 	hw.period_bytes_min = 256;
448*4882a593Smuzhiyun 	hw.period_bytes_max = dma_get_max_seg_size(dma_dev);
449*4882a593Smuzhiyun 	hw.buffer_bytes_max = SIZE_MAX;
450*4882a593Smuzhiyun 	hw.fifo_size = dma_data->fifo_size;
451*4882a593Smuzhiyun 
452*4882a593Smuzhiyun 	/**
453*4882a593Smuzhiyun 	 * FIXME: Remove the return value check to align with the code
454*4882a593Smuzhiyun 	 * before adding snd_dmaengine_pcm_refine_runtime_hwparams
455*4882a593Smuzhiyun 	 * function.
456*4882a593Smuzhiyun 	 */
457*4882a593Smuzhiyun 	snd_dmaengine_pcm_refine_runtime_hwparams(substream,
458*4882a593Smuzhiyun 						  dma_data,
459*4882a593Smuzhiyun 						  &hw,
460*4882a593Smuzhiyun 						  chan);
461*4882a593Smuzhiyun 
462*4882a593Smuzhiyun 	return snd_soc_set_runtime_hwparams(substream, &hw);
463*4882a593Smuzhiyun }
464*4882a593Smuzhiyun 
dmaengine_dlp_open(struct snd_soc_component * component,struct snd_pcm_substream * substream)465*4882a593Smuzhiyun static int dmaengine_dlp_open(struct snd_soc_component *component,
466*4882a593Smuzhiyun 			      struct snd_pcm_substream *substream)
467*4882a593Smuzhiyun {
468*4882a593Smuzhiyun 	struct dmaengine_dlp *dlp = soc_component_to_dlp(component);
469*4882a593Smuzhiyun 	struct dma_chan *chan = dlp->chan[substream->stream];
470*4882a593Smuzhiyun 	struct dmaengine_dlp_runtime_data *prtd;
471*4882a593Smuzhiyun 	int ret;
472*4882a593Smuzhiyun 
473*4882a593Smuzhiyun 	if (!chan)
474*4882a593Smuzhiyun 		return -ENXIO;
475*4882a593Smuzhiyun 
476*4882a593Smuzhiyun 	ret = dmaengine_pcm_set_runtime_hwparams(component, substream);
477*4882a593Smuzhiyun 	if (ret)
478*4882a593Smuzhiyun 		return ret;
479*4882a593Smuzhiyun 
480*4882a593Smuzhiyun 	ret = snd_pcm_hw_constraint_integer(substream->runtime,
481*4882a593Smuzhiyun 					    SNDRV_PCM_HW_PARAM_PERIODS);
482*4882a593Smuzhiyun 	if (ret < 0)
483*4882a593Smuzhiyun 		return ret;
484*4882a593Smuzhiyun 
485*4882a593Smuzhiyun 	prtd = kzalloc(sizeof(*prtd), GFP_KERNEL);
486*4882a593Smuzhiyun 	if (!prtd)
487*4882a593Smuzhiyun 		return -ENOMEM;
488*4882a593Smuzhiyun 
489*4882a593Smuzhiyun 	dlp_info("PRTD-CREATE: 0x%px (%s)\n",
490*4882a593Smuzhiyun 		 prtd, substream->stream ? "C" : "P");
491*4882a593Smuzhiyun 
492*4882a593Smuzhiyun 	kref_init(&prtd->refcount);
493*4882a593Smuzhiyun 	prtd->parent = dlp;
494*4882a593Smuzhiyun 	prtd->stream = substream->stream;
495*4882a593Smuzhiyun 	prtd->dma_chan = chan;
496*4882a593Smuzhiyun 
497*4882a593Smuzhiyun 	substream->runtime->private_data = prtd;
498*4882a593Smuzhiyun 
499*4882a593Smuzhiyun 	dlp_activate(dlp);
500*4882a593Smuzhiyun 
501*4882a593Smuzhiyun 	return 0;
502*4882a593Smuzhiyun }
503*4882a593Smuzhiyun 
dmaengine_free_prtd(struct kref * ref)504*4882a593Smuzhiyun static void dmaengine_free_prtd(struct kref *ref)
505*4882a593Smuzhiyun {
506*4882a593Smuzhiyun 	struct dmaengine_dlp_runtime_data *prtd =
507*4882a593Smuzhiyun 		container_of(ref, struct dmaengine_dlp_runtime_data, refcount);
508*4882a593Smuzhiyun 
509*4882a593Smuzhiyun 	dlp_info("PRTD-FREE: 0x%px\n", prtd);
510*4882a593Smuzhiyun 
511*4882a593Smuzhiyun 	kfree(prtd->buf);
512*4882a593Smuzhiyun 	kfree(prtd);
513*4882a593Smuzhiyun }
514*4882a593Smuzhiyun 
free_ref_list(struct snd_soc_component * component)515*4882a593Smuzhiyun static void free_ref_list(struct snd_soc_component *component)
516*4882a593Smuzhiyun {
517*4882a593Smuzhiyun 	struct dmaengine_dlp *dlp = soc_component_to_dlp(component);
518*4882a593Smuzhiyun 	struct dmaengine_dlp_runtime_data *prtd, *_pt;
519*4882a593Smuzhiyun 
520*4882a593Smuzhiyun 	spin_lock(&dlp->lock);
521*4882a593Smuzhiyun 	list_for_each_entry_safe(prtd, _pt, &dlp->ref_list, node) {
522*4882a593Smuzhiyun 		list_del(&prtd->node);
523*4882a593Smuzhiyun 		kref_put(&prtd->refcount, dmaengine_free_prtd);
524*4882a593Smuzhiyun 	}
525*4882a593Smuzhiyun 	spin_unlock(&dlp->lock);
526*4882a593Smuzhiyun }
527*4882a593Smuzhiyun 
dmaengine_dlp_close(struct snd_soc_component * component,struct snd_pcm_substream * substream)528*4882a593Smuzhiyun static int dmaengine_dlp_close(struct snd_soc_component *component,
529*4882a593Smuzhiyun 			       struct snd_pcm_substream *substream)
530*4882a593Smuzhiyun {
531*4882a593Smuzhiyun 	struct dmaengine_dlp *dlp = soc_component_to_dlp(component);
532*4882a593Smuzhiyun 	struct dmaengine_dlp_runtime_data *prtd = substream_to_prtd(substream);
533*4882a593Smuzhiyun 
534*4882a593Smuzhiyun 	dmaengine_synchronize(prtd->dma_chan);
535*4882a593Smuzhiyun 
536*4882a593Smuzhiyun 	/*
537*4882a593Smuzhiyun 	 * kref put should be after hw_ptr updated when stop,
538*4882a593Smuzhiyun 	 * ops->trigger: SNDRV_PCM_TRIGGER_STOP -> ops->close
539*4882a593Smuzhiyun 	 * obviously, it is!
540*4882a593Smuzhiyun 	 */
541*4882a593Smuzhiyun 	kref_put(&prtd->refcount, dmaengine_free_prtd);
542*4882a593Smuzhiyun 
543*4882a593Smuzhiyun 	dlp_deactivate(dlp);
544*4882a593Smuzhiyun 
545*4882a593Smuzhiyun 	return 0;
546*4882a593Smuzhiyun }
547*4882a593Smuzhiyun 
dmaengine_dlp_pointer(struct snd_soc_component * component,struct snd_pcm_substream * substream)548*4882a593Smuzhiyun static snd_pcm_uframes_t dmaengine_dlp_pointer(
549*4882a593Smuzhiyun 	struct snd_soc_component *component,
550*4882a593Smuzhiyun 	struct snd_pcm_substream *substream)
551*4882a593Smuzhiyun {
552*4882a593Smuzhiyun 	struct dmaengine_dlp_runtime_data *prtd = substream_to_prtd(substream);
553*4882a593Smuzhiyun 	struct dma_tx_state state;
554*4882a593Smuzhiyun 	unsigned int buf_size;
555*4882a593Smuzhiyun 	unsigned int pos = 0;
556*4882a593Smuzhiyun 
557*4882a593Smuzhiyun 	dmaengine_tx_status(prtd->dma_chan, prtd->cookie, &state);
558*4882a593Smuzhiyun 	buf_size = snd_pcm_lib_buffer_bytes(substream);
559*4882a593Smuzhiyun 	if (state.residue > 0 && state.residue <= buf_size)
560*4882a593Smuzhiyun 		pos = buf_size - state.residue;
561*4882a593Smuzhiyun 
562*4882a593Smuzhiyun 	return dlp_bytes_to_frames(prtd, pos);
563*4882a593Smuzhiyun }
564*4882a593Smuzhiyun 
dmaengine_dlp_dma_complete(void * arg)565*4882a593Smuzhiyun static void dmaengine_dlp_dma_complete(void *arg)
566*4882a593Smuzhiyun {
567*4882a593Smuzhiyun 	struct snd_pcm_substream *substream = arg;
568*4882a593Smuzhiyun 	struct dmaengine_dlp_runtime_data *prtd = substream_to_prtd(substream);
569*4882a593Smuzhiyun 	struct dmaengine_dlp *dlp = prtd->parent;
570*4882a593Smuzhiyun 
571*4882a593Smuzhiyun 	if (!substream->runtime)
572*4882a593Smuzhiyun 		return;
573*4882a593Smuzhiyun 
574*4882a593Smuzhiyun 	spin_lock(&dlp->lock);
575*4882a593Smuzhiyun 	prtd->period_elapsed++;
576*4882a593Smuzhiyun 	prtd->hw_ptr = prtd->period_elapsed * prtd->period_sz;
577*4882a593Smuzhiyun 	spin_unlock(&dlp->lock);
578*4882a593Smuzhiyun 	snd_pcm_period_elapsed(substream);
579*4882a593Smuzhiyun }
580*4882a593Smuzhiyun 
dmaengine_dlp_prepare_and_submit(struct snd_pcm_substream * substream)581*4882a593Smuzhiyun static int dmaengine_dlp_prepare_and_submit(struct snd_pcm_substream *substream)
582*4882a593Smuzhiyun {
583*4882a593Smuzhiyun 	struct dmaengine_dlp_runtime_data *prtd = substream_to_prtd(substream);
584*4882a593Smuzhiyun 	struct dma_chan *chan = prtd->dma_chan;
585*4882a593Smuzhiyun 	struct dma_async_tx_descriptor *desc;
586*4882a593Smuzhiyun 	enum dma_transfer_direction direction;
587*4882a593Smuzhiyun 	unsigned long flags = DMA_CTRL_ACK;
588*4882a593Smuzhiyun 
589*4882a593Smuzhiyun 	direction = snd_pcm_substream_to_dma_direction(substream);
590*4882a593Smuzhiyun 
591*4882a593Smuzhiyun 	if (!substream->runtime->no_period_wakeup)
592*4882a593Smuzhiyun 		flags |= DMA_PREP_INTERRUPT;
593*4882a593Smuzhiyun 
594*4882a593Smuzhiyun 	desc = dmaengine_prep_dma_cyclic(chan,
595*4882a593Smuzhiyun 		substream->runtime->dma_addr,
596*4882a593Smuzhiyun 		snd_pcm_lib_buffer_bytes(substream),
597*4882a593Smuzhiyun 		snd_pcm_lib_period_bytes(substream), direction, flags);
598*4882a593Smuzhiyun 
599*4882a593Smuzhiyun 	if (!desc)
600*4882a593Smuzhiyun 		return -ENOMEM;
601*4882a593Smuzhiyun 
602*4882a593Smuzhiyun 	desc->callback = dmaengine_dlp_dma_complete;
603*4882a593Smuzhiyun 	desc->callback_param = substream;
604*4882a593Smuzhiyun 	prtd->cookie = dmaengine_submit(desc);
605*4882a593Smuzhiyun 
606*4882a593Smuzhiyun 	return 0;
607*4882a593Smuzhiyun }
608*4882a593Smuzhiyun 
dmaengine_dlp_setup(struct snd_soc_component * component,struct snd_pcm_substream * substream)609*4882a593Smuzhiyun static int dmaengine_dlp_setup(struct snd_soc_component *component,
610*4882a593Smuzhiyun 			       struct snd_pcm_substream *substream)
611*4882a593Smuzhiyun {
612*4882a593Smuzhiyun 	struct dmaengine_dlp *dlp = soc_component_to_dlp(component);
613*4882a593Smuzhiyun 	int bstream = SNDRV_PCM_STREAM_LAST - substream->stream;
614*4882a593Smuzhiyun 	struct snd_pcm_str *bro = &substream->pcm->streams[bstream];
615*4882a593Smuzhiyun 	struct snd_pcm_substream *bsubstream = bro->substream;
616*4882a593Smuzhiyun 	struct dmaengine_dlp_runtime_data *prtd = substream_to_prtd(substream);
617*4882a593Smuzhiyun 	struct dmaengine_dlp_runtime_data *brtd = substream_to_prtd(bsubstream);
618*4882a593Smuzhiyun 	struct dmaengine_dlp_runtime_data *pref = dlp->pref;
619*4882a593Smuzhiyun 	bool is_playback = substream->stream == SNDRV_PCM_STREAM_PLAYBACK;
620*4882a593Smuzhiyun 	snd_pcm_uframes_t a = 0, b = 0, fifo_a = 0, fifo_b = 0;
621*4882a593Smuzhiyun 	snd_pcm_sframes_t delta = 0;
622*4882a593Smuzhiyun 
623*4882a593Smuzhiyun 	if (dlp->mode == DLP_MODE_DISABLED)
624*4882a593Smuzhiyun 		return -EINVAL;
625*4882a593Smuzhiyun 
626*4882a593Smuzhiyun 	fifo_a = dlp->config->get_fifo_count(dlp->dev, substream->stream);
627*4882a593Smuzhiyun 	a = dmaengine_dlp_pointer(component, substream);
628*4882a593Smuzhiyun 
629*4882a593Smuzhiyun 	if (bsubstream->runtime && snd_pcm_running(bsubstream)) {
630*4882a593Smuzhiyun 		fifo_b = dlp->config->get_fifo_count(dlp->dev, bstream);
631*4882a593Smuzhiyun 		b = dmaengine_dlp_pointer(component, bsubstream);
632*4882a593Smuzhiyun 
633*4882a593Smuzhiyun 		spin_lock(&dlp->lock);
634*4882a593Smuzhiyun 		if (!pref) {
635*4882a593Smuzhiyun 			spin_unlock(&dlp->lock);
636*4882a593Smuzhiyun 			return -EINVAL;
637*4882a593Smuzhiyun 		}
638*4882a593Smuzhiyun 
639*4882a593Smuzhiyun 		a = (prtd->period_elapsed * prtd->period_sz) + (a % prtd->period_sz);
640*4882a593Smuzhiyun 		b = (brtd->period_elapsed * brtd->period_sz) + (b % brtd->period_sz);
641*4882a593Smuzhiyun 
642*4882a593Smuzhiyun 		fifo_a = dlp_bytes_to_frames(prtd, fifo_a * 4);
643*4882a593Smuzhiyun 		fifo_b = dlp_bytes_to_frames(brtd, fifo_b * 4);
644*4882a593Smuzhiyun 
645*4882a593Smuzhiyun 		delta = is_playback ? (a - fifo_a) - (b + fifo_b) : (b - fifo_b) - (a + fifo_a);
646*4882a593Smuzhiyun 
647*4882a593Smuzhiyun 		pref->hw_ptr_delta = delta;
648*4882a593Smuzhiyun 		kref_get(&pref->refcount);
649*4882a593Smuzhiyun 		/* push valid playback into ref list */
650*4882a593Smuzhiyun 		list_add_tail(&pref->node, &dlp->ref_list);
651*4882a593Smuzhiyun 
652*4882a593Smuzhiyun 		spin_unlock(&dlp->lock);
653*4882a593Smuzhiyun 	}
654*4882a593Smuzhiyun 
655*4882a593Smuzhiyun 	if (is_playback)
656*4882a593Smuzhiyun 		dlp_info("START-P: DMA-P: %lu, DMA-C: %lu, FIFO-P: %lu, FIFO-C: %lu, DELTA: %ld\n",
657*4882a593Smuzhiyun 			 a, b, fifo_a, fifo_b, delta);
658*4882a593Smuzhiyun 	else
659*4882a593Smuzhiyun 		dlp_info("START-C: DMA-P: %lu, DMA-C: %lu, FIFO-P: %lu, FIFO-C: %lu, DELTA: %ld\n",
660*4882a593Smuzhiyun 			 b, a, fifo_b, fifo_a, delta);
661*4882a593Smuzhiyun 
662*4882a593Smuzhiyun 	return 0;
663*4882a593Smuzhiyun }
664*4882a593Smuzhiyun 
dmaengine_dlp_release(struct snd_soc_component * component,struct snd_pcm_substream * substream)665*4882a593Smuzhiyun static void dmaengine_dlp_release(struct snd_soc_component *component,
666*4882a593Smuzhiyun 				  struct snd_pcm_substream *substream)
667*4882a593Smuzhiyun {
668*4882a593Smuzhiyun 	struct dmaengine_dlp *dlp = soc_component_to_dlp(component);
669*4882a593Smuzhiyun 	struct dmaengine_dlp_runtime_data *prtd = substream_to_prtd(substream);
670*4882a593Smuzhiyun 	struct dmaengine_dlp_runtime_data *pref = dlp->pref;
671*4882a593Smuzhiyun 	struct snd_pcm_runtime *runtime = substream->runtime;
672*4882a593Smuzhiyun 	snd_pcm_uframes_t appl_ptr, hw_ptr;
673*4882a593Smuzhiyun 
674*4882a593Smuzhiyun 	if (dlp->mode == DLP_MODE_DISABLED)
675*4882a593Smuzhiyun 		return;
676*4882a593Smuzhiyun 
677*4882a593Smuzhiyun 	/* any data in FIFOs will be gone ,so don't care */
678*4882a593Smuzhiyun 	appl_ptr = READ_ONCE(runtime->control->appl_ptr);
679*4882a593Smuzhiyun 	hw_ptr = dmaengine_dlp_pointer(component, substream);
680*4882a593Smuzhiyun 	spin_lock(&dlp->lock);
681*4882a593Smuzhiyun 	hw_ptr = (prtd->period_elapsed * prtd->period_sz) + (hw_ptr % prtd->period_sz);
682*4882a593Smuzhiyun 	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
683*4882a593Smuzhiyun 		pref->hw_ptr = min(hw_ptr, appl_ptr);
684*4882a593Smuzhiyun 	prtd->period_elapsed = 0;
685*4882a593Smuzhiyun 	prtd->hw_ptr = 0;
686*4882a593Smuzhiyun 	spin_unlock(&dlp->lock);
687*4882a593Smuzhiyun 
688*4882a593Smuzhiyun 	/*
689*4882a593Smuzhiyun 	 * playback:
690*4882a593Smuzhiyun 	 *
691*4882a593Smuzhiyun 	 * snd_pcm_drop:  hw_ptr will be smaller than appl_ptr
692*4882a593Smuzhiyun 	 * snd_pcm_drain, hw_ptr will be equal to appl_ptr
693*4882a593Smuzhiyun 	 *
694*4882a593Smuzhiyun 	 * anyway, we should use the smaller one, obviously, it's hw_ptr.
695*4882a593Smuzhiyun 	 */
696*4882a593Smuzhiyun 	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
697*4882a593Smuzhiyun 		spin_lock(&dlp->pref_lock);
698*4882a593Smuzhiyun 		kref_put(&pref->refcount, dmaengine_free_prtd);
699*4882a593Smuzhiyun 		dlp->pref = NULL;
700*4882a593Smuzhiyun 		spin_unlock(&dlp->pref_lock);
701*4882a593Smuzhiyun 		dlp_info("STOP-P: applptr: %lu, hwptr: %lu\n", appl_ptr, hw_ptr);
702*4882a593Smuzhiyun 	} else {
703*4882a593Smuzhiyun 		/* free residue playback ref list for capture when stop */
704*4882a593Smuzhiyun 		free_ref_list(component);
705*4882a593Smuzhiyun 		dlp_info("STOP-C: applptr: %lu, hwptr: %lu\n", appl_ptr, hw_ptr);
706*4882a593Smuzhiyun 	}
707*4882a593Smuzhiyun }
708*4882a593Smuzhiyun 
dmaengine_dlp_trigger(struct snd_soc_component * component,struct snd_pcm_substream * substream,int cmd)709*4882a593Smuzhiyun static int dmaengine_dlp_trigger(struct snd_soc_component *component,
710*4882a593Smuzhiyun 				 struct snd_pcm_substream *substream, int cmd)
711*4882a593Smuzhiyun {
712*4882a593Smuzhiyun 	struct dmaengine_dlp_runtime_data *prtd = substream_to_prtd(substream);
713*4882a593Smuzhiyun 	struct snd_pcm_runtime *runtime = substream->runtime;
714*4882a593Smuzhiyun 	int ret;
715*4882a593Smuzhiyun 
716*4882a593Smuzhiyun 	switch (cmd) {
717*4882a593Smuzhiyun 	case SNDRV_PCM_TRIGGER_START:
718*4882a593Smuzhiyun 		ret = dmaengine_dlp_prepare_and_submit(substream);
719*4882a593Smuzhiyun 		if (ret)
720*4882a593Smuzhiyun 			return ret;
721*4882a593Smuzhiyun 		dma_async_issue_pending(prtd->dma_chan);
722*4882a593Smuzhiyun 		dmaengine_dlp_setup(component, substream);
723*4882a593Smuzhiyun 		break;
724*4882a593Smuzhiyun 	case SNDRV_PCM_TRIGGER_RESUME:
725*4882a593Smuzhiyun 	case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
726*4882a593Smuzhiyun 		dmaengine_resume(prtd->dma_chan);
727*4882a593Smuzhiyun 		break;
728*4882a593Smuzhiyun 	case SNDRV_PCM_TRIGGER_SUSPEND:
729*4882a593Smuzhiyun 		if (runtime->info & SNDRV_PCM_INFO_PAUSE) {
730*4882a593Smuzhiyun 			dmaengine_pause(prtd->dma_chan);
731*4882a593Smuzhiyun 		} else {
732*4882a593Smuzhiyun 			dmaengine_dlp_release(component, substream);
733*4882a593Smuzhiyun 			dmaengine_terminate_async(prtd->dma_chan);
734*4882a593Smuzhiyun 		}
735*4882a593Smuzhiyun 		break;
736*4882a593Smuzhiyun 	case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
737*4882a593Smuzhiyun 		dmaengine_pause(prtd->dma_chan);
738*4882a593Smuzhiyun 		break;
739*4882a593Smuzhiyun 	case SNDRV_PCM_TRIGGER_STOP:
740*4882a593Smuzhiyun 		dmaengine_dlp_release(component, substream);
741*4882a593Smuzhiyun 		dmaengine_terminate_async(prtd->dma_chan);
742*4882a593Smuzhiyun 		break;
743*4882a593Smuzhiyun 	default:
744*4882a593Smuzhiyun 		return -EINVAL;
745*4882a593Smuzhiyun 	}
746*4882a593Smuzhiyun 
747*4882a593Smuzhiyun 	return 0;
748*4882a593Smuzhiyun }
749*4882a593Smuzhiyun 
dmaengine_dlp_new(struct snd_soc_component * component,struct snd_soc_pcm_runtime * rtd)750*4882a593Smuzhiyun static int dmaengine_dlp_new(struct snd_soc_component *component,
751*4882a593Smuzhiyun 			     struct snd_soc_pcm_runtime *rtd)
752*4882a593Smuzhiyun {
753*4882a593Smuzhiyun 	struct dmaengine_dlp *dlp = soc_component_to_dlp(component);
754*4882a593Smuzhiyun 	struct snd_pcm_substream *substream;
755*4882a593Smuzhiyun 	size_t prealloc_buffer_size;
756*4882a593Smuzhiyun 	size_t max_buffer_size;
757*4882a593Smuzhiyun 	unsigned int i;
758*4882a593Smuzhiyun 
759*4882a593Smuzhiyun 	prealloc_buffer_size = prealloc_buffer_size_kbytes * 1024;
760*4882a593Smuzhiyun 	max_buffer_size = SIZE_MAX;
761*4882a593Smuzhiyun 
762*4882a593Smuzhiyun 	for_each_pcm_streams(i) {
763*4882a593Smuzhiyun 		substream = rtd->pcm->streams[i].substream;
764*4882a593Smuzhiyun 		if (!substream)
765*4882a593Smuzhiyun 			continue;
766*4882a593Smuzhiyun 
767*4882a593Smuzhiyun 		if (!dlp->chan[i]) {
768*4882a593Smuzhiyun 			dev_err(component->dev,
769*4882a593Smuzhiyun 				"Missing dma channel for stream: %d\n", i);
770*4882a593Smuzhiyun 			return -EINVAL;
771*4882a593Smuzhiyun 		}
772*4882a593Smuzhiyun 
773*4882a593Smuzhiyun 		snd_pcm_set_managed_buffer(substream,
774*4882a593Smuzhiyun 				SNDRV_DMA_TYPE_DEV_IRAM,
775*4882a593Smuzhiyun 				dmaengine_dma_dev(dlp, substream),
776*4882a593Smuzhiyun 				prealloc_buffer_size,
777*4882a593Smuzhiyun 				max_buffer_size);
778*4882a593Smuzhiyun 
779*4882a593Smuzhiyun 		if (rtd->pcm->streams[i].pcm->name[0] == '\0') {
780*4882a593Smuzhiyun 			strscpy_pad(rtd->pcm->streams[i].pcm->name,
781*4882a593Smuzhiyun 				    rtd->pcm->streams[i].pcm->id,
782*4882a593Smuzhiyun 				    sizeof(rtd->pcm->streams[i].pcm->name));
783*4882a593Smuzhiyun 		}
784*4882a593Smuzhiyun 	}
785*4882a593Smuzhiyun 
786*4882a593Smuzhiyun 	return 0;
787*4882a593Smuzhiyun }
788*4882a593Smuzhiyun 
get_ref(struct snd_soc_component * component)789*4882a593Smuzhiyun static struct dmaengine_dlp_runtime_data *get_ref(struct snd_soc_component *component)
790*4882a593Smuzhiyun {
791*4882a593Smuzhiyun 	struct dmaengine_dlp *dlp = soc_component_to_dlp(component);
792*4882a593Smuzhiyun 	struct dmaengine_dlp_runtime_data *pref = NULL;
793*4882a593Smuzhiyun 
794*4882a593Smuzhiyun 	spin_lock(&dlp->lock);
795*4882a593Smuzhiyun 	if (!list_empty(&dlp->ref_list)) {
796*4882a593Smuzhiyun 		pref = list_first_entry(&dlp->ref_list, struct dmaengine_dlp_runtime_data, node);
797*4882a593Smuzhiyun 		list_del(&pref->node);
798*4882a593Smuzhiyun 	}
799*4882a593Smuzhiyun 	spin_unlock(&dlp->lock);
800*4882a593Smuzhiyun 
801*4882a593Smuzhiyun 	return pref;
802*4882a593Smuzhiyun }
803*4882a593Smuzhiyun 
process_capture(struct snd_soc_component * component,struct snd_pcm_substream * substream,unsigned long hwoff,void __user * buf,unsigned long bytes)804*4882a593Smuzhiyun static int process_capture(struct snd_soc_component *component,
805*4882a593Smuzhiyun 			   struct snd_pcm_substream *substream,
806*4882a593Smuzhiyun 			   unsigned long hwoff,
807*4882a593Smuzhiyun 			   void __user *buf, unsigned long bytes)
808*4882a593Smuzhiyun {
809*4882a593Smuzhiyun 	struct dmaengine_dlp *dlp = soc_component_to_dlp(component);
810*4882a593Smuzhiyun 	struct snd_pcm_runtime *runtime = substream->runtime;
811*4882a593Smuzhiyun 	struct dmaengine_dlp_runtime_data *prtd = substream_to_prtd(substream);
812*4882a593Smuzhiyun 	struct dmaengine_dlp_runtime_data *pref = NULL;
813*4882a593Smuzhiyun 	void *dma_ptr = runtime->dma_area + hwoff;
814*4882a593Smuzhiyun 	snd_pcm_sframes_t frames = dlp_bytes_to_frames(prtd, bytes);
815*4882a593Smuzhiyun 	snd_pcm_sframes_t frames_consumed = 0, frames_residue = 0, frames_tmp = 0;
816*4882a593Smuzhiyun 	snd_pcm_sframes_t ofs = 0;
817*4882a593Smuzhiyun 	snd_pcm_uframes_t appl_ptr;
818*4882a593Smuzhiyun 	char *cbuf = prtd->buf, *pbuf = NULL;
819*4882a593Smuzhiyun 	int ofs_cap, ofs_play, size_cap, size_play;
820*4882a593Smuzhiyun 	int i = 0, j = 0, ret = 0;
821*4882a593Smuzhiyun 	bool free_ref = false, mix = false;
822*4882a593Smuzhiyun 
823*4882a593Smuzhiyun 	appl_ptr = READ_ONCE(runtime->control->appl_ptr);
824*4882a593Smuzhiyun 
825*4882a593Smuzhiyun 	memcpy(cbuf, dma_ptr, bytes);
826*4882a593Smuzhiyun #ifdef DLP_DBG
827*4882a593Smuzhiyun 	/* DBG: mark STUB in ch-REC for trace each read */
828*4882a593Smuzhiyun 	memset(cbuf, 0x22, dlp_channels_to_bytes(prtd, 1));
829*4882a593Smuzhiyun #endif
830*4882a593Smuzhiyun 	ret = dlp_get_offset_size(prtd, dlp->mode, &ofs_cap, &size_cap, NULL);
831*4882a593Smuzhiyun 	if (ret) {
832*4882a593Smuzhiyun 		dlp_info("fail to get dlp cap offset\n");
833*4882a593Smuzhiyun 		return -EINVAL;
834*4882a593Smuzhiyun 	}
835*4882a593Smuzhiyun 
836*4882a593Smuzhiyun 	/* clear channel-LP_CHN */
837*4882a593Smuzhiyun 	for (i = 0; i < frames; i++) {
838*4882a593Smuzhiyun 		cbuf = prtd->buf + dlp_frames_to_bytes(prtd, i) + ofs_cap;
839*4882a593Smuzhiyun 		memset(cbuf, 0x0, size_cap);
840*4882a593Smuzhiyun 	}
841*4882a593Smuzhiyun 
842*4882a593Smuzhiyun start:
843*4882a593Smuzhiyun 	if (!prtd->ref)
844*4882a593Smuzhiyun 		prtd->ref = get_ref(component);
845*4882a593Smuzhiyun 	pref = prtd->ref;
846*4882a593Smuzhiyun 
847*4882a593Smuzhiyun 	/* do nothing if play stop */
848*4882a593Smuzhiyun 	if (!pref)
849*4882a593Smuzhiyun 		return 0;
850*4882a593Smuzhiyun 
851*4882a593Smuzhiyun 	ret = dlp_get_offset_size(pref, dlp->mode, &ofs_play, &size_play, &mix);
852*4882a593Smuzhiyun 	if (ret) {
853*4882a593Smuzhiyun 		dlp_info("fail to get dlp play offset\n");
854*4882a593Smuzhiyun 		return 0;
855*4882a593Smuzhiyun 	}
856*4882a593Smuzhiyun 
857*4882a593Smuzhiyun 	ofs = appl_ptr + pref->hw_ptr_delta;
858*4882a593Smuzhiyun 
859*4882a593Smuzhiyun 	/*
860*4882a593Smuzhiyun 	 * if playback stop, kref_put ref, and we can check this to
861*4882a593Smuzhiyun 	 * know if playback stopped, then free prtd->ref if data consumed.
862*4882a593Smuzhiyun 	 *
863*4882a593Smuzhiyun 	 */
864*4882a593Smuzhiyun 	if (kref_read(&pref->refcount) == 1) {
865*4882a593Smuzhiyun 		if (ofs >= pref->hw_ptr) {
866*4882a593Smuzhiyun 			kref_put(&pref->refcount, dmaengine_free_prtd);
867*4882a593Smuzhiyun 			prtd->ref = NULL;
868*4882a593Smuzhiyun 			return 0;
869*4882a593Smuzhiyun 		} else if ((ofs + frames) > pref->hw_ptr) {
870*4882a593Smuzhiyun 			dlp_info("applptr: %8lu, ofs': %7ld, refhwptr: %lu, frames: %lu (*)\n",
871*4882a593Smuzhiyun 				 appl_ptr, ofs, pref->hw_ptr, frames);
872*4882a593Smuzhiyun 			/*
873*4882a593Smuzhiyun 			 * should ignore the data that after play stop
874*4882a593Smuzhiyun 			 * and care about if the next ref start in the
875*4882a593Smuzhiyun 			 * same window
876*4882a593Smuzhiyun 			 */
877*4882a593Smuzhiyun 			frames_tmp = pref->hw_ptr - ofs;
878*4882a593Smuzhiyun 			frames_residue = frames - frames_tmp;
879*4882a593Smuzhiyun 			frames = frames_tmp;
880*4882a593Smuzhiyun 			free_ref = true;
881*4882a593Smuzhiyun 		}
882*4882a593Smuzhiyun 	}
883*4882a593Smuzhiyun 
884*4882a593Smuzhiyun 	/*
885*4882a593Smuzhiyun 	 * should ignore the data that before play start:
886*4882a593Smuzhiyun 	 *
887*4882a593Smuzhiyun 	 * frames:
888*4882a593Smuzhiyun 	 * +---------------------------------------------+
889*4882a593Smuzhiyun 	 * |      ofs<0       |         ofs>0            |
890*4882a593Smuzhiyun 	 * +---------------------------------------------+
891*4882a593Smuzhiyun 	 *
892*4882a593Smuzhiyun 	 */
893*4882a593Smuzhiyun 	if ((ofs + frames) <= 0)
894*4882a593Smuzhiyun 		return 0;
895*4882a593Smuzhiyun 
896*4882a593Smuzhiyun 	/* skip if ofs < 0 and fixup ofs */
897*4882a593Smuzhiyun 	j = 0;
898*4882a593Smuzhiyun 	if (ofs < 0) {
899*4882a593Smuzhiyun 		dlp_info("applptr: %8lu, ofs: %8ld, frames: %lu (*)\n",
900*4882a593Smuzhiyun 			 appl_ptr, ofs, frames);
901*4882a593Smuzhiyun 		j = -ofs;
902*4882a593Smuzhiyun 		frames += ofs;
903*4882a593Smuzhiyun 		ofs = 0;
904*4882a593Smuzhiyun 	}
905*4882a593Smuzhiyun 
906*4882a593Smuzhiyun 	ofs %= pref->buf_sz;
907*4882a593Smuzhiyun 
908*4882a593Smuzhiyun 	dlp_info("applptr: %8lu, ofs: %8ld, frames: %lu\n", appl_ptr, ofs, frames);
909*4882a593Smuzhiyun 
910*4882a593Smuzhiyun 	for (i = 0; i < frames; i++, j++) {
911*4882a593Smuzhiyun 		cbuf = prtd->buf + dlp_frames_to_bytes(prtd, j + frames_consumed) + ofs_cap;
912*4882a593Smuzhiyun 		pbuf = pref->buf + dlp_frames_to_bytes(pref, ((i + ofs) % pref->buf_sz)) + ofs_play;
913*4882a593Smuzhiyun 		if (mix)
914*4882a593Smuzhiyun 			dlp_mix_frame_buffer(pref, pbuf);
915*4882a593Smuzhiyun 		memcpy(cbuf, pbuf, size_cap);
916*4882a593Smuzhiyun 	}
917*4882a593Smuzhiyun 
918*4882a593Smuzhiyun 	appl_ptr += frames;
919*4882a593Smuzhiyun 	frames_consumed += frames;
920*4882a593Smuzhiyun 
921*4882a593Smuzhiyun 	if (free_ref) {
922*4882a593Smuzhiyun 		kref_put(&pref->refcount, dmaengine_free_prtd);
923*4882a593Smuzhiyun 		prtd->ref = NULL;
924*4882a593Smuzhiyun 		free_ref = false;
925*4882a593Smuzhiyun 		if (frames_residue) {
926*4882a593Smuzhiyun 			frames = frames_residue;
927*4882a593Smuzhiyun 			frames_residue = 0;
928*4882a593Smuzhiyun 			goto start;
929*4882a593Smuzhiyun 		}
930*4882a593Smuzhiyun 	}
931*4882a593Smuzhiyun 
932*4882a593Smuzhiyun 	return 0;
933*4882a593Smuzhiyun }
934*4882a593Smuzhiyun 
process_playback(struct snd_soc_component * component,struct snd_pcm_substream * substream,unsigned long hwoff,void __user * buf,unsigned long bytes)935*4882a593Smuzhiyun static int process_playback(struct snd_soc_component *component,
936*4882a593Smuzhiyun 			    struct snd_pcm_substream *substream,
937*4882a593Smuzhiyun 			    unsigned long hwoff,
938*4882a593Smuzhiyun 			    void __user *buf, unsigned long bytes)
939*4882a593Smuzhiyun {
940*4882a593Smuzhiyun 	struct dmaengine_dlp *dlp = soc_component_to_dlp(component);
941*4882a593Smuzhiyun 	struct dmaengine_dlp_runtime_data *pref;
942*4882a593Smuzhiyun 	char *pbuf;
943*4882a593Smuzhiyun 	int ret = 0;
944*4882a593Smuzhiyun 
945*4882a593Smuzhiyun 	spin_lock(&dlp->pref_lock);
946*4882a593Smuzhiyun 	pref = dlp->pref;
947*4882a593Smuzhiyun 	if (!pref) {
948*4882a593Smuzhiyun 		ret = -EFAULT;
949*4882a593Smuzhiyun 		goto err_unlock;
950*4882a593Smuzhiyun 	}
951*4882a593Smuzhiyun 
952*4882a593Smuzhiyun 	pbuf = pref->buf + pref->buf_ofs;
953*4882a593Smuzhiyun 
954*4882a593Smuzhiyun 	if (copy_from_user(pbuf, buf, bytes)) {
955*4882a593Smuzhiyun 		ret = -EFAULT;
956*4882a593Smuzhiyun 		goto err_unlock;
957*4882a593Smuzhiyun 	}
958*4882a593Smuzhiyun 
959*4882a593Smuzhiyun 	pref->buf_ofs += bytes;
960*4882a593Smuzhiyun 	pref->buf_ofs %= dlp_frames_to_bytes(pref, pref->buf_sz);
961*4882a593Smuzhiyun 
962*4882a593Smuzhiyun err_unlock:
963*4882a593Smuzhiyun 	spin_unlock(&dlp->pref_lock);
964*4882a593Smuzhiyun 
965*4882a593Smuzhiyun 	return ret;
966*4882a593Smuzhiyun }
967*4882a593Smuzhiyun 
dmaengine_dlp_process(struct snd_soc_component * component,struct snd_pcm_substream * substream,unsigned long hwoff,void __user * buf,unsigned long bytes)968*4882a593Smuzhiyun static int dmaengine_dlp_process(struct snd_soc_component *component,
969*4882a593Smuzhiyun 				 struct snd_pcm_substream *substream,
970*4882a593Smuzhiyun 				 unsigned long hwoff,
971*4882a593Smuzhiyun 				 void __user *buf, unsigned long bytes)
972*4882a593Smuzhiyun {
973*4882a593Smuzhiyun 	struct dmaengine_dlp *dlp = soc_component_to_dlp(component);
974*4882a593Smuzhiyun 	int ret = 0;
975*4882a593Smuzhiyun 
976*4882a593Smuzhiyun 	if (dlp->mode == DLP_MODE_DISABLED)
977*4882a593Smuzhiyun 		return -EINVAL;
978*4882a593Smuzhiyun 
979*4882a593Smuzhiyun 	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
980*4882a593Smuzhiyun 		ret = process_playback(component, substream, hwoff, buf, bytes);
981*4882a593Smuzhiyun 	else
982*4882a593Smuzhiyun 		ret = process_capture(component, substream, hwoff, buf, bytes);
983*4882a593Smuzhiyun 
984*4882a593Smuzhiyun 	return ret;
985*4882a593Smuzhiyun }
986*4882a593Smuzhiyun 
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)987*4882a593Smuzhiyun static int dmaengine_dlp_copy_user(struct snd_soc_component *component,
988*4882a593Smuzhiyun 				   struct snd_pcm_substream *substream,
989*4882a593Smuzhiyun 				   int channel, unsigned long hwoff,
990*4882a593Smuzhiyun 				   void __user *buf, unsigned long bytes)
991*4882a593Smuzhiyun {
992*4882a593Smuzhiyun 	struct dmaengine_dlp_runtime_data *prtd = substream_to_prtd(substream);
993*4882a593Smuzhiyun 	struct snd_pcm_runtime *runtime = substream->runtime;
994*4882a593Smuzhiyun 	bool is_playback = substream->stream == SNDRV_PCM_STREAM_PLAYBACK;
995*4882a593Smuzhiyun 	void *dma_ptr = runtime->dma_area + hwoff +
996*4882a593Smuzhiyun 			channel * (runtime->dma_bytes / runtime->channels);
997*4882a593Smuzhiyun 	int ret;
998*4882a593Smuzhiyun 
999*4882a593Smuzhiyun 	if (is_playback)
1000*4882a593Smuzhiyun 		if (copy_from_user(dma_ptr, buf, bytes))
1001*4882a593Smuzhiyun 			return -EFAULT;
1002*4882a593Smuzhiyun 
1003*4882a593Smuzhiyun 	ret = dmaengine_dlp_process(component, substream, hwoff, buf, bytes);
1004*4882a593Smuzhiyun 	if (!ret)
1005*4882a593Smuzhiyun 		dma_ptr = prtd->buf;
1006*4882a593Smuzhiyun 
1007*4882a593Smuzhiyun 	if (!is_playback)
1008*4882a593Smuzhiyun 		if (copy_to_user(buf, dma_ptr, bytes))
1009*4882a593Smuzhiyun 			return -EFAULT;
1010*4882a593Smuzhiyun 
1011*4882a593Smuzhiyun 	return 0;
1012*4882a593Smuzhiyun }
1013*4882a593Smuzhiyun 
1014*4882a593Smuzhiyun static SOC_ENUM_SINGLE_EXT_DECL(dlp_mode, dlp_text);
1015*4882a593Smuzhiyun 
dmaengine_dlp_mode_get(struct snd_kcontrol * kcontrol,struct snd_ctl_elem_value * ucontrol)1016*4882a593Smuzhiyun static int dmaengine_dlp_mode_get(struct snd_kcontrol *kcontrol,
1017*4882a593Smuzhiyun 				  struct snd_ctl_elem_value *ucontrol)
1018*4882a593Smuzhiyun {
1019*4882a593Smuzhiyun 	struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol);
1020*4882a593Smuzhiyun 	struct dmaengine_dlp *dlp = soc_component_to_dlp(component);
1021*4882a593Smuzhiyun 
1022*4882a593Smuzhiyun 	ucontrol->value.enumerated.item[0] = dlp->mode;
1023*4882a593Smuzhiyun 
1024*4882a593Smuzhiyun 	return 0;
1025*4882a593Smuzhiyun }
1026*4882a593Smuzhiyun 
dmaengine_dlp_mode_put(struct snd_kcontrol * kcontrol,struct snd_ctl_elem_value * ucontrol)1027*4882a593Smuzhiyun static int dmaengine_dlp_mode_put(struct snd_kcontrol *kcontrol,
1028*4882a593Smuzhiyun 				  struct snd_ctl_elem_value *ucontrol)
1029*4882a593Smuzhiyun {
1030*4882a593Smuzhiyun 	struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol);
1031*4882a593Smuzhiyun 	struct dmaengine_dlp *dlp = soc_component_to_dlp(component);
1032*4882a593Smuzhiyun 	unsigned int mode = ucontrol->value.enumerated.item[0];
1033*4882a593Smuzhiyun 
1034*4882a593Smuzhiyun 	/* MUST: do not update mode while stream is running */
1035*4882a593Smuzhiyun 	if (snd_soc_component_active(component))
1036*4882a593Smuzhiyun 		return -EPERM;
1037*4882a593Smuzhiyun 
1038*4882a593Smuzhiyun 	if (mode == dlp->mode)
1039*4882a593Smuzhiyun 		return 0;
1040*4882a593Smuzhiyun 
1041*4882a593Smuzhiyun 	dlp->mode = mode;
1042*4882a593Smuzhiyun 
1043*4882a593Smuzhiyun 	return 1;
1044*4882a593Smuzhiyun }
1045*4882a593Smuzhiyun 
1046*4882a593Smuzhiyun static const struct snd_kcontrol_new dmaengine_dlp_controls[] = {
1047*4882a593Smuzhiyun 	SOC_ENUM_EXT("Software Digital Loopback Mode", dlp_mode,
1048*4882a593Smuzhiyun 		     dmaengine_dlp_mode_get,
1049*4882a593Smuzhiyun 		     dmaengine_dlp_mode_put),
1050*4882a593Smuzhiyun };
1051*4882a593Smuzhiyun 
dmaengine_dlp_prepare(struct snd_soc_component * component,struct snd_pcm_substream * substream)1052*4882a593Smuzhiyun static int dmaengine_dlp_prepare(struct snd_soc_component *component,
1053*4882a593Smuzhiyun 				  struct snd_pcm_substream *substream)
1054*4882a593Smuzhiyun {
1055*4882a593Smuzhiyun 	struct dmaengine_dlp *dlp = soc_component_to_dlp(component);
1056*4882a593Smuzhiyun 	struct dmaengine_dlp_runtime_data *prtd = substream_to_prtd(substream);
1057*4882a593Smuzhiyun 	struct dmaengine_dlp_runtime_data *pref = NULL;
1058*4882a593Smuzhiyun 	int buf_bytes = dlp_frames_to_bytes(prtd, prtd->buf_sz);
1059*4882a593Smuzhiyun 
1060*4882a593Smuzhiyun 	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
1061*4882a593Smuzhiyun 		pref = kmemdup(prtd, sizeof(*prtd), GFP_KERNEL);
1062*4882a593Smuzhiyun 		if (!pref)
1063*4882a593Smuzhiyun 			return -ENOMEM;
1064*4882a593Smuzhiyun 
1065*4882a593Smuzhiyun 		kref_init(&pref->refcount);
1066*4882a593Smuzhiyun 		pref->buf_ofs = 0;
1067*4882a593Smuzhiyun 		pref->buf = kzalloc(buf_bytes, GFP_KERNEL);
1068*4882a593Smuzhiyun 		if (!pref->buf) {
1069*4882a593Smuzhiyun 			kfree(pref);
1070*4882a593Smuzhiyun 			return -ENOMEM;
1071*4882a593Smuzhiyun 		}
1072*4882a593Smuzhiyun 
1073*4882a593Smuzhiyun 		spin_lock(&dlp->pref_lock);
1074*4882a593Smuzhiyun 		dlp->pref = pref;
1075*4882a593Smuzhiyun 		spin_unlock(&dlp->pref_lock);
1076*4882a593Smuzhiyun 		dlp_info("PREF-CREATE: 0x%px\n", pref);
1077*4882a593Smuzhiyun 	} else {
1078*4882a593Smuzhiyun 		prtd->buf = kzalloc(buf_bytes, GFP_KERNEL);
1079*4882a593Smuzhiyun 		if (!prtd->buf)
1080*4882a593Smuzhiyun 			return -ENOMEM;
1081*4882a593Smuzhiyun 	}
1082*4882a593Smuzhiyun 
1083*4882a593Smuzhiyun 	return 0;
1084*4882a593Smuzhiyun }
1085*4882a593Smuzhiyun static const struct snd_soc_component_driver dmaengine_dlp_component = {
1086*4882a593Smuzhiyun 	.name		= SND_DMAENGINE_DLP_DRV_NAME,
1087*4882a593Smuzhiyun 	.probe_order	= SND_SOC_COMP_ORDER_LATE,
1088*4882a593Smuzhiyun 	.open		= dmaengine_dlp_open,
1089*4882a593Smuzhiyun 	.close		= dmaengine_dlp_close,
1090*4882a593Smuzhiyun 	.hw_params	= dmaengine_dlp_hw_params,
1091*4882a593Smuzhiyun 	.prepare	= dmaengine_dlp_prepare,
1092*4882a593Smuzhiyun 	.trigger	= dmaengine_dlp_trigger,
1093*4882a593Smuzhiyun 	.pointer	= dmaengine_dlp_pointer,
1094*4882a593Smuzhiyun 	.copy_user	= dmaengine_dlp_copy_user,
1095*4882a593Smuzhiyun 	.pcm_construct	= dmaengine_dlp_new,
1096*4882a593Smuzhiyun 	.controls	= dmaengine_dlp_controls,
1097*4882a593Smuzhiyun 	.num_controls	= ARRAY_SIZE(dmaengine_dlp_controls),
1098*4882a593Smuzhiyun };
1099*4882a593Smuzhiyun 
1100*4882a593Smuzhiyun static const char * const dmaengine_pcm_dma_channel_names[] = {
1101*4882a593Smuzhiyun 	[SNDRV_PCM_STREAM_PLAYBACK] = "tx",
1102*4882a593Smuzhiyun 	[SNDRV_PCM_STREAM_CAPTURE] = "rx",
1103*4882a593Smuzhiyun };
1104*4882a593Smuzhiyun 
dmaengine_pcm_request_chan_of(struct dmaengine_dlp * dlp,struct device * dev,const struct snd_dmaengine_pcm_config * config)1105*4882a593Smuzhiyun static int dmaengine_pcm_request_chan_of(struct dmaengine_dlp *dlp,
1106*4882a593Smuzhiyun 	struct device *dev, const struct snd_dmaengine_pcm_config *config)
1107*4882a593Smuzhiyun {
1108*4882a593Smuzhiyun 	unsigned int i;
1109*4882a593Smuzhiyun 	const char *name;
1110*4882a593Smuzhiyun 	struct dma_chan *chan;
1111*4882a593Smuzhiyun 
1112*4882a593Smuzhiyun 	for_each_pcm_streams(i) {
1113*4882a593Smuzhiyun 		name = dmaengine_pcm_dma_channel_names[i];
1114*4882a593Smuzhiyun 		chan = dma_request_chan(dev, name);
1115*4882a593Smuzhiyun 		if (IS_ERR(chan)) {
1116*4882a593Smuzhiyun 			/*
1117*4882a593Smuzhiyun 			 * Only report probe deferral errors, channels
1118*4882a593Smuzhiyun 			 * might not be present for devices that
1119*4882a593Smuzhiyun 			 * support only TX or only RX.
1120*4882a593Smuzhiyun 			 */
1121*4882a593Smuzhiyun 			if (PTR_ERR(chan) == -EPROBE_DEFER)
1122*4882a593Smuzhiyun 				return -EPROBE_DEFER;
1123*4882a593Smuzhiyun 			dlp->chan[i] = NULL;
1124*4882a593Smuzhiyun 		} else {
1125*4882a593Smuzhiyun 			dlp->chan[i] = chan;
1126*4882a593Smuzhiyun 		}
1127*4882a593Smuzhiyun 	}
1128*4882a593Smuzhiyun 
1129*4882a593Smuzhiyun 	return 0;
1130*4882a593Smuzhiyun }
1131*4882a593Smuzhiyun 
dmaengine_pcm_release_chan(struct dmaengine_dlp * dlp)1132*4882a593Smuzhiyun static void dmaengine_pcm_release_chan(struct dmaengine_dlp *dlp)
1133*4882a593Smuzhiyun {
1134*4882a593Smuzhiyun 	unsigned int i;
1135*4882a593Smuzhiyun 
1136*4882a593Smuzhiyun 	for_each_pcm_streams(i) {
1137*4882a593Smuzhiyun 		if (!dlp->chan[i])
1138*4882a593Smuzhiyun 			continue;
1139*4882a593Smuzhiyun 		dma_release_channel(dlp->chan[i]);
1140*4882a593Smuzhiyun 	}
1141*4882a593Smuzhiyun }
1142*4882a593Smuzhiyun 
1143*4882a593Smuzhiyun /**
1144*4882a593Smuzhiyun  * snd_dmaengine_dlp_register - Register a dmaengine based DLP device
1145*4882a593Smuzhiyun  * @dev: The parent device for the DLP device
1146*4882a593Smuzhiyun  * @config: Platform specific DLP configuration
1147*4882a593Smuzhiyun  */
snd_dmaengine_dlp_register(struct device * dev,const struct snd_dlp_config * config)1148*4882a593Smuzhiyun static int snd_dmaengine_dlp_register(struct device *dev,
1149*4882a593Smuzhiyun 	const struct snd_dlp_config *config)
1150*4882a593Smuzhiyun {
1151*4882a593Smuzhiyun 	const struct snd_soc_component_driver *driver;
1152*4882a593Smuzhiyun 	struct dmaengine_dlp *dlp;
1153*4882a593Smuzhiyun 	int ret;
1154*4882a593Smuzhiyun 
1155*4882a593Smuzhiyun 	dlp = kzalloc(sizeof(*dlp), GFP_KERNEL);
1156*4882a593Smuzhiyun 	if (!dlp)
1157*4882a593Smuzhiyun 		return -ENOMEM;
1158*4882a593Smuzhiyun 
1159*4882a593Smuzhiyun 	dlp->dev = dev;
1160*4882a593Smuzhiyun 	dlp->config = config;
1161*4882a593Smuzhiyun 
1162*4882a593Smuzhiyun 	INIT_LIST_HEAD(&dlp->ref_list);
1163*4882a593Smuzhiyun 	spin_lock_init(&dlp->lock);
1164*4882a593Smuzhiyun 	spin_lock_init(&dlp->pref_lock);
1165*4882a593Smuzhiyun 
1166*4882a593Smuzhiyun #ifdef CONFIG_DEBUG_FS
1167*4882a593Smuzhiyun 	dlp->component.debugfs_prefix = "dma";
1168*4882a593Smuzhiyun #endif
1169*4882a593Smuzhiyun 	ret = dmaengine_pcm_request_chan_of(dlp, dev, NULL);
1170*4882a593Smuzhiyun 	if (ret)
1171*4882a593Smuzhiyun 		goto err_free_dma;
1172*4882a593Smuzhiyun 
1173*4882a593Smuzhiyun 	driver = &dmaengine_dlp_component;
1174*4882a593Smuzhiyun 
1175*4882a593Smuzhiyun 	ret = snd_soc_component_initialize(&dlp->component, driver, dev);
1176*4882a593Smuzhiyun 	if (ret)
1177*4882a593Smuzhiyun 		goto err_free_dma;
1178*4882a593Smuzhiyun 
1179*4882a593Smuzhiyun 	ret = snd_soc_add_component(&dlp->component, NULL, 0);
1180*4882a593Smuzhiyun 	if (ret)
1181*4882a593Smuzhiyun 		goto err_free_dma;
1182*4882a593Smuzhiyun 
1183*4882a593Smuzhiyun 	return 0;
1184*4882a593Smuzhiyun 
1185*4882a593Smuzhiyun err_free_dma:
1186*4882a593Smuzhiyun 	dmaengine_pcm_release_chan(dlp);
1187*4882a593Smuzhiyun 	kfree(dlp);
1188*4882a593Smuzhiyun 	return ret;
1189*4882a593Smuzhiyun }
1190*4882a593Smuzhiyun 
1191*4882a593Smuzhiyun /**
1192*4882a593Smuzhiyun  * snd_dmaengine_dlp_unregister - Removes a dmaengine based DLP device
1193*4882a593Smuzhiyun  * @dev: Parent device the DLP was register with
1194*4882a593Smuzhiyun  *
1195*4882a593Smuzhiyun  * Removes a dmaengine based DLP device previously registered with
1196*4882a593Smuzhiyun  * snd_dmaengine_dlp_register.
1197*4882a593Smuzhiyun  */
snd_dmaengine_dlp_unregister(struct device * dev)1198*4882a593Smuzhiyun static void snd_dmaengine_dlp_unregister(struct device *dev)
1199*4882a593Smuzhiyun {
1200*4882a593Smuzhiyun 	struct snd_soc_component *component;
1201*4882a593Smuzhiyun 	struct dmaengine_dlp *dlp;
1202*4882a593Smuzhiyun 
1203*4882a593Smuzhiyun 	component = snd_soc_lookup_component(dev, SND_DMAENGINE_DLP_DRV_NAME);
1204*4882a593Smuzhiyun 	if (!component)
1205*4882a593Smuzhiyun 		return;
1206*4882a593Smuzhiyun 
1207*4882a593Smuzhiyun 	dlp = soc_component_to_dlp(component);
1208*4882a593Smuzhiyun 
1209*4882a593Smuzhiyun 	snd_soc_unregister_component_by_driver(dev, component->driver);
1210*4882a593Smuzhiyun 	dmaengine_pcm_release_chan(dlp);
1211*4882a593Smuzhiyun 	kfree(dlp);
1212*4882a593Smuzhiyun }
1213*4882a593Smuzhiyun 
devm_dmaengine_dlp_release(struct device * dev,void * res)1214*4882a593Smuzhiyun static void devm_dmaengine_dlp_release(struct device *dev, void *res)
1215*4882a593Smuzhiyun {
1216*4882a593Smuzhiyun 	snd_dmaengine_dlp_unregister(*(struct device **)res);
1217*4882a593Smuzhiyun }
1218*4882a593Smuzhiyun 
1219*4882a593Smuzhiyun /**
1220*4882a593Smuzhiyun  * devm_snd_dmaengine_dlp_register - resource managed dmaengine DLP registration
1221*4882a593Smuzhiyun  * @dev: The parent device for the DLP device
1222*4882a593Smuzhiyun  * @config: Platform specific DLP configuration
1223*4882a593Smuzhiyun  *
1224*4882a593Smuzhiyun  * Register a dmaengine based DLP device with automatic unregistration when the
1225*4882a593Smuzhiyun  * device is unregistered.
1226*4882a593Smuzhiyun  */
devm_snd_dmaengine_dlp_register(struct device * dev,const struct snd_dlp_config * config)1227*4882a593Smuzhiyun int devm_snd_dmaengine_dlp_register(struct device *dev,
1228*4882a593Smuzhiyun 	const struct snd_dlp_config *config)
1229*4882a593Smuzhiyun {
1230*4882a593Smuzhiyun 	struct device **ptr;
1231*4882a593Smuzhiyun 	int ret;
1232*4882a593Smuzhiyun 
1233*4882a593Smuzhiyun 	ptr = devres_alloc(devm_dmaengine_dlp_release, sizeof(*ptr), GFP_KERNEL);
1234*4882a593Smuzhiyun 	if (!ptr)
1235*4882a593Smuzhiyun 		return -ENOMEM;
1236*4882a593Smuzhiyun 
1237*4882a593Smuzhiyun 	ret = snd_dmaengine_dlp_register(dev, config);
1238*4882a593Smuzhiyun 	if (ret == 0) {
1239*4882a593Smuzhiyun 		*ptr = dev;
1240*4882a593Smuzhiyun 		devres_add(dev, ptr);
1241*4882a593Smuzhiyun 	} else {
1242*4882a593Smuzhiyun 		devres_free(ptr);
1243*4882a593Smuzhiyun 	}
1244*4882a593Smuzhiyun 
1245*4882a593Smuzhiyun 	return ret;
1246*4882a593Smuzhiyun }
1247*4882a593Smuzhiyun EXPORT_SYMBOL_GPL(devm_snd_dmaengine_dlp_register);
1248*4882a593Smuzhiyun 
1249*4882a593Smuzhiyun MODULE_LICENSE("GPL");
1250