You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
546 lines
16 KiB
546 lines
16 KiB
/*
|
|
* 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 <log/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;
|
|
}
|
|
}
|
|
|