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