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 54struct ma_audio_cal_settings { 55 int app_type; 56 audio_devices_t device; 57}; 58 59struct ma_state { 60 float vol; 61 bool active; 62}; 63 64typedef 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 75typedef enum MA_CMD { 76 MA_CMD_VOL, 77 MA_CMD_SWAP_ENABLE, 78 MA_CMD_SWAP_DISABLE, 79} ma_cmd_t; 80 81typedef void *ma_audio_cal_handle_t; 82typedef int (*set_audio_cal_t)(const char *); 83 84typedef bool (*ma_param_init_t)(ma_audio_cal_handle_t *, const char *, 85 const char *, const char *, set_audio_cal_t); 86 87typedef bool (*ma_param_deinit_t)(ma_audio_cal_handle_t *); 88 89typedef bool (*ma_set_lr_swap_t)(ma_audio_cal_handle_t, 90 const struct ma_audio_cal_settings *, bool); 91 92typedef bool (*ma_set_sound_mode_t)(ma_audio_cal_handle_t, 93 const struct ma_audio_cal_settings *, 94 unsigned int); 95 96typedef bool (*ma_set_volume_t)(ma_audio_cal_handle_t, 97 const struct ma_audio_cal_settings *, double); 98 99typedef 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 103struct 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 115ma_audio_cal_handle_t g_ma_audio_cal_handle = NULL; 116static uint16_t g_supported_dev = 0; 117static struct ma_state ma_cur_state_table[STREAM_MAX_TYPES]; 118static struct ma_platform_data *my_data = NULL; 119 120static 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 128static 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 135static 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 142static 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 149static 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 158static 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 180static 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 192static 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 257static 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 283static 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 299static 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 336done: 337 if (fd >= 0) close(fd); 338} 339 340// adev_init lock held 341void 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 473error: 474 if (my_data) { free(my_data); } 475 my_data = NULL; 476} 477 478//adev_init lock held 479void 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 496bool 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 528void 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 579void 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 631bool 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