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

Commit 3987721c authored by Guodong Hu's avatar Guodong Hu
Browse files

asoc: support non-runtime per-stream, per-device asm channel map



For non-typical channel number playback and capture, we need to
provide asm channel map info, otherwise pcm prepare will fail.

But there is no such default asm map info in audio drivers.

This change is to provide mechanism to set asm channel map, to
ununblock non-typical channel playback and capture.

Change-Id: Idad08c6f3f50b36f5c9c0a813bc0ef493cc57c59
Signed-off-by: default avatarGuodong Hu <guodhu@codeaurora.org>
parent 965a68a2
Loading
Loading
Loading
Loading

asoc/msm-compress-q6-v2.c

100755 → 100644
+2 −1
Original line number Diff line number Diff line
// SPDX-License-Identifier: GPL-2.0-only
/* Copyright (c) 2012-2020, The Linux Foundation. All rights reserved.
/* Copyright (c) 2012-2021, The Linux Foundation. All rights reserved.
 */


@@ -1880,6 +1880,7 @@ static int msm_compr_configure_dsp_for_capture(struct snd_compr_stream *cstream)
			ret = q6asm_enc_cfg_blk_pcm_format_support_v5(
					prtd->audio_client,
					prtd->sample_rate, prtd->num_channels,
					true, NULL,
					bits_per_sample, sample_word_size,
					ASM_LITTLE_ENDIAN, DEFAULT_QF);
		else
+145 −70
Original line number Diff line number Diff line
@@ -59,6 +59,11 @@ struct snd_msm {
	struct snd_pcm *pcm;
};

struct msm_pcm_channel_map {
	bool set_channel_map;
	char channel_map[PCM_FORMAT_MAX_NUM_CHANNEL_V8];
};

#define CMD_EOS_MIN_TIMEOUT_LENGTH  50
#define CMD_EOS_TIMEOUT_MULTIPLIER  (HZ * 50)
#define MAX_PB_COPY_RETRIES         3
@@ -123,6 +128,8 @@ static struct snd_pcm_hw_constraint_list constraints_sample_rates = {
	.mask = 0,
};

struct msm_pcm_channel_map *chmap_pspd[MSM_FRONTEND_DAI_MM_SIZE][2];

static void msm_pcm_route_event_handler(enum msm_pcm_routing_event event,
					void *priv_data)
{
@@ -324,6 +331,23 @@ static void event_handler(uint32_t opcode,
	}
}

static struct msm_pcm_channel_map *msm_pcm_get_chmap(u64 fe_id,
			int session_type)
{
	if (fe_id >= MSM_FRONTEND_DAI_MM_SIZE) {
		pr_err("%s: invalid FE %llu\n", __func__, fe_id);
		return NULL;
	}

	if ((session_type != SESSION_TYPE_TX) &&
		(session_type != SESSION_TYPE_RX)) {
		pr_err("%s: invalid session type %d\n", __func__, session_type);
		return NULL;
	}

	return chmap_pspd[fe_id][session_type];
}

static int msm_pcm_playback_prepare(struct snd_pcm_substream *substream)
{
	struct snd_pcm_runtime *runtime = substream->runtime;
@@ -333,6 +357,7 @@ static int msm_pcm_playback_prepare(struct snd_pcm_substream *substream)
	struct msm_audio *prtd = runtime->private_data;
	struct msm_plat_data *pdata;
	struct snd_pcm_hw_params *params;
	struct msm_pcm_channel_map *chmap;
	int ret;
	uint32_t fmt_type = FORMAT_LINEAR_PCM;
	uint16_t bits_per_sample;
@@ -356,6 +381,12 @@ static int msm_pcm_playback_prepare(struct snd_pcm_substream *substream)
	}
	params = &soc_prtd->dpcm[substream->stream].hw_params;

	chmap = msm_pcm_get_chmap(soc_prtd->dai_link->id, SESSION_TYPE_RX);
	if (!chmap) {
		pr_err("%s: invalid chmap handle\n", __func__);
		return -EINVAL;
	}

	pr_debug("%s\n", __func__);
	prtd->pcm_size = snd_pcm_lib_buffer_bytes(substream);
	prtd->pcm_count = snd_pcm_lib_period_bytes(substream);
@@ -455,8 +486,8 @@ static int msm_pcm_playback_prepare(struct snd_pcm_substream *substream)

			ret = q6asm_media_format_block_multi_ch_pcm_v5(
				prtd->audio_client, runtime->rate,
				runtime->channels, !prtd->set_channel_map,
				prtd->channel_map, bits_per_sample,
				runtime->channels, !chmap->set_channel_map,
				chmap->channel_map, bits_per_sample,
				sample_word_size, ASM_LITTLE_ENDIAN,
				DEFAULT_QF);
		} else {
@@ -469,7 +500,7 @@ static int msm_pcm_playback_prepare(struct snd_pcm_substream *substream)
		}
	}
	if (ret < 0)
		pr_info("%s: CMD Format block failed\n", __func__);
		pr_err("%s: CMD Format block failed\n", __func__);

	atomic_set(&prtd->out_count, runtime->periods);

@@ -490,6 +521,7 @@ static int msm_pcm_capture_prepare(struct snd_pcm_substream *substream)
	struct msm_plat_data *pdata;
	struct snd_pcm_hw_params *params;
	struct msm_pcm_routing_evt event;
	struct msm_pcm_channel_map *chmap;
	int ret = 0;
	int i = 0;
	uint16_t bits_per_sample = 16;
@@ -512,6 +544,12 @@ static int msm_pcm_capture_prepare(struct snd_pcm_substream *substream)
		return -EINVAL;
	}

	chmap = msm_pcm_get_chmap(soc_prtd->dai_link->id, SESSION_TYPE_TX);
	if (!chmap) {
		pr_err("%s: invalid chmap handle\n", __func__);
		return -EINVAL;
	}

	if (prtd->enabled == IDLE) {
		pr_debug("%s:perf_mode=%d periods=%d\n", __func__,
			pdata->perf_mode, runtime->periods);
@@ -616,6 +654,8 @@ static int msm_pcm_capture_prepare(struct snd_pcm_substream *substream)
						prtd->audio_client,
						prtd->samp_rate,
						prtd->channel_mode,
						!chmap->set_channel_map,
						chmap->channel_map,
						bits_per_sample,
						sample_word_size,
						ASM_LITTLE_ENDIAN,
@@ -1835,9 +1875,12 @@ static int msm_pcm_chmap_ctl_put(struct snd_kcontrol *kcontrol,
	struct msm_plat_data *pdata = NULL;
	struct msm_pcm_channel_mixer *chmixer_pspd = NULL;
	struct snd_soc_component *component = NULL;
	u64 fe_id = 0;
	struct msm_pcm_channel_map *chmap;
	u64 fe_id = kcontrol->private_value & 0xFF;
	int session_type = (kcontrol->private_value >> 8) & 0xFF;

	pr_debug("%s", __func__);
	pr_debug("%s: chmap ctl for fe_id: %d, session_type: %d\n",
			__func__, fe_id, session_type);
	substream = snd_pcm_chmap_substream(info, idx);
	if (!substream)
		return -ENODEV;
@@ -1858,6 +1901,17 @@ static int msm_pcm_chmap_ctl_put(struct snd_kcontrol *kcontrol,
		}
	}

	chmap = msm_pcm_get_chmap(fe_id, session_type);
	if (!chmap) {
		pr_err("%s: invalid chmap handle\n", __func__);
		return -EINVAL;
	}
	for (i = 0; i < PCM_FORMAT_MAX_NUM_CHANNEL_V8; i++)
		chmap->channel_map[i] =
			ucontrol->value.integer.value[i];

	chmap->set_channel_map = true;

	if (!rtd)
		return 0;

@@ -1899,60 +1953,23 @@ static int msm_pcm_chmap_ctl_put(struct snd_kcontrol *kcontrol,
static int msm_pcm_chmap_ctl_get(struct snd_kcontrol *kcontrol,
				struct snd_ctl_elem_value *ucontrol)
{
	int i;
	struct snd_pcm_chmap *info = snd_kcontrol_chip(kcontrol);
	unsigned int idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
	struct snd_pcm_substream *substream;
	struct msm_audio *prtd;
	struct snd_soc_pcm_runtime *rtd = NULL;
	struct msm_plat_data *pdata = NULL;
	struct snd_soc_component *component = NULL;

	pr_debug("%s", __func__);
	substream = snd_pcm_chmap_substream(info, idx);
	if (!substream)
		return -ENODEV;

	rtd = substream->private_data;
	if (rtd) {
		component = snd_soc_rtdcom_lookup(rtd, DRV_NAME);
		if (component) {
			pdata = (struct msm_plat_data *)
						dev_get_drvdata(component->dev);
			if (!pdata) {
				pr_err("%s: pdata not found\n", __func__);
				return -ENODEV;
			}
		} else {
			pr_err("%s: component is NULL\n", __func__);
			return -EINVAL;
		}
	}
	int i = 0;
	struct msm_pcm_channel_map *chmap;
	u64 fe_id = kcontrol->private_value & 0xFF;
	int session_type = (kcontrol->private_value >> 8) & 0xFF;

	memset(ucontrol->value.integer.value, 0,
		sizeof(ucontrol->value.integer.value));
	if (!rtd)
		return 0; /* no channels set */
	pr_debug("%s: chmap ctl for fe_id: %d, session_type: %d\n",
			__func__, fe_id, session_type);

	mutex_lock(&pdata->lock);
	if (substream->runtime && substream->ref_count <= 0) {
		pr_err_ratelimited("%s: substream ref_count:%d invalid\n",
				__func__, substream->ref_count);
		mutex_unlock(&pdata->lock);
	chmap = msm_pcm_get_chmap(fe_id, session_type);
	if (!chmap) {
		pr_err("%s: invalid chmap handle\n", __func__);
		return -EINVAL;
	}
	prtd = substream->runtime ? substream->runtime->private_data : NULL;

	if (prtd && prtd->set_channel_map == true) {
	for (i = 0; i < PCM_FORMAT_MAX_NUM_CHANNEL_V8; i++)
		ucontrol->value.integer.value[i] =
					(int)prtd->channel_map[i];
	} else {
		for (i = 0; i < PCM_FORMAT_MAX_NUM_CHANNEL_V8; i++)
			ucontrol->value.integer.value[i] = 0;
	}
			chmap->channel_map[i];

	mutex_unlock(&pdata->lock);
	return 0;
}

@@ -1965,24 +1982,82 @@ static int msm_pcm_add_chmap_controls(struct snd_soc_pcm_runtime *rtd)
	int i, ret = 0;

	pr_debug("%s, Channel map cntrl add\n", __func__);

	if (rtd->pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream &&
			!chmap_pspd[rtd->dai_link->id][SESSION_TYPE_RX]) {

		chmap_pspd[rtd->dai_link->id][SESSION_TYPE_RX] =
			kzalloc(sizeof(struct msm_pcm_channel_map), GFP_KERNEL);
		if (!chmap_pspd[rtd->dai_link->id][SESSION_TYPE_RX]) {
			ret = -ENOMEM;
			goto fail;
		}

		ret = snd_pcm_add_chmap_ctls(pcm, SNDRV_PCM_STREAM_PLAYBACK,
					     snd_pcm_std_chmaps,
				     PCM_FORMAT_MAX_NUM_CHANNEL_V8, 0,
					     PCM_FORMAT_MAX_NUM_CHANNEL_V8,
					     (rtd->dai_link->id) | (SESSION_TYPE_RX << 8),
					     &chmap_info);
		if (ret < 0) {
			pr_err("%s, channel map cntrl add failed\n", __func__);
		return ret;
			goto fail;
		}
		kctl = chmap_info->kctl;
		for (i = 0; i < kctl->count; i++)
			kctl->vd[i].access |= SNDRV_CTL_ELEM_ACCESS_WRITE;

		snprintf(device_num, sizeof(device_num), "%d", pcm->device);
		strlcat(kctl->id.name, device_num, sizeof(kctl->id.name));
		pr_debug("%s, Overwriting channel map control name to: %s\n",
			__func__, kctl->id.name);

		kctl->put = msm_pcm_chmap_ctl_put;
		kctl->get = msm_pcm_chmap_ctl_get;
	}

	if (rtd->pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream &&
			!chmap_pspd[rtd->dai_link->id][SESSION_TYPE_TX]) {

		chmap_pspd[rtd->dai_link->id][SESSION_TYPE_TX] =
			kzalloc(sizeof(struct msm_pcm_channel_map), GFP_KERNEL);
		if (!chmap_pspd[rtd->dai_link->id][SESSION_TYPE_TX]) {
			ret = -ENOMEM;
			goto fail;
		}

		ret = snd_pcm_add_chmap_ctls(pcm, SNDRV_PCM_STREAM_CAPTURE,
					     snd_pcm_std_chmaps,
					     PCM_FORMAT_MAX_NUM_CHANNEL_V8,
					     (rtd->dai_link->id) | (SESSION_TYPE_TX << 8),
					     &chmap_info);
		if (ret < 0) {
			pr_err("%s, channel map cntrl add failed\n", __func__);
			goto fail;
		}
		kctl = chmap_info->kctl;
		for (i = 0; i < kctl->count; i++)
			kctl->vd[i].access |= SNDRV_CTL_ELEM_ACCESS_WRITE;

		snprintf(device_num, sizeof(device_num), "%d", pcm->device);
		strlcat(kctl->id.name, device_num, sizeof(kctl->id.name));
		pr_debug("%s, Overwriting channel map control name to: %s\n",
			__func__, kctl->id.name);

		kctl->put = msm_pcm_chmap_ctl_put;
		kctl->get = msm_pcm_chmap_ctl_get;
	}

	return 0;

fail:
	pr_err("%s: failed add chmap ctls, err = %d\n", __func__, ret);

	kfree(chmap_pspd[rtd->dai_link->id][SESSION_TYPE_RX]);
	kfree(chmap_pspd[rtd->dai_link->id][SESSION_TYPE_TX]);
	chmap_pspd[rtd->dai_link->id][SESSION_TYPE_RX] = NULL;
	chmap_pspd[rtd->dai_link->id][SESSION_TYPE_TX] = NULL;

	return ret;
}

#if IS_ENABLED(CONFIG_AUDIO_QGKI)
+16 −4
Original line number Diff line number Diff line
// SPDX-License-Identifier: GPL-2.0-only
/*
 * Copyright (c) 2012-2020, The Linux Foundation. All rights reserved.
 * Copyright (c) 2012-2021, The Linux Foundation. All rights reserved.
 * Author: Brian Swetland <swetland@google.com>
 *
 * This software is licensed under the terms of the GNU General Public
@@ -5442,13 +5442,17 @@ EXPORT_SYMBOL(q6asm_enc_cfg_blk_pcm_v2);

static int __q6asm_enc_cfg_blk_pcm_v5(struct audio_client *ac,
				      uint32_t rate, uint32_t channels,
				      bool use_default_chmap,
				      char *channel_map,
				      uint16_t bits_per_sample,
				      uint16_t sample_word_size,
				      uint16_t endianness,
				      uint16_t mode)
{
	return q6asm_enc_cfg_blk_pcm_v5(ac, rate, channels,
					bits_per_sample, true, false, NULL,
					bits_per_sample,
					use_default_chmap, false,
					channel_map,
					sample_word_size, endianness, mode);
}

@@ -5557,20 +5561,28 @@ EXPORT_SYMBOL(q6asm_enc_cfg_blk_pcm_format_support_v4);
 * @rate: sample rate
 * @channels: number of channels
 * @bits_per_sample: bit width of encoder session
 * @use_default_chmap: true if default channel map to be used
 * @channel_map: input channel map
 * @sample_word_size: Size in bits of the word that holds a sample of a channel
 * @endianness: endianness of the pcm data
 * @mode: Mode to provide additional info about the pcm input data
 */
int q6asm_enc_cfg_blk_pcm_format_support_v5(struct audio_client *ac,
					    uint32_t rate, uint32_t channels,
					    bool use_default_chmap,
					    char *channel_map,
					    uint16_t bits_per_sample,
					    uint16_t sample_word_size,
					    uint16_t endianness,
					    uint16_t mode)
{
	 return __q6asm_enc_cfg_blk_pcm_v5(ac, rate, channels,
					   bits_per_sample, sample_word_size,
					   endianness, mode);
					   use_default_chmap,
					   channel_map,
					   bits_per_sample,
					   sample_word_size,
					   endianness,
					   mode);
}

EXPORT_SYMBOL(q6asm_enc_cfg_blk_pcm_format_support_v5);
+8 −6
Original line number Diff line number Diff line
/* SPDX-License-Identifier: GPL-2.0-only */
/*
 * Copyright (c) 2012-2019, The Linux Foundation. All rights reserved.
 * Copyright (c) 2012-2019, 2021 The Linux Foundation. All rights reserved.
 */
#ifndef __Q6_ASM_V2_H__
#define __Q6_ASM_V2_H__
@@ -477,6 +477,8 @@ int q6asm_enc_cfg_blk_pcm_format_support_v4(struct audio_client *ac,

int q6asm_enc_cfg_blk_pcm_format_support_v5(struct audio_client *ac,
					    uint32_t rate, uint32_t channels,
					    bool use_default_chmap,
					    char *channel_map,
					    uint16_t bits_per_sample,
					    uint16_t sample_word_size,
					    uint16_t endianness,