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