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