Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 7581982c authored by Srinivasa Reddy M N's avatar Srinivasa Reddy M N Committed by Gerrit - the friendly Code Review server
Browse files

ASoC: Add support "adm cmd register" and "adm pp" events



Added support for "ADM_CMD_REGISTER_EVENT" and "ADM_PP_EVENT"
events.

Signed-off-by: default avatarSrinivasa Reddy M N <quic_c_mullan@quicinc.com>
Change-Id: I80d8dc6d9fccdac67ea616d19415aa2133a3f370
parent 5fb54fcd
Loading
Loading
Loading
Loading
+28 −1
Original line number Diff line number Diff line
@@ -565,6 +565,8 @@ static int msm_pcm_playback_prepare(struct snd_pcm_substream *substream)
	uint16_t format = 0;
	uint16_t req_format = 0;
	uint16_t input_file_format = 0;
	int port_id = 0, copp_idx = -1;
	bool found = false;

	if (!component) {
		pr_err("%s: component is NULL\n", __func__);
@@ -691,6 +693,15 @@ static int msm_pcm_playback_prepare(struct snd_pcm_substream *substream)
		return ret;
	}

	found = msm_pcm_routing_get_portid_copp_idx(soc_prtd->dai_link->id,
				SESSION_TYPE_RX, &port_id, &copp_idx);
	if (found) {
		q6adm_update_rtd_info(soc_prtd, port_id, copp_idx,
					soc_prtd->dai_link->id, 1);
	} else {
		pr_err("%s: copp_idx not found\n", __func__);
	}

	/*Format block is configure with input file bits_per_sample*/
	switch (input_file_format) {
	case SNDRV_PCM_FORMAT_S32_LE:
@@ -1070,8 +1081,10 @@ static int msm_pcm_open(struct snd_pcm_substream *substream)
	prtd->reset_event = false;
	runtime->private_data = prtd;

	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
		msm_adsp_init_mixer_ctl_pp_event_queue(soc_prtd);
		msm_adsp_init_mixer_ctl_adm_pp_event_queue(soc_prtd);
	}

	/* Vote to update the Rx thread priority to RT Thread for playback */
	if ((substream->stream == SNDRV_PCM_STREAM_PLAYBACK) &&
@@ -1189,6 +1202,8 @@ static int msm_pcm_playback_close(struct snd_pcm_substream *substream)
	uint32_t timeout;
	int dir = 0;
	int ret = 0;
	int port_id = 0, copp_idx = -1;
	bool found = false;

	pr_debug("%s: cmd_pending 0x%lx\n", __func__, prtd->cmd_pending);

@@ -1239,9 +1254,21 @@ static int msm_pcm_playback_close(struct snd_pcm_substream *substream)
					prtd->audio_client);
		q6asm_audio_client_free(prtd->audio_client);
	}

	found = msm_pcm_routing_get_portid_copp_idx(soc_prtd->dai_link->id,
				SESSION_TYPE_RX, &port_id, &copp_idx);
	if (found) {
		q6adm_update_rtd_info(soc_prtd, port_id, copp_idx,
					soc_prtd->dai_link->id, 0);
		q6adm_clear_callback();
	} else {
		pr_err("%s: copp_idx not found\n", __func__);
	}

	msm_pcm_routing_dereg_phy_stream(soc_prtd->dai_link->id,
						SNDRV_PCM_STREAM_PLAYBACK);
	msm_adsp_clean_mixer_ctl_pp_event_queue(soc_prtd);
	msm_adsp_clean_mixer_ctl_adm_pp_event_queue(soc_prtd);
	kfree(prtd);
	runtime->private_data = NULL;
	mutex_unlock(&pdata->lock);

asoc/msm-pcm-routing-auto.c

100755 → 100644
+339 −0
Original line number Diff line number Diff line
@@ -103,6 +103,10 @@ static int afe_loopback_tx_port_id = -1;
static struct msm_pcm_channel_mixer ec_ref_chmix_cfg[MSM_FRONTEND_DAI_MAX];
static struct msm_ec_ref_port_cfg ec_ref_port_cfg;
static uint32_t adm_pp_reg_event_opcode[] = {
        ADM_CMD_REGISTER_EVENT
};
#define WEIGHT_0_DB 0x4000
/* all the FEs which can support channel mixer */
static struct msm_pcm_channel_mixer channel_mixer[MSM_FRONTEND_DAI_MM_SIZE];
@@ -2418,6 +2422,56 @@ int msm_pcm_routing_set_channel_mixer_runtime(int be_id, int session_id,
}
EXPORT_SYMBOL(msm_pcm_routing_set_channel_mixer_runtime);
/*
 * msm_pcm_routing_get_portid_copp_idx:
 *	update the port_id and copp_idx for a given
 *	fe_id
 *
 * @fe_id: front end id
 * @session_type: indicates session is of type TX or RX
 * port: port_id to be updated as output
 * copp_idx: copp_idx is updated as output
 * @stream_type: indicates either Audio or Listen stream type
 */
bool msm_pcm_routing_get_portid_copp_idx(int fe_id,
				int session_type, int *port, int *copp_idx)
{
	int idx = 0;
	bool found = false;
	int be_index = 0, port_id = 0;
	pr_debug("%s:fe_id[%d] sess_type [%d]\n",
		 __func__, fe_id, session_type);
	if (!is_mm_lsm_fe_id(fe_id)) {
		/* bad ID assigned in machine driver */
		pr_err("%s: bad MM ID %d\n", __func__, fe_id);
		return -EINVAL;
	}
	for (be_index = 0; be_index < MSM_BACKEND_DAI_MAX; be_index++) {
		port_id = msm_bedais[be_index].port_id;
		if (!msm_bedais[be_index].active ||
		    !test_bit(fe_id, &msm_bedais[be_index].fe_sessions[0]))
			continue;
		for (idx = 0; idx < MAX_COPPS_PER_PORT; idx++) {
			unsigned long copp_id =
				session_copp_map[fe_id][session_type][be_index];
			if (test_bit(idx, &copp_id)) {
				pr_debug("%s: port_id: %d, copp_idx:%d\n",
				       __func__, port_id, idx);
				*port = port_id;
				*copp_idx = idx;
				found = true;
				break;
			}
		}
	}
	return found;
}
EXPORT_SYMBOL(msm_pcm_routing_get_portid_copp_idx);
int msm_pcm_routing_reg_phy_stream(int fedai_id, int perf_mode,
					int dspst_id, int stream_type)
{
@@ -32481,6 +32535,273 @@ static void msm_routing_unload_topology(uint32_t topology_id)
}
static int msm_audio_get_copp_idx_from_port_id(int port_id, int session_type,
					 int *copp_idx)
{
	int i = 0, idx = 0, be_idx = 0;
	int ret = 0;
	unsigned long copp = 0;
	pr_debug("%s: Enter, port_id=%d\n", __func__, port_id);
	ret = q6audio_validate_port(port_id);
	if (ret < 0) {
		pr_err("%s: port validation failed id 0x%x ret %d\n",
			__func__, port_id, ret);
		ret = -EINVAL;
		goto done;
	}
	for (be_idx = 0; be_idx < MSM_BACKEND_DAI_MAX; be_idx++) {
		if (msm_bedais[be_idx].port_id == port_id)
			break;
	}
	if (be_idx >= MSM_BACKEND_DAI_MAX) {
		pr_err("%s: Invalid be id %d\n", __func__, be_idx);
		ret = -EINVAL;
		goto done;
	}
	for_each_set_bit(i, &msm_bedais[be_idx].fe_sessions[0],
			 MSM_FRONTEND_DAI_MAX) {
		if (!(is_mm_lsm_fe_id(i) &&
				route_check_fe_id_adm_support(i)))
			continue;
		for (idx = 0; idx < MAX_COPPS_PER_PORT; idx++) {
			copp = session_copp_map[i]
				[session_type][be_idx];
			if (test_bit(idx, &copp))
				break;
		}
		if (idx >= MAX_COPPS_PER_PORT)
			continue;
		else
			break;
	}
	if (i >= MSM_FRONTEND_DAI_MAX) {
		pr_err("%s: Invalid FE, exiting\n", __func__);
		ret = -EINVAL;
		goto done;
	}
	*copp_idx = idx;
	pr_debug("%s: copp_idx=%d\n", __func__, *copp_idx);
done:
	return ret;
}
static int msm_routing_put_copp_dtmf_module_enable
		(struct snd_kcontrol *kcontrol,
				struct snd_ctl_elem_value *ucontrol)
{
	uint8_t *be_id = NULL, be_id_val = 0;
	int copp_idx = -1, port_id = 0;
	struct param_hdr_v3 param_hdr = {0};
	int rc = 0;
	u32 flag = (bool)ucontrol->value.integer.value[0];
	be_id = (uint8_t *)ucontrol->value.bytes.data;
	be_id_val = *be_id;
	if((be_id_val < 0) || (be_id_val >= MSM_BACKEND_DAI_MAX)) {
		pr_err("%s: received invalid be_id %lu\n", __func__, be_id_val);
		return -EINVAL;
	}
	port_id = msm_bedais[be_id_val].port_id;
	rc = msm_audio_get_copp_idx_from_port_id(port_id, SESSION_TYPE_RX,
					    &copp_idx);
	if (rc) {
		pr_err(" %s:failure in getting copp_idx\n", __func__);
		return rc;
	}
	memset(&param_hdr, 0, sizeof(param_hdr));
	param_hdr.module_id = AUDPROC_MODULE_ID_DTMF_DETECTION;
	param_hdr.instance_id = INSTANCE_ID_0;
	param_hdr.param_id = AUDPROC_PARAM_ID_ENABLE;
	param_hdr.param_size = 4;
	rc = adm_pack_and_set_one_pp_param(port_id, copp_idx, param_hdr,
					   (u8 *)&flag);
	if (rc)
		pr_err("%s: Failed to set adm_pack_and_set_one_pp_param, err %d\n",
					__func__, rc);
	return rc;
}
static const struct snd_kcontrol_new copp_dtmf_detect_enable_mixer_controls[] = {
	SOC_SINGLE_EXT("MultiMedia1 COPP DTMF Detect Enable", SND_SOC_NOPM,
	MSM_FRONTEND_DAI_MULTIMEDIA1, 1, 0, NULL,
	msm_routing_put_copp_dtmf_module_enable),
	SOC_SINGLE_EXT("MultiMedia6 COPP DTMF Detect Enable", SND_SOC_NOPM,
	MSM_FRONTEND_DAI_MULTIMEDIA6, 1, 0, NULL,
	msm_routing_put_copp_dtmf_module_enable),
	SOC_SINGLE_EXT("MultiMedia21 COPP DTMF Detect Enable", SND_SOC_NOPM,
	MSM_FRONTEND_DAI_MULTIMEDIA21, 1, 0, NULL,
	msm_routing_put_copp_dtmf_module_enable),
};
static int adsp_copp_event_handler(uint32_t opcode,
		uint32_t token_adm, uint32_t *payload, void *priv)
{
	struct snd_soc_pcm_runtime *rtd = priv;
	int ret = 0;
	if (!rtd) {
		pr_err("%s: rtd is NULL\n", __func__);
		return -EINVAL;
	}
	ret = msm_adsp_copp_inform_mixer_ctl(rtd, payload);
	if (ret) {
		pr_err("%s: failed to inform mixer ctrl. err = %d\n",
			__func__, ret);
		return -EINVAL;
	}
	return ret;
}
static int msm_routing_get_copp_callback_event(struct snd_kcontrol *kcontrol,
					struct snd_ctl_elem_value *ucontrol)
{
	uint32_t payload_size = 0;
	struct dsp_adm_callback_list *oldest_event = NULL;
	unsigned long spin_flags = 0;
	struct dsp_adm_callback_prtd *kctl_prtd = NULL;
	int ret = 0;
	kctl_prtd = (struct dsp_adm_callback_prtd *)
				kcontrol->private_data;
	if (kctl_prtd == NULL) {
		pr_err("%s: ADM PP event queue is not initialized.\n",
				__func__);
		ret = -EINVAL;
		goto done;
	}
	spin_lock_irqsave(&kctl_prtd->prtd_spin_lock, spin_flags);
	pr_debug("%s: %d events in queue.\n", __func__, kctl_prtd->event_count);
	if (list_empty(&kctl_prtd->event_queue)) {
		pr_err("%s: ADM PP event queue is empty.\n", __func__);
		ret = -EINVAL;
		spin_unlock_irqrestore(&kctl_prtd->prtd_spin_lock, spin_flags);
		goto done;
	}
	oldest_event = list_first_entry(&kctl_prtd->event_queue,
					struct dsp_adm_callback_list, list);
	list_del(&oldest_event->list);
	kctl_prtd->event_count--;
	spin_unlock_irqrestore(&kctl_prtd->prtd_spin_lock, spin_flags);
	payload_size = oldest_event->event.payload_len;
	pr_debug("%s: event fetched: type %d length %d\n",
				__func__, oldest_event->event.event_type,
				oldest_event->event.payload_len);
	memcpy(ucontrol->value.bytes.data, &oldest_event->event,
			sizeof(struct msm_adsp_event_data) + payload_size);
	kfree(oldest_event);
done:
	return ret;
}
static int msm_routing_put_copp_callback_event(struct snd_kcontrol *kcontrol,
					struct snd_ctl_elem_value *ucontrol)
{
	pr_debug("%s\n", __func__);
	return 0;
}
static int msm_copp_callback_event_info(struct snd_kcontrol *kcontrol,
			struct snd_ctl_elem_info *uinfo)
{
	uinfo->type = SNDRV_CTL_ELEM_TYPE_BYTES;
	uinfo->count =
		sizeof(((struct snd_ctl_elem_value *)0)->value.bytes.data);
	return 0;
}
static const struct snd_kcontrol_new copp_callback_event_controls[] = {
	{
		.access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
		.iface  = SNDRV_CTL_ELEM_IFACE_MIXER,
		.name   = "ADSP COPP Callback Event",
		.info   = msm_copp_callback_event_info,
		.get    = msm_routing_get_copp_callback_event,
		.put    = msm_routing_put_copp_callback_event,
	},
};
int msm_copp_event_info(struct snd_kcontrol *kcontrol,
			struct snd_ctl_elem_info *uinfo)
{
	uinfo->type = SNDRV_CTL_ELEM_TYPE_BYTES;
	uinfo->count =
		sizeof(((struct snd_ctl_elem_value *)0)->value.bytes.data);
	return 0;
}
static int msm_routing_get_copp_event_cmd(struct snd_kcontrol *kcontrol,
					struct snd_ctl_elem_value *ucontrol)
{
	pr_debug("%s\n", __func__);
	return 0;
}
static int msm_routing_put_copp_event_cmd(struct snd_kcontrol *kcontrol,
					struct snd_ctl_elem_value *ucontrol)
{
	struct msm_adm_event_data *ev = NULL;
	int be_id = 0, ret = 0, param_size = 0, opcode = 0;
	int copp_idx = -1, port_id = 0;
	ev = (struct msm_adm_event_data *)ucontrol->value.bytes.data;
	param_size = ev->payload_length - sizeof(struct module_info_data);
	opcode = adm_pp_reg_event_opcode[(ev->event_type)
						- ADSP_ADM_SERVICE_ID];
	be_id = ev->mod_info.be_id;
	port_id = msm_bedais[be_id].port_id;
	pr_debug("%s: port_id= 0x%x, be_id=%d\n", __func__,port_id, be_id);
	ret = msm_audio_get_copp_idx_from_port_id(port_id, SESSION_TYPE_RX,
					    &copp_idx);
	if (ret) {
		pr_debug("%s:failure in getting copp_idx\n", __func__);
		return ret;
	}
	q6adm_register_callback(&adsp_copp_event_handler);
	q6adm_send_event_register_cmd(port_id, copp_idx, (u8 *) (ev->payload),
					param_size, opcode);
	return ret;
}
static const struct snd_kcontrol_new copp_event_controls[] = {
	{
		.access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
		.iface  = SNDRV_CTL_ELEM_IFACE_MIXER,
		.name   = "COPP Event Cmd",
		.info   = msm_copp_event_info,
		.get    = msm_routing_get_copp_event_cmd,
		.put    = msm_routing_put_copp_event_cmd,
	},
};
static int msm_routing_put_device_pp_params_mixer(struct snd_kcontrol *kcontrol,
					struct snd_ctl_elem_value *ucontrol)
{
@@ -33661,6 +33982,7 @@ static void snd_soc_dapm_add_routes_aux_pcm(struct snd_soc_component *component)
/* Not used but frame seems to require it */
static int msm_routing_probe(struct snd_soc_component *component)
{
	struct snd_kcontrol *kctl = NULL;
	snd_soc_dapm_new_controls(&component->dapm, msm_qdsp6_widgets,
			   ARRAY_SIZE(msm_qdsp6_widgets));
@@ -33718,10 +34040,27 @@ static int msm_routing_probe(struct snd_soc_component *component)
				      ARRAY_SIZE(asrc_config_controls));
	snd_soc_add_component_controls(component, asrc_start_controls,
					ARRAY_SIZE(asrc_start_controls));
	snd_soc_add_component_controls(component,
			copp_dtmf_detect_enable_mixer_controls,
			ARRAY_SIZE(copp_dtmf_detect_enable_mixer_controls));
#ifdef CONFIG_MSM_INTERNAL_MCLK
	snd_soc_add_component_controls(component, internal_mclk_control,
				      ARRAY_SIZE(internal_mclk_control));
#endif
	snd_soc_add_component_controls(component, copp_callback_event_controls,
				  ARRAY_SIZE(copp_callback_event_controls));
	snd_soc_add_component_controls(component, copp_event_controls,
				  ARRAY_SIZE(copp_event_controls));
	kctl = snd_soc_card_get_kcontrol(component->dapm.card,
				"ADSP COPP Callback Event");
	if (!kctl) {
		pr_err("%s: failed to get kctl %s.\n", __func__,
				"ADSP COPP Callback Event");
		return -EINVAL;
	}
	kctl->private_data = NULL;
	return 0;
}

asoc/msm-pcm-routing-v2.c

100755 → 100644
+50 −0
Original line number Diff line number Diff line
@@ -2458,6 +2458,56 @@ int msm_pcm_routing_set_channel_mixer_runtime(int be_id, int session_id,
}
EXPORT_SYMBOL(msm_pcm_routing_set_channel_mixer_runtime);
/*
 * msm_pcm_routing_get_portid_copp_idx:
 *	update the port_id and copp_idx for a given
 *	fe_id
 *
 * @fe_id: front end id
 * @session_type: indicates session is of type TX or RX
 * port: port_id to be updated as output
 * copp_idx: copp_idx is updated as output
 * @stream_type: indicates either Audio or Listen stream type
 */
bool msm_pcm_routing_get_portid_copp_idx(int fe_id,
				int session_type, int *port, int *copp_idx)
{
	int idx = 0;
	bool found = false;
	int be_index = 0, port_id = 0;
	pr_debug("%s:fe_id[%d] sess_type [%d]\n",
		 __func__, fe_id, session_type);
	if (!is_mm_lsm_fe_id(fe_id)) {
		/* bad ID assigned in machine driver */
		pr_err("%s: bad MM ID %d\n", __func__, fe_id);
		return -EINVAL;
	}
	for (be_index = 0; be_index < MSM_BACKEND_DAI_MAX; be_index++) {
		port_id = msm_bedais[be_index].port_id;
		if (!msm_bedais[be_index].active ||
		    !test_bit(fe_id, &msm_bedais[be_index].fe_sessions[0]))
			continue;
		for (idx = 0; idx < MAX_COPPS_PER_PORT; idx++) {
			unsigned long copp_id =
				session_copp_map[fe_id][session_type][be_index];
			if (test_bit(idx, &copp_id)) {
				pr_debug("%s: port_id: %d, copp_idx:%d\n",
				       __func__, port_id, idx);
				*port = port_id;
				*copp_idx = idx;
				found = true;
				break;
			}
		}
	}
	return found;
}
EXPORT_SYMBOL(msm_pcm_routing_get_portid_copp_idx);
int msm_pcm_routing_reg_phy_stream(int fedai_id, int perf_mode,
					int dspst_id, int stream_type)
{
+3 −0
Original line number Diff line number Diff line
@@ -897,4 +897,7 @@ int snd_pcm_add_usr_ctls(struct snd_pcm *pcm, int stream,
    unsigned long private_value,
    struct snd_pcm_usr **info_ret);
#endif

bool msm_pcm_routing_get_portid_copp_idx(int fe_id,
	int session_type, int *port_id, int *copp_idx);
#endif /*_MSM_PCM_H*/
+211 −0
Original line number Diff line number Diff line
@@ -1207,6 +1207,217 @@ int msm_adsp_clean_mixer_ctl_pp_event_queue(struct snd_soc_pcm_runtime *rtd)
	return ret;
}

int msm_adsp_init_mixer_ctl_adm_pp_event_queue(struct snd_soc_pcm_runtime *rtd)
{
	struct snd_kcontrol *kctl = NULL;
	char *mixer_str = NULL;
	int ctl_len = 0, ret = 0;
	const char *mixer_ctl_name = DSP_ADM_CALLBACK;
	struct dsp_adm_callback_prtd *kctl_prtd = NULL;

	if (!rtd) {
		pr_err("%s: rtd is NULL\n", __func__);
		ret = -EINVAL;
		goto done;
	}

	ctl_len = strlen(mixer_ctl_name) + 1;
	mixer_str = kzalloc(ctl_len, GFP_KERNEL);
	if (!mixer_str) {
		ret = -EINVAL;
		goto done;
	}

	snprintf(mixer_str, ctl_len, "%s", mixer_ctl_name);
	kctl = snd_soc_card_get_kcontrol(rtd->card, mixer_str);
	kfree(mixer_str);
	if (!kctl) {
		pr_err("%s: failed to get kctl.\n", __func__);
		ret = -EINVAL;
		goto done;
	}

	if (kctl->private_data != NULL) {
		pr_err("%s: kctl_prtd is not NULL at initialization.\n",
			__func__);
		return -EINVAL;
	}

	kctl_prtd = kzalloc(sizeof(struct dsp_adm_callback_prtd),
			GFP_KERNEL);
	if (!kctl_prtd) {
		ret = -ENOMEM;
		goto done;
	}

	spin_lock_init(&kctl_prtd->prtd_spin_lock);
	INIT_LIST_HEAD(&kctl_prtd->event_queue);
	kctl_prtd->event_count = 0;
	kctl->private_data = kctl_prtd;

done:
	return ret;
}

int msm_adsp_clean_mixer_ctl_adm_pp_event_queue(struct snd_soc_pcm_runtime *rtd)
{
	struct snd_kcontrol *kctl = NULL;
	char *mixer_str = NULL;
	int ctl_len = 0, ret = 0;
	struct dsp_adm_callback_list *node = NULL, *n = NULL;
	unsigned long spin_flags = 0;
	const char *mixer_ctl_name = DSP_ADM_CALLBACK;
	struct dsp_adm_callback_prtd *kctl_prtd = NULL;

	if (!rtd) {
		pr_err("%s: rtd is NULL\n", __func__);
		ret = -EINVAL;
		goto done;
	}

	ctl_len = strlen(mixer_ctl_name) + 1;
	mixer_str = kzalloc(ctl_len, GFP_KERNEL);
	if (!mixer_str) {
		ret = -EINVAL;
		goto done;
	}

	snprintf(mixer_str, ctl_len, "%s", mixer_ctl_name);
	kctl = snd_soc_card_get_kcontrol(rtd->card, mixer_str);
	kfree(mixer_str);
	if (!kctl) {
		pr_err("%s: failed to get kctl.\n", __func__);
		ret = -EINVAL;
		goto done;
	}

	kctl_prtd = (struct dsp_adm_callback_prtd *)
			kctl->private_data;
	if (kctl_prtd != NULL) {
		spin_lock_irqsave(&kctl_prtd->prtd_spin_lock, spin_flags);
		/* clean the queue */
		list_for_each_entry_safe(node, n,
				&kctl_prtd->event_queue, list) {
			list_del(&node->list);
			kctl_prtd->event_count--;
			pr_debug("%s: %d remaining events after del.\n",
				__func__, kctl_prtd->event_count);
			kfree(node);
		}
		spin_unlock_irqrestore(&kctl_prtd->prtd_spin_lock, spin_flags);
	}

	kfree(kctl_prtd);
	kctl->private_data = NULL;

done:
	return ret;
}

int msm_adsp_copp_inform_mixer_ctl(struct snd_soc_pcm_runtime *rtd,
			uint32_t *payload)
{
	/* adsp adm pp event notifier */
	struct snd_kcontrol *kctl = NULL;
	struct snd_ctl_elem_value control = {0};
	char *mixer_str = NULL;
	int ctl_len = 0, ret = 0;
	struct dsp_adm_callback_list *new_event = NULL;
	struct dsp_adm_callback_list *oldest_event = NULL;
	unsigned long spin_flags = 0;
	struct dsp_adm_callback_prtd *kctl_prtd = NULL;
	struct msm_adsp_event_data *event_data = NULL;
	const char *mixer_ctl_name = DSP_ADM_CALLBACK;
	struct snd_ctl_elem_info kctl_info = {0};

	if (!rtd || !payload) {
		pr_err("%s: %s is NULL\n", __func__,
			(!rtd) ? "rtd" : "payload");
		ret = -EINVAL;
		goto done;
	}

	if (rtd->card->snd_card == NULL) {
		pr_err("%s: snd_card is null.\n", __func__);
		ret = -EINVAL;
		goto done;
	}

	ctl_len = strlen(mixer_ctl_name) + 1;
	mixer_str = kzalloc(ctl_len, GFP_ATOMIC);
	if (!mixer_str) {
		ret = -EINVAL;
		goto done;
	}

	snprintf(mixer_str, ctl_len, "%s", mixer_ctl_name);
	kctl = snd_soc_card_get_kcontrol(rtd->card, mixer_str);
	kfree(mixer_str);
	if (!kctl) {
		pr_err("%s: failed to get kctl.\n", __func__);
		ret = -EINVAL;
		goto done;
	}

	event_data = (struct msm_adsp_event_data *)payload;
	kctl->info(kctl, &kctl_info);
	if (sizeof(struct msm_adsp_event_data)
		+ event_data->payload_len > kctl_info.count) {
		pr_err("%s: payload length exceeds limit of %u bytes.\n",
			__func__, kctl_info.count);
		ret = -EINVAL;
		goto done;
	}

	kctl_prtd = (struct dsp_adm_callback_prtd *)
			kctl->private_data;
	if (kctl_prtd == NULL) {
		/* queue is not initialized */
		ret = -EINVAL;
		pr_err("%s: event queue is not initialized.\n", __func__);
		goto done;
	}

	new_event = kzalloc(sizeof(struct dsp_adm_callback_list)
			+ event_data->payload_len + sizeof(uint32_t),
			GFP_ATOMIC);
	if (new_event == NULL) {
		ret = -ENOMEM;
		goto done;
	}
	memcpy((void *)&new_event->event, (void *)payload,
		   event_data->payload_len
		   + sizeof(struct msm_adsp_event_data));


	spin_lock_irqsave(&kctl_prtd->prtd_spin_lock, spin_flags);
	while (kctl_prtd->event_count >= DSP_ADM_CALLBACK_QUEUE_SIZE) {
		pr_info("%s: queue of size %d is full. delete oldest one.\n",
			__func__, DSP_ADM_CALLBACK_QUEUE_SIZE);
		oldest_event = list_first_entry(&kctl_prtd->event_queue,
				struct dsp_adm_callback_list, list);
		pr_info("%s: event deleted: type %d length %d\n",
			__func__, oldest_event->event.event_type,
			oldest_event->event.payload_len);
		list_del(&oldest_event->list);
		kctl_prtd->event_count--;
		kfree(oldest_event);
	}

	list_add_tail(&new_event->list, &kctl_prtd->event_queue);
	kctl_prtd->event_count++;
	spin_unlock_irqrestore(&kctl_prtd->prtd_spin_lock, spin_flags);

	control.id = kctl->id;

	snd_ctl_notify(rtd->card->snd_card,
			SNDRV_CTL_EVENT_MASK_INFO,
			&control.id);

done:
	return ret;
}

int msm_adsp_inform_mixer_ctl(struct snd_soc_pcm_runtime *rtd,
			uint32_t *payload)
{
Loading