diff --git a/asoc/msm-lsm-client.c b/asoc/msm-lsm-client.c index 96fbee5bde90..017323161dcd 100644 --- a/asoc/msm-lsm-client.c +++ b/asoc/msm-lsm-client.c @@ -499,38 +499,71 @@ static int msm_lsm_get_conf_levels(struct lsm_client *client, { int rc = 0; - if (client->num_confidence_levels == 0) { - pr_debug("%s: no confidence levels provided\n", - __func__); - client->confidence_levels = NULL; - goto done; - } - - client->confidence_levels = - kzalloc((sizeof(uint8_t) * client->num_confidence_levels), - GFP_KERNEL); - if (!client->confidence_levels) { - pr_err("%s: No memory for confidence\n" - "levels num of level from user = %d\n", - __func__, client->num_confidence_levels); - rc = -ENOMEM; + if (client->num_sound_models != 0) { + if (client->num_keywords == 0) { + pr_debug("%s: no number of confidence_values provided\n", + __func__); + client->multi_snd_model_confidence_levels = NULL; goto done; - } + } - if (copy_from_user(client->confidence_levels, - conf_levels_ptr, - client->num_confidence_levels)) { - pr_err("%s: copy from user failed, size = %d\n", - __func__, client->num_confidence_levels); - rc = -EFAULT; - goto copy_err; - } + client->multi_snd_model_confidence_levels = + kzalloc((sizeof(uint32_t) * client->num_keywords), + GFP_KERNEL); + if (!client->multi_snd_model_confidence_levels) { + pr_err("%s: No memory for confidence\n" + "levels num of level from user = %d\n", + __func__, client->num_keywords); + rc = -ENOMEM; + goto done; + } + if (copy_from_user((u8 *)client->multi_snd_model_confidence_levels, + conf_levels_ptr, + sizeof(uint32_t) * client->num_keywords)) { + pr_err("%s: copy from user failed, number of keywords = %d\n", + __func__, client->num_keywords); + rc = -EFAULT; + goto copy_err; + } + } else { + if (client->num_confidence_levels == 0) { + pr_debug("%s: no confidence levels provided\n", + __func__); + client->confidence_levels = NULL; + goto done; + } + + client->confidence_levels = + kzalloc((sizeof(uint8_t) * client->num_confidence_levels), + GFP_KERNEL); + if (!client->confidence_levels) { + pr_err("%s: No memory for confidence\n" + "levels num of level from user = %d\n", + __func__, client->num_confidence_levels); + rc = -ENOMEM; + goto done; + } + + if (copy_from_user(client->confidence_levels, + conf_levels_ptr, + client->num_confidence_levels)) { + pr_err("%s: copy from user failed, size = %d\n", + __func__, client->num_confidence_levels); + rc = -EFAULT; + goto copy_err; + } + } return rc; copy_err: - kfree(client->confidence_levels); - client->confidence_levels = NULL; + if (client->num_sound_models != 0) { + kfree(client->multi_snd_model_confidence_levels); + client->multi_snd_model_confidence_levels = NULL; + } else { + kfree(client->confidence_levels); + client->confidence_levels = NULL; + } done: return rc; @@ -652,35 +685,67 @@ static int msm_lsm_set_conf(struct snd_pcm_substream *substream, struct snd_soc_pcm_runtime *rtd = substream->private_data; int rc = 0; - if (p_info->param_size > MAX_NUM_CONFIDENCE) { - dev_err(rtd->dev, - "%s: invalid confidence levels %d\n", - __func__, p_info->param_size); - return -EINVAL; - } + if (p_info->param_type == LSM_MULTI_SND_MODEL_CONFIDENCE_LEVELS) { + if (p_info->param_size > MAX_KEYWORDS_SUPPORTED) { + dev_err(rtd->dev, + "%s: invalid number of snd_model keywords %d, the max is %d\n", + __func__, p_info->param_size, MAX_KEYWORDS_SUPPORTED); + return -EINVAL; + } - prtd->lsm_client->num_confidence_levels = - p_info->param_size; - rc = msm_lsm_get_conf_levels(prtd->lsm_client, - p_info->param_data); - if (rc) { - dev_err(rtd->dev, - "%s: get_conf_levels failed, err = %d\n", - __func__, rc); - return rc; - } + prtd->lsm_client->num_keywords = p_info->param_size; + rc = msm_lsm_get_conf_levels(prtd->lsm_client, + p_info->param_data); + if (rc) { + dev_err(rtd->dev, + "%s: get_conf_levels failed for snd_model %d, err = %d\n", + __func__, p_info->model_id, rc); + return rc; + } - rc = q6lsm_set_one_param(prtd->lsm_client, p_info, - prtd->lsm_client->confidence_levels, - LSM_MIN_CONFIDENCE_LEVELS); - if (rc) - dev_err(rtd->dev, - "%s: Failed to set min_conf_levels, err = %d\n", - __func__, rc); + rc = q6lsm_set_one_param(prtd->lsm_client, p_info, + prtd->lsm_client->multi_snd_model_confidence_levels, + LSM_MULTI_SND_MODEL_CONFIDENCE_LEVELS); + if (rc) + dev_err(rtd->dev, + "%s: Failed to set multi_snd_model_confidence_levels, err = %d\n", + __func__, rc); - if (prtd->lsm_client->confidence_levels) { - kfree(prtd->lsm_client->confidence_levels); - prtd->lsm_client->confidence_levels = NULL; + if (prtd->lsm_client->multi_snd_model_confidence_levels) { + kfree(prtd->lsm_client->multi_snd_model_confidence_levels); + prtd->lsm_client->multi_snd_model_confidence_levels = NULL; + } + } else { + if (p_info->param_size > MAX_NUM_CONFIDENCE) { + dev_err(rtd->dev, + "%s: invalid confidence levels %d\n", + __func__, p_info->param_size); + return -EINVAL; + } + + prtd->lsm_client->num_confidence_levels = + p_info->param_size; + rc = msm_lsm_get_conf_levels(prtd->lsm_client, + p_info->param_data); + if (rc) { + dev_err(rtd->dev, + "%s: get_conf_levels failed, err = %d\n", + __func__, rc); + return rc; + } + + rc = q6lsm_set_one_param(prtd->lsm_client, p_info, + prtd->lsm_client->confidence_levels, + LSM_MIN_CONFIDENCE_LEVELS); + if (rc) + dev_err(rtd->dev, + "%s: Failed to set min_conf_levels, err = %d\n", + __func__, rc); + + if (prtd->lsm_client->confidence_levels) { + kfree(prtd->lsm_client->confidence_levels); + prtd->lsm_client->confidence_levels = NULL; + } } return rc; } @@ -691,66 +756,166 @@ static int msm_lsm_reg_model(struct snd_pcm_substream *substream, struct snd_pcm_runtime *runtime = substream->runtime; struct lsm_priv *prtd = runtime->private_data; struct snd_soc_pcm_runtime *rtd = substream->private_data; - int rc = 0; + int rc = 0, stage_idx = p_info->stage_idx; struct lsm_sound_model *sm = NULL; size_t offset = sizeof(union param_hdrs); + struct lsm_client *client = prtd->lsm_client; - rc = q6lsm_snd_model_buf_alloc(prtd->lsm_client, - p_info->param_size, p_info); - if (rc) { + if (p_info->model_id != 0 && + p_info->param_type == LSM_REG_MULTI_SND_MODEL) { + sm = kzalloc(sizeof(*sm), GFP_KERNEL); + if (sm == NULL) { + dev_err(rtd->dev, "%s: snd_model kzalloc failed\n", __func__); + return -ENOMEM; + } + + INIT_LIST_HEAD(&sm->list); + + rc = q6lsm_snd_model_buf_alloc(client, p_info->param_size, p_info, sm); + if (rc) { + dev_err(rtd->dev, "%s: snd_model buf alloc failed, size = %d\n", + __func__, p_info->param_size); + goto err_buf_alloc; + } + + q6lsm_sm_set_param_data(client, p_info, &offset, sm); + + /* + * For set_param, advance the sound model data with the + * number of bytes required by param_data. + */ + if (copy_from_user((u8 *)sm->data + offset, + p_info->param_data, p_info->param_size)) { + dev_err(rtd->dev, + "%s: copy_from_user for snd_model %d failed, size = %d\n", + __func__, p_info->model_id, p_info->param_size); + rc = -EFAULT; + goto err_copy; + } + /* Add this sound model to the list of multi sound models */ + list_add_tail(&sm->list, &client->stage_cfg[stage_idx].sound_models); + + rc = q6lsm_set_one_param(client, p_info, NULL, + LSM_REG_MULTI_SND_MODEL); + if (rc) { + dev_err(rtd->dev, + "%s: Failed to register snd_model %d, err = %d\n", + __func__, p_info->model_id, rc); + goto err_copy; + } + + client->num_sound_models++; + dev_dbg(rtd->dev, + "%s: registered snd_model: %d, total num of snd_model: %d\n", + __func__, p_info->model_id, client->num_sound_models); + } else if (p_info->model_id == 0 && + p_info->param_type == LSM_REG_SND_MODEL) { + sm = &client->stage_cfg[stage_idx].sound_model; + + rc = q6lsm_snd_model_buf_alloc(client, p_info->param_size, p_info, sm); + if (rc) { + dev_err(rtd->dev, "%s: snd_model buf alloc failed, size = %d\n", + __func__, p_info->param_size); + return rc; + } + + q6lsm_sm_set_param_data(client, p_info, &offset, sm); + + /* + * For set_param, advance the sound model data with the + * number of bytes required by param_data. + */ + + if (copy_from_user((u8 *)sm->data + offset, + p_info->param_data, p_info->param_size)) { + dev_err(rtd->dev, + "%s: copy_from_user for snd_model failed, size = %d\n", + __func__, p_info->param_size); + rc = -EFAULT; + goto err_copy; + } + rc = q6lsm_set_one_param(client, p_info, NULL, LSM_REG_SND_MODEL); + if (rc) { + dev_err(rtd->dev, "%s: Failed to register snd_model, err = %d\n", + __func__, rc); + goto err_copy; + } + } else { dev_err(rtd->dev, - "%s: snd_model buf alloc failed, size = %d\n", - __func__, p_info->param_size); - return rc; - } - - q6lsm_sm_set_param_data(prtd->lsm_client, p_info, &offset); - - /* - * For set_param, advance the sound model data with the - * number of bytes required by param_data. - */ - - sm = &prtd->lsm_client->stage_cfg[p_info->stage_idx].sound_model; - if (copy_from_user((u8 *)sm->data + offset, - p_info->param_data, p_info->param_size)) { - dev_err(rtd->dev, - "%s: copy_from_user for snd_model failed, size = %d\n", - __func__, p_info->param_size); - rc = -EFAULT; - goto err_copy; - } - rc = q6lsm_set_one_param(prtd->lsm_client, p_info, NULL, - LSM_REG_SND_MODEL); - if (rc) { - dev_err(rtd->dev, - "%s: Failed to set sound_model, err = %d\n", - __func__, rc); - goto err_copy; + "%s: snd_model id %d is invalid for param type %d\n", + __func__, p_info->model_id, p_info->param_type); } return rc; err_copy: - q6lsm_snd_model_buf_free(prtd->lsm_client, p_info); + q6lsm_snd_model_buf_free(client, p_info, sm); +err_buf_alloc: + if (p_info->model_id != 0) { + list_del(&sm->list); + kfree(sm); + sm = NULL; + } return rc; } static int msm_lsm_dereg_model(struct snd_pcm_substream *substream, - struct lsm_params_info_v2 *p_info) + struct lsm_params_info_v2 *p_info) { struct snd_pcm_runtime *runtime = substream->runtime; struct lsm_priv *prtd = runtime->private_data; struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct lsm_sound_model *sm = NULL; + struct lsm_client *client = prtd->lsm_client; int rc = 0; - rc = q6lsm_set_one_param(prtd->lsm_client, p_info, - NULL, LSM_DEREG_SND_MODEL); - if (rc) - dev_err(rtd->dev, - "%s: Failed to set det_mode param, err = %d\n", - __func__, rc); + if (p_info->model_id != 0 && + p_info->param_type == LSM_DEREG_MULTI_SND_MODEL) { + list_for_each_entry(sm, + &client->stage_cfg[p_info->stage_idx].sound_models, + list) { + dev_dbg(rtd->dev, + "%s: current snd_model: %d, looking for snd_model %d\n", + __func__, sm->model_id, p_info->model_id); + if (sm->model_id == p_info->model_id) + break; + } - q6lsm_snd_model_buf_free(prtd->lsm_client, p_info); + if (sm->model_id == p_info->model_id) { + rc = q6lsm_set_one_param(client, p_info, NULL, + LSM_DEREG_MULTI_SND_MODEL); + if (rc) + dev_err(rtd->dev, + "%s: Failed to deregister snd_model %d, err = %d\n", + __func__, p_info->model_id, rc); + + q6lsm_snd_model_buf_free(client, p_info, sm); + list_del(&sm->list); + kfree(sm); + sm = NULL; + client->num_sound_models--; + } else { + rc = -EINVAL; + dev_err(rtd->dev, + "%s: Failed to find snd_model, invalid model_id %d\n", + __func__, p_info->model_id); + } + } else if (p_info->model_id == 0 && + p_info->param_type == LSM_DEREG_SND_MODEL) { + rc = q6lsm_set_one_param(client, p_info, NULL, + LSM_DEREG_SND_MODEL); + if (rc) + dev_err(rtd->dev, + "%s: Failed to deregister snd_model, err = %d\n", + __func__, rc); + + sm = &client->stage_cfg[p_info->stage_idx].sound_model; + q6lsm_snd_model_buf_free(client, p_info, sm); + } else { + rc = -EINVAL; + dev_err(rtd->dev, + "%s: snd_model id %d is invalid for param type %d\n", + __func__, p_info->model_id, p_info->param_type); + } return rc; } @@ -992,6 +1157,22 @@ static int msm_lsm_process_params(struct snd_pcm_substream *substream, return -EINVAL; } + if (p_info->param_type == LSM_REG_MULTI_SND_MODEL && + prtd->lsm_client->num_sound_models == LSM_MAX_SOUND_MODELS_SUPPORTED) { + dev_err(rtd->dev, + "%s: maximum supported sound models(8) have already reached\n", + __func__); + return -EINVAL; + } + + if (p_info->param_type == LSM_DEREG_MULTI_SND_MODEL && + prtd->lsm_client->num_sound_models == 0) { + dev_err(rtd->dev, + "%s: no available sound model to be deregistered\n", + __func__); + return -EINVAL; + } + switch (p_info->param_type) { case LSM_ENDPOINT_DETECT_THRESHOLD: rc = msm_lsm_set_epd(substream, p_info); @@ -1003,12 +1184,15 @@ static int msm_lsm_process_params(struct snd_pcm_substream *substream, rc = msm_lsm_set_gain(substream, p_info); break; case LSM_MIN_CONFIDENCE_LEVELS: + case LSM_MULTI_SND_MODEL_CONFIDENCE_LEVELS: rc = msm_lsm_set_conf(substream, p_info); break; case LSM_REG_SND_MODEL: + case LSM_REG_MULTI_SND_MODEL: rc = msm_lsm_reg_model(substream, p_info); break; case LSM_DEREG_SND_MODEL: + case LSM_DEREG_MULTI_SND_MODEL: rc = msm_lsm_dereg_model(substream, p_info); break; case LSM_CUSTOM_PARAMS: @@ -1176,7 +1360,10 @@ static int msm_lsm_ioctl_shared(struct snd_pcm_substream *substream, * also set stage index to LSM_STAGE_INDEX_FIRST. */ struct lsm_params_info_v2 p_info = {0}; + struct lsm_sound_model *sm = NULL; p_info.stage_idx = LSM_STAGE_INDEX_FIRST; + p_info.param_type = LSM_DEREG_SND_MODEL; + sm = &prtd->lsm_client->stage_cfg[p_info.stage_idx].sound_model; dev_dbg(rtd->dev, "%s: Registering sound model V2\n", __func__); @@ -1192,21 +1379,20 @@ static int msm_lsm_ioctl_shared(struct snd_pcm_substream *substream, break; } rc = q6lsm_snd_model_buf_alloc(prtd->lsm_client, - snd_model_v2.data_size, &p_info); + snd_model_v2.data_size, &p_info, sm); if (rc) { dev_err(rtd->dev, "%s: q6lsm buffer alloc failed V2, size %d\n", __func__, snd_model_v2.data_size); break; } - if (copy_from_user( - prtd->lsm_client->stage_cfg[p_info.stage_idx].sound_model.data, - snd_model_v2.data, snd_model_v2.data_size)) { + if (copy_from_user(sm->data, snd_model_v2.data, + snd_model_v2.data_size)) { dev_err(rtd->dev, "%s: copy from user data failed\n" "data %pK size %d\n", __func__, snd_model_v2.data, snd_model_v2.data_size); - q6lsm_snd_model_buf_free(prtd->lsm_client, &p_info); + q6lsm_snd_model_buf_free(prtd->lsm_client, &p_info, sm); rc = -EFAULT; break; } @@ -1234,7 +1420,7 @@ static int msm_lsm_ioctl_shared(struct snd_pcm_substream *substream, dev_err(rtd->dev, "%s: Register snd Model v2 failed =%d\n", __func__, rc); - q6lsm_snd_model_buf_free(prtd->lsm_client, &p_info); + q6lsm_snd_model_buf_free(prtd->lsm_client, &p_info, sm); } if (prtd->lsm_client->confidence_levels) { kfree(prtd->lsm_client->confidence_levels); @@ -1697,6 +1883,7 @@ struct lsm_params_info_v2_32 { uint32_t param_type; u16 instance_id; u16 stage_idx; + u32 model_id; }; struct snd_lsm_module_params_32 { @@ -2033,9 +2220,9 @@ static int msm_lsm_ioctl_compat(struct snd_pcm_substream *substream, if (p_data.data_size != expected_size) { dev_err(rtd->dev, - "%s: %s: Invalid size %d\n", + "%s: %s: Invalid size %d, expected_size %d\n", __func__, "SET_MODULE_PARAMS(_V2)_32", - p_data.data_size); + p_data.data_size, expected_size); err = -EINVAL; goto done; } @@ -2071,6 +2258,7 @@ static int msm_lsm_ioctl_compat(struct snd_pcm_substream *substream, p_info.instance_id = INSTANCE_ID_0; p_info.stage_idx = LSM_STAGE_INDEX_FIRST; + p_info.model_id = 0; p_info_32++; } else { @@ -2082,6 +2270,14 @@ static int msm_lsm_ioctl_compat(struct snd_pcm_substream *substream, p_info.instance_id = p_info_v2_32->instance_id; p_info.stage_idx = p_info_v2_32->stage_idx; + /* set sound model id to 0 for backward compatibility */ + p_info.model_id = 0; + + if (LSM_REG_MULTI_SND_MODEL == p_info_v2_32->param_type || + LSM_DEREG_MULTI_SND_MODEL == p_info_v2_32->param_type || + LSM_MULTI_SND_MODEL_CONFIDENCE_LEVELS == + p_info_v2_32->param_type) + p_info.model_id = p_info_v2_32->model_id; p_info_v2_32++; } @@ -2208,7 +2404,6 @@ static int msm_lsm_ioctl(struct snd_pcm_substream *substream, case SNDRV_LSM_SET_MODULE_PARAMS_V2: { struct snd_lsm_module_params p_data; struct lsm_params_info *temp_ptr_info = NULL; - struct lsm_params_info_v2 info_v2; struct lsm_params_info_v2 *ptr_info_v2 = NULL, *temp_ptr_info_v2 = NULL; size_t p_size = 0, count; u8 *params; @@ -2277,26 +2472,37 @@ static int msm_lsm_ioctl(struct snd_pcm_substream *substream, for (count = 0; count < p_data.num_params; count++) { if (cmd == SNDRV_LSM_SET_MODULE_PARAMS) { /* convert to V2 param info struct from legacy param info */ - info_v2.module_id = temp_ptr_info->module_id; - info_v2.param_id = temp_ptr_info->param_id; - info_v2.param_size = temp_ptr_info->param_size; - info_v2.param_data = temp_ptr_info->param_data; - info_v2.param_type = temp_ptr_info->param_type; + ptr_info_v2->module_id = temp_ptr_info->module_id; + ptr_info_v2->param_id = temp_ptr_info->param_id; + ptr_info_v2->param_size = temp_ptr_info->param_size; + ptr_info_v2->param_data = temp_ptr_info->param_data; + ptr_info_v2->param_type = temp_ptr_info->param_type; - info_v2.instance_id = INSTANCE_ID_0; - info_v2.stage_idx = LSM_STAGE_INDEX_FIRST; + ptr_info_v2->instance_id = INSTANCE_ID_0; + ptr_info_v2->stage_idx = LSM_STAGE_INDEX_FIRST; + ptr_info_v2->model_id = 0; - ptr_info_v2 = &info_v2; temp_ptr_info++; } else { - /* Just copy the pointer as user already provided v2 params */ + if (LSM_REG_MULTI_SND_MODEL != temp_ptr_info_v2->param_type || + LSM_DEREG_MULTI_SND_MODEL != + temp_ptr_info_v2->param_type || + LSM_MULTI_SND_MODEL_CONFIDENCE_LEVELS != + temp_ptr_info_v2->param_type) { + /* set sound model id to 0 for backward compatibility */ + temp_ptr_info_v2->model_id = 0; + } + /* + * Just copy the pointer as user + * already provided sound model id + */ ptr_info_v2 = temp_ptr_info_v2; temp_ptr_info_v2++; } err = msm_lsm_process_params(substream, ptr_info_v2); if (err) dev_err(rtd->dev, - "%s: Failed to process param, type%d stage=%d err=%d\n", + "%s: Failed to process param, type=%d stage=%d err=%d\n", __func__, ptr_info_v2->param_type, ptr_info_v2->stage_idx, err); } @@ -2464,7 +2670,7 @@ static int msm_lsm_open(struct snd_pcm_substream *substream) struct snd_pcm_runtime *runtime = substream->runtime; struct lsm_priv *prtd; struct snd_soc_pcm_runtime *rtd = substream->private_data; - int ret = 0; + int ret = 0, i; pr_debug("%s\n", __func__); prtd = kzalloc(sizeof(struct lsm_priv), GFP_KERNEL); @@ -2530,6 +2736,12 @@ static int msm_lsm_open(struct snd_pcm_substream *substream) prtd->lsm_client->event_type = LSM_DET_EVENT_TYPE_LEGACY; prtd->lsm_client->fe_id = rtd->dai_link->id; prtd->lsm_client->unprocessed_data = 0; + prtd->lsm_client->num_sound_models = 0; + prtd->lsm_client->num_keywords = 0; + prtd->lsm_client->multi_snd_model_confidence_levels = NULL; + + for (i = 0; i < LSM_MAX_STAGES_PER_SESSION; i++) + INIT_LIST_HEAD(&prtd->lsm_client->stage_cfg[i].sound_models); prtd->ws = wakeup_source_register(rtd->dev, "lsm-client"); diff --git a/dsp/q6lsm.c b/dsp/q6lsm.c index 4332629d9b1c..b44a7f474f91 100644 --- a/dsp/q6lsm.c +++ b/dsp/q6lsm.c @@ -908,17 +908,15 @@ done: * @client: LSM client handle * @p_info: param info * @offset: pointer to retrieve size - * + * @sm: pointer to sound model */ void q6lsm_sm_set_param_data(struct lsm_client *client, - struct lsm_params_info_v2 *p_info, - size_t *offset) + struct lsm_params_info_v2 *p_info, + size_t *offset, struct lsm_sound_model *sm) { struct param_hdr_v3 param_hdr; int ret; - struct lsm_sound_model *sm; - sm = &client->stage_cfg[p_info->stage_idx].sound_model; memset(¶m_hdr, 0, sizeof(param_hdr)); param_hdr.module_id = p_info->module_id; @@ -926,6 +924,8 @@ void q6lsm_sm_set_param_data(struct lsm_client *client, param_hdr.param_id = p_info->param_id; param_hdr.param_size = p_info->param_size; + sm->model_id = p_info->model_id; + ret = q6lsm_pack_params(sm->data, ¶m_hdr, NULL, offset, LSM_SESSION_CMD_SET_PARAMS_V2); if (ret) @@ -1010,42 +1010,78 @@ EXPORT_SYMBOL(q6lsm_open); static int q6lsm_send_confidence_levels(struct lsm_client *client, struct param_hdr_v3 *param_info, - uint32_t set_param_opcode) + uint32_t set_param_opcode, uint32_t model_id) { struct lsm_param_confidence_levels *conf_levels = NULL; uint32_t num_conf_levels = client->num_confidence_levels; + struct lsm_param_multi_snd_model_conf_levels *multi_sm_conf_levels = NULL; + uint32_t num_keywords = client->num_keywords; uint8_t i = 0; uint8_t padd_size = 0; uint32_t param_size = 0; int rc = 0; - /* Data must be 4 byte aligned so add any necessary padding. */ - padd_size = (4 - (num_conf_levels % 4)) - 1; - param_size = (sizeof(uint8_t) + num_conf_levels + padd_size) * - sizeof(uint8_t); - param_info->param_size = param_size; - pr_debug("%s: Set Conf Levels PARAM SIZE = %d\n", __func__, param_size); + if (model_id != 0) { + /* No padding is need since the structure is always 4 byte aligend. + * The number "2" below represents the first two u32 variables in + * struct lsm_param_multi_snd_model_conf_levels. + */ + param_size = (2 + num_keywords) * sizeof(uint32_t); + param_info->param_size = param_size; + pr_debug("%s: Set Conf Levels PARAM SIZE: %d\n", __func__, param_size); - conf_levels = kzalloc(param_size, GFP_KERNEL); - if (!conf_levels) - return -ENOMEM; + multi_sm_conf_levels = kzalloc(param_size, GFP_KERNEL); + if (!multi_sm_conf_levels) + return -ENOMEM; - conf_levels->num_confidence_levels = num_conf_levels; - pr_debug("%s: Num conf_level = %d\n", __func__, num_conf_levels); + multi_sm_conf_levels->model_id = model_id; + multi_sm_conf_levels->num_keywords = num_keywords; + pr_debug("%s: snd_model id: %d, num_keywords: %d\n", + __func__, model_id, num_keywords); - memcpy(conf_levels->confidence_levels, client->confidence_levels, - num_conf_levels); - for (i = 0; i < num_conf_levels; i++) - pr_debug("%s: Confidence_level[%d] = %d\n", __func__, i, - conf_levels->confidence_levels[i]); + memcpy(multi_sm_conf_levels->confidence_levels, + client->multi_snd_model_confidence_levels, + sizeof(uint32_t) * num_keywords); + for (i = 0; i < num_keywords; i++) + pr_debug("%s: Confidence_level[%d] = %d\n", __func__, i, + multi_sm_conf_levels->confidence_levels[i]); - rc = q6lsm_pack_and_set_params(client, param_info, - (uint8_t *) conf_levels, - set_param_opcode); - if (rc) - pr_err("%s: Send confidence_levels cmd failed, err = %d\n", - __func__, rc); - kfree(conf_levels); + rc = q6lsm_pack_and_set_params(client, param_info, + (uint8_t *) multi_sm_conf_levels, + set_param_opcode); + if (rc) + pr_err("%s: Send multi_snd_model_conf_levels cmd failed, err %d\n", + __func__, rc); + kfree(multi_sm_conf_levels); + } else { + /* Data must be 4 byte aligned so add any necessary padding. */ + padd_size = (4 - (num_conf_levels % 4)) - 1; + param_size = (sizeof(uint8_t) + num_conf_levels + padd_size) * + sizeof(uint8_t); + param_info->param_size = param_size; + pr_debug("%s: Set Conf Levels PARAM SIZE = %d\n", __func__, param_size); + + conf_levels = kzalloc(param_size, GFP_KERNEL); + if (!conf_levels) + return -ENOMEM; + + conf_levels->num_confidence_levels = num_conf_levels; + pr_debug("%s: Num conf_level = %d\n", __func__, num_conf_levels); + + memcpy(conf_levels->confidence_levels, client->confidence_levels, + num_conf_levels); + for (i = 0; i < num_conf_levels; i++) + pr_debug("%s: Confidence_level[%d] = %d\n", __func__, i, + conf_levels->confidence_levels[i]); + + rc = q6lsm_pack_and_set_params(client, param_info, + (uint8_t *) conf_levels, + set_param_opcode); + if (rc) + pr_err("%s: Send confidence_levels cmd failed, err = %d\n", + __func__, rc); + kfree(conf_levels); + } return rc; } @@ -1459,7 +1495,7 @@ int q6lsm_set_data(struct lsm_client *client, param_hdr.param_id = LSM_PARAM_ID_MIN_CONFIDENCE_LEVELS; rc = q6lsm_send_confidence_levels(client, ¶m_hdr, - LSM_SESSION_CMD_SET_PARAMS); + LSM_SESSION_CMD_SET_PARAMS, 0); if (rc) { pr_err("%s: Failed to send conf_levels, err = %d\n", __func__, rc); @@ -1537,8 +1573,9 @@ EXPORT_SYMBOL(q6lsm_register_sound_model); */ int q6lsm_deregister_sound_model(struct lsm_client *client) { - int rc; + int rc = 0; struct lsm_cmd_reg_snd_model cmd; + struct lsm_sound_model *sm = NULL, *next = NULL; /* * With multi-stage support sm buff allocation/free usage param info * to check stage index for which this sound model is being set, and @@ -1564,19 +1601,39 @@ int q6lsm_deregister_sound_model(struct lsm_client *client) return -EINVAL; } - memset(&cmd, 0, sizeof(cmd)); - q6lsm_add_hdr(client, &cmd.hdr, sizeof(cmd.hdr), false); - cmd.hdr.opcode = LSM_SESSION_CMD_DEREGISTER_SOUND_MODEL; + if (client->num_sound_models > 0) { + p_info.param_type = LSM_DEREG_MULTI_SND_MODEL; + list_for_each_entry_safe(sm, next, + &client->stage_cfg[p_info.stage_idx].sound_models, + list) { + pr_debug("%s: current snd_model: %d, num of sound models left %d\n", + __func__, sm->model_id, client->num_sound_models); + q6lsm_snd_model_buf_free(client, &p_info, sm); + list_del(&sm->list); + kfree(sm); + sm = NULL; + client->num_sound_models--; - rc = q6lsm_apr_send_pkt(client, client->apr, &cmd.hdr, true, NULL); - if (rc) { - pr_err("%s: Failed cmd opcode 0x%x, rc %d\n", __func__, - cmd.hdr.opcode, rc); + if (0 == client->num_sound_models) + break; + } } else { - pr_debug("%s: Deregister sound model succeeded\n", __func__); - } + memset(&cmd, 0, sizeof(cmd)); + q6lsm_add_hdr(client, &cmd.hdr, sizeof(cmd.hdr), false); + cmd.hdr.opcode = LSM_SESSION_CMD_DEREGISTER_SOUND_MODEL; - q6lsm_snd_model_buf_free(client, &p_info); + rc = q6lsm_apr_send_pkt(client, client->apr, &cmd.hdr, true, NULL); + if (rc) { + pr_err("%s: Failed cmd opcode 0x%x, rc %d\n", __func__, + cmd.hdr.opcode, rc); + } else { + pr_debug("%s: Deregister sound model succeeded\n", __func__); + } + + p_info.param_type = LSM_DEREG_SND_MODEL; + sm = &client->stage_cfg[p_info.stage_idx].sound_model; + q6lsm_snd_model_buf_free(client, &p_info, sm); + } return rc; } @@ -1825,14 +1882,15 @@ fail: * * @client: LSM client handle * @p_info: sound model param info + * @sm: pointer to sound model * * Returns 0 on success or error on failure */ int q6lsm_snd_model_buf_free(struct lsm_client *client, - struct lsm_params_info_v2 *p_info) + struct lsm_params_info_v2 *p_info, + struct lsm_sound_model *sm) { int rc = 0, stage_idx = p_info->stage_idx; - struct lsm_sound_model *sm = NULL; pr_debug("%s: Session id %d\n", __func__, client->session); if (CHECK_SESSION(client->session)) { @@ -1840,11 +1898,11 @@ int q6lsm_snd_model_buf_free(struct lsm_client *client, return -EINVAL; } - if (!client->stage_cfg[stage_idx].sound_model.data) + if (p_info->param_type == LSM_DEREG_SND_MODEL && + !client->stage_cfg[stage_idx].sound_model.data) return 0; mutex_lock(&client->cmd_lock); - sm = &client->stage_cfg[stage_idx].sound_model; if (sm->mem_map_handle != 0) { rc = q6lsm_memory_unmap_regions(client, sm->mem_map_handle); if (rc) @@ -1858,7 +1916,10 @@ int q6lsm_snd_model_buf_free(struct lsm_client *client, sm->phys = 0; mutex_unlock(&client->cmd_lock); - rc = q6lsm_snd_cal_free(client, p_info); + if ((p_info->param_type == LSM_DEREG_MULTI_SND_MODEL && + client->num_sound_models == 1) || + p_info->param_type == LSM_DEREG_SND_MODEL) + rc = q6lsm_snd_cal_free(client, p_info); return rc; } EXPORT_SYMBOL(q6lsm_snd_model_buf_free); @@ -1980,20 +2041,20 @@ static int q6lsm_mmapcallback(struct apr_client_data *data, void *priv) * Returns 0 on success or error on failure */ int q6lsm_snd_model_buf_alloc(struct lsm_client *client, size_t len, - struct lsm_params_info_v2 *p_info) + struct lsm_params_info_v2 *p_info, + struct lsm_sound_model *sm) { int rc = -EINVAL, stage_idx = p_info->stage_idx; + int model_id = p_info->model_id; size_t total_mem = 0; - struct lsm_sound_model *sm = NULL; if (!client) return rc; - pr_debug("%s:Snd Model len = %zd, stage idx %d\n", - __func__, len, stage_idx); + pr_debug("%s:Snd Model len %zd, stage_idx %d, model_id %d\n", + __func__, len, stage_idx, model_id); mutex_lock(&client->cmd_lock); - sm = &client->stage_cfg[stage_idx].sound_model; if (!sm->data) { /* * If sound model is sent as set_param, i.e. param_id != 0, @@ -2008,7 +2069,7 @@ int q6lsm_snd_model_buf_alloc(struct lsm_client *client, size_t len, pr_debug("%s: sm param size %zd Total mem %zd, stage_idx %d\n", __func__, len, total_mem, stage_idx); rc = msm_audio_ion_alloc(&sm->dma_buf, total_mem, - &sm->phys, &len, &sm->data); + &sm->phys, &len, &sm->data); if (rc) { pr_err("%s: Audio ION alloc is failed, rc = %d, stage_idx = %d\n", __func__, rc, stage_idx); @@ -2040,7 +2101,7 @@ int q6lsm_snd_model_buf_alloc(struct lsm_client *client, size_t len, fail: mutex_unlock(&client->cmd_lock); fail_1: - q6lsm_snd_model_buf_free(client, p_info); + q6lsm_snd_model_buf_free(client, p_info, sm); return rc; } EXPORT_SYMBOL(q6lsm_snd_model_buf_alloc); @@ -2126,8 +2187,8 @@ static int q6lsm_send_param_gain(struct lsm_client *client, u16 gain, * Returns 0 on success or error on failure */ int q6lsm_set_one_param(struct lsm_client *client, - struct lsm_params_info_v2 *p_info, void *data, - uint32_t param_type) + struct lsm_params_info_v2 *p_info, + void *data, uint32_t param_type) { struct param_hdr_v3 param_info; int rc = 0; @@ -2186,14 +2247,19 @@ int q6lsm_set_one_param(struct lsm_client *client, } case LSM_MIN_CONFIDENCE_LEVELS: + case LSM_MULTI_SND_MODEL_CONFIDENCE_LEVELS: param_info.module_id = p_info->module_id; param_info.instance_id = p_info->instance_id; param_info.param_id = p_info->param_id; rc = q6lsm_send_confidence_levels( - client, ¶m_info, LSM_SESSION_CMD_SET_PARAMS_V2); + client, ¶m_info, LSM_SESSION_CMD_SET_PARAMS_V2, + p_info->model_id); if (rc) - pr_err("%s: CONFIDENCE_LEVELS cmd failed, rc %d\n", - __func__, rc); + pr_err("%s: %s cmd failed, rc %d\n", + __func__, + param_type == LSM_MIN_CONFIDENCE_LEVELS ? + "LSM_MIN_CONFIDENCE_LEVELS" : + "LSM_MULTI_SND_MODEL_CONFIDENCE_LEVELS", rc); break; case LSM_POLLING_ENABLE: { struct snd_lsm_poll_enable *lsm_poll_enable = @@ -2210,7 +2276,8 @@ int q6lsm_set_one_param(struct lsm_client *client, break; } - case LSM_REG_SND_MODEL: { + case LSM_REG_SND_MODEL: + case LSM_REG_MULTI_SND_MODEL: { struct mem_mapping_hdr mem_hdr; u32 payload_size; struct lsm_sound_model *sm = NULL; @@ -2224,8 +2291,18 @@ int q6lsm_set_one_param(struct lsm_client *client, payload_size = p_info->param_size + sizeof(struct param_hdr_v2); - sm = &client->stage_cfg[p_info->stage_idx].sound_model; - + if (param_type == LSM_REG_MULTI_SND_MODEL) { + list_for_each_entry(sm, + &client->stage_cfg[p_info->stage_idx].sound_models, + list) { + pr_debug("%s: current snd_model: %d, looking for snd_model %d\n", + __func__, sm->model_id, p_info->model_id); + if (sm->model_id == p_info->model_id) + break; + } + } else { + sm = &client->stage_cfg[p_info->stage_idx].sound_model; + } mem_hdr.data_payload_addr_lsw = lower_32_bits(sm->phys); mem_hdr.data_payload_addr_msw = @@ -2236,28 +2313,41 @@ int q6lsm_set_one_param(struct lsm_client *client, rc = q6lsm_set_params(client, &mem_hdr, NULL, payload_size, LSM_SESSION_CMD_SET_PARAMS_V2); if (rc) { - pr_err("%s: REG_SND_MODEL failed, rc %d\n", - __func__, rc); + pr_err("%s: %s failed, rc %d\n", + __func__, param_type == LSM_REG_SND_MODEL ? + "LSM_REG_SND_MODEL" : "LSM_REG_MULTI_SND_MODEL", rc); return rc; } - rc = q6lsm_send_cal(client, LSM_SESSION_CMD_SET_PARAMS, p_info); - if (rc) - pr_err("%s: Failed to send lsm cal, err = %d\n", - __func__, rc); + if (client->num_sound_models == 0) { + rc = q6lsm_send_cal(client, LSM_SESSION_CMD_SET_PARAMS, p_info); + if (rc) + pr_err("%s: Failed to send lsm cal, err = %d\n", + __func__, rc); + } break; } - case LSM_DEREG_SND_MODEL: { + case LSM_DEREG_SND_MODEL: + case LSM_DEREG_MULTI_SND_MODEL: { param_info.module_id = p_info->module_id; param_info.instance_id = p_info->instance_id; param_info.param_id = p_info->param_id; param_info.param_size = 0; - rc = q6lsm_pack_and_set_params(client, ¶m_info, NULL, - LSM_SESSION_CMD_SET_PARAMS_V2); + + if (param_type == LSM_DEREG_MULTI_SND_MODEL) { + param_info.param_size = p_info->param_size; + rc = q6lsm_pack_and_set_params(client, ¶m_info, + (uint8_t *)&p_info->model_id, + LSM_SESSION_CMD_SET_PARAMS_V2); + } else { + rc = q6lsm_pack_and_set_params(client, ¶m_info, NULL, + LSM_SESSION_CMD_SET_PARAMS_V2); + } if (rc) - pr_err("%s: DEREG_SND_MODEL failed, rc %d\n", - __func__, rc); + pr_err("%s: %s failed, rc %d\n", + __func__, param_type == LSM_DEREG_SND_MODEL ? + "LSM_DEREG_SND_MODEL" : "LSM_DEREG_MULTI_SND_MODEL", rc); break; } diff --git a/include/dsp/q6lsm.h b/include/dsp/q6lsm.h index 1eaeb3e586ed..e4431bc9127d 100644 --- a/include/dsp/q6lsm.h +++ b/include/dsp/q6lsm.h @@ -22,6 +22,10 @@ #define MAX_LSM_SESSIONS 8 +#define MAX_KEYWORDS_SUPPORTED 8 + +#define LSM_MAX_SOUND_MODELS_SUPPORTED 8 + typedef void (*lsm_app_cb)(uint32_t opcode, uint32_t token, uint32_t *payload, uint16_t client_size, void *priv); @@ -32,6 +36,8 @@ struct lsm_sound_model { uint32_t actual_size; /* actual number of bytes read by DSP */ struct dma_buf *dma_buf; uint32_t mem_map_handle; + uint32_t model_id; + struct list_head list; }; struct snd_lsm_event_status_v2 { @@ -71,6 +77,7 @@ struct lsm_stage_config { bool lab_enable; struct lsm_sound_model sound_model; struct lsm_cal_data_info cal_info; + struct list_head sound_models; }; @@ -106,6 +113,9 @@ struct lsm_client { struct lsm_stage_config stage_cfg[LSM_MAX_STAGES_PER_SESSION]; uint64_t fe_id; uint16_t unprocessed_data; + uint32_t num_sound_models; + uint32_t num_keywords; + uint32_t *multi_snd_model_confidence_levels; }; struct lsm_stream_cmd_open_tx { @@ -193,6 +203,11 @@ struct lsm_param_media_fmt_v2 { uint8_t channel_mapping[0]; } __packed; +struct lsm_param_multi_snd_model_conf_levels { + uint32_t model_id; + uint32_t num_keywords; + uint32_t confidence_levels[0]; +} __packed; struct lsm_param_confidence_levels { uint8_t num_confidence_levels; @@ -268,9 +283,9 @@ int q6lsm_open(struct lsm_client *client, uint16_t app_id); int q6lsm_start(struct lsm_client *client, bool wait); int q6lsm_stop(struct lsm_client *client, bool wait); int q6lsm_snd_model_buf_alloc(struct lsm_client *client, size_t len, - struct lsm_params_info_v2 *p_info); + struct lsm_params_info_v2 *p_info, struct lsm_sound_model *sm); int q6lsm_snd_model_buf_free(struct lsm_client *client, - struct lsm_params_info_v2 *p_info); + struct lsm_params_info_v2 *p_info, struct lsm_sound_model *sm); int q6lsm_close(struct lsm_client *client); int q6lsm_register_sound_model(struct lsm_client *client, enum lsm_detection_mode mode, @@ -290,8 +305,8 @@ int q6lsm_set_one_param(struct lsm_client *client, struct lsm_params_info_v2 *p_info, void *data, uint32_t param_type); void q6lsm_sm_set_param_data(struct lsm_client *client, - struct lsm_params_info_v2 *p_info, - size_t *offset); + struct lsm_params_info_v2 *p_info, size_t *offset, + struct lsm_sound_model *sm); int q6lsm_set_port_connected(struct lsm_client *client); int q6lsm_set_fwk_mode_cfg(struct lsm_client *client, uint32_t event_mode); int q6lsm_set_media_fmt_params(struct lsm_client *client); diff --git a/include/uapi/audio/sound/lsm_params.h b/include/uapi/audio/sound/lsm_params.h index fa435204fad4..722d3ba72677 100644 --- a/include/uapi/audio/sound/lsm_params.h +++ b/include/uapi/audio/sound/lsm_params.h @@ -6,7 +6,7 @@ #include -#define SNDRV_LSM_VERSION SNDRV_PROTOCOL_VERSION(0, 3, 1) +#define SNDRV_LSM_VERSION SNDRV_PROTOCOL_VERSION(0, 3, 2) #define LSM_MAX_STAGES_PER_SESSION 2 #define LSM_STAGE_INDEX_FIRST 0 @@ -33,7 +33,10 @@ #define LSM_POLLING_ENABLE (7) #define LSM_DET_EVENT_TYPE (8) #define LSM_LAB_CONTROL (9) -#define LSM_PARAMS_MAX (LSM_LAB_CONTROL + 1) +#define LSM_REG_MULTI_SND_MODEL (10) +#define LSM_DEREG_MULTI_SND_MODEL (11) +#define LSM_MULTI_SND_MODEL_CONFIDENCE_LEVELS (12) +#define LSM_PARAMS_MAX (LSM_MULTI_SND_MODEL_CONFIDENCE_LEVELS + 1) #define LSM_EVENT_NON_TIME_STAMP_MODE (0) #define LSM_EVENT_TIME_STAMP_MODE (1) @@ -226,6 +229,7 @@ struct lsm_params_info { * Member variables applicable only to V2: * @instance_id: instance id of the param to which parameter is to be set * @stage_idx: detection stage for which the param is applicable + * @model_id: an unique number to identify sound models in DSP */ struct lsm_params_info_v2 { __u32 module_id; @@ -235,6 +239,7 @@ struct lsm_params_info_v2 { __u32 param_type; __u16 instance_id; __u16 stage_idx; + __u32 model_id; }; /*