msm: audio: qdsp6v2: Add support for aac dual mono playback

Signed-off-by: Swaminathan Sathappan <Swami@codeaurora.org>
diff --git a/arch/arm/mach-msm/qdsp6v2/audio_aac.c b/arch/arm/mach-msm/qdsp6v2/audio_aac.c
index 4c9a9b4..e8f5d84 100644
--- a/arch/arm/mach-msm/qdsp6v2/audio_aac.c
+++ b/arch/arm/mach-msm/qdsp6v2/audio_aac.c
@@ -41,6 +41,7 @@
 #define NON_TUNNEL_MODE 0x0001
 #define AUDAAC_EOS_SET  0x00000001
 
+#define AUDIO_AAC_DUAL_MONO_INVALID -1
 /* Default number of pre-allocated event packets */
 #define AUDAAC_EVENT_NUM 10
 
@@ -1399,6 +1400,49 @@
 				 sizeof(struct msm_audio_aac_config))) {
 			rc = -EFAULT;
 			break;
+		} else {
+			uint16_t sce_left = 1, sce_right = 2;
+			struct msm_audio_aac_config *aac_config =
+					&audio->aac_config;
+			if ((aac_config->dual_mono_mode <
+				AUDIO_AAC_DUAL_MONO_PL_PR) ||
+				(aac_config->dual_mono_mode >
+				AUDIO_AAC_DUAL_MONO_PL_SR)) {
+				pr_err("%s:AUDIO_SET_AAC_CONFIG: Invalid"
+					"dual_mono mode =%d\n", __func__,
+					aac_config->dual_mono_mode);
+			} else {
+				/* convert the data from user into sce_left
+				 * and sce_right based on the definitions
+				 */
+				pr_debug("%s: AUDIO_SET_AAC_CONFIG: modify"
+					 "dual_mono mode =%d\n", __func__,
+					 aac_config->dual_mono_mode);
+				switch (aac_config->dual_mono_mode) {
+				case AUDIO_AAC_DUAL_MONO_PL_PR:
+					sce_left = 1;
+					sce_right = 1;
+					break;
+				case AUDIO_AAC_DUAL_MONO_SL_SR:
+					sce_left = 2;
+					sce_right = 2;
+					break;
+				case AUDIO_AAC_DUAL_MONO_SL_PR:
+					sce_left = 2;
+					sce_right = 1;
+					break;
+				case AUDIO_AAC_DUAL_MONO_PL_SR:
+				default:
+					sce_left = 1;
+					sce_right = 2;
+					break;
+				}
+				rc = q6asm_cfg_dual_mono_aac(audio->ac,
+							sce_left, sce_right);
+				if (rc < 0)
+					pr_err("%s: asm cmd dualmono failed"
+						" rc=%d\n", __func__, rc);
+			}
 		}
 		break;
 	}
@@ -1553,6 +1597,7 @@
 	audio->pcm_cfg.buffer_count = PCM_BUF_COUNT;
 	audio->pcm_cfg.sample_rate = 48000;
 	audio->pcm_cfg.channel_count = 2;
+	audio->aac_config.dual_mono_mode = AUDIO_AAC_DUAL_MONO_INVALID;
 
 	audio->ac = q6asm_audio_client_alloc((app_cb) q6_audaac_cb,
 					     (void *)audio);
diff --git a/include/sound/apr_audio.h b/include/sound/apr_audio.h
index 44f2541..27dbfec 100644
--- a/include/sound/apr_audio.h
+++ b/include/sound/apr_audio.h
@@ -733,6 +733,11 @@
 	u32 enable;
 };
 
+struct asm_dual_mono {
+	u16 sce_left;
+	u16 sce_right;
+};
+
 struct asm_encode_cfg_blk {
 	u32 frames_per_buf;
 	u32 format_id;
@@ -820,6 +825,7 @@
 #define ASM_STREAM_CMD_GET_ENCDEC_PARAM                  0x00010C11
 #define ASM_ENCDEC_CFG_BLK_ID				 0x00010C2C
 #define ASM_ENABLE_SBR_PS				 0x00010C63
+#define ASM_CONFIGURE_DUAL_MONO			 0x00010C64
 struct asm_stream_cmd_encdec_cfg_blk{
 	struct apr_hdr              hdr;
 	u32                         param_id;
@@ -847,6 +853,13 @@
 	struct asm_sbr_ps sbr_ps;
 } __attribute__((packed));
 
+struct asm_stream_cmd_encdec_dualmono {
+	struct apr_hdr hdr;
+	u32            param_id;
+	u32            param_size;
+	struct asm_dual_mono channel_map;
+} __packed;
+
 #define ASM_STREAM _CMD_ADJUST_SAMPLES                   0x00010C0A
 struct asm_stream_cmd_adjust_samples{
 	struct apr_hdr hdr;
diff --git a/include/sound/q6asm.h b/include/sound/q6asm.h
index af82b76..9c76942 100644
--- a/include/sound/q6asm.h
+++ b/include/sound/q6asm.h
@@ -202,6 +202,9 @@
 int q6asm_enable_sbrps(struct audio_client *ac,
 			uint32_t sbr_ps);
 
+int q6asm_cfg_dual_mono_aac(struct audio_client *ac,
+			uint16_t sce_left, uint16_t sce_right);
+
 int q6asm_enc_cfg_blk_qcelp(struct audio_client *ac, uint32_t frames_per_buf,
 		uint16_t min_rate, uint16_t max_rate,
 		uint16_t reduced_rate_level, uint16_t rate_modulation_cmd);
diff --git a/sound/soc/msm/qdsp6/q6asm.c b/sound/soc/msm/qdsp6/q6asm.c
index 28bd98d..ab09342 100644
--- a/sound/soc/msm/qdsp6/q6asm.c
+++ b/sound/soc/msm/qdsp6/q6asm.c
@@ -1229,6 +1229,44 @@
 	return -EINVAL;
 }
 
+int q6asm_cfg_dual_mono_aac(struct audio_client *ac,
+			uint16_t sce_left, uint16_t sce_right)
+{
+	struct asm_stream_cmd_encdec_dualmono dual_mono;
+
+	int rc = 0;
+
+	pr_debug("%s: Session %d, sce_left = %d, sce_right = %d\n",
+			 __func__, ac->session, sce_left, sce_right);
+
+	q6asm_add_hdr(ac, &dual_mono.hdr, sizeof(dual_mono), TRUE);
+
+	dual_mono.hdr.opcode = ASM_STREAM_CMD_SET_ENCDEC_PARAM;
+	dual_mono.param_id = ASM_CONFIGURE_DUAL_MONO;
+	dual_mono.param_size = sizeof(struct asm_dual_mono);
+	dual_mono.channel_map.sce_left = sce_left;
+	dual_mono.channel_map.sce_right = sce_right;
+
+	rc = apr_send_pkt(ac->apr, (uint32_t *) &dual_mono);
+	if (rc < 0) {
+		pr_err("%s:Command opcode[0x%x]paramid[0x%x] failed\n",
+				__func__, ASM_STREAM_CMD_SET_ENCDEC_PARAM,
+				ASM_CONFIGURE_DUAL_MONO);
+		rc = -EINVAL;
+		goto fail_cmd;
+	}
+	rc = wait_event_timeout(ac->cmd_wait,
+			(atomic_read(&ac->cmd_state) == 0), 5*HZ);
+	if (!rc) {
+		pr_err("%s:timeout opcode[0x%x]\n", __func__,
+						dual_mono.hdr.opcode);
+		goto fail_cmd;
+	}
+	return 0;
+fail_cmd:
+	return -EINVAL;
+}
+
 int q6asm_enc_cfg_blk_qcelp(struct audio_client *ac, uint32_t frames_per_buf,
 		uint16_t min_rate, uint16_t max_rate,
 		uint16_t reduced_rate_level, uint16_t rate_modulation_cmd)