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 <stdlib.h>
33 #include "bswap.h"
34 #include "pcm_local.h"
35 #include "pcm_plugin_generic.h"
36 
37 #include "plugin_ops.h"
38 
39 typedef struct {
40         /* This field need to be the first */
41         snd_pcm_plugin_t plug;
42         unsigned int use_getput;
43         unsigned int conv_idx;
44         unsigned int get_idx, put_idx;
45         snd_pcm_format_t sformat;
46 } snd_pcm_linear_t;
47 
snd_pcm_linear_convert_index(snd_pcm_format_t src_format,snd_pcm_format_t dst_format)48 int snd_pcm_linear_convert_index(snd_pcm_format_t src_format,
49                  snd_pcm_format_t dst_format)
50 {
51     int src_endian, dst_endian, sign, src_width, dst_width;
52 
53     sign = (snd_pcm_format_signed(src_format) !=
54         snd_pcm_format_signed(dst_format));
55 
56     src_endian = snd_pcm_format_big_endian(src_format);
57     dst_endian = snd_pcm_format_big_endian(dst_format);
58 
59     if (src_endian < 0)
60         src_endian = 0;
61     if (dst_endian < 0)
62         dst_endian = 0;
63 
64     src_width = snd_pcm_format_width(src_format) / 8 - 1;
65     dst_width = snd_pcm_format_width(dst_format) / 8 - 1;
66 
67     return src_width * 32 + src_endian * 16 + sign * 8 + dst_width * 2 + dst_endian;
68 }
69 
snd_pcm_linear_get_index(snd_pcm_format_t src_format,snd_pcm_format_t dst_format)70 int snd_pcm_linear_get_index(snd_pcm_format_t src_format, snd_pcm_format_t dst_format)
71 {
72     int sign, width, pwidth, endian;
73     sign = (snd_pcm_format_signed(src_format) !=
74         snd_pcm_format_signed(dst_format));
75 
76     endian = snd_pcm_format_big_endian(src_format);
77 
78     if (endian < 0)
79         endian = 0;
80     pwidth = snd_pcm_format_physical_width(src_format);
81     width = snd_pcm_format_width(src_format);
82     if (pwidth == 24) {
83         switch (width) {
84         case 24:
85             width = 0; break;
86         case 20:
87             width = 1; break;
88         case 18:
89         default:
90             width = 2; break;
91         }
92         return width * 4 + endian * 2 + sign + 16;
93     } else {
94         width = width / 8 - 1;
95         return width * 4 + endian * 2 + sign;
96     }
97 }
98 
snd_pcm_linear_put_index(snd_pcm_format_t src_format,snd_pcm_format_t dst_format)99 int snd_pcm_linear_put_index(snd_pcm_format_t src_format, snd_pcm_format_t dst_format)
100 {
101     int sign, width, pwidth, endian;
102     sign = (snd_pcm_format_signed(src_format) !=
103         snd_pcm_format_signed(dst_format));
104 
105     endian = snd_pcm_format_big_endian(dst_format);
106 
107     if (endian < 0)
108         endian = 0;
109     pwidth = snd_pcm_format_physical_width(dst_format);
110     width = snd_pcm_format_width(dst_format);
111     if (pwidth == 24) {
112         switch (width) {
113         case 24:
114             width = 0; break;
115         case 20:
116             width = 1; break;
117         case 18:
118         default:
119             width = 2; break;
120         }
121         return width * 4 + endian * 2 + sign + 16;
122     } else {
123         width = width / 8 - 1;
124         return width * 4 + endian * 2 + sign;
125     }
126 }
127 
snd_pcm_linear_convert(const snd_pcm_channel_area_t * dst_areas,snd_pcm_uframes_t dst_offset,const snd_pcm_channel_area_t * src_areas,snd_pcm_uframes_t src_offset,unsigned int channels,snd_pcm_uframes_t frames,unsigned int convidx)128 void snd_pcm_linear_convert(const snd_pcm_channel_area_t *dst_areas, snd_pcm_uframes_t dst_offset,
129                 const snd_pcm_channel_area_t *src_areas, snd_pcm_uframes_t src_offset,
130                 unsigned int channels, snd_pcm_uframes_t frames,
131                 unsigned int convidx)
132 {
133 #define CONV_LABELS
134 #include "plugin_ops.h"
135 #undef CONV_LABELS
136     void *conv = conv_labels[convidx];
137     unsigned int channel;
138     for (channel = 0; channel < channels; ++channel) {
139         const char *src;
140         char *dst;
141         int src_step, dst_step;
142         snd_pcm_uframes_t frames1;
143         const snd_pcm_channel_area_t *src_area = &src_areas[channel];
144         const snd_pcm_channel_area_t *dst_area = &dst_areas[channel];
145         src = snd_pcm_channel_area_addr(src_area, src_offset);
146         dst = snd_pcm_channel_area_addr(dst_area, dst_offset);
147         src_step = snd_pcm_channel_area_step(src_area);
148         dst_step = snd_pcm_channel_area_step(dst_area);
149         frames1 = frames;
150         while (frames1-- > 0) {
151             goto *conv;
152 #define CONV_END after
153 #include "plugin_ops.h"
154 #undef CONV_END
155         after:
156             src += src_step;
157             dst += dst_step;
158         }
159     }
160 }
161 
snd_pcm_linear_getput(const snd_pcm_channel_area_t * dst_areas,snd_pcm_uframes_t dst_offset,const snd_pcm_channel_area_t * src_areas,snd_pcm_uframes_t src_offset,unsigned int channels,snd_pcm_uframes_t frames,unsigned int get_idx,unsigned int put_idx)162 void snd_pcm_linear_getput(const snd_pcm_channel_area_t *dst_areas, snd_pcm_uframes_t dst_offset,
163                const snd_pcm_channel_area_t *src_areas, snd_pcm_uframes_t src_offset,
164                unsigned int channels, snd_pcm_uframes_t frames,
165                unsigned int get_idx, unsigned int put_idx)
166 {
167 #define CONV24_LABELS
168 #include "plugin_ops.h"
169 #undef CONV24_LABELS
170     void *get = get32_labels[get_idx];
171     void *put = put32_labels[put_idx];
172     unsigned int channel;
173     u_int32_t sample = 0;
174     for (channel = 0; channel < channels; ++channel) {
175         const char *src;
176         char *dst;
177         int src_step, dst_step;
178         snd_pcm_uframes_t frames1;
179         const snd_pcm_channel_area_t *src_area = &src_areas[channel];
180         const snd_pcm_channel_area_t *dst_area = &dst_areas[channel];
181         src = snd_pcm_channel_area_addr(src_area, src_offset);
182         dst = snd_pcm_channel_area_addr(dst_area, dst_offset);
183         src_step = snd_pcm_channel_area_step(src_area);
184         dst_step = snd_pcm_channel_area_step(dst_area);
185         frames1 = frames;
186         while (frames1-- > 0) {
187             goto *get;
188 #define CONV24_END after
189 #include "plugin_ops.h"
190 #undef CONV24_END
191         after:
192             src += src_step;
193             dst += dst_step;
194         }
195     }
196 }
197 
198 static snd_pcm_uframes_t
snd_pcm_linear_write_areas(snd_pcm_t * pcm,const snd_pcm_channel_area_t * areas,snd_pcm_uframes_t offset,snd_pcm_uframes_t size,const snd_pcm_channel_area_t * slave_areas,snd_pcm_uframes_t slave_offset,snd_pcm_uframes_t * slave_sizep)199 snd_pcm_linear_write_areas(snd_pcm_t *pcm,
200                const snd_pcm_channel_area_t *areas,
201                snd_pcm_uframes_t offset,
202                snd_pcm_uframes_t size,
203                const snd_pcm_channel_area_t *slave_areas,
204                snd_pcm_uframes_t slave_offset,
205                snd_pcm_uframes_t *slave_sizep)
206 {
207     snd_pcm_linear_t *linear = pcm->private_data;
208     if (size > *slave_sizep)
209         size = *slave_sizep;
210     if (linear->use_getput)
211         snd_pcm_linear_getput(slave_areas, slave_offset,
212                       areas, offset,
213                       pcm->channels, size,
214                       linear->get_idx, linear->put_idx);
215     else
216         snd_pcm_linear_convert(slave_areas, slave_offset,
217                        areas, offset,
218                        pcm->channels, size, linear->conv_idx);
219     *slave_sizep = size;
220     return size;
221 }
222 
223 static snd_pcm_uframes_t
snd_pcm_linear_read_areas(snd_pcm_t * pcm,const snd_pcm_channel_area_t * areas,snd_pcm_uframes_t offset,snd_pcm_uframes_t size,const snd_pcm_channel_area_t * slave_areas,snd_pcm_uframes_t slave_offset,snd_pcm_uframes_t * slave_sizep)224 snd_pcm_linear_read_areas(snd_pcm_t *pcm,
225               const snd_pcm_channel_area_t *areas,
226               snd_pcm_uframes_t offset,
227               snd_pcm_uframes_t size,
228               const snd_pcm_channel_area_t *slave_areas,
229               snd_pcm_uframes_t slave_offset,
230               snd_pcm_uframes_t *slave_sizep)
231 {
232     snd_pcm_linear_t *linear = pcm->private_data;
233     if (size > *slave_sizep)
234         size = *slave_sizep;
235     if (linear->use_getput)
236         snd_pcm_linear_getput(areas, offset,
237                       slave_areas, slave_offset,
238                       pcm->channels, size,
239                       linear->get_idx, linear->put_idx);
240     else
241         snd_pcm_linear_convert(areas, offset,
242                        slave_areas, slave_offset,
243                        pcm->channels, size, linear->conv_idx);
244     *slave_sizep = size;
245     return size;
246 }
247 
snd_pcm_linear_hw_refine(snd_pcm_t * pcm,snd_pcm_hw_params_t * params)248 static int snd_pcm_linear_hw_refine(snd_pcm_t *pcm, snd_pcm_hw_params_t *params)
249 {
250     snd_pcm_linear_t *linear = pcm->private_data;
251     snd_pcm_t *slave = linear->plug.gen.slave;
252     int ret;
253 
254     awalsa_debug("\n");
255     ret = slave->ops->hw_refine(slave->op_arg, params);
256     if (ret < 0)
257         return ret;
258 
259     return ret;
260 }
261 
snd_pcm_linear_hw_params(snd_pcm_t * pcm,snd_pcm_hw_params_t * params)262 static int snd_pcm_linear_hw_params(snd_pcm_t *pcm, snd_pcm_hw_params_t *params)
263 {
264     snd_pcm_linear_t *linear = pcm->private_data;
265     snd_pcm_t *slave = linear->plug.gen.slave;
266     int err;
267     snd_pcm_hw_params_t *slave_params;
268     snd_pcm_format_t format;
269 
270     snd_pcm_hw_params_alloca(&slave_params);
271     memcpy(slave_params, params, sizeof(snd_pcm_hw_params_t));
272 
273     snd_pcm_hw_params_set_format(slave, slave_params, linear->sformat);
274 
275     err = _snd_pcm_hw_params_internal(slave, slave_params);
276     if (err < 0) {
277         awalsa_err("failed to set slave hw params\n");
278         return err;
279     }
280 
281     snd_pcm_hw_params_get_format(params, &format);
282     linear->use_getput = (snd_pcm_format_physical_width(format) == 24 ||
283                 snd_pcm_format_physical_width(linear->sformat) == 24);
284     if (linear->use_getput) {
285         if (pcm->stream == SND_PCM_STREAM_PLAYBACK) {
286             linear->get_idx = snd_pcm_linear_get_index(format, SND_PCM_FORMAT_S32);
287             linear->put_idx = snd_pcm_linear_put_index(SND_PCM_FORMAT_S32, linear->sformat);
288         } else {
289             linear->get_idx = snd_pcm_linear_get_index(format, SND_PCM_FORMAT_S32);
290             linear->put_idx = snd_pcm_linear_put_index(SND_PCM_FORMAT_S32, linear->sformat);
291         }
292         /*awalsa_info("get_idx=%u, put_idx=%u\n", linear->get_idx, linear->put_idx);*/
293     } else {
294         if (pcm->stream == SND_PCM_STREAM_PLAYBACK)
295             linear->conv_idx = snd_pcm_linear_convert_index(format , linear->sformat);
296         else
297             linear->conv_idx = snd_pcm_linear_convert_index(linear->sformat , format);
298         /*awalsa_info("conv_idx=%u\n", linear->conv_idx);*/
299     }
300 
301     /*TODO*/
302     memcpy(params, slave_params, sizeof(snd_pcm_hw_params_t));
303 
304     return 0;
305 }
306 
snd_pcm_linear_dump(snd_pcm_t * pcm)307 static void snd_pcm_linear_dump(snd_pcm_t *pcm)
308 {
309     snd_pcm_linear_t *linear = pcm->private_data;
310     printf("Linear conversion PCM (%s)\n", snd_pcm_format_name(linear->sformat));
311     if (pcm->setup) {
312         printf("Its setup is:\n");
313         snd_pcm_dump_setup(pcm);
314     }
315     printf("Slave: ");
316     snd_pcm_dump_setup(linear->plug.gen.slave);
317 }
318 
319 static const snd_pcm_ops_t snd_pcm_linear_ops = {
320     .close = snd_pcm_generic_close,
321     /*.info = snd_pcm_generic_info,*/
322     .hw_refine = snd_pcm_linear_hw_refine,
323     .hw_params = snd_pcm_linear_hw_params,
324     .hw_free = snd_pcm_generic_hw_free,
325     .sw_params = snd_pcm_generic_sw_params,
326     .channel_info = snd_pcm_generic_channel_info,
327     .dump = snd_pcm_linear_dump,
328     /*.nonblock = snd_pcm_generic_nonblock,*/
329     /*.async = snd_pcm_generic_async,*/
330     .mmap = snd_pcm_generic_mmap,
331     .munmap = snd_pcm_generic_munmap,
332     .query_chmaps = snd_pcm_generic_query_chmaps,
333     .get_chmap = snd_pcm_generic_get_chmap,
334     .set_chmap = snd_pcm_generic_set_chmap,
335 };
336 
snd_pcm_linear_open(snd_pcm_t ** pcmp,const char * name,snd_pcm_format_t sformat,snd_pcm_t * slave,int close_slave)337 int snd_pcm_linear_open(snd_pcm_t **pcmp, const char *name, snd_pcm_format_t sformat, snd_pcm_t *slave, int close_slave)
338 {
339     snd_pcm_t *pcm;
340     snd_pcm_linear_t *linear;
341     int err;
342 
343     awalsa_debug("\n");
344     assert(pcmp && slave);
345     if (snd_pcm_format_linear(sformat) != 1)
346         return -EINVAL;
347     linear = calloc(1, sizeof(snd_pcm_linear_t));
348     if (!linear)
349         return -ENOMEM;
350     snd_pcm_plugin_init(&linear->plug);
351     linear->sformat = sformat;
352     linear->plug.read = snd_pcm_linear_read_areas;
353     linear->plug.write = snd_pcm_linear_write_areas;
354     linear->plug.undo_read = snd_pcm_plugin_undo_read_generic;
355     linear->plug.undo_write = snd_pcm_plugin_undo_write_generic;
356     linear->plug.gen.slave = slave;
357     linear->plug.gen.close_slave = close_slave;
358 
359     awalsa_debug("\n");
360     err = snd_pcm_new(&pcm, SND_PCM_TYPE_LINEAR, name, slave->stream, slave->mode);
361     if (err < 0) {
362         free(linear);
363         return err;
364     }
365     pcm->ops = &snd_pcm_linear_ops;
366     pcm->fast_ops = &snd_pcm_plugin_fast_ops;
367     pcm->private_data = linear;
368     snd_pcm_set_hw_ptr(pcm, &linear->plug.hw_ptr, 0, 0);
369     snd_pcm_set_appl_ptr(pcm, &linear->plug.appl_ptr, 0, 0);
370     *pcmp = pcm;
371 
372     return 0;
373 }
374 
375 
376 #if 0
377 int _snd_pcm_linear_open(snd_pcm_t **pcmp, const snd_pcm_config_t *pcm_config,
378         snd_pcm_stream_t stream, int mode)
379 {
380     int err;
381     const snd_pcm_linear_config_t *linear_config = (const snd_pcm_linear_config_t *)(pcm_config->config);
382     const snd_pcm_config_t *sconf = NULL;
383     snd_pcm_t *spcm;
384     snd_pcm_format_t sformat = SND_PCM_FORMAT_UNKNOWN;
385 
386 
387     err = snd_pcm_linear_open(pcmp, name, sformat, spcm, 1);
388     if (err < 0)
389         snd_pcm_close(spcm);
390     return err;
391 }
392 #endif
393