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

Commit 2273bd94 authored by Linux Build Service Account's avatar Linux Build Service Account Committed by Gerrit - the friendly Code Review server
Browse files

Merge "ASoC: msm: qdsp6v2: dynamic perf mode control"

parents 32b29f7e ba7649e3
Loading
Loading
Loading
Loading
+215 −4
Original line number Diff line number Diff line
@@ -58,6 +58,7 @@ struct snd_msm {
#define CMD_EOS_MIN_TIMEOUT_LENGTH  50
#define CMD_EOS_TIMEOUT_MULTIPLIER  (HZ * 50)
#define MAX_PB_COPY_RETRIES         3
#define MAX_PCM_STREAMS 2

static struct snd_pcm_hardware msm_pcm_hardware_capture = {
	.info =                 (SNDRV_PCM_INFO_MMAP |
@@ -117,6 +118,17 @@ static struct snd_pcm_hw_constraint_list constraints_sample_rates = {
	.mask = 0,
};

static unsigned long msm_pcm_fe_topology[MSM_FRONTEND_DAI_MAX][MAX_PCM_STREAMS];

/* default value is DTS (i.e read from device tree) */
static char const *msm_pcm_fe_topology_text[] = {
	"DTS", "ULL", "ULL_PP", "LL" };

static const struct soc_enum msm_pcm_fe_topology_enum[] = {
	SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(msm_pcm_fe_topology_text),
			    msm_pcm_fe_topology_text),
};

static void msm_pcm_route_event_handler(enum msm_pcm_routing_event event,
					void *priv_data)
{
@@ -305,8 +317,8 @@ static int msm_pcm_playback_prepare(struct snd_pcm_substream *substream)
	if (prtd->enabled)
		return 0;

	prtd->audio_client->perf_mode = pdata->perf_mode;
	pr_debug("%s: perf: %x\n", __func__, pdata->perf_mode);
	prtd->audio_client->perf_mode = pdata->perf_mode_set;
	pr_debug("%s: perf: %x\n", __func__, prtd->audio_client->perf_mode);

	switch (params_format(params)) {
	case SNDRV_PCM_FORMAT_S24_LE:
@@ -393,14 +405,14 @@ static int msm_pcm_capture_prepare(struct snd_pcm_substream *substream)

	if (prtd->enabled == IDLE) {
		pr_debug("%s:perf_mode=%d periods=%d\n", __func__,
			pdata->perf_mode, runtime->periods);
			pdata->perf_mode_set, runtime->periods);
		params = &soc_prtd->dpcm[substream->stream].hw_params;
		if ((params_format(params) == SNDRV_PCM_FORMAT_S24_LE) ||
			(params_format(params) == SNDRV_PCM_FORMAT_S24_3LE))
			bits_per_sample = 24;

		/* ULL mode is not supported in capture path */
		if (pdata->perf_mode == LEGACY_PCM_MODE)
		if (pdata->perf_mode_set == LEGACY_PCM_MODE)
			prtd->audio_client->perf_mode = LEGACY_PCM_MODE;
		else
			prtd->audio_client->perf_mode = LOW_LATENCY_PCM_MODE;
@@ -939,15 +951,41 @@ static int msm_pcm_hw_params(struct snd_pcm_substream *substream,
				struct snd_pcm_hw_params *params)
{
	struct snd_pcm_runtime *runtime = substream->runtime;
	struct snd_soc_pcm_runtime *soc_prtd = substream->private_data;
	struct msm_audio *prtd = runtime->private_data;
	struct msm_plat_data *pdata;
	struct snd_dma_buffer *dma_buf = &substream->dma_buffer;
	struct audio_buffer *buf;
	int dir, ret;
	unsigned long fe_id = soc_prtd->dai_link->be_id;
	unsigned long topology;

	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
		dir = IN;
	else
		dir = OUT;

	pdata = (struct msm_plat_data *)
		dev_get_drvdata(soc_prtd->platform->dev);
	if (!pdata) {
		ret = -EINVAL;
		pr_err("%s: platform data not populated ret: %d\n", __func__,
		       ret);
		return ret;
	}

	topology = msm_pcm_fe_topology[fe_id][substream->stream];

	if (!strcmp(msm_pcm_fe_topology_text[topology], "ULL_PP"))
		pdata->perf_mode_set = ULL_POST_PROCESSING_PCM_MODE;
	else if (!strcmp(msm_pcm_fe_topology_text[topology], "ULL"))
		pdata->perf_mode_set = ULTRA_LOW_LATENCY_PCM_MODE;
	else if (!strcmp(msm_pcm_fe_topology_text[topology], "LL"))
		pdata->perf_mode_set = LOW_LATENCY_PCM_MODE;
	else
		/* use the default from the device tree */
		pdata->perf_mode_set = pdata->perf_mode;

	ret = q6asm_audio_client_buf_alloc_contiguous(dir,
			prtd->audio_client,
			(params_buffer_bytes(params) / params_periods(params)),
@@ -1162,6 +1200,173 @@ static int msm_pcm_add_chmap_controls(struct snd_soc_pcm_runtime *rtd)
	return 0;
}

static int msm_pcm_fe_topology_info(struct snd_kcontrol *kcontrol,
				    struct snd_ctl_elem_info *uinfo)
{
	const struct soc_enum *e = &msm_pcm_fe_topology_enum[0];

	return snd_ctl_enum_info(uinfo, 1, e->items, e->texts);
}

static int msm_pcm_fe_playback_topology_get(struct snd_kcontrol *kcontrol,
				   struct snd_ctl_elem_value *ucontrol)
{
	unsigned long fe_id = kcontrol->private_value;
	unsigned long topology;

	if (fe_id >= MSM_FRONTEND_DAI_MAX) {
		pr_err("%s Received out of bound fe_id %lu\n", __func__, fe_id);
		return -EINVAL;
	}

	topology = msm_pcm_fe_topology[fe_id][SNDRV_PCM_STREAM_PLAYBACK];
	pr_debug("%s: %lu topology %s\n", __func__, fe_id,
		 msm_pcm_fe_topology_text[topology]);
	ucontrol->value.enumerated.item[0] = topology;
	return 0;
}

static int msm_pcm_fe_playback_topology_put(struct snd_kcontrol *kcontrol,
				   struct snd_ctl_elem_value *ucontrol)
{
	unsigned long fe_id = kcontrol->private_value;
	unsigned int item;

	if (fe_id >= MSM_FRONTEND_DAI_MAX) {
		pr_err("%s Received out of bound fe_id %lu\n", __func__, fe_id);
		return -EINVAL;
	}

	item = ucontrol->value.enumerated.item[0];
	if (item >= ARRAY_SIZE(msm_pcm_fe_topology_text)) {
		pr_err("%s Received out of bound topology %lu\n", __func__,
		       fe_id);
		return -EINVAL;
	}

	pr_debug("%s: %lu new topology %s\n", __func__, fe_id,
		 msm_pcm_fe_topology_text[item]);
	msm_pcm_fe_topology[fe_id][SNDRV_PCM_STREAM_PLAYBACK] = item;
	return 0;
}

static int msm_pcm_fe_capture_topology_get(struct snd_kcontrol *kcontrol,
				   struct snd_ctl_elem_value *ucontrol)
{
	unsigned long fe_id = kcontrol->private_value;
	unsigned long topology;

	if (fe_id >= MSM_FRONTEND_DAI_MAX) {
		pr_err("%s Received out of bound fe_id %lu\n", __func__, fe_id);
		return -EINVAL;
	}

	topology = msm_pcm_fe_topology[fe_id][SNDRV_PCM_STREAM_CAPTURE];
	pr_debug("%s: %lu topology %s\n", __func__, fe_id,
		 msm_pcm_fe_topology_text[topology]);
	ucontrol->value.enumerated.item[0] = topology;
	return 0;
}

static int msm_pcm_fe_capture_topology_put(struct snd_kcontrol *kcontrol,
				   struct snd_ctl_elem_value *ucontrol)
{
	unsigned long fe_id = kcontrol->private_value;
	unsigned int item;

	if (fe_id >= MSM_FRONTEND_DAI_MAX) {
		pr_err("%s Received out of bound fe_id %lu\n", __func__, fe_id);
		return -EINVAL;
	}

	item = ucontrol->value.enumerated.item[0];
	if (item >= ARRAY_SIZE(msm_pcm_fe_topology_text)) {
		pr_err("%s Received out of bound topology %lu\n", __func__,
		       fe_id);
		return -EINVAL;
	}

	pr_debug("%s: %lu new topology %s\n", __func__, fe_id,
		 msm_pcm_fe_topology_text[item]);
	msm_pcm_fe_topology[fe_id][SNDRV_PCM_STREAM_CAPTURE] = item;
	return 0;
}

static int msm_pcm_add_fe_topology_control(struct snd_soc_pcm_runtime *rtd)
{
	struct snd_pcm *pcm = rtd->pcm->streams[0].pcm;
	const char *mixer_ctl_name = "PCM_Dev";
	const char *deviceNo       = "NN";
	const char *topo_playback  = "Topology Playback";
	const char *topo_capture   = "Topology Capture";
	char *mixer_str = NULL, *mixer_str1 = NULL;
	int ctl_len, ret;
	unsigned long fe_id = rtd->dai_link->be_id;
	struct snd_kcontrol_new topology_control[2] = {
		{
			.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
			.access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
			.name =  "?",
			.info =  msm_pcm_fe_topology_info,
			.get = msm_pcm_fe_playback_topology_get,
			.put = msm_pcm_fe_playback_topology_put,
			.private_value = 0,
		},
		{
			.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
			.access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
			.name =  "?",
			.info =  msm_pcm_fe_topology_info,
			.get = msm_pcm_fe_capture_topology_get,
			.put = msm_pcm_fe_capture_topology_put,
			.private_value = 0,
		},
	};

	if (pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream) {
		ctl_len = strlen(mixer_ctl_name) + 1 + strlen(deviceNo) + 1 +
			  strlen(topo_playback) + 1;
		mixer_str = kzalloc(ctl_len, GFP_KERNEL);

		if (!mixer_str) {
			ret = -ENOMEM;
			goto err1;
		}

		snprintf(mixer_str, ctl_len, "%s %d %s", mixer_ctl_name,
			 rtd->pcm->device, topo_playback);

		topology_control[0].name = mixer_str;
		topology_control[0].private_value = fe_id;
	}

	if (pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream) {
		ctl_len = strlen(mixer_ctl_name) + 1 + strlen(deviceNo) + 1 +
			  strlen(topo_capture) + 1;
		mixer_str1 = kzalloc(ctl_len, GFP_KERNEL);

		if (!mixer_str1) {
			ret = -ENOMEM;
			goto err;
		}

		snprintf(mixer_str1, ctl_len, "%s %d %s", mixer_ctl_name,
			 rtd->pcm->device, topo_capture);

		topology_control[1].name = mixer_str1;
		topology_control[1].private_value = fe_id;
	}
	ret = snd_soc_add_platform_controls(rtd->platform, topology_control,
					    ARRAY_SIZE(topology_control));
	msm_pcm_fe_topology[fe_id][SNDRV_PCM_STREAM_PLAYBACK] = 0;
	msm_pcm_fe_topology[fe_id][SNDRV_PCM_STREAM_CAPTURE] = 0;
	kfree(mixer_str1);
err:
	kfree(mixer_str);
err1:
	return ret;
}

static int msm_pcm_playback_app_type_cfg_ctl_put(struct snd_kcontrol *kcontrol,
					struct snd_ctl_elem_value *ucontrol)
{
@@ -1349,6 +1554,12 @@ static int msm_pcm_add_controls(struct snd_soc_pcm_runtime *rtd)
	if (ret)
		pr_err("%s: pcm add app type controls failed:%d\n",
			__func__, ret);

	ret = msm_pcm_add_fe_topology_control(rtd);
	if (ret) {
		pr_err("%s: Could not add pcm topology control %d\n",
			__func__, ret);
	}
	return ret;
}

+2 −1
Original line number Diff line number Diff line
/*
 * Copyright (C) 2008 Google, Inc.
 * Copyright (C) 2008 HTC Corporation
 * Copyright (c) 2012-2016 The Linux Foundation. All rights reserved.
 * Copyright (c) 2012-2017 The Linux Foundation. All rights reserved.
 *
 * This software is licensed under the terms of the GNU General Public
 * License version 2, as published by the Free Software Foundation, and
@@ -123,6 +123,7 @@ struct output_meta_data_st {

struct msm_plat_data {
	int perf_mode;
	int perf_mode_set;
};

#endif /*_MSM_PCM_H*/