summaryrefslogtreecommitdiffstats
path: root/hal
diff options
context:
space:
mode:
authorChaithanya Krishna Bacharaju <chaithan@codeaurora.org>2017-05-26 18:22:46 +0530
committerShiv Maliyappanahalli <smaliyap@codeaurora.org>2017-08-15 15:18:51 -0700
commit69d2e4c51eb69717e305daecb9bde2eca473f921 (patch)
tree48e1d2c1afb2d07742ee60881e81ad36698296e2 /hal
parent0515e043cb70a5991bfc3251ff3c4d652c6451ef (diff)
downloadhardware_qcom_audio-69d2e4c51eb69717e305daecb9bde2eca473f921.tar.gz
hardware_qcom_audio-69d2e4c51eb69717e305daecb9bde2eca473f921.tar.bz2
hardware_qcom_audio-69d2e4c51eb69717e305daecb9bde2eca473f921.zip
hal: Handle A2dp suspend scenario
Avoid writing data to A2dp output in case of non-offload and mute offload streams if A2dp is suspended. Restore device and unmute streams if muted as part of suspend. CRs-Fixed: 2066578 Change-Id: I3a222f128659de602ba1f6bed4d4e3cdcc7a379a
Diffstat (limited to 'hal')
-rw-r--r--hal/audio_extn/a2dp.c27
-rw-r--r--hal/audio_extn/audio_extn.h2
-rw-r--r--hal/audio_hw.c205
-rw-r--r--hal/audio_hw.h6
4 files changed, 209 insertions, 31 deletions
diff --git a/hal/audio_extn/a2dp.c b/hal/audio_extn/a2dp.c
index 5239a308..b96a147e 100644
--- a/hal/audio_extn/a2dp.c
+++ b/hal/audio_extn/a2dp.c
@@ -765,6 +765,8 @@ void audio_extn_a2dp_set_parameters(struct str_parms *parms)
{
int ret, val;
char value[32]={0};
+ struct audio_usecase *uc_info;
+ struct listnode *node;
if(a2dp.is_a2dp_offload_supported == false) {
ALOGV("no supported encoders identified,ignoring a2dp setparam");
@@ -801,6 +803,14 @@ void audio_extn_a2dp_set_parameters(struct str_parms *parms)
if ((!strncmp(value,"true",sizeof(value)))) {
ALOGD("Setting a2dp to suspend state");
a2dp.a2dp_suspended = true;
+ list_for_each(node, &a2dp.adev->usecase_list) {
+ uc_info = node_to_item(node, struct audio_usecase, list);
+ if (uc_info->type == PCM_PLAYBACK) {
+ pthread_mutex_unlock(&a2dp.adev->lock);
+ check_a2dp_restore(a2dp.adev, uc_info->stream.out, false);
+ pthread_mutex_lock(&a2dp.adev->lock);
+ }
+ }
reset_a2dp_enc_config_params();
if(a2dp.audio_suspend_stream)
a2dp.audio_suspend_stream();
@@ -832,12 +842,13 @@ void audio_extn_a2dp_set_parameters(struct str_parms *parms)
}
}
}
- // restore A2DP device for active usecases
list_for_each(node, &a2dp.adev->usecase_list) {
uc_info = node_to_item(node, struct audio_usecase, list);
- if ((uc_info->stream.out->devices & AUDIO_DEVICE_OUT_ALL_A2DP) &&
- (uc_info->out_snd_device != SND_DEVICE_OUT_BT_A2DP))
- select_devices(a2dp.adev, uc_info->id);
+ if (uc_info->type == PCM_PLAYBACK) {
+ pthread_mutex_unlock(&a2dp.adev->lock);
+ check_a2dp_restore(a2dp.adev, uc_info->stream.out, true);
+ pthread_mutex_lock(&a2dp.adev->lock);
+ }
}
}
}
@@ -874,6 +885,9 @@ bool audio_extn_a2dp_is_ready()
{
bool ret = false;
+ if (a2dp.a2dp_suspended)
+ return ret;
+
if ((a2dp.bt_state != A2DP_STATE_DISCONNECTED) &&
(a2dp.is_a2dp_offload_supported) &&
(a2dp.audio_check_a2dp_ready))
@@ -881,6 +895,11 @@ bool audio_extn_a2dp_is_ready()
return ret;
}
+bool audio_extn_a2dp_is_suspended()
+{
+ return a2dp.a2dp_suspended;
+}
+
void audio_extn_a2dp_init (void *adev)
{
a2dp.adev = (struct audio_device*)adev;
diff --git a/hal/audio_extn/audio_extn.h b/hal/audio_extn/audio_extn.h
index d0c3395a..c2a54843 100644
--- a/hal/audio_extn/audio_extn.h
+++ b/hal/audio_extn/audio_extn.h
@@ -222,6 +222,7 @@ bool audio_extn_usb_is_capture_supported();
#define audio_extn_a2dp_get_apptype_params(sample_rate,bit_width) (0)
#define audio_extn_a2dp_get_encoder_latency() (0)
#define audio_extn_a2dp_is_ready() (0)
+#define audio_extn_a2dp_is_suspended() (0)
#else
void audio_extn_a2dp_init(void *adev);
int audio_extn_a2dp_start_playback();
@@ -233,6 +234,7 @@ void audio_extn_a2dp_get_apptype_params(uint32_t *sample_rate,
uint32_t *bit_width);
uint32_t audio_extn_a2dp_get_encoder_latency();
bool audio_extn_a2dp_is_ready();
+bool audio_extn_a2dp_is_suspended();
#endif
#ifndef SSR_ENABLED
diff --git a/hal/audio_hw.c b/hal/audio_hw.c
index 2531b85a..ace2a712 100644
--- a/hal/audio_hw.c
+++ b/hal/audio_hw.c
@@ -346,6 +346,9 @@ static unsigned int audio_device_ref_count;
//cache last MBDRC cal step level
static int last_known_cal_step = -1 ;
+static int check_a2dp_restore_l(struct audio_device *adev, struct stream_out *out, bool restore);
+static int out_set_compr_volume(struct audio_stream_out *stream, float left, float right);
+
static bool may_use_noirq_mode(struct audio_device *adev, audio_usecase_t uc_id,
int flags __unused)
{
@@ -1728,6 +1731,12 @@ int select_devices(struct audio_device *adev, audio_usecase_t uc_id)
voice_check_and_update_aanc_path(adev, usecase->out_snd_device, false);
}
+ if ((usecase->out_snd_device == SND_DEVICE_OUT_SPEAKER_AND_BT_A2DP) &&
+ (!audio_extn_a2dp_is_ready())) {
+ ALOGW("%s: A2DP profile is not ready, routing to speaker only", __func__);
+ usecase->out_snd_device = SND_DEVICE_OUT_SPEAKER;
+ }
+
/* Disable current sound devices */
if (usecase->out_snd_device != SND_DEVICE_NONE) {
disable_audio_route(adev, usecase);
@@ -2355,6 +2364,7 @@ int start_output_stream(struct stream_out *out)
char mixer_ctl_name[128];
struct mixer_ctl *ctl = NULL;
char* perf_mode[] = {"ULL", "ULL_PP", "LL"};
+ bool a2dp_combo = false;
if ((out->usecase < 0) || (out->usecase >= AUDIO_USECASE_MAX)) {
ret = -EINVAL;
@@ -2375,13 +2385,13 @@ int start_output_stream(struct stream_out *out)
if (out->devices & AUDIO_DEVICE_OUT_ALL_A2DP) {
if (!audio_extn_a2dp_is_ready()) {
if (out->devices & AUDIO_DEVICE_OUT_SPEAKER) {
- //combo usecase just by pass a2dp
- ALOGW("%s: A2DP profile is not ready, route it to speaker", __func__);
- out->devices = AUDIO_DEVICE_OUT_SPEAKER;
+ a2dp_combo = true;
} else {
- ALOGE("%s: A2DP profile is not ready, return error", __func__);
- ret = -EAGAIN;
- goto error_config;
+ if (!(out->flags & AUDIO_OUTPUT_FLAG_COMPRESS_OFFLOAD)) {
+ ALOGE("%s: A2DP profile is not ready, return error", __func__);
+ ret = -EAGAIN;
+ goto error_config;
+ }
}
}
}
@@ -2434,7 +2444,19 @@ int start_output_stream(struct stream_out *out)
}
}
- select_devices(adev, out->usecase);
+ if ((out->devices & AUDIO_DEVICE_OUT_ALL_A2DP) &&
+ (!audio_extn_a2dp_is_ready())) {
+ if (!a2dp_combo) {
+ check_a2dp_restore_l(adev, out, false);
+ } else {
+ audio_devices_t dev = out->devices;
+ out->devices = AUDIO_DEVICE_OUT_SPEAKER;
+ select_devices(adev, out->usecase);
+ out->devices = dev;
+ }
+ } else {
+ select_devices(adev, out->usecase);
+ }
ALOGV("%s: Opening PCM device card_id(%d) device_id(%d) format(%#x)",
__func__, adev->snd_card, out->pcm_device_id, out->config.format);
@@ -2976,6 +2998,7 @@ static int out_set_parameters(struct audio_stream *stream, const char *kvpairs)
struct str_parms *parms;
char value[32];
int ret = 0, val = 0, err;
+ bool bypass_a2dp = false;
ALOGD("%s: enter: usecase(%d: %s) kvpairs: %s",
__func__, out->usecase, use_case_table[out->usecase], kvpairs);
@@ -3020,7 +3043,7 @@ static int out_set_parameters(struct audio_stream *stream, const char *kvpairs)
if (val & AUDIO_DEVICE_OUT_SPEAKER) {
//combo usecase just by pass a2dp
ALOGW("%s: A2DP profile is not ready,routing to speaker only", __func__);
- val = AUDIO_DEVICE_OUT_SPEAKER;
+ bypass_a2dp = true;
} else {
ALOGE("%s: A2DP profile is not ready,ignoring routing request", __func__);
/* update device to a2dp and don't route as BT returned error
@@ -3079,9 +3102,24 @@ static int out_set_parameters(struct audio_stream *stream, const char *kvpairs)
adev->adm_on_routing_change(adev->adm_data,
out->handle);
}
- select_devices(adev, out->usecase);
+ if (!bypass_a2dp) {
+ select_devices(adev, out->usecase);
+ } else {
+ out->devices = AUDIO_DEVICE_OUT_SPEAKER;
+ select_devices(adev, out->usecase);
+ out->devices = new_dev;
+ }
if (!same_dev)
audio_extn_perf_lock_release(&adev->perf_lock_handle);
+ if ((out->flags & AUDIO_OUTPUT_FLAG_COMPRESS_OFFLOAD) &&
+ out->a2dp_compress_mute &&
+ (!(out->devices & AUDIO_DEVICE_OUT_ALL_A2DP))) {
+ pthread_mutex_lock(&out->compr_mute_lock);
+ out->a2dp_compress_mute = false;
+ out_set_compr_volume(&out->stream, out->volume_l, out->volume_r);
+ pthread_mutex_unlock(&out->compr_mute_lock);
+ }
+
}
}
@@ -3287,11 +3325,40 @@ static float AmpToDb(float amplification)
return db;
}
+static int out_set_compr_volume(struct audio_stream_out *stream, float left,
+ float right)
+{
+ struct stream_out *out = (struct stream_out *)stream;
+ int volume[2];
+ char mixer_ctl_name[128];
+ struct audio_device *adev = out->dev;
+ struct mixer_ctl *ctl;
+ int pcm_device_id = platform_get_pcm_device_id(out->usecase,
+ PCM_PLAYBACK);
+
+ snprintf(mixer_ctl_name, sizeof(mixer_ctl_name),
+ "Compress 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;
+ }
+ ALOGE("%s:ctl for mixer cmd - %s, left %f, right %f",
+ __func__, mixer_ctl_name, left, right);
+ volume[0] = (int)(left * COMPRESS_PLAYBACK_VOLUME_MAX);
+ volume[1] = (int)(right * COMPRESS_PLAYBACK_VOLUME_MAX);
+ mixer_ctl_set_array(ctl, volume, sizeof(volume)/sizeof(volume[0]));
+
+ return 0;
+}
+
static int out_set_volume(struct audio_stream_out *stream, float left,
float right)
{
struct stream_out *out = (struct stream_out *)stream;
int volume[2];
+ int ret = 0;
if (out->usecase == USECASE_AUDIO_PLAYBACK_MULTI_CH) {
/* only take left channel into account: the API is for stereo anyway */
@@ -3320,24 +3387,14 @@ static int out_set_volume(struct audio_stream_out *stream, float left,
mixer_ctl_set_array(ctl, volume, sizeof(volume)/sizeof(volume[0]));
return 0;
} else {
- char mixer_ctl_name[128];
- struct audio_device *adev = out->dev;
- struct mixer_ctl *ctl;
- int pcm_device_id = platform_get_pcm_device_id(out->usecase,
- PCM_PLAYBACK);
-
- snprintf(mixer_ctl_name, sizeof(mixer_ctl_name),
- "Compress 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[0] = (int)(left * COMPRESS_PLAYBACK_VOLUME_MAX);
- volume[1] = (int)(right * COMPRESS_PLAYBACK_VOLUME_MAX);
- mixer_ctl_set_array(ctl, volume, sizeof(volume)/sizeof(volume[0]));
- return 0;
+ pthread_mutex_lock(&out->compr_mute_lock);
+ ALOGE("%s: compress mute %d", __func__, out->a2dp_compress_mute);
+ if (!out->a2dp_compress_mute)
+ ret = out_set_compr_volume(stream, left, right);
+ out->volume_l = left;
+ out->volume_r = right;
+ pthread_mutex_unlock(&out->compr_mute_lock);
+ return ret;
}
} else if (out->usecase == USECASE_AUDIO_PLAYBACK_VOIP) {
char mixer_ctl_name[] = "App Type Gain";
@@ -3412,6 +3469,21 @@ static ssize_t out_write(struct audio_stream_out *stream, const void *buffer,
out->is_iec61937_info_available = true;
}
}
+
+ if ((out->devices & AUDIO_DEVICE_OUT_ALL_A2DP) &&
+ (audio_extn_a2dp_is_suspended())) {
+ if (!(out->devices & AUDIO_DEVICE_OUT_SPEAKER)) {
+ if (!(out->flags & AUDIO_OUTPUT_FLAG_COMPRESS_OFFLOAD)) {
+ size_t bpf = audio_bytes_per_sample(out->format) *
+ audio_channel_count_from_out_mask(out->channel_mask);
+ if (bpf != 0)
+ out->written += bytes / bpf;
+ ret = -EIO;
+ goto exit;
+ }
+ }
+ }
+
if (out->standby) {
out->standby = false;
pthread_mutex_lock(&adev->lock);
@@ -4300,6 +4372,7 @@ int adev_open_output_stream(struct audio_hw_device *dev,
pthread_mutex_init(&out->lock, (const pthread_mutexattr_t *) NULL);
pthread_mutex_init(&out->pre_lock, (const pthread_mutexattr_t *) NULL);
+ pthread_mutex_init(&out->compr_mute_lock, (const pthread_mutexattr_t *) NULL);
pthread_cond_init(&out->cond, (const pthread_condattr_t *) NULL);
if (devices == AUDIO_DEVICE_NONE)
@@ -4320,6 +4393,7 @@ int adev_open_output_stream(struct audio_hw_device *dev,
out->non_blocking = 0;
out->convert_buffer = NULL;
out->started = 0;
+ out->a2dp_compress_mute = false;
if (out->devices & AUDIO_DEVICE_OUT_AUX_DIGITAL &&
(flags & AUDIO_OUTPUT_FLAG_DIRECT)) {
@@ -4862,6 +4936,8 @@ void adev_close_output_stream(struct audio_hw_device *dev __unused,
free(out->compr_config.codec);
}
+ out->a2dp_compress_mute = false;
+
if (out->convert_buffer != NULL) {
free(out->convert_buffer);
out->convert_buffer = NULL;
@@ -5626,6 +5702,81 @@ static void adev_snd_mon_cb(void *cookie, struct str_parms *parms)
return;
}
+/* out and adev lock held */
+static int check_a2dp_restore_l(struct audio_device *adev, struct stream_out *out, bool restore)
+{
+ struct audio_usecase *uc_info;
+ float left_p;
+ float right_p;
+ audio_devices_t devices;
+
+ uc_info = get_usecase_from_list(adev, out->usecase);
+ if (uc_info == NULL) {
+ ALOGE("%s: Could not find the usecase (%d) in the list",
+ __func__, out->usecase);
+ return -EINVAL;
+ }
+
+ ALOGD("%s: enter: usecase(%d: %s)", __func__,
+ out->usecase, use_case_table[out->usecase]);
+
+ if (restore) {
+ // restore A2DP device for active usecases and unmute if required
+ if ((out->devices & AUDIO_DEVICE_OUT_ALL_A2DP) &&
+ (uc_info->out_snd_device != SND_DEVICE_OUT_BT_A2DP)) {
+ ALOGD("%s: restoring A2dp and unmuting stream", __func__);
+ select_devices(adev, uc_info->id);
+ pthread_mutex_lock(&out->compr_mute_lock);
+ if ((out->flags & AUDIO_OUTPUT_FLAG_COMPRESS_OFFLOAD) &&
+ (out->a2dp_compress_mute)) {
+ out->a2dp_compress_mute = false;
+ out_set_compr_volume(&out->stream, out->volume_l, out->volume_r);
+ }
+ pthread_mutex_unlock(&out->compr_mute_lock);
+ }
+ } else {
+ // mute compress stream if suspended
+ pthread_mutex_lock(&out->compr_mute_lock);
+ if ((out->flags & AUDIO_OUTPUT_FLAG_COMPRESS_OFFLOAD) &&
+ (!out->a2dp_compress_mute)) {
+ if (!out->standby) {
+ ALOGD("%s: selecting speaker and muting stream", __func__);
+ devices = out->devices;
+ out->devices = AUDIO_DEVICE_OUT_SPEAKER;
+ left_p = out->volume_l;
+ right_p = out->volume_r;
+ if (out->offload_state == OFFLOAD_STATE_PLAYING)
+ compress_pause(out->compr);
+ out_set_compr_volume(&out->stream, (float)0, (float)0);
+ out->a2dp_compress_mute = true;
+ select_devices(adev, out->usecase);
+ if (out->offload_state == OFFLOAD_STATE_PLAYING)
+ compress_resume(out->compr);
+ out->devices = devices;
+ out->volume_l = left_p;
+ out->volume_r = right_p;
+ }
+ }
+ pthread_mutex_unlock(&out->compr_mute_lock);
+ }
+ ALOGV("%s: exit", __func__);
+ return 0;
+}
+
+int check_a2dp_restore(struct audio_device *adev, struct stream_out *out, bool restore)
+{
+ int ret = 0;
+
+ lock_output_stream(out);
+ pthread_mutex_lock(&adev->lock);
+
+ ret = check_a2dp_restore_l(adev, out, restore);
+
+ pthread_mutex_unlock(&adev->lock);
+ pthread_mutex_unlock(&out->lock);
+ return ret;
+}
+
static int adev_open(const hw_module_t *module, const char *name,
hw_device_t **device)
{
diff --git a/hal/audio_hw.h b/hal/audio_hw.h
index 91bf9548..561a967e 100644
--- a/hal/audio_hw.h
+++ b/hal/audio_hw.h
@@ -236,6 +236,7 @@ struct stream_out {
struct audio_stream_out stream;
pthread_mutex_t lock; /* see note below on mutex acquisition order */
pthread_mutex_t pre_lock; /* acquire before lock to avoid DOS by playback thread */
+ pthread_mutex_t compr_mute_lock; /* acquire before setting compress volume */
pthread_cond_t cond;
struct pcm_config config;
struct compr_config compr_config;
@@ -301,6 +302,9 @@ struct stream_out {
qahwi_stream_out_t qahwi_out;
bool is_iec61937_info_available;
+ bool a2dp_compress_mute;
+ float volume_l;
+ float volume_r;
};
struct stream_in {
@@ -502,6 +506,8 @@ int pcm_ioctl(struct pcm *pcm, int request, ...);
audio_usecase_t get_usecase_id_from_usecase_type(const struct audio_device *adev,
usecase_type_t type);
+int check_a2dp_restore(struct audio_device *adev, struct stream_out *out, bool restore);
+
int adev_open_output_stream(struct audio_hw_device *dev,
audio_io_handle_t handle,
audio_devices_t devices,