blob: 83e5c10c9a410f3eca27a19022e475918feaa4ed [file] [log] [blame]
/*
* Copyright (c) 2019-2021, The Linux Foundation. All rights reserved.
* Copyright (c) 2022 Qualcomm Innovation Center, Inc. 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.
*/
#define LOG_TAG "AGM: graph_module"
#include <errno.h>
#include <pthread.h>
#include "gsl_intf.h"
#include <agm/graph.h>
#include <agm/graph_module.h>
#include <agm/metadata.h>
#include <agm/utils.h>
#ifdef DYNAMIC_LOG_ENABLED
#include <log_xml_parser.h>
#define LOG_MASK AGM_MOD_FILE_GRAPH_MODULE
#include <log_utils.h>
#endif
#define MONO 1
#define GET_BITS_PER_SAMPLE(format, bit_width) \
(format == AGM_FORMAT_PCM_S24_LE? 32 : bit_width)
/*qfactor should be set to 23 only for 24_3LE and 24_LE formats*/
#define GET_Q_FACTOR(format, bit_width) (bit_width - 1)
static void get_default_channel_map(uint8_t *channel_map, int channels)
{
switch (channels) {
case CHANNEL_1:
channel_map[0] = PCM_CHANNEL_C;
break;
case CHANNEL_2:
channel_map[0] = PCM_CHANNEL_L;
channel_map[1] = PCM_CHANNEL_R;
break;
case CHANNEL_3:
channel_map[0] = PCM_CHANNEL_L;
channel_map[1] = PCM_CHANNEL_R;
channel_map[2] = PCM_CHANNEL_C;
break;
case CHANNEL_4:
channel_map[0] = PCM_CHANNEL_L;
channel_map[1] = PCM_CHANNEL_R;
channel_map[2] = PCM_CHANNEL_LB;
channel_map[3] = PCM_CHANNEL_RB;
break;
case CHANNEL_5:
channel_map[0] = PCM_CHANNEL_L;
channel_map[1] = PCM_CHANNEL_R;
channel_map[2] = PCM_CHANNEL_C;
channel_map[3] = PCM_CHANNEL_LB;
channel_map[4] = PCM_CHANNEL_RB;
break;
case CHANNEL_6:
channel_map[0] = PCM_CHANNEL_L;
channel_map[1] = PCM_CHANNEL_R;
channel_map[2] = PCM_CHANNEL_C;
channel_map[3] = PCM_CHANNEL_LFE;
channel_map[4] = PCM_CHANNEL_LB;
channel_map[5] = PCM_CHANNEL_RB;
break;
case CHANNEL_7:
/*
* Configured for 5.1 channel mapping + 1 channel for debug
* Can be customized based on DSP.
*/
channel_map[0] = PCM_CHANNEL_L;
channel_map[1] = PCM_CHANNEL_R;
channel_map[2] = PCM_CHANNEL_C;
channel_map[3] = PCM_CHANNEL_LFE;
channel_map[4] = PCM_CHANNEL_LB;
channel_map[5] = PCM_CHANNEL_RB;
channel_map[6] = PCM_CHANNEL_CS;
break;
case CHANNEL_8:
channel_map[0] = PCM_CHANNEL_L;
channel_map[1] = PCM_CHANNEL_R;
channel_map[2] = PCM_CHANNEL_C;
channel_map[3] = PCM_CHANNEL_LFE;
channel_map[4] = PCM_CHANNEL_LB;
channel_map[5] = PCM_CHANNEL_RB;
channel_map[6] = PCM_CHANNEL_LS;
channel_map[7] = PCM_CHANNEL_RS;
}
}
static bool is_format_pcm(enum agm_media_format fmt_id)
{
if (fmt_id >= AGM_FORMAT_PCM_S8 && fmt_id <= AGM_FORMAT_PCM_S32_LE)
return true;
return false;
}
static bool is_format_bypassed(enum agm_media_format fmt_id)
{
if (fmt_id == AGM_FORMAT_VORBIS) {
return true;
}
return false;
}
int get_pcm_bit_width(enum agm_media_format fmt_id)
{
int bit_width = 16;
switch (fmt_id) {
case AGM_FORMAT_PCM_S24_3LE:
/*
*This api returns the number of audio data bit width specific to the format
*e.g. In S24_LE, even if the number of bytes is 4, the audio data is only in 3 bytes
*Hence we return 24 as the bit_width, whereas the bitspersample for this format would
*return 32
*/
case AGM_FORMAT_PCM_S24_LE:
bit_width = 24;
break;
case AGM_FORMAT_PCM_S32_LE:
bit_width = 32;
break;
case AGM_FORMAT_PCM_S16_LE:
default:
break;
}
return bit_width;
}
static int get_media_bit_width(struct session_obj *sess_obj,
struct agm_media_config *media_config)
{
int bit_width = 16;
if (is_format_pcm(media_config->format))
return get_pcm_bit_width(media_config->format);
switch (media_config->format) {
case AGM_FORMAT_FLAC:
bit_width = sess_obj->stream_config.codec.flac_dec.sample_size;
break;
case AGM_FORMAT_ALAC:
bit_width = sess_obj->stream_config.codec.alac_dec.bit_depth;
break;
case AGM_FORMAT_APE:
bit_width = sess_obj->stream_config.codec.ape_dec.bit_width;
break;
case AGM_FORMAT_WMASTD:
bit_width = sess_obj->stream_config.codec.wma_dec.bits_per_sample;
break;
case AGM_FORMAT_WMAPRO:
bit_width = sess_obj->stream_config.codec.wmapro_dec.bits_per_sample;
break;
case AGM_FORMAT_MP3:
case AGM_FORMAT_AAC:
default:
break;
}
return bit_width;
}
static int configure_codec_dma_ep(struct module_info *mod,
struct graph_obj *graph_obj)
{
int ret = 0;
struct device_obj *dev_obj = mod->dev_obj;
hw_ep_info_t hw_ep_info = dev_obj->hw_ep_info;
struct apm_module_param_data_t *header;
struct param_id_codec_dma_intf_cfg_t* codec_config;
size_t payload_sz;
uint8_t *payload = NULL;
uint32_t *chmap = NULL;
struct agm_media_config media_config = (dev_obj->group_data) ?
dev_obj->group_data->media_config.config :dev_obj->media_config;
AGM_LOGV("entry mod tag %x miid %x mid %x\n", mod->tag, mod->miid, mod->mid);
payload_sz = sizeof(struct apm_module_param_data_t) +
sizeof(struct param_id_codec_dma_intf_cfg_t);
ALIGN_PAYLOAD(payload_sz, 8);
payload = (uint8_t*)calloc(1, (size_t)payload_sz);
if (!payload) {
AGM_LOGE("Not enough memory for payload");
ret = -ENOMEM;
goto done;
}
header = (struct apm_module_param_data_t*)payload;
codec_config = (struct param_id_codec_dma_intf_cfg_t*)
(payload + sizeof(struct apm_module_param_data_t));
ret = device_get_channel_map(dev_obj, &chmap);
if (ret || chmap == NULL) {
AGM_LOGE("get channel map failed");
goto done;
}
if (chmap[0] < media_config.channels) {
AGM_LOGE("Mismatch in num channels, expected %d, received %d",
media_config.channels, chmap[0]);
ret = -EINVAL;
goto done;
}
header->module_instance_id = mod->miid;
header->param_id = PARAM_ID_CODEC_DMA_INTF_CFG;
header->error_code = 0x0;
header->param_size = sizeof(struct param_id_codec_dma_intf_cfg_t);
codec_config->lpaif_type = hw_ep_info.ep_config.cdc_dma_i2s_tdm_config.lpaif_type;
codec_config->intf_indx = hw_ep_info.ep_config.cdc_dma_i2s_tdm_config.intf_idx;
/* chmap[0] contains num_ch and chmap[1] contains channelmap */
codec_config->active_channels_mask = chmap[1];
AGM_LOGD("cdc_dma intf cfg lpaif %d indx %d ch_msk %x",
codec_config->lpaif_type, codec_config->intf_indx,
codec_config->active_channels_mask);
ret = gsl_set_custom_config(graph_obj->graph_handle, payload, payload_sz);
if (ret != 0) {
ret = ar_err_get_lnx_err_code(ret);
AGM_LOGE("custom_config for module %d failed with error %d",
mod->tag, ret);
}
done:
if (chmap)
free(chmap);
if (payload)
free(payload);
AGM_LOGD("exit, ret %d", ret);
return ret;
}
static int configure_i2s_ep(struct module_info *mod,
struct graph_obj *graph_obj)
{
int ret = 0;
struct device_obj *dev_obj = mod->dev_obj;
hw_ep_info_t hw_ep_info = dev_obj->hw_ep_info;
struct gsl_key_vector tag_key_vect;
struct apm_module_param_data_t *header;
struct param_id_i2s_intf_cfg_t* i2s_config;
size_t payload_sz, ret_payload_sz = 0;
uint8_t *payload = NULL;
struct agm_media_config media_config = (dev_obj->group_data) ?
dev_obj->group_data->media_config.config :dev_obj->media_config;
AGM_LOGV("entry mod tag %x miid %x mid %x", mod->tag, mod->miid, mod->mid);
payload_sz = sizeof(struct apm_module_param_data_t) +
sizeof(struct param_id_i2s_intf_cfg_t);
ALIGN_PAYLOAD(payload_sz, 8);
ret_payload_sz = payload_sz;
payload = (uint8_t*)calloc(1, (size_t)payload_sz);
if (!payload) {
AGM_LOGE("Not enough memory for payload");
ret = -ENOMEM;
goto done;
}
header = (struct apm_module_param_data_t*)payload;
i2s_config = (struct param_id_i2s_intf_cfg_t*)
(payload + sizeof(struct apm_module_param_data_t));
/*
* For Codec dma we need to configure the following tags
* 1.Channels - Channels are reused to derive the active channel mask
*/
tag_key_vect.num_kvps = 1;
tag_key_vect.kvp = calloc(tag_key_vect.num_kvps,
sizeof(struct gsl_key_value_pair));
if (!tag_key_vect.kvp) {
AGM_LOGE("Not enough memory for KVP");
ret = -ENOMEM;
goto free_payload;
}
tag_key_vect.kvp[0].key = CHANNELS;
tag_key_vect.kvp[0].value = media_config.channels;
ret = gsl_get_tagged_data((struct gsl_key_vector *)mod->gkv,
mod->tag, &tag_key_vect, (uint8_t *)payload,
&ret_payload_sz);
if (ret != 0) {
if (ret == AR_ENEEDMORE)
AGM_LOGE("payload buffer sz %zu smaller than expected size %zu",
payload_sz, ret_payload_sz);
ret = ar_err_get_lnx_err_code(ret);
AGM_LOGD("get_tagged_data for module %d failed with error %d",
mod->tag, ret);
goto free_kvp;
}
AGM_LOGV("hdr mid %x pid %x er_cd %x param_sz %d",
header->module_instance_id, header->param_id, header->error_code,
header->param_size);
i2s_config->lpaif_type = hw_ep_info.ep_config.cdc_dma_i2s_tdm_config.lpaif_type;
i2s_config->intf_idx = hw_ep_info.ep_config.cdc_dma_i2s_tdm_config.intf_idx;
AGM_LOGV("i2s intf cfg lpaif %d indx %d sd_ln_idx %x ws_src %d",
i2s_config->lpaif_type, i2s_config->intf_idx,
i2s_config->sd_line_idx, i2s_config->ws_src);
ret = gsl_set_custom_config(graph_obj->graph_handle, payload, payload_sz);
if (ret != 0) {
ret = ar_err_get_lnx_err_code(ret);
AGM_LOGE("custom_config for module %d failed with error %d",
mod->tag, ret);
}
free_kvp:
free(tag_key_vect.kvp);
free_payload:
free(payload);
done:
AGM_LOGD("exit, ret %d", ret);
return ret;
}
static int configure_tdm_ep(struct module_info *mod,
struct graph_obj *graph_obj)
{
int ret = 0;
struct device_obj *dev_obj = mod->dev_obj;
hw_ep_info_t hw_ep_info = dev_obj->hw_ep_info;
struct gsl_key_vector tag_key_vect;
struct apm_module_param_data_t *header;
struct param_id_tdm_intf_cfg_t* tdm_config;
size_t payload_sz, ret_payload_sz = 0;
uint8_t *payload = NULL;
struct agm_media_config media_config = (dev_obj->group_data) ?
dev_obj->group_data->media_config.config :dev_obj->media_config;
AGM_LOGV("entry mod tag %x miid %x mid %x", mod->tag, mod->miid, mod->mid);
payload_sz = sizeof(struct apm_module_param_data_t) +
sizeof(struct param_id_tdm_intf_cfg_t);
ALIGN_PAYLOAD(payload_sz, 8);
ret_payload_sz = payload_sz;
payload = (uint8_t*)calloc(1, (size_t)payload_sz);
if (!payload) {
AGM_LOGE("Not enough memory for payload");
ret = -ENOMEM;
goto done;
}
header = (struct apm_module_param_data_t*)payload;
tdm_config = (struct param_id_tdm_intf_cfg_t*)
(payload + sizeof(struct apm_module_param_data_t));
/*
* For Codec dma we need to configure the following tags
* 1.Channels - Channels are reused to derive the active channel mask
*/
tag_key_vect.num_kvps = 1;
tag_key_vect.kvp = calloc(tag_key_vect.num_kvps,
sizeof(struct gsl_key_value_pair));
if (!tag_key_vect.kvp) {
AGM_LOGE("Not enough memory for KVP");
ret = -ENOMEM;
goto free_payload;
}
tag_key_vect.kvp[0].key = CHANNELS;
tag_key_vect.kvp[0].value = media_config.channels;
ret = gsl_get_tagged_data((struct gsl_key_vector *)mod->gkv,
mod->tag, &tag_key_vect, (uint8_t *)payload,
&ret_payload_sz);
if (ret != 0) {
if (ret == AR_ENEEDMORE)
AGM_LOGE("payload buffer sz %zu smaller than expected size %zu",
payload_sz, ret_payload_sz);
ret = ar_err_get_lnx_err_code(ret);
AGM_LOGD("get_tagged_data for module %d failed with error %d",
mod->tag, ret);
goto free_kvp;
}
tdm_config->lpaif_type = hw_ep_info.ep_config.cdc_dma_i2s_tdm_config.lpaif_type;
tdm_config->intf_idx = hw_ep_info.ep_config.cdc_dma_i2s_tdm_config.intf_idx;
/* Update slot_mask from AGM only in case of group TDM */
if (dev_obj->group_data)
tdm_config->slot_mask = dev_obj->group_data->media_config.slot_mask;
AGM_LOGV("tdm intf cfg lpaif %d idx %d sync_src %d ctrl_dt_ot_enb %d",
tdm_config->lpaif_type, tdm_config->intf_idx, tdm_config->sync_src,
tdm_config->ctrl_data_out_enable);
AGM_LOGV("slt_msk %x nslts_per_frm %x slt_wdth %d sync_mode %d",
tdm_config->slot_mask, tdm_config->nslots_per_frame,
tdm_config->slot_width, tdm_config->sync_mode);
AGM_LOGV("inv_sync_pulse %d sync_data_delay %d",
tdm_config->ctrl_invert_sync_pulse, tdm_config->ctrl_sync_data_delay);
ret = gsl_set_custom_config(graph_obj->graph_handle, payload, payload_sz);
if (ret != 0) {
ret = ar_err_get_lnx_err_code(ret);
AGM_LOGE("custom_config for module %d failed with error %d",
mod->tag, ret);
}
free_kvp:
free(tag_key_vect.kvp);
free_payload:
free(payload);
done:
AGM_LOGD("exit, ret %d", ret);
return ret;
}
static int configure_aux_pcm_ep(struct module_info *mod,
struct graph_obj *graph_obj)
{
int ret = 0;
struct device_obj *dev_obj = mod->dev_obj;
hw_ep_info_t hw_ep_info = dev_obj->hw_ep_info;
struct gsl_key_vector tag_key_vect;
struct apm_module_param_data_t *header;
struct param_id_hw_pcm_intf_cfg_t* aux_pcm_cfg;
size_t payload_sz ,ret_payload_sz = 0;
uint8_t *payload = NULL;
AGM_LOGV("entry mod tag %x miid %x mid %x", mod->tag, mod->miid, mod->mid);
payload_sz = sizeof(struct apm_module_param_data_t) +
sizeof(struct param_id_hw_pcm_intf_cfg_t);
ALIGN_PAYLOAD(payload_sz, 8);
ret_payload_sz = payload_sz;
payload = (uint8_t*)calloc(1, (size_t)payload_sz);
if (!payload) {
AGM_LOGE("Not enough memory for payload");
ret = -ENOMEM;
goto done;
}
header = (struct apm_module_param_data_t*)payload;
aux_pcm_cfg = (struct param_id_hw_pcm_intf_cfg_t*)
(payload + sizeof(struct apm_module_param_data_t));
/*
* For Codec dma we need to configure the following tags
* 1.Channels - Channels are reused to derive the active channel mask
*/
tag_key_vect.num_kvps = 1;
tag_key_vect.kvp = calloc(tag_key_vect.num_kvps,
sizeof(struct gsl_key_value_pair));
if (!tag_key_vect.kvp) {
AGM_LOGE("Not enough memory for KVP");
ret = -ENOMEM;
goto free_payload;
}
tag_key_vect.kvp[0].key = CHANNELS;
tag_key_vect.kvp[0].value = dev_obj->media_config.channels;
ret = gsl_get_tagged_data((struct gsl_key_vector *)mod->gkv,
mod->tag, &tag_key_vect, (uint8_t *)payload,
&ret_payload_sz);
if (ret != 0) {
if (ret == AR_ENEEDMORE)
AGM_LOGE("payload buffer sz %zu smaller than expected size %zu",
payload_sz, ret_payload_sz);
ret = ar_err_get_lnx_err_code(ret);
AGM_LOGD("get_tagged_data for module %d failed with error %d",
mod->tag, ret);
goto free_kvp;
}
aux_pcm_cfg->lpaif_type = hw_ep_info.ep_config.cdc_dma_i2s_tdm_config.lpaif_type;
aux_pcm_cfg->intf_idx = hw_ep_info.ep_config.cdc_dma_i2s_tdm_config.intf_idx;
AGM_LOGV("aux intf cfg lpaif %d idx %d sync_src %d ctrl_dt_ot_enb %d",
aux_pcm_cfg->lpaif_type, aux_pcm_cfg->intf_idx, aux_pcm_cfg->sync_src,
aux_pcm_cfg->ctrl_data_out_enable);
AGM_LOGV("slt_msk %x frm_setting %x aux_mode %d",
aux_pcm_cfg->slot_mask, aux_pcm_cfg->frame_setting,
aux_pcm_cfg->aux_mode);
ret = gsl_set_custom_config(graph_obj->graph_handle, payload, payload_sz);
if (ret != 0) {
ret = ar_err_get_lnx_err_code(ret);
AGM_LOGE("custom_config for module %d failed with error %d",
mod->tag, ret);
}
free_kvp:
free(tag_key_vect.kvp);
free_payload:
free(payload);
done:
AGM_LOGD("exit, ret %d", ret);
return ret;
}
static int configure_slimbus_ep(struct module_info *mod,
struct graph_obj *graph_obj)
{
int ret = 0;
struct device_obj *dev_obj = mod->dev_obj;
hw_ep_info_t hw_ep_info = dev_obj->hw_ep_info;
struct apm_module_param_data_t *header;
struct param_id_slimbus_cfg_t* slimbus_cfg;
size_t payload_sz;
uint8_t *payload = NULL;
int i = 0;
uint32_t *chmap = NULL;
AGM_LOGD("entry mod tag %x miid %x mid %x", mod->tag, mod->miid, mod->mid);
if (dev_obj->media_config.channels > SB_MAX_CHAN_CNT) {
AGM_LOGE("device channels %d exceed max supported ch %d for Slimbus",
dev_obj->media_config.channels, SB_MAX_CHAN_CNT);
ret = -EINVAL;
goto done;
}
payload_sz = sizeof(struct apm_module_param_data_t) +
sizeof(struct param_id_slimbus_cfg_t);
ALIGN_PAYLOAD(payload_sz, 8);
payload = (uint8_t*)calloc(1, (size_t)payload_sz);
if (!payload) {
AGM_LOGE("Not enough memory for payload");
ret = -ENOMEM;
goto done;
}
header = (struct apm_module_param_data_t*)payload;
slimbus_cfg = (struct param_id_slimbus_cfg_t*)
(payload + sizeof(struct apm_module_param_data_t));
ret = device_get_channel_map(dev_obj, &chmap);
if (ret || chmap == NULL) {
AGM_LOGE("get channel map failed");
goto done;
}
if (chmap[0] < dev_obj->media_config.channels) {
AGM_LOGE("Mismatch in num channels, expected %d, received %d",
dev_obj->media_config.channels, chmap[0]);
ret = -EINVAL;
goto done;
}
header->module_instance_id = mod->miid;
header->param_id = PARAM_ID_SLIMBUS_CONFIG;
header->error_code = 0x0;
header->param_size = sizeof(struct param_id_slimbus_cfg_t);
slimbus_cfg->slimbus_dev_id = hw_ep_info.ep_config.slim_config.dev_id;
AGM_LOGD("slimbus intf cfg dev id %d ch %d", slimbus_cfg->slimbus_dev_id,
dev_obj->media_config.channels);
for (i = 0; i < dev_obj->media_config.channels; i++) {
slimbus_cfg->shared_channel_mapping[i] = chmap[i + 1];
AGM_LOGV("shared_chnl_mapping[%d] = 0x%x\n", i, slimbus_cfg->shared_channel_mapping[i]);
}
ret = gsl_set_custom_config(graph_obj->graph_handle, payload, payload_sz);
if (ret != 0) {
ret = ar_err_get_lnx_err_code(ret);
AGM_LOGE("custom_config for module %d failed with error %d",
mod->tag, ret);
}
done:
if (payload)
free(payload);
if (chmap)
free(chmap);
AGM_LOGD("exit, ret %d", ret);
return ret;
}
int configure_hw_ep_media_config(struct module_info *mod,
struct graph_obj *graph_obj)
{
int ret = 0;
uint8_t *payload = NULL;
size_t payload_size = 0;
struct device_obj *dev_obj = mod->dev_obj;
struct apm_module_param_data_t* header;
struct param_id_hw_ep_mf_t* hw_ep_media_conf;
struct agm_media_config media_config = (dev_obj->group_data) ?
dev_obj->group_data->media_config.config :dev_obj->media_config;
AGM_LOGD("entry mod tag %x miid %x mid %x",mod->tag, mod->miid, mod->mid);
payload_size = sizeof(struct apm_module_param_data_t) +
sizeof(struct param_id_hw_ep_mf_t);
/*ensure that the payloadszie is byte multiple atleast*/
ALIGN_PAYLOAD(payload_size, 8);
payload = calloc(1, (size_t)payload_size);
if (!payload) {
AGM_LOGE("No memory to allocate for payload");
ret = -ENOMEM;
goto done;
}
header = (struct apm_module_param_data_t*)payload;
hw_ep_media_conf = (struct param_id_hw_ep_mf_t*)
(payload + sizeof(struct apm_module_param_data_t));
header->module_instance_id = mod->miid;
header->param_id = PARAM_ID_HW_EP_MF_CFG;
header->error_code = 0x0;
header->param_size = sizeof(struct param_id_hw_ep_mf_t);
hw_ep_media_conf->sample_rate = media_config.rate;
hw_ep_media_conf->bit_width = get_pcm_bit_width(media_config.format);
hw_ep_media_conf->num_channels = media_config.channels;
hw_ep_media_conf->data_format = media_config.data_format;
AGM_LOGD("rate %d bw %d ch %d, data_fmt %d", media_config.rate,
hw_ep_media_conf->bit_width, media_config.channels,
media_config.data_format);
ret = gsl_set_custom_config(graph_obj->graph_handle, payload, payload_size);
if (ret != 0) {
ret = ar_err_get_lnx_err_code(ret);
AGM_LOGE("custom_config command for module %d failed with error %d",
mod->tag, ret);
}
free(payload);
done:
AGM_LOGD("exit, ret %d", ret);
return ret;
}
int configure_hw_ep(struct module_info *mod,
struct graph_obj *graph_obj)
{
int ret = 0;
struct device_obj *dev_obj = mod->dev_obj;
if(dev_obj->hw_ep_info.intf == PCM_RT_PROXY || dev_obj->hw_ep_info.intf == PCM_DUMMY) {
AGM_LOGD("no ep media config for %d\n", dev_obj->hw_ep_info.intf);
}
else {
ret = configure_hw_ep_media_config(mod, graph_obj);
if (ret) {
AGM_LOGE("hw_ep_media_config failed %d", ret);
return ret;
}
}
switch (dev_obj->hw_ep_info.intf) {
case CODEC_DMA:
ret = configure_codec_dma_ep(mod, graph_obj);
break;
case MI2S:
ret = configure_i2s_ep(mod, graph_obj);
break;
case AUXPCM:
ret = configure_aux_pcm_ep(mod, graph_obj);
break;
case TDM:
ret = configure_tdm_ep(mod, graph_obj);
break;
case SLIMBUS:
ret = configure_slimbus_ep(mod, graph_obj);
break;
case DISPLAY_PORT:
case USB_AUDIO:
AGM_LOGD("no ep configuration for %d\n", dev_obj->hw_ep_info.intf);
break;
case PCM_RT_PROXY:
AGM_LOGD("no ep configuration for %d\n", dev_obj->hw_ep_info.intf);
break;
case AUDIOSS_DMA:
AGM_LOGD("no ep configuration for %d\n", dev_obj->hw_ep_info.intf);
break;
case PCM_DUMMY:
AGM_LOGD("no ep configuration for %d\n", dev_obj->hw_ep_info.intf);
break;
default:
AGM_LOGE("hw intf %d not enabled yet", dev_obj->hw_ep_info.intf);
break;
}
return ret;
}
/**
*PCM DECODER/ENCODER and PCM CONVERTER are configured with the
*same PCM_FORMAT_CFG hence reuse the implementation
*/
int configure_output_media_format(struct module_info *mod,
struct graph_obj *graph_obj)
{
struct session_obj *sess_obj = graph_obj->sess_obj;
struct media_format_t *media_fmt_hdr;
struct payload_pcm_output_format_cfg_t *pcm_output_fmt_payload;
struct apm_module_param_data_t *header;
uint8_t *payload = NULL;
size_t payload_size = 0;
uint8_t *channel_map;
int ret = 0;
int num_channels = MONO;
struct agm_media_config media_config = {0};
/*We use in_media_config for Record usecases or
*if it is NON_TUNNEL_MODE decode usecase otherwise
*out_media_config is used to configure PCM_Convertor
*/
if ((sess_obj->stream_config.dir == TX) ||
((sess_obj->stream_config.sess_mode == AGM_SESSION_NON_TUNNEL) &&
(is_format_pcm(sess_obj->in_media_config.format)))) {
media_config = sess_obj->in_media_config;
}
else
media_config = sess_obj->out_media_config;
AGM_LOGD("entry mod tag %x miid %x mid %x",mod->tag, mod->miid, mod->mid);
num_channels = media_config.channels;
payload_size = sizeof(struct apm_module_param_data_t) +
sizeof(struct media_format_t) +
sizeof(struct payload_pcm_output_format_cfg_t) +
sizeof(uint8_t)*num_channels;
/*ensure that the payloadszie is byte multiple atleast*/
ALIGN_PAYLOAD(payload_size, 8);
payload = calloc(1, (size_t)payload_size);
if (!payload) {
AGM_LOGE("Not enough memory for payload");
ret = -ENOMEM;
goto done;
}
header = (struct apm_module_param_data_t*)payload;
media_fmt_hdr = (struct media_format_t*)(payload +
sizeof(struct apm_module_param_data_t));
pcm_output_fmt_payload = (struct payload_pcm_output_format_cfg_t*)(payload +
sizeof(struct apm_module_param_data_t) +
sizeof(struct media_format_t));
channel_map = (uint8_t*)(payload + sizeof(struct apm_module_param_data_t) +
sizeof(struct media_format_t) +
sizeof(struct payload_pcm_output_format_cfg_t));
header->module_instance_id = mod->miid;
header->param_id = PARAM_ID_PCM_OUTPUT_FORMAT_CFG;
header->error_code = 0x0;
header->param_size = sizeof(struct media_format_t) +
sizeof(struct payload_pcm_output_format_cfg_t) +
sizeof(uint8_t)*num_channels;
media_fmt_hdr->data_format = AGM_DATA_FORMAT_FIXED_POINT;
media_fmt_hdr->fmt_id = MEDIA_FMT_ID_PCM;
media_fmt_hdr->payload_size =
(uint32_t)(sizeof(payload_pcm_output_format_cfg_t) +
sizeof(uint8_t) * num_channels);
pcm_output_fmt_payload->endianness = PCM_LITTLE_ENDIAN;
pcm_output_fmt_payload->bit_width = get_media_bit_width(sess_obj, &media_config);
/**
*alignment field is referred to only in case where bit width is
*24 and bits per sample is 32, tiny alsa only supports 24 bit
*in 32 word size in LSB aligned mode(AGM_FORMAT_PCM_S24_LE).
*Hence we hardcode this to PCM_LSB_ALIGNED;
*/
pcm_output_fmt_payload->alignment = PCM_LSB_ALIGNED;
pcm_output_fmt_payload->num_channels = num_channels;
if (((sess_obj->stream_config.dir == TX) ||
((sess_obj->stream_config.sess_mode == AGM_SESSION_NON_TUNNEL) && mod->module == MODULE_PCM_CONVERTER)) &&
is_format_pcm(media_config.format)) {
/*for PCM capture usecase, we want native data to be captured hence
*configure pcm convertor accordingly
*Also in case of Non Tunnel Mode decode, we configure PCM_CONVERTOR with
*media config same as what is configured by client on the read path.
*We have added the module check above to ensure that the decoder
*PCM_OUTPUT_FORMAT_CFG is configured in the else part.
*/
pcm_output_fmt_payload->bits_per_sample =
GET_BITS_PER_SAMPLE(media_config.format,
pcm_output_fmt_payload->bit_width);
pcm_output_fmt_payload->q_factor =
GET_Q_FACTOR(media_config.format,
pcm_output_fmt_payload->bit_width);
if (sess_obj->stream_config.sess_mode == AGM_SESSION_NON_TUNNEL)
/*
* Setting num channels to native mode for PCM convetter
* so that channels always match upstream decoder module
* channel configuration
*/
pcm_output_fmt_payload->num_channels = PARAM_VAL_NATIVE;
} else {
switch (pcm_output_fmt_payload->bit_width) {
case 16:
case 32:
pcm_output_fmt_payload->bits_per_sample =
pcm_output_fmt_payload->bit_width;
pcm_output_fmt_payload->q_factor =
pcm_output_fmt_payload->bit_width - 1;
break;
case 24:
/*
*modules after pcm convertor only work on 16 or 32bit samples hence
*even for 24 bit input data configure pcm convertor output with
*32 bits per sample also to accomodate post processing, SPF
*tean recommends to set the q_factor as 27 even for 24_LE/24_3LE formats
*This is to be done only for pcm_convertor on the playback path
*/
pcm_output_fmt_payload->bits_per_sample = 32;
pcm_output_fmt_payload->q_factor = 27;
break;
default:
AGM_LOGE("wrong bitwidth %d", pcm_output_fmt_payload->bit_width);
ret = -EINVAL;
goto done;
}
}
if (sess_obj->stream_config.dir == RX)
pcm_output_fmt_payload->interleaved = PCM_DEINTERLEAVED_UNPACKED;
else
pcm_output_fmt_payload->interleaved = PCM_INTERLEAVED;
/**
*#TODO:As of now channel_map is not part of media_config
*ADD channel map part as part of the session/device media config
*structure and use that channel map if set by client otherwise
* use the default channel map
*/
get_default_channel_map(channel_map, num_channels);
ret = gsl_set_custom_config(graph_obj->graph_handle, payload, payload_size);
if (ret != 0) {
ret = ar_err_get_lnx_err_code(ret);
AGM_LOGE("custom_config command for module %d failed with error %d",
mod->tag, ret);
}
done:
if (payload) {
free(payload);
}
AGM_LOGD("exit, ret %d", ret);
return ret;
}
static int configure_pcm_encoder_frame_size(struct module_info *mod,
struct graph_obj *graph_obj,
uint32_t frame_size_samples)
{
struct apm_module_param_data_t *header;
struct param_id_pcm_encoder_frame_size_t *frame_size_payload;
uint8_t *payload = NULL;
size_t payload_size = 0;
int ret = 0;
payload_size = sizeof(struct apm_module_param_data_t) +
sizeof(struct param_id_pcm_encoder_frame_size_t);
payload = calloc(1, (size_t)payload_size);
if (!payload) {
AGM_LOGE("Not enough memory for payload");
return -ENOMEM;
}
header = (struct apm_module_param_data_t*)payload;
frame_size_payload = (struct param_id_pcm_encoder_frame_size_t*)(payload +
sizeof(struct apm_module_param_data_t));
header->module_instance_id = mod->miid;
header->param_id = PARAM_ID_PCM_ENCODER_FRAME_SIZE;
header->error_code = 0x0;
header->param_size = sizeof(struct param_id_pcm_encoder_frame_size_t);
frame_size_payload->frame_size_type = 1; /* frame_size_in_samples */
frame_size_payload->frame_size_in_samples = frame_size_samples;
ret = gsl_set_custom_config(graph_obj->graph_handle, payload, payload_size);
if (ret != 0) {
ret = ar_err_get_lnx_err_code(ret);
AGM_LOGE("pcm encoder frame size config for module %d failed with error %d",
mod->tag, ret);
}
free(payload);
return ret;
}
int configure_pcm_encoder_params(struct module_info *mod,
struct graph_obj *graph_obj)
{
int ret = 0;
struct session_obj *sess_obj = graph_obj->sess_obj;
uint32_t samples_per_msec = 0, frame_size = 0;
uint32_t channels = MONO, bits = 16;
/* configure output media format */
ret = configure_output_media_format(mod, graph_obj);
if (ret)
return ret;
/* configure pcm encoder frame size */
if (sess_obj->stream_config.sess_mode != AGM_SESSION_NON_TUNNEL) {
samples_per_msec = sess_obj->in_media_config.rate/1000;
channels = sess_obj->in_media_config.channels;
bits = get_pcm_bit_width(sess_obj->in_media_config.format);
bits = GET_BITS_PER_SAMPLE(sess_obj->in_media_config.format, bits);
channels = (channels == 0) ? MONO : channels;
frame_size = (sess_obj->in_buffer_config.size * 8) /
(channels * bits);
if (samples_per_msec &&
(((frame_size * 1000) % sess_obj->in_media_config.rate) != 0)) {
AGM_LOGD("pcm encoder: frame_size %d\n", frame_size);
ret = configure_pcm_encoder_frame_size(mod, graph_obj, frame_size);
}
}
return ret;
}
static int get_media_fmt_id_and_size(enum agm_media_format fmt_id,
size_t *payload_size, size_t *real_fmt_id)
{
int ret = 0;
size_t format_size = 0;
if (is_format_pcm(fmt_id)) {
format_size = sizeof(struct payload_media_fmt_pcm_t);;
*real_fmt_id = MEDIA_FMT_PCM;
goto pl_size;
}
switch (fmt_id) {
case AGM_FORMAT_MP3:
format_size = 0;
*real_fmt_id = MEDIA_FMT_MP3;
break;
case AGM_FORMAT_AAC:
format_size = sizeof(struct payload_media_fmt_aac_t);
*real_fmt_id = MEDIA_FMT_AAC;
break;
case AGM_FORMAT_FLAC:
format_size = sizeof(struct payload_media_fmt_flac_t);
*real_fmt_id = MEDIA_FMT_FLAC;
break;
case AGM_FORMAT_ALAC:
format_size = sizeof(struct payload_media_fmt_alac_t);
*real_fmt_id = MEDIA_FMT_ALAC;
break;
case AGM_FORMAT_APE:
format_size = sizeof(struct payload_media_fmt_ape_t);
*real_fmt_id = MEDIA_FMT_APE;
break;
case AGM_FORMAT_WMASTD:
format_size = sizeof(struct payload_media_fmt_wmastd_t);
*real_fmt_id = MEDIA_FMT_WMASTD;
break;
case AGM_FORMAT_WMAPRO:
format_size = sizeof(struct payload_media_fmt_wmapro_t);
*real_fmt_id = MEDIA_FMT_WMAPRO;
break;
case AGM_FORMAT_VORBIS:
format_size = 0;
*real_fmt_id = MEDIA_FMT_VORBIS;
break;
case AGM_FORMAT_AMR_NB:
format_size = 0;
*real_fmt_id = MEDIA_FMT_AMRNB;
break;
case AGM_FORMAT_AMR_WB:
format_size = 0;
*real_fmt_id = MEDIA_FMT_AMRWB;
break;
case AGM_FORMAT_AMR_WB_PLUS:
format_size = 0;
*real_fmt_id = MEDIA_FMT_AMRWBPLUS;
break;
case AGM_FORMAT_EVRC:
format_size = 0;
*real_fmt_id = MEDIA_FMT_EVRC;
break;
case AGM_FORMAT_G711:
format_size = 0;
*real_fmt_id = MEDIA_FMT_G711;
break;
case AGM_FORMAT_QCELP:
format_size = 0;
*real_fmt_id = MEDIA_FMT_QCELP;
break;
default:
ret = -EINVAL;
break;
}
pl_size:
*payload_size = sizeof(struct apm_module_param_data_t) +
sizeof(struct media_format_t) + format_size;
return ret;
}
int set_compressed_media_format(enum agm_media_format fmt_id,
struct media_format_t *media_fmt_hdr,
struct session_obj *sess_obj)
{
int ret = 0;
size_t fmt_size = 0;
switch (fmt_id) {
case AGM_FORMAT_MP3:
{
media_fmt_hdr->data_format = AGM_DATA_FORMAT_RAW_COMPRESSED ;
media_fmt_hdr->fmt_id = MEDIA_FMT_ID_MP3;
media_fmt_hdr->payload_size = 0;
break;
}
case AGM_FORMAT_AAC:
{
struct payload_media_fmt_aac_t *fmt_pl;
fmt_size = sizeof(struct payload_media_fmt_aac_t);
media_fmt_hdr->data_format = AGM_DATA_FORMAT_RAW_COMPRESSED ;
media_fmt_hdr->fmt_id = MEDIA_FMT_ID_AAC;
media_fmt_hdr->payload_size = fmt_size;
fmt_pl =
(struct payload_media_fmt_aac_t*)(((uint8_t*)media_fmt_hdr) +
sizeof(struct media_format_t));
memcpy(fmt_pl, &sess_obj->stream_config.codec.aac_dec,
fmt_size);
AGM_LOGD("AAC payload: fmt:%d, Obj_type:%d, ch:%d, SR:%d",
fmt_pl->aac_fmt_flag, fmt_pl->audio_obj_type,
fmt_pl->num_channels, fmt_pl->sample_rate);
break;
}
case AGM_FORMAT_FLAC:
{
struct payload_media_fmt_flac_t *fmt_pl;
fmt_size = sizeof(struct payload_media_fmt_flac_t);
media_fmt_hdr->data_format = AGM_DATA_FORMAT_RAW_COMPRESSED ;
media_fmt_hdr->fmt_id = MEDIA_FMT_ID_FLAC;
media_fmt_hdr->payload_size = fmt_size;
fmt_pl = (struct payload_media_fmt_flac_t*)(((uint8_t*)media_fmt_hdr) +
sizeof(struct media_format_t));
memcpy(fmt_pl, &sess_obj->stream_config.codec.flac_dec,
fmt_size);
AGM_LOGD("FLAC payload: ch:%d, sample_size:%d, SR:%d",
fmt_pl->num_channels, fmt_pl->sample_size, fmt_pl->sample_rate);
break;
}
case AGM_FORMAT_ALAC:
{
struct payload_media_fmt_alac_t *fmt_pl;
fmt_size = sizeof(struct payload_media_fmt_alac_t);
media_fmt_hdr->data_format = AGM_DATA_FORMAT_RAW_COMPRESSED ;
media_fmt_hdr->fmt_id = MEDIA_FMT_ID_ALAC;
media_fmt_hdr->payload_size = fmt_size;
fmt_pl = (struct payload_media_fmt_alac_t*)(((uint8_t*)media_fmt_hdr) +
sizeof(struct media_format_t));
memcpy(fmt_pl, &sess_obj->stream_config.codec.alac_dec,
fmt_size);
AGM_LOGD("ALAC payload: bit_depth:%d, ch:%d, SR:%d",
fmt_pl->bit_depth, fmt_pl->num_channels,
fmt_pl->sample_rate);
break;
}
case AGM_FORMAT_APE:
{
struct payload_media_fmt_ape_t *fmt_pl;
fmt_size = sizeof(struct payload_media_fmt_ape_t);
media_fmt_hdr->data_format = AGM_DATA_FORMAT_RAW_COMPRESSED ;
media_fmt_hdr->fmt_id = MEDIA_FMT_ID_APE;
media_fmt_hdr->payload_size = fmt_size;
fmt_pl = (struct payload_media_fmt_ape_t*)(((uint8_t*)media_fmt_hdr) +
sizeof(struct media_format_t));
memcpy(fmt_pl, &sess_obj->stream_config.codec.ape_dec,
fmt_size);
AGM_LOGD("APE payload: bit_width:%d, ch:%d, SR:%d",
fmt_pl->bit_width, fmt_pl->num_channels,
fmt_pl->sample_rate);
break;
}
case AGM_FORMAT_WMASTD:
{
struct payload_media_fmt_wmastd_t *fmt_pl;
fmt_size = sizeof(struct payload_media_fmt_wmastd_t);
media_fmt_hdr->data_format = AGM_DATA_FORMAT_RAW_COMPRESSED ;
media_fmt_hdr->fmt_id = MEDIA_FMT_ID_WMASTD;
media_fmt_hdr->payload_size = fmt_size;
fmt_pl = (struct payload_media_fmt_wmastd_t*)(((uint8_t*)media_fmt_hdr) +
sizeof(struct media_format_t));
memcpy(fmt_pl, &sess_obj->stream_config.codec.wma_dec,
fmt_size);
AGM_LOGD("WMA payload: fmt:%d, ch:%d, SR:%d, bit_width:%d",
fmt_pl->fmt_tag, fmt_pl->num_channels,
fmt_pl->sample_rate, fmt_pl->bits_per_sample);
break;
}
case AGM_FORMAT_WMAPRO:
{
struct payload_media_fmt_wmapro_t *fmt_pl;
fmt_size = sizeof(struct payload_media_fmt_wmapro_t);
media_fmt_hdr->data_format = AGM_DATA_FORMAT_RAW_COMPRESSED ;
media_fmt_hdr->fmt_id = MEDIA_FMT_ID_WMAPRO;
media_fmt_hdr->payload_size = fmt_size;
fmt_pl = (struct payload_media_fmt_wmapro_t*)(((uint8_t*)media_fmt_hdr) +
sizeof(struct media_format_t));
memcpy(fmt_pl, &sess_obj->stream_config.codec.wmapro_dec,
fmt_size);
AGM_LOGD("WMAPro payload: fmt:%d, ch:%d, SR:%d, bit_width:%d",
fmt_pl->fmt_tag, fmt_pl->num_channels,
fmt_pl->sample_rate, fmt_pl->bits_per_sample);
break;
}
default:
return -EINVAL;
}
return ret;
}
/**
*Configure placeholder decoder
*/
int configure_placeholder_dec(struct module_info *mod,
struct graph_obj *graph_obj)
{
int ret = 0;
struct gsl_key_vector tkv;
struct session_obj *sess_obj = NULL;
size_t payload_size = 0, real_fmt_id = 0;
AGM_LOGE("enter");
memset(&tkv, 0, sizeof(struct gsl_key_vector));
if (graph_obj == NULL) {
AGM_LOGE("invalid graph object");
goto done;
}
sess_obj = graph_obj->sess_obj;
/* 1. Configure placeholder decoder with Real ID */
ret = get_media_fmt_id_and_size(sess_obj->out_media_config.format,
&payload_size, &real_fmt_id);
if (ret) {
AGM_LOGD("module is not configured for format: %d\n",
sess_obj->out_media_config.format);
/* If ret is non-zero then placeholder module would be
* configured by client so return from here.
*/
ret = 0;
goto done;
}
tkv.num_kvps = 1;
tkv.kvp = calloc(tkv.num_kvps, sizeof(struct gsl_key_value_pair));
if (!tkv.kvp) {
AGM_LOGE("Not enough memory for tkv.kvp\n");
ret = -ENOMEM;
goto done;
}
tkv.kvp->key = MEDIA_FMT_ID;
tkv.kvp->value = real_fmt_id;
AGM_LOGD("Placeholder mod TKV key:%0x value: %0x", tkv.kvp->key,
tkv.kvp->value);
ret = gsl_set_config(graph_obj->graph_handle, (struct gsl_key_vector *)mod->gkv,
TAG_STREAM_PLACEHOLDER_DECODER, &tkv);
if (ret != 0) {
ret = ar_err_get_lnx_err_code(ret);
AGM_LOGE("set_config command failed with error: %d", ret);
goto done;
}
/* 2. Set output media format cfg for placeholder decoder */
ret = configure_output_media_format(mod, graph_obj);
if (ret != 0)
AGM_LOGE("output_media_format cfg failed: %d", ret);
done:
if (tkv.kvp)
free(tkv.kvp);
AGM_LOGE("exit, ret %d", ret);
return ret;
}
/**
*Configure placeholder encoder
*/
int configure_placeholder_enc(struct module_info *mod,
struct graph_obj *graph_obj)
{
int ret = 0;
struct gsl_key_vector tkv;
struct session_obj *sess_obj = NULL;
size_t payload_size = 0, real_fmt_id = 0;
AGM_LOGE("enter");
if (graph_obj == NULL) {
AGM_LOGE("invalid graph object");
return -EINVAL;
}
sess_obj = graph_obj->sess_obj;
/* 1. Configure placeholder encoder with Real ID */
ret = get_media_fmt_id_and_size(sess_obj->in_media_config.format,
&payload_size, &real_fmt_id);
if (ret) {
AGM_LOGD("module is not configured for format: %d\n",
sess_obj->in_media_config.format);
/* If ret is non-zero then placeholder module would be
* configured by client so return from here.
*/
return 0;
}
tkv.num_kvps = 1;
tkv.kvp = calloc(tkv.num_kvps, sizeof(struct gsl_key_value_pair));
if (!tkv.kvp) {
AGM_LOGE("Not enough memory for tkv.kvp\n");
return -ENOMEM;
}
tkv.kvp->key = MEDIA_FMT_ID;
tkv.kvp->value = real_fmt_id;
AGM_LOGD("Placeholder mod TKV key:%0x value: %0x", tkv.kvp->key,
tkv.kvp->value);
ret = gsl_set_config(graph_obj->graph_handle, (struct gsl_key_vector *)mod->gkv,
TAG_STREAM_PLACEHOLDER_ENCODER, &tkv);
if (tkv.kvp)
free(tkv.kvp);
if (ret != 0) {
ret = ar_err_get_lnx_err_code(ret);
AGM_LOGE("set_config command failed with error: %d", ret);
return ret;
}
return ret;
}
int configure_compress_shared_mem_ep(struct module_info *mod,
struct graph_obj *graph_obj)
{
int ret = 0;
struct session_obj *sess_obj = graph_obj->sess_obj;
struct media_format_t *media_fmt_hdr;
struct apm_module_param_data_t *header;
uint8_t *payload = NULL;
size_t payload_size = 0, real_fmt_id = 0;
size_t actual_param_sz = 0;
if (is_format_bypassed(sess_obj->out_media_config.format) ||
sess_obj->stream_config.sess_mode == AGM_SESSION_NON_TUNNEL) {
AGM_LOGI("bypass shared mem ep config for format %x or sess_mode %d",
sess_obj->out_media_config.format, sess_obj->stream_config.sess_mode);
return 0;
}
ret = get_media_fmt_id_and_size(sess_obj->out_media_config.format,
&payload_size, &real_fmt_id);
if (ret) {
AGM_LOGD("module is not configured for format: %d\n",
sess_obj->out_media_config.format);
/* If ret is non-zero then shared memory module would be
* configured by client so return from here.
*/
return 0;
}
/*
*We get the actual size of the PID being sent before the payload_size
*is updated for 8 byte alignment.
*/
actual_param_sz = payload_size - sizeof(struct apm_module_param_data_t);
/*ensure that the payloadsize is byte multiple atleast*/
ALIGN_PAYLOAD(payload_size, 8);
payload = calloc(1, (size_t)payload_size);
if (!payload) {
AGM_LOGE("Not enough memory for payload\n");
return -ENOMEM;
}
header = (struct apm_module_param_data_t*)payload;
media_fmt_hdr = (struct media_format_t*)(payload +
sizeof(struct apm_module_param_data_t));
header->module_instance_id = mod->miid;
header->param_id = PARAM_ID_MEDIA_FORMAT;
header->error_code = 0x0;
header->param_size = actual_param_sz;
ret = set_compressed_media_format(sess_obj->out_media_config.format,
media_fmt_hdr, sess_obj);
if (ret) {
AGM_LOGD("Shared mem EP is not configured for format: %d\n",
sess_obj->out_media_config.format);
/* If ret is non-zero then shared memory module would be
* configured by client so return from here.
*/
goto free_payload;
}
ret = gsl_set_custom_config(graph_obj->graph_handle, payload, payload_size);
if (ret != 0) {
ret = ar_err_get_lnx_err_code(ret);
AGM_LOGE("custom_config command for module %d failed with error %d",
mod->tag, ret);
}
free_payload:
free(payload);
return ret;
}
static int configure_compress_shared_mem_ep_datapath(struct module_info *mod,
struct graph_obj *graph_obj)
{
int ret = 0;
struct session_obj *sess_obj = graph_obj->sess_obj;
struct media_format_t *media_fmt_hdr;
uint8_t *payload = NULL;
size_t payload_size = 0, real_fmt_id = 0;
struct agm_buff buffer = {0};
size_t consumed_size = 0;
if (is_format_bypassed(sess_obj->out_media_config.format) ||
sess_obj->stream_config.sess_mode == AGM_SESSION_NON_TUNNEL) {
AGM_LOGI("bypass shared mem ep config for format %x or sess_mode %d",
sess_obj->out_media_config.format, sess_obj->stream_config.sess_mode);
return 0;
}
ret = get_media_fmt_id_and_size(sess_obj->out_media_config.format,
&payload_size, &real_fmt_id);
if (ret) {
AGM_LOGD("module is not configured for format: %d\n",
sess_obj->out_media_config.format);
/* If ret is non-zero then shared memory module would be
* configured by client so return from here.
*/
return 0;
}
payload_size = payload_size - sizeof(struct apm_module_param_data_t);
media_fmt_hdr = (struct media_format_t *) calloc(1, (size_t)payload_size);
if (!media_fmt_hdr) {
AGM_LOGE("Not enough memory for payload\n");
return -ENOMEM;
}
buffer.timestamp = 0x0;
buffer.flags = AGM_BUFF_FLAG_MEDIA_FORMAT;
buffer.size = payload_size;
buffer.addr = (uint8_t *)media_fmt_hdr;
ret = set_compressed_media_format(sess_obj->out_media_config.format,
media_fmt_hdr, sess_obj);
if (ret) {
AGM_LOGD("Shared mem EP is not configured for format: %d\n",
sess_obj->out_media_config.format);
/* If ret is non-zero then shared memory module would be
* configured by client so return from here.
*/
goto free_payload;
}
ret = graph_write(graph_obj, &buffer, &consumed_size);
if (ret != 0) {
ret = ar_err_get_lnx_err_code(ret);
AGM_LOGE("custom_config command for module %d failed with error %d",
mod->tag, ret);
}
free_payload:
free(media_fmt_hdr);
return ret;
}
int configure_pcm_shared_mem_ep(struct module_info *mod,
struct graph_obj *graph_obj)
{
int ret = 0;
struct session_obj *sess_obj = graph_obj->sess_obj;
struct media_format_t *media_fmt_hdr;
struct apm_module_param_data_t *header;
struct payload_media_fmt_pcm_t *media_fmt_payload;
uint8_t *payload = NULL;
size_t payload_size = 0;
int num_channels = MONO;
uint8_t *channel_map;
AGM_LOGD("entry mod tag %x miid %x mid %x",mod->tag, mod->miid, mod->mid);
num_channels = sess_obj->out_media_config.channels;
payload_size = sizeof(struct apm_module_param_data_t) +
sizeof(struct media_format_t) +
sizeof(struct payload_media_fmt_pcm_t) +
sizeof(uint8_t)*num_channels;
/*ensure that the payloadszie is byte multiple atleast*/
ALIGN_PAYLOAD(payload_size, 8);
payload = calloc(1,(size_t)payload_size);
if (!payload) {
AGM_LOGE("Not enough memory for payload");
ret = -ENOMEM;
goto done;
}
header = (struct apm_module_param_data_t*)payload;
media_fmt_hdr = (struct media_format_t*)(payload +
sizeof(struct apm_module_param_data_t));
media_fmt_payload = (struct payload_media_fmt_pcm_t*)(payload +
sizeof(struct apm_module_param_data_t) +
sizeof(struct media_format_t));
channel_map = (uint8_t*)(payload + sizeof(struct apm_module_param_data_t) +
sizeof(struct media_format_t) +
sizeof(struct payload_media_fmt_pcm_t));
header->module_instance_id = mod->miid;
header->param_id = PARAM_ID_MEDIA_FORMAT;
header->error_code = 0x0;
header->param_size = sizeof(struct media_format_t) +
sizeof(struct payload_media_fmt_pcm_t) +
sizeof(uint8_t)*num_channels;
media_fmt_hdr->data_format = AGM_DATA_FORMAT_FIXED_POINT;
media_fmt_hdr->fmt_id = MEDIA_FMT_ID_PCM;
media_fmt_hdr->payload_size = (uint32_t)(sizeof(payload_media_fmt_pcm_t) +
sizeof(uint8_t) * num_channels);
media_fmt_payload->endianness = PCM_LITTLE_ENDIAN;
media_fmt_payload->bit_width = get_pcm_bit_width(sess_obj->out_media_config.format);
media_fmt_payload->sample_rate = sess_obj->out_media_config.rate;
/**
*alignment field is referred to only in case where bit width is
*24 and bits per sample is 32, tiny alsa only supports 24 bit
*in 32 word size in LSB aligned mode(AGM_FORMAT_PCM_S24_LE).
*Hence we hardcode this to PCM_LSB_ALIGNED;
*/
media_fmt_payload->alignment = PCM_LSB_ALIGNED;
media_fmt_payload->num_channels = num_channels;
media_fmt_payload->bits_per_sample =
GET_BITS_PER_SAMPLE(sess_obj->out_media_config.format,
media_fmt_payload->bit_width);
media_fmt_payload->q_factor = GET_Q_FACTOR(sess_obj->out_media_config.format,
media_fmt_payload->bit_width);
/**
*#TODO:As of now channel_map is not part of media_config
*ADD channel map part as part of the session/device media config
*structure and use that channel map if set by client otherwise
* use the default channel map
*/
get_default_channel_map(channel_map, num_channels);
ret = gsl_set_custom_config(graph_obj->graph_handle, payload, payload_size);
if (ret != 0) {
ret = ar_err_get_lnx_err_code(ret);
AGM_LOGE("custom_config command for module %d failed with error %d",
mod->tag, ret);
}
done:
if (payload) {
free(payload);
}
AGM_LOGD("exit, ret %d", ret);
return ret;
}
int configure_wr_shared_mem_ep(struct module_info *mod,
struct graph_obj *graph_obj)
{
int ret = 0;
struct session_obj *sess_obj = graph_obj->sess_obj;
/*
*This is for the write shared mem endpoint,
*hence we use out media config.
*/
if (is_format_pcm(sess_obj->out_media_config.format))
ret = configure_pcm_shared_mem_ep(mod, graph_obj);
else {
if (graph_obj->state != STARTED)
ret = configure_compress_shared_mem_ep(mod, graph_obj);
else
ret = configure_compress_shared_mem_ep_datapath(mod, graph_obj);
}
if (ret)
return ret;
return 0;
}
int configure_rd_shared_mem_ep(struct module_info *mod,
struct graph_obj *graph_obj)
{
int ret = 0;
struct session_obj *sess_obj = graph_obj->sess_obj;
struct apm_module_param_data_t *header;
struct param_id_rd_sh_mem_cfg_t *rd_sh_mem_cfg;
uint8_t *payload = NULL;
size_t payload_size = 0;
/*
*Note: read shared mem ep is configured only in case of non-tunnel
*decode sessions where the client has configured the session with
*AGM_SESSION_FLAG_INBAND_SRCM flag
*In case of non-tunnel encode sessions, we set the num_frames_per_buff cfg
*as a part of calibration itself.
*/
if (!(sess_obj->stream_config.sess_flags & AGM_SESSION_FLAG_INBAND_SRCM))
goto done;
AGM_LOGD("entry mod tag %x miid %x mid %x sess_flags %x",mod->tag, mod->miid, mod->mid,
sess_obj->stream_config.sess_flags);
payload_size = sizeof(struct apm_module_param_data_t) +
sizeof(struct param_id_rd_sh_mem_cfg_t);
/*ensure that the payloadszie is byte multiple atleast*/
ALIGN_PAYLOAD(payload_size, 8);
payload = calloc(1,(size_t)payload_size);
if (!payload) {
AGM_LOGE("Not enough memory for payload");
ret = -ENOMEM;
goto done;
}
header = (struct apm_module_param_data_t*)payload;
rd_sh_mem_cfg = (struct param_id_rd_sh_mem_cfg_t *)(payload
+ sizeof(struct apm_module_param_data_t));
header->module_instance_id = mod->miid;
header->param_id = PARAM_ID_RD_SH_MEM_CFG;
header->error_code = 0x0;
header->param_size = sizeof(struct param_id_rd_sh_mem_cfg_t);
/*
*In NT mode session in_media config represents config for data being captured
*Hence for NT Mode decode it would mean PCM data.
*/
if (is_format_pcm(sess_obj->in_media_config.format))
rd_sh_mem_cfg->num_frames_per_buffer = 0x0; /*As many frames as possible*/
else
/*TODO:This is encode usecase hence ideally client wont enable SRCM event
*Even if the client wants it enabled, then we configure 1 frame every for
*every read call;
*/
rd_sh_mem_cfg->num_frames_per_buffer = 0x1;
rd_sh_mem_cfg->metadata_control_flags = 0x2; /*ENABLE_MEDIA_FORMAT_MD*/
ret = gsl_set_custom_config(graph_obj->graph_handle, payload, payload_size);
if (ret != 0) {
ret = ar_err_get_lnx_err_code(ret);
AGM_LOGE("custom_config command for module %d failed with error %d",
mod->tag, ret);
}
free(payload);
done:
AGM_LOGD("exit, ret %d", ret);
return ret;
}
int configure_spr(struct module_info *spr_mod,
struct graph_obj *graph_obj)
{
int ret = 0;
struct listnode *node = NULL;
struct module_info *mod;
struct apm_module_param_data_t *header;
struct param_id_spr_delay_path_end_t *spr_hwep_delay;
uint8_t *payload = NULL;
size_t payload_size = 0;
AGM_LOGD("SPR module IID %x", spr_mod->miid);
graph_obj->spr_miid = spr_mod->miid;
payload_size = sizeof(struct apm_module_param_data_t) +
sizeof(struct param_id_spr_delay_path_end_t);
ALIGN_PAYLOAD(payload_size, 8);
payload = calloc(1, (size_t)payload_size);
if (!payload) {
AGM_LOGE("No memory to allocate for payload");
ret = -ENOMEM;
goto done;
}
header = (struct apm_module_param_data_t*)payload;
spr_hwep_delay = (struct param_id_spr_delay_path_end_t *)(payload
+ sizeof(struct apm_module_param_data_t));
header->module_instance_id = spr_mod->miid;
header->param_id = PARAM_ID_SPR_DELAY_PATH_END;
header->error_code = 0x0;
header->param_size = sizeof(struct param_id_spr_delay_path_end_t);
list_for_each(node, &graph_obj->tagged_mod_list) {
mod = node_to_item(node, module_info_t, list);
if (mod->tag == DEVICE_HW_ENDPOINT_RX) {
AGM_LOGD("HW EP module IID %x", mod->miid);
spr_hwep_delay->module_instance_id = mod->miid;
ret = gsl_set_custom_config(graph_obj->graph_handle, payload, payload_size);
if (ret !=0) {
ret = ar_err_get_lnx_err_code(ret);
AGM_LOGE("graph_set_custom_config failed %d", ret);
}
}
}
done:
if (payload)
free(payload);
return ret;
}
int configure_gapless(struct module_info *gapless_mod,
struct graph_obj *gph_obj)
{
int ret = 0;
struct gsl_cmd_register_custom_event *reg_ev_payload = NULL;
size_t payload_size = 0;
AGM_LOGD("GAPLESS module \n");
if (gph_obj->graph_handle == NULL) {
pthread_mutex_unlock(&gph_obj->lock);
AGM_LOGE("invalid graph handle\n");
ret = -EINVAL;
goto done;
}
payload_size = sizeof(struct gsl_cmd_register_custom_event);
reg_ev_payload = calloc(1, payload_size);
if (reg_ev_payload == NULL) {
pthread_mutex_unlock(&gph_obj->lock);
AGM_LOGE("calloc failed for reg_ev_payload\n");
ret = -ENOMEM;
goto done;
}
AGM_LOGD("GAPLESS module IID = %d\n", gapless_mod->miid);
reg_ev_payload->event_id = EVENT_ID_EARLY_EOS;
// Write shared memory end point module IID
reg_ev_payload->module_instance_id = gapless_mod->miid;
// No payload for early eos registration
reg_ev_payload->event_config_payload_size = 0;
reg_ev_payload->is_register = 1;
ret = gsl_ioctl(gph_obj->graph_handle, GSL_CMD_REGISTER_CUSTOM_EVENT,
reg_ev_payload, payload_size);
if (ret != 0) {
ret = ar_err_get_lnx_err_code(ret);
AGM_LOGE("Early EOS event registration failed with error %d\n", ret);
}
done:
if (reg_ev_payload)
free(reg_ev_payload);
return ret;
}
module_info_t stream_module_list[] = {
{
.module = MODULE_PCM_ENCODER,
.tag = STREAM_PCM_ENCODER,
.configure = configure_pcm_encoder_params,
},
{
.module = MODULE_PCM_DECODER,
.tag = STREAM_PCM_DECODER,
.configure = configure_output_media_format,
},
{
.module = MODULE_PLACEHOLDER_ENCODER,
.tag = TAG_STREAM_PLACEHOLDER_ENCODER,
.configure = configure_placeholder_enc,
},
{
.module = MODULE_PLACEHOLDER_DECODER,
.tag = TAG_STREAM_PLACEHOLDER_DECODER,
.configure = configure_placeholder_dec,
},
{
.module = MODULE_PCM_CONVERTER,
.tag = STREAM_PCM_CONVERTER,
.configure = configure_output_media_format,
},
{
.module = MODULE_WR_SHARED_MEM,
.tag = STREAM_INPUT_MEDIA_FORMAT,
.configure = configure_wr_shared_mem_ep,
},
{
.module = MODULE_STREAM_PAUSE,
.tag = TAG_PAUSE,
.configure = NULL,
},
{
.module = MODULE_STREAM_SPR,
.tag = TAG_STREAM_SPR,
.configure = configure_spr,
},
{
.module = MODULE_STREAM_GAPLESS,
.tag = MODULE_GAPLESS,
.configure = configure_gapless,
},
{
.module = MODULE_RD_SHARED_MEM,
.tag = RD_SHMEM_ENDPOINT,
.configure = configure_rd_shared_mem_ep,
},
};
module_info_t hw_ep_module[] = {
{
.module = MODULE_HW_EP_RX,
.tag = DEVICE_HW_ENDPOINT_RX,
.configure = configure_hw_ep,
},
{
.module = MODULE_HW_EP_TX,
.tag = DEVICE_HW_ENDPOINT_TX,
.configure = configure_hw_ep,
}
};
void get_stream_module_list_array(module_info_t **info, size_t *size)
{
*size = sizeof(stream_module_list);
*info = stream_module_list;
}
void get_hw_ep_module_list_array(module_info_t **info, size_t *size)
{
*size = sizeof(hw_ep_module);
*info = hw_ep_module;
}