blob: 7235640a1e02f4a41b075d5e7bdfa372cbca7097 [file] [log] [blame]
/*
* Copyright (c) 2019-2021, The Linux Foundation. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following
* disclaimer in the documentation and/or other materials provided
* with the distribution.
* * Neither the name of The Linux Foundation nor the names of its
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
* BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
* OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
* IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* Changes from Qualcomm Innovation Center are provided under the following license:
*
* Copyright (c) 2022 Qualcomm Innovation Center, Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted (subject to the limitations in the
* disclaimer below) provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following
* disclaimer in the documentation and/or other materials provided
* with the distribution.
*
* * Neither the name of Qualcomm Innovation Center, Inc. nor the names of its
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* NO EXPRESS OR IMPLIED LICENSES TO ANY PARTY'S PATENT RIGHTS ARE
* GRANTED BY THIS LICENSE. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT
* HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
* GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
* IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
* IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* Changes from Qualcomm Innovation Center are provided under the following license:
*
* Copyright (c) 2023 Qualcomm Innovation Center, Inc. All rights reserved.
* SPDX-License-Identifier: BSD-3-Clause-Clear
*/
#define LOG_TAG "PAL: Session"
#include "Session.h"
#include "Stream.h"
#include "ResourceManager.h"
#include "SessionGsl.h"
#include "SessionAlsaPcm.h"
#include "SessionAlsaCompress.h"
#include "SessionAgm.h"
#include "SessionAlsaUtils.h"
#include "SessionAlsaVoice.h"
#include <sstream>
struct pcm *Session::pcmEcTx = NULL;
std::vector<int> Session::pcmDevEcTxIds = {0};
int Session::extECRefCnt = 0;
std::mutex Session::extECMutex;
Session::Session()
{
isMixerEventCbRegd = false;
isPauseRegistrationDone = false;
}
Session::~Session()
{
}
void Session::setPmQosMixerCtl(pmQosVote vote)
{
struct mixer *hwMixer;
struct mixer_ctl *ctl;
if (0 == rm->getHwAudioMixer(&hwMixer)) {
ctl = mixer_get_ctl_by_name(hwMixer, "PM_QOS Vote");
if (!ctl) {
PAL_ERR(LOG_TAG, "Invalid mixer control: %s\n",
"PM_QOS Vote");
} else {
if (vote == PM_QOS_VOTE_DISABLE) {
mixer_ctl_set_enum_by_string(ctl, "Disable");
PAL_DBG(LOG_TAG,"mixer control disabled for PM_QOS Vote \n");
} else if (vote == PM_QOS_VOTE_ENABLE) {
mixer_ctl_set_enum_by_string(ctl, "Enable");
PAL_DBG(LOG_TAG,"mixer control enabled for PM_QOS Vote \n");
}
}
}
else
PAL_ERR(LOG_TAG,"could not get hwMixer, not setting mixer control for PM_QOS \n");
}
Session* Session::makeSession(const std::shared_ptr<ResourceManager>& rm, const struct pal_stream_attributes *sAttr)
{
if (!rm || !sAttr) {
PAL_ERR(LOG_TAG, "Invalid parameters passed");
return nullptr;
}
Session* s = (Session*) nullptr;
switch (sAttr->type) {
//create compressed if the stream type is compressed
case PAL_STREAM_COMPRESSED:
s = new SessionAlsaCompress(rm);
break;
case PAL_STREAM_VOICE_CALL:
s = new SessionAlsaVoice(rm);
break;
case PAL_STREAM_NON_TUNNEL:
s = new SessionAgm(rm);
break;
default:
s = new SessionAlsaPcm(rm);
break;
}
return s;
}
Session* Session::makeACDBSession(const std::shared_ptr<ResourceManager>& rm,
const struct pal_stream_attributes *sAttr)
{
if (!rm || !sAttr) {
PAL_ERR(LOG_TAG,"Invalid parameters passed");
return nullptr;
}
Session* s = (Session*) nullptr;
s = new SessionAgm(rm);
return s;
}
void Session::getSamplerateChannelBitwidthTags(struct pal_media_config *config,
uint32_t &mfc_sr_tag, uint32_t &ch_tag, uint32_t &bitwidth_tag)
{
switch (config->sample_rate) {
case SAMPLINGRATE_8K :
mfc_sr_tag = MFC_SR_8K;
break;
case SAMPLINGRATE_16K :
mfc_sr_tag = MFC_SR_16K;
break;
case SAMPLINGRATE_32K :
mfc_sr_tag = MFC_SR_32K;
break;
case SAMPLINGRATE_44K :
mfc_sr_tag = MFC_SR_44K;
break;
case SAMPLINGRATE_48K :
mfc_sr_tag = MFC_SR_48K;
break;
case SAMPLINGRATE_96K :
mfc_sr_tag = MFC_SR_96K;
break;
case SAMPLINGRATE_192K :
mfc_sr_tag = MFC_SR_192K;
break;
case SAMPLINGRATE_384K :
mfc_sr_tag = MFC_SR_384K;
break;
default:
mfc_sr_tag = MFC_SR_48K;
break;
}
switch (config->ch_info.channels) {
case CHANNELS_1:
ch_tag = CHS_1;
break;
case CHANNELS_2:
ch_tag = CHS_2;
break;
case CHANNELS_3:
ch_tag = CHS_3;
break;
case CHANNELS_4:
ch_tag = CHS_4;
break;
case CHANNELS_5:
ch_tag = CHS_5;
break;
case CHANNELS_6:
ch_tag = CHS_6;
break;
case CHANNELS_7:
ch_tag = CHS_7;
break;
case CHANNELS_8:
ch_tag = CHS_8;
break;
default:
ch_tag = CHS_1;
break;
}
switch (config->bit_width) {
case BITWIDTH_16:
bitwidth_tag = BW_16;
break;
case BITWIDTH_24:
bitwidth_tag = BW_24;
break;
case BITWIDTH_32:
bitwidth_tag = BW_32;
break;
default:
bitwidth_tag = BW_16;
break;
}
}
uint32_t Session::getModuleInfo(const char *control, uint32_t tagId, uint32_t *miid, struct mixer_ctl **ctl, int *device)
{
int status = 0;
int dev = 0;
struct mixer_ctl *mixer_ctl = NULL;
if (!rxAifBackEnds.empty()) { /** search in RX GKV */
mixer_ctl = getFEMixerCtl(control, &dev, PAL_AUDIO_OUTPUT);
if (!mixer_ctl) {
PAL_ERR(LOG_TAG, "Invalid mixer control\n");
status = -ENOENT;
goto exit;
}
status = SessionAlsaUtils::getModuleInstanceId(mixer, dev, rxAifBackEnds[0].second.data(), tagId, miid);
if (status) /** if not found, reset miid to 0 again */
*miid = 0;
}
if (!txAifBackEnds.empty() && !(*miid)) { /** search in TX GKV */
mixer_ctl = getFEMixerCtl(control, &dev, PAL_AUDIO_INPUT);
if (!mixer_ctl) {
PAL_ERR(LOG_TAG, "Invalid mixer control\n");
status = -ENOENT;
goto exit;
}
status = SessionAlsaUtils::getModuleInstanceId(mixer, dev, txAifBackEnds[0].second.data(), tagId, miid);
if (status)
*miid = 0;
}
if (*miid == 0) {
PAL_ERR(LOG_TAG, "failed to look for module with tagID 0x%x", tagId);
status = -EINVAL;
goto exit;
}
if (device)
*device = dev;
if (ctl)
*ctl = mixer_ctl;
PAL_DBG(LOG_TAG, "got miid = 0x%04x, device = %d", *miid, dev);
exit:
if (status) {
if (device)
*device = 0;
if (ctl)
*ctl = NULL;
*miid = 0;
PAL_ERR(LOG_TAG, "Exit. status %d", status);
}
return status;
}
int Session::getEffectParameters(Stream *s __unused, effect_pal_payload_t *effectPayload)
{
int status = 0;
uint8_t *ptr = NULL;
uint8_t *payloadData = NULL;
size_t payloadSize = 0;
uint32_t miid = 0;
const char *control = "getParam";
struct mixer_ctl *ctl = NULL;
pal_effect_custom_payload_t *effectCustomPayload = nullptr;
PayloadBuilder builder;
PAL_DBG(LOG_TAG, "Enter.");
status = getModuleInfo(control, effectPayload->tag, &miid, &ctl, NULL);
if (status || !miid) {
PAL_ERR(LOG_TAG, "failed to look for module with tagID 0x%x, status = %d",
effectPayload->tag, status);
status = -EINVAL;
goto exit;
}
effectCustomPayload = (pal_effect_custom_payload_t *)(effectPayload->payload);
if (effectPayload->payloadSize < sizeof(pal_effect_custom_payload_t)) {
status = -EINVAL;
PAL_ERR(LOG_TAG, "memory for retrieved data is too small");
goto exit;
}
builder.payloadQuery(&payloadData, &payloadSize,
miid, effectCustomPayload->paramId,
effectPayload->payloadSize - sizeof(uint32_t));
status = mixer_ctl_set_array(ctl, payloadData, payloadSize);
if (0 != status) {
PAL_ERR(LOG_TAG, "Set custom config failed, status = %d", status);
goto exit;
}
status = mixer_ctl_get_array(ctl, payloadData, payloadSize);
if (0 != status) {
PAL_ERR(LOG_TAG, "Get custom config failed, status = %d", status);
goto exit;
}
ptr = (uint8_t *)payloadData + sizeof(struct apm_module_param_data_t);
ar_mem_cpy(effectCustomPayload->data, effectPayload->payloadSize,
ptr, effectPayload->payloadSize);
exit:
ctl = NULL;
if (payloadData)
free(payloadData);
PAL_ERR(LOG_TAG, "Exit. status %d", status);
return status;
}
int Session::setEffectParametersTKV(Stream *s __unused, effect_pal_payload_t *effectPayload)
{
int status = 0;
int device = 0;
uint32_t tag;
uint32_t nTkvs;
uint32_t tagConfigSize;
struct mixer_ctl *ctl;
pal_key_vector_t *palKVPair;
struct agm_tag_config* tagConfig = NULL;
std::vector <std::pair<int, int>> tkv;
const char *control = "setParamTag";
PAL_DBG(LOG_TAG, "Enter.");
palKVPair = (pal_key_vector_t *)effectPayload->payload;
nTkvs = palKVPair->num_tkvs;
tkv.clear();
for (int i = 0; i < nTkvs; i++) {
tkv.push_back(std::make_pair(palKVPair->kvp[i].key, palKVPair->kvp[i].value));
}
if (tkv.size() == 0) {
status = -EINVAL;
goto exit;
}
tagConfigSize = sizeof(struct agm_tag_config) + (tkv.size() * sizeof(agm_key_value));
tagConfig = (struct agm_tag_config *) malloc(tagConfigSize);
if(!tagConfig) {
status = -ENOMEM;
goto exit;
}
tag = effectPayload->tag;
status = SessionAlsaUtils::getTagMetadata(tag, tkv, tagConfig);
if (0 != status) {
goto exit;
}
/* Prepare mixer control */
ctl = getFEMixerCtl(control, &device, PAL_AUDIO_OUTPUT);
if (!ctl) {
PAL_ERR(LOG_TAG, "Invalid mixer control\n");
status = -ENOENT;
goto exit;
}
status = mixer_ctl_set_array(ctl, tagConfig, tagConfigSize);
if (status != 0) {
PAL_DBG(LOG_TAG, "Unable to set TKV in Rx path, trying in Tx\n");
/* Rx set failed, Try on Tx path we well */
ctl = getFEMixerCtl(control, &device, PAL_AUDIO_INPUT);
if (!ctl) {
PAL_ERR(LOG_TAG, "Invalid mixer control\n");
status = -ENOENT;
goto exit;
}
status = mixer_ctl_set_array(ctl, tagConfig, tagConfigSize);
if (status != 0) {
PAL_ERR(LOG_TAG, "failed to set the param %d", status);
goto exit;
}
}
exit:
ctl = NULL;
if (tagConfig) {
free(tagConfig);
tagConfig = NULL;
}
PAL_INFO(LOG_TAG, "mixer set tkv status = %d\n", status);
return status;
}
int Session::setEffectParametersNonTKV(Stream *s __unused, effect_pal_payload_t *effectPayload)
{
int status = 0;
int device = 0;
PayloadBuilder builder;
uint32_t miid = 0;
const char *control = "setParam";
size_t payloadSize = 0;
uint8_t *payloadData = NULL;
pal_effect_custom_payload_t *effectCustomPayload = nullptr;
PAL_DBG(LOG_TAG, "Enter.");
if (!effectPayload) {
PAL_ERR(LOG_TAG, "Invalid effectPayload address.\n");
return -EINVAL;
}
/* This is set param call, find out miid first */
status = getModuleInfo(control, effectPayload->tag, &miid, NULL, &device);
if (status || !miid) {
PAL_ERR(LOG_TAG, "failed to look for module with tagID 0x%x, status = %d",
effectPayload->tag, status);
return -EINVAL;
}
/* Now we got the miid, build set param payload */
effectCustomPayload = (pal_effect_custom_payload_t *)effectPayload->payload;
status = builder.payloadCustomParam(&payloadData, &payloadSize,
effectCustomPayload->data,
effectPayload->payloadSize - sizeof(uint32_t),
miid, effectCustomPayload->paramId);
if (status != 0) {
PAL_ERR(LOG_TAG, "payloadCustomParam failed. status = %d",
status);
goto exit;
}
/* set param through set mixer param */
status = SessionAlsaUtils::setMixerParameter(mixer,
device,
payloadData,
payloadSize);
PAL_INFO(LOG_TAG, "mixer set param status = %d\n", status);
exit:
if (payloadData) {
free(payloadData);
payloadData = NULL;
}
if (status && effectCustomPayload) {
PAL_ERR(LOG_TAG, "setEffectParameters for param_id %d failed, status = %d",
effectCustomPayload->paramId, status);
}
return status;
}
int Session::setEffectParameters(Stream *s, effect_pal_payload_t *effectPayload)
{
int status = 0;
PAL_DBG(LOG_TAG, "Enter.");
/* Identify whether this is tkv or set param call */
if (effectPayload->isTKV) {
/* This is tkv set call */
status = setEffectParametersTKV(s, effectPayload);
if (0 != status) {
PAL_ERR(LOG_TAG, "Get setEffectParameters with TKV payload failed"
", status = %d", status);
goto exit;
}
} else {
status = setEffectParametersNonTKV(s, effectPayload);
if (0 != status) {
PAL_ERR(LOG_TAG, "Get setEffectParameters with non TKV payload failed"
", status = %d", status);
goto exit;
}
}
exit:
if (status)
PAL_ERR(LOG_TAG, "Exit. status %d", status);
return status;
}
int Session::rwACDBParameters(void *payload, uint32_t sampleRate,
bool isParamWrite)
{
int status = 0;
uint8_t *payloadData = NULL;
size_t payloadSize = 0;
uint32_t miid = 0;
char const *control = "setParamTagACDB";
struct mixer_ctl *ctl = NULL;
pal_effect_custom_payload_t *effectCustomPayload = nullptr;
PayloadBuilder builder;
pal_param_payload *paramPayload = nullptr;
agm_acdb_param *effectACDBPayload = nullptr;
paramPayload = (pal_param_payload *)payload;
if (!paramPayload)
return -EINVAL;
effectACDBPayload = (agm_acdb_param *)(paramPayload->payload);
if (!effectACDBPayload)
return -EINVAL;
PAL_DBG(LOG_TAG, "Enter.");
status = getModuleInfo(control, effectACDBPayload->tag, &miid, &ctl, NULL);
if (status || !miid) {
PAL_ERR(LOG_TAG, "failed to look for module with tagID 0x%x, status = %d",
effectACDBPayload->tag, status);
status = -EINVAL;
goto exit;
}
effectCustomPayload =
(pal_effect_custom_payload_t *)(effectACDBPayload->blob);
status = builder.payloadACDBParam(&payloadData, &payloadSize,
(uint8_t *)effectACDBPayload,
miid, sampleRate);
if (!payloadData) {
PAL_ERR(LOG_TAG, "failed to create payload data.");
goto exit;
}
if (isParamWrite) {
status = mixer_ctl_set_array(ctl, payloadData, payloadSize);
if (0 != status) {
PAL_ERR(LOG_TAG, "Set custom config failed, status = %d", status);
goto exit;
}
}
exit:
ctl = NULL;
free(payloadData);
PAL_ERR(LOG_TAG, "Exit. status %d", status);
return status;
}
int Session::rwACDBParamTunnel(void *payload, pal_device_id_t palDeviceId,
pal_stream_type_t palStreamType, uint32_t sampleRate,
uint32_t instanceId, bool isParamWrite, Stream * s)
{
int status = -EINVAL;
struct pal_stream_attributes sAttr;
PAL_DBG(LOG_TAG, "Enter");
status = s->getStreamAttributes(&sAttr);
streamHandle = s;
if (0 != status) {
PAL_ERR(LOG_TAG,"getStreamAttributes Failed \n");
goto exit;
}
PAL_INFO(LOG_TAG, "PAL device id=0x%x", palDeviceId);
status = SessionAlsaUtils::rwACDBTunnel(s, rm, palDeviceId, payload, isParamWrite, instanceId);
if (status) {
PAL_ERR(LOG_TAG, "session alsa open failed with %d", status);
}
exit:
PAL_DBG(LOG_TAG, "Exit status: %d", status);
return status;
}
int Session::updateCustomPayload(void *payload, size_t size)
{
if (!customPayloadSize || !customPayload) {
customPayload = calloc(1, size);
} else {
customPayload = realloc(customPayload, customPayloadSize + size);
}
if (!customPayload) {
PAL_ERR(LOG_TAG, "failed to allocate memory for custom payload");
return -ENOMEM;
}
memcpy((uint8_t *)customPayload + customPayloadSize, payload, size);
customPayloadSize += size;
PAL_INFO(LOG_TAG, "customPayloadSize = %zu", customPayloadSize);
return 0;
}
int Session::getCustomPayload(uint8_t **payload, size_t *payloadSize)
{
if (customPayloadSize) {
*payload = (uint8_t *)customPayload;
*payloadSize = customPayloadSize;
}
return 0;
}
int Session::freeCustomPayload(uint8_t **payload, size_t *payloadSize)
{
if (*payload) {
free(*payload);
*payload = NULL;
*payloadSize = 0;
}
return 0;
}
int Session::freeCustomPayload()
{
if (customPayload) {
free(customPayload);
customPayload = NULL;
customPayloadSize = 0;
}
return 0;
}
int Session::pause(Stream * s __unused)
{
return 0;
}
int Session::resume(Stream * s __unused)
{
return 0;
}
int Session::handleDeviceRotation(Stream *s, pal_speaker_rotation_type rotation_type,
int device, struct mixer *mixer, PayloadBuilder* builder,
std::vector<std::pair<int32_t, std::string>> rxAifBackEnds)
{
int status = 0;
struct pal_stream_attributes sAttr;
struct pal_device dAttr;
uint32_t miid = 0;
uint8_t* alsaParamData = NULL;
size_t alsaPayloadSize = 0;
int mfc_tag = TAG_MFC_SPEAKER_SWAP;
std::vector<std::shared_ptr<Device>> associatedDevices;
status = s->getStreamAttributes(&sAttr);
if (status != 0) {
PAL_ERR(LOG_TAG, "stream get attributes failed");
return status;
}
if (PAL_AUDIO_OUTPUT== sAttr.direction) {
status = s->getAssociatedDevices(associatedDevices);
if (0 != status) {
PAL_ERR(LOG_TAG, "getAssociatedDevices Failed\n");
return status;
}
for (int i = 0; i < associatedDevices.size(); i++) {
status = associatedDevices[i]->getDeviceAttributes(&dAttr);
if (0 != status) {
PAL_ERR(LOG_TAG, "get Device Attributes Failed\n");
return status;
}
if ((PAL_DEVICE_OUT_SPEAKER == dAttr.id) &&
(2 == dAttr.config.ch_info.channels) &&
(strcmp(dAttr.custom_config.custom_key, "mspp") != 0)) {
/* Get DevicePP MFC MIID and configure to match to device config */
/* This has to be done after sending all mixer controls and
* before connect
*/
status =
SessionAlsaUtils::getModuleInstanceId(mixer,
device,
rxAifBackEnds[i].second.data(),
mfc_tag, &miid);
if (status != 0) {
PAL_ERR(LOG_TAG, "getModuleInstanceId failed");
return status;
}
PAL_DBG(LOG_TAG, "miid : %x id = %d, data %s, dev id = %d\n", miid,
device, rxAifBackEnds[i].second.data(), dAttr.id);
if (rm->activeGroupDevConfig) {
if (rm->activeGroupDevConfig->devpp_mfc_cfg.channels)
dAttr.config.ch_info.channels =rm->activeGroupDevConfig->devpp_mfc_cfg.channels;
}
builder->payloadMFCMixerCoeff((uint8_t **)&alsaParamData,
&alsaPayloadSize, miid,
dAttr.config.ch_info.channels,
rotation_type);
if (alsaPayloadSize) {
status = updateCustomPayload(alsaParamData, alsaPayloadSize);
freeCustomPayload(&alsaParamData, &alsaPayloadSize);
if (0 != status) {
PAL_ERR(LOG_TAG, "updateCustomPayload Failed\n");
return status;
}
}
status = SessionAlsaUtils::setMixerParameter(mixer,
device,
customPayload,
customPayloadSize);
freeCustomPayload();
if (status != 0) {
PAL_ERR(LOG_TAG, "setMixerParameter failed");
return status;
}
}
}
}
return status;
}
/* This set slot mask tag for device with virtual port enabled */
int Session::setSlotMask(const std::shared_ptr<ResourceManager>& rm, struct pal_stream_attributes &sAttr,
struct pal_device &dAttr, const std::vector<int> &pcmDevIds)
{
int status = 0;
std::vector <std::pair<int, int>> tkv;
struct agm_tag_config* tagConfig = NULL;
const char *setParamTagControl = " setParamTag";
const char *streamPcm = "PCM";
const char *streamComp = "COMPRESS";
const char *streamVoice = "VOICEMMODE";
const char *feCtl = " control";
struct mixer_ctl *ctl;
std::ostringstream tagCntrlName;
std::ostringstream feName;
std::string backendname;
int tkv_size = 0;
uint32_t slot_mask = 0;
if (rm->activeGroupDevConfig) {
tkv.push_back(std::make_pair(TAG_KEY_SLOT_MASK, rm->activeGroupDevConfig->grp_dev_hwep_cfg.slot_mask));
} else if (rm->isDeviceMuxConfigEnabled) {
slot_mask = slotMaskLUT.at(dAttr.config.ch_info.channels) |
slotMaskBwLUT.at(dAttr.config.bit_width);
tkv.push_back(std::make_pair(TAG_KEY_SLOT_MASK, slot_mask));
}
tagConfig = (struct agm_tag_config*)malloc(sizeof(struct agm_tag_config) +
(tkv.size() * sizeof(agm_key_value)));
if (!tagConfig) {
status = -EINVAL;
goto exit;
}
status = SessionAlsaUtils::getTagMetadata(TAG_DEVICE_MUX, tkv, tagConfig);
if (0 != status) {
goto exit;
}
if (PAL_STREAM_COMPRESSED == sAttr.type) {
tagCntrlName<<streamComp<<pcmDevIds.at(0)<<setParamTagControl;
feName<<streamComp<<pcmDevIds.at(0)<<feCtl;
} else if (PAL_STREAM_VOICE_CALL == sAttr.type) {
if (sAttr.info.voice_call_info.VSID == VOICEMMODE1 ||
sAttr.info.voice_call_info.VSID == VOICELBMMODE1){
tagCntrlName<<streamVoice<<1<<"p"<<setParamTagControl;
feName<<streamVoice<<1<<"p"<<feCtl;
} else {
tagCntrlName<<streamVoice<<2<<"p"<<setParamTagControl;
feName<<streamVoice<<2<<"p"<<feCtl;
}
} else {
tagCntrlName<<streamPcm<<pcmDevIds.at(0)<<setParamTagControl;
feName << streamPcm<<pcmDevIds.at(0)<<feCtl;
}
// set FE ctl to BE first in case this is called from connectionSessionDevice
rm->getBackendName(dAttr.id, backendname);
ctl = mixer_get_ctl_by_name(mixer, feName.str().data());
if (!ctl) {
PAL_ERR(LOG_TAG, "Invalid mixer control: %s\n", feName.str().data());
status = -EINVAL;
goto exit;
}
mixer_ctl_set_enum_by_string(ctl, backendname.c_str());
ctl = NULL;
// set tag data
ctl = mixer_get_ctl_by_name(mixer, tagCntrlName.str().data());
if (!ctl) {
PAL_ERR(LOG_TAG, "Invalid mixer control: %s\n", tagCntrlName.str().data());
status = -EINVAL;
goto exit;
}
tkv_size = tkv.size()*sizeof(struct agm_key_value);
status = mixer_ctl_set_array(ctl, tagConfig, sizeof(struct agm_tag_config) + tkv_size);
if (status != 0) {
PAL_ERR(LOG_TAG, "failed to set the tag calibration %d", status);
}
exit:
if (tagConfig)
free(tagConfig);
return status;
}
/* This is to set devicePP MFC(if exists) and PSPD MFC */
int Session::configureMFC(const std::shared_ptr<ResourceManager>& rm, struct pal_stream_attributes &sAttr,
struct pal_device &dAttr, const std::vector<int> &pcmDevIds, const char* intf)
{
int status = 0;
std::shared_ptr<Device> dev = nullptr;
uint8_t* payload = NULL;
size_t payloadSize = 0;
struct pal_media_config codecConfig;
struct sessionToPayloadParam mfcData;
PayloadBuilder* builder = new PayloadBuilder();
uint32_t miid = 0;
bool devicePPMFCSet = true;
// clear any cached custom payload
freeCustomPayload();
/* Prepare devicePP MFC payload */
/* Try to set devicePP MFC for virtual port enabled device to match to DMA config */
if (rm->activeGroupDevConfig &&
(dAttr.id == PAL_DEVICE_OUT_SPEAKER ||
dAttr.id == PAL_DEVICE_OUT_HANDSET ||
dAttr.id == PAL_DEVICE_OUT_ULTRASOUND)) {
status = SessionAlsaUtils::getModuleInstanceId(mixer, pcmDevIds.at(0), intf,
TAG_DEVICE_PP_MFC, &miid);
if (status == 0) {
PAL_DBG(LOG_TAG, "miid : %x id = %d, data %s, dev id = %d\n", miid,
pcmDevIds.at(0), intf, dAttr.id);
if (rm->activeGroupDevConfig->devpp_mfc_cfg.bit_width)
mfcData.bitWidth = rm->activeGroupDevConfig->devpp_mfc_cfg.bit_width;
else
mfcData.bitWidth = dAttr.config.bit_width;
if (rm->activeGroupDevConfig->devpp_mfc_cfg.sample_rate)
mfcData.sampleRate = rm->activeGroupDevConfig->devpp_mfc_cfg.sample_rate;
else
mfcData.sampleRate = dAttr.config.sample_rate;
if (rm->activeGroupDevConfig->devpp_mfc_cfg.channels)
mfcData.numChannel = rm->activeGroupDevConfig->devpp_mfc_cfg.channels;
else
mfcData.numChannel = dAttr.config.ch_info.channels;
mfcData.ch_info = nullptr;
builder->payloadMFCConfig((uint8_t**)&payload, &payloadSize, miid, &mfcData);
if (!payloadSize) {
PAL_ERR(LOG_TAG, "payloadMFCConfig failed\n");
status = -EINVAL;
goto exit;
}
status = updateCustomPayload(payload, payloadSize);
freeCustomPayload(&payload, &payloadSize);
if (0 != status) {
PAL_ERR(LOG_TAG, "updateCustomPayload Failed\n");
goto exit;
}
} else {
PAL_INFO(LOG_TAG, "deviePP MFC doesn't exist for stream %d \n", sAttr.type);
devicePPMFCSet = false;
}
/* set TKV for slot mask */
setSlotMask(rm, sAttr, dAttr, pcmDevIds);
} else if (rm->isDeviceMuxConfigEnabled && (dAttr.id == PAL_DEVICE_OUT_SPEAKER ||
dAttr.id == PAL_DEVICE_OUT_HANDSET)) {
setSlotMask(rm, sAttr, dAttr, pcmDevIds);
}
/* Prepare PSPD MFC payload */
/* Get PSPD MFC MIID and configure to match to device config */
/* This has to be done after sending all mixer controls and before connect */
status = SessionAlsaUtils::getModuleInstanceId(mixer, pcmDevIds.at(0), intf,
TAG_DEVICE_MFC_SR, &miid);
if (status == 0) {
PAL_DBG(LOG_TAG, "miid : %x id = %d, data %s, dev id = %d\n", miid,
pcmDevIds.at(0), intf, dAttr.id);
if (dAttr.id == PAL_DEVICE_OUT_BLUETOOTH_A2DP ||
dAttr.id == PAL_DEVICE_OUT_BLUETOOTH_SCO ||
dAttr.id == PAL_DEVICE_OUT_BLUETOOTH_BLE ||
dAttr.id == PAL_DEVICE_OUT_BLUETOOTH_BLE_BROADCAST) {
dev = Device::getInstance((struct pal_device *)&dAttr , rm);
if (!dev) {
PAL_ERR(LOG_TAG, "Device getInstance failed");
status = -EINVAL;
goto exit;
}
status = dev->getCodecConfig(&codecConfig);
if(0 != status) {
PAL_ERR(LOG_TAG, "getCodecConfig Failed \n");
goto exit;
}
mfcData.bitWidth = codecConfig.bit_width;
mfcData.sampleRate = codecConfig.sample_rate;
mfcData.numChannel = codecConfig.ch_info.channels;
mfcData.ch_info = nullptr;
} else {
mfcData.bitWidth = dAttr.config.bit_width;
if (!devicePPMFCSet && rm->activeGroupDevConfig->devpp_mfc_cfg.sample_rate)
mfcData.sampleRate = rm->activeGroupDevConfig->devpp_mfc_cfg.sample_rate;
else
mfcData.sampleRate = dAttr.config.sample_rate;
if (!devicePPMFCSet && rm->activeGroupDevConfig->devpp_mfc_cfg.channels)
mfcData.numChannel = rm->activeGroupDevConfig->devpp_mfc_cfg.channels;
else
mfcData.numChannel = dAttr.config.ch_info.channels;
mfcData.ch_info = nullptr;
}
if (dAttr.id == PAL_DEVICE_OUT_AUX_DIGITAL ||
dAttr.id == PAL_DEVICE_OUT_AUX_DIGITAL_1 ||
dAttr.id == PAL_DEVICE_OUT_HDMI)
mfcData.ch_info = &dAttr.config.ch_info;
builder->payloadMFCConfig((uint8_t **)&payload, &payloadSize, miid, &mfcData);
if (!payloadSize) {
PAL_ERR(LOG_TAG, "payloadMFCConfig failed\n");
status = -EINVAL;
goto exit;
}
status = updateCustomPayload(payload, payloadSize);
freeCustomPayload(&payload, &payloadSize);
if (0 != status) {
PAL_ERR(LOG_TAG, "updateCustomPayload Failed\n");
goto exit;
}
} else {
PAL_ERR(LOG_TAG, "getModuleInstanceId failed");
if (sAttr.direction == (PAL_AUDIO_INPUT | PAL_AUDIO_OUTPUT))
status = 0;
}
exit:
if (builder) {
delete builder;
builder = NULL;
}
return status;
}
int Session::checkAndSetExtEC(const std::shared_ptr<ResourceManager>& rm,
Stream *s, bool is_enable)
{
struct pcm_config config;
struct pal_stream_attributes sAttr;
int32_t status = 0;
std::shared_ptr<Device> dev = nullptr;
std::vector <std::shared_ptr<Device>> extEcTxDeviceList;
int32_t extEcbackendId;
std::vector <std::string> extEcbackendNames;
struct pal_device device;
PAL_DBG(LOG_TAG, "Enter.");
extECMutex.lock();
status = s->getStreamAttributes(&sAttr);
if (status != 0) {
PAL_ERR(LOG_TAG,"stream get attributes failed");
status = -EINVAL;
goto exit;
}
device.id = PAL_DEVICE_IN_EXT_EC_REF;
rm->getDeviceConfig(&device, &sAttr);
dev = Device::getInstance(&device, rm);
if (!dev) {
PAL_ERR(LOG_TAG, "dev get instance failed");
status = -EINVAL;
goto exit;
}
if(!is_enable) {
if (extECRefCnt > 0)
extECRefCnt --;
if (extECRefCnt == 0) {
if (pcmEcTx) {
status = pcm_stop(pcmEcTx);
if (status) {
PAL_ERR(LOG_TAG, "pcm_stop - ec_tx failed %d", status);
}
dev->stop();
status = pcm_close(pcmEcTx);
if (status) {
PAL_ERR(LOG_TAG, "pcm_close - ec_tx failed %d", status);
}
dev->close();
rm->freeFrontEndEcTxIds(pcmDevEcTxIds);
pcmEcTx = NULL;
extECMutex.unlock();
rm->restoreInternalECRefs();
extECMutex.lock();
}
}
} else {
extECRefCnt ++;
if (extECRefCnt == 1) {
extECMutex.unlock();
rm->disableInternalECRefs(s);
extECMutex.lock();
extEcTxDeviceList.push_back(dev);
pcmDevEcTxIds = rm->allocateFrontEndExtEcIds();
if (pcmDevEcTxIds.size() == 0) {
PAL_ERR(LOG_TAG, "ResourceManger::getBackEndNames returned no EXT_EC device Ids");
status = -EINVAL;
goto exit;
}
status = dev->open();
if (0 != status) {
PAL_ERR(LOG_TAG, "dev open failed");
status = -EINVAL;
rm->freeFrontEndEcTxIds(pcmDevEcTxIds);
goto exit;
}
status = dev->start();
if (0 != status) {
PAL_ERR(LOG_TAG, "dev start failed");
dev->close();
rm->freeFrontEndEcTxIds(pcmDevEcTxIds);
status = -EINVAL;
goto exit;
}
extEcbackendId = extEcTxDeviceList[0]->getSndDeviceId();
extEcbackendNames = rm->getBackEndNames(extEcTxDeviceList);
status = SessionAlsaUtils::openDev(rm, pcmDevEcTxIds, extEcbackendId,
extEcbackendNames.at(0).c_str());
if (0 != status) {
PAL_ERR(LOG_TAG, "SessionAlsaUtils::openDev failed");
dev->stop();
dev->close();
rm->freeFrontEndEcTxIds(pcmDevEcTxIds);
status = -EINVAL;
goto exit;
}
pcmEcTx = pcm_open(rm->getVirtualSndCard(), pcmDevEcTxIds.at(0), PCM_IN, &config);
if (!pcmEcTx) {
PAL_ERR(LOG_TAG, "Exit pcm-ec-tx open failed");
dev->stop();
dev->close();
rm->freeFrontEndEcTxIds(pcmDevEcTxIds);
status = -EINVAL;
goto exit;
}
if (!pcm_is_ready(pcmEcTx)) {
PAL_ERR(LOG_TAG, "Exit pcm-ec-tx open not ready");
pcmEcTx = NULL;
dev->stop();
dev->close();
rm->freeFrontEndEcTxIds(pcmDevEcTxIds);
status = -EINVAL;
goto exit;
}
status = pcm_start(pcmEcTx);
if (status) {
PAL_ERR(LOG_TAG, "pcm_start ec_tx failed %d", status);
pcm_close(pcmEcTx);
pcmEcTx = NULL;
dev->stop();
dev->close();
rm->freeFrontEndEcTxIds(pcmDevEcTxIds);
status = -EINVAL;
goto exit;
}
}
}
exit:
if (is_enable && status) {
PAL_DBG(LOG_TAG, "Reset extECRefCnt as EXT EC graph fails to setup");
extECRefCnt = 0;
}
extECMutex.unlock();
PAL_DBG(LOG_TAG, "Exit.");
return status;
}
/*
Handle case when charging going is on and PB starts on speaker.
Below steps are to ensure HW transition from charging->boost->charging
which will avoid USB collapse due to HW transition in less moment.
1. Set concurrency bit to update charger driver for respective session
2. Enable Speaker boost when Audio done voting as part of PCM_open
3. Set ICL config in device:Speaker module.
*/
int Session::NotifyChargerConcurrency(std::shared_ptr<ResourceManager>rm, bool state)
{
int status = -EINVAL;
if (!rm)
goto exit;
PAL_DBG(LOG_TAG, "Enter concurrency state %d Notify state %d \n",
rm->getConcurrentBoostState(), state);
ResourceManager::mChargerBoostMutex.lock();
if (rm->getChargerOnlineState()) {
if (rm->getConcurrentBoostState() ^ state)
status = rm->chargerListenerSetBoostState(state, CHARGER_ON_PB_STARTS);
if (0 != status)
PAL_ERR(LOG_TAG, "Failed to notify PMIC: %d", status);
}
PAL_DBG(LOG_TAG, "Exit concurrency state %d with status %d",
rm->getConcurrentBoostState(), status);
ResourceManager::mChargerBoostMutex.unlock();
exit:
return status;
}
/* Handle case when charging going on and PB starts on speaker*/
int Session::EnableChargerConcurrency(std::shared_ptr<ResourceManager>rm, Stream *s)
{
int status = -EINVAL;
if (!rm)
goto exit;
PAL_DBG(LOG_TAG, "Enter concurrency state %d", rm->getConcurrentBoostState());
if ((s && rm->getChargerOnlineState()) &&
(rm->getConcurrentBoostState())) {
status = rm->setSessionParamConfig(PAL_PARAM_ID_CHARGER_STATE, s,
CHARGE_CONCURRENCY_ON_TAG);
if (0 != status) {
PAL_DBG(LOG_TAG, "Set SessionParamConfig with status %d", status);
status = rm->chargerListenerSetBoostState(false, CHARGER_ON_PB_STARTS);
if (0 != status)
PAL_ERR(LOG_TAG, "Failed to notify PMIC: %d", status);
}
}
PAL_DBG(LOG_TAG, "Exit concurrency state %d with status %d",
rm->getConcurrentBoostState(), status);
exit:
return status;
}
#if 0
int setConfig(Stream * s, pal_stream_type_t sType, configType type, uint32_t tag1,
uint32_t tag2, uint32_t tag3)
{
int status = 0;
uint32_t tagsent = 0;
struct agm_tag_config* tagConfig = nullptr;
std::ostringstream tagCntrlName;
char const *stream = "PCM";
const char *setParamTagControl = "setParamTag";
struct mixer_ctl *ctl = nullptr;
uint32_t tkv_size = 0;
if (sType == PAL_STREAM_COMPRESSED)
stream = "COMPRESS";
switch (type) {
case MODULE:
tkv.clear();
if (tag1)
builder->populateTagKeyVector(s, tkv, tag1, &tagsent);
if (tag2)
builder->populateTagKeyVector(s, tkv, tag2, &tagsent);
if (tag3)
builder->populateTagKeyVector(s, tkv, tag3, &tagsent);
if (tkv.size() == 0) {
status = -EINVAL;
goto exit;
}
tagConfig = (struct agm_tag_config*)malloc (sizeof(struct agm_tag_config) +
(tkv.size() * sizeof(agm_key_value)));
if(!tagConfig) {
status = -ENOMEM;
goto exit;
}
status = SessionAlsaUtils::getTagMetadata(tagsent, tkv, tagConfig);
if (0 != status) {
goto exit;
}
tagCntrlName << stream << pcmDevIds.at(0) << " " << setParamTagControl;
ctl = mixer_get_ctl_by_name(mixer, tagCntrlName.str().data());
if (!ctl) {
PAL_ERR(LOG_TAG, "Invalid mixer control: %s\n", tagCntrlName.str().data());
return -ENOENT;
}
tkv_size = tkv.size() * sizeof(struct agm_key_value);
status = mixer_ctl_set_array(ctl, tagConfig, sizeof(struct agm_tag_config) + tkv_size);
if (status != 0) {
PAL_ERR(LOG_TAG, "failed to set the tag calibration %d", status);
goto exit;
}
ctl = NULL;
tkv.clear();
break;
default:
status = 0;
break;
}
exit:
return status;
}
#endif