Merge "audio: Add control to set USB AFE port service level"
diff --git a/asoc/msm-dai-q6-v2.c b/asoc/msm-dai-q6-v2.c
index d0df70c..27d8bf3 100644
--- a/asoc/msm-dai-q6-v2.c
+++ b/asoc/msm-dai-q6-v2.c
@@ -2215,6 +2215,38 @@
 	return 0;
 }
 
+static int msm_dai_q6_usb_audio_svc_interval_put(struct snd_kcontrol *kcontrol,
+					 struct snd_ctl_elem_value *ucontrol)
+{
+	struct msm_dai_q6_dai_data *dai_data = kcontrol->private_data;
+	u32 val = ucontrol->value.integer.value[0];
+
+	if (!dai_data) {
+		pr_err("%s: dai_data is NULL\n", __func__);
+		return -EINVAL;
+	}
+	dai_data->port_config.usb_audio.service_interval = val;
+	pr_debug("%s: new service interval = %u\n",  __func__,
+		dai_data->port_config.usb_audio.service_interval);
+	return 0;
+}
+
+static int msm_dai_q6_usb_audio_svc_interval_get(struct snd_kcontrol *kcontrol,
+					struct snd_ctl_elem_value *ucontrol)
+{
+	struct msm_dai_q6_dai_data *dai_data = kcontrol->private_data;
+
+	if (!dai_data) {
+		pr_err("%s: dai_data is NULL\n", __func__);
+		return -EINVAL;
+	}
+	ucontrol->value.integer.value[0] =
+		 dai_data->port_config.usb_audio.service_interval;
+	pr_debug("%s: service interval = %d\n",  __func__,
+		     dai_data->port_config.usb_audio.service_interval);
+	return 0;
+}
+
 static int  msm_dai_q6_afe_enc_cfg_info(struct snd_kcontrol *kcontrol,
 					struct snd_ctl_elem_info *uinfo)
 {
@@ -2571,6 +2603,10 @@
 	SOC_SINGLE_EXT("USB_AUDIO_TX endian", 0, 0, 1, 0,
 			msm_dai_q6_usb_audio_endian_cfg_get,
 			msm_dai_q6_usb_audio_endian_cfg_put),
+	SOC_SINGLE_EXT("USB_AUDIO_RX service_interval", SND_SOC_NOPM, 0,
+			UINT_MAX, 0,
+			msm_dai_q6_usb_audio_svc_interval_get,
+			msm_dai_q6_usb_audio_svc_interval_put),
 };
 
 static const struct snd_kcontrol_new avd_drift_config_controls[] = {
@@ -2667,6 +2703,9 @@
 		rc = snd_ctl_add(dai->component->card->snd_card,
 				 snd_ctl_new1(&usb_audio_cfg_controls[1],
 				 dai_data));
+		rc = snd_ctl_add(dai->component->card->snd_card,
+				 snd_ctl_new1(&usb_audio_cfg_controls[4],
+				 dai_data));
 		break;
 	case AFE_PORT_ID_USB_TX:
 		rc = snd_ctl_add(dai->component->card->snd_card,
diff --git a/dsp/q6afe.c b/dsp/q6afe.c
index a401d60..98f4bd6 100644
--- a/dsp/q6afe.c
+++ b/dsp/q6afe.c
@@ -131,6 +131,7 @@
 
 #define TIMEOUT_MS 1000
 #define Q6AFE_MAX_VOLUME 0x3FFF
+#define HS_USB_INTERVAL_US 125
 
 static int pcm_afe_instance[2];
 static int proxy_afe_instance[2];
@@ -2971,6 +2972,25 @@
 		goto exit;
 	}
 
+	config.pdata.param_id = AFE_PARAM_ID_PORT_LATENCY_MODE_CONFIG;
+	config.pdata.param_size = sizeof(config.latency_config);
+	config.latency_config.minor_version =
+		AFE_API_MINOR_VERSION_USB_AUDIO_LATENCY_MODE;
+	if (afe_config->usb_audio.service_interval > 0 &&
+		afe_config->usb_audio.service_interval <= HS_USB_INTERVAL_US)
+		config.latency_config.mode = AFE_PORT_LOW_LATENCY_MODE;
+	else
+		config.latency_config.mode = AFE_PORT_DEFAULT_LATENCY_MODE;
+
+	ret = afe_apr_send_pkt(&config, &this_afe.wait[index]);
+	if (ret) {
+		pr_debug("%s: AFE device param cmd latency mode failed %d\n",
+			__func__, ret);
+		/* latency mode is an optimization, not a requirement */
+		ret = 0;
+		goto exit;
+	}
+
 exit:
 	return ret;
 }
diff --git a/include/dsp/apr_audio-v2.h b/include/dsp/apr_audio-v2.h
index 4cef82e..ffe5903 100644
--- a/include/dsp/apr_audio-v2.h
+++ b/include/dsp/apr_audio-v2.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2012-2017, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2012-2018, The Linux Foundation. All rights reserved.
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License version 2 and
@@ -2539,6 +2539,18 @@
 /* Minor version used for tracking USB audio  configuration */
 #define AFE_API_MINIOR_VERSION_USB_AUDIO_CONFIG 0x1
 
+/* ID of the parameter used to set the latency mode of the
+ * USB audio device.
+ */
+#define AFE_PARAM_ID_PORT_LATENCY_MODE_CONFIG  0x000102B3
+
+/* Minor version used for tracking USB audio latency mode */
+#define AFE_API_MINOR_VERSION_USB_AUDIO_LATENCY_MODE 0x1
+
+/* Supported AFE port latency modes */
+#define AFE_PORT_DEFAULT_LATENCY_MODE     0x0
+#define AFE_PORT_LOW_LATENCY_MODE         0x1
+
 /* Payload of the AFE_PARAM_ID_USB_AUDIO_DEV_PARAMS parameter used by
  * AFE_MODULE_AUDIO_DEV_INTERFACE.
  */
@@ -2560,6 +2572,17 @@
 	u32                  endian;
 } __packed;
 
+struct afe_param_id_usb_audio_dev_latency_mode {
+/* Minor version used for tracking USB audio device parameter.
+ * Supported values: AFE_API_MINOR_VERSION_USB_AUDIO_LATENCY_MODE
+ */
+	u32                  minor_version;
+/* latency mode for the USB audio device */
+	u32                  mode;
+} __packed;
+
+
+
 /* ID of the parameter used by AFE_PARAM_ID_USB_AUDIO_CONFIG to configure
  * USB audio interface. It should be used with AFE_MODULE_AUDIO_DEV_INTERFACE
  */
@@ -2605,7 +2628,9 @@
 /* device token of actual end USB aduio device */
 	u32                  dev_token;
 /* endianness of this interface */
-	u32                   endian;
+	u32                  endian;
+/* service interval */
+	u32                  service_interval;
 } __packed;
 
 struct afe_usb_audio_dev_param_command {
@@ -2615,6 +2640,7 @@
 	union {
 		struct afe_param_id_usb_audio_dev_params usb_dev;
 		struct afe_param_id_usb_audio_dev_lpcm_fmt lpcm_fmt;
+		struct afe_param_id_usb_audio_dev_latency_mode latency_config;
 	};
 } __packed;