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