1 /*
2 * Copyright (c) 2021 Rockchip Electronics Co., Ltd.
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17 #include "alsa_audio.h"
18 static struct DevInfo devIn[SND_IN_SOUND_CARD_MAX];
19 static struct DevInfo devOut[SND_OUT_SOUND_CARD_MAX];
20
21 struct DevProcInfo SPEAKER_OUT_NAME[] = {
22 {"realtekrt5616c", NULL},
23 {"realtekrt5651co", "rt5651-aif1"},
24 {"realtekrt5670c", NULL},
25 {"realtekrt5672c", NULL},
26 {"realtekrt5678co", NULL},
27 {"rkhdmianalogsnd", NULL},
28 {"rockchipcx2072x", NULL},
29 {"rockchipes8316c", NULL},
30 {"rockchipes8323c", NULL},
31 {"rockchipes8388c", NULL},
32 {"rockchipes8396c", NULL},
33 {"rockchiprk", NULL},
34 {"rockchiprk809co", NULL},
35 {"rockchiprk817co", NULL},
36 {"rockchiprt5640c", "rt5640-aif1"},
37 {"rockchiprt5670c", NULL},
38 {"rockchiprt5672c", NULL},
39 {NULL, NULL}, /* Note! Must end with NULL, else will cause crash */
40 };
41
42 struct DevProcInfo HDMI_OUT_NAME[] = {
43 {"realtekrt5651co", "i2s-hifi"},
44 {"realtekrt5670co", "i2s-hifi"},
45 {"rkhdmidpsound", NULL},
46 {"hdmisound", NULL},
47 {"rockchiphdmi", NULL},
48 {"rockchiprt5640c", "i2s-hifi"},
49 {NULL, NULL}, /* Note! Must end with NULL, else will cause crash */
50 };
51
52
53 struct DevProcInfo SPDIF_OUT_NAME[] = {
54 {"ROCKCHIPSPDIF", "dit-hifi"},
55 {"rockchipspdif", NULL},
56 {"rockchipcdndp", NULL},
57 {NULL, NULL}, /* Note! Must end with NULL, else will cause crash */
58 };
59
60 struct DevProcInfo BT_OUT_NAME[] = {
61 {"rockchipbt", NULL},
62 {NULL, NULL}, /* Note! Must end with NULL, else will cause crash */
63 };
64
65 struct DevProcInfo MIC_IN_NAME[] = {
66 {"realtekrt5616c", NULL},
67 {"realtekrt5651co", "rt5651-aif1"},
68 {"realtekrt5670c", NULL},
69 {"realtekrt5672c", NULL},
70 {"realtekrt5678co", NULL},
71 {"rockchipes8316c", NULL},
72 {"rockchipes8323c", NULL},
73 {"rockchipes8396c", NULL},
74 {"rockchipes7210", NULL},
75 {"rockchipes7243", NULL},
76 {"rockchiprk", NULL},
77 {"rockchiprk809co", NULL},
78 {"rockchiprk817co", NULL},
79 {"rockchiprt5640c", NULL},
80 {"rockchiprt5670c", NULL},
81 {"rockchiprt5672c", NULL},
82 {NULL, NULL}, /* Note! Must end with NULL, else will cause crash */
83 };
84
85
86 struct DevProcInfo HDMI_IN_NAME[] = {
87 {"realtekrt5651co", "tc358749x-audio"},
88 {"hdmiin", NULL},
89 {NULL, NULL}, /* Note! Must end with NULL, else will cause crash */
90 };
91
92 struct DevProcInfo BT_IN_NAME[] = {
93 {"rockchipbt", NULL},
94 {NULL, NULL}, /* Note! Must end with NULL, else will cause crash */
95 };
96 static struct pcm_config g_renderPcmCfg;
97
98 struct RenderParamcheckList {
99 enum pcm_param param;
100 uint32_t value;
101 char *paramName;
102 char *paramUnit;
103 };
SetDefaultDevInfo(struct DevInfo * info,int32_t size,int32_t rid)104 static void SetDefaultDevInfo(struct DevInfo *info, int32_t size, int32_t rid)
105 {
106 for (int32_t i = 0; i < size; i++) {
107 if (rid) {
108 info[i].id = NULL;
109 }
110 info[i].card = (int32_t)SND_OUT_SOUND_CARD_UNKNOWN;
111 }
112 }
113
name_match(const char * dst,const char * src)114 static int32_t name_match(const char* dst, const char* src)
115 {
116 int32_t score = 0;
117 if (!strcmp(dst, src)) {
118 score = 100; // match 100%, total equal
119 } else if (strstr(dst, src)) {
120 score = 50; // match 50%, part equal
121 }
122
123 return score;
124 }
125
dev_id_match(const char * info,const char * did)126 static bool dev_id_match(const char *info, const char *did)
127 {
128 const char *deli = "id:";
129 char *id;
130 int32_t idx = 0;
131
132 if (!did) {
133 return true;
134 }
135 if (!info) {
136 return false;
137 }
138 /* find str like-> id: ff880000.i2s-rt5651-aif1 rt5651-aif1-0 */
139 id = strstr(info, deli);
140 if (!id) {
141 return false;
142 }
143 id += strlen(deli);
144 while (id[idx] != '\0') {
145 if (id[idx] == '\r' || id[idx] == '\n') {
146 id[idx] = '\0';
147 break;
148 }
149 idx++;
150 }
151 if (strstr(id, did)) {
152 LOG_PARA_INFO("match dai!!!: %s", id, did);
153 return true;
154 }
155 return false;
156 }
157
GetSpecifiedDevicesCheck(struct DevInfo * devinfo,int32_t card,const char * id,struct DevProcInfo * match,int32_t * index)158 static bool GetSpecifiedDevicesCheck(struct DevInfo *devinfo, int32_t card,
159 const char *id, struct DevProcInfo *match, int32_t *index)
160 {
161 int32_t i = 0;
162 int32_t better = 0;
163 /* parse card id */
164 if (!match) {
165 return true; /* match any */
166 }
167 while (match[i].cid) {
168 int32_t score = name_match(id, match[i].cid);
169 if (score > better) {
170 better = score;
171 *index = i;
172 }
173 i++;
174 }
175
176 if (*index < 0) {
177 return false;
178 }
179 if (!match[*index].cid) {
180 return false;
181 }
182 if (!match[*index].did) { /* no exist dai info, exit */
183 devinfo->card = card;
184 devinfo->device = 0;
185 LOG_PARA_INFO("%s card, got card=%d,device=%d", devinfo->id,
186 devinfo->card, devinfo->device);
187 return true;
188 }
189 return true;
190 }
191
GetSpecifiedOutDev(struct DevInfo * devinfo,int32_t card,const char * id,struct DevProcInfo * match)192 static bool GetSpecifiedOutDev(struct DevInfo *devinfo, int32_t card,
193 const char *id, struct DevProcInfo *match)
194 {
195 int32_t device;
196 char deviceInfoPath[32];
197 char info[256];
198 size_t len;
199 FILE* file = NULL;
200 int32_t index = -1;
201 bool ret = GetSpecifiedDevicesCheck(devinfo, card, id, match, &index);
202 if (!ret) {
203 return false;
204 }
205 /* parse device id */
206 for (device = 0; device < SNDRV_DEVICES; device++) {
207 int32_t ret = sprintf_s(deviceInfoPath, sizeof(deviceInfoPath) - 1,
208 "proc/asound/card%d/pcm%dp/info", card, device);
209 if (ret < 0) {
210 LOG_PARA_INFO("out card %d devices %d info path sprintf failed", card, device);
211 break;
212 }
213 if (access(deviceInfoPath, 0)) {
214 LOG_PARA_INFO("No exist %s, break and finish parsing", deviceInfoPath);
215 break;
216 }
217 file = fopen(deviceInfoPath, "r");
218 if (!file) {
219 LOG_PARA_INFO("Could reading %s property", deviceInfoPath);
220 continue;
221 }
222 len = fread(info, sizeof(char), sizeof(info) / sizeof(char), file);
223 if (fclose(file)) {
224 LOG_FUN_ERR("fclose(%s) failed", deviceInfoPath);
225 }
226 if (len == 0 || len > sizeof(info) / sizeof(char)) {
227 continue;
228 }
229 info[len - 1] = '\0';
230 /* parse device dai */
231 if (dev_id_match(info, match[index].did)) {
232 devinfo->card = card;
233 devinfo->device = device;
234 LOG_PARA_INFO("%s card, got card=%d,device=%d", devinfo->id,
235 devinfo->card, devinfo->device);
236 return true;
237 }
238 }
239 return false;
240 }
241
GetSpecifiedInDev(struct DevInfo * devinfo,int32_t card,const char * id,struct DevProcInfo * match)242 static bool GetSpecifiedInDev(struct DevInfo *devinfo, int32_t card,
243 const char *id, struct DevProcInfo *match)
244 {
245 int32_t device;
246 char deviceInfoPath[32];
247 char info[256];
248 size_t len;
249 FILE* file = NULL;
250 int32_t index = -1;
251 bool ret = GetSpecifiedDevicesCheck(devinfo, card, id, match, &index);
252 if (!ret) {
253 return false;
254 }
255 /* parse device id */
256 for (device = 0; device < SNDRV_DEVICES; device++) {
257 int32_t ret = sprintf_s(deviceInfoPath, sizeof(deviceInfoPath) - 1,
258 "proc/asound/card%d/pcm%dc/info", card, device);
259 if (ret < 0) {
260 LOG_PARA_INFO("in card %d devices %d info path sprintf failed", card, device);
261 break;
262 }
263 if (access(deviceInfoPath, 0)) {
264 LOG_PARA_INFO("No exist %s, break and finish parsing", deviceInfoPath);
265 break;
266 }
267 file = fopen(deviceInfoPath, "r");
268 if (!file) {
269 LOG_PARA_INFO("Could reading %s property", deviceInfoPath);
270 continue;
271 }
272 len = fread(info, sizeof(char), sizeof(info) / sizeof(char), file);
273 if (fclose(file)) {
274 LOG_FUN_ERR("fclose(%s) failed", deviceInfoPath);
275 }
276 if (len == 0 || len > sizeof(info) / sizeof(char)) {
277 continue;
278 }
279 info[len - 1] = '\0';
280 /* parse device dai */
281 if (dev_id_match(info, match[index].did)) {
282 devinfo->card = card;
283 devinfo->device = device;
284 LOG_PARA_INFO("%s card, got card=%d,device=%d", devinfo->id,
285 devinfo->card, devinfo->device);
286 return true;
287 }
288 }
289 return false;
290 }
291
dumpdev_info(const char * tag,struct DevInfo * devInfo,int32_t count)292 static void dumpdev_info(const char *tag, struct DevInfo *devInfo, int32_t count)
293 {
294 LOG_PARA_INFO("dump %s device info", tag);
295 for (int32_t i = 0; i < count; i++) {
296 if (devInfo[i].id && devInfo[i].card != SND_OUT_SOUND_CARD_UNKNOWN) {
297 LOG_PARA_INFO("dev_info %s card=%d, device:%d",
298 devInfo[i].id, devInfo[i].card, devInfo[i].device);
299 }
300 }
301 }
302
ReadInSoundCard(void)303 void ReadInSoundCard(void)
304 {
305 char sndCardId[32];
306 char CardIdInfo[20];
307 size_t cardIdLen;
308 FILE* file = NULL;
309 devIn[SND_IN_SOUND_CARD_MIC].id = "MIC";
310 devIn[SND_IN_SOUND_CARD_BT].id = "BT";
311 SetDefaultDevInfo(devIn, SND_IN_SOUND_CARD_UNKNOWN, 0);
312 for (int32_t card = 0; card < SNDRV_CARDS; card++) {
313 int32_t ret = sprintf_s(sndCardId, sizeof(sndCardId) - 1, "proc/asound/card%d/id", card);
314 if (ret < 0) {
315 LOG_PARA_INFO("in card %d idinfo path sprintf failed", card);
316 break;
317 }
318 if (access(sndCardId, 0)) {
319 LOG_PARA_INFO("No exist %s, break and finish parsing", sndCardId);
320 break;
321 }
322 file = fopen(sndCardId, "r");
323 if (!file) {
324 LOG_PARA_INFO("Could reading %s property", sndCardId);
325 continue;
326 }
327 cardIdLen = fread(CardIdInfo, sizeof(char), sizeof(CardIdInfo) / sizeof(char), file);
328 if (fclose(file)) {
329 LOG_FUN_ERR("fclose(%s) failed", sndCardId);
330 }
331 if (cardIdLen == 0 || cardIdLen > sizeof(CardIdInfo) / sizeof(char)) {
332 continue;
333 }
334 CardIdInfo[cardIdLen - 1] = '\0';
335 GetSpecifiedInDev(&devIn[SND_IN_SOUND_CARD_MIC], card, CardIdInfo, MIC_IN_NAME);
336 /* set HDMI audio input info if need hdmi audio input */
337 GetSpecifiedInDev(&devIn[SND_IN_SOUND_CARD_HDMI], card, CardIdInfo, HDMI_IN_NAME);
338 GetSpecifiedInDev(&devIn[SND_IN_SOUND_CARD_BT], card, CardIdInfo, BT_IN_NAME);
339 }
340 dumpdev_info("in", devIn, SND_IN_SOUND_CARD_MAX);
341 return;
342 }
343
ReadOutSoundCard(void)344 void ReadOutSoundCard(void)
345 {
346 char sndCardId[32];
347 char CardIdInfo[20];
348 size_t cardIdLen;
349 FILE* file = NULL;
350 devOut[SND_OUT_SOUND_CARD_SPEAKER].id = "SPEAKER";
351 devOut[SND_OUT_SOUND_CARD_HDMI].id = "HDMI";
352 devOut[SND_OUT_SOUND_CARD_SPDIF].id = "SPDIF";
353 devOut[SND_OUT_SOUND_CARD_BT].id = "BT";
354 SetDefaultDevInfo(devOut, SND_OUT_SOUND_CARD_UNKNOWN, 0);
355 for (int32_t card = 0; card < SNDRV_CARDS; card++) {
356 int32_t ret = sprintf_s(sndCardId, sizeof(sndCardId) - 1, "proc/asound/card%d/id", card);
357 if (ret < 0) {
358 LOG_PARA_INFO("out card %d idinfo path sprintf failed", card);
359 break;
360 }
361 if (access(sndCardId, 0)) {
362 LOG_PARA_INFO("No exist %s, break and finish parsing", sndCardId);
363 break;
364 }
365 file = fopen(sndCardId, "r");
366 if (!file) {
367 LOG_PARA_INFO("Could reading %s property", sndCardId);
368 continue;
369 }
370 cardIdLen = fread(CardIdInfo, sizeof(char), sizeof(CardIdInfo) / sizeof(char), file);
371 if (fclose(file)) {
372 LOG_FUN_ERR("fclose(%s) failed", sndCardId);
373 }
374 if (cardIdLen == 0 || cardIdLen > sizeof(CardIdInfo) / sizeof(char)) {
375 continue;
376 }
377 CardIdInfo[cardIdLen - 1] = '\0';
378 LOG_PARA_INFO("card%d id:%s", card, CardIdInfo);
379 GetSpecifiedOutDev(&devOut[SND_OUT_SOUND_CARD_SPEAKER], card, CardIdInfo, SPEAKER_OUT_NAME);
380 GetSpecifiedOutDev(&devOut[SND_OUT_SOUND_CARD_HDMI], card, CardIdInfo, HDMI_OUT_NAME);
381 GetSpecifiedOutDev(&devOut[SND_OUT_SOUND_CARD_SPDIF], card, CardIdInfo, SPDIF_OUT_NAME);
382 GetSpecifiedOutDev(&devOut[SND_OUT_SOUND_CARD_BT], card, CardIdInfo, BT_OUT_NAME);
383 }
384 dumpdev_info("out", devOut, SND_OUT_SOUND_CARD_MAX);
385 return;
386 }
387
GetOutDevInfo(int32_t index,struct DevInfo * devInfo)388 int32_t GetOutDevInfo(int32_t index, struct DevInfo* devInfo)
389 {
390 for (int32_t i = 0; i < SND_OUT_SOUND_CARD_MAX; i++) {
391 if (i == index) {
392 devInfo->card = devOut[i].card;
393 devInfo->device = devOut[i].device;
394 return true;
395 }
396 }
397 return false;
398 }
399
GetInDevInfo(int32_t index,struct DevInfo * devInfo)400 int32_t GetInDevInfo(int32_t index, struct DevInfo* devInfo)
401 {
402 for (int32_t i = 0; i < SND_IN_SOUND_CARD_MAX; i++) {
403 if (i == index) {
404 devInfo->card = devIn[i].card;
405 devInfo->device = devIn[i].device;
406 return true;
407 }
408 }
409 return false;
410 }
411
AudioRenderParamCheck(struct pcm_params * params,uint32_t param,uint32_t value,char * paramName,char * paramUnit)412 int32_t AudioRenderParamCheck(struct pcm_params *params, uint32_t param, uint32_t value,
413 char *paramName, char *paramUnit)
414 {
415 int32_t paramIsOk = true;
416
417 uint32_t min = pcm_params_get_min(params, param);
418 uint32_t max = pcm_params_get_max(params, param);
419 if ((value < min) || (value > max)) {
420 LOG_FUN_ERR("device supports %s\t min=%u%s \t max=%u%s, pls check it.\n", paramName,
421 min, paramUnit, max, paramUnit);
422 paramIsOk = false;
423 }
424
425 return paramIsOk;
426 }
427
IsPlayable(uint32_t card,uint32_t device,uint32_t bits)428 int32_t IsPlayable(uint32_t card, uint32_t device, uint32_t bits)
429 {
430 struct pcm_params *params;
431 int32_t paramIsOk = true;
432
433 params = pcm_params_get(card, device, PCM_OUT);
434 if (params == NULL) {
435 LOG_FUN_ERR("Unable to open PCM device (/dev/snd/pcmC%uD%up)\n", card, device);
436 return 0;
437 }
438 struct RenderParamcheckList g_RenderParamcheckList[] = {
439 {PCM_PARAM_RATE, g_renderPcmCfg.rate, "Sample rate", "Hz"},
440 {PCM_PARAM_CHANNELS, g_renderPcmCfg.channels, "Sample", "channels"},
441 {PCM_PARAM_SAMPLE_BITS, bits, "Bitrate", "bits"},
442 {PCM_PARAM_PERIOD_SIZE, g_renderPcmCfg.period_size, "Period size", "frames"},
443 {PCM_PARAM_PERIODS, g_renderPcmCfg.period_count, "Period count", "periods"},
444 };
445 int32_t checkNums = sizeof(g_RenderParamcheckList) / sizeof(struct RenderParamcheckList);
446 for (int32_t i = 0; i < checkNums; i++) {
447 paramIsOk &= AudioRenderParamCheck(params, g_RenderParamcheckList[i].param, g_RenderParamcheckList[i].value,
448 g_RenderParamcheckList[i].paramName, g_RenderParamcheckList[i].paramUnit);
449 }
450
451 pcm_params_free(params);
452 return paramIsOk;
453 }
454
initRenderFormat(uint32_t bits)455 void initRenderFormat(uint32_t bits)
456 {
457 switch (bits) {
458 case TINYALSAPCM_32_BIT:
459 g_renderPcmCfg.format = PCM_FORMAT_S32_LE;
460 return;
461 case TINYALSAPCM_24_BIT:
462 g_renderPcmCfg.format = PCM_FORMAT_S24_3LE;
463 return;
464 case TINYALSAPCM_8_BIT:
465 g_renderPcmCfg.format = PCM_FORMAT_S8;
466 return;
467 default:
468 case TINYALSAPCM_16_BIT:
469 g_renderPcmCfg.format = PCM_FORMAT_S16_LE;
470 return;
471 }
472 }
473
TinyAlsaPlayParamInit(uint32_t channels,uint32_t rate,uint32_t periodSize,uint32_t periodCount)474 void TinyAlsaPlayParamInit(uint32_t channels, uint32_t rate,
475 uint32_t periodSize, uint32_t periodCount)
476 {
477 (void)memset_s(&g_renderPcmCfg, sizeof(struct pcm_config), 0, sizeof(struct pcm_config));
478 g_renderPcmCfg.channels = channels;
479 g_renderPcmCfg.rate = rate;
480 g_renderPcmCfg.period_size = periodSize;
481 g_renderPcmCfg.period_count = periodCount;
482
483 g_renderPcmCfg.start_threshold = 0;
484 g_renderPcmCfg.stop_threshold = 0;
485 g_renderPcmCfg.silence_threshold = 0;
486 return;
487 }
488
RenderSample(struct pcm ** pcm,struct PcmRenderParam * param)489 void RenderSample(struct pcm **pcm, struct PcmRenderParam *param)
490 {
491 TinyAlsaPlayParamInit(param->channels, param->rate, param->periodSize, param->periodCount);
492 initRenderFormat(param->bits);
493 if (!IsPlayable(param->card, param->device, param->bits)) {
494 return;
495 }
496
497 *pcm = pcm_open(param->card, param->device, PCM_OUT, &g_renderPcmCfg);
498 if (((*pcm) == NULL) || !pcm_is_ready(*pcm)) {
499 LOG_FUN_ERR("Unable to open PCM device (/dev/snd/pcmC%uD%up):(%s)\n",
500 param->card, param->device, pcm_get_error(*pcm));
501 return;
502 }
503 LOG_FUN_ERR("Playing sample: %u ch, %u hz, %u\n", param->channels, param->rate, param->bits);
504 }
505
CaptureSample(struct pcm ** pcm,struct PcmCaptureParam * param)506 uint32_t CaptureSample(struct pcm **pcm, struct PcmCaptureParam *param)
507 {
508 struct pcm_config config;
509 (void)memset_s(&config, sizeof(config), 0, sizeof(config));
510 config.channels = param->channels;
511 config.rate = param->rate;
512 config.period_size = param->periodSize;
513 config.period_count = param->periodCount;
514 config.format = param->format;
515 config.start_threshold = 0;
516 config.stop_threshold = 0;
517 config.silence_threshold = 0;
518 *pcm = pcm_open(param->card, param->device, PCM_IN, &config);
519 if (((*pcm) == NULL) || !pcm_is_ready(*pcm)) {
520 LOG_FUN_ERR("Unable to open PCM device (/dev/snd/pcmC%uD%uc):(%s)\n",
521 param->card, param->device, pcm_get_error(*pcm));
522 return -1;
523 }
524 return 0;
525 }
526