/* * Copyright (C) 2013-2016 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #define LOG_TAG "tfa_98xx" /*#define LOG_NDEBUG 0*/ #include <cutils/log.h> #include <stdlib.h> #include <audio_hw.h> #include <dlfcn.h> #include "audio_extn.h" #include <platform.h> #include <math.h> #define LIB_SPEAKER_BUNDLE "/system/lib/libexTfa98xx.so" enum exTfa98xx_Audio_Mode { Audio_Mode_None = -1, Audio_Mode_Music_Normal, Audio_Mode_Hfp_Client, Audio_Mode_Voice, Audio_Mode_Hs_Hfp, Audio_Mode_Max }; typedef enum exTfa98xx_Audio_Mode exTfa98xx_audio_mode_t; enum exTfa98xx_Func_Mode { Func_Mode_None = -1, Func_Mode_Speaker, Func_Mode_BT }; typedef enum exTfa98xx_Func_Mode exTfa98xx_func_mode_t; #define I2S_CLOCK_ENABLE 1 #define I2S_CLOCK_DISABLE 0 #define HFP_MAX_VOLUME (15.000000) #define TFA_98XX_HFP_VSETPS (5.0) exTfa98xx_audio_mode_t current_audio_mode = Audio_Mode_None; typedef int (*set_speaker_on_t)(exTfa98xx_audio_mode_t); typedef int (*set_speaker_off_t)(void); typedef int (*set_speaker_calibration_t)(int); typedef void (*set_speaker_volume_step_t)(int, int); struct speaker_data { struct audio_device *adev; void *speaker_bundle; set_speaker_on_t set_speaker_on; set_speaker_off_t set_speaker_off; set_speaker_calibration_t set_speaker_calibration; set_speaker_volume_step_t set_speaker_volume_step; int ref_cnt[Audio_Mode_Max]; int route_cnt[Audio_Mode_Max]; bool update_ref_cnt; }; struct speaker_data *tfa98xx_speaker_data = NULL; static struct speaker_data* open_speaker_bundle() { struct speaker_data *sd = calloc(1, sizeof(struct speaker_data)); sd->speaker_bundle = dlopen(LIB_SPEAKER_BUNDLE, RTLD_NOW); if (sd->speaker_bundle == NULL) { ALOGE("%s: DLOPEN failed for %s", __func__, LIB_SPEAKER_BUNDLE); goto error; } else { ALOGV("%s: DLOPEN successful for %s", __func__, LIB_SPEAKER_BUNDLE); sd->set_speaker_on = (set_speaker_on_t)dlsym(sd->speaker_bundle, "exTfa98xx_speakeron"); if (sd->set_speaker_on == NULL) { ALOGE("%s: dlsym error %s for exTfa98xx_speakeron", __func__, dlerror()); goto error; } sd->set_speaker_off = (set_speaker_off_t)dlsym(sd->speaker_bundle, "exTfa98xx_speakeroff"); if (sd->set_speaker_off == NULL) { ALOGE("%s: dlsym error %s for exTfa98xx_speakeroff", __func__, dlerror()); goto error; } sd->set_speaker_volume_step = (set_speaker_volume_step_t)dlsym(sd->speaker_bundle, "exTfa98xx_setvolumestep"); if (sd->set_speaker_volume_step == NULL) { ALOGE("%s: dlsym error %s for exTfa98xx_setvolumestep", __func__, dlerror()); goto error; } sd->set_speaker_calibration = (set_speaker_calibration_t)dlsym(sd->speaker_bundle, "exTfa98xx_calibration"); if (sd->set_speaker_calibration == NULL) { ALOGE("%s: dlsym error %s for exTfa98xx_calibration", __func__, dlerror()); goto error; } } return sd; error: free(sd); return 0; } static void close_speaker_bundle(struct speaker_data *sd) { if (sd != NULL) { dlclose(sd->speaker_bundle); free(sd); sd = NULL; } } static int adev_i2s_clock_operation(int enable, struct audio_device *adev, char *paths) { int ret = -1; ALOGD("%s: mixer paths is: %s, enable: %d\n", __func__, paths, enable); if(I2S_CLOCK_ENABLE == enable) { ret = audio_route_apply_and_update_path(adev->audio_route, paths); if(ret) { ALOGE("%s: audio_route_apply_and_update_path return %d\n", __func__, ret); return ret; } } else { ret = audio_route_reset_and_update_path(adev->audio_route, paths); if(ret) { ALOGE("%s: audio_route_reset_and_update_path return %d\n", __func__, ret); return ret; } } return 0; } static int tfa_98xx_set_audio_mode(int enable, struct audio_device *adev, exTfa98xx_audio_mode_t audio_mode) { char paths[32] = "init_smart_pa"; switch(audio_mode) { case Audio_Mode_Music_Normal: strcat(paths, " music"); break; case Audio_Mode_Voice: case Audio_Mode_Hfp_Client: case Audio_Mode_Hs_Hfp: strcat(paths, " voice"); break; default: ALOGE("%s: function %d not support!\n",__func__, audio_mode); return -EINVAL; } ALOGV("%s: mixer paths is: %s, enable: %d\n", __func__, paths, enable); adev_i2s_clock_operation(enable, adev, paths); return 0; } static exTfa98xx_audio_mode_t tfa_98xx_get_audio_mode(struct speaker_data *data) { exTfa98xx_audio_mode_t tfa_98xx_audio_mode = Audio_Mode_None; struct listnode *node; struct audio_usecase *usecase; audio_mode_t mode = data->adev->mode; int i = 0; ALOGV("%s: enter\n", __func__); for (i = 0; i < Audio_Mode_Max; i++) data->route_cnt[i] = 0; list_for_each(node, &data->adev->usecase_list) { usecase = node_to_item(node, struct audio_usecase, list); if (usecase->devices & AUDIO_DEVICE_OUT_ALL_SCO) { if(data->adev->snd_dev_ref_cnt[usecase->out_snd_device] != 0) { tfa_98xx_audio_mode = Audio_Mode_Hs_Hfp; data->route_cnt[tfa_98xx_audio_mode]++; ALOGV("%s: audio_mode hs_hfp\n", __func__); } } else if (usecase->devices & AUDIO_DEVICE_OUT_SPEAKER) { if ((mode == AUDIO_MODE_IN_CALL) || audio_extn_hfp_is_active(data->adev)) { if (audio_extn_hfp_is_active(data->adev)) { if(data->adev->snd_dev_ref_cnt[usecase->out_snd_device] != 0) { tfa_98xx_audio_mode = Audio_Mode_Hfp_Client; data->route_cnt[tfa_98xx_audio_mode]++; ALOGV("%s: audio_mode hfp client\n", __func__); } } else { if(data->adev->snd_dev_ref_cnt[usecase->out_snd_device] != 0) { tfa_98xx_audio_mode = Audio_Mode_Voice; data->route_cnt[tfa_98xx_audio_mode]++; ALOGV("%s: audio_mode voice\n", __func__); } } } else { if (data->adev->snd_dev_ref_cnt[usecase->out_snd_device] != 0) { tfa_98xx_audio_mode = Audio_Mode_Music_Normal; data->route_cnt[tfa_98xx_audio_mode]++; ALOGV("%s: tfa_98xx_audio_mode music\n", __func__); } } } else { ALOGE("%s: no device match \n", __func__); } } ALOGV("%s: tfa_98xx_audio_mode %d exit\n", __func__, tfa_98xx_audio_mode); return tfa_98xx_audio_mode; } static int tfa_98xx_set_func_mode(int enable, struct audio_device *adev, exTfa98xx_func_mode_t func_mode) { struct speaker_data *data = tfa98xx_speaker_data; char paths[32] = "init_smart_pa"; if (data) { switch(func_mode) { case Func_Mode_Speaker: strcat(paths, " func_speaker"); break; case Func_Mode_BT: strcat(paths, " func_bt"); break; default: ALOGE("%s: function %d not support!\n",__func__, func_mode); return -EINVAL; } ALOGV("%s: mixer paths is: %s, enable: %d\n", __func__, paths, enable); adev_i2s_clock_operation(enable, adev, paths); } return 0; } static exTfa98xx_func_mode_t tfa_98xx_get_func_mode(exTfa98xx_audio_mode_t audio_mode) { exTfa98xx_func_mode_t func_mode = Func_Mode_None; switch(audio_mode) { case Audio_Mode_Music_Normal: case Audio_Mode_Voice: ALOGV("%s: tfa_98xx_func_mode speaker \n", __func__); func_mode = Func_Mode_Speaker; break; case Audio_Mode_Hfp_Client: case Audio_Mode_Hs_Hfp: ALOGV("%s: tfa_98xx_func_mode bt \n", __func__); func_mode = Func_Mode_BT; break; default: break; } return func_mode; } static void tfa_98xx_disable_speaker(void) { struct speaker_data *data = tfa98xx_speaker_data; int ret = 0; ret = data->set_speaker_off(); if (ret) { ALOGE("%s: exTfa98xx_speakeroff failed result = %d\n", __func__, ret); goto on_error; } ret = tfa_98xx_set_audio_mode(I2S_CLOCK_DISABLE, data->adev, current_audio_mode); if (ret) { ALOGE("%s: tfa_98xx_set_audio_mode disable failed return %d\n", __func__, ret); goto on_error; } current_audio_mode = Audio_Mode_None; on_error: return; } void audio_extn_tfa_98xx_disable_speaker(snd_device_t snd_device) { struct speaker_data *data = tfa98xx_speaker_data; int i = 0; exTfa98xx_audio_mode_t new_audio_mode = Audio_Mode_None; ALOGV("%s: enter\n", __func__); if (data) { if ((current_audio_mode == Audio_Mode_None) || (snd_device > SND_DEVICE_OUT_END)) goto on_exit; switch(snd_device) { case SND_DEVICE_OUT_SPEAKER: new_audio_mode = Audio_Mode_Music_Normal; break; case SND_DEVICE_OUT_VOICE_SPEAKER: new_audio_mode = Audio_Mode_Voice; break; case SND_DEVICE_OUT_VOICE_SPEAKER_HFP: new_audio_mode = Audio_Mode_Hfp_Client; break; case SND_DEVICE_OUT_BT_SCO: new_audio_mode = Audio_Mode_Hs_Hfp; break; default: break; } if ((new_audio_mode == Audio_Mode_None) || (data->ref_cnt[new_audio_mode] <= 0)) { ALOGE("%s: device ref cnt is already 0", __func__); goto on_exit; } data->ref_cnt[new_audio_mode]--; for (i = 0; i < Audio_Mode_Max; i++) { if (data->ref_cnt[i] > 0) { ALOGD("%s: exTfa98xx_speaker still in use\n", __func__); goto on_exit; } } if (data->adev->enable_hfp) data->set_speaker_volume_step(0, 0); tfa_98xx_disable_speaker(); } ALOGV("%s: exit\n", __func__); on_exit: return; } int audio_extn_tfa_98xx_enable_speaker(void) { struct speaker_data *data = tfa98xx_speaker_data; exTfa98xx_audio_mode_t new_audio_mode = Audio_Mode_Music_Normal; int ret = 0; int i = 0; ALOGV("%s: enter\n", __func__); if (data) { new_audio_mode = tfa_98xx_get_audio_mode(data); if ((new_audio_mode != Audio_Mode_None) && (data->ref_cnt[new_audio_mode] >= 1)) { ALOGD("%s, mode %d already active!", __func__, new_audio_mode); data->ref_cnt[new_audio_mode]++; goto on_exit; } ret = tfa_98xx_set_audio_mode(I2S_CLOCK_ENABLE, data->adev, new_audio_mode); if (ret) { ALOGE("%s: tfa_98xx_set_audio_mode enable failed return %d\n", __func__, ret); goto on_exit; } ret = data->set_speaker_on(new_audio_mode); if (ret) { ALOGE("%s: exTfa98xx_speakeron failed result = %d\n", __func__, ret); goto on_exit; } current_audio_mode = new_audio_mode; for (i = 0; i < Audio_Mode_Max; i++) { data->ref_cnt[i] = data->route_cnt[i]; } data->update_ref_cnt = false; } ALOGV("%s: exit\n", __func__); on_exit: return ret; } void audio_extn_tfa_98xx_set_mode(void) { int ret = 0; struct speaker_data *data = tfa98xx_speaker_data; exTfa98xx_audio_mode_t new_audio_mode = Audio_Mode_None; exTfa98xx_func_mode_t new_func_mode = Func_Mode_None; ALOGV("%s: enter\n", __func__); if (data) { new_audio_mode = tfa_98xx_get_audio_mode(data); new_func_mode = tfa_98xx_get_func_mode(new_audio_mode); if (new_func_mode == Func_Mode_None) return; ret = tfa_98xx_set_func_mode(I2S_CLOCK_ENABLE, data->adev, new_func_mode); if (ret) { ALOGE("%s: tfa_98xx_set_func_mode enable return %d\n", __func__, ret); } data->update_ref_cnt = true; } ALOGV("%s: exit\n", __func__); } void audio_extn_tfa_98xx_set_mode_bt(void) { struct speaker_data *data = tfa98xx_speaker_data; int ret = 0; if (data) { ret = tfa_98xx_set_func_mode(I2S_CLOCK_ENABLE, data->adev, Func_Mode_BT); if (ret) { ALOGE("%s: tfa_98xx_set_func_mode enable return %d\n", __func__, ret); } } } void audio_extn_tfa_98xx_update(void) { struct speaker_data *data = tfa98xx_speaker_data; exTfa98xx_audio_mode_t new_audio_mode = Audio_Mode_Music_Normal; ALOGD("%s: enter\n", __func__); if (data) { new_audio_mode = tfa_98xx_get_audio_mode(data); if (new_audio_mode <= current_audio_mode) { ALOGE("%s: audio_extn_tfa_98xx_update same mode\n", __func__); if (data->update_ref_cnt == true) { data->ref_cnt[new_audio_mode]++; data->update_ref_cnt = false; } goto on_error; } if (current_audio_mode != Audio_Mode_None) { tfa_98xx_disable_speaker(); } audio_extn_tfa_98xx_enable_speaker(); } ALOGV("%s: exit\n", __func__); on_error: return; } void audio_extn_tfa_98xx_set_voice_vol(float vol) { struct speaker_data *data = tfa98xx_speaker_data; int vsteps = 0; if (data) { if (data->adev->enable_hfp) { if (vol < 0.0) { vol = 0.0; } else { vol = ((vol > HFP_MAX_VOLUME) ? 1.0 : (vol / HFP_MAX_VOLUME)); } vsteps = (int)floorf((1.0 - vol) * TFA_98XX_HFP_VSETPS); } else { return; } ALOGD("%s: vsteps %d\n", __func__, vsteps); data->set_speaker_volume_step(vsteps, vsteps); } } bool audio_extn_tfa_98xx_is_supported(void) { struct speaker_data *data = tfa98xx_speaker_data; if (data) return true; else return false; } int audio_extn_tfa_98xx_init(struct audio_device *adev) { int ret = 0; struct speaker_data *data = open_speaker_bundle(); ALOGV("%s: enter\n", __func__); if (data) { ret = tfa_98xx_set_audio_mode(I2S_CLOCK_ENABLE, adev, Audio_Mode_Music_Normal); if (ret) { ALOGE("%s: tfa_98xx_set_audio_mode enable return %d\n", __func__, ret); goto err_init; } ret = data->set_speaker_calibration(0); if (ret) { ALOGE("%s: exTfa98xx_calibration return %d\n", __func__, ret); } ret = tfa_98xx_set_audio_mode(I2S_CLOCK_DISABLE, adev, Audio_Mode_Music_Normal); if (ret) { ALOGE("%s: tfa_98xx_set_audio_mode disable return %d\n", __func__, ret); goto err_init; } data->adev = adev; tfa98xx_speaker_data = data; ALOGV("%s: exit\n", __func__); return 0; } err_init: close_speaker_bundle(data); return -EINVAL; } void audio_extn_tfa_98xx_deinit(void) { struct speaker_data *data = tfa98xx_speaker_data; if (data) { data->set_speaker_off(); close_speaker_bundle(data); tfa98xx_speaker_data = NULL; } }