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