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