1. 程式人生 > >高通輸入輸出裝置選擇

高通輸入輸出裝置選擇

static int adev_open_input_stream(struct audio_hw_device *dev,
                                  audio_io_handle_t handle __unused,
                                  audio_devices_t devices,
                                  struct audio_config *config,
                                  struct audio_stream_in **
stream_in, audio_input_flags_t flags __unused, const char *address __unused, audio_source_t source __unused) { struct audio_device *adev = (struct audio_device *)dev; struct stream_in *in; int ret =
0, buffer_size, frame_size; int channel_count = audio_channel_count_from_in_mask(config->channel_mask); bool is_low_latency = false; *stream_in = NULL; if (check_input_parameters(config->sample_rate, config->format, channel_count) != 0) return -EINVAL; in = (struct stream_in *
)calloc(1, sizeof(struct stream_in)); pthread_mutex_init(&in->lock, (const pthread_mutexattr_t *) NULL); pthread_mutex_init(&in->pre_lock, (const pthread_mutexattr_t *) NULL); in->stream.common.get_sample_rate = in_get_sample_rate; in->stream.common.set_sample_rate = in_set_sample_rate; in->stream.common.get_buffer_size = in_get_buffer_size; in->stream.common.get_channels = in_get_channels; in->stream.common.get_format = in_get_format; in->stream.common.set_format = in_set_format; in->stream.common.standby = in_standby; in->stream.common.dump = in_dump; in->stream.common.set_parameters = in_set_parameters; in->stream.common.get_parameters = in_get_parameters; in->stream.common.add_audio_effect = in_add_audio_effect; in->stream.common.remove_audio_effect = in_remove_audio_effect; in->stream.set_gain = in_set_gain; in->stream.read = in_read; in->stream.get_input_frames_lost = in_get_input_frames_lost; in->device = devices; in->source = AUDIO_SOURCE_DEFAULT; in->dev = adev; in->standby = 1; in->channel_mask = config->channel_mask; in->capture_handle = handle; /* Update config params with the requested sample rate and channels */ in->usecase = USECASE_AUDIO_RECORD; if (config->sample_rate == LOW_LATENCY_CAPTURE_SAMPLE_RATE && (flags & AUDIO_INPUT_FLAG_FAST) != 0) { is_low_latency = true; in->usecase = USECASE_AUDIO_RECORD_LOW_LATENCY; } in->config = pcm_config_audio_capture; in->config.rate = config->sample_rate; in->format = config->format; if (in->device == AUDIO_DEVICE_IN_TELEPHONY_RX) { if (config->sample_rate == 0) config->sample_rate = AFE_PROXY_SAMPLING_RATE; if (config->sample_rate != 48000 && config->sample_rate != 16000 && config->sample_rate != 8000) { config->sample_rate = AFE_PROXY_SAMPLING_RATE; ret = -EINVAL; goto err_open; } if (config->format == AUDIO_FORMAT_DEFAULT) config->format = AUDIO_FORMAT_PCM_16_BIT; if (config->format != AUDIO_FORMAT_PCM_16_BIT) { config->format = AUDIO_FORMAT_PCM_16_BIT; ret = -EINVAL; goto err_open; } in->usecase = USECASE_AUDIO_RECORD_AFE_PROXY; in->config = pcm_config_afe_proxy_record; in->config.channels = channel_count; in->config.rate = config->sample_rate; } else if (channel_count == 6) { if(audio_extn_ssr_get_enabled()) { if(audio_extn_ssr_init(in)) { ret = -EINVAL; goto err_open; } } else { ALOGW("%s: surround sound recording is not supported", __func__); } } else if (audio_extn_compr_cap_enabled() && audio_extn_compr_cap_format_supported(config->format) && (in->dev->mode != AUDIO_MODE_IN_COMMUNICATION)) { audio_extn_compr_cap_init(in); } else { in->config.channels = channel_count; frame_size = audio_stream_in_frame_size(&in->stream); buffer_size = get_input_buffer_size(config->sample_rate, config->format, channel_count, is_low_latency); in->config.period_size = buffer_size / frame_size; } /* This stream could be for sound trigger lab, get sound trigger pcm if present */ audio_extn_sound_trigger_check_and_get_session(in); audio_extn_perf_lock_init(); *stream_in = &in->stream; return ret; err_open: free(in); *stream_in = NULL; return ret; }
static ssize_t in_read(struct audio_stream_in *stream, void *buffer,
                       size_t bytes)
{
    struct stream_in *in = (struct stream_in *)stream;
    struct audio_device *adev = in->dev;
    int ret = -1;
    int snd_scard_state = get_snd_card_state(adev);

    lock_input_stream(in);

    if (in->standby) {
        if (!in->is_st_session) {
            pthread_mutex_lock(&adev->lock);
            if (in->usecase == USECASE_COMPRESS_VOIP_CALL){
                ret = voice_extn_compress_voip_start_input_stream(in);
            }
            else{
                ret = start_input_stream(in);
            }
            pthread_mutex_unlock(&adev->lock);
            if (ret != 0) {
                goto exit;
            }
        }
        in->standby = 0;
    }

    if (in->pcm) {
        if (audio_extn_ssr_get_enabled() &&
                audio_channel_count_from_in_mask(in->channel_mask) == 6)
            ret = audio_extn_ssr_read(stream, buffer, bytes);
        else if (audio_extn_compr_cap_usecase_supported(in->usecase))
            ret = audio_extn_compr_cap_read(in, buffer, bytes);
        else if (in->usecase == USECASE_AUDIO_RECORD_AFE_PROXY)
            ret = pcm_mmap_read(in->pcm, buffer, bytes);
        else
            ret = pcm_read(in->pcm, buffer, bytes);
        if (ret < 0)
            ret = -errno;
    }

    /*
     * Instead of writing zeroes here, we could trust the hardware
     * to always provide zeroes when muted.
     */
    if (ret == 0 && voice_get_mic_mute(adev) && !voice_is_in_call_rec_stream(in))
        memset(buffer, 0, bytes);

exit:
    /* ToDo: There may be a corner case when SSR happens back to back during
       start/stop. Need to post different error to handle that. */
    if (-ENETRESET == ret) {
        set_snd_card_state(adev,SND_CARD_STATE_OFFLINE);
    }
    pthread_mutex_unlock(&in->lock);

    if (ret != 0) {
        if (in->usecase == USECASE_COMPRESS_VOIP_CALL) {
            pthread_mutex_lock(&adev->lock);
            voice_extn_compress_voip_close_input_stream(&in->stream.common);
            pthread_mutex_unlock(&adev->lock);
            in->standby = true;
        }
        memset(buffer, 0, bytes);
        in_standby(&in->stream.common);
        usleep(bytes * 1000000 / audio_stream_in_frame_size(stream) /
                                   in_get_sample_rate(&in->stream.common));
    }
    return bytes;
}
int voice_extn_compress_voip_start_input_stream(struct stream_in *in)
{
    int ret = 0;
    struct audio_device *adev = in->dev;
    int snd_card_status = get_snd_card_state(adev);

    if (SND_CARD_STATE_OFFLINE == snd_card_status) {
        ret = -ENETRESET;
        goto error;
    }

    if (!voip_data.in_stream_count)
        ret = voice_extn_compress_voip_open_input_stream(in);

    adev->active_input = in;
    ret = voip_start_call(adev, &in->config);
    in->pcm = voip_data.pcm_tx;

error:
    return ret;
}
int start_input_stream(struct stream_in *in)
{
    /* 1. Enable output device and stream routing controls */
    int ret = 0;
    struct audio_usecase *uc_info;
    struct audio_device *adev = in->dev;
    int snd_card_status = get_snd_card_state(adev);

    int usecase = platform_update_usecase_from_source(in->source,in->usecase);
    if (get_usecase_from_list(adev, usecase) == NULL)
        in->usecase = usecase;

    if (SND_CARD_STATE_OFFLINE == snd_card_status) {
        ret = -EIO;
        goto error_config;
    }

    /* Check if source matches incall recording usecase criteria */
    ret = voice_check_and_set_incall_rec_usecase(adev, in);

    if (get_usecase_from_list(adev, in->usecase) != NULL) {
        goto error_config;
    }

    in->pcm_device_id = platform_get_pcm_device_id(in->usecase, PCM_CAPTURE);

    adev->active_input = in;
    uc_info = (struct audio_usecase *)calloc(1, sizeof(struct audio_usecase));

    uc_info->id = in->usecase;
    uc_info->type = PCM_CAPTURE;
    uc_info->stream.in = in;
    uc_info->devices = in->device;
    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_perf_lock_acquire();
    select_devices(adev, in->usecase);

    unsigned int flags = PCM_IN;
    unsigned int pcm_open_retry_count = 0;

    if (in->usecase == USECASE_AUDIO_RECORD_AFE_PROXY) {
        flags |= PCM_MMAP | PCM_NOIRQ;
        pcm_open_retry_count = PROXY_OPEN_RETRY_COUNT;
    }

    while (1) {
        in->pcm = pcm_open(adev->snd_card, in->pcm_device_id, flags, &in->config);
        if (in->pcm == NULL || !pcm_is_ready(in->pcm)) {
            if (in->pcm != NULL) {
                pcm_close(in->pcm);
                in->pcm = NULL;
            }
            if (pcm_open_retry_count-- == 0) {
                ret = -EIO;
                goto error_open;
            }
            usleep(PROXY_OPEN_WAIT_TIME * 1000);
            continue;
        }
        break;
    }

    ret = pcm_prepare(in->pcm);
    if (ret < 0) {
        pcm_close(in->pcm);
        in->pcm = NULL;
        goto error_open;
    }

    return ret;
}
int select_devices(struct audio_device *adev, audio_usecase_t uc_id)
{
    snd_device_t out_snd_device = SND_DEVICE_NONE;
    snd_device_t in_snd_device = SND_DEVICE_NONE;
    struct audio_usecase *usecase = NULL;
    struct audio_usecase *vc_usecase = NULL;
    struct audio_usecase *voip_usecase = NULL;
    struct audio_usecase *hfp_usecase = NULL;
    audio_usecase_t hfp_ucid;
    int status = 0;

    usecase = get_usecase_from_list(adev, uc_id);
    if (usecase == NULL) {
        return -EINVAL;
    }
    ALOGD("++%s: start usecase->type %d",__func__, usecase->type);

    if ((usecase->type == VOICE_CALL) ||
        (usecase->type == VOIP_CALL)  ||
        (usecase->type == PCM_HFP_CALL)) {
        out_snd_device = platform_get_output_snd_device(adev->platform,
                                                        usecase->stream.out->devices);
        in_snd_device = platform_get_input_snd_device(adev->platform, usecase->stream.out->devices);
        usecase->devices = usecase->stream.out->devices;
    } else {
        /*
         * If the voice call is active, use the sound devices of voice call usecase
         * so that it would not result any device switch. All the usecases will
         * be switched to new device when select_devices() is called for voice call
         * usecase. This is to avoid switching devices for voice call when
         * check_usecases_codec_backend() is called below.
         */
        if (voice_is_in_call(adev) && adev->mode == AUDIO_MODE_IN_CALL) {
            vc_usecase = get_usecase_from_list(adev,
                                               get_usecase_id_from_usecase_type(adev, VOICE_CALL));
            if ((vc_usecase) && ((vc_usecase->devices & AUDIO_DEVICE_OUT_ALL_CODEC_BACKEND) ||
                (usecase->devices == AUDIO_DEVICE_IN_VOICE_CALL))) {
                in_snd_device = vc_usecase->in_snd_device;
                out_snd_device = vc_usecase->out_snd_device;
            }
        } else if (voice_extn_compress_voip_is_active(adev)) {
            voip_usecase = get_usecase_from_list(adev, USECASE_COMPRESS_VOIP_CALL);
            if ((voip_usecase) && ((voip_usecase->devices & AUDIO_DEVICE_OUT_ALL_CODEC_BACKEND) &&
                (usecase->devices & AUDIO_DEVICE_OUT_ALL_CODEC_BACKEND) &&
                 (voip_usecase->stream.out != adev->primary_output))) {
                    in_snd_device = voip_usecase->in_snd_device;
                    out_snd_device = voip_usecase->out_snd_device;
            }
        } else if (audio_extn_hfp_is_active(adev)) {
            hfp_ucid = audio_extn_hfp_get_usecase();
            hfp_usecase = get_usecase_from_list(adev, hfp_ucid);
            if ((hfp_usecase) && (hfp_usecase->devices & AUDIO_DEVICE_OUT_ALL_CODEC_BACKEND)) {
                   in_snd_device = hfp_usecase->in_snd_device;
                   out_snd_device = hfp_usecase->out_snd_device;
            }
        }

        if (usecase->type == PCM_PLAYBACK) {
            usecase->devices = usecase->stream.out->devices;
            in_snd_device = SND_DEVICE_NONE;
            if (out_snd_device == SND_DEVICE_NONE) {
                out_snd_device = platform_get_output_snd_device(adev->platform,
                                            usecase->stream.out->devices);
                if (usecase->stream.out == adev->primary_output &&
                        adev->active_input &&
                        out_snd_device != usecase->out_snd_device) {
                    select_devices(adev, adev->active_input->usecase);
                }
            }
        } else if (usecase->type == PCM_CAPTURE) {
            usecase->devices = usecase->stream.in->device;
            out_snd_device = SND_DEVICE_NONE;
            if (in_snd_device == SND_DEVICE_NONE) {
                audio_devices_t out_device = AUDIO_DEVICE_NONE;
                if ((adev->active_input->source == AUDIO_SOURCE_VOICE_COMMUNICATION ||
                    (adev->mode == AUDIO_MODE_IN_COMMUNICATION &&
                     adev->active_input->source == AUDIO_SOURCE_MIC)) &&
                     adev->primary_output && !adev->primary_output->standby) {
                    out_device = adev->primary_output->devices;
                    platform_set_echo_reference(adev->platform, false);
                } else if (usecase->id == USECASE_AUDIO_RECORD_AFE_PROXY) {
                    out_device = AUDIO_DEVICE_OUT_TELEPHONY_TX;
                }
                in_snd_device = platform_get_input_snd_device(adev->platform, out_device);
            }
        }
    }

    if (out_snd_device == usecase->out_snd_device &&
        in_snd_device == usecase->in_snd_device) {
        return 0;
    }

    /*
     * Limitation: While in call, to do a device switch we need to disable
     * and enable both RX and TX devices though one of them is same as current
     * device.
     */
    if ((usecase->type == VOICE_CALL) &&
        (usecase->in_snd_device != SND_DEVICE_NONE) &&
        (usecase->out_snd_device != SND_DEVICE_NONE)) {
        status = platform_switch_voice_call_device_pre(adev->platform);
    }

    /* Disable current sound devices */
    if (usecase->out_snd_device != SND_DEVICE_NONE) {
        disable_audio_route(adev, usecase);
        disable_snd_device(adev, usecase->out_snd_device);
    }

    if (usecase->in_snd_device != SND_DEVICE_NONE) {
        disable_audio_route(adev, usecase);
        disable_snd_device(adev, usecase->in_snd_device);
    }

    /* Applicable only on the targets that has external modem.
     * New device information should be sent to modem before enabling
     * the devices to reduce in-call device switch time.
     */
    if ((usecase->type == VOICE_CALL) &&
        (usecase->in_snd_device != SND_DEVICE_NONE) &&
        (usecase->out_snd_device != SND_DEVICE_NONE)) {
        status = platform_switch_voice_call_enable_device_config(adev->platform,
                                                                 out_snd_device,
                                                                 in_snd_device);
    }

    /* Enable new sound devices */
    if (out_snd_device != SND_DEVICE_NONE) {
        if (usecase->devices & AUDIO_DEVICE_OUT_ALL_CODEC_BACKEND)
            check_usecases_codec_backend(adev, usecase, out_snd_device);
        enable_snd_device(adev, out_snd_device);
    }

    if (in_snd_device != SND_DEVICE_NONE) {
        check_and_route_capture_usecases(adev, usecase, in_snd_device);
        enable_snd_device(adev, in_snd_device);
    }

    if (usecase->type == VOICE_CALL || usecase->type == VOIP_CALL) {
        status = platform_switch_voice_call_device_post(adev->platform,
                                                        out_snd_device,
                                                        in_snd_device);
        enable_audio_route_for_voice_usecases(adev, usecase);
    }

    usecase->in_snd_device = in_snd_device;
    usecase->out_snd_device = out_snd_device;

    if (usecase->type == PCM_PLAYBACK) {
        audio_extn_utils_update_stream_app_type_cfg(adev->platform,
                                                &adev->streams_output_cfg_list,
                                                usecase->stream.out->devices,
                                                usecase->stream.out->flags,
                                                usecase->stream.out->format,
                                                usecase->stream.out->sample_rate,
                                                usecase->stream.out->bit_width,
                                                &usecase->stream.out->app_type_cfg);
    }

    enable_audio_route(adev, usecase);

    /* Applicable only on the targets that has external modem.
     * Enable device command should be sent to modem only after
     * enabling voice call mixer controls
     */
    if (usecase->type == VOICE_CALL)
        status = platform_switch_voice_call_usecase_route_post(adev->platform,
                                                               out_snd_device,
                                                               in_snd_device);

    return status;
}
static int adev_open_output_stream(struct audio_hw_device *dev,
                                   audio_io_handle_t handle,
                                   audio_devices_t devices,
                                   audio_output_flags_t flags,
                                   struct audio_config *config,
                                   struct audio_stream_out **stream_out,
                                   const char *address __unused)
{
    struct audio_device *adev = (struct audio_device *)dev;
    struct stream_out *out;
    int ret = 0;
    audio_format_t format;

    *stream_out = NULL;

    if ((flags & AUDIO_OUTPUT_FLAG_COMPRESS_OFFLOAD) &&
             (SND_CARD_STATE_OFFLINE == get_snd_card_state(adev))) {
        ALOGE(" sound card is not active rejecting compress output open request");
        return -EINVAL;
    }

    out = (struct stream_out *)calloc(1, sizeof(struct stream_out));

    if (devices == AUDIO_DEVICE_NONE)
        devices = AUDIO_DEVICE_OUT_SPEAKER;

    out->flags = flags;
    out->devices = devices;
    out->dev = adev;
    format = out->format = config->format;
    out->sample_rate = config->sample_rate;
    out->channel_mask = AUDIO_CHANNEL_OUT_STEREO;
    out->supported_channel_masks[0] = AUDIO_CHANNEL_OUT_STEREO;
    out->handle = handle;
    out->bit_width = CODEC_BACKEND_DEFAULT_BIT_WIDTH;

    /* Init use case and pcm_config */
    if ((out->flags == AUDIO_OUTPUT_FLAG_DIRECT) &&
        (out->devices & AUDIO_DEVICE_OUT_AUX_DIGITAL ||
        out->devices & AUDIO_DEVICE_OUT_PROXY)) {

        pthread_mutex_lock(&adev->lock);
        if (out->devices & AUDIO_DEVICE_OUT_AUX_DIGITAL)
            ret = read_hdmi_channel_masks(out);

        if (out->devices & AUDIO_DEVICE_OUT_PROXY)
            ret = audio_extn_read_afe_proxy_channel_masks(out);

        if (config->sample_rate == 0)
            config->sample_rate = DEFAULT_OUTPUT_SAMPLING_RATE;
        if (config->channel_mask == 0)
            config->channel_mask = AUDIO_CHANNEL_OUT_5POINT1;

        out->channel_mask = config->channel_mask;
        out->sample_rate = config->sample_rate;
        out->usecase = USECASE_AUDIO_PLAYBACK_MULTI_CH;
        out->config = pcm_config_hdmi_multi;
        out->config.rate = config->sample_rate;
        out->config.channels = audio_channel_count_from_out_mask(out->channel_mask);
        out->config.period_size = HDMI_MULTI_PERIOD_BYTES / (out->config.channels * 2);
    } else if ((out->dev->mode == AUDIO_MODE_IN_COMMUNICATION) &&
               (out->flags == (AUDIO_OUTPUT_FLAG_DIRECT | AUDIO_OUTPUT_FLAG_VOIP_RX)) &&
               (voice_extn_compress_voip_is_config_supported(config))) {
        ret = voice_extn_compress_voip_open_output_stream(out);
    } else if (out->flags & AUDIO_OUTPUT_FLAG_COMPRESS_OFFLOAD) {
        if (config->offload_info.version != AUDIO_INFO_INITIALIZER.version ||
            config->offload_info.size != AUDIO_INFO_INITIALIZER.size) {
            ALOGE("%s: Unsupported Offload information", __func__);
            ret = -EINVAL;
            goto error_open;
        }
        if (!is_supported_format(config->offload_info.format) &&
                !audio_extn_is_dolby_format(config->offload_info.format)) {
            ALOGE("%s: Unsupported audio format", __func__);
            ret = -EINVAL;
            goto error_open;
        }

        out->compr_config.codec = (struct snd_codec *)
                                    calloc(1, sizeof(struct snd_codec));

        if (!out->compr_config.codec) {
            ret = -ENOMEM;
            goto error_open;
        }

        out->usecase = get_offload_usecase(adev);
        if (config->offload_info.channel_mask)
            out->channel_mask = config->offload_info.channel_mask;
        else if (config->channel_mask) {
            out->channel_mask = config->channel_mask;
            config->offload_info.channel_mask = config->channel_mask;
        }
        format = out->format = config->offload_info.format;
        out->sample_rate = config->offload_info.sample_rate;

        out->stream.set_callback = out_set_callback;
        out->stream.pause = out_pause;
        out->stream.resume = out_resume;
        out->stream.drain = out_drain;
        out->stream.flush = out_flush;
        out->bit_width = CODEC_BACKEND_DEFAULT_BIT_WIDTH;

        if (audio_extn_is_dolby_format(config->offload_info.format))
            out->compr_config.codec->id =
                audio_extn_dolby_get_snd_codec_id(adev, out,
                                                  config->offload_info.format);
        else
            out->compr_config.codec->id =
                get_snd_codec_id(config->offload_info.format);
        if (audio_is_offload_pcm(config->offload_info.format)) {
            out->compr_config.fragment_size =
                       platform_get_pcm_offload_buffer_size(&config->offload_info);
        } else {
            out->compr_config.fragment_size =
                       platform_get_compress_offload_buffer_size(&config->offload_info);
        }
        out->compr_config.fragments = COMPRESS_OFFLOAD_NUM_FRAGMENTS;
        out->compr_config.codec->sample_rate =
                    config->offload_info.sample_rate;
        out->compr_config.codec->bit_rate =
                    config->offload_info.bit_rate;
        out->compr_config.codec->ch_in =
                audio_channel_count_from_out_mask(config->channel_mask);
        out->compr_config.codec->ch_out = out->compr_config.codec->ch_in;
        out->bit_width = PCM_OUTPUT_BIT_WIDTH;

        if (config->offload_info.format == AUDIO_FORMAT_AAC)
            out->compr_config.codec->format = SND_AUDIOSTREAMFORMAT_RAW;
        if (config->offload_info.format == AUDIO_FORMAT_PCM_16_BIT_OFFLOAD)
            out->compr_config.codec->format = SNDRV_PCM_FORMAT_S16_LE;
        if(config->offload_info.format == AUDIO_FORMAT_PCM_24_BIT_OFFLOAD)
            out->compr_config.codec->format = SNDRV_PCM_FORMAT_S24_LE;

        if (out->bit_width == 24) {
            out->compr_config.codec->format = SNDRV_PCM_FORMAT_S24_LE;
        }

        if (config->offload_info.format == AUDIO_FORMAT_FLAC)
            out->compr_config.codec->options.flac_dec.sample_size = PCM_OUTPUT_BIT_WIDTH;

        if (flags & AUDIO_OUTPUT_FLAG_NON_BLOCKING)
            out->non_blocking = 1;

        out->send_new_metadata = 1;
        out->offload_state = OFFLOAD_STATE_IDLE;
        out->playback_started = 0;

        create_offload_callback_thread(out);
        //Decide if we need to use gapless mode by default
        check_and_set_gapless_mode(adev);

    } else if (out->flags & AUDIO_OUTPUT_FLAG_INCALL_MUSIC) {
        ret = voice_check_and_set_incall_music_usecase(adev, out);
        if (ret != 0) {
            ALOGE("%s: Incall music delivery usecase cannot be set error:%d",
                  __func__, ret);
            goto error_open;
        }
    } else  if (out->devices == AUDIO_DEVICE_OUT_TELEPHONY_TX) {
        if (config->sample_rate == 0)
            config->sample_rate = AFE_PROXY_SAMPLING_RATE;
        if (config->sample_rate != 48000 && config->sample_rate != 16000 &&
                config->sample_rate != 8000) {
            config->sample_rate = AFE_PROXY_SAMPLING_RATE;
            ret = -EINVAL;
            goto error_open;
        }
        out->sample_rate = config->sample_rate;
        out->config.rate = config->sample_rate;
        if (config->format == AUDIO_FORMAT_DEFAULT)
            config->format = AUDIO_FORMAT_PCM_16_BIT;
        if (config->format != AUDIO_FORMAT_PCM_16_BIT) {
            config->format = AUDIO_FORMAT_PCM_16_BIT;
            ret = -EINVAL;
            goto error_open;
        }
        out->format = config->format;
        out->usecase = USECASE_AUDIO_PLAYBACK_AFE_PROXY;
        out->config = pcm_config_afe_proxy_playback;
        adev->voice_tx_output = out;
    } else if (out->flags & AUDIO_OUTPUT_FLAG_FAST) {
        format = AUDIO_FORMAT_PCM_16_BIT;
        out->usecase = USECASE_AUDIO_PLAYBACK_LOW_LATENCY;
        out->config = pcm_config_low_latency;
        out->sample_rate = out->config.rate;
    } else {
        /* primary path is the default path selected if no other outputs are available/suitable */
        format = AUDIO_FORMAT_PCM_16_BIT;
        out->usecase = USECASE_AUDIO_PLAYBACK_PRIMARY;
        out->config = pcm_config_deep_buffer;
        out->sample_rate = out->config.rate;
    }

    audio_extn_utils_update_stream_app_type_cfg(adev->platform,
                                                &adev->streams_output_cfg_list,
                                                devices, flags, format, out->sample_rate,
                                                out->bit_width, &out->app_type_cfg);
    if ((out->usecase == USECASE_AUDIO_PLAYBACK_PRIMARY) ||
        (flags & AUDIO_OUTPUT_FLAG_PRIMARY)) {
        /* Ensure the default output is not selected twice */
        if(adev->primary_output == NULL)
            adev->primary_output = out;
        else {
            ALOGE("%s: Primary output is already opened", __func__);
            ret = -EEXIST;
            goto error_open;
        }
    }

    /* Check if this usecase is already existing */
    pthread_mutex_lock(&adev->lock);
    if ((get_usecase_from_list(adev, out->usecase) != NULL) &&
        (out->usecase != USECASE_COMPRESS_VOIP_CALL)) {
        ALOGE("%s: Usecase (%d) is already present", __func__, out->usecase);
        pthread_mutex_unlock(&adev->lock);
        ret = -EEXIST;
        goto error_open;
    }
    pthread_mutex_unlock(&adev->lock);

    out->stream.common.get_sample_rate = out_get_sample_rate;
    out->stream.common.set_sample_rate = out_set_sample_rate;
    out->stream.common.get_buffer_size = out_get_buffer_size;
    out->stream.common.get_channels = out_get_channels;
    out->stream.common.get_format = out_get_format;
    out->stream.common.set_format = out_set_format;
    out->stream.common.standby = out_standby;
    out->stream.common.dump = out_dump;
    out->stream.common.set_parameters = out_set_parameters;
    out->stream.common.get_parameters = out_get_parameters;
    out->stream.common.add_audio_effect = out_add_audio_effect;
    out->stream.common.remove_audio_effect = out_remove_audio_effect;
    out->stream.get_latency = out_get_latency;
    out->stream.set_volume = out_set_volume;
    out->stream.write = out_write;
    out->stream.get_render_position = out_get_render_position;
    out->stream.get_next_write_timestamp = out_get_next_write_timestamp;
    out->stream.get_presentation_position = out_get_presentation_position;

    out->standby = 1;
    /* out->muted = false; by calloc() */
    /* out->written = 0; by calloc() */

    pthread_mutex_init(&out->lock, (const pthread_mutexattr_t *) NULL);
    pthread_mutex_init(&out->pre_lock, (const pthread_mutexattr_t *) NULL);
    pthread_cond_init(&out->cond, (const pthread_condattr_t *) NULL);

    config->format = out->stream.common.get_format(&out->stream.common);
    config->channel_mask = out->stream.common.get_channels(&out->stream.common);
    config->sample_rate = out->stream.common.get_sample_rate(&out->stream.common);

    *stream_out = &out->stream;
    ALOGV("%s: exit", __func__);
    return 0;

error_open:
    free(out);
    *stream_out = NULL;
    return ret;
}
static ssize_t out_write(struct audio_stream_out *stream, const void *buffer,
                         size_t bytes)
{
    struct stream_out *out = (struct stream_out *)stream;
    struct audio_device *adev = out->dev;
    int snd_scard_state = get_snd_card_state(adev);
    ssize_t ret = 0;

    lock_output_stream(out);

    if (SND_CARD_STATE_OFFLINE == snd_scard_state) {
        // increase written size during SSR to avoid mismatch
        // with the written frames count in AF
        if (!is_offload_usecase(out->usecase))
            out->written += bytes / (out->config.channels * sizeof(short));

        if (out->pcm) {
            ALOGD(" %s: sound card is not active/SSR state", __func__);
            ret= -EIO;
            goto exit;
        } else if (out->usecase == USECASE_AUDIO_PLAYBACK_OFFLOAD) {
            //during SSR for compress usecase we should return er