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 <string.h>
33 #include <sound/snd_core.h>
34 #include <sound/snd_pcm.h>
35 #include <sound/snd_misc.h>
36 #include <aw_common.h>
37 #include <sound/pcm_common.h>
38
39
40
41 struct pcm_format_data {
42 unsigned char width; /* bit width */
43 unsigned char phys; /* physical bit width */
44 signed char le; /* 0 = big-endian, 1 = little-endian, -1 = others */
45 signed char signd; /* 0 = unsigned, 1 = signed, -1 = others */
46 unsigned char silence[8]; /* silence data to fill */
47 };
48
49 static struct pcm_format_data pcm_formats[(int)SND_PCM_FORMAT_LAST+1] = {
50 [SND_PCM_FORMAT_S8] = {
51 .width = 8, .phys = 8, .le = -1, .signd = 1,
52 .silence = {},
53 },
54 [SND_PCM_FORMAT_U8] = {
55 .width = 8, .phys = 8, .le = -1, .signd = 0,
56 .silence = { 0x80 },
57 },
58 [SND_PCM_FORMAT_S16_LE] = {
59 .width = 16, .phys = 16, .le = 1, .signd = 1,
60 .silence = {},
61 },
62 [SND_PCM_FORMAT_S16_BE] = {
63 .width = 16, .phys = 16, .le = 0, .signd = 1,
64 .silence = {},
65 },
66 [SND_PCM_FORMAT_U16_LE] = {
67 .width = 16, .phys = 16, .le = 1, .signd = 0,
68 .silence = { 0x00, 0x80 },
69 },
70 [SND_PCM_FORMAT_U16_BE] = {
71 .width = 16, .phys = 16, .le = 0, .signd = 0,
72 .silence = { 0x80, 0x00 },
73 },
74 [SND_PCM_FORMAT_S24_LE] = {
75 .width = 24, .phys = 32, .le = 1, .signd = 1,
76 .silence = {},
77 },
78 [SND_PCM_FORMAT_S24_BE] = {
79 .width = 24, .phys = 32, .le = 0, .signd = 1,
80 .silence = {},
81 },
82 [SND_PCM_FORMAT_U24_LE] = {
83 .width = 24, .phys = 32, .le = 1, .signd = 0,
84 .silence = { 0x00, 0x00, 0x80 },
85 },
86 [SND_PCM_FORMAT_U24_BE] = {
87 .width = 24, .phys = 32, .le = 0, .signd = 0,
88 .silence = { 0x00, 0x80, 0x00, 0x00 },
89 },
90 [SND_PCM_FORMAT_S32_LE] = {
91 .width = 32, .phys = 32, .le = 1, .signd = 1,
92 .silence = {},
93 },
94 [SND_PCM_FORMAT_S32_BE] = {
95 .width = 32, .phys = 32, .le = 0, .signd = 1,
96 .silence = {},
97 },
98 [SND_PCM_FORMAT_U32_LE] = {
99 .width = 32, .phys = 32, .le = 1, .signd = 0,
100 .silence = { 0x00, 0x00, 0x00, 0x80 },
101 },
102 [SND_PCM_FORMAT_U32_BE] = {
103 .width = 32, .phys = 32, .le = 0, .signd = 0,
104 .silence = { 0x80, 0x00, 0x00, 0x00 },
105 },
106 };
107
108
snd_pcm_format_physical_width(snd_pcm_format_t format)109 int snd_pcm_format_physical_width(snd_pcm_format_t format)
110 {
111 int val;
112 if ((int)format < 0 || (int)format > (int)SND_PCM_FORMAT_LAST)
113 return -EINVAL;
114 if ((val = pcm_formats[(int)format].phys) == 0)
115 return -EINVAL;
116 return val;
117 }
118
119
snd_pcm_rate_mask_sanitize(unsigned int rates)120 static unsigned int snd_pcm_rate_mask_sanitize(unsigned int rates)
121 {
122 if (rates & SNDRV_PCM_RATE_CONTINUOUS)
123 return SNDRV_PCM_RATE_CONTINUOUS;
124 else if (rates & SNDRV_PCM_RATE_KNOT)
125 return SNDRV_PCM_RATE_KNOT;
126 return rates;
127 }
128
snd_pcm_rate_mask_intersect(unsigned int rates_a,unsigned int rates_b)129 unsigned int snd_pcm_rate_mask_intersect(unsigned int rates_a, unsigned int rates_b)
130 {
131 rates_a = snd_pcm_rate_mask_sanitize(rates_a);
132 rates_b = snd_pcm_rate_mask_sanitize(rates_b);
133
134 if (rates_a & SNDRV_PCM_RATE_CONTINUOUS)
135 return rates_b;
136 else if (rates_b & SNDRV_PCM_RATE_CONTINUOUS)
137 return rates_a;
138 else if (rates_a & SNDRV_PCM_RATE_KNOT)
139 return rates_b;
140 else if (rates_b & SNDRV_PCM_RATE_KNOT)
141 return rates_a;
142 return rates_a & rates_b;
143 }
144
145 static unsigned int rates[] = { 5512, 8000, 11025, 16000, 22050, 32000, 44100,
146 48000, 64000, 88200, 96000, 176400, 192000 };
147
148 const struct snd_pcm_hw_constraint_list snd_pcm_known_rates = {
149 .count = ARRAY_SIZE(rates),
150 .list = rates,
151 };
152
snd_pcm_limit_hw_rates(struct snd_pcm_runtime * runtime)153 int snd_pcm_limit_hw_rates(struct snd_pcm_runtime *runtime)
154 {
155 int i;
156 for (i = 0; i < (int)snd_pcm_known_rates.count; i++) {
157 if (runtime->hw.rates & (1 << i)) {
158 runtime->hw.rate_min = snd_pcm_known_rates.list[i];
159 break;
160 }
161 }
162 for (i = (int)snd_pcm_known_rates.count - 1; i >= 0; i--) {
163 if (runtime->hw.rates & (1 << i)) {
164 runtime->hw.rate_max = snd_pcm_known_rates.list[i];
165 break;
166 }
167 }
168 return 0;
169 }
170
snd_pcm_format_set_silence(snd_pcm_format_t format,void * data,unsigned int samples)171 int snd_pcm_format_set_silence(snd_pcm_format_t format, void *data, unsigned int samples)
172 {
173 int width;
174 unsigned char *dst, *pat;
175
176 if (format < 0 || format > SND_PCM_FORMAT_LAST)
177 return -EINVAL;
178 if (samples == 0)
179 return 0;
180 width = pcm_formats[format].phys;
181 pat = pcm_formats[format].silence;
182 if (!width)
183 return -EINVAL;
184 /* signed or 1 byte data */
185 if (pcm_formats[format].signd == 1 || width <= 8) {
186 unsigned int bytes = samples * width / 8;
187 memset(data, *pat, bytes);
188 }
189
190 /* non-zero samples, fill using a loop */
191 width /= 8;
192 dst = data;
193 #if 0
194 while (samples--) {
195 memcpy(dst, pat, width);
196 dst += width;
197 }
198 #else
199 /* a bit optimization for constant width */
200 switch (width) {
201 case 2:
202 while (samples--) {
203 memcpy(dst, pat, 2);
204 dst += 2;
205 }
206 break;
207 case 3:
208 while (samples--) {
209 memcpy(dst, pat, 3);
210 dst += 3;
211 }
212 break;
213 case 4:
214 while (samples--) {
215 memcpy(dst, pat, 4);
216 dst += 4;
217 }
218 break;
219 case 8:
220 while (samples--) {
221 memcpy(dst, pat, 8);
222 dst += 8;
223 }
224 break;
225 }
226 #endif
227 return 0;
228 }
229