diff options
author | Chaithanya Krishna Bacharaju <chaithan@codeaurora.org> | 2017-05-26 18:22:46 +0530 |
---|---|---|
committer | Shiv Maliyappanahalli <smaliyap@codeaurora.org> | 2017-08-15 15:18:51 -0700 |
commit | 69d2e4c51eb69717e305daecb9bde2eca473f921 (patch) | |
tree | 48e1d2c1afb2d07742ee60881e81ad36698296e2 /hal | |
parent | 0515e043cb70a5991bfc3251ff3c4d652c6451ef (diff) | |
download | hardware_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.c | 27 | ||||
-rw-r--r-- | hal/audio_extn/audio_extn.h | 2 | ||||
-rw-r--r-- | hal/audio_hw.c | 205 | ||||
-rw-r--r-- | hal/audio_hw.h | 6 |
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, |