diff --git a/src/audio/kpb.c b/src/audio/kpb.c index 3187446b47e6..2d52a35d7928 100644 --- a/src/audio/kpb.c +++ b/src/audio/kpb.c @@ -38,6 +38,7 @@ #include #include #include +#include #include #include #include @@ -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; @@ -141,6 +144,23 @@ 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); +#ifdef CONFIG_IPC_MAJOR_4 +/* 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 int 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); +/* 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); +#endif static uint64_t kpb_task_deadline(void *data) { @@ -376,7 +396,8 @@ static int kpb_unbind(struct comp_dev *dev, void *data) else kpb->host_sink = NULL; - return 0; + /* Clear fmt config */ + return clear_fmt_modules_list(&kpb->fmt_device_list, bu->extension.r.src_queue); } #else /* CONFIG_IPC_MAJOR_4 */ @@ -2378,15 +2399,177 @@ static int kpb_set_micselect(struct comp_dev *dev, const void *data, return 0; } +#ifdef CONFIG_IPC_MAJOR_4 + +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) +{ + /* clear 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 inline int alloc_fmt_module_list_item(struct kpb_fmt_dev_list *fmt_device_list, + struct comp_dev *mi_ptr, struct comp_dev ***item) +{ + /* -1 means we did not find the slot yet */ + 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) + 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 to 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) +{ + int ret; + struct comp_dev *dev; + struct kpb_fmt_dev_list *fmt_device_list = + &((struct comp_data *)comp_get_drvdata(kpb_dev))->fmt_device_list; + + 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) { + uint32_t comp_id = IPC4_COMP_ID(modules_to_prepare->dev_ids[mod_idx].module_id, + modules_to_prepare->dev_ids[mod_idx].instance_id); + + dev = ipc4_get_comp_dev(comp_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; + } + return 0; +} + +static int clear_fmt_modules_list(struct kpb_fmt_dev_list *fmt_device_list, + uint32_t outpin_idx) +{ + if (outpin_idx >= KPB_MAX_SINK_CNT) + return -EINVAL; + + devicelist_reset(&fmt_device_list->device_list[outpin_idx], true); + return 0; +} + +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 configure_fast_mode_task(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) + return -EINVAL; + + 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; + + ret = clear_fmt_modules_list(&priv_data->fmt_device_list, pin); + if (ret) + return -EINVAL; + + /* When modules count IS 0 we only need to remove modules from Fast Mode. */ + if (cfg->number_of_modules > 0) { + ret = prepare_fmt_modules_list(kpb_dev, pin, cfg); + if (!ret) + ret = register_modules_list(&priv_data->fmt, + &priv_data->fmt_device_list.device_list[pin], + pin); + } + return ret; +} +#endif + static int kpb_set_large_config(struct comp_dev *dev, uint32_t param_id, bool first_block, bool last_block, uint32_t data_offset, const char *data) { - /* We can use extended param id for both extended - * and standard param id - */ + /* We can use extended param id for both extended and standard param id */ union ipc4_extended_param_id extended_param_id; comp_info(dev, "kpb_set_large_config()"); @@ -2394,6 +2577,18 @@ static int kpb_set_large_config(struct comp_dev *dev, uint32_t param_id, extended_param_id.full = param_id; switch (extended_param_id.part.parameter_type) { +#ifdef CONFIG_IPC_MAJOR_4 + case KP_BUF_CFG_FM_MODULE: { + /* Modules count equals 0 is a special case in which we want to clear list for + * given pin. Reference FW also allowed for cfg/data to be NULL, but this is no + * longer the case. + */ + const struct kpb_task_params *cfg = (struct kpb_task_params *)data; + uint32_t outpin_id = extended_param_id.part.parameter_instance; + + return configure_fast_mode_task(dev, cfg, outpin_id); + } +#endif case KP_BUF_CLIENT_MIC_SELECT: return kpb_set_micselect(dev, data, data_offset); default: diff --git a/src/include/ipc4/kpb.h b/src/include/ipc4/kpb.h index ad558bc4ac25..0c56942313c3 100644 --- a/src/include/ipc4/kpb.h +++ b/src/include/ipc4/kpb.h @@ -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 + diff --git a/src/include/sof/audio/kpb.h b/src/include/sof/audio/kpb.h index 852659f86c98..dbec1207c467 100644 --- a/src/include/sof/audio/kpb.h +++ b/src/include/sof/audio/kpb.h @@ -43,6 +43,7 @@ struct comp_buffer; (KPB_SAMPLE_CONTAINER_SIZE(sw) / 8) * KPB_MAX_BUFF_TIME * \ (channels_number)) #define KPB_MAX_NO_OF_CLIENTS 2 +#define KPB_MAX_SINK_CNT (1 + KPB_MAX_NO_OF_CLIENTS) #define KPB_NO_OF_HISTORY_BUFFERS 2 /**< no of internal buffers */ #define KPB_ALLOCATION_STEP 0x100 #define KPB_NO_OF_MEM_POOLS 3 @@ -66,6 +67,9 @@ struct comp_buffer; * i.e. number of max supported channels - reference channels) */ #define KPB_MAX_MICSEL_CHANNELS 4 +/* Used in FMT */ +#define FAST_MODE_TASK_MAX_MODULES_COUNT 16 +#define REALTIME_PIN_ID 0 /** All states below as well as relations between them are documented in * the sof-dosc in [kpbm-state-diagram] @@ -159,18 +163,88 @@ struct history_data { struct history_buffer *c_hb; /**< current buffer used for writing */ }; -enum ipc4_kpb_module_config_params { - /* 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, -}; +/* moved to ipc4/kpb.h */ +/* enum ipc4_kpb_module_config_params */ /* Stores KPB mic selector config */ struct kpb_micselector_config { /* channel bit set to 1 implies channel selection */ uint32_t mask; }; + +struct kpb_task_params { + /* If largeconfigset is set to KP_POS_IN_BUFFER then number of modules must + * correspond to number of modules between kpb and copier attached to hostdma. + * Once draining path is configured, cannot be reinitialized/changed. + */ + uint32_t number_of_modules; + struct { + uint16_t module_id; + uint16_t instance_id; + } dev_ids[1]; +}; + +/* fmt namespace: */ +#define FAST_MODE_TASK_MAX_LIST_COUNT 5 + +struct fast_mode_task { + /*! Array of pointers to all module lists to be processed. */ + struct device_list *device_list[FAST_MODE_TASK_MAX_LIST_COUNT]; +}; + +/* The +1 is here because we also push the kbp device + * handle in addition to the max number of modules + */ +#define DEVICE_LIST_SIZE (FAST_MODE_TASK_MAX_MODULES_COUNT + 1) + +/* Devicelist type + * In Reference FW KPB used Bi-dir lists to store modules for FMT. It is possible that lists are + * not necessary, but in case it might be wrong, here we use an array + * with a list interface to switch it to a list easily. + */ +struct device_list { + struct comp_dev **devs[DEVICE_LIST_SIZE]; + /* number of items AND index of next empty box */ + size_t count; +}; + +/* + * + * + * KPB FMT config set steps: + * 1. Get dev_ids of module instances from IPC + * 2. Alloc this kpb module instance on kpb_list_item, save address of where it was allocated + * 3. Push the address on dev_list.device_list + * 2. For each dev_id get device handler(module instance) + * 3. Alloc device handler in modules_list_item, save address of where it was allocated + * 4. Register this address in fmt.device_list + * + * Pointer structure: + * + * COMP_DEVS + * ^ + * | + * dev_list.modules_list_item(comp_dev* ) + * ^ + * | + * dev_list.device_list(comp_dev**) + * ^ + * | + * fmt.device_list(device_list*) + * + * + */ + +/* KpbFastModeTaskModulesList Namespace */ +struct kpb_fmt_dev_list { + /*! Array of all module lists to be processed. */ + struct device_list device_list[FAST_MODE_TASK_MAX_LIST_COUNT]; + /* One for each sinkpin. */ + struct comp_dev *kpb_list_item[KPB_MAX_SINK_CNT]; + struct comp_dev *modules_list_item[FAST_MODE_TASK_MAX_MODULES_COUNT]; + struct comp_dev *kpb_mi_ptr; +}; + #ifdef UNIT_TEST void sys_comp_kpb_init(void); #endif