ASoC: msm: Support setting channel map Multi channel PCM playback

In the usecase of multi channel PCM playback from the formats of
Wave, AAC and AC3, the output is reordered to wave format channel
order. Default the channel map to Wave format and add support to
configure desired channel map from userspace if reordering is not
performed

CRs-Fixed: 395165

Change-Id: I0b3fc94328ec35d41cd789e020fca6bead24bcd8
Signed-off-by: Subhash Chandra Bose Naripeddy <snariped@codeaurora.org>
diff --git a/include/sound/q6asm.h b/include/sound/q6asm.h
index 4021d48..5f4a7be 100644
--- a/include/sound/q6asm.h
+++ b/include/sound/q6asm.h
@@ -283,7 +283,8 @@
 			uint32_t rate, uint32_t channels);
 
 int q6asm_media_format_block_multi_ch_pcm(struct audio_client *ac,
-				uint32_t rate, uint32_t channels);
+				uint32_t rate, uint32_t channels,
+				char *channel_map);
 
 int q6asm_media_format_block_aac(struct audio_client *ac,
 			struct asm_aac_cfg *cfg);
diff --git a/sound/soc/msm/msm-lowlatency-pcm-q6.c b/sound/soc/msm/msm-lowlatency-pcm-q6.c
index 129f69f..fcfcb66 100644
--- a/sound/soc/msm/msm-lowlatency-pcm-q6.c
+++ b/sound/soc/msm/msm-lowlatency-pcm-q6.c
@@ -218,8 +218,27 @@
 	if (prtd->enabled)
 		return 0;
 
+	if (!prtd->set_channel_map) {
+		memset(prtd->channel_map, 0, PCM_FORMAT_MAX_NUM_CHANNEL);
+		if (prtd->channel_mode == 1) {
+			prtd->channel_map[0] = PCM_CHANNEL_FL;
+		} else if (prtd->channel_mode == 2) {
+			prtd->channel_map[0] = PCM_CHANNEL_FL;
+			prtd->channel_map[0] = PCM_CHANNEL_FR;
+		} else if (prtd->channel_mode == 6) {
+			prtd->channel_map[0] = PCM_CHANNEL_FC;
+			prtd->channel_map[0] = PCM_CHANNEL_FL;
+			prtd->channel_map[0] = PCM_CHANNEL_FR;
+			prtd->channel_map[0] = PCM_CHANNEL_LB;
+			prtd->channel_map[0] = PCM_CHANNEL_RB;
+			prtd->channel_map[0] = PCM_CHANNEL_LFE;
+		} else {
+			pr_err("%s: ERROR.unsupported num_ch = %u\n", __func__,
+				prtd->channel_mode);
+		}
+	}
 	ret = q6asm_media_format_block_multi_ch_pcm(prtd->audio_client,
-			runtime->rate, runtime->channels);
+			runtime->rate, runtime->channels, prtd->channel_map);
 	if (ret < 0)
 		pr_info("%s: CMD Format block failed\n", __func__);
 
@@ -389,6 +408,7 @@
 	}
 
 	prtd->dsp_cnt = 0;
+	prtd->set_channel_map = false;
 	runtime->private_data = prtd;
 	pr_debug("substream->pcm->device = %d\n", substream->pcm->device);
 	pr_debug("soc_prtd->dai_link->be_id = %d\n", soc_prtd->dai_link->be_id);
diff --git a/sound/soc/msm/msm-multi-ch-pcm-q6.c b/sound/soc/msm/msm-multi-ch-pcm-q6.c
index 5b0759c..7d04f95 100644
--- a/sound/soc/msm/msm-multi-ch-pcm-q6.c
+++ b/sound/soc/msm/msm-multi-ch-pcm-q6.c
@@ -269,9 +269,29 @@
 	prtd->channel_mode = runtime->channels;
 	if (prtd->enabled)
 		return 0;
-
+	pr_debug("prtd->set_channel_map: %d", prtd->set_channel_map);
+	if (!prtd->set_channel_map) {
+		pr_debug("using default channel map");
+		memset(prtd->channel_map, 0, PCM_FORMAT_MAX_NUM_CHANNEL);
+		if (prtd->channel_mode == 1) {
+			prtd->channel_map[0] = PCM_CHANNEL_FL;
+		} else if (prtd->channel_mode == 2) {
+			prtd->channel_map[1] = PCM_CHANNEL_FL;
+			prtd->channel_map[2] = PCM_CHANNEL_FR;
+		} else if (prtd->channel_mode == 6) {
+			prtd->channel_map[0] = PCM_CHANNEL_FC;
+			prtd->channel_map[1] = PCM_CHANNEL_FL;
+			prtd->channel_map[2] = PCM_CHANNEL_FR;
+			prtd->channel_map[3] = PCM_CHANNEL_LB;
+			prtd->channel_map[4] = PCM_CHANNEL_RB;
+			prtd->channel_map[5] = PCM_CHANNEL_LFE;
+		} else {
+			pr_err("%s: ERROR.unsupported num_ch = %u\n", __func__,
+				prtd->channel_mode);
+		}
+	}
 	ret = q6asm_media_format_block_multi_ch_pcm(prtd->audio_client,
-			runtime->rate, runtime->channels);
+			runtime->rate, runtime->channels, prtd->channel_map);
 	if (ret < 0)
 		pr_info("%s: CMD Format block failed\n", __func__);
 
@@ -452,6 +472,7 @@
 	}
 
 	prtd->dsp_cnt = 0;
+	prtd->set_channel_map = false;
 	runtime->private_data = prtd;
 	pr_debug("substream->pcm->device = %d\n", substream->pcm->device);
 	pr_debug("soc_prtd->dai_link->be_id = %d\n", soc_prtd->dai_link->be_id);
@@ -492,6 +513,15 @@
 	return rc;
 }
 
+void multi_ch_pcm_set_channel_map(char *channel_mapping)
+{
+	pr_debug("%s\n", __func__);
+	if (multi_ch_pcm_audio.prtd) {
+		multi_ch_pcm_audio.prtd->set_channel_map = true;
+		memcpy(multi_ch_pcm_audio.prtd->channel_map, channel_mapping,
+			 PCM_FORMAT_MAX_NUM_CHANNEL);
+	}
+}
 
 static int msm_pcm_playback_copy(struct snd_pcm_substream *substream, int a,
 	snd_pcm_uframes_t hwoff, void __user *buf, snd_pcm_uframes_t frames)
diff --git a/sound/soc/msm/msm-pcm-q6.h b/sound/soc/msm/msm-pcm-q6.h
index f1af99a..2678498 100644
--- a/sound/soc/msm/msm-pcm-q6.h
+++ b/sound/soc/msm/msm-pcm-q6.h
@@ -81,6 +81,8 @@
 	int periods;
 	int mmap_flag;
 	atomic_t pending_buffer;
+	bool set_channel_map;
+	char channel_map[8];
 };
 
 struct output_meta_data_st {
diff --git a/sound/soc/msm/msm-pcm-routing.c b/sound/soc/msm/msm-pcm-routing.c
index 4d0caa3..f28d01a 100644
--- a/sound/soc/msm/msm-pcm-routing.c
+++ b/sound/soc/msm/msm-pcm-routing.c
@@ -78,6 +78,7 @@
 static const DECLARE_TLV_DB_LINEAR(compressed2_rx_vol_gain, 0,
 			INT_RX_VOL_MAX_STEPS);
 static int msm_route_ec_ref_rx;
+static char channel_mapping[PCM_FORMAT_MAX_NUM_CHANNEL];
 
 /* Equal to Frontend after last of the MULTIMEDIA SESSIONS */
 #define MAX_EQ_SESSIONS		MSM_FRONTEND_DAI_CS_VOICE
@@ -862,6 +863,27 @@
 	return 0;
 }
 
+static int msm_routing_get_channel_map_mixer(struct snd_kcontrol *kcontrol,
+				struct snd_ctl_elem_value *ucontrol)
+{
+	int i;
+	for (i = 0; i < PCM_FORMAT_MAX_NUM_CHANNEL; i++)
+		ucontrol->value.integer.value[i] = channel_mapping[i];
+	return 0;
+}
+
+static int msm_routing_put_channel_map_mixer(struct snd_kcontrol *kcontrol,
+				struct snd_ctl_elem_value *ucontrol)
+{
+	int i;
+
+	for (i = 0; i < PCM_FORMAT_MAX_NUM_CHANNEL; i++)
+		channel_mapping[i] = (char)(ucontrol->value.integer.value[i]);
+	multi_ch_pcm_set_channel_map(channel_mapping);
+
+	return 0;
+}
+
 static int msm_routing_set_compressed_vol_mixer(struct snd_kcontrol *kcontrol,
 				struct snd_ctl_elem_value *ucontrol)
 {
@@ -1891,6 +1913,12 @@
 	msm_routing_set_compressed2_vol_mixer, compressed2_rx_vol_gain),
 };
 
+static const struct snd_kcontrol_new multi_ch_channel_map_mixer_controls[] = {
+	SOC_SINGLE_MULTI_EXT("Playback Channel Map", SND_SOC_NOPM, 0, 8,
+	0, 8, msm_routing_get_channel_map_mixer,
+	msm_routing_put_channel_map_mixer),
+};
+
 static const struct snd_kcontrol_new lpa_SRS_trumedia_controls[] = {
 	{.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
 	.name = "SRS TruMedia",
@@ -2867,6 +2895,10 @@
 	snd_soc_add_platform_controls(platform,
 				ec_ref_rx_mixer_controls,
 			ARRAY_SIZE(ec_ref_rx_mixer_controls));
+
+	snd_soc_add_platform_controls(platform,
+				multi_ch_channel_map_mixer_controls,
+			ARRAY_SIZE(multi_ch_channel_map_mixer_controls));
 	return 0;
 }
 
diff --git a/sound/soc/msm/msm-pcm-routing.h b/sound/soc/msm/msm-pcm-routing.h
index 6b87475..14f330b 100644
--- a/sound/soc/msm/msm-pcm-routing.h
+++ b/sound/soc/msm/msm-pcm-routing.h
@@ -126,4 +126,6 @@
 
 int compressed_set_volume(unsigned volume);
 
+void multi_ch_pcm_set_channel_map(char *channel_mapping);
+
 #endif /*_MSM_PCM_H*/
diff --git a/sound/soc/msm/qdsp6/q6asm.c b/sound/soc/msm/qdsp6/q6asm.c
index 1e75c53..b8eb1df 100644
--- a/sound/soc/msm/qdsp6/q6asm.c
+++ b/sound/soc/msm/qdsp6/q6asm.c
@@ -2295,7 +2295,7 @@
 }
 
 int q6asm_media_format_block_multi_ch_pcm(struct audio_client *ac,
-				uint32_t rate, uint32_t channels)
+			uint32_t rate, uint32_t channels, char *channel_map)
 {
 	struct asm_stream_media_format_update fmt;
 	u8 *channel_mapping;
@@ -2318,39 +2318,7 @@
 	channel_mapping =
 		fmt.write_cfg.multi_ch_pcm_cfg.channel_mapping;
 
-	memset(channel_mapping, 0, PCM_FORMAT_MAX_NUM_CHANNEL);
-
-	if (channels == 1)  {
-		channel_mapping[0] = PCM_CHANNEL_FL;
-	} else if (channels == 2) {
-		channel_mapping[0] = PCM_CHANNEL_FL;
-		channel_mapping[1] = PCM_CHANNEL_FR;
-	} else if (channels == 4) {
-		channel_mapping[0] = PCM_CHANNEL_FL;
-		channel_mapping[1] = PCM_CHANNEL_FR;
-		channel_mapping[1] = PCM_CHANNEL_LB;
-		channel_mapping[1] = PCM_CHANNEL_RB;
-	} else if (channels == 6) {
-		channel_mapping[0] = PCM_CHANNEL_FC;
-		channel_mapping[1] = PCM_CHANNEL_FL;
-		channel_mapping[2] = PCM_CHANNEL_FR;
-		channel_mapping[3] = PCM_CHANNEL_LB;
-		channel_mapping[4] = PCM_CHANNEL_RB;
-		channel_mapping[5] = PCM_CHANNEL_LFE;
-	} else if (channels == 8) {
-		channel_mapping[0] = PCM_CHANNEL_FC;
-		channel_mapping[1] = PCM_CHANNEL_FL;
-		channel_mapping[2] = PCM_CHANNEL_FR;
-		channel_mapping[3] = PCM_CHANNEL_LB;
-		channel_mapping[4] = PCM_CHANNEL_RB;
-		channel_mapping[5] = PCM_CHANNEL_LFE;
-		channel_mapping[6] = PCM_CHANNEL_FLC;
-		channel_mapping[7] = PCM_CHANNEL_FRC;
-	} else {
-		pr_err("%s: ERROR.unsupported num_ch = %u\n", __func__,
-				channels);
-		return -EINVAL;
-	}
+	memcpy(channel_mapping, channel_map, PCM_FORMAT_MAX_NUM_CHANNEL);
 
 	rc = apr_send_pkt(ac->apr, (uint32_t *) &fmt);
 	if (rc < 0) {