ASoC: wcd9310: add I2C\I2S support for tabla codec

Add support for I2C\I2S interface for tabla codec along
With SLIMBUS interface.

Change-Id: Ib0f2ebe62121c1e43a9bdbcaec7621e9d96ac3c4
Signed-off-by: Santosh Mardi <gsantosh@codeaurora.org>
diff --git a/drivers/mfd/wcd9310-core.c b/drivers/mfd/wcd9310-core.c
index 8eca7aa..aabc2cc 100644
--- a/drivers/mfd/wcd9310-core.c
+++ b/drivers/mfd/wcd9310-core.c
@@ -22,10 +22,25 @@
 #include <linux/gpio.h>
 #include <linux/debugfs.h>
 #include <linux/regulator/consumer.h>
+#include <linux/i2c.h>
 #include <sound/soc.h>
 
 #define TABLA_SLIM_GLA_MAX_RETRIES 5
 #define TABLA_REGISTER_START_OFFSET 0x800
+
+#define MAX_TABLA_DEVICE	4
+#define TABLA_I2C_MODE	0x03
+
+struct tabla_i2c {
+	struct i2c_client *client;
+	struct i2c_msg xfer_msg[2];
+	struct mutex xfer_lock;
+	int mod_id;
+};
+
+struct tabla_i2c tabla_modules[MAX_TABLA_DEVICE];
+static int tabla_intf;
+
 static int tabla_read(struct tabla *tabla, unsigned short reg,
 		       int bytes, void *dest, bool interface_reg)
 {
@@ -566,6 +581,214 @@
 	kfree(tabla->supplies);
 }
 
+int tabla_get_intf_type(void)
+{
+	return tabla_intf;
+}
+EXPORT_SYMBOL_GPL(tabla_get_intf_type);
+
+struct tabla_i2c *get_i2c_tabla_device_info(u16 reg)
+{
+	u16 mask = 0x0f00;
+	int value = 0;
+	struct tabla_i2c *tabla = NULL;
+	value = ((reg & mask) >> 8) & 0x000f;
+	switch (value) {
+	case 0:
+		tabla = &tabla_modules[0];
+		break;
+	case 1:
+		tabla = &tabla_modules[1];
+		break;
+	case 2:
+		tabla = &tabla_modules[2];
+		break;
+	case 3:
+		tabla = &tabla_modules[3];
+		break;
+	default:
+		break;
+	}
+	return tabla;
+}
+
+int tabla_i2c_write_device(u16 reg, u8 *value,
+				u32 bytes)
+{
+
+	struct i2c_msg *msg;
+	int ret = 0;
+	u8 reg_addr = 0;
+	u8 data[bytes + 1];
+	struct tabla_i2c *tabla;
+
+	tabla = get_i2c_tabla_device_info(reg);
+	if (tabla->client == NULL) {
+		pr_err("failed to get device info\n");
+		return -ENODEV;
+	}
+	reg_addr = (u8)reg;
+	msg = &tabla->xfer_msg[0];
+	msg->addr = tabla->client->addr;
+	msg->len = bytes + 1;
+	msg->flags = 0;
+	data[0] = reg;
+	data[1] = *value;
+	msg->buf = data;
+	ret = i2c_transfer(tabla->client->adapter, tabla->xfer_msg, 1);
+	/* Try again if the write fails */
+	if (ret != 1) {
+		ret = i2c_transfer(tabla->client->adapter,
+						tabla->xfer_msg, 1);
+		if (ret != 1) {
+			pr_err("failed to write the device\n");
+			return ret;
+		}
+	}
+	pr_debug("write sucess register = %x val = %x\n", reg, data[1]);
+	return 0;
+}
+
+
+int tabla_i2c_read_device(unsigned short reg,
+				  int bytes, unsigned char *dest)
+{
+	struct i2c_msg *msg;
+	int ret = 0;
+	u8 reg_addr = 0;
+	struct tabla_i2c *tabla;
+	u8 i = 0;
+
+	tabla = get_i2c_tabla_device_info(reg);
+	if (tabla->client == NULL) {
+		pr_err("failed to get device info\n");
+		return -ENODEV;
+	}
+	for (i = 0; i < bytes; i++) {
+		reg_addr = (u8)reg++;
+		msg = &tabla->xfer_msg[0];
+		msg->addr = tabla->client->addr;
+		msg->len = 1;
+		msg->flags = 0;
+		msg->buf = &reg_addr;
+
+		msg = &tabla->xfer_msg[1];
+		msg->addr = tabla->client->addr;
+		msg->len = 1;
+		msg->flags = I2C_M_RD;
+		msg->buf = dest++;
+		ret = i2c_transfer(tabla->client->adapter, tabla->xfer_msg, 2);
+
+		/* Try again if read fails first time */
+		if (ret != 2) {
+			ret = i2c_transfer(tabla->client->adapter,
+							tabla->xfer_msg, 2);
+			if (ret != 2) {
+				pr_err("failed to read tabla register\n");
+				return ret;
+			}
+		}
+	}
+	return 0;
+}
+
+int tabla_i2c_read(struct tabla *tabla, unsigned short reg,
+			int bytes, void *dest, bool interface_reg)
+{
+	return tabla_i2c_read_device(reg, bytes, dest);
+}
+
+int tabla_i2c_write(struct tabla *tabla, unsigned short reg,
+			 int bytes, void *src, bool interface_reg)
+{
+	return tabla_i2c_write_device(reg, src, bytes);
+}
+
+static int __devinit tabla_i2c_probe(struct i2c_client *client,
+			const struct i2c_device_id *id)
+{
+	struct tabla *tabla;
+	struct tabla_pdata *pdata = client->dev.platform_data;
+	int val = 0;
+	int ret = 0;
+	static int device_id;
+
+	if (device_id > 0) {
+		tabla_modules[device_id++].client = client;
+		pr_info("probe for other slaves devices of tabla\n");
+		return ret;
+	}
+
+	tabla = kzalloc(sizeof(struct tabla), GFP_KERNEL);
+	if (tabla == NULL) {
+		pr_err("%s: error, allocation failed\n", __func__);
+		ret = -ENOMEM;
+		goto fail;
+	}
+
+	if (!pdata) {
+		dev_dbg(&client->dev, "no platform data?\n");
+		ret = -EINVAL;
+		goto fail;
+	}
+	if (i2c_check_functionality(client->adapter, I2C_FUNC_I2C) == 0) {
+		dev_dbg(&client->dev, "can't talk I2C?\n");
+		ret = -EIO;
+		goto fail;
+	}
+	tabla->dev = &client->dev;
+	tabla->reset_gpio = pdata->reset_gpio;
+
+	ret = tabla_enable_supplies(tabla);
+	if (ret) {
+		pr_err("%s: Fail to enable Tabla supplies\n", __func__);
+		goto err_tabla;
+	}
+
+	usleep_range(5, 5);
+	ret = tabla_reset(tabla);
+	if (ret) {
+		pr_err("%s: Resetting Tabla failed\n", __func__);
+		goto err_supplies;
+	}
+	tabla_modules[device_id++].client = client;
+
+	tabla->read_dev = tabla_i2c_read;
+	tabla->write_dev = tabla_i2c_write;
+	tabla->irq = pdata->irq;
+	tabla->irq_base = pdata->irq_base;
+
+	/*read the tabla status before initializing the device type*/
+	ret = tabla_read(tabla, TABLA_A_CHIP_STATUS, 1, &val, 0);
+	if ((ret < 0) || (val != TABLA_I2C_MODE)) {
+		pr_err("failed to read the tabla status\n");
+		goto err_device_init;
+	}
+
+	ret = tabla_device_init(tabla, tabla->irq);
+	if (ret) {
+		pr_err("%s: error, initializing device failed\n", __func__);
+		goto err_device_init;
+	}
+	tabla_intf = TABLA_INTERFACE_TYPE_I2C;
+
+	return ret;
+err_device_init:
+	tabla_free_reset(tabla);
+err_supplies:
+	tabla_disable_supplies(tabla);
+err_tabla:
+	kfree(tabla);
+fail:
+	return ret;
+}
+
+static int __devexit tabla_i2c_remove(struct i2c_client *client)
+{
+	pr_debug("exit\n");
+	return 0;
+}
+
 static int tabla_slim_probe(struct slim_device *slim)
 {
 	struct tabla *tabla;
@@ -600,7 +823,7 @@
 
 	ret = tabla_enable_supplies(tabla);
 	if (ret) {
-		pr_info("%s: Fail to enable Tabla supplies\n", __func__);
+		pr_err("%s: Fail to enable Tabla supplies\n", __func__);
 		goto err_tabla;
 	}
 	usleep_range(5, 5);
@@ -661,6 +884,7 @@
 		break;
 	}
 	tabla_inf_la = tabla->slim_slave->laddr;
+	tabla_intf = TABLA_INTERFACE_TYPE_SLIMBUS;
 
 	ret = tabla_device_init(tabla, tabla->irq);
 	if (ret) {
@@ -744,9 +968,33 @@
 	.id_table = slimtest2x_id,
 };
 
+#define TABLA_I2C_TOP_LEVEL 0
+#define TABLA_I2C_ANALOG       1
+#define TABLA_I2C_DIGITAL_1    2
+#define TABLA_I2C_DIGITAL_2    3
+
+static struct i2c_device_id tabla_id_table[] = {
+	{"tabla top level", TABLA_I2C_TOP_LEVEL},
+	{"tabla analog", TABLA_I2C_TOP_LEVEL},
+	{"tabla digital1", TABLA_I2C_TOP_LEVEL},
+	{"tabla digital2", TABLA_I2C_TOP_LEVEL},
+	{}
+};
+MODULE_DEVICE_TABLE(i2c, tabla_id_table);
+
+static struct i2c_driver tabla_i2c_driver = {
+	.driver                 = {
+		.owner          =       THIS_MODULE,
+		.name           =       "tabla-i2c-core",
+	},
+	.id_table               =       tabla_id_table,
+	.probe                  =       tabla_i2c_probe,
+	.remove                 =       __devexit_p(tabla_i2c_remove),
+};
+
 static int __init tabla_init(void)
 {
-	int ret1, ret2;
+	int ret1, ret2, ret3;
 
 	ret1 = slim_driver_register(&tabla_slim_driver);
 	if (ret1 != 0)
@@ -756,7 +1004,11 @@
 	if (ret2 != 0)
 		pr_err("Failed to register tabla2x SB driver: %d\n", ret2);
 
-	return (ret1 && ret2) ? -1 : 0;
+	ret3 = i2c_add_driver(&tabla_i2c_driver);
+	if (ret3 != 0)
+		pr_err("failed to add the I2C driver\n");
+
+	return (ret1 && ret2 && ret3) ? -1 : 0;
 }
 module_init(tabla_init);
 
diff --git a/drivers/mfd/wcd9310-irq.c b/drivers/mfd/wcd9310-irq.c
index bc7841e..9cc4761 100644
--- a/drivers/mfd/wcd9310-irq.c
+++ b/drivers/mfd/wcd9310-irq.c
@@ -110,11 +110,19 @@
 				(i >= TABLA_IRQ_MBHC_REMOVAL)) {
 				tabla_reg_write(tabla, TABLA_A_INTR_CLEAR0 +
 					BIT_BYTE(i), BYTE_BIT_MASK(i));
+				if (tabla_get_intf_type() ==
+					TABLA_INTERFACE_TYPE_I2C)
+					tabla_reg_write(tabla,
+						TABLA_A_INTR_MODE, 0x02);
 				handle_nested_irq(tabla->irq_base + i);
 			} else {
 				handle_nested_irq(tabla->irq_base + i);
 				tabla_reg_write(tabla, TABLA_A_INTR_CLEAR0 +
 					BIT_BYTE(i), BYTE_BIT_MASK(i));
+				if (tabla_get_intf_type() ==
+					TABLA_INTERFACE_TYPE_I2C)
+					tabla_reg_write(tabla,
+						TABLA_A_INTR_MODE, 0x02);
 			}
 			break;
 		}
diff --git a/include/linux/mfd/wcd9310/core.h b/include/linux/mfd/wcd9310/core.h
index 2d03c95..0eb875c 100644
--- a/include/linux/mfd/wcd9310/core.h
+++ b/include/linux/mfd/wcd9310/core.h
@@ -19,6 +19,9 @@
 
 #define TABLA_SLIM_NUM_PORT_REG 3
 
+#define TABLA_INTERFACE_TYPE_SLIMBUS	0x00
+#define TABLA_INTERFACE_TYPE_I2C	0x01
+
 enum {
 	TABLA_IRQ_SLIMBUS = 0,
 	TABLA_IRQ_MBHC_REMOVAL,
@@ -82,6 +85,7 @@
 int tabla_irq_init(struct tabla *tabla);
 void tabla_irq_exit(struct tabla *tabla);
 int tabla_get_logical_addresses(u8 *pgd_la, u8 *inf_la);
+int tabla_get_intf_type(void);
 
 static inline int tabla_request_irq(struct tabla *tabla, int irq,
 				     irq_handler_t handler, const char *name,
diff --git a/sound/soc/codecs/wcd9310.c b/sound/soc/codecs/wcd9310.c
index 7f7bdc9..d330f7a 100644
--- a/sound/soc/codecs/wcd9310.c
+++ b/sound/soc/codecs/wcd9310.c
@@ -14,12 +14,15 @@
 #include <linux/firmware.h>
 #include <linux/slab.h>
 #include <linux/platform_device.h>
+#include <linux/device.h>
 #include <linux/printk.h>
 #include <linux/ratelimit.h>
 #include <linux/debugfs.h>
 #include <linux/mfd/wcd9310/core.h>
 #include <linux/mfd/wcd9310/registers.h>
 #include <linux/mfd/wcd9310/pdata.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
 #include <sound/jack.h>
 #include <sound/soc.h>
 #include <sound/soc-dapm.h>
@@ -39,6 +42,8 @@
 
 #define TABLA_JACK_MASK (SND_JACK_HEADSET | SND_JACK_OC_HPHL | SND_JACK_OC_HPHR)
 
+#define TABLA_I2S_MASTER_MODE_MASK 0x08
+
 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);
@@ -87,6 +92,9 @@
 	u8 cfilt_k_value;
 	bool mbhc_micbias_switched;
 
+	/*track tabla interface type*/
+	u8 intf_type;
+
 	u32 hph_status; /* track headhpone status */
 	/* define separate work for left and right headphone OCP to avoid
 	 * additional checking on which OCP event to report so no locking
@@ -1426,6 +1434,12 @@
 		break;
 	}
 }
+static const struct snd_soc_dapm_widget tabla_dapm_i2s_widgets[] = {
+	SND_SOC_DAPM_SUPPLY("RX_I2S_CLK", TABLA_A_CDC_CLK_RX_I2S_CTL,
+	4, 0, NULL, 0),
+	SND_SOC_DAPM_SUPPLY("TX_I2S_CLK", TABLA_A_CDC_CLK_TX_I2S_CTL, 4,
+	0, NULL, 0),
+};
 
 static const struct snd_soc_dapm_widget tabla_dapm_widgets[] = {
 	/*RX stuff */
@@ -1438,6 +1452,8 @@
 
 	SND_SOC_DAPM_AIF_IN("SLIM RX1", "AIF1 Playback", 0, SND_SOC_NOPM, 0, 0),
 	SND_SOC_DAPM_AIF_IN("SLIM RX2", "AIF1 Playback", 0, SND_SOC_NOPM, 0, 0),
+	SND_SOC_DAPM_AIF_IN("SLIM RX3", "AIF1 Playback", 0, SND_SOC_NOPM, 0, 0),
+	SND_SOC_DAPM_AIF_IN("SLIM RX4", "AIF1 Playback", 0, SND_SOC_NOPM, 0, 0),
 
 	/* Headphone */
 	SND_SOC_DAPM_OUTPUT("HEADPHONE"),
@@ -1701,6 +1717,19 @@
 	SND_SOC_DAPM_PGA("IIR1", TABLA_A_CDC_CLK_SD_CTL, 0, 0, NULL, 0),
 };
 
+static const struct snd_soc_dapm_route audio_i2s_map[] = {
+	{"RX_I2S_CLK", NULL, "CDC_CONN"},
+	{"SLIM RX1", NULL, "RX_I2S_CLK"},
+	{"SLIM RX2", NULL, "RX_I2S_CLK"},
+	{"SLIM RX3", NULL, "RX_I2S_CLK"},
+	{"SLIM RX4", NULL, "RX_I2S_CLK"},
+
+	{"SLIM TX7", NULL, "TX_I2S_CLK"},
+	{"SLIM TX8", NULL, "TX_I2S_CLK"},
+	{"SLIM TX9", NULL, "TX_I2S_CLK"},
+	{"SLIM TX10", NULL, "TX_I2S_CLK"},
+};
+
 static const struct snd_soc_dapm_route audio_map[] = {
 	/* SLIMBUS Connections */
 
@@ -1814,45 +1843,73 @@
 
 	{"RX1 MIX1 INP1", "RX1", "SLIM RX1"},
 	{"RX1 MIX1 INP1", "RX2", "SLIM RX2"},
+	{"RX1 MIX1 INP1", "RX3", "SLIM RX3"},
+	{"RX1 MIX1 INP1", "RX4", "SLIM RX4"},
 	{"RX1 MIX1 INP1", "IIR1", "IIR1"},
 	{"RX1 MIX1 INP2", "RX1", "SLIM RX1"},
 	{"RX1 MIX1 INP2", "RX2", "SLIM RX2"},
+	{"RX1 MIX1 INP2", "RX3", "SLIM RX3"},
+	{"RX1 MIX1 INP2", "RX4", "SLIM RX4"},
 	{"RX1 MIX1 INP2", "IIR1", "IIR1"},
 	{"RX2 MIX1 INP1", "RX1", "SLIM RX1"},
 	{"RX2 MIX1 INP1", "RX2", "SLIM RX2"},
+	{"RX2 MIX1 INP1", "RX3", "SLIM RX3"},
+	{"RX2 MIX1 INP1", "RX4", "SLIM RX4"},
 	{"RX2 MIX1 INP1", "IIR1", "IIR1"},
 	{"RX2 MIX1 INP2", "RX1", "SLIM RX1"},
 	{"RX2 MIX1 INP2", "RX2", "SLIM RX2"},
+	{"RX2 MIX1 INP2", "RX3", "SLIM RX3"},
+	{"RX2 MIX1 INP2", "RX4", "SLIM RX4"},
 	{"RX2 MIX1 INP2", "IIR1", "IIR1"},
 	{"RX3 MIX1 INP1", "RX1", "SLIM RX1"},
 	{"RX3 MIX1 INP1", "RX2", "SLIM RX2"},
+	{"RX3 MIX1 INP1", "RX3", "SLIM RX3"},
+	{"RX3 MIX1 INP1", "RX4", "SLIM RX4"},
 	{"RX3 MIX1 INP1", "IIR1", "IIR1"},
 	{"RX3 MIX1 INP2", "RX1", "SLIM RX1"},
 	{"RX3 MIX1 INP2", "RX2", "SLIM RX2"},
+	{"RX3 MIX1 INP2", "RX3", "SLIM RX3"},
+	{"RX3 MIX1 INP2", "RX4", "SLIM RX4"},
 	{"RX3 MIX1 INP2", "IIR1", "IIR1"},
 	{"RX4 MIX1 INP1", "RX1", "SLIM RX1"},
 	{"RX4 MIX1 INP1", "RX2", "SLIM RX2"},
+	{"RX4 MIX1 INP1", "RX3", "SLIM RX3"},
+	{"RX4 MIX1 INP1", "RX4", "SLIM RX4"},
 	{"RX4 MIX1 INP1", "IIR1", "IIR1"},
 	{"RX4 MIX1 INP2", "RX1", "SLIM RX1"},
 	{"RX4 MIX1 INP2", "RX2", "SLIM RX2"},
+	{"RX4 MIX1 INP2", "RX3", "SLIM RX3"},
+	{"RX4 MIX1 INP2", "RX4", "SLIM RX4"},
 	{"RX4 MIX1 INP2", "IIR1", "IIR1"},
 	{"RX5 MIX1 INP1", "RX1", "SLIM RX1"},
 	{"RX5 MIX1 INP1", "RX2", "SLIM RX2"},
+	{"RX5 MIX1 INP1", "RX3", "SLIM RX3"},
+	{"RX5 MIX1 INP1", "RX4", "SLIM RX4"},
 	{"RX5 MIX1 INP1", "IIR1", "IIR1"},
 	{"RX5 MIX1 INP2", "RX1", "SLIM RX1"},
 	{"RX5 MIX1 INP2", "RX2", "SLIM RX2"},
+	{"RX5 MIX1 INP2", "RX3", "SLIM RX3"},
+	{"RX5 MIX1 INP2", "RX4", "SLIM RX4"},
 	{"RX5 MIX1 INP2", "IIR1", "IIR1"},
 	{"RX6 MIX1 INP1", "RX1", "SLIM RX1"},
 	{"RX6 MIX1 INP1", "RX2", "SLIM RX2"},
+	{"RX6 MIX1 INP1", "RX3", "SLIM RX3"},
+	{"RX6 MIX1 INP1", "RX4", "SLIM RX4"},
 	{"RX6 MIX1 INP1", "IIR1", "IIR1"},
 	{"RX6 MIX1 INP2", "RX1", "SLIM RX1"},
 	{"RX6 MIX1 INP2", "RX2", "SLIM RX2"},
+	{"RX6 MIX1 INP2", "RX3", "SLIM RX3"},
+	{"RX6 MIX1 INP2", "RX4", "SLIM RX4"},
 	{"RX6 MIX1 INP2", "IIR1", "IIR1"},
 	{"RX7 MIX1 INP1", "RX1", "SLIM RX1"},
 	{"RX7 MIX1 INP1", "RX2", "SLIM RX2"},
+	{"RX7 MIX1 INP1", "RX3", "SLIM RX3"},
+	{"RX7 MIX1 INP1", "RX4", "SLIM RX4"},
 	{"RX7 MIX1 INP1", "IIR1", "IIR1"},
 	{"RX7 MIX1 INP2", "RX1", "SLIM RX1"},
 	{"RX7 MIX1 INP2", "RX2", "SLIM RX2"},
+	{"RX7 MIX1 INP2", "RX3", "SLIM RX3"},
+	{"RX7 MIX1 INP2", "RX4", "SLIM RX4"},
 	{"RX7 MIX1 INP2", "IIR1", "IIR1"},
 
 	/* Decimator Inputs */
@@ -2191,7 +2248,39 @@
 
 static int tabla_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt)
 {
+	u8 val = 0;
+	struct tabla_priv *tabla = snd_soc_codec_get_drvdata(dai->codec);
+
 	pr_debug("%s\n", __func__);
+	switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+	case SND_SOC_DAIFMT_CBS_CFS:
+		/* CPU is master */
+		if (tabla->intf_type == TABLA_INTERFACE_TYPE_I2C) {
+			if (dai->id == TABLA_TX_DAI_ID)
+				snd_soc_update_bits(dai->codec,
+					TABLA_A_CDC_CLK_TX_I2S_CTL,
+					TABLA_I2S_MASTER_MODE_MASK, 0);
+			else if (dai->id == TABLA_RX_DAI_ID)
+				snd_soc_update_bits(dai->codec,
+					TABLA_A_CDC_CLK_RX_I2S_CTL,
+					TABLA_I2S_MASTER_MODE_MASK, 0);
+		}
+		break;
+	case SND_SOC_DAIFMT_CBM_CFM:
+	/* CPU is slave */
+		if (tabla->intf_type == TABLA_INTERFACE_TYPE_I2C) {
+			val = TABLA_I2S_MASTER_MODE_MASK;
+			if (dai->id == TABLA_TX_DAI_ID)
+				snd_soc_update_bits(dai->codec,
+					TABLA_A_CDC_CLK_TX_I2S_CTL, val, val);
+			else if (dai->id == TABLA_RX_DAI_ID)
+				snd_soc_update_bits(dai->codec,
+					TABLA_A_CDC_CLK_RX_I2S_CTL, val, val);
+		}
+		break;
+	default:
+		return -EINVAL;
+	}
 	return 0;
 }
 
@@ -2200,6 +2289,7 @@
 			    struct snd_soc_dai *dai)
 {
 	struct snd_soc_codec *codec = dai->codec;
+	struct tabla_priv *tabla = snd_soc_codec_get_drvdata(dai->codec);
 	u8 path, shift;
 	u16 tx_fs_reg, rx_fs_reg;
 	u8 tx_fs_rate, rx_fs_rate, rx_state, tx_state;
@@ -2255,6 +2345,25 @@
 							0x03, tx_fs_rate);
 			}
 		}
+		if (tabla->intf_type == TABLA_INTERFACE_TYPE_I2C) {
+			switch (params_format(params)) {
+			case SNDRV_PCM_FORMAT_S16_LE:
+				snd_soc_update_bits(codec,
+					TABLA_A_CDC_CLK_TX_I2S_CTL,
+					0x20, 0x20);
+				break;
+			case SNDRV_PCM_FORMAT_S32_LE:
+				snd_soc_update_bits(codec,
+					TABLA_A_CDC_CLK_TX_I2S_CTL,
+					0x20, 0x00);
+				break;
+			default:
+				pr_err("invalid format\n");
+				break;
+			}
+			snd_soc_update_bits(codec, TABLA_A_CDC_CLK_TX_I2S_CTL,
+						0x03, tx_fs_rate);
+		}
 	}
 
 	/**
@@ -2281,6 +2390,25 @@
 						0xE0, rx_fs_rate);
 			}
 		}
+		if (tabla->intf_type == TABLA_INTERFACE_TYPE_I2C) {
+			switch (params_format(params)) {
+			case SNDRV_PCM_FORMAT_S16_LE:
+				snd_soc_update_bits(codec,
+					TABLA_A_CDC_CLK_RX_I2S_CTL,
+					0x20, 0x20);
+				break;
+			case SNDRV_PCM_FORMAT_S32_LE:
+				snd_soc_update_bits(codec,
+					TABLA_A_CDC_CLK_RX_I2S_CTL,
+					0x20, 0x00);
+				break;
+			default:
+				pr_err("invalid format\n");
+				break;
+			}
+			snd_soc_update_bits(codec, TABLA_A_CDC_CLK_RX_I2S_CTL,
+						0x03, (rx_fs_rate >> 0x05));
+		}
 	}
 
 	return 0;
@@ -2324,6 +2452,37 @@
 		.ops = &tabla_dai_ops,
 	},
 };
+
+static struct snd_soc_dai_driver tabla_i2s_dai[] = {
+	{
+		.name = "tabla_i2s_rx1",
+		.id = 1,
+		.playback = {
+			.stream_name = "AIF1 Playback",
+			.rates = WCD9310_RATES,
+			.formats = TABLA_FORMATS,
+			.rate_max = 48000,
+			.rate_min = 8000,
+			.channels_min = 1,
+			.channels_max = 4,
+		},
+		.ops = &tabla_dai_ops,
+	},
+	{
+		.name = "tabla_i2s_tx1",
+		.id = 2,
+		.capture = {
+			.stream_name = "AIF1 Capture",
+			.rates = WCD9310_RATES,
+			.formats = TABLA_FORMATS,
+			.rate_max = 48000,
+			.rate_min = 8000,
+			.channels_min = 1,
+			.channels_max = 4,
+		},
+		.ops = &tabla_dai_ops,
+	},
+};
 static short tabla_codec_read_sta_result(struct snd_soc_codec *codec)
 {
 	u8 bias_msb, bias_lsb;
@@ -3186,6 +3345,7 @@
 	tabla->no_mic_headset_override = false;
 	tabla->codec = codec;
 	tabla->pdata = dev_get_platdata(codec->dev->parent);
+	tabla->intf_type = tabla_get_intf_type();
 
 	tabla_update_reg_defaults(codec);
 	tabla_codec_init_reg(codec);
@@ -3204,6 +3364,12 @@
 		ARRAY_SIZE(tabla_snd_controls));
 	snd_soc_dapm_new_controls(dapm, tabla_dapm_widgets,
 		ARRAY_SIZE(tabla_dapm_widgets));
+	if (tabla->intf_type == TABLA_INTERFACE_TYPE_I2C) {
+		snd_soc_dapm_new_controls(dapm, tabla_dapm_i2s_widgets,
+			ARRAY_SIZE(tabla_dapm_i2s_widgets));
+		snd_soc_dapm_add_routes(dapm, audio_i2s_map,
+			ARRAY_SIZE(audio_i2s_map));
+	}
 	snd_soc_dapm_add_routes(dapm, audio_map, ARRAY_SIZE(audio_map));
 	snd_soc_dapm_sync(dapm);
 
@@ -3362,13 +3528,19 @@
 
 static int __devinit tabla_probe(struct platform_device *pdev)
 {
+	int ret = 0;
 #ifdef CONFIG_DEBUG_FS
 	debugfs_poke = debugfs_create_file("TRRS",
 		S_IFREG | S_IRUGO, NULL, (void *) "TRRS", &codec_debug_ops);
 
 #endif
-	return snd_soc_register_codec(&pdev->dev, &soc_codec_dev_tabla,
-		tabla_dai, ARRAY_SIZE(tabla_dai));
+	if (tabla_get_intf_type() == TABLA_INTERFACE_TYPE_SLIMBUS)
+		ret = snd_soc_register_codec(&pdev->dev, &soc_codec_dev_tabla,
+			tabla_dai, ARRAY_SIZE(tabla_dai));
+	else if (tabla_get_intf_type() == TABLA_INTERFACE_TYPE_I2C)
+		ret = snd_soc_register_codec(&pdev->dev, &soc_codec_dev_tabla,
+			tabla_i2s_dai, ARRAY_SIZE(tabla_i2s_dai));
+	return ret;
 }
 static int __devexit tabla_remove(struct platform_device *pdev)
 {