1 /*
2 * Copyright (c) 2019-2025 Allwinner Technology Co., Ltd. ALL rights reserved.
3 *
4 * Allwinner is a trademark of Allwinner Technology Co.,Ltd., registered in
5 * the the people's Republic of China and other countries.
6 * All Allwinner Technology Co.,Ltd. trademarks are used with permission.
7 *
8 * DISCLAIMER
9 * THIRD PARTY LICENCES MAY BE REQUIRED TO IMPLEMENT THE SOLUTION/PRODUCT.
10 * IF YOU NEED TO INTEGRATE THIRD PARTY’S TECHNOLOGY (SONY, DTS, DOLBY, AVS OR MPEGLA, ETC.)
11 * IN ALLWINNERS’SDK OR PRODUCTS, YOU SHALL BE SOLELY RESPONSIBLE TO OBTAIN
12 * ALL APPROPRIATELY REQUIRED THIRD PARTY LICENCES.
13 * ALLWINNER SHALL HAVE NO WARRANTY, INDEMNITY OR OTHER OBLIGATIONS WITH RESPECT TO MATTERS
14 * COVERED UNDER ANY REQUIRED THIRD PARTY LICENSE.
15 * YOU ARE SOLELY RESPONSIBLE FOR YOUR USAGE OF THIRD PARTY’S TECHNOLOGY.
16 *
17 *
18 * THIS SOFTWARE IS PROVIDED BY ALLWINNER"AS IS" AND TO THE MAXIMUM EXTENT
19 * PERMITTED BY LAW, ALLWINNER EXPRESSLY DISCLAIMS ALL WARRANTIES OF ANY KIND,
20 * WHETHER EXPRESS, IMPLIED OR STATUTORY, INCLUDING WITHOUT LIMITATION REGARDING
21 * THE TITLE, NON-INFRINGEMENT, ACCURACY, CONDITION, COMPLETENESS, PERFORMANCE
22 * OR MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
23 * IN NO EVENT SHALL ALLWINNER BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
24 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
25 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
26 * LOSS OF USE, DATA, OR PROFITS, OR BUSINESS INTERRUPTION)
27 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
28 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
29 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
30 * OF THE POSSIBILITY OF SUCH DAMAGE.
31 */
32 #include <sound/snd_core.h>
33 #include <sound/snd_dma.h>
34 #include <sound/snd_pcm.h>
35 #include <hal_dma.h>
36 #include <sound/dma_wrap.h>
37 #include <hal_cache.h>
38
39 struct dmaengine_pcm_runtime_data {
40 struct dma_chan *dma_chan;
41 /* used for no residue, noneed now */
42 uint32_t pos;
43 };
44
45 void snd_pcm_period_elapsed(struct snd_pcm_substream *substream);
46
substream_to_prtd(const struct snd_pcm_substream * substream)47 static inline struct dmaengine_pcm_runtime_data *substream_to_prtd(
48 const struct snd_pcm_substream *substream)
49 {
50 return substream->runtime->private_data;
51 }
52
snd_dmaengine_pcm_get_chan(struct snd_pcm_substream * substream)53 struct dma_chan *snd_dmaengine_pcm_get_chan(struct snd_pcm_substream *substream)
54 {
55 struct dmaengine_pcm_runtime_data *prtd = substream_to_prtd(substream);
56
57 return prtd->dma_chan;
58 }
59
snd_hwparams_to_dma_slave_config(const struct snd_pcm_substream * substream,const struct snd_pcm_hw_params * params,struct dma_slave_config * slave_config)60 int snd_hwparams_to_dma_slave_config(const struct snd_pcm_substream *substream,
61 const struct snd_pcm_hw_params *params,
62 struct dma_slave_config *slave_config)
63 {
64 enum dma_slave_buswidth buswidth;
65 switch (params_format(params)) {
66 case SND_PCM_FORMAT_S8:
67 buswidth = DMA_SLAVE_BUSWIDTH_1_BYTE;
68 break;
69 case SND_PCM_FORMAT_S16_LE:
70 buswidth = DMA_SLAVE_BUSWIDTH_2_BYTES;
71 break;
72 case SND_PCM_FORMAT_S24_LE:
73 case SND_PCM_FORMAT_S32_LE:
74 buswidth = DMA_SLAVE_BUSWIDTH_4_BYTES;
75 break;
76 default:
77 return -EINVAL;
78 }
79
80 if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
81 slave_config->direction = DMA_MEM_TO_DEV;
82 slave_config->dst_addr_width = buswidth;
83 } else {
84 slave_config->direction = DMA_DEV_TO_MEM;
85 slave_config->src_addr_width = buswidth;
86 }
87 return 0;
88 }
89
snd_dmaengine_pcm_open_request_chan(struct snd_pcm_substream * substream)90 int snd_dmaengine_pcm_open_request_chan(struct snd_pcm_substream *substream)
91 {
92 struct dma_chan *chan;
93 struct dmaengine_pcm_runtime_data *prtd;
94
95 chan = dma_request_channel();
96 if (!chan)
97 return -ENXIO;
98 prtd = snd_malloc(sizeof(struct dmaengine_pcm_runtime_data));
99 if (!prtd)
100 return -ENOMEM;
101 prtd->dma_chan = chan;
102 substream->runtime->private_data = prtd;
103
104 return 0;
105 }
106
snd_dmaengine_pcm_close_release_chan(struct snd_pcm_substream * substream)107 int snd_dmaengine_pcm_close_release_chan(struct snd_pcm_substream *substream)
108 {
109 struct dmaengine_pcm_runtime_data *prtd = substream_to_prtd(substream);
110
111 dma_release_channel(prtd->dma_chan);
112 snd_free(prtd);
113 substream->runtime->private_data = NULL;
114 return 0;
115 }
116
snd_dmaengine_pcm_pointer(struct snd_pcm_substream * substream)117 snd_pcm_uframes_t snd_dmaengine_pcm_pointer(struct snd_pcm_substream *substream)
118 {
119 struct dmaengine_pcm_runtime_data *prtd = substream_to_prtd(substream);
120 uint32_t residue = 0;
121 enum dma_status status;
122 uint32_t pos = 0;
123 uint32_t buf_size;
124
125 status = dmaengine_tx_status(prtd->dma_chan, &residue);
126 snd_print("dma status:%u, residue:%u(0x%x) bytes\n", status, residue, residue);
127 if (status == DMA_IN_PROGRESS || status == DMA_PAUSED) {
128 buf_size = snd_pcm_lib_buffer_bytes(substream);
129 if (residue > 0 && residue <= buf_size)
130 pos = buf_size - residue;
131 }
132 snd_print("----pos:0x%x(%u) bytes, pos frames offset:0x%lx\n",
133 pos, pos, bytes_to_frames(substream->runtime, pos));
134 return bytes_to_frames(substream->runtime, pos);
135 }
136
137 static inline enum dma_transfer_direction
snd_pcm_substream_to_dma_direction(const struct snd_pcm_substream * substream)138 snd_pcm_substream_to_dma_direction(const struct snd_pcm_substream *substream)
139 {
140 if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
141 return DMA_MEM_TO_DEV;
142 else
143 return DMA_DEV_TO_MEM;
144 }
145
dmaengine_pcm_dma_complete(void * arg)146 static void dmaengine_pcm_dma_complete(void *arg)
147 {
148 struct snd_pcm_substream *substream = arg;
149 struct dmaengine_pcm_runtime_data *prtd;
150
151 snd_print("=========dma callback start============\n");
152 if (!substream->runtime)
153 return;
154 prtd = substream_to_prtd(substream);
155 prtd->pos += snd_pcm_lib_period_bytes(substream);
156 if (prtd->pos >= snd_pcm_lib_buffer_bytes(substream))
157 prtd->pos = 0;
158 snd_pcm_period_elapsed(substream);
159 snd_print("==========dma callback finish===========\n");
160 }
161
dmaengine_pcm_prepare_and_submit(struct snd_pcm_substream * substream)162 static int dmaengine_pcm_prepare_and_submit(struct snd_pcm_substream *substream)
163 {
164 struct dmaengine_pcm_runtime_data *prtd = substream_to_prtd(substream);
165 struct dma_chan *chan = prtd->dma_chan;
166 enum dma_transfer_direction direction;
167 int ret = 0;
168 dma_callback callback;
169
170 direction = snd_pcm_substream_to_dma_direction(substream);
171
172 prtd->pos = 0;
173
174 /* flush ringbuffer */
175 hal_dcache_clean_invalidate((unsigned long)(substream->runtime->dma_addr),
176 snd_pcm_lib_buffer_bytes(substream));
177
178 ret = dmaengine_prep_dma_cyclic(chan,
179 substream->runtime->dma_addr,
180 snd_pcm_lib_buffer_bytes(substream),
181 snd_pcm_lib_period_bytes(substream), direction);
182 if (ret != 0) {
183 snd_err("[%s] dma cyclic failed!!!\n", __func__);
184 return ret;
185 }
186 callback = dmaengine_pcm_dma_complete;
187
188 dmaengine_submit(chan, callback, (void *)substream);
189
190 return 0;
191 }
192
snd_dmaengine_pcm_trigger(struct snd_pcm_substream * substream,int cmd)193 int snd_dmaengine_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
194 {
195 struct dmaengine_pcm_runtime_data *prtd = substream_to_prtd(substream);
196 int ret;
197
198 snd_print("\n");
199 switch (cmd) {
200 case SNDRV_PCM_TRIGGER_START:
201 ret = dmaengine_pcm_prepare_and_submit(substream);
202 if (ret < 0)
203 return ret;
204 dma_async_issue_pending(prtd->dma_chan);
205 break;
206 case SNDRV_PCM_TRIGGER_RESUME:
207 case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
208 dmaengine_resume(prtd->dma_chan);
209 break;
210 case SNDRV_PCM_TRIGGER_SUSPEND:
211 #if 0
212 if (runtime->info & SNDRV_PCM_INFO_PAUSE)
213 dmaengine_pause(prtd->dma_chan);
214 else
215 #endif
216 dmaengine_terminate_async(prtd->dma_chan);
217 break;
218 case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
219 dmaengine_pause(prtd->dma_chan);
220 break;
221 case SNDRV_PCM_TRIGGER_STOP:
222 dmaengine_terminate_async(prtd->dma_chan);
223 break;
224 default:
225 return -EINVAL;
226 }
227
228 return 0;
229 }
230