Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit d17a17ee authored by Chaithanya Krishna Bacharaju's avatar Chaithanya Krishna Bacharaju Committed by Satya Krishna Pindiproli
Browse files

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
parent 8da61230
Loading
Loading
Loading
Loading
+23 −4
Original line number Diff line number Diff line
@@ -753,6 +753,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");
@@ -789,6 +791,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();
@@ -820,12 +830,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);
                    }
                }
            }
        }
@@ -862,6 +873,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))
@@ -869,6 +883,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;
+2 −0
Original line number Diff line number Diff line
@@ -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
+178 −27
Original line number Diff line number Diff line
@@ -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)
{
@@ -1722,6 +1725,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);
@@ -2349,6 +2358,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;
@@ -2369,16 +2379,16 @@ 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 {
                if (!(out->flags & AUDIO_OUTPUT_FLAG_COMPRESS_OFFLOAD)) {
                    ALOGE("%s: A2DP profile is not ready, return error", __func__);
                    ret = -EAGAIN;
                    goto error_config;
                }
            }
        }
    }
    if (out->devices & AUDIO_DEVICE_OUT_ALL_SCO) {
        if (!adev->bt_sco_on) {
            if (out->devices & AUDIO_DEVICE_OUT_SPEAKER) {
@@ -2428,7 +2438,19 @@ int start_output_stream(struct stream_out *out)
        }
    }

    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);
@@ -2961,6 +2983,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);
@@ -3005,7 +3028,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
@@ -3064,9 +3087,24 @@ static int out_set_parameters(struct audio_stream *stream, const char *kvpairs)
                        adev->adm_on_routing_change(adev->adm_data,
                                                    out->handle);
                }
                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);
                }

            }
        }

@@ -3273,11 +3311,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 */
@@ -3306,24 +3373,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";
@@ -3398,6 +3455,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);
@@ -4279,6 +4351,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)
@@ -4296,6 +4369,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)) {
@@ -4838,6 +4912,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;
@@ -5602,6 +5678,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)
{
+6 −0
Original line number Diff line number Diff line
@@ -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,