upload android base code part4
This commit is contained in:
parent
b9e30e05b1
commit
78ea2404cd
23455 changed files with 5250148 additions and 0 deletions
162
android/hardware/qcom/audio/hal/audio_extn/audio_extn.c
Normal file
162
android/hardware/qcom/audio/hal/audio_extn/audio_extn.c
Normal file
|
@ -0,0 +1,162 @@
|
|||
/*
|
||||
* Copyright (C) 2015 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 "audio_hw_extn"
|
||||
/*#define LOG_NDEBUG 0*/
|
||||
#define LOG_NDDEBUG 0
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <errno.h>
|
||||
#include <dlfcn.h>
|
||||
#include <cutils/properties.h>
|
||||
#include <cutils/log.h>
|
||||
|
||||
#include "audio_hw.h"
|
||||
#include "audio_extn.h"
|
||||
#include "platform.h"
|
||||
#include "platform_api.h"
|
||||
|
||||
struct snd_card_split cur_snd_card_split = {
|
||||
.device = {0},
|
||||
.snd_card = {0},
|
||||
.form_factor = {0},
|
||||
};
|
||||
|
||||
struct snd_card_split *audio_extn_get_snd_card_split()
|
||||
{
|
||||
return &cur_snd_card_split;
|
||||
}
|
||||
|
||||
void audio_extn_set_snd_card_split(const char* in_snd_card_name)
|
||||
{
|
||||
/* sound card name follows below mentioned convention
|
||||
<target name>-<sound card name>-<form factor>-snd-card
|
||||
parse target name, sound card name and form factor
|
||||
*/
|
||||
char *snd_card_name = strdup(in_snd_card_name);
|
||||
char *tmp = NULL;
|
||||
char *device = NULL;
|
||||
char *snd_card = NULL;
|
||||
char *form_factor = NULL;
|
||||
|
||||
if (in_snd_card_name == NULL) {
|
||||
ALOGE("%s: snd_card_name passed is NULL", __func__);
|
||||
goto on_error;
|
||||
}
|
||||
|
||||
device = strtok_r(snd_card_name, "-", &tmp);
|
||||
if (device == NULL) {
|
||||
ALOGE("%s: called on invalid snd card name", __func__);
|
||||
goto on_error;
|
||||
}
|
||||
strlcpy(cur_snd_card_split.device, device, HW_INFO_ARRAY_MAX_SIZE);
|
||||
|
||||
snd_card = strtok_r(NULL, "-", &tmp);
|
||||
if (snd_card == NULL) {
|
||||
ALOGE("%s: called on invalid snd card name", __func__);
|
||||
goto on_error;
|
||||
}
|
||||
strlcpy(cur_snd_card_split.snd_card, snd_card, HW_INFO_ARRAY_MAX_SIZE);
|
||||
|
||||
form_factor = strtok_r(NULL, "-", &tmp);
|
||||
if (form_factor == NULL) {
|
||||
ALOGE("%s: called on invalid snd card name", __func__);
|
||||
goto on_error;
|
||||
}
|
||||
strlcpy(cur_snd_card_split.form_factor, form_factor, HW_INFO_ARRAY_MAX_SIZE);
|
||||
|
||||
ALOGI("%s: snd_card_name(%s) device(%s) snd_card(%s) form_factor(%s)",
|
||||
__func__, in_snd_card_name, device, snd_card, form_factor);
|
||||
|
||||
on_error:
|
||||
if (snd_card_name)
|
||||
free(snd_card_name);
|
||||
}
|
||||
|
||||
#ifdef KPI_OPTIMIZE_ENABLED
|
||||
typedef int (*perf_lock_acquire_t)(int, int, int*, int);
|
||||
typedef int (*perf_lock_release_t)(int);
|
||||
|
||||
static void *qcopt_handle;
|
||||
static perf_lock_acquire_t perf_lock_acq;
|
||||
static perf_lock_release_t perf_lock_rel;
|
||||
|
||||
static int perf_lock_handle;
|
||||
char opt_lib_path[PROPERTY_VALUE_MAX] = {0};
|
||||
|
||||
int perf_lock_opts[] = {0x101, 0x20E, 0x30E};
|
||||
|
||||
int audio_extn_perf_lock_init(void)
|
||||
{
|
||||
int ret = 0;
|
||||
if (qcopt_handle == NULL) {
|
||||
if (property_get("ro.vendor.extension_library",
|
||||
opt_lib_path, NULL) <= 0) {
|
||||
ALOGE("%s: Failed getting perf property", __func__);
|
||||
ret = -EINVAL;
|
||||
goto err;
|
||||
}
|
||||
if ((qcopt_handle = dlopen(opt_lib_path, RTLD_NOW)) == NULL) {
|
||||
ALOGE("%s: Failed to open perf handle", __func__);
|
||||
ret = -EINVAL;
|
||||
goto err;
|
||||
} else {
|
||||
perf_lock_acq = (perf_lock_acquire_t)dlsym(qcopt_handle,
|
||||
"perf_lock_acq");
|
||||
if (perf_lock_acq == NULL) {
|
||||
ALOGE("%s: Perf lock Acquire NULL", __func__);
|
||||
dlclose(qcopt_handle);
|
||||
qcopt_handle = NULL;
|
||||
ret = -EINVAL;
|
||||
goto err;
|
||||
}
|
||||
perf_lock_rel = (perf_lock_release_t)dlsym(qcopt_handle,
|
||||
"perf_lock_rel");
|
||||
if (perf_lock_rel == NULL) {
|
||||
ALOGE("%s: Perf lock Release NULL", __func__);
|
||||
dlclose(qcopt_handle);
|
||||
qcopt_handle = NULL;
|
||||
ret = -EINVAL;
|
||||
goto err;
|
||||
}
|
||||
ALOGD("%s: Perf lock handles Success", __func__);
|
||||
}
|
||||
}
|
||||
err:
|
||||
return ret;
|
||||
}
|
||||
|
||||
void audio_extn_perf_lock_acquire(void)
|
||||
{
|
||||
if (perf_lock_acq) {
|
||||
perf_lock_handle = perf_lock_acq(perf_lock_handle, 0, perf_lock_opts, 3);
|
||||
ALOGV("%s: Perf lock acquired", __func__);
|
||||
} else {
|
||||
ALOGE("%s: Perf lock acquire error", __func__);
|
||||
}
|
||||
}
|
||||
|
||||
void audio_extn_perf_lock_release(void)
|
||||
{
|
||||
if (perf_lock_rel && perf_lock_handle) {
|
||||
perf_lock_rel(perf_lock_handle);
|
||||
perf_lock_handle = 0;
|
||||
ALOGV("%s: Perf lock released", __func__);
|
||||
} else {
|
||||
ALOGE("%s: Perf lock release error", __func__);
|
||||
}
|
||||
}
|
||||
#endif /* KPI_OPTIMIZE_ENABLED */
|
198
android/hardware/qcom/audio/hal/audio_extn/audio_extn.h
Normal file
198
android/hardware/qcom/audio/hal/audio_extn/audio_extn.h
Normal file
|
@ -0,0 +1,198 @@
|
|||
/*
|
||||
* Copyright (C) 2014 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.
|
||||
*/
|
||||
|
||||
#ifndef AUDIO_EXTN_H
|
||||
#define AUDIO_EXTN_H
|
||||
|
||||
#include <cutils/str_parms.h>
|
||||
|
||||
#define HW_INFO_ARRAY_MAX_SIZE 32
|
||||
|
||||
struct snd_card_split {
|
||||
char device[HW_INFO_ARRAY_MAX_SIZE];
|
||||
char snd_card[HW_INFO_ARRAY_MAX_SIZE];
|
||||
char form_factor[HW_INFO_ARRAY_MAX_SIZE];
|
||||
};
|
||||
|
||||
void *audio_extn_extspk_init(struct audio_device *adev);
|
||||
void audio_extn_extspk_deinit(void *extn);
|
||||
void audio_extn_extspk_update(void* extn);
|
||||
void audio_extn_extspk_set_mode(void* extn, audio_mode_t mode);
|
||||
void audio_extn_extspk_set_voice_vol(void* extn, float vol);
|
||||
struct snd_card_split *audio_extn_get_snd_card_split();
|
||||
void audio_extn_set_snd_card_split(const char* in_snd_card_name);
|
||||
|
||||
#ifndef SPKR_PROT_ENABLED
|
||||
#define audio_extn_spkr_prot_init(adev) (0)
|
||||
#define audio_extn_spkr_prot_start_processing(snd_device) (-EINVAL)
|
||||
#define audio_extn_spkr_prot_calib_cancel(adev) (0)
|
||||
#define audio_extn_spkr_prot_stop_processing(snd_device) (0)
|
||||
#define audio_extn_spkr_prot_is_enabled() (false)
|
||||
#define audio_extn_spkr_prot_get_acdb_id(snd_device) (-EINVAL)
|
||||
#define audio_extn_get_spkr_prot_snd_device(snd_device) (snd_device)
|
||||
#else
|
||||
void audio_extn_spkr_prot_init(void *adev);
|
||||
int audio_extn_spkr_prot_start_processing(snd_device_t snd_device);
|
||||
void audio_extn_spkr_prot_stop_processing(snd_device_t snd_device);
|
||||
bool audio_extn_spkr_prot_is_enabled();
|
||||
int audio_extn_spkr_prot_get_acdb_id(snd_device_t snd_device);
|
||||
int audio_extn_get_spkr_prot_snd_device(snd_device_t snd_device);
|
||||
void audio_extn_spkr_prot_calib_cancel(void *adev);
|
||||
#endif
|
||||
|
||||
#ifndef HFP_ENABLED
|
||||
#define audio_extn_hfp_is_active(adev) (0)
|
||||
#define audio_extn_hfp_get_usecase() (-1)
|
||||
#define audio_extn_hfp_set_parameters(adev, params) (0)
|
||||
#define audio_extn_hfp_set_mic_mute(adev, state) (0)
|
||||
|
||||
#else
|
||||
bool audio_extn_hfp_is_active(struct audio_device *adev);
|
||||
|
||||
audio_usecase_t audio_extn_hfp_get_usecase();
|
||||
|
||||
void audio_extn_hfp_set_parameters(struct audio_device *adev,
|
||||
struct str_parms *parms);
|
||||
int audio_extn_hfp_set_mic_mute(struct audio_device *adev, bool state);
|
||||
|
||||
#endif
|
||||
|
||||
#ifndef USB_TUNNEL_ENABLED
|
||||
#define audio_extn_usb_init(adev) (0)
|
||||
#define audio_extn_usb_deinit() (0)
|
||||
#define audio_extn_usb_add_device(device, card) (0)
|
||||
#define audio_extn_usb_remove_device(device, card) (0)
|
||||
#define audio_extn_usb_is_config_supported(bit_width, sample_rate, ch, pb) (false)
|
||||
#define audio_extn_usb_enable_sidetone(device, enable) (0)
|
||||
#define audio_extn_usb_set_sidetone_gain(parms, value, len) (0)
|
||||
#define audio_extn_usb_is_capture_supported() (false)
|
||||
#define audio_extn_usb_get_max_channels(dir) (0)
|
||||
#define audio_extn_usb_get_max_bit_width(dir) (0)
|
||||
#define audio_extn_usb_sup_sample_rates(t, s, l) (0)
|
||||
#define audio_extn_usb_alive(adev) (false)
|
||||
#else
|
||||
void audio_extn_usb_init(void *adev);
|
||||
void audio_extn_usb_deinit();
|
||||
void audio_extn_usb_add_device(audio_devices_t device, int card);
|
||||
void audio_extn_usb_remove_device(audio_devices_t device, int card);
|
||||
bool audio_extn_usb_is_config_supported(unsigned int *bit_width,
|
||||
unsigned int *sample_rate,
|
||||
unsigned int *ch,
|
||||
bool is_playback);
|
||||
int audio_extn_usb_enable_sidetone(int device, bool enable);
|
||||
int audio_extn_usb_set_sidetone_gain(struct str_parms *parms,
|
||||
char *value, int len);
|
||||
bool audio_extn_usb_is_capture_supported();
|
||||
int audio_extn_usb_get_max_channels(bool is_playback);
|
||||
int audio_extn_usb_get_max_bit_width(bool is_playback);
|
||||
int audio_extn_usb_sup_sample_rates(bool is_playback, uint32_t *sr, uint32_t l);
|
||||
bool audio_extn_usb_alive(int card);
|
||||
#endif
|
||||
|
||||
|
||||
#ifndef SOUND_TRIGGER_ENABLED
|
||||
#define audio_extn_sound_trigger_init(adev) (0)
|
||||
#define audio_extn_sound_trigger_deinit(adev) (0)
|
||||
#define audio_extn_sound_trigger_update_device_status(snd_dev, event) (0)
|
||||
#define audio_extn_sound_trigger_set_parameters(adev, parms) (0)
|
||||
#define audio_extn_sound_trigger_check_and_get_session(in) (0)
|
||||
#define audio_extn_sound_trigger_stop_lab(in) (0)
|
||||
#define audio_extn_sound_trigger_read(in, buffer, bytes) (0)
|
||||
|
||||
#else
|
||||
|
||||
enum st_event_type {
|
||||
ST_EVENT_SND_DEVICE_FREE,
|
||||
ST_EVENT_SND_DEVICE_BUSY,
|
||||
ST_EVENT_STREAM_FREE,
|
||||
ST_EVENT_STREAM_BUSY
|
||||
};
|
||||
typedef enum st_event_type st_event_type_t;
|
||||
|
||||
int audio_extn_sound_trigger_init(struct audio_device *adev);
|
||||
void audio_extn_sound_trigger_deinit(struct audio_device *adev);
|
||||
void audio_extn_sound_trigger_update_device_status(snd_device_t snd_device,
|
||||
st_event_type_t event);
|
||||
void audio_extn_sound_trigger_set_parameters(struct audio_device *adev,
|
||||
struct str_parms *parms);
|
||||
void audio_extn_sound_trigger_check_and_get_session(struct stream_in *in);
|
||||
void audio_extn_sound_trigger_stop_lab(struct stream_in *in);
|
||||
int audio_extn_sound_trigger_read(struct stream_in *in, void *buffer,
|
||||
size_t bytes);
|
||||
#endif
|
||||
|
||||
#ifndef DSM_FEEDBACK_ENABLED
|
||||
#define audio_extn_dsm_feedback_enable(adev, snd_device, benable) (0)
|
||||
#else
|
||||
void audio_extn_dsm_feedback_enable(struct audio_device *adev,
|
||||
snd_device_t snd_device,
|
||||
bool benable);
|
||||
#endif
|
||||
|
||||
void audio_extn_utils_send_default_app_type_cfg(void *platform, struct mixer *mixer);
|
||||
int audio_extn_utils_send_app_type_cfg(struct audio_device *adev,
|
||||
struct audio_usecase *usecase);
|
||||
void audio_extn_utils_send_audio_calibration(struct audio_device *adev,
|
||||
struct audio_usecase *usecase);
|
||||
int audio_extn_utils_send_app_type_gain(struct audio_device *adev,
|
||||
int app_type,
|
||||
int *gain);
|
||||
#ifndef HWDEP_CAL_ENABLED
|
||||
#define audio_extn_hwdep_cal_send(snd_card, acdb_handle) (0)
|
||||
#else
|
||||
void audio_extn_hwdep_cal_send(int snd_card, void *acdb_handle);
|
||||
#endif
|
||||
|
||||
#ifndef KPI_OPTIMIZE_ENABLED
|
||||
#define audio_extn_perf_lock_init() (0)
|
||||
#define audio_extn_perf_lock_acquire() (0)
|
||||
#define audio_extn_perf_lock_release() (0)
|
||||
#else
|
||||
int audio_extn_perf_lock_init(void);
|
||||
void audio_extn_perf_lock_acquire(void);
|
||||
void audio_extn_perf_lock_release(void);
|
||||
#endif /* KPI_OPTIMIZE_ENABLED */
|
||||
|
||||
#ifndef HW_VARIANTS_ENABLED
|
||||
#define hw_info_init(snd_card_name) (0)
|
||||
#define hw_info_deinit(hw_info) (0)
|
||||
#define hw_info_append_hw_type(hw_info,\
|
||||
snd_device, device_name) (0)
|
||||
#else
|
||||
void *hw_info_init(const char *snd_card_name);
|
||||
void hw_info_deinit(void *hw_info);
|
||||
void hw_info_append_hw_type(void *hw_info, snd_device_t snd_device,
|
||||
char *device_name);
|
||||
#endif /* HW_VARIANTS_ENABLED */
|
||||
|
||||
typedef void (* snd_mon_cb)(void * stream, struct str_parms * parms);
|
||||
#ifndef SND_MONITOR_ENABLED
|
||||
#define audio_extn_snd_mon_init() (0)
|
||||
#define audio_extn_snd_mon_deinit() (0)
|
||||
#define audio_extn_snd_mon_register_listener(stream, cb) (0)
|
||||
#define audio_extn_snd_mon_unregister_listener(stream) (0)
|
||||
#else
|
||||
int audio_extn_snd_mon_init();
|
||||
int audio_extn_snd_mon_deinit();
|
||||
int audio_extn_snd_mon_register_listener(void *stream, snd_mon_cb cb);
|
||||
int audio_extn_snd_mon_unregister_listener(void *stream);
|
||||
#endif
|
||||
|
||||
bool audio_extn_utils_resolve_config_file(char[]);
|
||||
int audio_extn_utils_get_platform_info(const char* snd_card_name,
|
||||
char* platform_info_file);
|
||||
int audio_extn_utils_get_snd_card_num();
|
||||
#endif /* AUDIO_EXTN_H */
|
105
android/hardware/qcom/audio/hal/audio_extn/dsm_feedback.c
Executable file
105
android/hardware/qcom/audio/hal/audio_extn/dsm_feedback.c
Executable file
|
@ -0,0 +1,105 @@
|
|||
/*
|
||||
* Copyright (C) 2015 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 "audio_hw_dsm_feedback"
|
||||
/*#define LOG_NDEBUG 0*/
|
||||
#define LOG_NDDEBUG 0
|
||||
|
||||
#include <errno.h>
|
||||
#include <math.h>
|
||||
#include <cutils/log.h>
|
||||
|
||||
#include "audio_hw.h"
|
||||
#include "platform.h"
|
||||
#include "platform_api.h"
|
||||
#include <stdlib.h>
|
||||
|
||||
|
||||
static struct pcm_config pcm_config_dsm = {
|
||||
.channels = 2,
|
||||
.rate = 48000,
|
||||
.period_size = 256,
|
||||
.period_count = 4,
|
||||
.format = PCM_FORMAT_S16_LE,
|
||||
.start_threshold = 0,
|
||||
.stop_threshold = INT_MAX,
|
||||
.avail_min = 0,
|
||||
};
|
||||
|
||||
int start_dsm_feedback_processing(struct audio_device *adev, int enable)
|
||||
{
|
||||
int ret = 0;
|
||||
int32_t pcm_dev_tx_id = -1;
|
||||
static struct pcm *dsm_pcm_handle = NULL;
|
||||
|
||||
if (enable) {
|
||||
/*do nothing if already enabled*/
|
||||
if (dsm_pcm_handle)
|
||||
return ret;
|
||||
|
||||
pcm_dev_tx_id = platform_get_pcm_device_id(USECASE_AUDIO_DSM_FEEDBACK, PCM_CAPTURE);
|
||||
if (pcm_dev_tx_id < 0) {
|
||||
ALOGE("%s: Invalid pcm device for usecase (%d)",
|
||||
__func__, USECASE_AUDIO_DSM_FEEDBACK);
|
||||
ret = -ENODEV;
|
||||
goto close;
|
||||
}
|
||||
|
||||
dsm_pcm_handle = pcm_open(adev->snd_card,
|
||||
pcm_dev_tx_id,
|
||||
PCM_IN, &pcm_config_dsm);
|
||||
if (dsm_pcm_handle && !pcm_is_ready(dsm_pcm_handle)) {
|
||||
ALOGE("%s: %s", __func__, pcm_get_error(dsm_pcm_handle));
|
||||
ret = -EIO;
|
||||
goto close;
|
||||
}
|
||||
|
||||
if (pcm_start(dsm_pcm_handle) < 0) {
|
||||
ALOGE("%s: pcm start for RX failed", __func__);
|
||||
ret = -EINVAL;
|
||||
goto close;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
close:
|
||||
/*close pcm if disable or error happend in opening*/
|
||||
if (dsm_pcm_handle) {
|
||||
pcm_close(dsm_pcm_handle);
|
||||
dsm_pcm_handle = NULL;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
void audio_extn_dsm_feedback_enable(struct audio_device *adev,
|
||||
snd_device_t snd_device,
|
||||
int benable)
|
||||
{
|
||||
if ( NULL == adev )
|
||||
return;
|
||||
|
||||
if( snd_device == SND_DEVICE_OUT_SPEAKER ||
|
||||
snd_device == SND_DEVICE_OUT_SPEAKER_REVERSE ||
|
||||
snd_device == SND_DEVICE_OUT_VOICE_SPEAKER ||
|
||||
snd_device == SND_DEVICE_OUT_SPEAKER_SAFE ||
|
||||
snd_device == SND_DEVICE_OUT_SPEAKER_AND_HEADPHONES ||
|
||||
snd_device == SND_DEVICE_OUT_SPEAKER_AND_LINE ||
|
||||
snd_device == SND_DEVICE_OUT_SPEAKER_SAFE_AND_HEADPHONES ||
|
||||
snd_device == SND_DEVICE_OUT_SPEAKER_SAFE_AND_LINE )
|
||||
start_dsm_feedback_processing(adev, benable);
|
||||
}
|
158
android/hardware/qcom/audio/hal/audio_extn/ext_speaker.c
Normal file
158
android/hardware/qcom/audio/hal/audio_extn/ext_speaker.c
Normal file
|
@ -0,0 +1,158 @@
|
|||
/*
|
||||
* Copyright (C) 2014 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 "ext_speaker"
|
||||
/*#define LOG_NDEBUG 0*/
|
||||
#include <cutils/log.h>
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <audio_hw.h>
|
||||
#include <dlfcn.h>
|
||||
|
||||
#ifdef __LP64__
|
||||
#define LIB_SPEAKER_BUNDLE "/vendor/lib64/soundfx/libspeakerbundle.so"
|
||||
#else
|
||||
#define LIB_SPEAKER_BUNDLE "/vendor/lib/soundfx/libspeakerbundle.so"
|
||||
#endif
|
||||
|
||||
typedef void (*set_mode_t)(int);
|
||||
typedef void (*set_speaker_on_t)(bool);
|
||||
typedef void (*set_earpiece_on_t)(bool);
|
||||
typedef void (*set_voice_vol_t)(float);
|
||||
|
||||
struct speaker_data {
|
||||
struct audio_device *adev;
|
||||
void *speaker_bundle;
|
||||
set_mode_t set_mode;
|
||||
set_speaker_on_t set_speaker_on;
|
||||
set_earpiece_on_t set_earpiece_on;
|
||||
set_voice_vol_t set_voice_vol;
|
||||
};
|
||||
|
||||
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_mode = (set_mode_t)dlsym(sd->speaker_bundle,
|
||||
"set_mode");
|
||||
if (sd->set_mode == NULL) {
|
||||
ALOGE("%s: dlsym error %s for set_mode", __func__,
|
||||
dlerror());
|
||||
goto error;
|
||||
}
|
||||
sd->set_speaker_on = (set_speaker_on_t)dlsym(sd->speaker_bundle,
|
||||
"set_speaker_on");
|
||||
if (sd->set_speaker_on == NULL) {
|
||||
ALOGE("%s: dlsym error %s for set_speaker_on", __func__,
|
||||
dlerror());
|
||||
goto error;
|
||||
}
|
||||
sd->set_earpiece_on = (set_earpiece_on_t)dlsym(sd->speaker_bundle,
|
||||
"set_earpiece_on");
|
||||
if (sd->set_earpiece_on == NULL) {
|
||||
ALOGE("%s: dlsym error %s for set_earpiece_on", __func__,
|
||||
dlerror());
|
||||
goto error;
|
||||
}
|
||||
sd->set_voice_vol = (set_voice_vol_t)dlsym(sd->speaker_bundle,
|
||||
"set_voice_volume");
|
||||
if (sd->set_voice_vol == NULL) {
|
||||
ALOGE("%s: dlsym error %s for set_voice_volume",
|
||||
__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;
|
||||
}
|
||||
}
|
||||
|
||||
void *audio_extn_extspk_init(struct audio_device *adev)
|
||||
{
|
||||
struct speaker_data *data = open_speaker_bundle();
|
||||
|
||||
if (data)
|
||||
data->adev = adev;
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
void audio_extn_extspk_deinit(void *extn)
|
||||
{
|
||||
struct speaker_data *data = (struct speaker_data*)extn;
|
||||
close_speaker_bundle(data);
|
||||
}
|
||||
|
||||
void audio_extn_extspk_update(void* extn)
|
||||
{
|
||||
struct speaker_data *data = (struct speaker_data*)extn;
|
||||
|
||||
if (data) {
|
||||
bool speaker_on = false;
|
||||
bool earpiece_on = false;
|
||||
struct listnode *node;
|
||||
struct audio_usecase *usecase;
|
||||
list_for_each(node, &data->adev->usecase_list) {
|
||||
usecase = node_to_item(node, struct audio_usecase, list);
|
||||
if (usecase->devices & AUDIO_DEVICE_OUT_EARPIECE) {
|
||||
if(data->adev->snd_dev_ref_cnt[usecase->out_snd_device] != 0) {
|
||||
earpiece_on = true;
|
||||
}
|
||||
}
|
||||
if (usecase->devices & AUDIO_DEVICE_OUT_SPEAKER) {
|
||||
if(data->adev->snd_dev_ref_cnt[usecase->out_snd_device] != 0) {
|
||||
speaker_on = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
data->set_earpiece_on(earpiece_on);
|
||||
data->set_speaker_on(speaker_on);
|
||||
}
|
||||
}
|
||||
|
||||
void audio_extn_extspk_set_mode(void* extn, audio_mode_t mode)
|
||||
{
|
||||
struct speaker_data *data = (struct speaker_data*)extn;
|
||||
|
||||
if (data)
|
||||
data->set_mode(mode);
|
||||
}
|
||||
|
||||
void audio_extn_extspk_set_voice_vol(void* extn, float vol)
|
||||
{
|
||||
struct speaker_data *data = (struct speaker_data*)extn;
|
||||
|
||||
if (data)
|
||||
data->set_voice_vol(vol);
|
||||
}
|
509
android/hardware/qcom/audio/hal/audio_extn/hfp.c
Normal file
509
android/hardware/qcom/audio/hal/audio_extn/hfp.c
Normal file
|
@ -0,0 +1,509 @@
|
|||
/*
|
||||
* Copyright (C) 2014 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 "audio_hw_hfp"
|
||||
/*#define LOG_NDEBUG 0*/
|
||||
#define LOG_NDDEBUG 0
|
||||
|
||||
#include <errno.h>
|
||||
#include <math.h>
|
||||
#include <cutils/log.h>
|
||||
|
||||
#include "audio_hw.h"
|
||||
#include "platform.h"
|
||||
#include "platform_api.h"
|
||||
#include <stdlib.h>
|
||||
#include <cutils/str_parms.h>
|
||||
#include "audio_extn/tfa_98xx.h"
|
||||
#include "audio_extn.h"
|
||||
|
||||
#define AUDIO_PARAMETER_HFP_ENABLE "hfp_enable"
|
||||
#define AUDIO_PARAMETER_HFP_SET_SAMPLING_RATE "hfp_set_sampling_rate"
|
||||
#define AUDIO_PARAMETER_KEY_HFP_VOLUME "hfp_volume"
|
||||
#define AUDIO_PARAMETER_HFP_VOL_MIXER_CTL "hfp_vol_mixer_ctl"
|
||||
#define AUDIO_PARAMATER_HFP_VALUE_MAX 128
|
||||
|
||||
#define AUDIO_PARAMETER_KEY_HFP_MIC_VOLUME "hfp_mic_volume"
|
||||
#define PLAYBACK_VOLUME_MAX 0x2000
|
||||
#define CAPTURE_VOLUME_DEFAULT (15.0)
|
||||
|
||||
static int32_t start_hfp(struct audio_device *adev,
|
||||
struct str_parms *parms);
|
||||
|
||||
static int32_t stop_hfp(struct audio_device *adev);
|
||||
|
||||
struct hfp_module {
|
||||
struct pcm *hfp_sco_rx;
|
||||
struct pcm *hfp_sco_tx;
|
||||
struct pcm *hfp_pcm_rx;
|
||||
struct pcm *hfp_pcm_tx;
|
||||
float hfp_volume;
|
||||
float mic_volume;
|
||||
char hfp_vol_mixer_ctl[AUDIO_PARAMATER_HFP_VALUE_MAX];
|
||||
bool is_hfp_running;
|
||||
bool mic_mute;
|
||||
audio_usecase_t ucid;
|
||||
};
|
||||
|
||||
static struct hfp_module hfpmod = {
|
||||
.hfp_sco_rx = NULL,
|
||||
.hfp_sco_tx = NULL,
|
||||
.hfp_pcm_rx = NULL,
|
||||
.hfp_pcm_tx = NULL,
|
||||
.hfp_volume = 0,
|
||||
.mic_volume = CAPTURE_VOLUME_DEFAULT,
|
||||
.hfp_vol_mixer_ctl = {0, },
|
||||
.is_hfp_running = 0,
|
||||
.mic_mute = 0,
|
||||
.ucid = USECASE_AUDIO_HFP_SCO,
|
||||
};
|
||||
static struct pcm_config pcm_config_hfp = {
|
||||
.channels = 1,
|
||||
.rate = 8000,
|
||||
.period_size = 240,
|
||||
.period_count = 2,
|
||||
.format = PCM_FORMAT_S16_LE,
|
||||
.start_threshold = 0,
|
||||
.stop_threshold = INT_MAX,
|
||||
.avail_min = 0,
|
||||
};
|
||||
|
||||
static int32_t hfp_set_volume(struct audio_device *adev, float value)
|
||||
{
|
||||
int32_t vol, ret = 0;
|
||||
struct mixer_ctl *ctl;
|
||||
|
||||
ALOGV("%s: entry", __func__);
|
||||
ALOGD("%s: (%f)\n", __func__, value);
|
||||
|
||||
hfpmod.hfp_volume = value;
|
||||
audio_extn_tfa_98xx_set_voice_vol(value);
|
||||
|
||||
if (value < 0.0) {
|
||||
ALOGW("%s: (%f) Under 0.0, assuming 0.0\n", __func__, value);
|
||||
value = 0.0;
|
||||
} else {
|
||||
value = ((value > 15.000000) ? 1.0 : (value / 15));
|
||||
ALOGW("%s: Volume brought with in range (%f)\n", __func__, value);
|
||||
}
|
||||
vol = lrint((value * 0x2000) + 0.5);
|
||||
|
||||
if (!hfpmod.is_hfp_running) {
|
||||
ALOGV("%s: HFP not active, ignoring set_hfp_volume call", __func__);
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
ALOGD("%s: Setting HFP volume to %d \n", __func__, vol);
|
||||
if (0 == hfpmod.hfp_vol_mixer_ctl[0]) {
|
||||
#ifdef EXTERNAL_BT_SUPPORTED
|
||||
strcpy(hfpmod.hfp_vol_mixer_ctl, "PRI AUXPCM LOOPBACK Volume");
|
||||
#else
|
||||
strcpy(hfpmod.hfp_vol_mixer_ctl, "Internal HFP RX Volume");
|
||||
#endif
|
||||
ALOGW("%s: Defaulting hfp mixer control to: %s",
|
||||
__func__, hfpmod.hfp_vol_mixer_ctl);
|
||||
}
|
||||
ctl = mixer_get_ctl_by_name(adev->mixer, hfpmod.hfp_vol_mixer_ctl);
|
||||
if (!ctl) {
|
||||
ALOGE("%s: Could not get ctl for mixer cmd - %s",
|
||||
__func__, hfpmod.hfp_vol_mixer_ctl);
|
||||
return -EINVAL;
|
||||
}
|
||||
if(mixer_ctl_set_value(ctl, 0, vol) < 0) {
|
||||
ALOGE("%s: Couldn't set HFP Volume: [%d]", __func__, vol);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
ALOGV("%s: exit", __func__);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
/*Set mic volume to value.
|
||||
*
|
||||
* This interface is used for mic volume control, set mic volume as value(range 0 ~ 15).
|
||||
*/
|
||||
static int hfp_set_mic_volume(struct audio_device *adev, float value)
|
||||
{
|
||||
int volume, ret = 0;
|
||||
char mixer_ctl_name[128];
|
||||
struct mixer_ctl *ctl;
|
||||
int pcm_device_id = HFP_ASM_RX_TX;
|
||||
|
||||
if (!hfpmod.is_hfp_running) {
|
||||
ALOGE("%s: HFP not active, ignoring set_hfp_mic_volume call", __func__);
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
if (value < 0.0) {
|
||||
ALOGW("%s: (%f) Under 0.0, assuming 0.0\n", __func__, value);
|
||||
value = 0.0;
|
||||
} else if (value > CAPTURE_VOLUME_DEFAULT) {
|
||||
value = CAPTURE_VOLUME_DEFAULT;
|
||||
ALOGW("%s: Volume brought within range (%f)\n", __func__, value);
|
||||
}
|
||||
|
||||
value = value / CAPTURE_VOLUME_DEFAULT;
|
||||
memset(mixer_ctl_name, 0, sizeof(mixer_ctl_name));
|
||||
snprintf(mixer_ctl_name, sizeof(mixer_ctl_name),
|
||||
"Playback %d Volume", pcm_device_id);
|
||||
ctl = mixer_get_ctl_by_name(adev->mixer, mixer_ctl_name);
|
||||
if (!ctl) {
|
||||
ALOGE("%s: Could not get ctl for mixer cmd - %s",
|
||||
__func__, mixer_ctl_name);
|
||||
return -EINVAL;
|
||||
}
|
||||
volume = (int)(value * PLAYBACK_VOLUME_MAX);
|
||||
|
||||
ALOGD("%s: Setting volume to %d (%s)\n", __func__, volume, mixer_ctl_name);
|
||||
if (mixer_ctl_set_value(ctl, 0, volume) < 0) {
|
||||
ALOGE("%s: Couldn't set HFP Volume: [%d]", __func__, volume);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static float hfp_get_mic_volume(struct audio_device *adev)
|
||||
{
|
||||
int volume, ret = 0;
|
||||
char mixer_ctl_name[128];
|
||||
struct mixer_ctl *ctl;
|
||||
int pcm_device_id = HFP_ASM_RX_TX;
|
||||
float value = 0.0;
|
||||
|
||||
if (!hfpmod.is_hfp_running) {
|
||||
ALOGE("%s: HFP not active, ignoring set_hfp_mic_volume call", __func__);
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
memset(mixer_ctl_name, 0, sizeof(mixer_ctl_name));
|
||||
snprintf(mixer_ctl_name, sizeof(mixer_ctl_name),
|
||||
"Playback %d Volume", pcm_device_id);
|
||||
ctl = mixer_get_ctl_by_name(adev->mixer, mixer_ctl_name);
|
||||
if (!ctl) {
|
||||
ALOGE("%s: Could not get ctl for mixer cmd - %s",
|
||||
__func__, mixer_ctl_name);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
volume = mixer_ctl_get_value(ctl, 0);
|
||||
if ( volume < 0) {
|
||||
ALOGE("%s: Couldn't set HFP Volume: [%d]", __func__, volume);
|
||||
return -EINVAL;
|
||||
}
|
||||
ALOGD("%s: getting mic volume %d \n", __func__, volume);
|
||||
|
||||
value = (volume / PLAYBACK_VOLUME_MAX) * CAPTURE_VOLUME_DEFAULT;
|
||||
if (value < 0.0) {
|
||||
ALOGW("%s: (%f) Under 0.0, assuming 0.0\n", __func__, value);
|
||||
value = 0.0;
|
||||
} else if (value > CAPTURE_VOLUME_DEFAULT) {
|
||||
value = CAPTURE_VOLUME_DEFAULT;
|
||||
ALOGW("%s: Volume brought within range (%f)\n", __func__, value);
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
/*Set mic mute state.
|
||||
*
|
||||
* This interface is used for mic mute state control
|
||||
*/
|
||||
int audio_extn_hfp_set_mic_mute(struct audio_device *adev, bool state)
|
||||
{
|
||||
int rc = 0;
|
||||
|
||||
if (state == hfpmod.mic_mute)
|
||||
return rc;
|
||||
|
||||
if (state == true) {
|
||||
hfpmod.mic_volume = hfp_get_mic_volume(adev);
|
||||
}
|
||||
rc = hfp_set_mic_volume(adev, (state == true) ? 0.0 : hfpmod.mic_volume);
|
||||
adev->voice.mic_mute = state;
|
||||
hfpmod.mic_mute = state;
|
||||
ALOGD("%s: Setting mute state %d, rc %d\n", __func__, state, rc);
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int32_t start_hfp(struct audio_device *adev,
|
||||
struct str_parms *parms __unused)
|
||||
{
|
||||
int32_t i, ret = 0;
|
||||
struct audio_usecase *uc_info;
|
||||
int32_t pcm_dev_rx_id, pcm_dev_tx_id, pcm_dev_asm_rx_id, pcm_dev_asm_tx_id;
|
||||
|
||||
ALOGD("%s: enter", __func__);
|
||||
|
||||
if (adev->enable_hfp == true) {
|
||||
ALOGD("%s: HFP is already active!\n", __func__);
|
||||
return 0;
|
||||
}
|
||||
adev->enable_hfp = true;
|
||||
platform_set_mic_mute(adev->platform, false);
|
||||
|
||||
uc_info = (struct audio_usecase *)calloc(1, sizeof(struct audio_usecase));
|
||||
uc_info->id = hfpmod.ucid;
|
||||
uc_info->type = PCM_HFP_CALL;
|
||||
uc_info->stream.out = adev->primary_output;
|
||||
uc_info->devices = adev->primary_output->devices;
|
||||
uc_info->in_snd_device = SND_DEVICE_NONE;
|
||||
uc_info->out_snd_device = SND_DEVICE_NONE;
|
||||
|
||||
list_add_tail(&adev->usecase_list, &uc_info->list);
|
||||
|
||||
audio_extn_tfa_98xx_set_mode_bt();
|
||||
|
||||
select_devices(adev, hfpmod.ucid);
|
||||
|
||||
pcm_dev_rx_id = platform_get_pcm_device_id(uc_info->id, PCM_PLAYBACK);
|
||||
pcm_dev_tx_id = platform_get_pcm_device_id(uc_info->id, PCM_CAPTURE);
|
||||
pcm_dev_asm_rx_id = HFP_ASM_RX_TX;
|
||||
pcm_dev_asm_tx_id = HFP_ASM_RX_TX;
|
||||
if (pcm_dev_rx_id < 0 || pcm_dev_tx_id < 0 ||
|
||||
pcm_dev_asm_rx_id < 0 || pcm_dev_asm_tx_id < 0 ) {
|
||||
ALOGE("%s: Invalid PCM devices (rx: %d tx: %d asm: rx tx %d) for the usecase(%d)",
|
||||
__func__, pcm_dev_rx_id, pcm_dev_tx_id, pcm_dev_asm_rx_id, uc_info->id);
|
||||
ret = -EIO;
|
||||
goto exit;
|
||||
}
|
||||
|
||||
ALOGV("%s: HFP PCM devices (hfp rx tx: %d pcm rx tx: %d) for the usecase(%d)",
|
||||
__func__, pcm_dev_rx_id, pcm_dev_tx_id, uc_info->id);
|
||||
|
||||
ALOGV("%s: Opening PCM playback device card_id(%d) device_id(%d)",
|
||||
__func__, adev->snd_card, pcm_dev_rx_id);
|
||||
hfpmod.hfp_sco_rx = pcm_open(adev->snd_card,
|
||||
pcm_dev_asm_rx_id,
|
||||
PCM_OUT, &pcm_config_hfp);
|
||||
if (hfpmod.hfp_sco_rx && !pcm_is_ready(hfpmod.hfp_sco_rx)) {
|
||||
ALOGE("%s: %s", __func__, pcm_get_error(hfpmod.hfp_sco_rx));
|
||||
ret = -EIO;
|
||||
goto exit;
|
||||
}
|
||||
ALOGD("%s: Opening PCM capture device card_id(%d) device_id(%d)",
|
||||
__func__, adev->snd_card, pcm_dev_tx_id);
|
||||
|
||||
if (audio_extn_tfa_98xx_is_supported() == false) {
|
||||
hfpmod.hfp_pcm_rx = pcm_open(adev->snd_card,
|
||||
pcm_dev_rx_id,
|
||||
PCM_OUT, &pcm_config_hfp);
|
||||
if (hfpmod.hfp_pcm_rx && !pcm_is_ready(hfpmod.hfp_pcm_rx)) {
|
||||
ALOGE("%s: %s", __func__, pcm_get_error(hfpmod.hfp_pcm_rx));
|
||||
ret = -EIO;
|
||||
goto exit;
|
||||
}
|
||||
}
|
||||
hfpmod.hfp_sco_tx = pcm_open(adev->snd_card,
|
||||
pcm_dev_asm_tx_id,
|
||||
PCM_IN, &pcm_config_hfp);
|
||||
if (hfpmod.hfp_sco_tx && !pcm_is_ready(hfpmod.hfp_sco_tx)) {
|
||||
ALOGE("%s: %s", __func__, pcm_get_error(hfpmod.hfp_sco_tx));
|
||||
ret = -EIO;
|
||||
goto exit;
|
||||
}
|
||||
ALOGV("%s: Opening PCM capture device card_id(%d) device_id(%d)",
|
||||
__func__, adev->snd_card, pcm_dev_tx_id);
|
||||
|
||||
if (audio_extn_tfa_98xx_is_supported() == false) {
|
||||
hfpmod.hfp_pcm_tx = pcm_open(adev->snd_card,
|
||||
pcm_dev_tx_id,
|
||||
PCM_IN, &pcm_config_hfp);
|
||||
if (hfpmod.hfp_pcm_tx && !pcm_is_ready(hfpmod.hfp_pcm_tx)) {
|
||||
ALOGE("%s: %s", __func__, pcm_get_error(hfpmod.hfp_pcm_tx));
|
||||
ret = -EIO;
|
||||
goto exit;
|
||||
}
|
||||
}
|
||||
pcm_start(hfpmod.hfp_sco_rx);
|
||||
pcm_start(hfpmod.hfp_sco_tx);
|
||||
if (audio_extn_tfa_98xx_is_supported() == false) {
|
||||
pcm_start(hfpmod.hfp_pcm_rx);
|
||||
pcm_start(hfpmod.hfp_pcm_tx);
|
||||
}
|
||||
|
||||
audio_extn_tfa_98xx_enable_speaker();
|
||||
|
||||
hfpmod.is_hfp_running = true;
|
||||
hfp_set_volume(adev, hfpmod.hfp_volume);
|
||||
|
||||
/* Set mic volume by mute status, we don't provide set mic volume in phone app, only
|
||||
provide mute and unmute. */
|
||||
audio_extn_hfp_set_mic_mute(adev, adev->mic_muted);
|
||||
|
||||
ALOGD("%s: exit: status(%d)", __func__, ret);
|
||||
return 0;
|
||||
|
||||
exit:
|
||||
stop_hfp(adev);
|
||||
ALOGE("%s: Problem in HFP start: status(%d)", __func__, ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int32_t stop_hfp(struct audio_device *adev)
|
||||
{
|
||||
int32_t i, ret = 0;
|
||||
struct audio_usecase *uc_info;
|
||||
|
||||
ALOGD("%s: enter", __func__);
|
||||
hfpmod.is_hfp_running = false;
|
||||
|
||||
/* 1. Close the PCM devices */
|
||||
if (hfpmod.hfp_sco_rx) {
|
||||
pcm_close(hfpmod.hfp_sco_rx);
|
||||
hfpmod.hfp_sco_rx = NULL;
|
||||
}
|
||||
if (hfpmod.hfp_sco_tx) {
|
||||
pcm_close(hfpmod.hfp_sco_tx);
|
||||
hfpmod.hfp_sco_tx = NULL;
|
||||
}
|
||||
if (hfpmod.hfp_pcm_rx) {
|
||||
pcm_close(hfpmod.hfp_pcm_rx);
|
||||
hfpmod.hfp_pcm_rx = NULL;
|
||||
}
|
||||
if (hfpmod.hfp_pcm_tx) {
|
||||
pcm_close(hfpmod.hfp_pcm_tx);
|
||||
hfpmod.hfp_pcm_tx = NULL;
|
||||
}
|
||||
|
||||
uc_info = get_usecase_from_list(adev, hfpmod.ucid);
|
||||
if (uc_info == NULL) {
|
||||
ALOGE("%s: Could not find the usecase (%d) in the list",
|
||||
__func__, hfpmod.ucid);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* 2. Get and set stream specific mixer controls */
|
||||
disable_audio_route(adev, uc_info);
|
||||
|
||||
/* 3. Disable the rx and tx devices */
|
||||
disable_snd_device(adev, uc_info->out_snd_device);
|
||||
disable_snd_device(adev, uc_info->in_snd_device);
|
||||
|
||||
/* Disable the echo reference for HFP Tx */
|
||||
platform_set_echo_reference(adev, false, AUDIO_DEVICE_NONE);
|
||||
|
||||
/* Set the unmute Tx mixer control */
|
||||
if (voice_get_mic_mute(adev)) {
|
||||
platform_set_mic_mute(adev->platform, false);
|
||||
ALOGD("%s: unMute HFP Tx", __func__);
|
||||
}
|
||||
adev->enable_hfp = false;
|
||||
|
||||
list_remove(&uc_info->list);
|
||||
free(uc_info);
|
||||
|
||||
ALOGD("%s: exit: status(%d)", __func__, ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool audio_extn_hfp_is_active(struct audio_device *adev)
|
||||
{
|
||||
struct audio_usecase *hfp_usecase = NULL;
|
||||
hfp_usecase = get_usecase_from_list(adev, hfpmod.ucid);
|
||||
|
||||
if (hfp_usecase != NULL)
|
||||
return true;
|
||||
else
|
||||
return false;
|
||||
}
|
||||
|
||||
audio_usecase_t audio_extn_hfp_get_usecase()
|
||||
{
|
||||
return hfpmod.ucid;
|
||||
}
|
||||
|
||||
void audio_extn_hfp_set_parameters(struct audio_device *adev, struct str_parms *parms)
|
||||
{
|
||||
int ret;
|
||||
int rate;
|
||||
int val;
|
||||
float vol;
|
||||
char value[AUDIO_PARAMATER_HFP_VALUE_MAX] = {0, };
|
||||
|
||||
ret = str_parms_get_str(parms, AUDIO_PARAMETER_HFP_ENABLE, value,
|
||||
sizeof(value));
|
||||
if (ret >= 0) {
|
||||
if (!strncmp(value,"true",sizeof(value)))
|
||||
ret = start_hfp(adev,parms);
|
||||
else
|
||||
stop_hfp(adev);
|
||||
}
|
||||
memset(value, 0, sizeof(value));
|
||||
ret = str_parms_get_str(parms,AUDIO_PARAMETER_HFP_SET_SAMPLING_RATE, value,
|
||||
sizeof(value));
|
||||
if (ret >= 0) {
|
||||
rate = atoi(value);
|
||||
if (rate == 8000){
|
||||
hfpmod.ucid = USECASE_AUDIO_HFP_SCO;
|
||||
pcm_config_hfp.rate = rate;
|
||||
} else if (rate == 16000){
|
||||
hfpmod.ucid = USECASE_AUDIO_HFP_SCO_WB;
|
||||
pcm_config_hfp.rate = rate;
|
||||
} else
|
||||
ALOGE("Unsupported rate..");
|
||||
}
|
||||
|
||||
if (hfpmod.is_hfp_running) {
|
||||
memset(value, 0, sizeof(value));
|
||||
ret = str_parms_get_str(parms, AUDIO_PARAMETER_STREAM_ROUTING,
|
||||
value, sizeof(value));
|
||||
if (ret >= 0) {
|
||||
val = atoi(value);
|
||||
if (val > 0)
|
||||
select_devices(adev, hfpmod.ucid);
|
||||
}
|
||||
}
|
||||
|
||||
memset(value, 0, sizeof(value));
|
||||
ret = str_parms_get_str(parms, AUDIO_PARAMETER_HFP_VOL_MIXER_CTL,
|
||||
value, sizeof(value));
|
||||
if (ret >= 0) {
|
||||
ALOGD("%s: mixer ctl name: %s", __func__, value);
|
||||
strcpy(hfpmod.hfp_vol_mixer_ctl, value);
|
||||
str_parms_del(parms, AUDIO_PARAMETER_HFP_VOL_MIXER_CTL);
|
||||
}
|
||||
|
||||
memset(value, 0, sizeof(value));
|
||||
ret = str_parms_get_str(parms, AUDIO_PARAMETER_KEY_HFP_VOLUME,
|
||||
value, sizeof(value));
|
||||
if (ret >= 0) {
|
||||
if (sscanf(value, "%f", &vol) != 1){
|
||||
ALOGE("%s: error in retrieving hfp volume", __func__);
|
||||
ret = -EIO;
|
||||
goto exit;
|
||||
}
|
||||
ALOGD("%s: set_hfp_volume usecase, Vol: [%f]", __func__, vol);
|
||||
hfp_set_volume(adev, vol);
|
||||
}
|
||||
|
||||
memset(value, 0, sizeof(value));
|
||||
ret = str_parms_get_str(parms, AUDIO_PARAMETER_KEY_HFP_MIC_VOLUME,
|
||||
value, sizeof(value));
|
||||
if (ret >= 0) {
|
||||
if (sscanf(value, "%f", &vol) != 1){
|
||||
ALOGE("%s: error in retrieving hfp mic volume", __func__);
|
||||
ret = -EIO;
|
||||
goto exit;
|
||||
}
|
||||
ALOGD("%s: set_hfp_mic_volume usecase, Vol: [%f]", __func__, vol);
|
||||
hfp_set_mic_volume(adev, vol);
|
||||
}
|
||||
|
||||
exit:
|
||||
ALOGV("%s Exit",__func__);
|
||||
}
|
147
android/hardware/qcom/audio/hal/audio_extn/hwdep_cal.c
Normal file
147
android/hardware/qcom/audio/hal/audio_extn/hwdep_cal.c
Normal file
|
@ -0,0 +1,147 @@
|
|||
/*
|
||||
* Copyright (C) 2015 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 "hardware_cal"
|
||||
/*#define LOG_NDEBUG 0*/
|
||||
#define LOG_NDDEBUG 0
|
||||
|
||||
#ifdef HWDEP_CAL_ENABLED
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <dlfcn.h>
|
||||
#include <fcntl.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <unistd.h>
|
||||
#include <cutils/log.h>
|
||||
#include <audio_hw.h>
|
||||
#include "audio_extn.h"
|
||||
#include "sound/msmcal-hwdep.h"
|
||||
|
||||
#define SOUND_TRIGGER_DEVICE_HANDSET_MONO_LOW_POWER_ACDB_ID (100)
|
||||
#define MAX_CAL_NAME 20
|
||||
|
||||
typedef struct acdb_audio_cal_cfg {
|
||||
uint32_t persist;
|
||||
uint32_t snd_dev_id;
|
||||
audio_devices_t dev_id;
|
||||
int32_t acdb_dev_id;
|
||||
uint32_t app_type;
|
||||
uint32_t topo_id;
|
||||
uint32_t sampling_rate;
|
||||
uint32_t cal_type;
|
||||
uint32_t module_id;
|
||||
uint32_t param_id;
|
||||
} acdb_audio_cal_cfg_t;
|
||||
|
||||
struct param_data {
|
||||
int use_case;
|
||||
int acdb_id;
|
||||
int get_size;
|
||||
int buff_size;
|
||||
int data_size;
|
||||
void *buff;
|
||||
};
|
||||
|
||||
char cal_name_info[WCD9XXX_MAX_CAL][MAX_CAL_NAME] = {
|
||||
[WCD9XXX_ANC_CAL] = "anc_cal",
|
||||
[WCD9XXX_MBHC_CAL] = "mbhc_cal",
|
||||
[WCD9XXX_MAD_CAL] = "mad_cal",
|
||||
};
|
||||
|
||||
typedef int (*acdb_get_calibration_t)(char *attr, int size, void *data);
|
||||
acdb_get_calibration_t acdb_get_calibration;
|
||||
|
||||
static int hw_util_open(int card_no)
|
||||
{
|
||||
int fd = -1;
|
||||
char dev_name[256];
|
||||
|
||||
snprintf(dev_name, sizeof(dev_name), "/dev/snd/hwC%uD%u",
|
||||
card_no, WCD9XXX_CODEC_HWDEP_NODE);
|
||||
ALOGD("%s: Opening device %s\n", __func__, dev_name);
|
||||
fd = open(dev_name, O_WRONLY);
|
||||
if (fd < 0) {
|
||||
ALOGE("%s: cannot open device '%s'\n", __func__, dev_name);
|
||||
return fd;
|
||||
}
|
||||
ALOGD("%s: success", __func__);
|
||||
return fd;
|
||||
}
|
||||
|
||||
static int send_codec_cal(acdb_get_calibration_t acdb_loader_get_calibration, int fd)
|
||||
{
|
||||
int ret = 0, type;
|
||||
|
||||
for (type = WCD9XXX_ANC_CAL; type < WCD9XXX_MAX_CAL; type++) {
|
||||
struct wcdcal_ioctl_buffer codec_buffer;
|
||||
struct param_data calib;
|
||||
|
||||
if (!strcmp(cal_name_info[type], "mad_cal"))
|
||||
calib.acdb_id = SOUND_TRIGGER_DEVICE_HANDSET_MONO_LOW_POWER_ACDB_ID;
|
||||
calib.get_size = 1;
|
||||
ret = acdb_loader_get_calibration(cal_name_info[type], sizeof(struct param_data),
|
||||
&calib);
|
||||
if (ret < 0) {
|
||||
ALOGE("%s get_calibration failed\n", __func__);
|
||||
return ret;
|
||||
}
|
||||
calib.get_size = 0;
|
||||
calib.buff = malloc(calib.buff_size);
|
||||
ret = acdb_loader_get_calibration(cal_name_info[type],
|
||||
sizeof(struct param_data), &calib);
|
||||
if (ret < 0) {
|
||||
ALOGE("%s get_calibration failed\n", __func__);
|
||||
free(calib.buff);
|
||||
return ret;
|
||||
}
|
||||
codec_buffer.buffer = calib.buff;
|
||||
codec_buffer.size = calib.data_size;
|
||||
codec_buffer.cal_type = type;
|
||||
if (ioctl(fd, SNDRV_CTL_IOCTL_HWDEP_CAL_TYPE, &codec_buffer) < 0)
|
||||
ALOGE("Failed to call ioctl for %s err=%d",
|
||||
cal_name_info[type], errno);
|
||||
ALOGD("%s cal sent for %s", __func__, cal_name_info[type]);
|
||||
free(calib.buff);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
void audio_extn_hwdep_cal_send(int snd_card, void *acdb_handle)
|
||||
{
|
||||
int fd;
|
||||
|
||||
fd = hw_util_open(snd_card);
|
||||
if (fd == -1) {
|
||||
ALOGE("%s error open\n", __func__);
|
||||
return;
|
||||
}
|
||||
|
||||
acdb_get_calibration = (acdb_get_calibration_t)
|
||||
dlsym(acdb_handle, "acdb_loader_get_calibration");
|
||||
|
||||
if (acdb_get_calibration == NULL) {
|
||||
ALOGE("%s: ERROR. dlsym Error:%s acdb_loader_get_calibration", __func__,
|
||||
dlerror());
|
||||
return;
|
||||
}
|
||||
if (send_codec_cal(acdb_get_calibration, fd) < 0)
|
||||
ALOGE("%s: Could not send anc cal", __FUNCTION__);
|
||||
|
||||
close(fd);
|
||||
}
|
||||
|
||||
#endif
|
670
android/hardware/qcom/audio/hal/audio_extn/sndmonitor.c
Normal file
670
android/hardware/qcom/audio/hal/audio_extn/sndmonitor.c
Normal file
|
@ -0,0 +1,670 @@
|
|||
/*
|
||||
* Copyright (C) 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 "audio_hw_sndmonitor"
|
||||
/*#define LOG_NDEBUG 0*/
|
||||
#define LOG_NDDEBUG 0
|
||||
|
||||
/* monitor sound card, cpe state
|
||||
|
||||
audio_dev registers for a callback from this module in adev_open
|
||||
Each stream in audio_hal registers for a callback in
|
||||
adev_open_*_stream.
|
||||
|
||||
A thread is spawned to poll() on sound card state files in /proc.
|
||||
On observing a sound card state change, this thread invokes the
|
||||
callbacks registered.
|
||||
|
||||
Callbacks are deregistered in adev_close_*_stream and adev_close
|
||||
*/
|
||||
#include <stdlib.h>
|
||||
#include <dirent.h>
|
||||
#include <unistd.h>
|
||||
#include <fcntl.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/poll.h>
|
||||
#include <pthread.h>
|
||||
#include <cutils/list.h>
|
||||
#include <cutils/hashmap.h>
|
||||
#include <cutils/log.h>
|
||||
#include <cutils/str_parms.h>
|
||||
#include <ctype.h>
|
||||
|
||||
#include "audio_hw.h"
|
||||
#include "audio_extn.h"
|
||||
|
||||
//#define MONITOR_DEVICE_EVENTS
|
||||
#define CPE_MAGIC_NUM 0x2000
|
||||
#define MAX_CPE_SLEEP_RETRY 2
|
||||
#define CPE_SLEEP_WAIT 100
|
||||
|
||||
#define MAX_SLEEP_RETRY 100
|
||||
#define AUDIO_INIT_SLEEP_WAIT 100 /* 100 ms */
|
||||
|
||||
#define AUDIO_PARAMETER_KEY_EXT_AUDIO_DEVICE "ext_audio_device"
|
||||
|
||||
typedef enum {
|
||||
audio_event_on,
|
||||
audio_event_off
|
||||
} audio_event_status;
|
||||
|
||||
typedef struct {
|
||||
int card;
|
||||
int fd;
|
||||
struct listnode node; // membership in sndcards list
|
||||
card_status_t status;
|
||||
} sndcard_t;
|
||||
|
||||
typedef struct {
|
||||
char * dev;
|
||||
int fd;
|
||||
int status;
|
||||
struct listnode node; // membership in deviceevents list;
|
||||
} dev_event_t;
|
||||
|
||||
typedef void (* notifyfn)(const void * target, const char * msg);
|
||||
|
||||
typedef struct {
|
||||
const void * target;
|
||||
notifyfn notify;
|
||||
struct listnode cards;
|
||||
unsigned int num_cards;
|
||||
struct listnode dev_events;
|
||||
unsigned int num_dev_events;
|
||||
pthread_t monitor_thread;
|
||||
int intpipe[2];
|
||||
Hashmap * listeners; // from stream * -> callback func
|
||||
bool initcheck;
|
||||
} sndmonitor_state_t;
|
||||
|
||||
static sndmonitor_state_t sndmonitor;
|
||||
|
||||
static char * read_state(int fd)
|
||||
{
|
||||
struct stat buf;
|
||||
if (fstat(fd, &buf) < 0)
|
||||
return NULL;
|
||||
|
||||
off_t pos = lseek(fd, 0, SEEK_CUR);
|
||||
off_t avail = buf.st_size - pos;
|
||||
if (avail <= 0) {
|
||||
ALOGD("avail %ld", avail);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
char * state = (char *)calloc(avail+1, sizeof(char));
|
||||
if (!state)
|
||||
return NULL;
|
||||
|
||||
ssize_t bytes=read(fd, state, avail);
|
||||
if (bytes <= 0)
|
||||
return NULL;
|
||||
|
||||
// trim trailing whitespace
|
||||
while (bytes && isspace(*(state+bytes-1))) {
|
||||
*(state + bytes - 1) = '\0';
|
||||
--bytes;
|
||||
}
|
||||
lseek(fd, 0, SEEK_SET);
|
||||
return state;
|
||||
}
|
||||
|
||||
static int add_new_sndcard(int card, int fd)
|
||||
{
|
||||
sndcard_t * s = (sndcard_t *)calloc(sizeof(sndcard_t), 1);
|
||||
|
||||
if (!s)
|
||||
return -1;
|
||||
|
||||
s->card = card;
|
||||
s->fd = fd; // dup?
|
||||
|
||||
char * state = read_state(fd);
|
||||
bool online = state && !strcmp(state, "ONLINE");
|
||||
|
||||
ALOGV("card %d initial state %s %d", card, state, online);
|
||||
|
||||
if (state)
|
||||
free(state);
|
||||
|
||||
s->status = online ? CARD_STATUS_ONLINE : CARD_STATUS_OFFLINE;
|
||||
list_add_tail(&sndmonitor.cards, &s->node);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int enum_sndcards()
|
||||
{
|
||||
const char* cards = "/proc/asound/cards";
|
||||
int tries = 10;
|
||||
char *line = NULL;
|
||||
size_t len = 0;
|
||||
ssize_t bytes_read;
|
||||
char path[128] = {0};
|
||||
char *ptr, *saveptr, *card_id;
|
||||
int line_no=0;
|
||||
unsigned int num_cards=0, num_cpe=0;
|
||||
FILE *fp;
|
||||
int fd, ret;
|
||||
|
||||
while (--tries) {
|
||||
if ((fp = fopen(cards, "r")) == NULL) {
|
||||
ALOGE("Cannot open %s file to get list of sound cards", cards);
|
||||
usleep(100000);
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
if (!tries)
|
||||
return -ENODEV;
|
||||
|
||||
while ((bytes_read = getline(&line, &len, fp) != -1)) {
|
||||
// skip every other line to to match
|
||||
// the output format of /proc/asound/cards
|
||||
if (line_no++ % 2)
|
||||
continue;
|
||||
|
||||
ptr = strtok_r(line, " [", &saveptr);
|
||||
if (!ptr)
|
||||
continue;
|
||||
|
||||
card_id = strtok_r(saveptr+1, "]", &saveptr);
|
||||
if (!card_id)
|
||||
continue;
|
||||
|
||||
// Limit to sound cards associated with ADSP
|
||||
if ((strncasecmp(card_id, "msm", 3) != 0) &&
|
||||
(strncasecmp(card_id, "sdm", 3) != 0) &&
|
||||
(strncasecmp(card_id, "sdc", 3) != 0) &&
|
||||
(strncasecmp(card_id, "apq", 3) != 0)) {
|
||||
ALOGW("Skip over non-ADSP snd card %s", card_id);
|
||||
continue;
|
||||
}
|
||||
|
||||
snprintf(path, sizeof(path), "/proc/asound/card%s/state", ptr);
|
||||
ALOGV("Opening sound card state : %s", path);
|
||||
|
||||
fd = open(path, O_RDONLY);
|
||||
if (fd == -1) {
|
||||
ALOGE("Open %s failed : %s", path, strerror(errno));
|
||||
continue;
|
||||
}
|
||||
|
||||
ret = add_new_sndcard(atoi(ptr), fd);
|
||||
if (ret != 0) {
|
||||
close(fd); // card state fd ownership is taken by sndcard on success
|
||||
continue;
|
||||
}
|
||||
|
||||
num_cards++;
|
||||
|
||||
// query cpe state for this card as well
|
||||
tries=MAX_CPE_SLEEP_RETRY;
|
||||
snprintf(path, sizeof(path), "/proc/asound/card%s/cpe0_state", ptr);
|
||||
|
||||
if (access(path, R_OK) < 0) {
|
||||
ALOGW("access %s failed w/ err %s", path, strerror(errno));
|
||||
continue;
|
||||
}
|
||||
|
||||
ALOGV("Open cpe state card state %s", path);
|
||||
while (--tries) {
|
||||
if ((fd = open(path, O_RDONLY)) < 0) {
|
||||
ALOGW("Open cpe state card state failed, retry : %s", path);
|
||||
usleep(CPE_SLEEP_WAIT*1000);
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
if (!tries)
|
||||
continue;
|
||||
|
||||
ret = add_new_sndcard(CPE_MAGIC_NUM+num_cpe, fd);
|
||||
if (ret != 0) {
|
||||
close(fd); // card state fd ownership is taken by sndcard on success
|
||||
continue;
|
||||
}
|
||||
|
||||
num_cpe++;
|
||||
num_cards++;
|
||||
}
|
||||
if (line)
|
||||
free(line);
|
||||
fclose(fp);
|
||||
ALOGV("sndmonitor registerer num_cards %d", num_cards);
|
||||
sndmonitor.num_cards = num_cards;
|
||||
return num_cards ? 0 : -1;
|
||||
}
|
||||
|
||||
static void free_sndcards()
|
||||
{
|
||||
while (!list_empty(&sndmonitor.cards)) {
|
||||
struct listnode * n = list_head(&sndmonitor.cards);
|
||||
sndcard_t * s = node_to_item(n, sndcard_t, node);
|
||||
list_remove(n);
|
||||
close(s->fd);
|
||||
free(s);
|
||||
}
|
||||
}
|
||||
|
||||
static int add_new_dev_event(char * d_name, int fd)
|
||||
{
|
||||
dev_event_t * d = (dev_event_t *)calloc(sizeof(dev_event_t), 1);
|
||||
|
||||
if (!d)
|
||||
return -1;
|
||||
|
||||
d->dev = strdup(d_name);
|
||||
d->fd = fd;
|
||||
list_add_tail(&sndmonitor.dev_events, &d->node);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int enum_dev_events()
|
||||
{
|
||||
const char* events_dir = "/sys/class/switch/";
|
||||
DIR *dp;
|
||||
struct dirent* in_file;
|
||||
int fd;
|
||||
char path[128] = {0};
|
||||
unsigned int num_dev_events = 0;
|
||||
|
||||
if ((dp = opendir(events_dir)) == NULL) {
|
||||
ALOGE("Cannot open switch directory %s err %s",
|
||||
events_dir, strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
|
||||
while ((in_file = readdir(dp)) != NULL) {
|
||||
if (!strstr(in_file->d_name, "qc_"))
|
||||
continue;
|
||||
|
||||
snprintf(path, sizeof(path), "%s/%s/state",
|
||||
events_dir, in_file->d_name);
|
||||
|
||||
ALOGV("Opening audio dev event state : %s ", path);
|
||||
fd = open(path, O_RDONLY);
|
||||
if (fd == -1) {
|
||||
ALOGE("Open %s failed : %s", path, strerror(errno));
|
||||
} else {
|
||||
if (!add_new_dev_event(in_file->d_name, fd))
|
||||
num_dev_events++;
|
||||
}
|
||||
}
|
||||
closedir(dp);
|
||||
sndmonitor.num_dev_events = num_dev_events;
|
||||
return num_dev_events ? 0 : -1;
|
||||
}
|
||||
|
||||
static void free_dev_events()
|
||||
{
|
||||
while (!list_empty(&sndmonitor.dev_events)) {
|
||||
struct listnode * n = list_head(&sndmonitor.dev_events);
|
||||
dev_event_t * d = node_to_item(n, dev_event_t, node);
|
||||
list_remove(n);
|
||||
close(d->fd);
|
||||
free(d->dev);
|
||||
free(d);
|
||||
}
|
||||
}
|
||||
|
||||
static int notify(const struct str_parms * params)
|
||||
{
|
||||
if (!params)
|
||||
return -1;
|
||||
|
||||
char * str = str_parms_to_str((struct str_parms *)params);
|
||||
|
||||
if (!str)
|
||||
return -1;
|
||||
|
||||
if (sndmonitor.notify)
|
||||
sndmonitor.notify(sndmonitor.target, str);
|
||||
|
||||
ALOGV("%s", str);
|
||||
free(str);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int on_dev_event(dev_event_t * dev_event)
|
||||
{
|
||||
char state_buf[2];
|
||||
if (read(dev_event->fd, state_buf, 1) <= 0)
|
||||
return -1;
|
||||
|
||||
lseek(dev_event->fd, 0, SEEK_SET);
|
||||
state_buf[1]='\0';
|
||||
if (atoi(state_buf) == dev_event->status)
|
||||
return 0;
|
||||
|
||||
dev_event->status = atoi(state_buf);
|
||||
|
||||
struct str_parms * params = str_parms_create();
|
||||
|
||||
if (!params)
|
||||
return -1;
|
||||
|
||||
char val[32] = {0};
|
||||
snprintf(val, sizeof(val), "%s,%s", dev_event->dev,
|
||||
dev_event->status ? "ON" : "OFF");
|
||||
|
||||
if (str_parms_add_str(params, AUDIO_PARAMETER_KEY_EXT_AUDIO_DEVICE, val) < 0)
|
||||
return -1;
|
||||
|
||||
int ret = notify(params);
|
||||
str_parms_destroy(params);
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool on_sndcard_state_update(sndcard_t * s)
|
||||
{
|
||||
char rd_buf[9]={0};
|
||||
card_status_t status;
|
||||
|
||||
if (read(s->fd, rd_buf, 8) <= 0)
|
||||
return -1;
|
||||
|
||||
rd_buf[8] = '\0';
|
||||
lseek(s->fd, 0, SEEK_SET);
|
||||
|
||||
ALOGV("card num %d, new state %s", s->card, rd_buf);
|
||||
|
||||
bool is_cpe = (s->card >= CPE_MAGIC_NUM);
|
||||
if (strstr(rd_buf, "OFFLINE"))
|
||||
status = CARD_STATUS_OFFLINE;
|
||||
else if (strstr(rd_buf, "ONLINE"))
|
||||
status = CARD_STATUS_ONLINE;
|
||||
else {
|
||||
ALOGE("unknown state");
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (status == s->status) // no change
|
||||
return 0;
|
||||
|
||||
s->status = status;
|
||||
|
||||
struct str_parms * params = str_parms_create();
|
||||
|
||||
if (!params)
|
||||
return -1;
|
||||
|
||||
char val[32] = {0};
|
||||
// cpe actual card num is (card - MAGIC_NUM). so subtract accordingly
|
||||
snprintf(val, sizeof(val), "%d,%s", s->card - (is_cpe ? CPE_MAGIC_NUM : 0),
|
||||
status == CARD_STATUS_ONLINE ? "ONLINE" : "OFFLINE");
|
||||
|
||||
if (str_parms_add_str(params, is_cpe ? "CPE_STATUS" : "SND_CARD_STATUS",
|
||||
val) < 0)
|
||||
return -1;
|
||||
|
||||
int ret = notify(params);
|
||||
str_parms_destroy(params);
|
||||
return ret;
|
||||
}
|
||||
|
||||
void * monitor_thread_loop(void * args __unused)
|
||||
{
|
||||
ALOGV("Start threadLoop()");
|
||||
unsigned int num_poll_fds = sndmonitor.num_cards +
|
||||
sndmonitor.num_dev_events + 1/*pipe*/;
|
||||
struct pollfd * pfd = (struct pollfd *)calloc(sizeof(struct pollfd),
|
||||
num_poll_fds);
|
||||
if (!pfd)
|
||||
return NULL;
|
||||
|
||||
pfd[0].fd = sndmonitor.intpipe[0];
|
||||
pfd[0].events = POLLPRI|POLLIN;
|
||||
|
||||
int i=1;
|
||||
struct listnode *node;
|
||||
list_for_each(node, &sndmonitor.cards) {
|
||||
sndcard_t * s = node_to_item(node, sndcard_t, node);
|
||||
pfd[i].fd = s->fd;
|
||||
pfd[i].events = POLLPRI;
|
||||
++i;
|
||||
}
|
||||
|
||||
list_for_each(node, &sndmonitor.dev_events) {
|
||||
dev_event_t * d = node_to_item(node, dev_event_t, node);
|
||||
pfd[i].fd = d->fd;
|
||||
pfd[i].events = POLLPRI;
|
||||
++i;
|
||||
}
|
||||
|
||||
while (1) {
|
||||
if (poll(pfd, num_poll_fds, -1) < 0) {
|
||||
int errno_ = errno;
|
||||
ALOGE("poll() failed w/ err %s", strerror(errno));
|
||||
switch (errno_) {
|
||||
case EINTR:
|
||||
case ENOMEM:
|
||||
sleep(2);
|
||||
continue;
|
||||
default:
|
||||
/* above errors can be caused due to current system
|
||||
state .. any other error is not expected */
|
||||
LOG_ALWAYS_FATAL("unxpected poll() system call failure");
|
||||
break;
|
||||
}
|
||||
}
|
||||
ALOGV("out of poll()");
|
||||
|
||||
#define READY_TO_READ(p) ((p)->revents & (POLLIN|POLLPRI))
|
||||
#define ERROR_IN_FD(p) ((p)->revents & (POLLERR|POLLHUP|POLLNVAL))
|
||||
|
||||
// check if requested to exit
|
||||
if (READY_TO_READ(&pfd[0])) {
|
||||
char buf[2]={0};
|
||||
read(pfd[0].fd, buf, 1);
|
||||
if (!strcmp(buf, "Q"))
|
||||
break;
|
||||
} else if (ERROR_IN_FD(&pfd[0])) {
|
||||
// do not consider for poll again
|
||||
// POLLERR - can this happen?
|
||||
// POLLHUP - adev must not close pipe
|
||||
// POLLNVAL - fd is valid
|
||||
LOG_ALWAYS_FATAL("unxpected error in pipe poll fd 0x%x",
|
||||
pfd[0].revents);
|
||||
pfd[0].fd *= -1;
|
||||
}
|
||||
|
||||
i=1;
|
||||
list_for_each(node, &sndmonitor.cards) {
|
||||
sndcard_t * s = node_to_item(node, sndcard_t, node);
|
||||
if (READY_TO_READ(&pfd[i]))
|
||||
on_sndcard_state_update(s);
|
||||
else if (ERROR_IN_FD(&pfd[i])) {
|
||||
// do not consider for poll again
|
||||
// POLLERR - can this happen as we are reading from a fs?
|
||||
// POLLHUP - not valid for cardN/state
|
||||
// POLLNVAL - fd is valid
|
||||
LOG_ALWAYS_FATAL("unxpected error in card poll fd 0x%x",
|
||||
pfd[i].revents);
|
||||
pfd[i].fd *= -1;
|
||||
}
|
||||
++i;
|
||||
}
|
||||
|
||||
list_for_each(node, &sndmonitor.dev_events) {
|
||||
dev_event_t * d = node_to_item(node, dev_event_t, node);
|
||||
if (READY_TO_READ(&pfd[i]))
|
||||
on_dev_event(d);
|
||||
else if (ERROR_IN_FD(&pfd[i])) {
|
||||
// do not consider for poll again
|
||||
// POLLERR - can this happen as we are reading from a fs?
|
||||
// POLLHUP - not valid for switch/state
|
||||
// POLLNVAL - fd is valid
|
||||
LOG_ALWAYS_FATAL("unxpected error in dev poll fd 0x%x",
|
||||
pfd[i].revents);
|
||||
pfd[i].fd *= -1;
|
||||
}
|
||||
++i;
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// ---- listener static APIs ---- //
|
||||
static int hashfn(void * key)
|
||||
{
|
||||
return (int)key;
|
||||
}
|
||||
|
||||
static bool hasheq(void * key1, void *key2)
|
||||
{
|
||||
return key1 == key2;
|
||||
}
|
||||
|
||||
static bool snd_cb(void* key, void* value, void* context)
|
||||
{
|
||||
snd_mon_cb cb = (snd_mon_cb)value;
|
||||
cb(key, context);
|
||||
return true;
|
||||
}
|
||||
|
||||
static void snd_mon_update(const void * target __unused, const char * msg)
|
||||
{
|
||||
// target can be used to check if this message is intended for the
|
||||
// recipient or not. (using some statically saved state)
|
||||
|
||||
struct str_parms *parms = str_parms_create_str(msg);
|
||||
|
||||
if (!parms)
|
||||
return;
|
||||
|
||||
hashmapLock(sndmonitor.listeners);
|
||||
hashmapForEach(sndmonitor.listeners, snd_cb, parms);
|
||||
hashmapUnlock(sndmonitor.listeners);
|
||||
|
||||
str_parms_destroy(parms);
|
||||
}
|
||||
|
||||
static int listeners_init()
|
||||
{
|
||||
sndmonitor.listeners = hashmapCreate(5, hashfn, hasheq);
|
||||
if (!sndmonitor.listeners)
|
||||
return -1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int listeners_deinit()
|
||||
{
|
||||
// XXX TBD
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int add_listener(void *stream, snd_mon_cb cb)
|
||||
{
|
||||
Hashmap * map = sndmonitor.listeners;
|
||||
hashmapLock(map);
|
||||
hashmapPut(map, stream, cb);
|
||||
hashmapUnlock(map);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int del_listener(void * stream)
|
||||
{
|
||||
Hashmap * map = sndmonitor.listeners;
|
||||
hashmapLock(map);
|
||||
hashmapRemove(map, stream);
|
||||
hashmapUnlock(map);
|
||||
return 0;
|
||||
}
|
||||
|
||||
// --- public APIs --- //
|
||||
|
||||
int audio_extn_snd_mon_deinit()
|
||||
{
|
||||
if (!sndmonitor.initcheck)
|
||||
return -1;
|
||||
|
||||
write(sndmonitor.intpipe[1], "Q", 1);
|
||||
pthread_join(sndmonitor.monitor_thread, (void **) NULL);
|
||||
free_dev_events();
|
||||
listeners_deinit();
|
||||
free_sndcards();
|
||||
close(sndmonitor.intpipe[0]);
|
||||
close(sndmonitor.intpipe[1]);
|
||||
|
||||
sndmonitor.initcheck = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int audio_extn_snd_mon_init()
|
||||
{
|
||||
sndmonitor.notify = snd_mon_update;
|
||||
sndmonitor.target = NULL; // unused for now
|
||||
list_init(&sndmonitor.cards);
|
||||
list_init(&sndmonitor.dev_events);
|
||||
sndmonitor.initcheck = false;
|
||||
|
||||
if (pipe(sndmonitor.intpipe) < 0)
|
||||
goto pipe_error;
|
||||
|
||||
if (enum_sndcards() < 0)
|
||||
goto enum_sncards_error;
|
||||
|
||||
if (listeners_init() < 0)
|
||||
goto listeners_error;
|
||||
|
||||
#ifdef MONITOR_DEVICE_EVENTS
|
||||
enum_dev_events(); // failure here isn't fatal
|
||||
#endif
|
||||
|
||||
int ret = pthread_create(&sndmonitor.monitor_thread,
|
||||
(const pthread_attr_t *) NULL,
|
||||
monitor_thread_loop, NULL);
|
||||
|
||||
if (ret) {
|
||||
goto monitor_thread_create_error;
|
||||
}
|
||||
sndmonitor.initcheck = true;
|
||||
return 0;
|
||||
|
||||
monitor_thread_create_error:
|
||||
listeners_deinit();
|
||||
listeners_error:
|
||||
free_sndcards();
|
||||
enum_sncards_error:
|
||||
close(sndmonitor.intpipe[0]);
|
||||
close(sndmonitor.intpipe[1]);
|
||||
pipe_error:
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
int audio_extn_snd_mon_register_listener(void *stream, snd_mon_cb cb)
|
||||
{
|
||||
if (!sndmonitor.initcheck) {
|
||||
ALOGW("sndmonitor initcheck failed, cannot register");
|
||||
return -1;
|
||||
}
|
||||
|
||||
return add_listener(stream, cb);
|
||||
}
|
||||
|
||||
int audio_extn_snd_mon_unregister_listener(void * stream)
|
||||
{
|
||||
if (!sndmonitor.initcheck) {
|
||||
ALOGW("sndmonitor initcheck failed, cannot deregister");
|
||||
return -1;
|
||||
}
|
||||
|
||||
ALOGV("deregister listener for stream %p ", stream);
|
||||
return del_listener(stream);
|
||||
}
|
369
android/hardware/qcom/audio/hal/audio_extn/soundtrigger.c
Normal file
369
android/hardware/qcom/audio/hal/audio_extn/soundtrigger.c
Normal file
|
@ -0,0 +1,369 @@
|
|||
/*
|
||||
* Copyright (C) 2015 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 "soundtrigger"
|
||||
/* #define LOG_NDEBUG 0 */
|
||||
#define LOG_NDDEBUG 0
|
||||
|
||||
#include <errno.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdlib.h>
|
||||
#include <dlfcn.h>
|
||||
#include <pthread.h>
|
||||
#include <unistd.h>
|
||||
#include <cutils/log.h>
|
||||
#include "audio_hw.h"
|
||||
#include "audio_extn.h"
|
||||
#include "platform.h"
|
||||
#include "platform_api.h"
|
||||
#include "sound_trigger_prop_intf.h"
|
||||
|
||||
#define XSTR(x) STR(x)
|
||||
#define STR(x) #x
|
||||
|
||||
#ifdef __LP64__
|
||||
#define SOUND_TRIGGER_LIBRARY_PATH "/system/vendor/lib64/hw/sound_trigger.primary.%s.so"
|
||||
#else
|
||||
#define SOUND_TRIGGER_LIBRARY_PATH "/system/vendor/lib/hw/sound_trigger.primary.%s.so"
|
||||
#endif
|
||||
|
||||
struct sound_trigger_info {
|
||||
struct sound_trigger_session_info st_ses;
|
||||
bool lab_stopped;
|
||||
struct listnode list;
|
||||
};
|
||||
|
||||
struct sound_trigger_audio_device {
|
||||
void *lib_handle;
|
||||
struct audio_device *adev;
|
||||
sound_trigger_hw_call_back_t st_callback;
|
||||
struct listnode st_ses_list;
|
||||
pthread_mutex_t lock;
|
||||
};
|
||||
|
||||
static struct sound_trigger_audio_device *st_dev;
|
||||
|
||||
static struct sound_trigger_info *
|
||||
get_sound_trigger_info(int capture_handle)
|
||||
{
|
||||
struct sound_trigger_info *st_ses_info = NULL;
|
||||
struct listnode *node;
|
||||
ALOGV("%s: list %d capture_handle %d", __func__,
|
||||
list_empty(&st_dev->st_ses_list), capture_handle);
|
||||
list_for_each(node, &st_dev->st_ses_list) {
|
||||
st_ses_info = node_to_item(node, struct sound_trigger_info , list);
|
||||
if (st_ses_info->st_ses.capture_handle == capture_handle)
|
||||
return st_ses_info;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void stdev_snd_mon_cb(void * stream __unused, struct str_parms * parms)
|
||||
{
|
||||
if (!parms)
|
||||
return;
|
||||
|
||||
audio_extn_sound_trigger_set_parameters(NULL, parms);
|
||||
return;
|
||||
}
|
||||
|
||||
int audio_hw_call_back(sound_trigger_event_type_t event,
|
||||
sound_trigger_event_info_t* config)
|
||||
{
|
||||
int status = 0;
|
||||
struct sound_trigger_info *st_ses_info;
|
||||
|
||||
if (!st_dev)
|
||||
return -EINVAL;
|
||||
|
||||
pthread_mutex_lock(&st_dev->lock);
|
||||
switch (event) {
|
||||
case ST_EVENT_SESSION_REGISTER:
|
||||
if (!config) {
|
||||
ALOGE("%s: NULL config", __func__);
|
||||
status = -EINVAL;
|
||||
break;
|
||||
}
|
||||
st_ses_info= calloc(1, sizeof(struct sound_trigger_info ));
|
||||
if (!st_ses_info) {
|
||||
ALOGE("%s: st_ses_info alloc failed", __func__);
|
||||
status = -ENOMEM;
|
||||
break;
|
||||
}
|
||||
memcpy(&st_ses_info->st_ses, &config->st_ses, sizeof (config->st_ses));
|
||||
ALOGV("%s: add capture_handle %d pcm %p", __func__,
|
||||
st_ses_info->st_ses.capture_handle, st_ses_info->st_ses.pcm);
|
||||
list_add_tail(&st_dev->st_ses_list, &st_ses_info->list);
|
||||
break;
|
||||
|
||||
case ST_EVENT_SESSION_DEREGISTER:
|
||||
if (!config) {
|
||||
ALOGE("%s: NULL config", __func__);
|
||||
status = -EINVAL;
|
||||
break;
|
||||
}
|
||||
st_ses_info = get_sound_trigger_info(config->st_ses.capture_handle);
|
||||
if (!st_ses_info) {
|
||||
ALOGE("%s: pcm %p not in the list!", __func__, config->st_ses.pcm);
|
||||
status = -EINVAL;
|
||||
break;
|
||||
}
|
||||
ALOGV("%s: remove capture_handle %d pcm %p", __func__,
|
||||
st_ses_info->st_ses.capture_handle, st_ses_info->st_ses.pcm);
|
||||
list_remove(&st_ses_info->list);
|
||||
free(st_ses_info);
|
||||
break;
|
||||
default:
|
||||
ALOGW("%s: Unknown event %d", __func__, event);
|
||||
break;
|
||||
}
|
||||
pthread_mutex_unlock(&st_dev->lock);
|
||||
return status;
|
||||
}
|
||||
|
||||
int audio_extn_sound_trigger_read(struct stream_in *in, void *buffer,
|
||||
size_t bytes)
|
||||
{
|
||||
int ret = -1;
|
||||
struct sound_trigger_info *st_info = NULL;
|
||||
audio_event_info_t event;
|
||||
|
||||
if (!st_dev)
|
||||
return ret;
|
||||
|
||||
if (!in->is_st_session_active) {
|
||||
ALOGE(" %s: Sound trigger is not active", __func__);
|
||||
goto exit;
|
||||
}
|
||||
if (in->standby)
|
||||
in->standby = false;
|
||||
|
||||
pthread_mutex_lock(&st_dev->lock);
|
||||
st_info = get_sound_trigger_info(in->capture_handle);
|
||||
pthread_mutex_unlock(&st_dev->lock);
|
||||
if (st_info) {
|
||||
event.u.aud_info.ses_info = &st_info->st_ses;
|
||||
event.u.aud_info.buf = buffer;
|
||||
event.u.aud_info.num_bytes = bytes;
|
||||
ret = st_dev->st_callback(AUDIO_EVENT_READ_SAMPLES, &event);
|
||||
}
|
||||
|
||||
exit:
|
||||
if (ret) {
|
||||
if (-ENETRESET == ret)
|
||||
in->is_st_session_active = false;
|
||||
memset(buffer, 0, bytes);
|
||||
ALOGV("%s: read failed status %d - sleep", __func__, ret);
|
||||
usleep((bytes * 1000000) / (audio_stream_in_frame_size((struct audio_stream_in *)in) *
|
||||
in->config.rate));
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
void audio_extn_sound_trigger_stop_lab(struct stream_in *in)
|
||||
{
|
||||
int status = 0;
|
||||
struct sound_trigger_info *st_ses_info = NULL;
|
||||
audio_event_info_t event;
|
||||
|
||||
if (!st_dev || !in)
|
||||
return;
|
||||
|
||||
pthread_mutex_lock(&st_dev->lock);
|
||||
st_ses_info = get_sound_trigger_info(in->capture_handle);
|
||||
pthread_mutex_unlock(&st_dev->lock);
|
||||
if (st_ses_info) {
|
||||
event.u.ses_info = st_ses_info->st_ses;
|
||||
ALOGV("%s: AUDIO_EVENT_STOP_LAB pcm %p", __func__, st_ses_info->st_ses.pcm);
|
||||
st_dev->st_callback(AUDIO_EVENT_STOP_LAB, &event);
|
||||
}
|
||||
}
|
||||
void audio_extn_sound_trigger_check_and_get_session(struct stream_in *in)
|
||||
{
|
||||
struct sound_trigger_info *st_ses_info = NULL;
|
||||
struct listnode *node;
|
||||
|
||||
if (!st_dev || !in)
|
||||
return;
|
||||
|
||||
pthread_mutex_lock(&st_dev->lock);
|
||||
in->is_st_session = false;
|
||||
ALOGV("%s: list %d capture_handle %d", __func__,
|
||||
list_empty(&st_dev->st_ses_list), in->capture_handle);
|
||||
list_for_each(node, &st_dev->st_ses_list) {
|
||||
st_ses_info = node_to_item(node, struct sound_trigger_info , list);
|
||||
if (st_ses_info->st_ses.capture_handle == in->capture_handle) {
|
||||
in->pcm = st_ses_info->st_ses.pcm;
|
||||
in->config = st_ses_info->st_ses.config;
|
||||
in->channel_mask = audio_channel_in_mask_from_count(in->config.channels);
|
||||
in->is_st_session = true;
|
||||
in->is_st_session_active = true;
|
||||
ALOGV("%s: capture_handle %d is sound trigger", __func__, in->capture_handle);
|
||||
break;
|
||||
}
|
||||
}
|
||||
pthread_mutex_unlock(&st_dev->lock);
|
||||
}
|
||||
|
||||
void audio_extn_sound_trigger_update_device_status(snd_device_t snd_device,
|
||||
st_event_type_t event)
|
||||
{
|
||||
int device_type = -1;
|
||||
|
||||
if (!st_dev)
|
||||
return;
|
||||
|
||||
if (snd_device >= SND_DEVICE_OUT_BEGIN &&
|
||||
snd_device < SND_DEVICE_OUT_END) {
|
||||
device_type = PCM_PLAYBACK;
|
||||
} else if (snd_device >= SND_DEVICE_IN_BEGIN &&
|
||||
snd_device < SND_DEVICE_IN_END) {
|
||||
if (snd_device == SND_DEVICE_IN_CAPTURE_VI_FEEDBACK)
|
||||
return;
|
||||
device_type = PCM_CAPTURE;
|
||||
} else {
|
||||
ALOGE("%s: invalid device 0x%x, for event %d",
|
||||
__func__, snd_device, event);
|
||||
return;
|
||||
}
|
||||
|
||||
ALOGV("%s: device 0x%x of type %d for Event %d",
|
||||
__func__, snd_device, device_type, event);
|
||||
if (device_type == PCM_CAPTURE) {
|
||||
switch(event) {
|
||||
case ST_EVENT_SND_DEVICE_FREE:
|
||||
st_dev->st_callback(AUDIO_EVENT_CAPTURE_DEVICE_INACTIVE, NULL);
|
||||
break;
|
||||
case ST_EVENT_SND_DEVICE_BUSY:
|
||||
st_dev->st_callback(AUDIO_EVENT_CAPTURE_DEVICE_ACTIVE, NULL);
|
||||
break;
|
||||
default:
|
||||
ALOGW("%s:invalid event %d for device 0x%x",
|
||||
__func__, event, snd_device);
|
||||
}
|
||||
}/*Events for output device, if required can be placed here in else*/
|
||||
}
|
||||
|
||||
void audio_extn_sound_trigger_set_parameters(struct audio_device *adev __unused,
|
||||
struct str_parms *params)
|
||||
{
|
||||
audio_event_info_t event;
|
||||
char value[32];
|
||||
int ret, val;
|
||||
|
||||
if(!st_dev || !params) {
|
||||
ALOGE("%s: str_params NULL", __func__);
|
||||
return;
|
||||
}
|
||||
|
||||
ret = str_parms_get_str(params, "SND_CARD_STATUS", value,
|
||||
sizeof(value));
|
||||
if (ret > 0) {
|
||||
if (strstr(value, "OFFLINE")) {
|
||||
event.u.status = SND_CARD_STATUS_OFFLINE;
|
||||
st_dev->st_callback(AUDIO_EVENT_SSR, &event);
|
||||
}
|
||||
else if (strstr(value, "ONLINE")) {
|
||||
event.u.status = SND_CARD_STATUS_ONLINE;
|
||||
st_dev->st_callback(AUDIO_EVENT_SSR, &event);
|
||||
}
|
||||
else
|
||||
ALOGE("%s: unknown snd_card_status", __func__);
|
||||
}
|
||||
|
||||
ret = str_parms_get_str(params, "CPE_STATUS", value, sizeof(value));
|
||||
if (ret > 0) {
|
||||
if (strstr(value, "OFFLINE")) {
|
||||
event.u.status = CPE_STATUS_OFFLINE;
|
||||
st_dev->st_callback(AUDIO_EVENT_SSR, &event);
|
||||
}
|
||||
else if (strstr(value, "ONLINE")) {
|
||||
event.u.status = CPE_STATUS_ONLINE;
|
||||
st_dev->st_callback(AUDIO_EVENT_SSR, &event);
|
||||
}
|
||||
else
|
||||
ALOGE("%s: unknown CPE status", __func__);
|
||||
}
|
||||
|
||||
ret = str_parms_get_int(params, "SVA_NUM_SESSIONS", &val);
|
||||
if (ret >= 0) {
|
||||
event.u.value = val;
|
||||
st_dev->st_callback(AUDIO_EVENT_NUM_ST_SESSIONS, &event);
|
||||
}
|
||||
}
|
||||
|
||||
int audio_extn_sound_trigger_init(struct audio_device *adev)
|
||||
{
|
||||
int status = 0;
|
||||
char sound_trigger_lib[100];
|
||||
void *lib_handle;
|
||||
|
||||
ALOGV("%s: Enter", __func__);
|
||||
|
||||
st_dev = (struct sound_trigger_audio_device*)
|
||||
calloc(1, sizeof(struct sound_trigger_audio_device));
|
||||
if (!st_dev) {
|
||||
ALOGE("%s: ERROR. sound trigger alloc failed", __func__);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
snprintf(sound_trigger_lib, sizeof(sound_trigger_lib),
|
||||
SOUND_TRIGGER_LIBRARY_PATH,
|
||||
XSTR(SOUND_TRIGGER_PLATFORM_NAME));
|
||||
|
||||
st_dev->lib_handle = dlopen(sound_trigger_lib, RTLD_NOW);
|
||||
|
||||
if (st_dev->lib_handle == NULL) {
|
||||
ALOGE("%s: DLOPEN failed for %s. error = %s", __func__, sound_trigger_lib,
|
||||
dlerror());
|
||||
status = -EINVAL;
|
||||
goto cleanup;
|
||||
}
|
||||
ALOGV("%s: DLOPEN successful for %s", __func__, sound_trigger_lib);
|
||||
|
||||
st_dev->st_callback = (sound_trigger_hw_call_back_t)
|
||||
dlsym(st_dev->lib_handle, "sound_trigger_hw_call_back");
|
||||
|
||||
if (st_dev->st_callback == NULL) {
|
||||
ALOGE("%s: ERROR. dlsym Error:%s sound_trigger_hw_call_back", __func__,
|
||||
dlerror());
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
st_dev->adev = adev;
|
||||
list_init(&st_dev->st_ses_list);
|
||||
audio_extn_snd_mon_register_listener(st_dev, stdev_snd_mon_cb);
|
||||
|
||||
return 0;
|
||||
|
||||
cleanup:
|
||||
if (st_dev->lib_handle)
|
||||
dlclose(st_dev->lib_handle);
|
||||
free(st_dev);
|
||||
st_dev = NULL;
|
||||
return status;
|
||||
|
||||
}
|
||||
|
||||
void audio_extn_sound_trigger_deinit(struct audio_device *adev)
|
||||
{
|
||||
ALOGV("%s: Enter", __func__);
|
||||
if (st_dev && (st_dev->adev == adev) && st_dev->lib_handle) {
|
||||
audio_extn_snd_mon_unregister_listener(st_dev);
|
||||
dlclose(st_dev->lib_handle);
|
||||
free(st_dev);
|
||||
st_dev = NULL;
|
||||
}
|
||||
}
|
907
android/hardware/qcom/audio/hal/audio_extn/spkr_protection.c
Normal file
907
android/hardware/qcom/audio/hal/audio_extn/spkr_protection.c
Normal file
|
@ -0,0 +1,907 @@
|
|||
/*
|
||||
* Copyright (C) 2015 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 "audio_hw_spkr_prot"
|
||||
/*#define LOG_NDEBUG 0*/
|
||||
//#define LOG_NDDEBUG 0
|
||||
|
||||
#include <errno.h>
|
||||
#include <math.h>
|
||||
#include <cutils/log.h>
|
||||
#include <fcntl.h>
|
||||
#include "audio_hw.h"
|
||||
#include "platform.h"
|
||||
#include "platform_api.h"
|
||||
#include <sys/stat.h>
|
||||
#include <stdlib.h>
|
||||
#include <dlfcn.h>
|
||||
#include <math.h>
|
||||
#include <cutils/properties.h>
|
||||
#include "audio_extn.h"
|
||||
#include <linux/msm_audio_calibration.h>
|
||||
|
||||
#define THERMAL_CLIENT_LIBRARY_PATH "libthermalclient.so"
|
||||
|
||||
#ifdef SPKR_PROT_ENABLED
|
||||
|
||||
/*Range of spkr temparatures -30C to 80C*/
|
||||
#define MIN_SPKR_TEMP_Q6 (-30 * (1 << 6))
|
||||
#define MAX_SPKR_TEMP_Q6 (80 * (1 << 6))
|
||||
#define VI_FEED_CHANNEL "VI_FEED_TX Channels"
|
||||
|
||||
/*Set safe temp value to 40C*/
|
||||
#define SAFE_SPKR_TEMP 40
|
||||
#define SAFE_SPKR_TEMP_Q6 (SAFE_SPKR_TEMP * (1 << 6))
|
||||
|
||||
/*Range of resistance values 2ohms to 40 ohms*/
|
||||
#define MIN_RESISTANCE_SPKR_Q24 (2 * (1 << 24))
|
||||
#define MAX_RESISTANCE_SPKR_Q24 (40 * (1 << 24))
|
||||
|
||||
/*Path where the calibration file will be stored*/
|
||||
#define CALIB_FILE "/data/misc/audio/audio.cal"
|
||||
|
||||
/*Time between retries for calibartion or intial wait time
|
||||
after boot up*/
|
||||
#define WAIT_TIME_SPKR_CALIB (60 * 1000 * 1000)
|
||||
|
||||
#define MIN_SPKR_IDLE_SEC (60 * 30)
|
||||
|
||||
/*Once calibration is started sleep for 1 sec to allow
|
||||
the calibration to kick off*/
|
||||
#define SLEEP_AFTER_CALIB_START (3000)
|
||||
|
||||
/*If calibration is in progress wait for 200 msec before querying
|
||||
for status again*/
|
||||
#define WAIT_FOR_GET_CALIB_STATUS (200)
|
||||
#define GET_SPKR_PROT_CAL_TIMEOUT_MSEC (5000)
|
||||
|
||||
/*Speaker states*/
|
||||
#define SPKR_NOT_CALIBRATED -1
|
||||
#define SPKR_CALIBRATED 1
|
||||
|
||||
/*Speaker processing state*/
|
||||
#define SPKR_PROCESSING_IN_PROGRESS 1
|
||||
#define SPKR_PROCESSING_IN_IDLE 0
|
||||
|
||||
/*Modes of Speaker Protection*/
|
||||
enum speaker_protection_mode {
|
||||
SPKR_PROTECTION_DISABLED = -1,
|
||||
SPKR_PROTECTION_MODE_PROCESSING = 0,
|
||||
SPKR_PROTECTION_MODE_CALIBRATE = 1,
|
||||
};
|
||||
|
||||
struct speaker_prot_session {
|
||||
int spkr_prot_mode;
|
||||
int spkr_processing_state;
|
||||
int thermal_client_handle;
|
||||
pthread_mutex_t mutex_spkr_prot;
|
||||
pthread_t spkr_calibration_thread;
|
||||
pthread_mutex_t spkr_prot_thermalsync_mutex;
|
||||
pthread_cond_t spkr_prot_thermalsync;
|
||||
int cancel_spkr_calib;
|
||||
pthread_cond_t spkr_calib_cancel;
|
||||
pthread_mutex_t spkr_calib_cancelack_mutex;
|
||||
pthread_cond_t spkr_calibcancel_ack;
|
||||
pthread_t speaker_prot_threadid;
|
||||
void *thermal_handle;
|
||||
void *adev_handle;
|
||||
int spkr_prot_t0;
|
||||
struct pcm *pcm_rx;
|
||||
struct pcm *pcm_tx;
|
||||
int (*thermal_client_register_callback)
|
||||
(char *client_name, int (*callback)(int), void *data);
|
||||
void (*thermal_client_unregister_callback)(int handle);
|
||||
int (*thermal_client_request)(char *client_name, int req_data);
|
||||
bool spkr_prot_enable;
|
||||
bool spkr_in_use;
|
||||
struct timespec spkr_last_time_used;
|
||||
};
|
||||
|
||||
static struct pcm_config pcm_config_skr_prot = {
|
||||
.channels = 4,
|
||||
.rate = 48000,
|
||||
.period_size = 256,
|
||||
.period_count = 4,
|
||||
.format = PCM_FORMAT_S16_LE,
|
||||
.start_threshold = 0,
|
||||
.stop_threshold = INT_MAX,
|
||||
.avail_min = 0,
|
||||
};
|
||||
|
||||
static struct speaker_prot_session handle;
|
||||
static int vi_feed_no_channels;
|
||||
|
||||
static void spkr_prot_set_spkrstatus(bool enable)
|
||||
{
|
||||
struct timespec ts;
|
||||
if (enable)
|
||||
handle.spkr_in_use = true;
|
||||
else {
|
||||
handle.spkr_in_use = false;
|
||||
clock_gettime(CLOCK_BOOTTIME, &handle.spkr_last_time_used);
|
||||
}
|
||||
}
|
||||
|
||||
void audio_extn_spkr_prot_calib_cancel(void *adev)
|
||||
{
|
||||
pthread_t threadid;
|
||||
struct audio_usecase *uc_info;
|
||||
int count = 0;
|
||||
threadid = pthread_self();
|
||||
ALOGV("%s: Entry", __func__);
|
||||
if (pthread_equal(handle.speaker_prot_threadid, threadid) || !adev) {
|
||||
ALOGV("%s: Calibration not in progress.. nothihg to cancel", __func__);
|
||||
return;
|
||||
}
|
||||
uc_info = get_usecase_from_list(adev, USECASE_AUDIO_SPKR_CALIB_RX);
|
||||
if (uc_info) {
|
||||
pthread_mutex_lock(&handle.mutex_spkr_prot);
|
||||
pthread_mutex_lock(&handle.spkr_calib_cancelack_mutex);
|
||||
handle.cancel_spkr_calib = 1;
|
||||
pthread_cond_signal(&handle.spkr_calib_cancel);
|
||||
pthread_mutex_unlock(&handle.mutex_spkr_prot);
|
||||
pthread_cond_wait(&handle.spkr_calibcancel_ack,
|
||||
&handle.spkr_calib_cancelack_mutex);
|
||||
pthread_mutex_unlock(&handle.spkr_calib_cancelack_mutex);
|
||||
}
|
||||
ALOGV("%s: Exit", __func__);
|
||||
}
|
||||
|
||||
static bool is_speaker_in_use(unsigned long *sec)
|
||||
{
|
||||
struct timespec temp;
|
||||
if (!sec) {
|
||||
ALOGE("%s: Invalid params", __func__);
|
||||
return true;
|
||||
}
|
||||
if (handle.spkr_in_use) {
|
||||
*sec = 0;
|
||||
return true;
|
||||
} else {
|
||||
clock_gettime(CLOCK_BOOTTIME, &temp);
|
||||
*sec = temp.tv_sec - handle.spkr_last_time_used.tv_sec;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static int get_spkr_prot_cal(int cal_fd,
|
||||
struct audio_cal_info_msm_spk_prot_status *status)
|
||||
{
|
||||
int ret = 0;
|
||||
struct audio_cal_fb_spk_prot_status cal_data;
|
||||
|
||||
if (cal_fd < 0) {
|
||||
ALOGE("%s: Error: cal_fd = %d", __func__, cal_fd);
|
||||
ret = -EINVAL;
|
||||
goto done;
|
||||
}
|
||||
|
||||
if (status == NULL) {
|
||||
ALOGE("%s: Error: status NULL", __func__);
|
||||
ret = -EINVAL;
|
||||
goto done;
|
||||
}
|
||||
|
||||
cal_data.hdr.data_size = sizeof(cal_data);
|
||||
cal_data.hdr.version = VERSION_0_0;
|
||||
cal_data.hdr.cal_type = AFE_FB_SPKR_PROT_CAL_TYPE;
|
||||
cal_data.hdr.cal_type_size = sizeof(cal_data.cal_type);
|
||||
cal_data.cal_type.cal_hdr.version = VERSION_0_0;
|
||||
cal_data.cal_type.cal_hdr.buffer_number = 0;
|
||||
cal_data.cal_type.cal_data.mem_handle = -1;
|
||||
|
||||
if (ioctl(cal_fd, AUDIO_GET_CALIBRATION, &cal_data)) {
|
||||
ALOGE("%s: Error: AUDIO_GET_CALIBRATION failed!",
|
||||
__func__);
|
||||
ret = -ENODEV;
|
||||
goto done;
|
||||
}
|
||||
|
||||
status->r0[SP_V2_SPKR_1] = cal_data.cal_type.cal_info.r0[SP_V2_SPKR_1];
|
||||
status->r0[SP_V2_SPKR_2] = cal_data.cal_type.cal_info.r0[SP_V2_SPKR_2];
|
||||
status->status = cal_data.cal_type.cal_info.status;
|
||||
done:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int set_spkr_prot_cal(int cal_fd,
|
||||
struct audio_cal_info_spk_prot_cfg *protCfg)
|
||||
{
|
||||
int ret = 0;
|
||||
struct audio_cal_fb_spk_prot_cfg cal_data;
|
||||
char value[PROPERTY_VALUE_MAX];
|
||||
|
||||
if (cal_fd < 0) {
|
||||
ALOGE("%s: Error: cal_fd = %d", __func__, cal_fd);
|
||||
ret = -EINVAL;
|
||||
goto done;
|
||||
}
|
||||
|
||||
if (protCfg == NULL) {
|
||||
ALOGE("%s: Error: status NULL", __func__);
|
||||
ret = -EINVAL;
|
||||
goto done;
|
||||
}
|
||||
|
||||
memset(&cal_data, 0, sizeof(cal_data));
|
||||
cal_data.hdr.data_size = sizeof(cal_data);
|
||||
cal_data.hdr.version = VERSION_0_0;
|
||||
cal_data.hdr.cal_type = AFE_FB_SPKR_PROT_CAL_TYPE;
|
||||
cal_data.hdr.cal_type_size = sizeof(cal_data.cal_type);
|
||||
cal_data.cal_type.cal_hdr.version = VERSION_0_0;
|
||||
cal_data.cal_type.cal_hdr.buffer_number = 0;
|
||||
cal_data.cal_type.cal_info.r0[SP_V2_SPKR_1] = protCfg->r0[SP_V2_SPKR_1];
|
||||
cal_data.cal_type.cal_info.r0[SP_V2_SPKR_2] = protCfg->r0[SP_V2_SPKR_2];
|
||||
cal_data.cal_type.cal_info.t0[SP_V2_SPKR_1] = protCfg->t0[SP_V2_SPKR_1];
|
||||
cal_data.cal_type.cal_info.t0[SP_V2_SPKR_2] = protCfg->t0[SP_V2_SPKR_2];
|
||||
cal_data.cal_type.cal_info.mode = protCfg->mode;
|
||||
property_get("persist.spkr.cal.duration", value, "0");
|
||||
if (atoi(value) > 0) {
|
||||
ALOGD("%s: quick calibration enabled", __func__);
|
||||
cal_data.cal_type.cal_info.quick_calib_flag = 1;
|
||||
} else {
|
||||
ALOGD("%s: quick calibration disabled", __func__);
|
||||
cal_data.cal_type.cal_info.quick_calib_flag = 0;
|
||||
}
|
||||
|
||||
cal_data.cal_type.cal_data.mem_handle = -1;
|
||||
|
||||
if (ioctl(cal_fd, AUDIO_SET_CALIBRATION, &cal_data)) {
|
||||
ALOGE("%s: Error: AUDIO_SET_CALIBRATION failed!",
|
||||
__func__);
|
||||
ret = -ENODEV;
|
||||
goto done;
|
||||
}
|
||||
done:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int vi_feed_get_channels(struct audio_device *adev)
|
||||
{
|
||||
struct mixer_ctl *ctl;
|
||||
const char *mixer_ctl_name = VI_FEED_CHANNEL;
|
||||
int value;
|
||||
|
||||
ALOGV("%s: entry", __func__);
|
||||
ctl = mixer_get_ctl_by_name(adev->mixer, mixer_ctl_name);
|
||||
if (!ctl) {
|
||||
ALOGE("%s: Could not get ctl for mixer cmd - %s",
|
||||
__func__, mixer_ctl_name);
|
||||
goto error;
|
||||
}
|
||||
value = mixer_ctl_get_value(ctl, 0);
|
||||
if (value < 0)
|
||||
goto error;
|
||||
else
|
||||
return value+1;
|
||||
error:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
// must be called with adev->lock acquired
|
||||
static int spkr_calibrate(int t0)
|
||||
{
|
||||
struct audio_device *adev = handle.adev_handle;
|
||||
struct audio_cal_info_spk_prot_cfg protCfg;
|
||||
struct audio_cal_info_msm_spk_prot_status status;
|
||||
bool cleanup = false, disable_rx = false, disable_tx = false;
|
||||
int acdb_fd = -1;
|
||||
struct audio_usecase *uc_info_rx = NULL, *uc_info_tx = NULL;
|
||||
int32_t pcm_dev_rx_id = -1, pcm_dev_tx_id = -1;
|
||||
struct timespec ts;
|
||||
int retry_duration;
|
||||
|
||||
if (!adev) {
|
||||
ALOGE("%s: Invalid params", __func__);
|
||||
return -EINVAL;
|
||||
}
|
||||
if (!list_empty(&adev->usecase_list)) {
|
||||
ALOGD("%s: Usecase present retry speaker protection", __func__);
|
||||
return -EAGAIN;
|
||||
}
|
||||
acdb_fd = open("/dev/msm_audio_cal",O_RDWR | O_NONBLOCK);
|
||||
if (acdb_fd < 0) {
|
||||
ALOGE("%s: spkr_prot_thread open msm_acdb failed", __func__);
|
||||
return -ENODEV;
|
||||
} else {
|
||||
protCfg.mode = MSM_SPKR_PROT_CALIBRATION_IN_PROGRESS;
|
||||
/* HAL for speaker protection gets only one Temperature */
|
||||
protCfg.t0[SP_V2_SPKR_1] = t0;
|
||||
protCfg.t0[SP_V2_SPKR_2] = t0;
|
||||
if (set_spkr_prot_cal(acdb_fd, &protCfg)) {
|
||||
ALOGE("%s: spkr_prot_thread set failed AUDIO_SET_SPEAKER_PROT",
|
||||
__func__);
|
||||
status.status = -ENODEV;
|
||||
goto exit;
|
||||
}
|
||||
}
|
||||
uc_info_rx = (struct audio_usecase *)calloc(1, sizeof(struct audio_usecase));
|
||||
if (!uc_info_rx) {
|
||||
return -ENOMEM;
|
||||
}
|
||||
uc_info_rx->id = USECASE_AUDIO_SPKR_CALIB_RX;
|
||||
uc_info_rx->type = PCM_PLAYBACK;
|
||||
uc_info_rx->in_snd_device = SND_DEVICE_NONE;
|
||||
uc_info_rx->stream.out = adev->primary_output;
|
||||
uc_info_rx->out_snd_device = SND_DEVICE_OUT_SPEAKER_PROTECTED;
|
||||
disable_rx = true;
|
||||
list_add_tail(&adev->usecase_list, &uc_info_rx->list);
|
||||
enable_snd_device(adev, SND_DEVICE_OUT_SPEAKER_PROTECTED);
|
||||
enable_audio_route(adev, uc_info_rx);
|
||||
|
||||
pcm_dev_rx_id = platform_get_pcm_device_id(uc_info_rx->id, PCM_PLAYBACK);
|
||||
ALOGV("%s: pcm device id %d", __func__, pcm_dev_rx_id);
|
||||
if (pcm_dev_rx_id < 0) {
|
||||
ALOGE("%s: Invalid pcm device for usecase (%d)",
|
||||
__func__, uc_info_rx->id);
|
||||
status.status = -ENODEV;
|
||||
goto exit;
|
||||
}
|
||||
handle.pcm_rx = handle.pcm_tx = NULL;
|
||||
handle.pcm_rx = pcm_open(adev->snd_card,
|
||||
pcm_dev_rx_id,
|
||||
PCM_OUT, &pcm_config_skr_prot);
|
||||
if (handle.pcm_rx && !pcm_is_ready(handle.pcm_rx)) {
|
||||
ALOGE("%s: %s", __func__, pcm_get_error(handle.pcm_rx));
|
||||
status.status = -EIO;
|
||||
goto exit;
|
||||
}
|
||||
uc_info_tx = (struct audio_usecase *)
|
||||
calloc(1, sizeof(struct audio_usecase));
|
||||
if (!uc_info_tx) {
|
||||
status.status = -ENOMEM;
|
||||
goto exit;
|
||||
}
|
||||
uc_info_tx->id = USECASE_AUDIO_SPKR_CALIB_TX;
|
||||
uc_info_tx->type = PCM_CAPTURE;
|
||||
uc_info_tx->in_snd_device = SND_DEVICE_IN_CAPTURE_VI_FEEDBACK;
|
||||
uc_info_tx->out_snd_device = SND_DEVICE_NONE;
|
||||
|
||||
disable_tx = true;
|
||||
list_add_tail(&adev->usecase_list, &uc_info_tx->list);
|
||||
enable_snd_device(adev, SND_DEVICE_IN_CAPTURE_VI_FEEDBACK);
|
||||
enable_audio_route(adev, uc_info_tx);
|
||||
|
||||
pcm_dev_tx_id = platform_get_pcm_device_id(uc_info_tx->id, PCM_CAPTURE);
|
||||
if (pcm_dev_tx_id < 0) {
|
||||
ALOGE("%s: Invalid pcm device for usecase (%d)",
|
||||
__func__, uc_info_tx->id);
|
||||
status.status = -ENODEV;
|
||||
goto exit;
|
||||
}
|
||||
handle.pcm_tx = pcm_open(adev->snd_card,
|
||||
pcm_dev_tx_id,
|
||||
PCM_IN, &pcm_config_skr_prot);
|
||||
if (handle.pcm_tx && !pcm_is_ready(handle.pcm_tx)) {
|
||||
ALOGE("%s: %s", __func__, pcm_get_error(handle.pcm_tx));
|
||||
status.status = -EIO;
|
||||
goto exit;
|
||||
}
|
||||
if (pcm_start(handle.pcm_rx) < 0) {
|
||||
ALOGE("%s: pcm start for RX failed", __func__);
|
||||
status.status = -EINVAL;
|
||||
goto exit;
|
||||
}
|
||||
if (pcm_start(handle.pcm_tx) < 0) {
|
||||
ALOGE("%s: pcm start for TX failed", __func__);
|
||||
status.status = -EINVAL;
|
||||
goto exit;
|
||||
}
|
||||
cleanup = true;
|
||||
clock_gettime(CLOCK_REALTIME, &ts);
|
||||
ts.tv_sec += (SLEEP_AFTER_CALIB_START/1000);
|
||||
ts.tv_nsec = 0;
|
||||
pthread_mutex_lock(&handle.mutex_spkr_prot);
|
||||
pthread_mutex_unlock(&adev->lock);
|
||||
|
||||
(void)pthread_cond_timedwait(&handle.spkr_calib_cancel,
|
||||
&handle.mutex_spkr_prot, &ts);
|
||||
ALOGD("%s: Speaker calibration done", __func__);
|
||||
pthread_mutex_lock(&handle.spkr_calib_cancelack_mutex);
|
||||
if (handle.cancel_spkr_calib) {
|
||||
status.status = -EAGAIN;
|
||||
goto exit;
|
||||
}
|
||||
|
||||
if (acdb_fd >= 0) {
|
||||
status.status = -EINVAL;
|
||||
retry_duration = 0;
|
||||
while (!get_spkr_prot_cal(acdb_fd, &status) &&
|
||||
retry_duration < GET_SPKR_PROT_CAL_TIMEOUT_MSEC) {
|
||||
if (!status.status) {
|
||||
ALOGD("%s: spkr_prot_thread calib Success R0 %d %d",
|
||||
__func__, status.r0[SP_V2_SPKR_1], status.r0[SP_V2_SPKR_2]);
|
||||
FILE *fp;
|
||||
|
||||
vi_feed_no_channels = vi_feed_get_channels(adev);
|
||||
ALOGD("%s: vi_feed_no_channels %d", __func__, vi_feed_no_channels);
|
||||
if (vi_feed_no_channels < 0) {
|
||||
ALOGE("%s: no of channels negative !!", __func__);
|
||||
/* limit the number of channels to 2*/
|
||||
vi_feed_no_channels = 2;
|
||||
}
|
||||
|
||||
fp = fopen(CALIB_FILE,"wb");
|
||||
if (!fp) {
|
||||
ALOGE("%s: spkr_prot_thread File open failed %s",
|
||||
__func__, strerror(errno));
|
||||
status.status = -ENODEV;
|
||||
} else {
|
||||
int i;
|
||||
/* HAL for speaker protection is always calibrating for stereo usecase*/
|
||||
for (i = 0; i < vi_feed_no_channels; i++) {
|
||||
fwrite(&status.r0[i], sizeof(status.r0[i]), 1, fp);
|
||||
fwrite(&protCfg.t0[i], sizeof(protCfg.t0[i]), 1, fp);
|
||||
}
|
||||
fclose(fp);
|
||||
}
|
||||
break;
|
||||
} else if (status.status == -EAGAIN) {
|
||||
ALOGD("%s: spkr_prot_thread try again", __func__);
|
||||
usleep(WAIT_FOR_GET_CALIB_STATUS * 1000);
|
||||
retry_duration += WAIT_FOR_GET_CALIB_STATUS;
|
||||
} else {
|
||||
ALOGE("%s: spkr_prot_thread get failed status %d",
|
||||
__func__, status.status);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
exit:
|
||||
if (handle.pcm_rx)
|
||||
pcm_close(handle.pcm_rx);
|
||||
handle.pcm_rx = NULL;
|
||||
|
||||
if (handle.pcm_tx)
|
||||
pcm_close(handle.pcm_tx);
|
||||
handle.pcm_tx = NULL;
|
||||
|
||||
/* Clear TX calibration to handset mic */
|
||||
platform_send_audio_calibration(adev->platform, SND_DEVICE_IN_HANDSET_MIC);
|
||||
if (!status.status) {
|
||||
protCfg.mode = MSM_SPKR_PROT_CALIBRATED;
|
||||
protCfg.r0[SP_V2_SPKR_1] = status.r0[SP_V2_SPKR_1];
|
||||
protCfg.r0[SP_V2_SPKR_2] = status.r0[SP_V2_SPKR_2];
|
||||
if (set_spkr_prot_cal(acdb_fd, &protCfg))
|
||||
ALOGE("%s: spkr_prot_thread disable calib mode", __func__);
|
||||
else
|
||||
handle.spkr_prot_mode = MSM_SPKR_PROT_CALIBRATED;
|
||||
} else {
|
||||
protCfg.mode = MSM_SPKR_PROT_NOT_CALIBRATED;
|
||||
handle.spkr_prot_mode = MSM_SPKR_PROT_NOT_CALIBRATED;
|
||||
if (set_spkr_prot_cal(acdb_fd, &protCfg))
|
||||
ALOGE("%s: spkr_prot_thread disable calib mode failed", __func__);
|
||||
}
|
||||
if (acdb_fd >= 0)
|
||||
close(acdb_fd);
|
||||
|
||||
if (!handle.cancel_spkr_calib && cleanup) {
|
||||
pthread_mutex_unlock(&handle.spkr_calib_cancelack_mutex);
|
||||
pthread_cond_wait(&handle.spkr_calib_cancel, &handle.mutex_spkr_prot);
|
||||
pthread_mutex_lock(&handle.spkr_calib_cancelack_mutex);
|
||||
}
|
||||
if (disable_rx) {
|
||||
list_remove(&uc_info_rx->list);
|
||||
disable_snd_device(adev, SND_DEVICE_OUT_SPEAKER_PROTECTED);
|
||||
disable_audio_route(adev, uc_info_rx);
|
||||
}
|
||||
if (disable_tx) {
|
||||
list_remove(&uc_info_tx->list);
|
||||
disable_snd_device(adev, SND_DEVICE_IN_CAPTURE_VI_FEEDBACK);
|
||||
disable_audio_route(adev, uc_info_tx);
|
||||
}
|
||||
if (uc_info_rx) free(uc_info_rx);
|
||||
if (uc_info_tx) free(uc_info_tx);
|
||||
if (cleanup) {
|
||||
if (handle.cancel_spkr_calib)
|
||||
pthread_cond_signal(&handle.spkr_calibcancel_ack);
|
||||
handle.cancel_spkr_calib = 0;
|
||||
pthread_mutex_unlock(&handle.spkr_calib_cancelack_mutex);
|
||||
pthread_mutex_unlock(&handle.mutex_spkr_prot);
|
||||
pthread_mutex_lock(&adev->lock);
|
||||
}
|
||||
|
||||
return status.status;
|
||||
}
|
||||
|
||||
static void* spkr_calibration_thread()
|
||||
{
|
||||
unsigned long sec = 0;
|
||||
int t0;
|
||||
bool goahead = false;
|
||||
struct audio_cal_info_spk_prot_cfg protCfg;
|
||||
FILE *fp;
|
||||
int acdb_fd;
|
||||
struct audio_device *adev = handle.adev_handle;
|
||||
unsigned long min_idle_time = MIN_SPKR_IDLE_SEC;
|
||||
char value[PROPERTY_VALUE_MAX];
|
||||
|
||||
/* If the value of this persist.spkr.cal.duration is 0
|
||||
* then it means it will take 30min to calibrate
|
||||
* and if the value is greater than zero then it would take
|
||||
* that much amount of time to calibrate.
|
||||
*/
|
||||
property_get("persist.spkr.cal.duration", value, "0");
|
||||
if (atoi(value) > 0)
|
||||
min_idle_time = atoi(value);
|
||||
handle.speaker_prot_threadid = pthread_self();
|
||||
ALOGD("spkr_prot_thread enable prot Entry");
|
||||
acdb_fd = open("/dev/msm_audio_cal",O_RDWR | O_NONBLOCK);
|
||||
if (acdb_fd >= 0) {
|
||||
/*Set processing mode with t0/r0*/
|
||||
protCfg.mode = MSM_SPKR_PROT_NOT_CALIBRATED;
|
||||
if (set_spkr_prot_cal(acdb_fd, &protCfg)) {
|
||||
ALOGE("%s: spkr_prot_thread enable prot failed", __func__);
|
||||
handle.spkr_prot_mode = MSM_SPKR_PROT_DISABLED;
|
||||
close(acdb_fd);
|
||||
} else
|
||||
handle.spkr_prot_mode = MSM_SPKR_PROT_NOT_CALIBRATED;
|
||||
} else {
|
||||
handle.spkr_prot_mode = MSM_SPKR_PROT_DISABLED;
|
||||
ALOGE("%s: Failed to open acdb node", __func__);
|
||||
}
|
||||
if (handle.spkr_prot_mode == MSM_SPKR_PROT_DISABLED) {
|
||||
ALOGD("%s: Speaker protection disabled", __func__);
|
||||
pthread_exit(0);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
fp = fopen(CALIB_FILE,"rb");
|
||||
if (fp) {
|
||||
int i;
|
||||
bool spkr_calibrated = true;
|
||||
/* HAL for speaker protection is always calibrating for stereo usecase*/
|
||||
vi_feed_no_channels = vi_feed_get_channels(adev);
|
||||
ALOGD("%s: vi_feed_no_channels %d", __func__, vi_feed_no_channels);
|
||||
if (vi_feed_no_channels < 0) {
|
||||
ALOGE("%s: no of channels negative !!", __func__);
|
||||
/* limit the number of channels to 2*/
|
||||
vi_feed_no_channels = 2;
|
||||
}
|
||||
for (i = 0; i < vi_feed_no_channels; i++) {
|
||||
fread(&protCfg.r0[i], sizeof(protCfg.r0[i]), 1, fp);
|
||||
fread(&protCfg.t0[i], sizeof(protCfg.t0[i]), 1, fp);
|
||||
}
|
||||
ALOGD("%s: spkr_prot_thread r0 value %d %d",
|
||||
__func__, protCfg.r0[SP_V2_SPKR_1], protCfg.r0[SP_V2_SPKR_2]);
|
||||
ALOGD("%s: spkr_prot_thread t0 value %d %d",
|
||||
__func__, protCfg.t0[SP_V2_SPKR_1], protCfg.t0[SP_V2_SPKR_2]);
|
||||
fclose(fp);
|
||||
/*Valid tempature range: -30C to 80C(in q6 format)
|
||||
Valid Resistance range: 2 ohms to 40 ohms(in q24 format)*/
|
||||
for (i = 0; i < vi_feed_no_channels; i++) {
|
||||
if (!((protCfg.t0[i] > MIN_SPKR_TEMP_Q6) && (protCfg.t0[i] < MAX_SPKR_TEMP_Q6)
|
||||
&& (protCfg.r0[i] >= MIN_RESISTANCE_SPKR_Q24)
|
||||
&& (protCfg.r0[i] < MAX_RESISTANCE_SPKR_Q24))) {
|
||||
spkr_calibrated = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (spkr_calibrated) {
|
||||
ALOGD("%s: Spkr calibrated", __func__);
|
||||
protCfg.mode = MSM_SPKR_PROT_CALIBRATED;
|
||||
if (set_spkr_prot_cal(acdb_fd, &protCfg)) {
|
||||
ALOGE("%s: enable prot failed", __func__);
|
||||
handle.spkr_prot_mode = MSM_SPKR_PROT_DISABLED;
|
||||
} else
|
||||
handle.spkr_prot_mode = MSM_SPKR_PROT_CALIBRATED;
|
||||
close(acdb_fd);
|
||||
pthread_exit(0);
|
||||
return NULL;
|
||||
}
|
||||
close(acdb_fd);
|
||||
}
|
||||
|
||||
while (1) {
|
||||
ALOGV("%s: start calibration", __func__);
|
||||
if (!handle.thermal_client_request("spkr",1)) {
|
||||
ALOGD("%s: wait for callback from thermal daemon", __func__);
|
||||
pthread_mutex_lock(&handle.spkr_prot_thermalsync_mutex);
|
||||
pthread_cond_wait(&handle.spkr_prot_thermalsync,
|
||||
&handle.spkr_prot_thermalsync_mutex);
|
||||
/*Convert temp into q6 format*/
|
||||
t0 = (handle.spkr_prot_t0 * (1 << 6));
|
||||
pthread_mutex_unlock(&handle.spkr_prot_thermalsync_mutex);
|
||||
if (t0 < MIN_SPKR_TEMP_Q6 || t0 > MAX_SPKR_TEMP_Q6) {
|
||||
ALOGE("%s: Calibration temparature error %d", __func__,
|
||||
handle.spkr_prot_t0);
|
||||
continue;
|
||||
}
|
||||
ALOGD("%s: Request t0 success value %d", __func__,
|
||||
handle.spkr_prot_t0);
|
||||
} else {
|
||||
ALOGE("%s: Request t0 failed", __func__);
|
||||
/*Assume safe value for temparature*/
|
||||
t0 = SAFE_SPKR_TEMP_Q6;
|
||||
}
|
||||
goahead = false;
|
||||
pthread_mutex_lock(&adev->lock);
|
||||
if (is_speaker_in_use(&sec)) {
|
||||
ALOGD("%s: Speaker in use retry calibration", __func__);
|
||||
pthread_mutex_unlock(&adev->lock);
|
||||
continue;
|
||||
} else {
|
||||
ALOGD("%s: speaker idle %ld min time %ld", __func__, sec, min_idle_time);
|
||||
if (sec < min_idle_time) {
|
||||
ALOGD("%s: speaker idle is less retry", __func__);
|
||||
pthread_mutex_unlock(&adev->lock);
|
||||
continue;
|
||||
}
|
||||
goahead = true;
|
||||
}
|
||||
if (!list_empty(&adev->usecase_list)) {
|
||||
ALOGD("%s: Usecase active re-try calibration", __func__);
|
||||
goahead = false;
|
||||
pthread_mutex_unlock(&adev->lock);
|
||||
}
|
||||
if (goahead) {
|
||||
int status;
|
||||
status = spkr_calibrate(t0);
|
||||
pthread_mutex_unlock(&adev->lock);
|
||||
if (status == -EAGAIN) {
|
||||
ALOGE("%s: failed to calibrate try again %s",
|
||||
__func__, strerror(status));
|
||||
continue;
|
||||
} else {
|
||||
ALOGE("%s: calibrate status %s", __func__, strerror(status));
|
||||
}
|
||||
ALOGD("%s: spkr_prot_thread end calibration", __func__);
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (handle.thermal_client_handle)
|
||||
handle.thermal_client_unregister_callback(handle.thermal_client_handle);
|
||||
handle.thermal_client_handle = 0;
|
||||
if (handle.thermal_handle)
|
||||
dlclose(handle.thermal_handle);
|
||||
handle.thermal_handle = NULL;
|
||||
pthread_exit(0);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static int thermal_client_callback(int temp)
|
||||
{
|
||||
pthread_mutex_lock(&handle.spkr_prot_thermalsync_mutex);
|
||||
ALOGD("%s: spkr_prot set t0 %d and signal", __func__, temp);
|
||||
if (handle.spkr_prot_mode == MSM_SPKR_PROT_NOT_CALIBRATED)
|
||||
handle.spkr_prot_t0 = temp;
|
||||
pthread_cond_signal(&handle.spkr_prot_thermalsync);
|
||||
pthread_mutex_unlock(&handle.spkr_prot_thermalsync_mutex);
|
||||
return 0;
|
||||
}
|
||||
|
||||
void audio_extn_spkr_prot_init(void *adev)
|
||||
{
|
||||
char value[PROPERTY_VALUE_MAX];
|
||||
ALOGD("%s: Initialize speaker protection module", __func__);
|
||||
memset(&handle, 0, sizeof(handle));
|
||||
if (!adev) {
|
||||
ALOGE("%s: Invalid params", __func__);
|
||||
return;
|
||||
}
|
||||
property_get("persist.speaker.prot.enable", value, "");
|
||||
handle.spkr_prot_enable = false;
|
||||
if (!strncmp("true", value, 4))
|
||||
handle.spkr_prot_enable = true;
|
||||
if (!handle.spkr_prot_enable) {
|
||||
ALOGD("%s: Speaker protection disabled", __func__);
|
||||
return;
|
||||
}
|
||||
handle.adev_handle = adev;
|
||||
handle.spkr_prot_mode = MSM_SPKR_PROT_DISABLED;
|
||||
handle.spkr_processing_state = SPKR_PROCESSING_IN_IDLE;
|
||||
handle.spkr_prot_t0 = -1;
|
||||
pthread_cond_init(&handle.spkr_prot_thermalsync, NULL);
|
||||
pthread_cond_init(&handle.spkr_calib_cancel, NULL);
|
||||
pthread_cond_init(&handle.spkr_calibcancel_ack, NULL);
|
||||
pthread_mutex_init(&handle.mutex_spkr_prot, NULL);
|
||||
pthread_mutex_init(&handle.spkr_calib_cancelack_mutex, NULL);
|
||||
pthread_mutex_init(&handle.spkr_prot_thermalsync_mutex, NULL);
|
||||
handle.thermal_handle = dlopen(THERMAL_CLIENT_LIBRARY_PATH,
|
||||
RTLD_NOW);
|
||||
if (!handle.thermal_handle) {
|
||||
ALOGE("%s: DLOPEN for thermal client failed", __func__);
|
||||
} else {
|
||||
/*Query callback function symbol*/
|
||||
handle.thermal_client_register_callback =
|
||||
(int (*)(char *, int (*)(int),void *))
|
||||
dlsym(handle.thermal_handle, "thermal_client_register_callback");
|
||||
handle.thermal_client_unregister_callback =
|
||||
(void (*)(int) )
|
||||
dlsym(handle.thermal_handle, "thermal_client_unregister_callback");
|
||||
if (!handle.thermal_client_register_callback ||
|
||||
!handle.thermal_client_unregister_callback) {
|
||||
ALOGE("%s: DLSYM thermal_client_register_callback failed", __func__);
|
||||
} else {
|
||||
/*Register callback function*/
|
||||
handle.thermal_client_handle =
|
||||
handle.thermal_client_register_callback("spkr", thermal_client_callback, NULL);
|
||||
if (!handle.thermal_client_handle) {
|
||||
ALOGE("%s: thermal_client_register_callback failed", __func__);
|
||||
} else {
|
||||
ALOGD("%s: spkr_prot thermal_client_register_callback success", __func__);
|
||||
handle.thermal_client_request = (int (*)(char *, int))
|
||||
dlsym(handle.thermal_handle, "thermal_client_request");
|
||||
}
|
||||
}
|
||||
}
|
||||
if (handle.thermal_client_request) {
|
||||
ALOGD("%s: Create calibration thread", __func__);
|
||||
(void)pthread_create(&handle.spkr_calibration_thread,
|
||||
(const pthread_attr_t *) NULL, spkr_calibration_thread, &handle);
|
||||
} else {
|
||||
ALOGE("%s: thermal_client_request failed", __func__);
|
||||
if (handle.thermal_client_handle &&
|
||||
handle.thermal_client_unregister_callback)
|
||||
handle.thermal_client_unregister_callback(handle.thermal_client_handle);
|
||||
if (handle.thermal_handle)
|
||||
dlclose(handle.thermal_handle);
|
||||
handle.thermal_handle = NULL;
|
||||
handle.spkr_prot_enable = false;
|
||||
}
|
||||
|
||||
if (handle.spkr_prot_enable) {
|
||||
char platform[PROPERTY_VALUE_MAX];
|
||||
property_get("ro.board.platform", platform, "");
|
||||
if (!strncmp("apq8084", platform, sizeof("apq8084"))) {
|
||||
platform_set_snd_device_backend(SND_DEVICE_OUT_VOICE_SPEAKER,
|
||||
"speaker-protected",
|
||||
"SLIMBUS_0_RX");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int audio_extn_spkr_prot_get_acdb_id(snd_device_t snd_device)
|
||||
{
|
||||
int acdb_id;
|
||||
|
||||
switch(snd_device) {
|
||||
case SND_DEVICE_OUT_SPEAKER:
|
||||
acdb_id = platform_get_snd_device_acdb_id(SND_DEVICE_OUT_SPEAKER_PROTECTED);
|
||||
break;
|
||||
case SND_DEVICE_OUT_VOICE_SPEAKER:
|
||||
acdb_id = platform_get_snd_device_acdb_id(SND_DEVICE_OUT_VOICE_SPEAKER_PROTECTED);
|
||||
break;
|
||||
default:
|
||||
acdb_id = -EINVAL;
|
||||
break;
|
||||
}
|
||||
return acdb_id;
|
||||
}
|
||||
|
||||
int audio_extn_get_spkr_prot_snd_device(snd_device_t snd_device)
|
||||
{
|
||||
if (!handle.spkr_prot_enable)
|
||||
return snd_device;
|
||||
|
||||
switch(snd_device) {
|
||||
case SND_DEVICE_OUT_SPEAKER:
|
||||
return SND_DEVICE_OUT_SPEAKER_PROTECTED;
|
||||
case SND_DEVICE_OUT_VOICE_SPEAKER:
|
||||
return SND_DEVICE_OUT_VOICE_SPEAKER_PROTECTED;
|
||||
default:
|
||||
return snd_device;
|
||||
}
|
||||
}
|
||||
|
||||
int audio_extn_spkr_prot_start_processing(snd_device_t snd_device)
|
||||
{
|
||||
struct audio_usecase *uc_info_tx;
|
||||
struct audio_device *adev = handle.adev_handle;
|
||||
int32_t pcm_dev_tx_id = -1, ret = 0;
|
||||
|
||||
ALOGV("%s: Entry", __func__);
|
||||
if (!adev) {
|
||||
ALOGE("%s: Invalid params", __func__);
|
||||
return -EINVAL;
|
||||
}
|
||||
snd_device = audio_extn_get_spkr_prot_snd_device(snd_device);
|
||||
spkr_prot_set_spkrstatus(true);
|
||||
uc_info_tx = (struct audio_usecase *)calloc(1, sizeof(struct audio_usecase));
|
||||
if (!uc_info_tx) {
|
||||
return -ENOMEM;
|
||||
}
|
||||
ALOGV("%s: snd_device(%d: %s)", __func__, snd_device,
|
||||
platform_get_snd_device_name(snd_device));
|
||||
audio_route_apply_and_update_path(adev->audio_route,
|
||||
platform_get_snd_device_name(snd_device));
|
||||
|
||||
pthread_mutex_lock(&handle.mutex_spkr_prot);
|
||||
if (handle.spkr_processing_state == SPKR_PROCESSING_IN_IDLE) {
|
||||
uc_info_tx->id = USECASE_AUDIO_SPKR_CALIB_TX;
|
||||
uc_info_tx->type = PCM_CAPTURE;
|
||||
uc_info_tx->in_snd_device = SND_DEVICE_IN_CAPTURE_VI_FEEDBACK;
|
||||
uc_info_tx->out_snd_device = SND_DEVICE_NONE;
|
||||
handle.pcm_tx = NULL;
|
||||
list_add_tail(&adev->usecase_list, &uc_info_tx->list);
|
||||
enable_snd_device(adev, SND_DEVICE_IN_CAPTURE_VI_FEEDBACK);
|
||||
enable_audio_route(adev, uc_info_tx);
|
||||
|
||||
pcm_dev_tx_id = platform_get_pcm_device_id(uc_info_tx->id, PCM_CAPTURE);
|
||||
if (pcm_dev_tx_id < 0) {
|
||||
ALOGE("%s: Invalid pcm device for usecase (%d)",
|
||||
__func__, uc_info_tx->id);
|
||||
ret = -ENODEV;
|
||||
goto exit;
|
||||
}
|
||||
handle.pcm_tx = pcm_open(adev->snd_card,
|
||||
pcm_dev_tx_id,
|
||||
PCM_IN, &pcm_config_skr_prot);
|
||||
if (handle.pcm_tx && !pcm_is_ready(handle.pcm_tx)) {
|
||||
ALOGE("%s: %s", __func__, pcm_get_error(handle.pcm_tx));
|
||||
ret = -EIO;
|
||||
goto exit;
|
||||
}
|
||||
if (pcm_start(handle.pcm_tx) < 0) {
|
||||
ALOGE("%s: pcm start for TX failed", __func__);
|
||||
ret = -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
exit:
|
||||
/* Clear VI feedback cal and replace with handset MIC */
|
||||
platform_send_audio_calibration(adev->platform, SND_DEVICE_IN_HANDSET_MIC);
|
||||
if (ret) {
|
||||
if (handle.pcm_tx)
|
||||
pcm_close(handle.pcm_tx);
|
||||
handle.pcm_tx = NULL;
|
||||
list_remove(&uc_info_tx->list);
|
||||
disable_snd_device(adev, SND_DEVICE_IN_CAPTURE_VI_FEEDBACK);
|
||||
disable_audio_route(adev, uc_info_tx);
|
||||
free(uc_info_tx);
|
||||
} else
|
||||
handle.spkr_processing_state = SPKR_PROCESSING_IN_PROGRESS;
|
||||
pthread_mutex_unlock(&handle.mutex_spkr_prot);
|
||||
ALOGV("%s: Exit", __func__);
|
||||
return ret;
|
||||
}
|
||||
|
||||
void audio_extn_spkr_prot_stop_processing(snd_device_t snd_device)
|
||||
{
|
||||
struct audio_usecase *uc_info_tx;
|
||||
struct audio_device *adev = handle.adev_handle;
|
||||
|
||||
ALOGV("%s: Entry", __func__);
|
||||
snd_device = audio_extn_get_spkr_prot_snd_device(snd_device);
|
||||
spkr_prot_set_spkrstatus(false);
|
||||
pthread_mutex_lock(&handle.mutex_spkr_prot);
|
||||
if (adev && handle.spkr_processing_state == SPKR_PROCESSING_IN_PROGRESS) {
|
||||
uc_info_tx = get_usecase_from_list(adev, USECASE_AUDIO_SPKR_CALIB_TX);
|
||||
if (handle.pcm_tx)
|
||||
pcm_close(handle.pcm_tx);
|
||||
handle.pcm_tx = NULL;
|
||||
disable_snd_device(adev, SND_DEVICE_IN_CAPTURE_VI_FEEDBACK);
|
||||
if (uc_info_tx) {
|
||||
list_remove(&uc_info_tx->list);
|
||||
disable_audio_route(adev, uc_info_tx);
|
||||
free(uc_info_tx);
|
||||
}
|
||||
}
|
||||
handle.spkr_processing_state = SPKR_PROCESSING_IN_IDLE;
|
||||
pthread_mutex_unlock(&handle.mutex_spkr_prot);
|
||||
if (adev)
|
||||
audio_route_reset_and_update_path(adev->audio_route,
|
||||
platform_get_snd_device_name(snd_device));
|
||||
ALOGV("%s: Exit", __func__);
|
||||
}
|
||||
|
||||
bool audio_extn_spkr_prot_is_enabled()
|
||||
{
|
||||
return handle.spkr_prot_enable;
|
||||
}
|
||||
#endif /*SPKR_PROT_ENABLED*/
|
545
android/hardware/qcom/audio/hal/audio_extn/tfa_98xx.c
Normal file
545
android/hardware/qcom/audio/hal/audio_extn/tfa_98xx.c
Normal file
|
@ -0,0 +1,545 @@
|
|||
/*
|
||||
* 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;
|
||||
}
|
||||
}
|
||||
|
42
android/hardware/qcom/audio/hal/audio_extn/tfa_98xx.h
Normal file
42
android/hardware/qcom/audio/hal/audio_extn/tfa_98xx.h
Normal file
|
@ -0,0 +1,42 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef TFA_98XX_H
|
||||
#define TFA_98XX_H
|
||||
|
||||
#ifdef SMART_PA_TFA_98XX_SUPPORTED
|
||||
int audio_extn_tfa_98xx_enable_speaker(void);
|
||||
void audio_extn_tfa_98xx_disable_speaker(snd_device_t snd_device);
|
||||
void audio_extn_tfa_98xx_set_mode();
|
||||
void audio_extn_tfa_98xx_set_mode_bt(void);
|
||||
void audio_extn_tfa_98xx_update(void);
|
||||
void audio_extn_tfa_98xx_set_voice_vol(float vol);
|
||||
int audio_extn_tfa_98xx_init(struct audio_device *adev);
|
||||
void audio_extn_tfa_98xx_deinit(void);
|
||||
bool audio_extn_tfa_98xx_is_supported(void);
|
||||
#else
|
||||
#define audio_extn_tfa_98xx_enable_speaker(void) (0)
|
||||
#define audio_extn_tfa_98xx_disable_speaker(snd_device) (0)
|
||||
#define audio_extn_tfa_98xx_set_mode() (0)
|
||||
#define audio_extn_tfa_98xx_set_mode_bt() (0)
|
||||
#define audio_extn_tfa_98xx_update(void) (0)
|
||||
#define audio_extn_tfa_98xx_set_voice_vol(vol) (0)
|
||||
#define audio_extn_tfa_98xx_init(adev) (0)
|
||||
#define audio_extn_tfa_98xx_deinit(void) (0)
|
||||
#define audio_extn_tfa_98xx_is_supported(void) (false)
|
||||
#endif
|
||||
|
||||
#endif /* TFA_98XX_H */
|
1140
android/hardware/qcom/audio/hal/audio_extn/usb.c
Normal file
1140
android/hardware/qcom/audio/hal/audio_extn/usb.c
Normal file
File diff suppressed because it is too large
Load diff
608
android/hardware/qcom/audio/hal/audio_extn/utils.c
Normal file
608
android/hardware/qcom/audio/hal/audio_extn/utils.c
Normal file
|
@ -0,0 +1,608 @@
|
|||
/*
|
||||
* Copyright (C) 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 "audio_hw_utils"
|
||||
//#define LOG_NDEBUG 0
|
||||
|
||||
#include <errno.h>
|
||||
#include <cutils/properties.h>
|
||||
#include <cutils/config_utils.h>
|
||||
#include <stdlib.h>
|
||||
#include <dlfcn.h>
|
||||
#include <unistd.h>
|
||||
#include <cutils/str_parms.h>
|
||||
#include <cutils/log.h>
|
||||
#include <cutils/misc.h>
|
||||
|
||||
#include "acdb.h"
|
||||
#include "audio_hw.h"
|
||||
#include "platform.h"
|
||||
#include "platform_api.h"
|
||||
#include "audio_extn.h"
|
||||
|
||||
#define MAX_LENGTH_MIXER_CONTROL_IN_INT 128
|
||||
|
||||
static int set_stream_app_type_mixer_ctrl(struct audio_device *adev,
|
||||
int pcm_device_id, int app_type,
|
||||
int acdb_dev_id, int sample_rate,
|
||||
int stream_type,
|
||||
snd_device_t snd_device)
|
||||
{
|
||||
|
||||
char mixer_ctl_name[MAX_LENGTH_MIXER_CONTROL_IN_INT];
|
||||
struct mixer_ctl *ctl;
|
||||
int app_type_cfg[MAX_LENGTH_MIXER_CONTROL_IN_INT], len = 0, rc = 0;
|
||||
int snd_device_be_idx = -1;
|
||||
|
||||
if (stream_type == PCM_PLAYBACK) {
|
||||
snprintf(mixer_ctl_name, sizeof(mixer_ctl_name),
|
||||
"Audio Stream %d App Type Cfg", pcm_device_id);
|
||||
} else if (stream_type == PCM_CAPTURE) {
|
||||
snprintf(mixer_ctl_name, sizeof(mixer_ctl_name),
|
||||
"Audio Stream Capture %d App Type Cfg", pcm_device_id);
|
||||
}
|
||||
|
||||
ctl = mixer_get_ctl_by_name(adev->mixer, mixer_ctl_name);
|
||||
if (!ctl) {
|
||||
ALOGE("%s: Could not get ctl for mixer cmd - %s",
|
||||
__func__, mixer_ctl_name);
|
||||
rc = -EINVAL;
|
||||
goto exit;
|
||||
}
|
||||
app_type_cfg[len++] = app_type;
|
||||
app_type_cfg[len++] = acdb_dev_id;
|
||||
app_type_cfg[len++] = sample_rate;
|
||||
|
||||
snd_device_be_idx = platform_get_snd_device_backend_index(snd_device);
|
||||
if (snd_device_be_idx > 0)
|
||||
app_type_cfg[len++] = snd_device_be_idx;
|
||||
ALOGV("%s: stream type %d app_type %d, acdb_dev_id %d "
|
||||
"sample rate %d, snd_device_be_idx %d",
|
||||
__func__, stream_type, app_type, acdb_dev_id, sample_rate,
|
||||
snd_device_be_idx);
|
||||
mixer_ctl_set_array(ctl, app_type_cfg, len);
|
||||
|
||||
exit:
|
||||
return rc;
|
||||
}
|
||||
|
||||
void audio_extn_utils_send_default_app_type_cfg(void *platform, struct mixer *mixer)
|
||||
{
|
||||
int app_type_cfg[MAX_LENGTH_MIXER_CONTROL_IN_INT] = {-1};
|
||||
int length = 0, app_type = 0,rc = 0;
|
||||
struct mixer_ctl *ctl = NULL;
|
||||
const char *mixer_ctl_name = "App Type Config";
|
||||
|
||||
ctl = mixer_get_ctl_by_name(mixer, mixer_ctl_name);
|
||||
if (!ctl) {
|
||||
ALOGE("%s: Could not get ctl for mixer cmd - %s",__func__, mixer_ctl_name);
|
||||
return;
|
||||
}
|
||||
rc = platform_get_default_app_type_v2(platform, PCM_PLAYBACK, &app_type);
|
||||
if (rc == 0) {
|
||||
app_type_cfg[length++] = 1;
|
||||
app_type_cfg[length++] = app_type;
|
||||
app_type_cfg[length++] = 48000;
|
||||
app_type_cfg[length++] = 16;
|
||||
mixer_ctl_set_array(ctl, app_type_cfg, length);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
static const char *flags_to_mode(int dir, uint32_t flags)
|
||||
{
|
||||
if (dir == 0) {
|
||||
if (flags & AUDIO_OUTPUT_FLAG_VOIP_RX) {
|
||||
return "voip";
|
||||
}
|
||||
} else if (dir == 1) {
|
||||
if (flags & AUDIO_INPUT_FLAG_VOIP_TX) {
|
||||
return "voip";
|
||||
}
|
||||
}
|
||||
return "default";
|
||||
}
|
||||
|
||||
static int audio_extn_utils_send_app_type_cfg_hfp(struct audio_device *adev,
|
||||
struct audio_usecase *usecase)
|
||||
{
|
||||
struct mixer_ctl *ctl;
|
||||
int pcm_device_id, acdb_dev_id = 0, snd_device = usecase->out_snd_device;
|
||||
int32_t sample_rate = DEFAULT_OUTPUT_SAMPLING_RATE;
|
||||
int app_type = 0, rc = 0;
|
||||
|
||||
ALOGV("%s", __func__);
|
||||
|
||||
if (usecase->type != PCM_HFP_CALL) {
|
||||
ALOGV("%s: not a playback or HFP path, no need to cfg app type", __func__);
|
||||
rc = 0;
|
||||
goto exit_send_app_type_cfg;
|
||||
}
|
||||
if ((usecase->id != USECASE_AUDIO_HFP_SCO) &&
|
||||
(usecase->id != USECASE_AUDIO_HFP_SCO_WB)) {
|
||||
ALOGV("%s: a playback path where app type cfg is not required", __func__);
|
||||
rc = 0;
|
||||
goto exit_send_app_type_cfg;
|
||||
}
|
||||
|
||||
snd_device = usecase->out_snd_device;
|
||||
pcm_device_id = platform_get_pcm_device_id(usecase->id, PCM_PLAYBACK);
|
||||
|
||||
snd_device = (snd_device == SND_DEVICE_OUT_SPEAKER) ?
|
||||
audio_extn_get_spkr_prot_snd_device(snd_device) : snd_device;
|
||||
acdb_dev_id = platform_get_snd_device_acdb_id(snd_device);
|
||||
if (acdb_dev_id < 0) {
|
||||
ALOGE("%s: Couldn't get the acdb dev id", __func__);
|
||||
rc = -EINVAL;
|
||||
goto exit_send_app_type_cfg;
|
||||
}
|
||||
|
||||
if (usecase->type == PCM_HFP_CALL) {
|
||||
|
||||
/* config HFP session:1 playback path */
|
||||
rc = platform_get_default_app_type_v2(adev->platform, PCM_PLAYBACK, &app_type);
|
||||
if (rc < 0)
|
||||
goto exit_send_app_type_cfg;
|
||||
|
||||
sample_rate= CODEC_BACKEND_DEFAULT_SAMPLE_RATE;
|
||||
rc = set_stream_app_type_mixer_ctrl(adev, pcm_device_id, app_type,
|
||||
acdb_dev_id, sample_rate,
|
||||
PCM_PLAYBACK,
|
||||
SND_DEVICE_NONE); // use legacy behavior
|
||||
if (rc < 0)
|
||||
goto exit_send_app_type_cfg;
|
||||
/* config HFP session:1 capture path */
|
||||
rc = platform_get_default_app_type_v2(adev->platform, PCM_CAPTURE, &app_type);
|
||||
|
||||
if (rc == 0) {
|
||||
rc = set_stream_app_type_mixer_ctrl(adev, pcm_device_id, app_type,
|
||||
acdb_dev_id, sample_rate,
|
||||
PCM_CAPTURE,
|
||||
SND_DEVICE_NONE);
|
||||
if (rc < 0)
|
||||
goto exit_send_app_type_cfg;
|
||||
}
|
||||
/* config HFP session:2 capture path */
|
||||
pcm_device_id = HFP_ASM_RX_TX;
|
||||
snd_device = usecase->in_snd_device;
|
||||
acdb_dev_id = platform_get_snd_device_acdb_id(snd_device);
|
||||
if (acdb_dev_id <= 0) {
|
||||
ALOGE("%s: Couldn't get the acdb dev id", __func__);
|
||||
rc = -EINVAL;
|
||||
goto exit_send_app_type_cfg;
|
||||
}
|
||||
rc = platform_get_default_app_type_v2(adev->platform, PCM_CAPTURE, &app_type);
|
||||
if (rc == 0) {
|
||||
rc = set_stream_app_type_mixer_ctrl(adev, pcm_device_id, app_type,
|
||||
acdb_dev_id, sample_rate, PCM_CAPTURE,
|
||||
SND_DEVICE_NONE);
|
||||
if (rc < 0)
|
||||
goto exit_send_app_type_cfg;
|
||||
}
|
||||
|
||||
/* config HFP session:2 playback path */
|
||||
rc = platform_get_default_app_type_v2(adev->platform, PCM_PLAYBACK, &app_type);
|
||||
if (rc == 0) {
|
||||
rc = set_stream_app_type_mixer_ctrl(adev, pcm_device_id, app_type,
|
||||
acdb_dev_id, sample_rate,
|
||||
PCM_PLAYBACK, SND_DEVICE_NONE);
|
||||
if (rc < 0)
|
||||
goto exit_send_app_type_cfg;
|
||||
}
|
||||
}
|
||||
|
||||
rc = 0;
|
||||
exit_send_app_type_cfg:
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
static int derive_capture_app_type_cfg(struct audio_device *adev,
|
||||
struct audio_usecase *usecase,
|
||||
int *app_type,
|
||||
int *sample_rate)
|
||||
{
|
||||
if (usecase->stream.in == NULL) {
|
||||
return -1;
|
||||
}
|
||||
struct stream_in *in = usecase->stream.in;
|
||||
struct stream_app_type_cfg *app_type_cfg = &in->app_type_cfg;
|
||||
|
||||
*sample_rate = DEFAULT_INPUT_SAMPLING_RATE;
|
||||
if (audio_is_usb_in_device(in->device)) {
|
||||
platform_check_and_update_copp_sample_rate(adev->platform,
|
||||
usecase->in_snd_device,
|
||||
in->sample_rate,
|
||||
sample_rate);
|
||||
}
|
||||
|
||||
app_type_cfg->mode = flags_to_mode(1 /*capture*/, in->flags);
|
||||
ALOGV("%s mode %s", __func__, app_type_cfg->mode);
|
||||
if (in->format == AUDIO_FORMAT_PCM_16_BIT) {
|
||||
platform_get_app_type_v2(adev->platform,
|
||||
PCM_CAPTURE,
|
||||
app_type_cfg->mode,
|
||||
16,
|
||||
app_type_cfg->sample_rate,
|
||||
app_type);
|
||||
} else if (in->format == AUDIO_FORMAT_PCM_24_BIT_PACKED ||
|
||||
in->format == AUDIO_FORMAT_PCM_8_24_BIT) {
|
||||
platform_get_app_type_v2(adev->platform,
|
||||
PCM_CAPTURE,
|
||||
app_type_cfg->mode,
|
||||
24,
|
||||
app_type_cfg->sample_rate,
|
||||
app_type);
|
||||
} else if (in->format == AUDIO_FORMAT_PCM_32_BIT) {
|
||||
platform_get_app_type_v2(adev->platform,
|
||||
PCM_CAPTURE,
|
||||
app_type_cfg->mode,
|
||||
32,
|
||||
app_type_cfg->sample_rate,
|
||||
app_type);
|
||||
} else {
|
||||
ALOGE("%s bad format\n", __func__);
|
||||
return -1;
|
||||
}
|
||||
|
||||
app_type_cfg->app_type = *app_type;
|
||||
app_type_cfg->sample_rate = *sample_rate;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int derive_playback_app_type_cfg(struct audio_device *adev,
|
||||
struct audio_usecase *usecase,
|
||||
int *app_type,
|
||||
int *sample_rate)
|
||||
{
|
||||
if (usecase->stream.out == NULL) {
|
||||
return -1;
|
||||
}
|
||||
struct stream_out *out = usecase->stream.out;
|
||||
struct stream_app_type_cfg *app_type_cfg = &out->app_type_cfg;
|
||||
|
||||
*sample_rate = DEFAULT_OUTPUT_SAMPLING_RATE;
|
||||
|
||||
// add speaker prot changes if needed
|
||||
// and use that to check for device
|
||||
if (audio_is_usb_out_device(out->devices)) {
|
||||
platform_check_and_update_copp_sample_rate(adev->platform,
|
||||
usecase->out_snd_device,
|
||||
out->sample_rate,
|
||||
sample_rate);
|
||||
}
|
||||
|
||||
app_type_cfg->mode = flags_to_mode(0 /*playback*/, out->flags);
|
||||
if (!audio_is_linear_pcm(out->format)) {
|
||||
platform_get_app_type_v2(adev->platform,
|
||||
PCM_PLAYBACK,
|
||||
app_type_cfg->mode,
|
||||
24,
|
||||
*sample_rate,
|
||||
app_type);
|
||||
ALOGV("Non pcm got app type %d", *app_type);
|
||||
} else if (out->format == AUDIO_FORMAT_PCM_16_BIT) {
|
||||
platform_get_app_type_v2(adev->platform,
|
||||
PCM_PLAYBACK,
|
||||
app_type_cfg->mode,
|
||||
16,
|
||||
*sample_rate,
|
||||
app_type);
|
||||
} else if (out->format == AUDIO_FORMAT_PCM_24_BIT_PACKED ||
|
||||
out->format == AUDIO_FORMAT_PCM_8_24_BIT) {
|
||||
platform_get_app_type_v2(adev->platform,
|
||||
PCM_PLAYBACK,
|
||||
app_type_cfg->mode,
|
||||
24,
|
||||
*sample_rate,
|
||||
app_type);
|
||||
} else if (out->format == AUDIO_FORMAT_PCM_32_BIT) {
|
||||
platform_get_app_type_v2(adev->platform,
|
||||
PCM_PLAYBACK,
|
||||
app_type_cfg->mode,
|
||||
32,
|
||||
*sample_rate,
|
||||
app_type);
|
||||
} else {
|
||||
ALOGE("%s bad format\n", __func__);
|
||||
return -1;
|
||||
}
|
||||
|
||||
app_type_cfg->app_type = *app_type;
|
||||
app_type_cfg->sample_rate = *sample_rate;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int derive_acdb_dev_id(struct audio_device *adev __unused,
|
||||
struct audio_usecase *usecase)
|
||||
{
|
||||
struct stream_out *out;
|
||||
struct stream_in *in;
|
||||
|
||||
if (usecase->type == PCM_PLAYBACK) {
|
||||
return platform_get_snd_device_acdb_id(usecase->out_snd_device);
|
||||
} else if(usecase->type == PCM_CAPTURE) {
|
||||
return platform_get_snd_device_acdb_id(usecase->in_snd_device);
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
int audio_extn_utils_send_app_type_cfg(struct audio_device *adev,
|
||||
struct audio_usecase *usecase)
|
||||
{
|
||||
int len = 0;
|
||||
int sample_rate;
|
||||
int app_type;
|
||||
int acdb_dev_id;
|
||||
size_t app_type_cfg[MAX_LENGTH_MIXER_CONTROL_IN_INT] = {0};
|
||||
char mixer_ctl_name[MAX_LENGTH_MIXER_CONTROL_IN_INT] = {0};
|
||||
int pcm_device_id;
|
||||
struct mixer_ctl *ctl;
|
||||
int ret;
|
||||
|
||||
if (usecase->type == PCM_HFP_CALL) {
|
||||
return audio_extn_utils_send_app_type_cfg_hfp(adev, usecase);
|
||||
}
|
||||
|
||||
if (!platform_supports_app_type_cfg())
|
||||
return -1;
|
||||
|
||||
if (usecase->type == PCM_PLAYBACK) {
|
||||
ret = derive_playback_app_type_cfg(adev,
|
||||
usecase,
|
||||
&app_type,
|
||||
&sample_rate);
|
||||
} else if (usecase->type == PCM_CAPTURE) {
|
||||
ret = derive_capture_app_type_cfg(adev,
|
||||
usecase,
|
||||
&app_type,
|
||||
&sample_rate);
|
||||
} else {
|
||||
ALOGE("%s: Invalid uc type : 0x%x", __func__, usecase->type);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (ret < 0) {
|
||||
ALOGE("%s: Failed to derive app_type for uc type : 0x%x", __func__,
|
||||
usecase->type);
|
||||
return -1;
|
||||
}
|
||||
|
||||
acdb_dev_id = derive_acdb_dev_id(adev, usecase);
|
||||
if (acdb_dev_id <= 0) {
|
||||
ALOGE("%s: Couldn't get the acdb dev id", __func__);
|
||||
return -1;
|
||||
}
|
||||
|
||||
pcm_device_id = platform_get_pcm_device_id(usecase->id, usecase->type);
|
||||
set_stream_app_type_mixer_ctrl(adev, pcm_device_id, app_type, acdb_dev_id,
|
||||
sample_rate,
|
||||
usecase->type,
|
||||
usecase->type == PCM_PLAYBACK ? usecase->out_snd_device :
|
||||
usecase->in_snd_device);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int audio_extn_utils_send_app_type_gain(struct audio_device *adev,
|
||||
int app_type,
|
||||
int *gain)
|
||||
{
|
||||
int gain_cfg[4];
|
||||
const char *mixer_ctl_name = "App Type Gain";
|
||||
struct mixer_ctl *ctl;
|
||||
ctl = mixer_get_ctl_by_name(adev->mixer, mixer_ctl_name);
|
||||
if (!ctl) {
|
||||
ALOGE("%s: Could not get volume ctl mixer %s", __func__,
|
||||
mixer_ctl_name);
|
||||
return -EINVAL;
|
||||
}
|
||||
gain_cfg[0] = 0;
|
||||
gain_cfg[1] = app_type;
|
||||
gain_cfg[2] = gain[0];
|
||||
gain_cfg[3] = gain[1];
|
||||
ALOGV("%s app_type %d l(%d) r(%d)", __func__, app_type, gain[0], gain[1]);
|
||||
return mixer_ctl_set_array(ctl, gain_cfg,
|
||||
sizeof(gain_cfg)/sizeof(gain_cfg[0]));
|
||||
}
|
||||
|
||||
// this assumes correct app_type and sample_rate fields
|
||||
// have been set for the stream using audio_extn_utils_send_app_type_cfg
|
||||
void audio_extn_utils_send_audio_calibration(struct audio_device *adev,
|
||||
struct audio_usecase *usecase)
|
||||
{
|
||||
int type = usecase->type;
|
||||
int app_type = 0;
|
||||
|
||||
if (type == PCM_PLAYBACK && usecase->stream.out != NULL) {
|
||||
struct stream_out *out = usecase->stream.out;
|
||||
ALOGV("%s send cal for app_type %d, rate %d", __func__,
|
||||
out->app_type_cfg.app_type,
|
||||
out->app_type_cfg.sample_rate);
|
||||
platform_send_audio_calibration_v2(adev->platform, usecase,
|
||||
out->app_type_cfg.app_type,
|
||||
out->app_type_cfg.sample_rate);
|
||||
} else if (type == PCM_CAPTURE && usecase->stream.in != NULL) {
|
||||
struct stream_in *in = usecase->stream.in;
|
||||
ALOGV("%s send cal for capture app_type %d, rate %d", __func__,
|
||||
in->app_type_cfg.app_type,
|
||||
in->app_type_cfg.sample_rate);
|
||||
platform_send_audio_calibration_v2(adev->platform, usecase,
|
||||
in->app_type_cfg.app_type,
|
||||
in->app_type_cfg.sample_rate);
|
||||
} else {
|
||||
/* when app type is default. the sample rate is not used to send cal */
|
||||
platform_get_default_app_type_v2(adev->platform, type, &app_type);
|
||||
platform_send_audio_calibration_v2(adev->platform, usecase, app_type,
|
||||
48000);
|
||||
}
|
||||
}
|
||||
|
||||
#define MAX_SND_CARD 8
|
||||
#define RETRY_US 500000
|
||||
#define RETRY_NUMBER 10
|
||||
|
||||
#define min(a, b) ((a) < (b) ? (a) : (b))
|
||||
|
||||
static const char *kConfigLocationList[] =
|
||||
{"/odm/etc", "/vendor/etc", "/system/etc"};
|
||||
static const int kConfigLocationListSize =
|
||||
(sizeof(kConfigLocationList) / sizeof(kConfigLocationList[0]));
|
||||
|
||||
bool audio_extn_utils_resolve_config_file(char file_name[MIXER_PATH_MAX_LENGTH])
|
||||
{
|
||||
char full_config_path[MIXER_PATH_MAX_LENGTH];
|
||||
for (int i = 0; i < kConfigLocationListSize; i++) {
|
||||
snprintf(full_config_path,
|
||||
MIXER_PATH_MAX_LENGTH,
|
||||
"%s/%s",
|
||||
kConfigLocationList[i],
|
||||
file_name);
|
||||
if (F_OK == access(full_config_path, 0)) {
|
||||
strcpy(file_name, full_config_path);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/* platform_info_file should be size 'MIXER_PATH_MAX_LENGTH' */
|
||||
int audio_extn_utils_get_platform_info(const char* snd_card_name, char* platform_info_file)
|
||||
{
|
||||
if (NULL == snd_card_name) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
struct snd_card_split *snd_split_handle = NULL;
|
||||
int ret = 0;
|
||||
audio_extn_set_snd_card_split(snd_card_name);
|
||||
snd_split_handle = audio_extn_get_snd_card_split();
|
||||
|
||||
snprintf(platform_info_file, MIXER_PATH_MAX_LENGTH, "%s_%s_%s.xml",
|
||||
PLATFORM_INFO_XML_BASE_STRING, snd_split_handle->snd_card,
|
||||
snd_split_handle->form_factor);
|
||||
|
||||
if (!audio_extn_utils_resolve_config_file(platform_info_file)) {
|
||||
memset(platform_info_file, 0, MIXER_PATH_MAX_LENGTH);
|
||||
snprintf(platform_info_file, MIXER_PATH_MAX_LENGTH, "%s_%s.xml",
|
||||
PLATFORM_INFO_XML_BASE_STRING, snd_split_handle->snd_card);
|
||||
|
||||
if (!audio_extn_utils_resolve_config_file(platform_info_file)) {
|
||||
memset(platform_info_file, 0, MIXER_PATH_MAX_LENGTH);
|
||||
strlcpy(platform_info_file, PLATFORM_INFO_XML_PATH, MIXER_PATH_MAX_LENGTH);
|
||||
ret = audio_extn_utils_resolve_config_file(platform_info_file) ? 0 : -1;
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int audio_extn_utils_get_snd_card_num()
|
||||
{
|
||||
|
||||
void *hw_info = NULL;
|
||||
struct mixer *mixer = NULL;
|
||||
int retry_num = 0;
|
||||
int snd_card_num = 0;
|
||||
const char* snd_card_name = NULL;
|
||||
char platform_info_file[MIXER_PATH_MAX_LENGTH]= {0};
|
||||
|
||||
struct acdb_platform_data *my_data = calloc(1, sizeof(struct acdb_platform_data));
|
||||
|
||||
bool card_verifed[MAX_SND_CARD] = {0};
|
||||
const int retry_limit = property_get_int32("audio.snd_card.open.retries", RETRY_NUMBER);
|
||||
|
||||
for (;;) {
|
||||
if (snd_card_num >= MAX_SND_CARD) {
|
||||
if (retry_num++ >= retry_limit) {
|
||||
ALOGE("%s: Unable to find correct sound card, aborting.", __func__);
|
||||
snd_card_num = -1;
|
||||
goto done;
|
||||
}
|
||||
|
||||
snd_card_num = 0;
|
||||
usleep(RETRY_US);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (card_verifed[snd_card_num]) {
|
||||
++snd_card_num;
|
||||
continue;
|
||||
}
|
||||
|
||||
mixer = mixer_open(snd_card_num);
|
||||
|
||||
if (!mixer) {
|
||||
ALOGE("%s: Unable to open the mixer card: %d", __func__,
|
||||
snd_card_num);
|
||||
++snd_card_num;
|
||||
continue;
|
||||
}
|
||||
|
||||
card_verifed[snd_card_num] = true;
|
||||
|
||||
snd_card_name = mixer_get_name(mixer);
|
||||
hw_info = hw_info_init(snd_card_name);
|
||||
|
||||
if (audio_extn_utils_get_platform_info(snd_card_name, platform_info_file) < 0) {
|
||||
ALOGE("Failed to find platform_info_file");
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
/* Initialize snd card name specific ids and/or backends*/
|
||||
if (snd_card_info_init(platform_info_file, my_data,
|
||||
&acdb_set_parameters) < 0) {
|
||||
ALOGE("Failed to find platform_info_file");
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
/* validate the sound card name
|
||||
* my_data->snd_card_name can contain
|
||||
* <a> complete sound card name, i.e. <device>-<codec>-<form_factor>-snd-card
|
||||
* example: msm8994-tomtom-mtp-snd-card
|
||||
* <b> or sub string of the card name, i.e. <device>-<codec>
|
||||
* example: msm8994-tomtom
|
||||
* snd_card_name is truncated to 32 charaters as per mixer_get_name() implementation
|
||||
* so use min of my_data->snd_card_name and snd_card_name length for comparison
|
||||
*/
|
||||
|
||||
if (my_data->snd_card_name != NULL &&
|
||||
strncmp(snd_card_name, my_data->snd_card_name,
|
||||
min(strlen(snd_card_name), strlen(my_data->snd_card_name))) != 0) {
|
||||
ALOGI("%s: found valid sound card %s, but not primary sound card %s",
|
||||
__func__, snd_card_name, my_data->snd_card_name);
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
ALOGI("%s: found sound card %s, primary sound card expected is %s",
|
||||
__func__, snd_card_name, my_data->snd_card_name);
|
||||
break;
|
||||
cleanup:
|
||||
++snd_card_num;
|
||||
mixer_close(mixer);
|
||||
mixer = NULL;
|
||||
hw_info_deinit(hw_info);
|
||||
hw_info = NULL;
|
||||
}
|
||||
|
||||
done:
|
||||
mixer_close(mixer);
|
||||
hw_info_deinit(hw_info);
|
||||
|
||||
if (my_data)
|
||||
free(my_data);
|
||||
|
||||
return snd_card_num;
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue