1 /*
2 * Copyright (C) 2018 The Android Open Source Project
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 #define LOG_TAG "audio_hw_waves"
18 /*#define LOG_NDEBUG 0*/
19
20 #include <audio_hw.h>
21 #include <cutils/str_parms.h>
22 #include <dlfcn.h>
23 #include <log/log.h>
24 #include <math.h>
25 #include <platform_api.h>
26 #include <pthread.h>
27 #include <stdlib.h>
28 #include <string.h>
29 #include <system/audio.h>
30 #include <unistd.h>
31
32 #include "audio_extn.h"
33 #include "maxxaudio.h"
34
35 #define LIB_MA_PARAM "libmaxxaudioqdsp.so"
36 #define LIB_MA_PATH "vendor/lib/"
37 #define PRESET_PATH "/vendor/etc"
38 #define MPS_BASE_STRING "default"
39 #define USER_PRESET_PATH ""
40 #define CONFIG_PATH "/vendor/etc/maxx_conf.ini"
41 #define CAL_PRESIST_STR "cal_persist"
42 #define CAL_SAMPLERATE_STR "cal_samplerate"
43
44 #define MA_QDSP_PARAM_INIT "maxxaudio_qdsp_initialize"
45 #define MA_QDSP_PARAM_DEINIT "maxxaudio_qdsp_uninitialize"
46 #define MA_QDSP_SET_LR_SWAP "maxxaudio_qdsp_set_lr_swap"
47 #define MA_QDSP_SET_MODE "maxxaudio_qdsp_set_sound_mode"
48 #define MA_QDSP_SET_VOL "maxxaudio_qdsp_set_volume"
49 #define MA_QDSP_SET_VOLT "maxxaudio_qdsp_set_volume_table"
50
51 #define SUPPORT_DEV "Blackbird"
52 #define SUPPORTED_USB 0x01
53
54 struct ma_audio_cal_settings {
55 int app_type;
56 audio_devices_t device;
57 };
58
59 struct ma_state {
60 float vol;
61 bool active;
62 };
63
64 typedef enum MA_STREAM_TYPE {
65 STREAM_MIN_STREAM_TYPES,
66 STREAM_VOICE = STREAM_MIN_STREAM_TYPES,
67 STREAM_SYSTEM,
68 STREAM_RING,
69 STREAM_MUSIC,
70 STREAM_ALARM,
71 STREAM_NOTIFICATION ,
72 STREAM_MAX_TYPES,
73 } ma_stream_type_t;
74
75 typedef enum MA_CMD {
76 MA_CMD_VOL,
77 MA_CMD_SWAP_ENABLE,
78 MA_CMD_SWAP_DISABLE,
79 } ma_cmd_t;
80
81 typedef void *ma_audio_cal_handle_t;
82 typedef int (*set_audio_cal_t)(const char *);
83
84 typedef bool (*ma_param_init_t)(ma_audio_cal_handle_t *, const char *,
85 const char *, const char *, set_audio_cal_t);
86
87 typedef bool (*ma_param_deinit_t)(ma_audio_cal_handle_t *);
88
89 typedef bool (*ma_set_lr_swap_t)(ma_audio_cal_handle_t,
90 const struct ma_audio_cal_settings *, bool);
91
92 typedef bool (*ma_set_sound_mode_t)(ma_audio_cal_handle_t,
93 const struct ma_audio_cal_settings *,
94 unsigned int);
95
96 typedef bool (*ma_set_volume_t)(ma_audio_cal_handle_t,
97 const struct ma_audio_cal_settings *, double);
98
99 typedef bool (*ma_set_volume_table_t)(ma_audio_cal_handle_t,
100 const struct ma_audio_cal_settings *,
101 size_t, struct ma_state *);
102
103 struct ma_platform_data {
104 void *waves_handle;
105 void *platform;
106 pthread_mutex_t lock;
107 ma_param_init_t ma_param_init;
108 ma_param_deinit_t ma_param_deinit;
109 ma_set_lr_swap_t ma_set_lr_swap;
110 ma_set_sound_mode_t ma_set_sound_mode;
111 ma_set_volume_t ma_set_volume;
112 ma_set_volume_table_t ma_set_volume_table;
113 };
114
115 ma_audio_cal_handle_t g_ma_audio_cal_handle = NULL;
116 static uint16_t g_supported_dev = 0;
117 static struct ma_state ma_cur_state_table[STREAM_MAX_TYPES];
118 static struct ma_platform_data *my_data = NULL;
119
set_audio_cal(const char * audio_cal)120 static int set_audio_cal(const char *audio_cal)
121 {
122 ALOGV("set_audio_cal: %s", audio_cal);
123
124 return platform_set_parameters(my_data->platform,
125 str_parms_create_str(audio_cal));
126 }
127
ma_set_lr_swap_l(const struct ma_audio_cal_settings * audio_cal_settings,bool swap)128 static bool ma_set_lr_swap_l(
129 const struct ma_audio_cal_settings *audio_cal_settings, bool swap)
130 {
131 return my_data->ma_set_lr_swap(g_ma_audio_cal_handle,
132 audio_cal_settings, swap);
133 }
134
ma_set_sound_mode_l(const struct ma_audio_cal_settings * audio_cal_settings,int sound_mode)135 static bool ma_set_sound_mode_l(
136 const struct ma_audio_cal_settings *audio_cal_settings, int sound_mode)
137 {
138 return my_data->ma_set_sound_mode(g_ma_audio_cal_handle,
139 audio_cal_settings, sound_mode);
140 }
141
ma_set_volume_l(const struct ma_audio_cal_settings * audio_cal_settings,double volume)142 static bool ma_set_volume_l(
143 const struct ma_audio_cal_settings *audio_cal_settings, double volume)
144 {
145 return my_data->ma_set_volume(g_ma_audio_cal_handle, audio_cal_settings,
146 volume);
147 }
148
ma_set_volume_table_l(const struct ma_audio_cal_settings * audio_cal_settings,size_t num_streams,struct ma_state * volume_table)149 static bool ma_set_volume_table_l(
150 const struct ma_audio_cal_settings *audio_cal_settings,
151 size_t num_streams, struct ma_state *volume_table)
152 {
153 return my_data->ma_set_volume_table(g_ma_audio_cal_handle,
154 audio_cal_settings, num_streams,
155 volume_table);
156 }
157
valid_usecase(struct audio_usecase * usecase)158 static inline bool valid_usecase(struct audio_usecase *usecase)
159 {
160 if ((usecase->type == PCM_PLAYBACK) &&
161 /* supported usecases */
162 ((usecase->id == USECASE_AUDIO_PLAYBACK_DEEP_BUFFER) ||
163 (usecase->id == USECASE_AUDIO_PLAYBACK_LOW_LATENCY) ||
164 (usecase->id == USECASE_AUDIO_PLAYBACK_OFFLOAD)) &&
165 /* support devices */
166 ((usecase->devices & AUDIO_DEVICE_OUT_SPEAKER) ||
167 (usecase->devices & AUDIO_DEVICE_OUT_SPEAKER_SAFE) ||
168 /* TODO: enable A2DP when it is ready */
169 (usecase->devices & AUDIO_DEVICE_OUT_ALL_USB)))
170
171 return true;
172
173 ALOGV("%s: not support type %d usecase %d device %d",
174 __func__, usecase->type, usecase->id, usecase->devices);
175
176 return false;
177 }
178
179 // already hold lock
is_active()180 static inline bool is_active()
181 {
182 ma_stream_type_t i = 0;
183
184 for (i = 0; i < STREAM_MAX_TYPES; i++)
185 if (ma_cur_state_table[i].active &&
186 (ma_cur_state_table[i].vol != 0))
187 return true;
188
189 return false;
190 }
191
check_and_send_all_audio_cal(struct audio_device * adev,ma_cmd_t cmd)192 static bool check_and_send_all_audio_cal(struct audio_device *adev, ma_cmd_t cmd)
193 {
194 int i = 0;
195 bool ret = false;
196 float vol = 0;
197 struct listnode *node;
198 struct audio_usecase *usecase;
199 struct ma_audio_cal_settings *ma_cal = NULL;
200
201 // alloct
202 ma_cal = (struct ma_audio_cal_settings *)malloc(sizeof(struct ma_audio_cal_settings));
203
204 if (ma_cal == NULL) {
205 ALOGE("%s: ma_cal alloct fail", __func__);
206 return ret;
207 }
208
209 list_for_each(node, &adev->usecase_list) {
210 usecase = node_to_item(node, struct audio_usecase, list);
211 if (valid_usecase(usecase)) {
212 ma_cal->app_type = usecase->stream.out->app_type_cfg.app_type;
213 ma_cal->device = usecase->stream.out->devices;
214 ALOGV("%s: send usecase(%d) app_type(%d) device(%d)",
215 __func__, usecase->id, ma_cal->app_type, ma_cal->device);
216
217 switch (cmd) {
218 case MA_CMD_VOL:
219 ret = ma_set_volume_table_l(ma_cal, STREAM_MAX_TYPES,
220 ma_cur_state_table);
221 if (ret)
222 ALOGV("Waves: ma_set_volume_table_l success");
223 else
224 ALOGE("Waves: ma_set_volume_table_l %f returned with error.", vol);
225
226 ALOGV("%s: send volume table === Start", __func__);
227 for (i = 0; i < STREAM_MAX_TYPES; i++)
228 ALOGV("%s: stream(%d) volume(%f) active(%s)", __func__,
229 i, ma_cur_state_table[i].vol,
230 ma_cur_state_table[i].active ? "T" : "F");
231 ALOGV("%s: send volume table === End", __func__);
232 break;
233 case MA_CMD_SWAP_ENABLE:
234 ret = ma_set_lr_swap_l(ma_cal, true);
235 if (ret)
236 ALOGV("Waves: ma_set_lr_swap_l enable returned with success.");
237 else
238 ALOGE("Waves: ma_set_lr_swap_l enable returned with error.");
239 break;
240 case MA_CMD_SWAP_DISABLE:
241 ret = ma_set_lr_swap_l(ma_cal, false);
242 if (ret)
243 ALOGV("Waves: ma_set_lr_swap_l disable returned with success.");
244 else
245 ALOGE("Waves: ma_set_lr_swap_l disable returned with error.");
246 break;
247 default:
248 ALOGE("%s: unsupported cmd %d", __func__, cmd);
249 }
250 }
251 }
252 free(ma_cal);
253
254 return ret;
255 }
256
find_sup_dev(char * name)257 static bool find_sup_dev(char *name)
258 {
259 char *token;
260 const char s[2] = ",";
261 bool ret = false;
262 char sup_devs[128];
263
264 // the rule of comforming suppored dev's name
265 // 1. Both string len are equal
266 // 2. Both string content are equal
267
268 strncpy(sup_devs, SUPPORT_DEV, sizeof(sup_devs));
269 token = strtok(sup_devs, s);
270 while (token != NULL) {
271 if (strncmp(token, name, strlen(token)) == 0 &&
272 strlen(token) == strlen(name)) {
273 ALOGD("%s: support dev %s", __func__, token);
274 ret = true;
275 break;
276 }
277 token = strtok(NULL, s);
278 }
279
280 return ret;
281 }
282
ma_set_swap_l(struct audio_device * adev,bool enable)283 static void ma_set_swap_l(struct audio_device *adev, bool enable)
284 {
285 // do platform LR swap if it enables on Waves effect
286 // but there is no Waves implementation
287 if (!my_data) {
288 platform_check_and_set_swap_lr_channels(adev, enable);
289 ALOGV("%s: maxxaudio isn't initialized.", __func__);
290 return;
291 }
292
293 if (enable)
294 check_and_send_all_audio_cal(adev, MA_CMD_SWAP_ENABLE);
295 else
296 check_and_send_all_audio_cal(adev, MA_CMD_SWAP_DISABLE);
297 }
298
ma_support_usb(bool enable,int card)299 static void ma_support_usb(bool enable, int card)
300 {
301 char path[128];
302 char id[32];
303 int ret = 0;
304 int32_t fd = -1;
305 char *idd;
306
307 if (enable) {
308 ret = snprintf(path, sizeof(path), "/proc/asound/card%u/id", card);
309 if (ret < 0) {
310 ALOGE("%s: failed on snprintf (%d) to path %s\n",
311 __func__, ret, path);
312 goto done;
313 }
314 fd = open(path, O_RDONLY);
315 if (fd < 0) {
316 ALOGE("%s: error failed to open id file %s error: %d\n",
317 __func__, path, errno);
318 goto done;
319 }
320 if (read(fd, id, sizeof(id)) < 0) {
321 ALOGE("%s: file read error", __func__);
322 goto done;
323 }
324 //replace '\n' to '\0'
325 idd = strtok(id, "\n");
326
327 if (find_sup_dev(idd)) {
328 ALOGV("%s: support device name is %s", __func__, id);
329 g_supported_dev |= SUPPORTED_USB;
330 } else
331 ALOGV("%s: device %s isn't found from %s", __func__, id, SUPPORT_DEV);
332 } else {
333 g_supported_dev &= ~SUPPORTED_USB;
334 }
335
336 done:
337 if (fd >= 0) close(fd);
338 }
339
340 // adev_init lock held
audio_extn_ma_init(void * platform)341 void audio_extn_ma_init(void *platform)
342 {
343 ma_stream_type_t i = 0;
344 int ret = 0;
345 char lib_path[128] = {0};
346 char mps_path[128] = {0};
347 struct snd_card_split *snd_split_handle = NULL;
348 snd_split_handle = audio_extn_get_snd_card_split();
349
350 if (platform == NULL) {
351 ALOGE("%s: platform is NULL", __func__);
352 goto error;
353 }
354
355 if (my_data) { free(my_data); }
356 my_data = calloc(1, sizeof(struct ma_platform_data));
357 if (my_data == NULL) {
358 ALOGE("%s: ma_cal alloct fail", __func__);
359 goto error;
360 }
361
362 pthread_mutex_init(&my_data->lock, NULL);
363
364 my_data->platform = platform;
365 ret = snprintf(lib_path, sizeof(lib_path), "%s/%s", LIB_MA_PATH, LIB_MA_PARAM);
366 if (ret < 0) {
367 ALOGE("%s: snprintf failed for lib %s, ret %d", __func__, LIB_MA_PARAM, ret);
368 goto error;
369 }
370
371 my_data->waves_handle = dlopen(lib_path, RTLD_NOW);
372 if (my_data->waves_handle == NULL) {
373 ALOGE("%s: DLOPEN failed for %s", __func__, LIB_MA_PARAM);
374 goto error;
375 } else {
376 ALOGV("%s: DLOPEN successful for %s", __func__, LIB_MA_PARAM);
377
378 my_data->ma_param_init = (ma_param_init_t)dlsym(my_data->waves_handle,
379 MA_QDSP_PARAM_INIT);
380 if (!my_data->ma_param_init) {
381 ALOGE("%s: dlsym error %s for ma_param_init", __func__, dlerror());
382 goto error;
383 }
384
385 my_data->ma_param_deinit = (ma_param_deinit_t)dlsym(
386 my_data->waves_handle, MA_QDSP_PARAM_DEINIT);
387 if (!my_data->ma_param_deinit) {
388 ALOGE("%s: dlsym error %s for ma_param_deinit", __func__, dlerror());
389 goto error;
390 }
391
392 my_data->ma_set_lr_swap = (ma_set_lr_swap_t)dlsym(my_data->waves_handle,
393 MA_QDSP_SET_LR_SWAP);
394 if (!my_data->ma_set_lr_swap) {
395 ALOGE("%s: dlsym error %s for ma_set_lr_swap", __func__, dlerror());
396 goto error;
397 }
398
399 my_data->ma_set_sound_mode = (ma_set_sound_mode_t)dlsym(
400 my_data->waves_handle, MA_QDSP_SET_MODE);
401 if (!my_data->ma_set_sound_mode) {
402 ALOGE("%s: dlsym error %s for ma_set_sound_mode", __func__, dlerror());
403 goto error;
404 }
405
406 my_data->ma_set_volume = (ma_set_volume_t)dlsym(my_data->waves_handle,
407 MA_QDSP_SET_VOL);
408 if (!my_data->ma_set_volume) {
409 ALOGE("%s: dlsym error %s for ma_set_volume", __func__, dlerror());
410 goto error;
411 }
412
413 my_data->ma_set_volume_table = (ma_set_volume_table_t)dlsym(
414 my_data->waves_handle, MA_QDSP_SET_VOLT);
415 if (!my_data->ma_set_volume_table) {
416 ALOGE("%s: dlsym error %s for ma_set_volume_table", __func__, dlerror());
417 goto error;
418 }
419 }
420
421 /* get preset table */
422 if (snd_split_handle == NULL) {
423 snprintf(mps_path, sizeof(mps_path), "%s/%s.mps",
424 PRESET_PATH, MPS_BASE_STRING);
425 } else {
426 snprintf(mps_path, sizeof(mps_path), "%s/%s_%s.mps",
427 PRESET_PATH, MPS_BASE_STRING, snd_split_handle->form_factor);
428 }
429
430 /* check file */
431 if (access(mps_path, F_OK) < 0) {
432 ALOGW("%s: file %s isn't existed.", __func__, mps_path);
433 goto error;
434 } else
435 ALOGD("%s: Loading mps file: %s", __func__, mps_path);
436
437 /* TODO: check user preset table once the feature is enabled
438 if (access(USER_PRESET_PATH, F_OK) < 0 ){
439 ALOGW("%s: file %s isn't existed.", __func__, USER_PRESET_PATH);
440 goto error;
441 }
442 */
443 if (access(CONFIG_PATH, F_OK) < 0) {
444 ALOGW("%s: file %s isn't existed.", __func__, CONFIG_PATH);
445 goto error;
446 }
447
448 /* init ma parameter */
449 if (my_data->ma_param_init(&g_ma_audio_cal_handle,
450 mps_path,
451 USER_PRESET_PATH, /* unused */
452 CONFIG_PATH,
453 &set_audio_cal)) {
454 if (!g_ma_audio_cal_handle) {
455 ALOGE("%s: ma parameters initialize failed", __func__);
456 my_data->ma_param_deinit(&g_ma_audio_cal_handle);
457 goto error;
458 }
459 ALOGD("%s: ma parameters initialize successful", __func__);
460 } else {
461 ALOGE("%s: ma parameters initialize failed", __func__);
462 goto error;
463 }
464
465 /* init volume table */
466 for (i = 0; i < STREAM_MAX_TYPES; i++) {
467 ma_cur_state_table[i].vol = 0.0;
468 ma_cur_state_table[i].active = false;
469 }
470
471 return;
472
473 error:
474 if (my_data) { free(my_data); }
475 my_data = NULL;
476 }
477
478 //adev_init lock held
audio_extn_ma_deinit()479 void audio_extn_ma_deinit()
480 {
481 if (my_data) {
482 /* deinit ma parameter */
483 if (my_data->ma_param_deinit &&
484 my_data->ma_param_deinit(&g_ma_audio_cal_handle))
485 ALOGD("%s: ma parameters uninitialize successful", __func__);
486 else
487 ALOGD("%s: ma parameters uninitialize failed", __func__);
488
489 pthread_mutex_destroy(&my_data->lock);
490 free(my_data);
491 my_data = NULL;
492 }
493 }
494
495 // adev_init and adev lock held
audio_extn_ma_set_state(struct audio_device * adev,int stream_type,float vol,bool active)496 bool audio_extn_ma_set_state(struct audio_device *adev, int stream_type,
497 float vol, bool active)
498 {
499 bool ret = false;
500 ma_stream_type_t stype = (ma_stream_type_t)stream_type;
501
502 ALOGV("%s: stream[%d] vol[%f] active[%s]",
503 __func__, stream_type, vol, active ? "true" : "false");
504
505 if (!my_data) {
506 ALOGV("%s: maxxaudio isn't initialized.", __func__);
507 return ret;
508 }
509
510 // update condition
511 // 1. start track: active and volume isn't zero
512 // 2. stop track: no tracks are active
513 if ((active && vol != 0) ||
514 (!active)) {
515 pthread_mutex_lock(&my_data->lock);
516
517 ma_cur_state_table[stype].vol = vol;
518 ma_cur_state_table[stype].active = active;
519 if (is_active())
520 ret = check_and_send_all_audio_cal(adev, MA_CMD_VOL);
521
522 pthread_mutex_unlock(&my_data->lock);
523 }
524
525 return ret;
526 }
527
audio_extn_ma_set_device(struct audio_usecase * usecase)528 void audio_extn_ma_set_device(struct audio_usecase *usecase)
529 {
530 int i = 0;
531 int u_index = -1;
532 float vol = 0;
533 struct ma_audio_cal_settings *ma_cal = NULL;
534
535 if (!my_data) {
536 ALOGV("%s: maxxaudio isn't initialized.", __func__);
537 return;
538 }
539
540 if (!valid_usecase(usecase)) {
541 ALOGV("%s: %d is not supported usecase", __func__, usecase->id);
542 return;
543 }
544
545 ma_cal = (struct ma_audio_cal_settings *)malloc(sizeof(struct ma_audio_cal_settings));
546
547 /* update audio_cal and send it */
548 if (ma_cal != NULL){
549 ma_cal->app_type = usecase->stream.out->app_type_cfg.app_type;
550 ma_cal->device = usecase->stream.out->devices;
551 ALOGV("%s: send usecase(%d) app_type(%d) device(%d)",
552 __func__, usecase->id, ma_cal->app_type, ma_cal->device);
553
554 pthread_mutex_lock(&my_data->lock);
555
556 if (is_active()) {
557 ALOGV("%s: send volume table === Start", __func__);
558 for (i = 0; i < STREAM_MAX_TYPES; i++)
559 ALOGV("%s: stream(%d) volume(%f) active(%s)", __func__, i,
560 ma_cur_state_table[i].vol,
561 ma_cur_state_table[i].active ? "T" : "F");
562 ALOGV("%s: send volume table === End", __func__);
563
564 if (!ma_set_volume_table_l(ma_cal,
565 STREAM_MAX_TYPES,
566 ma_cur_state_table))
567 ALOGE("Waves: ma_set_volume_table_l %f returned with error.", vol);
568 else
569 ALOGV("Waves: ma_set_volume_table_l success");
570
571 }
572 pthread_mutex_unlock(&my_data->lock);
573 free(ma_cal);
574 } else {
575 ALOGE("%s: ma_cal alloct fail", __func__);
576 }
577 }
578
audio_extn_ma_set_parameters(struct audio_device * adev,struct str_parms * parms)579 void audio_extn_ma_set_parameters(struct audio_device *adev,
580 struct str_parms *parms)
581 {
582 int ret;
583 bool ret_b;
584 int val;
585 char value[128];
586
587 // do LR swap and usb recognition
588 ret = str_parms_get_int(parms, "rotation", &val);
589 if (ret >= 0) {
590 switch (val) {
591 case 270:
592 ma_set_swap_l(adev, true);
593 break;
594 case 0:
595 case 90:
596 case 180:
597 ma_set_swap_l(adev, false);
598 break;
599 }
600 }
601
602 // check connect status
603 ret = str_parms_get_str(parms, AUDIO_PARAMETER_DEVICE_CONNECT, value,
604 sizeof(value));
605 if (ret >= 0) {
606 audio_devices_t device = (audio_devices_t)strtoul(value, NULL, 10);
607 if (audio_is_usb_out_device(device)) {
608 ret = str_parms_get_str(parms, "card", value, sizeof(value));
609 if (ret >= 0) {
610 const int card = atoi(value);
611 ma_support_usb(true, card);
612 }
613 }
614 }
615
616 // check disconnect status
617 ret = str_parms_get_str(parms, AUDIO_PARAMETER_DEVICE_DISCONNECT, value,
618 sizeof(value));
619 if (ret >= 0) {
620 audio_devices_t device = (audio_devices_t)strtoul(value, NULL, 10);
621 if (audio_is_usb_out_device(device)) {
622 ret = str_parms_get_str(parms, "card", value, sizeof(value));
623 if (ret >= 0) {
624 const int card = atoi(value);
625 ma_support_usb(false, card /*useless*/);
626 }
627 }
628 }
629 }
630
audio_extn_ma_supported_usb()631 bool audio_extn_ma_supported_usb()
632 {
633 ALOGV("%s: current support 0x%x", __func__, g_supported_dev);
634 return (g_supported_dev & SUPPORTED_USB) ? true : false;
635 }
636