ASoC: WCD9310: Add support for multiple sampling rates

It is unknown to the codec driver which path will be
used for a audio stream. To support multiple sample rates,
add logic in hw_params to set the sample rates only to
the rx/tx paths that are not already active.

Signed-off-by: Bhalchandra Gajare <gajare@codeaurora.org>
diff --git a/sound/soc/codecs/wcd9310.c b/sound/soc/codecs/wcd9310.c
index 8384abc..4c32be5 100644
--- a/sound/soc/codecs/wcd9310.c
+++ b/sound/soc/codecs/wcd9310.c
@@ -27,6 +27,15 @@
 #include <linux/delay.h>
 #include "wcd9310.h"
 
+#define WCD9310_RATES (SNDRV_PCM_RATE_8000|SNDRV_PCM_RATE_16000|\
+			SNDRV_PCM_RATE_32000|SNDRV_PCM_RATE_48000)
+
+#define NUM_DECIMATORS 10
+#define NUM_INTERPOLATORS 7
+#define BITS_PER_REG 8
+#define TABLA_RX_DAI_ID 1
+#define TABLA_TX_DAI_ID 2
+
 static const DECLARE_TLV_DB_SCALE(digital_gain, 0, 1, 0);
 static const DECLARE_TLV_DB_SCALE(line_gain, 0, 7, 1);
 static const DECLARE_TLV_DB_SCALE(analog_gain, 0, 25, 1);
@@ -1716,7 +1725,89 @@
 			    struct snd_pcm_hw_params *params,
 			    struct snd_soc_dai *dai)
 {
+	struct snd_soc_codec *codec = dai->codec;
+	u8 path, tx_fs_reg, rx_fs_reg, shift;
+	u8 tx_fs_rate, rx_fs_rate, rx_state, tx_state;
+
 	pr_debug("%s: DAI-ID %x\n", __func__, dai->id);
+
+	switch (params_rate(params)) {
+	case 8000:
+		tx_fs_rate = 0x00;
+		rx_fs_rate = 0x00;
+		break;
+	case 16000:
+		tx_fs_rate = 0x01;
+		rx_fs_rate = 0x20;
+		break;
+	case 32000:
+		tx_fs_rate = 0x02;
+		rx_fs_rate = 0x40;
+		break;
+	case 48000:
+		tx_fs_rate = 0x03;
+		rx_fs_rate = 0x60;
+		break;
+	default:
+		pr_err("%s: Invalid sampling rate %d\n", __func__,
+				params_rate(params));
+		return -EINVAL;
+	}
+
+
+	/**
+	 * If current dai is a tx dai, set sample rate to
+	 * all the txfe paths that are currently not active
+	 */
+	if (dai->id == TABLA_TX_DAI_ID) {
+
+		tx_state = snd_soc_read(codec,
+				TABLA_A_CDC_CLK_TX_CLK_EN_B1_CTL);
+
+		for (path = 1, shift = 0;
+				path <= NUM_DECIMATORS; path++, shift++) {
+
+			if (path == BITS_PER_REG + 1) {
+				shift = 0;
+				tx_state = snd_soc_read(codec,
+					TABLA_A_CDC_CLK_TX_CLK_EN_B2_CTL);
+			}
+
+			if (!(tx_state & (1 << shift))) {
+				tx_fs_reg = TABLA_A_CDC_TX1_CLK_FS_CTL
+						+ (BITS_PER_REG*(path-1));
+				snd_soc_update_bits(codec, tx_fs_reg,
+							0x03, tx_fs_rate);
+			}
+		}
+	}
+
+	/**
+	 * TODO: Need to handle case where same RX chain takes 2 or more inputs
+	 * with varying sample rates
+	 */
+
+	/**
+	 * If current dai is a rx dai, set sample rate to
+	 * all the rx paths that are currently not active
+	 */
+	if (dai->id == TABLA_RX_DAI_ID) {
+
+		rx_state = snd_soc_read(codec,
+			TABLA_A_CDC_CLK_RX_B1_CTL);
+
+		for (path = 1, shift = 0;
+				path <= NUM_INTERPOLATORS; path++, shift++) {
+
+			if (!(rx_state & (1 << shift))) {
+				rx_fs_reg = TABLA_A_CDC_RX1_B5_CTL
+						+ (BITS_PER_REG*(path-1));
+				snd_soc_update_bits(codec, rx_fs_reg,
+						0xE0, rx_fs_rate);
+			}
+		}
+	}
+
 	return 0;
 }
 
@@ -1735,7 +1826,7 @@
 		.id = 1,
 		.playback = {
 			.stream_name = "AIF1 Playback",
-			.rates = SNDRV_PCM_RATE_8000_48000,
+			.rates = WCD9310_RATES,
 			.formats = TABLA_FORMATS,
 			.rate_max = 48000,
 			.rate_min = 8000,
@@ -1749,7 +1840,7 @@
 		.id = 2,
 		.capture = {
 			.stream_name = "AIF1 Capture",
-			.rates = SNDRV_PCM_RATE_8000_48000,
+			.rates = WCD9310_RATES,
 			.formats = TABLA_FORMATS,
 			.rate_max = 48000,
 			.rate_min = 8000,