Skip to content

Commit

Permalink
kpb: Implement configuration part of fast mode task
Browse files Browse the repository at this point in the history
Fix an issue regarding fast mode task configuration. DSP returned an error
when Host sent Vendor Config Set IPC message for configuring FMT. This is
due to fact that FMT is not yet implemented. This patch implements handling
of the configuration IPC for FMT in KPB module. KPB now saves the list of
module instances to be processed by FMT. This is the first step for
implementing FMT functionality.

Signed-off-by: Tobiasz Dryjanski <[email protected]>
  • Loading branch information
tobonex committed Aug 22, 2023
1 parent 8568c28 commit ccb5694
Show file tree
Hide file tree
Showing 4 changed files with 327 additions and 6 deletions.
234 changes: 234 additions & 0 deletions src/audio/kpb.c
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@
#include <rtos/string.h>
#include <sof/ut.h>
#include <ipc/topology.h>
#include <ipc4/module.h>
#include <ipc4/kpb.h>
#include <user/kpb.h>
#include <user/trace.h>
Expand Down Expand Up @@ -106,6 +107,8 @@ struct comp_data {
uint32_t num_of_in_channels;
uint32_t offsets[KPB_MAX_MICSEL_CHANNELS];
struct kpb_micselector_config mic_sel;
struct kpb_fmt_dev_list fmt_device_list;
struct fast_mode_task fmt;

#if CONFIG_AMS
uint32_t kpd_uuid_id;
Expand Down Expand Up @@ -141,6 +144,22 @@ static inline bool validate_host_params(struct comp_dev *dev,
size_t hb_size_req);
static inline void kpb_change_state(struct comp_data *kpb,
enum kpb_state state);
/* KpbFastModeTaskModulesList Namespace */
static inline int alloc_fmt_module_list_item(struct kpb_fmt_dev_list *fmt_device_list,
struct comp_dev *mi_ptr, struct comp_dev ***item);
static void clear_fmt_modules_list(struct kpb_fmt_dev_list *fmt_device_list,
uint32_t outpin_idx);
static int prepare_fmt_modules_list(struct comp_dev *kpb_dev, uint32_t outpin_idx,
const struct kpb_task_params *modules_to_prepare,
struct comp_dev **last_copier_mi);
/* FMT Namespace */
static int register_modules_list(struct fast_mode_task *fmt,
struct device_list *new_list, size_t list_idx);
static int unregister_modules_list(struct fast_mode_task *fmt,
struct device_list *list_to_remove, size_t list_idx);
/* Devicelist */
static int devicelist_push(struct device_list *devlist, struct comp_dev **dev);
static void devicelist_reset(struct device_list *devlist, bool remove_items);

static uint64_t kpb_task_deadline(void *data)
{
Expand Down Expand Up @@ -376,6 +395,8 @@ static int kpb_unbind(struct comp_dev *dev, void *data)
else
kpb->host_sink = NULL;

/* Clear fmt config */
clear_fmt_modules_list(&kpb->fmt_device_list, bu->extension.r.src_queue);
return 0;
}

Expand Down Expand Up @@ -2378,6 +2399,192 @@ static int kpb_set_micselect(struct comp_dev *dev, const void *data,
return 0;
}

static int devicelist_push(struct device_list *devlist, struct comp_dev **dev)
{
if (devlist->count != DEVICE_LIST_SIZE) {
devlist->devs[devlist->count] = dev;
devlist->count++;
return 0;
}
return -EINVAL;
}

static void devicelist_reset(struct device_list *devlist, bool remove_items)
{
/* deallocate items */
if (remove_items) {
for (int i = 0; i < DEVICE_LIST_SIZE; i++)
*devlist->devs[i] = NULL;
}
/* zero the pointers */
for (int i = 0; i < DEVICE_LIST_SIZE; i++)
devlist->devs[i] = NULL;

devlist->count = 0;
}

static struct comp_dev *get_dev_from_mi_id(uint32_t module_id, uint32_t instance_id)
{
struct comp_dev *dev = NULL;
/* for now considering only IPC4 */
#ifdef CONFIG_IPC_MAJOR_4
uint32_t comp_id = IPC4_COMP_ID(module_id, instance_id);

dev = ipc4_get_comp_dev(comp_id);
#endif
return dev;
}

static inline int alloc_fmt_module_list_item(struct kpb_fmt_dev_list *fmt_device_list,
struct comp_dev *mi_ptr, struct comp_dev ***item)
{
int first_empty_slot_idx = -1;

for (size_t module_slot_idx = 0; module_slot_idx < FAST_MODE_TASK_MAX_MODULES_COUNT;
++module_slot_idx){
/* check if module already added */
if (fmt_device_list->modules_list_item[module_slot_idx] == mi_ptr)
/* was ADSP_INVALID_REQUEST; */
return -EINVAL;
/* finding first available empty slot */
if (first_empty_slot_idx < 0 &&
!fmt_device_list->modules_list_item[module_slot_idx])
first_empty_slot_idx = module_slot_idx;
}
/* add item first available empty slot */
if (first_empty_slot_idx >= 0) {
fmt_device_list->modules_list_item[first_empty_slot_idx] = mi_ptr;
*item = &fmt_device_list->modules_list_item[first_empty_slot_idx];
return 0;
}
return -ENOMEM;
}

static int prepare_fmt_modules_list(struct comp_dev *kpb_dev,
uint32_t outpin_idx,
const struct kpb_task_params *modules_to_prepare,
struct comp_dev **last_copier_mi)
{
if (!kpb_dev)
return -EINVAL;
if (!modules_to_prepare)
return -EINVAL;
if (!last_copier_mi)
return -EINVAL;
if (outpin_idx >= KPB_MAX_SINK_CNT)
return -EINVAL;

int ret = 0;
struct comp_dev *dev = NULL;
struct kpb_fmt_dev_list *fmt_device_list =
&((struct comp_data *)comp_get_drvdata(kpb_dev))->fmt_device_list;

if (modules_to_prepare->number_of_modules != 0) {
fmt_device_list->kpb_list_item[outpin_idx] = kpb_dev;
ret = devicelist_push(&fmt_device_list->device_list[outpin_idx],
&fmt_device_list->kpb_list_item[outpin_idx]);
if (ret < 0)
return ret;
}
for (size_t mod_idx = 0; mod_idx < modules_to_prepare->number_of_modules; ++mod_idx) {
dev = get_dev_from_mi_id(modules_to_prepare->dev_ids[mod_idx].module_id,
modules_to_prepare->dev_ids[mod_idx].instance_id);

if (!dev)
return -EINVAL;

struct comp_dev **new_list_item_ptr;

ret = alloc_fmt_module_list_item(fmt_device_list, dev, &new_list_item_ptr);
if (ret < 0)
return ret;
*new_list_item_ptr = dev;
ret = devicelist_push(&fmt_device_list->device_list[outpin_idx],
new_list_item_ptr);
if (ret < 0)
return ret;
}

*last_copier_mi = dev;

return ret;
}

static void clear_fmt_modules_list(struct kpb_fmt_dev_list *fmt_device_list,
uint32_t outpin_idx)
{
/* Note: this should be validated in layer above. */
assert(outpin_idx < KPB_MAX_SINK_CNT);

devicelist_reset(&fmt_device_list->device_list[outpin_idx], true);
}

static int unregister_modules_list(struct fast_mode_task *fmt,
struct device_list *list_to_remove, size_t list_idx)
{
if (list_to_remove == fmt->device_list[list_idx]) {
fmt->device_list[list_idx] = NULL;
return 0;
}
if (!fmt->device_list[list_idx]) {
/* Nothing to do here */
return 0;
}
return -EINVAL;
}

/* Comment from Old FW, may be outdated:
* Important: function below should be called only from within critical section
* (Goto KPB for more details)
*/
static int register_modules_list(struct fast_mode_task *fmt,
struct device_list *new_list, size_t list_idx)
{
if (list_idx >= ARRAY_SIZE(fmt->device_list))
return -EINVAL;

/* Check if slot is free */
if (!fmt->device_list[list_idx]) {
fmt->device_list[list_idx] = new_list;
return 0;
}
if (new_list == fmt->device_list[list_idx]) {
/* Already registered. */
return 0;
}
/* was ADSP_ALREADY_IN_USE */
return -EINVAL;
}

static int ConfigureFastModeTask(struct comp_dev *kpb_dev, const struct kpb_task_params *cfg,
size_t pin)
{
if (pin >= KPB_MAX_SINK_CNT || pin == REALTIME_PIN_ID || cfg->dev_ids <= 0)
return -EINVAL;

/* not sure if this var is needed for anything */
struct comp_dev *last_copier_ptr = NULL;
struct comp_data *priv_data = (struct comp_data *)comp_get_drvdata(kpb_dev);
int ret = unregister_modules_list(&priv_data->fmt,
&priv_data->fmt_device_list.device_list[pin],
pin);
if (!ret)
return -EINVAL;

clear_fmt_modules_list(&priv_data->fmt_device_list, pin);

/* When modules count IS 0 we only need to remove modules from Fast Mode. */
if (cfg && cfg->number_of_modules > 0) {
if (ret == 0)
ret = prepare_fmt_modules_list(kpb_dev, pin, cfg, &last_copier_ptr);
if (ret == 0)
ret = register_modules_list(&priv_data->fmt,
&priv_data->fmt_device_list.device_list[pin],
pin);
}
return ret;
}

static int kpb_set_large_config(struct comp_dev *dev, uint32_t param_id,
bool first_block,
bool last_block,
Expand All @@ -2386,14 +2593,41 @@ static int kpb_set_large_config(struct comp_dev *dev, uint32_t param_id,
{
/* We can use extended param id for both extended
* and standard param id
* Also we have to smuggle a struct through the char pointer
*/
const struct byte_array_simple *array = (struct byte_array_simple *)data;
union ipc4_extended_param_id extended_param_id;

comp_info(dev, "kpb_set_large_config()");

extended_param_id.full = param_id;

switch (extended_param_id.part.parameter_type) {
case KP_BUF_CFG_FM_MODULE: {
/* Old FW comment:
* Modules count equals 0 is a special case in which we want to clear list for
* given pin. In case of that however dataAs<KpBufferingTaskParams> will
* return NULL. To avoid this we first checking only modules count first
* and full config after that. Other solution would be for driver to always
* send 12 bytes even if there is no module on list - which is also not
* very elegant.
*/
const struct kpb_task_params *cfg = NULL;
/* get first dword from payload, dword size field */
uint32_t nr_of_modules = *(uint32_t *)array->data;
uint32_t outpin_id = extended_param_id.part.parameter_instance;

if (nr_of_modules != 0) {
cfg = (struct kpb_task_params *)array->data;
if (!cfg)
return -EINVAL;
}

if (outpin_id >= KPB_MAX_SINK_CNT)
return -EINVAL;

return ConfigureFastModeTask(dev, cfg, outpin_id);
}
case KP_BUF_CLIENT_MIC_SELECT:
return kpb_set_micselect(dev, data, data_offset);
default:
Expand Down
12 changes: 12 additions & 0 deletions src/include/ipc4/kpb.h
Original file line number Diff line number Diff line change
Expand Up @@ -19,4 +19,16 @@
struct ipc4_kpb_module_cfg {
struct ipc4_base_module_cfg base_cfg;
} __packed __aligned(4);

/* For the sake of compatibility, do not change IDs, only add new ones.*/
enum ipc4_kpb_module_config_params {
/*! Configure the module ID's which would be part of the Fast mode tasks */
KP_BUF_CFG_FM_MODULE = 1,
/* Mic selector for client - sets microphone id for real time sink mic selector
* IPC4-compatible ID - please do not change the number
*/
KP_BUF_CLIENT_MIC_SELECT = 11,
};

#endif

Loading

0 comments on commit ccb5694

Please sign in to comment.