public inbox for linux-kernel@vger.kernel.org
 help / color / mirror / Atom feed
* [RESEND: PATCH]: Mfd-Initial-support-for-Texas-Instruments-AIC-family-Codecs
       [not found] <511DFF8A.1080801@ti.com>
@ 2013-02-17  5:15 ` BAJWA, MEHAR
  2013-04-05 15:51   ` Samuel Ortiz
  0 siblings, 1 reply; 4+ messages in thread
From: BAJWA, MEHAR @ 2013-02-17  5:15 UTC (permalink / raw)
  To: linux-kernel@vger.kernel.org, Samuel Ortiz
  Cc: Navada Kanyana, Mukund, liam.r.girdwood@intel.com

[-- Attachment #1: Type: text/plain, Size: 452 bytes --]

Hi Samuel,
    I am re-sending the MFD support for AIC family of Audio CODECs
from Texas Instruments. The patch was tested on mfd-for-linus-3.8-1.
I would like to request that this be considered for inclusion in the kernel
at the next available window. Could you please let me know if you have
any comments/recommendations for this patch. We would like to work on it
and get it up-streamed.

Thanks for your consideration.

Regards,
Mehar.

[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #2: 0001-mfd-Initial-support-for-Texas-Instruments-AIC-family.patch --]
[-- Type: text/x-patch; name="0001-mfd-Initial-support-for-Texas-Instruments-AIC-family.patch", Size: 53191 bytes --]

From 9f26bca6eb69744f259dff1827cac53b49a71884 Mon Sep 17 00:00:00 2001
From: Mehar Bajwa <mehar.bajwa@ti.com>
Date: Thu, 14 Feb 2013 17:14:34 +0530
Subject: [PATCH] mfd: Initial support for Texas Instruments AIC family of CODECs

Initial support for TI's AIC platform and TLV320AIC3262 CODEC device.

The AIC platform provides common interface to series of low power audio CODECS.
This MFD core driver instantiates subdevices that help in supporting range of features
provided by AIC family of devices

Signed-off-by: Mehar Bajwa <mehar.bajwa@ti.com>
---
 drivers/mfd/Kconfig                         |   41 +++
 drivers/mfd/Makefile                        |    3 +
 drivers/mfd/tlv320aic3xxx-core.c            |  462 +++++++++++++++++++++++++++
 drivers/mfd/tlv320aic3xxx-i2c.c             |   97 ++++++
 drivers/mfd/tlv320aic3xxx-irq.c             |  234 ++++++++++++++
 drivers/mfd/tlv320aic3xxx-spi.c             |   96 ++++++
 include/linux/mfd/tlv320aic3262-registers.h |  312 ++++++++++++++++++
 include/linux/mfd/tlv320aic3xxx-core.h      |  153 +++++++++
 include/linux/mfd/tlv320aic3xxx-registers.h |   75 +++++
 9 files changed, 1473 insertions(+), 0 deletions(-)
 create mode 100644 drivers/mfd/tlv320aic3xxx-core.c
 create mode 100644 drivers/mfd/tlv320aic3xxx-i2c.c
 create mode 100644 drivers/mfd/tlv320aic3xxx-irq.c
 create mode 100644 drivers/mfd/tlv320aic3xxx-spi.c
 create mode 100644 include/linux/mfd/tlv320aic3262-registers.h
 create mode 100644 include/linux/mfd/tlv320aic3xxx-core.h
 create mode 100644 include/linux/mfd/tlv320aic3xxx-registers.h

diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig
index ff553ba..6f273ea 100644
--- a/drivers/mfd/Kconfig
+++ b/drivers/mfd/Kconfig
@@ -45,6 +45,47 @@ config MFD_88PM805
 	  components like codec device, headset/Mic device under the
 	  corresponding menus.
 
+config MFD_AIC3XXX
+	bool "Support for AIC3XXX"
+	select REGMAP
+	select MFD_CORE
+	help
+	  Say yes here if you want support for Texas Instruments AIC audio
+	  codec.
+	  You have to select individual I2C or SPI depending on
+	  AIC3XXX interfacing with platform.
+
+config MFD_AIC3XXX_I2C
+	bool "Support Texas Instruments AIC3XXX platform with I2C"
+	select MFD_AIC3XXX
+	select REGMAP_I2C
+	depends on I2C
+	help
+	  Support for the Texas Instruments TLV320AIC3XXX family of audio SoC
+	  core functionality controlled via I2C.  This driver provides common
+	  support for accessing the device, additional drivers must be enabled
+	  in order to use the functionality of the device.
+
+config MFD_AIC3XXX_SPI
+	bool "Support Texas Instruments AIC3XXX  platform with SPI"
+	select MFD_AIC3XXX
+	select REGMAP_SPI
+	depends on SPI_MASTER
+	help
+	  Support for the Texas Instruments TLV320AIC3XXX family of audio SoC
+	  core functionality controlled via SPI.  This driver provides common
+	  support for accessing the device, additional drivers must be enabled
+	  in order to use the functionality of the device.
+
+config MFD_AIC3262
+	bool "Support Texas Instruments AIC3262"
+	depends on MFD_AIC3XXX
+	help
+	  If you say yes here you will get support for Texas Instrument
+	  TLV320AIC3262 low power audio SoC.
+	  Addition driver must be enabled to use this in order to use
+	  functionality on device.
+
 config MFD_SM501
 	tristate "Support for Silicon Motion SM501"
 	 ---help---
diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile
index 8b977f8..7627ffb 100644
--- a/drivers/mfd/Makefile
+++ b/drivers/mfd/Makefile
@@ -6,6 +6,9 @@
 obj-$(CONFIG_MFD_88PM860X)	+= 88pm860x.o
 obj-$(CONFIG_MFD_88PM800)	+= 88pm800.o 88pm80x.o
 obj-$(CONFIG_MFD_88PM805)	+= 88pm805.o 88pm80x.o
+obj-$(CONFIG_MFD_AIC3XXX)	+= tlv320aic3xxx-core.o tlv320aic3xxx-irq.o
+obj-$(CONFIG_MFD_AIC3XXX_I2C)	+= tlv320aic3xxx-i2c.o
+obj-$(CONFIG_MFD_AIC3XXX_SPI)	+= tlv320aic3xxx-spi.o
 obj-$(CONFIG_MFD_SM501)		+= sm501.o
 obj-$(CONFIG_MFD_ASIC3)		+= asic3.o tmio_core.o
 
diff --git a/drivers/mfd/tlv320aic3xxx-core.c b/drivers/mfd/tlv320aic3xxx-core.c
new file mode 100644
index 0000000..6fd151b
--- /dev/null
+++ b/drivers/mfd/tlv320aic3xxx-core.c
@@ -0,0 +1,462 @@
+/*
+ * tlv320aic3xxx-core.c  -- driver for TLV320AIC3XXX
+ *
+ * Author:      Mukund Navada <navada@ti.com>
+ *              Mehar Bajwa <mehar.bajwa@ti.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ *
+ */
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/i2c.h>
+#include <linux/err.h>
+#include <linux/delay.h>
+#include <linux/regmap.h>
+#include <linux/mfd/core.h>
+#include <linux/pm_runtime.h>
+#include <linux/regulator/consumer.h>
+#include <linux/regulator/machine.h>
+#include <linux/gpio.h>
+
+#include <linux/mfd/tlv320aic3xxx-core.h>
+#include <linux/mfd/tlv320aic3xxx-registers.h>
+
+
+/**
+ * set_aic3xxx_book: change book which we have to write/read to.
+ *
+ * @aic3xxx: Device to write/read to.
+ * @book: Book to write/read to.
+ */
+int set_aic3xxx_book(struct aic3xxx *aic3xxx, int book)
+{
+	int ret = 0;
+	u8 page_buf[] = { 0x0, 0x0 };
+	u8 book_buf[] = { 0x7f, 0x0 };
+
+	ret = regmap_write(aic3xxx->regmap, page_buf[0], page_buf[1]);
+
+	if (ret < 0)
+		return ret;
+	book_buf[1] = book;
+	ret = regmap_write(aic3xxx->regmap, book_buf[0], book_buf[1]);
+
+	if (ret < 0)
+		return ret;
+	aic3xxx->book_no = book;
+	aic3xxx->page_no = 0;
+
+	return ret;
+}
+
+/**
+ * set_aic3xxx_page: change page which we have to write/read to.
+ *
+ * @aic3xxx: Device to write/read to.
+ * @page: Book to write/read to.
+ */
+int set_aic3xxx_page(struct aic3xxx *aic3xxx, int page)
+{
+	int ret = 0;
+	u8 page_buf[] = { 0x0, 0x0 };
+
+	page_buf[1] = page;
+	ret = regmap_write(aic3xxx->regmap, page_buf[0], page_buf[1]);
+
+	if (ret < 0)
+		return ret;
+	aic3xxx->page_no = page;
+	return ret;
+}
+/**
+ * aic3xxx_reg_read: Read a single TLV320AIC3xxx register.
+ *
+ * @aic3xxx: Device to read from.
+ * @reg: Register to read.
+ */
+int aic3xxx_reg_read(struct aic3xxx *aic3xxx, unsigned int reg)
+{
+	unsigned int val;
+	int ret;
+	union aic3xxx_reg_union *aic_reg = (union aic3xxx_reg_union *) &reg;
+	u8 book, page, offset;
+
+	page = aic_reg->aic3xxx_register.page;
+	book = aic_reg->aic3xxx_register.book;
+	offset = aic_reg->aic3xxx_register.offset;
+
+	mutex_lock(&aic3xxx->io_lock);
+	if (aic3xxx->book_no != book) {
+		ret = set_aic3xxx_book(aic3xxx, book);
+		if (ret < 0) {
+			mutex_unlock(&aic3xxx->io_lock);
+			return ret;
+		}
+	}
+
+	if (aic3xxx->page_no != page) {
+		ret = set_aic3xxx_page(aic3xxx, page);
+		if (ret < 0) {
+			mutex_unlock(&aic3xxx->io_lock);
+			return ret;
+		}
+	}
+	ret = regmap_read(aic3xxx->regmap, offset, &val);
+	mutex_unlock(&aic3xxx->io_lock);
+
+	if (ret < 0)
+		return ret;
+	else
+		return val;
+}
+EXPORT_SYMBOL_GPL(aic3xxx_reg_read);
+
+/**
+ * aic3xxx_bulk_read: Read multiple TLV320AIC3XXX registers
+ *
+ * @aic3xxx: Device to read from
+ * @reg: First register
+ * @count: Number of registers
+ * @buf: Buffer to fill.  The data will be returned big endian.
+ */
+int aic3xxx_bulk_read(struct aic3xxx *aic3xxx, unsigned int reg,
+		      int count, u8 *buf)
+{
+	int ret;
+	union aic3xxx_reg_union *aic_reg = (union aic3xxx_reg_union *) &reg;
+	u8 book, page, offset;
+
+	page = aic_reg->aic3xxx_register.page;
+	book = aic_reg->aic3xxx_register.book;
+	offset = aic_reg->aic3xxx_register.offset;
+
+	mutex_lock(&aic3xxx->io_lock);
+	if (aic3xxx->book_no != book) {
+		ret = set_aic3xxx_book(aic3xxx, book);
+		if (ret < 0) {
+			mutex_unlock(&aic3xxx->io_lock);
+			return ret;
+		}
+	}
+
+	if (aic3xxx->page_no != page) {
+		ret = set_aic3xxx_page(aic3xxx, page);
+		if (ret < 0) {
+			mutex_unlock(&aic3xxx->io_lock);
+			return ret;
+		}
+	}
+	ret = regmap_bulk_read(aic3xxx->regmap, offset, buf, count);
+	mutex_unlock(&aic3xxx->io_lock);
+		return ret;
+}
+EXPORT_SYMBOL_GPL(aic3xxx_bulk_read);
+
+/**
+ * aic3xxx_reg_write: Write a single TLV320AIC3XXX register.
+ *
+ * @aic3xxx: Device to write to.
+ * @reg: Register to write to.
+ * @val: Value to write.
+ */
+int aic3xxx_reg_write(struct aic3xxx *aic3xxx, unsigned int reg,
+		      unsigned char val)
+{
+	union aic3xxx_reg_union *aic_reg = (union aic3xxx_reg_union *) &reg;
+	int ret = 0;
+	u8 page, book, offset;
+
+	page = aic_reg->aic3xxx_register.page;
+	book = aic_reg->aic3xxx_register.book;
+	offset = aic_reg->aic3xxx_register.offset;
+
+	mutex_lock(&aic3xxx->io_lock);
+	if (book != aic3xxx->book_no) {
+		ret = set_aic3xxx_book(aic3xxx, book);
+		if (ret < 0) {
+			mutex_unlock(&aic3xxx->io_lock);
+			return ret;
+		}
+	}
+	if (page != aic3xxx->page_no) {
+		ret = set_aic3xxx_page(aic3xxx, page);
+		if (ret < 0) {
+			mutex_unlock(&aic3xxx->io_lock);
+			return ret;
+		}
+	}
+	ret = regmap_write(aic3xxx->regmap, offset, val);
+	mutex_unlock(&aic3xxx->io_lock);
+	return ret;
+
+}
+EXPORT_SYMBOL_GPL(aic3xxx_reg_write);
+
+/**
+ * aic3xxx_bulk_write: Write multiple TLV320AIC3XXX registers
+ *
+ * @aic3xxx: Device to write to
+ * @reg: First register
+ * @count: Number of registers
+ * @buf: Buffer to write from.  Data must be big-endian formatted.
+ */
+int aic3xxx_bulk_write(struct aic3xxx *aic3xxx, unsigned int reg,
+		       int count, const u8 *buf)
+{
+	union aic3xxx_reg_union *aic_reg = (union aic3xxx_reg_union *) &reg;
+	int ret = 0;
+	u8 page, book, offset;
+
+	page = aic_reg->aic3xxx_register.page;
+	book = aic_reg->aic3xxx_register.book;
+	offset = aic_reg->aic3xxx_register.offset;
+
+	mutex_lock(&aic3xxx->io_lock);
+	if (book != aic3xxx->book_no) {
+		ret = set_aic3xxx_book(aic3xxx, book);
+		if (ret < 0) {
+			mutex_unlock(&aic3xxx->io_lock);
+			return ret;
+		}
+	}
+	if (page != aic3xxx->page_no) {
+		ret = set_aic3xxx_page(aic3xxx, page);
+		if (ret < 0) {
+			mutex_unlock(&aic3xxx->io_lock);
+			return ret;
+		}
+	}
+	ret = regmap_raw_write(aic3xxx->regmap, offset, buf, count);
+	mutex_unlock(&aic3xxx->io_lock);
+	return ret;
+}
+EXPORT_SYMBOL_GPL(aic3xxx_bulk_write);
+
+/**
+ * aic3xxx_set_bits: Set the value of a bitfield in a TLV320AIC3XXX register
+ *
+ * @aic3xxx: Device to write to.
+ * @reg: Register to write to.
+ * @mask: Mask of bits to set.
+ * @val: Value to set (unshifted)
+ */
+int aic3xxx_set_bits(struct aic3xxx *aic3xxx, unsigned int reg,
+		     unsigned char mask, unsigned char val)
+{
+	union aic3xxx_reg_union *aic_reg = (union aic3xxx_reg_union *) &reg;
+	int ret = 0;
+	u8 page, book, offset;
+
+	page = aic_reg->aic3xxx_register.page;
+	book = aic_reg->aic3xxx_register.book;
+	offset = aic_reg->aic3xxx_register.offset;
+
+	mutex_lock(&aic3xxx->io_lock);
+	if (book != aic3xxx->book_no) {
+		ret = set_aic3xxx_book(aic3xxx, book);
+		if (ret < 0) {
+			mutex_unlock(&aic3xxx->io_lock);
+			return ret;
+		}
+	}
+	if (page != aic3xxx->page_no) {
+		ret = set_aic3xxx_page(aic3xxx, page);
+		if (ret < 0) {
+			mutex_unlock(&aic3xxx->io_lock);
+			return ret;
+		}
+	}
+	ret = regmap_update_bits(aic3xxx->regmap, offset, mask, val);
+	mutex_unlock(&aic3xxx->io_lock);
+	return ret;
+
+}
+EXPORT_SYMBOL_GPL(aic3xxx_set_bits);
+
+/**
+ * aic3xxx_wait_bits: wait for a value of a bitfield in a TLV320AIC3XXX register
+ *
+ * @aic3xxx: Device to write to.
+ * @reg: Register to write to.
+ * @mask: Mask of bits to set.
+ * @val: Value to set (unshifted)
+ * @sleep: delay value in each iteration in micro seconds
+ * @count: iteration count for timeout
+ */
+int aic3xxx_wait_bits(struct aic3xxx *aic3xxx, unsigned int reg,
+		      unsigned char mask, unsigned char val, int sleep,
+		      int counter)
+{
+	int status;
+	int timeout = sleep * counter;
+
+	status = aic3xxx_reg_read(aic3xxx, reg);
+	while (((status & mask) != val) && counter) {
+		usleep_range(sleep, sleep + 500);
+		status = aic3xxx_reg_read(aic3xxx, reg);
+		counter--;
+	};
+	if (!counter)
+		dev_err(aic3xxx->dev,
+			"wait_bits timedout (%d millisecs). lastval 0x%x\n",
+			timeout, status);
+	return counter;
+}
+EXPORT_SYMBOL_GPL(aic3xxx_wait_bits);
+
+static struct mfd_cell aic3262_devs[] = {
+	{
+	.name = "tlv320aic3262-codec",
+	},
+	{
+	.name = "tlv320aic3262-gpio",
+	},
+	{
+	.name = "tlv320aic3262-extcon",
+	}
+};
+
+
+/**
+ * Instantiate the generic non-control parts of the device.
+ */
+int aic3xxx_device_init(struct aic3xxx *aic3xxx)
+{
+	const char *devname;
+	int ret, i;
+	u8 reset = 1;
+
+	mutex_init(&aic3xxx->io_lock);
+	dev_set_drvdata(aic3xxx->dev, aic3xxx);
+
+	if (dev_get_platdata(aic3xxx->dev))
+		memcpy(&aic3xxx->pdata, dev_get_platdata(aic3xxx->dev),
+			sizeof(aic3xxx->pdata));
+
+	/* GPIO reset for TLV320AIC3xxx codec */
+	if (gpio_is_valid(aic3xxx->pdata.gpio_reset)) {
+		ret = gpio_request_one(aic3xxx->pdata.gpio_reset,
+					GPIOF_DIR_OUT | GPIOF_INIT_LOW,
+					"aic3xxx-reset-pin");
+		if (ret != 0) {
+			dev_err(aic3xxx->dev, "not able to acquire gpio\n");
+			goto err_return;
+		}
+	}
+
+	/* run the codec through software reset */
+	ret = aic3xxx_reg_write(aic3xxx, AIC3XXX_RESET, reset);
+	if (ret < 0) {
+		dev_err(aic3xxx->dev, "Could not write to AIC3XXX register\n");
+		goto err_return;
+	}
+
+	usleep_range(10000, 10500);
+
+	ret = aic3xxx_reg_read(aic3xxx, AIC3XXX_DEVICE_ID);
+	if (ret < 0) {
+		dev_err(aic3xxx->dev, "Failed to read ID register\n");
+		goto err_return;
+	}
+
+	switch (ret) {
+	case 3:
+		devname = "TLV320AIC3262";
+		if (aic3xxx->type != TLV320AIC3262)
+			dev_warn(aic3xxx->dev, "Device registered as type %d\n",
+				 aic3xxx->type);
+		aic3xxx->type = TLV320AIC3262;
+		break;
+	default:
+		dev_err(aic3xxx->dev, "Device is not a TLV320AIC3XXX");
+		ret = -EINVAL;
+		goto err_return;
+	}
+
+	dev_info(aic3xxx->dev, "%s\n", devname);
+
+	/*If naudint is gpio convert it to irq number */
+	if (aic3xxx->pdata.gpio_irq == 1) {
+		aic3xxx->irq = gpio_to_irq(aic3xxx->pdata.naudint_irq);
+		gpio_request(aic3xxx->pdata.naudint_irq, "aic3xxx-gpio-irq");
+		gpio_direction_input(aic3xxx->pdata.naudint_irq);
+	} else {
+		aic3xxx->irq = aic3xxx->pdata.naudint_irq;
+	}
+
+	for (i = 0; i < aic3xxx->pdata.num_gpios; i++) {
+		aic3xxx_reg_write(aic3xxx, aic3xxx->pdata.gpio_defaults[i].reg,
+			aic3xxx->pdata.gpio_defaults[i].value);
+	}
+
+	if (aic3xxx->irq) {
+		ret = aic3xxx_irq_init(aic3xxx);
+		if (ret < 0)
+			goto err_irq;
+	}
+	switch (aic3xxx->type) {
+	case TLV320AIC3262:
+		ret = mfd_add_devices(aic3xxx->dev, -1, aic3262_devs,
+				ARRAY_SIZE(aic3262_devs), NULL,
+				0, aic3xxx->domain);
+		break;
+	default:
+		dev_err(aic3xxx->dev, "unable to recognize codec\n");
+		break;
+	}
+	if (ret != 0) {
+		dev_err(aic3xxx->dev, "Failed to add children: %d\n", ret);
+		goto err_mfd;
+	}
+	dev_info(aic3xxx->dev, "aic3xxx_device_init added mfd devices\n");
+
+	return 0;
+
+err_mfd:
+
+	aic3xxx_irq_exit(aic3xxx);
+err_irq:
+
+	if (aic3xxx->pdata.gpio_irq)
+		gpio_free(aic3xxx->pdata.naudint_irq);
+err_return:
+
+	if (aic3xxx->pdata.gpio_reset)
+		gpio_free(aic3xxx->pdata.gpio_reset);
+
+	return ret;
+}
+EXPORT_SYMBOL_GPL(aic3xxx_device_init);
+
+void aic3xxx_device_exit(struct aic3xxx *aic3xxx)
+{
+
+	mfd_remove_devices(aic3xxx->dev);
+	aic3xxx_irq_exit(aic3xxx);
+
+	if (aic3xxx->pdata.gpio_irq)
+		gpio_free(aic3xxx->pdata.naudint_irq);
+	if (aic3xxx->pdata.gpio_reset)
+		gpio_free(aic3xxx->pdata.gpio_reset);
+
+}
+EXPORT_SYMBOL_GPL(aic3xxx_device_exit);
+
+MODULE_AUTHOR("Mukund Navada <navada@ti.comm>");
+MODULE_AUTHOR("Mehar Bajwa <mehar.bajwa@ti.com>");
+MODULE_DESCRIPTION("Core support for the TLV320AIC3XXX audio CODEC");
+MODULE_LICENSE("GPL");
diff --git a/drivers/mfd/tlv320aic3xxx-i2c.c b/drivers/mfd/tlv320aic3xxx-i2c.c
new file mode 100644
index 0000000..3d6e11f
--- /dev/null
+++ b/drivers/mfd/tlv320aic3xxx-i2c.c
@@ -0,0 +1,97 @@
+/*
+ * tlv320aic3xxx-i2c.c  -- driver for TLV320AIC3XXX
+ *
+ * Author:	Mukund Navada <navada@ti.com>
+ *		Mehar Bajwa <mehar.bajwa@ti.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/err.h>
+#include <linux/i2c.h>
+#include <linux/module.h>
+#include <linux/pm_runtime.h>
+#include <linux/regmap.h>
+#include <linux/regulator/consumer.h>
+#include <linux/slab.h>
+
+#include <linux/mfd/tlv320aic3xxx-core.h>
+
+struct regmap_config aic3xxx_i2c_regmap = {
+	.reg_bits = 8,
+	.val_bits = 8,
+	.cache_type = REGCACHE_NONE,
+};
+
+static int aic3xxx_i2c_probe(struct i2c_client *i2c,
+					  const struct i2c_device_id *id)
+{
+	struct aic3xxx *aic3xxx;
+	const struct regmap_config *regmap_config;
+	int ret;
+
+	regmap_config = &aic3xxx_i2c_regmap;
+
+	aic3xxx = devm_kzalloc(&i2c->dev, sizeof(*aic3xxx), GFP_KERNEL);
+	if (aic3xxx == NULL)
+		return -ENOMEM;
+
+	aic3xxx->regmap = devm_regmap_init_i2c(i2c, regmap_config);
+
+	if (IS_ERR(aic3xxx->regmap)) {
+		ret = PTR_ERR(aic3xxx->regmap);
+		dev_err(&i2c->dev, "Failed to allocate register map: %d\n",
+			ret);
+		return ret;
+	}
+
+	aic3xxx->type = id->driver_data;
+	aic3xxx->dev = &i2c->dev;
+	aic3xxx->irq = i2c->irq;
+
+	return aic3xxx_device_init(aic3xxx);
+}
+
+static int aic3xxx_i2c_remove(struct i2c_client *i2c)
+{
+	struct aic3xxx *aic3xxx = dev_get_drvdata(&i2c->dev);
+
+	aic3xxx_device_exit(aic3xxx);
+	return 0;
+}
+
+static const struct i2c_device_id aic3xxx_i2c_id[] = {
+	{"tlv320aic3262", TLV320AIC3262},
+	{ }
+};
+MODULE_DEVICE_TABLE(i2c, aic3xxx_i2c_id);
+
+static struct i2c_driver aic3xxx_i2c_driver = {
+	.driver = {
+		.name	= "tlv320aic3xxx",
+		.owner	= THIS_MODULE,
+	},
+	.probe		= aic3xxx_i2c_probe,
+	.remove		= aic3xxx_i2c_remove,
+	.id_table	= aic3xxx_i2c_id,
+};
+
+module_i2c_driver(aic3xxx_i2c_driver);
+
+MODULE_DESCRIPTION("TLV320AIC3XXX I2C bus interface");
+MODULE_AUTHOR("Mukund Navada <navada@ti.com>");
+MODULE_LICENSE("GPL");
diff --git a/drivers/mfd/tlv320aic3xxx-irq.c b/drivers/mfd/tlv320aic3xxx-irq.c
new file mode 100644
index 0000000..8830fba
--- /dev/null
+++ b/drivers/mfd/tlv320aic3xxx-irq.c
@@ -0,0 +1,234 @@
+/*
+ * tlv320aic3xxx-irq.c  --  Interrupt controller support for
+ *			 TI TLV320AIC3xxx family
+ *
+ * Author:      Mukund Navada <navada@ti.com>
+ *              Mehar Bajwa <mehar.bajwa@ti.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/i2c.h>
+#include <linux/irq.h>
+#include <linux/mfd/core.h>
+#include <linux/interrupt.h>
+#include <linux/irqdomain.h>
+
+#include <linux/mfd/tlv320aic3xxx-core.h>
+#include <linux/mfd/tlv320aic3xxx-registers.h>
+
+#include <linux/delay.h>
+
+struct aic3xxx_irq_data {
+	int mask;
+	int status;
+};
+
+static struct aic3xxx_irq_data aic3xxx_irqs[] = {
+	{
+	 .mask = AIC3XXX_HEADSET_IN_M,
+	 .status = AIC3XXX_HEADSET_PLUG_UNPLUG_INT,
+	 },
+	{
+	 .mask = AIC3XXX_BUTTON_PRESS_M,
+	 .status = AIC3XXX_BUTTON_PRESS_INT,
+	 },
+	{
+	 .mask = AIC3XXX_DAC_DRC_THRES_M,
+	 .status = AIC3XXX_LEFT_DRC_THRES_INT | AIC3XXX_RIGHT_DRC_THRES_INT,
+	 },
+	{
+	 .mask = AIC3XXX_AGC_NOISE_M,
+	 .status = AIC3XXX_LEFT_AGC_NOISE_INT | AIC3XXX_RIGHT_AGC_NOISE_INT,
+	 },
+	{
+	 .mask = AIC3XXX_OVER_CURRENT_M,
+	 .status = AIC3XXX_LEFT_OUTPUT_DRIVER_OVERCURRENT_INT
+	 | AIC3XXX_RIGHT_OUTPUT_DRIVER_OVERCURRENT_INT,
+	 },
+	{
+	 .mask = AIC3XXX_OVERFLOW_M,
+	 .status =
+	 AIC3XXX_LEFT_DAC_OVERFLOW_INT | AIC3XXX_RIGHT_DAC_OVERFLOW_INT |
+	 AIC3XXX_MINIDSP_D_BARREL_SHIFT_OVERFLOW_INT |
+	 AIC3XXX_LEFT_ADC_OVERFLOW_INT | AIC3XXX_RIGHT_ADC_OVERFLOW_INT |
+	 AIC3XXX_MINIDSP_D_BARREL_SHIFT_OVERFLOW_INT,
+	 },
+	{
+	 .mask = AIC3XXX_SPK_OVERCURRENT_M,
+	 .status = AIC3XXX_SPK_OVER_CURRENT_INT,
+	 },
+
+};
+
+static void aic3xxx_irq_lock(struct irq_data *data)
+{
+	struct aic3xxx *aic3xxx = irq_data_get_irq_chip_data(data);
+
+	mutex_lock(&aic3xxx->irq_lock);
+}
+
+static void aic3xxx_irq_sync_unlock(struct irq_data *data)
+{
+	struct aic3xxx *aic3xxx = irq_data_get_irq_chip_data(data);
+
+	/* write back to hardware any change in irq mask */
+	if (aic3xxx->irq_masks_cur != aic3xxx->irq_masks_cache) {
+		aic3xxx->irq_masks_cache = aic3xxx->irq_masks_cur;
+		aic3xxx_reg_write(aic3xxx, AIC3XXX_INT1_CNTL,
+				  aic3xxx->irq_masks_cur);
+	}
+
+	mutex_unlock(&aic3xxx->irq_lock);
+}
+
+
+static void aic3xxx_irq_enable(struct irq_data *data)
+{
+	struct aic3xxx *aic3xxx = irq_data_get_irq_chip_data(data);
+	struct aic3xxx_irq_data *irq_data = &aic3xxx_irqs[data->hwirq];
+	aic3xxx->irq_masks_cur |= irq_data->mask;
+}
+
+static void aic3xxx_irq_disable(struct irq_data *data)
+{
+	struct aic3xxx *aic3xxx = irq_data_get_irq_chip_data(data);
+	struct aic3xxx_irq_data *irq_data = &aic3xxx_irqs[data->hwirq];
+
+	aic3xxx->irq_masks_cur &= ~irq_data->mask;
+}
+
+static struct irq_chip aic3xxx_irq_chip = {
+	.name = "tlv320aic3xxx",
+	.irq_bus_lock = aic3xxx_irq_lock,
+	.irq_bus_sync_unlock = aic3xxx_irq_sync_unlock,
+	.irq_enable = aic3xxx_irq_enable,
+	.irq_disable = aic3xxx_irq_disable
+};
+
+static irqreturn_t aic3xxx_irq_thread(int irq, void *data)
+{
+	struct aic3xxx *aic3xxx = data;
+	u8 status[4];
+
+	/* Reading sticky bit registers acknowledges
+		the interrupt to the device */
+	aic3xxx_bulk_read(aic3xxx, AIC3XXX_INT_STICKY_FLAG1, 4, status);
+
+	/* report  */
+	if (status[2] & aic3xxx_irqs[AIC3XXX_IRQ_HEADSET_DETECT].status)
+		handle_nested_irq(aic3xxx->irq_base);
+	if (status[2] & aic3xxx_irqs[AIC3XXX_IRQ_BUTTON_PRESS].status)
+		handle_nested_irq(aic3xxx->irq_base + 1);
+	if (status[2] & aic3xxx_irqs[AIC3XXX_IRQ_DAC_DRC].status)
+		handle_nested_irq(aic3xxx->irq_base + 2);
+	if (status[3] & aic3xxx_irqs[AIC3XXX_IRQ_AGC_NOISE].status)
+		handle_nested_irq(aic3xxx->irq_base + 3);
+	if (status[2] & aic3xxx_irqs[AIC3XXX_IRQ_OVER_CURRENT].status)
+		handle_nested_irq(aic3xxx->irq_base + 4);
+	if (status[0] & aic3xxx_irqs[AIC3XXX_IRQ_OVERFLOW_EVENT].status)
+		handle_nested_irq(aic3xxx->irq_base + 5);
+	if (status[3] & aic3xxx_irqs[AIC3XXX_IRQ_SPEAKER_OVER_TEMP].status)
+		handle_nested_irq(aic3xxx->irq_base + 6);
+
+	return IRQ_HANDLED;
+}
+
+static int aic3xxx_irq_map(struct irq_domain *h, unsigned int virq,
+				irq_hw_number_t hw)
+{
+	struct aic3xxx *aic3xxx = h->host_data;
+
+	irq_set_chip_data(virq, aic3xxx);
+	irq_set_chip_and_handler(virq, &aic3xxx_irq_chip, handle_edge_irq);
+	irq_set_nested_thread(virq, 1);
+
+	/* ARM needs us to explicitly flag the IRQ as valid
+	 * and will set them noprobe when we do so. */
+#ifdef CONFIG_ARM
+	set_irq_flags(virq, IRQF_VALID);
+#else
+	irq_set_noprobe(virq);
+#endif
+
+	return 0;
+}
+
+static const struct irq_domain_ops aic3xxx_domain_ops = {
+	.map    = aic3xxx_irq_map,
+	.xlate  = irq_domain_xlate_twocell,
+};
+
+int aic3xxx_irq_init(struct aic3xxx *aic3xxx)
+{
+	int ret;
+
+	mutex_init(&aic3xxx->irq_lock);
+
+	/* mask the individual interrupt sources */
+	aic3xxx->irq_masks_cur = 0x0;
+	aic3xxx->irq_masks_cache = 0x0;
+	aic3xxx_reg_write(aic3xxx, AIC3XXX_INT1_CNTL, 0x0);
+
+	if (!aic3xxx->irq) {
+		dev_warn(aic3xxx->dev, "no interrupt specified\n");
+		aic3xxx->irq_base = 0;
+		return 0;
+	}
+	if (aic3xxx->irq_base) {
+		aic3xxx->domain = irq_domain_add_legacy(aic3xxx->dev->of_node,
+					ARRAY_SIZE(aic3xxx_irqs),
+					aic3xxx->irq_base, 0,
+					&aic3xxx_domain_ops, aic3xxx);
+	} else {
+		aic3xxx->domain = irq_domain_add_linear(aic3xxx->dev->of_node,
+					ARRAY_SIZE(aic3xxx_irqs),
+					&aic3xxx_domain_ops, aic3xxx);
+		/* initiallizing irq_base from irq_domain*/
+	}
+	if (!aic3xxx->domain) {
+		dev_err(aic3xxx->dev, "Failed to create IRQ domain\n");
+		return -ENOMEM;
+	}
+
+	aic3xxx->irq_base = irq_create_mapping(aic3xxx->domain, 0);
+
+	ret = request_threaded_irq(aic3xxx->irq, NULL, aic3xxx_irq_thread,
+				   IRQF_ONESHOT,
+				   "tlv320aic3xxx", aic3xxx);
+	if (ret < 0) {
+		dev_err(aic3xxx->dev, "failed to request IRQ %d: %d\n",
+			aic3xxx->irq, ret);
+		return ret;
+	}
+	irq_set_irq_type(aic3xxx->irq, IRQF_TRIGGER_RISING);
+
+	return 0;
+}
+EXPORT_SYMBOL(aic3xxx_irq_init);
+
+void aic3xxx_irq_exit(struct aic3xxx *aic3xxx)
+{
+	if (aic3xxx->irq)
+		free_irq(aic3xxx->irq, aic3xxx);
+}
+EXPORT_SYMBOL(aic3xxx_irq_exit);
+MODULE_AUTHOR("Mukund navada <navada@ti.com>");
+MODULE_AUTHOR("Mehar Bajwa <mehar.bajwa@ti.com>");
+MODULE_DESCRIPTION("Interrupt controller support for TI TLV320AIC3XXX family");
+MODULE_LICENSE("GPL");
diff --git a/drivers/mfd/tlv320aic3xxx-spi.c b/drivers/mfd/tlv320aic3xxx-spi.c
new file mode 100644
index 0000000..b981378
--- /dev/null
+++ b/drivers/mfd/tlv320aic3xxx-spi.c
@@ -0,0 +1,96 @@
+/*
+ * tlv320aic3xxx-spi.c  -- driver for TLV320AIC3XXX
+ *
+ * Author:      Mukund Navada <navada@ti.com>
+ *		Mehar Bajwa <mehar.bajwa@ti.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ *
+ */
+
+#include <linux/err.h>
+#include <linux/module.h>
+#include <linux/pm_runtime.h>
+#include <linux/regmap.h>
+#include <linux/regulator/consumer.h>
+#include <linux/slab.h>
+#include <linux/spi/spi.h>
+
+#include <linux/mfd/tlv320aic3xxx-core.h>
+
+struct regmap_config aic3xxx_spi_regmap = {
+	.reg_bits = 7,
+	.val_bits = 8,
+	.cache_type = REGCACHE_NONE,
+	.read_flag_mask = 0x1,
+	.pad_bits = 1;
+};
+
+static int __devinit tlv320aic3xxx_spi_probe(struct spi_device *spi)
+{
+	const struct spi_device_id *id = spi_get_device_id(spi);
+	struct aic3xxx *aic3xxx;
+	const struct regmap_config *regmap_config;
+	int ret;
+
+	regmap_config = &aic3xxx_spi_regmap;
+
+	aic3xxx = devm_kzalloc(&spi->dev, sizeof(struct aic3xxx), GFP_KERNEL);
+	if (aic3xxx == NULL)
+		return -ENOMEM;
+
+	aic3xxx->regmap = devm_regmap_init_spi(spi, regmap_config);
+	if (IS_ERR(aic3xxx->regmap)) {
+		ret = PTR_ERR(aic3xxx->regmap);
+		dev_err(&spi->dev, "Failed to allocate register map: %d\n",
+			ret);
+		return ret;
+	}
+
+	aic3xxx->type = id->driver_data;
+	aic3xxx->dev = &spi->dev;
+	aic3xxx->irq = spi->irq;
+
+	return aic3xxx_device_init(aic3xxx);
+}
+
+static int __devexit tlv320aic3xxx_spi_remove(struct spi_device *spi)
+{
+	struct aic3xxx *aic3xxx = dev_get_drvdata(&spi->dev);
+	aic3xxx_device_exit(aic3xxx);
+	return 0;
+}
+
+static const struct spi_device_id aic3xxx_spi_ids[] = {
+	{"tlv320aic3262", TLV320AIC3262},
+	{ }
+};
+MODULE_DEVICE_TABLE(spi, aic3xxx_spi_ids);
+
+static struct spi_driver aic3xxx_spi_driver = {
+	.driver = {
+		.name	= "tlv320aic3xxx",
+		.owner	= THIS_MODULE,
+	},
+	.probe		= tlv320aic3xxx_spi_probe,
+	.remove		= __devexit_p(tlv320aic3xxx_spi_remove),
+	.id_table	= aic3xxx_spi_ids,
+};
+
+module_spi_driver(tlv320aic3xxx_spi_driver);
+
+MODULE_DESCRIPTION("TLV320AIC3XXX SPI bus interface");
+MODULE_AUTHOR("Mukund Navada <navada@ti.com>");
+MODULE_LICENSE("GPL");
diff --git a/include/linux/mfd/tlv320aic3262-registers.h b/include/linux/mfd/tlv320aic3262-registers.h
new file mode 100644
index 0000000..0fa2e4c
--- /dev/null
+++ b/include/linux/mfd/tlv320aic3262-registers.h
@@ -0,0 +1,312 @@
+
+/*
+ *tlv320aic3262-registers: Register bits for AIC3262 codecs
+ *
+ *
+ * Author:      Mukund Navada <navada@ti.com>
+ *              Mehar Bajwa <mehar.bajwa@ti.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ *
+ */
+
+#ifndef __MFD_AIC3262_REGISTERS_H__
+#define __MFD_AIC3262_REGISTERS_H__
+
+
+/* ****************** Book 0 Registers **************************************/
+
+/* ****************** Page 0 Registers **************************************/
+#define AIC3262_PAGE_SEL_REG		AIC3XXX_MAKE_REG(0, 0, 0)
+#define AIC3262_RESET_REG		AIC3XXX_MAKE_REG(0, 0, 1)
+#define AIC3262_REV_PG_ID		AIC3XXX_MAKE_REG(0, 0, 2)
+#define AIC3262_REV_MASK		(0b01110000)
+#define AIC3262_REV_SHIFT		4
+#define AIC3262_PG_MASK			(0b00000111)
+#define AIC3262_PG_SHIFT		0
+#define AIC3262_DAC_ADC_CLKIN_REG	AIC3XXX_MAKE_REG(0, 0, 4)
+#define AIC3262_PLL_CLKIN_REG		AIC3XXX_MAKE_REG(0, 0, 5)
+#define AIC3262_PLL_CLKIN_MASK		(0b00111100)
+#define AIC3262_PLL_CLKIN_SHIFT		2
+#define AIC3262_PLL_CLKIN_MCLK1		0
+#define AIC3262_PLL_CLKIN_BCLK1		1
+#define AIC3262_PLL_CLKIN_GPIO1		2
+#define AIC3262_PLL_CLKIN_DIN1		3
+#define AIC3262_PLL_CLKIN_BCLK2		4
+#define AIC3262_PLL_CLKIN_GPI1		5
+#define AIC3262_PLL_CLKIN_HF_REF_CLK	6
+#define AIC3262_PLL_CLKIN_GPIO2		7
+#define AIC3262_PLL_CLKIN_GPI2		8
+#define AIC3262_PLL_CLKIN_MCLK2		9
+#define AIC3262_CLK_VAL_MASK		0x7f
+#define AIC3262_PLL_CLK_RANGE_REG	AIC3XXX_MAKE_REG(0, 0, 5)
+#define AIC3262_PLL_PR_POW_REG		AIC3XXX_MAKE_REG(0, 0, 6)
+#define AIC3262_PLL_PVAL_MASK		0x70
+#define AIC3262_PLL_RVAL_MASK		0x0F
+
+#define AIC3262_ENABLE_CLK_MASK		0x80
+#define AIC3262_ENABLE_CLK		0x80
+
+#define AIC3262_PLL_J_REG		AIC3XXX_MAKE_REG(0, 0, 7)
+#define AIC3262_JVAL_MASK		0x3f
+#define AIC3262_PLL_D_MSB		AIC3XXX_MAKE_REG(0, 0, 8)
+#define AIC3262_DVAL_MSB_MASK		0xf
+#define AIC3262_DVAL_LSB_MASK		0xff
+#define AIC3262_PLL_D_LSB		AIC3XXX_MAKE_REG(0, 0, 9)
+#define AIC3262_PLL_CKIN_DIV		AIC3XXX_MAKE_REG(0, 0, 10)
+
+#define AIC3262_NDAC_DIV_POW_REG	AIC3XXX_MAKE_REG(0, 0, 11)
+#define AIC3262_MDAC_DIV_POW_REG	AIC3XXX_MAKE_REG(0, 0, 12)
+#define AIC3262_DOSR_MSB_REG		AIC3XXX_MAKE_REG(0, 0, 13)
+#define AIC3262_DOSR_MSB_MASK		0x3
+#define AIC3262_DOSR_LSB_REG		AIC3XXX_MAKE_REG(0, 0, 14)
+#define AIC3262_DOSR_LSB_MASK		0xFF
+
+#define AIC3262_NADC_DIV_POW_REG	AIC3XXX_MAKE_REG(0, 0, 18)
+#define AIC3262_MADC_DIV_POW_REG	AIC3XXX_MAKE_REG(0, 0, 19)
+#define AIC3262_AOSR_REG		AIC3XXX_MAKE_REG(0, 0, 20)
+#define AIC3262_CLKOUT_MUX		AIC3XXX_MAKE_REG(0, 0, 21)
+#define AIC3262_CLKOUT_MDIV_VAL		AIC3XXX_MAKE_REG(0, 0, 22)
+#define AIC3262_TIMER_REG		AIC3XXX_MAKE_REG(0, 0, 23)
+
+#define AIC3262_LF_CLK_CNTL		AIC3XXX_MAKE_REG(0, 0, 24)
+#define AIC3262_HF_CLK_CNTL_R1		AIC3XXX_MAKE_REG(0, 0, 25)
+#define AIC3262_HF_CLK_CNTL_R2		AIC3XXX_MAKE_REG(0, 0, 26)
+#define AIC3262_HF_CLK_CNTL_R3		AIC3XXX_MAKE_REG(0, 0, 27)
+#define AIC3262_HF_CLK_CNTL_R4		AIC3XXX_MAKE_REG(0, 0, 28)
+#define AIC3262_HF_CLK_TRIM_R1		AIC3XXX_MAKE_REG(0, 0, 29)
+#define AIC3262_HF_CLK_TRIM_R2		AIC3XXX_MAKE_REG(0, 0, 30)
+#define AIC3262_HF_CLK_TRIM_R3		AIC3XXX_MAKE_REG(0, 0, 31)
+#define AIC3262_HF_CLK_TRIM_R4		AIC3XXX_MAKE_REG(0, 0, 32)
+#define AIC3262_LDAC_POWER_STATUS_MASK		0x80
+#define AIC3262_RDAC_POWER_STATUS_MASK		0x08
+#define AIC3262_DAC_POWER_MASK		0x88
+#define AIC3262_DAC_FLAG		AIC3XXX_MAKE_REG(0, 0, 37)
+#define AIC3262_LADC_POWER_MASK		0x40
+#define AIC3262_RADC_POWER_MASK		0x04
+#define AIC3262_ADC_POWER_MASK		0x44
+#define AIC3262_ADC_FLAG                AIC3XXX_MAKE_REG(0, 0, 36)
+#define AIC3262_JACK_WITH_STEREO_HS	(0b00000010)
+#define AIC3262_JACK_WITH_MIC		(0b00110000)
+#define AIC3262_HEADSET_NOT_INSERTED	(0b00000011)
+#define AIC3262_JACK_TYPE_MASK		(0b00110000)
+#define AIC3262_JACK_NOT_INSERTED	(0b00000000)
+#define AIC3262_JACK_WITHOUT_MIC	(0b00010000)
+
+#define AIC3262_RIGHT_DRC_AUX_INT			0x01
+#define AIC3262_INT1_CNTL		AIC3XXX_MAKE_REG(0, 0, 48)
+
+#define AIC3262_DAC_PRB			AIC3XXX_MAKE_REG(0, 0, 60)
+#define AIC3262_ADC_PRB			AIC3XXX_MAKE_REG(0, 0, 61)
+#define AIC3262_PASI_DAC_DP_SETUP	AIC3XXX_MAKE_REG(0, 0, 63)
+
+#define AIC3262_DAC_MVOL_CONF		AIC3XXX_MAKE_REG(0, 0, 64)
+#define AIC3262_DAC_LR_MUTE_MASK	0xc
+#define AIC3262_DAC_LR_MUTE		0xc
+
+#define AIC3262_DAC_LVOL		AIC3XXX_MAKE_REG(0, 0, 65)
+#define AIC3262_DAC_RVOL		AIC3XXX_MAKE_REG(0, 0, 66)
+#define AIC3262_HP_DETECT		AIC3XXX_MAKE_REG(0, 0, 67)
+#define AIC3262_DRC_CNTL_R1		AIC3XXX_MAKE_REG(0, 0, 68)
+#define AIC3262_DRC_CNTL_R2		AIC3XXX_MAKE_REG(0, 0, 69)
+#define AIC3262_DRC_CNTL_R3		AIC3XXX_MAKE_REG(0, 0, 70)
+#define AIC3262_BEEP_CNTL_R1		AIC3XXX_MAKE_REG(0, 0, 71)
+#define AIC3262_BEEP_CNTL_R2		AIC3XXX_MAKE_REG(0, 0, 72)
+
+#define AIC3262_ADC_CHANNEL_POW		AIC3XXX_MAKE_REG(0, 0, 81)
+#define AIC3262_ADC_FINE_GAIN		AIC3XXX_MAKE_REG(0, 0, 82)
+#define AIC3262_LADC_VOL		AIC3XXX_MAKE_REG(0, 0, 83)
+#define AIC3262_RADC_VOL		AIC3XXX_MAKE_REG(0, 0, 84)
+#define AIC3262_ADC_PHASE		AIC3XXX_MAKE_REG(0, 0, 85)
+
+#define AIC3262_LAGC_CNTL		AIC3XXX_MAKE_REG(0, 0, 86)
+#define AIC3262_LAGC_CNTL_R2		AIC3XXX_MAKE_REG(0, 0, 87)
+#define AIC3262_LAGC_CNTL_R3		AIC3XXX_MAKE_REG(0, 0, 88)
+#define AIC3262_LAGC_CNTL_R4		AIC3XXX_MAKE_REG(0, 0, 89)
+#define AIC3262_LAGC_CNTL_R5		AIC3XXX_MAKE_REG(0, 0, 90)
+#define AIC3262_LAGC_CNTL_R6		AIC3XXX_MAKE_REG(0, 0, 91)
+#define AIC3262_LAGC_CNTL_R7		AIC3XXX_MAKE_REG(0, 0, 92)
+#define AIC3262_LAGC_CNTL_R8		AIC3XXX_MAKE_REG(0, 0, 93)
+
+#define AIC3262_RAGC_CNTL		AIC3XXX_MAKE_REG(0, 0, 94)
+#define AIC3262_RAGC_CNTL_R2		AIC3XXX_MAKE_REG(0, 0, 95)
+#define AIC3262_RAGC_CNTL_R3		AIC3XXX_MAKE_REG(0, 0, 96)
+#define AIC3262_RAGC_CNTL_R4		AIC3XXX_MAKE_REG(0, 0, 97)
+#define AIC3262_RAGC_CNTL_R5		AIC3XXX_MAKE_REG(0, 0, 98)
+#define AIC3262_RAGC_CNTL_R6		AIC3XXX_MAKE_REG(0, 0, 99)
+#define AIC3262_RAGC_CNTL_R7		AIC3XXX_MAKE_REG(0, 0, 100)
+#define AIC3262_RAGC_CNTL_R8		AIC3XXX_MAKE_REG(0, 0, 101)
+#define AIC3262_MINIDSP_ACCESS_CTRL	AIC3XXX_MAKE_REG(0, 0, 121)
+/* ****************** Page 1 Registers **************************************/
+#define AIC3262_PAGE_1			128
+
+#define AIC3262_POWER_CONF		AIC3XXX_MAKE_REG(0, 1, 1)
+
+#define AIC3262_AVDD_TO_DVDD_MASK	(0b00001000)
+#define	AIC3262_AVDD_TO_DVDD		0x8
+#define AIC3262_EXT_ANALOG_SUPPLY_MASK	(0b00000100)
+#define	AIC3262_EXT_ANALOG_SUPPLY_OFF	0x4
+
+#define AIC3262_LDAC_PTM		AIC3XXX_MAKE_REG(0, 1, 3)
+#define AIC3262_RDAC_PTM		AIC3XXX_MAKE_REG(0, 1, 4)
+#define AIC3262_CM_REG			AIC3XXX_MAKE_REG(0, 1, 8)
+#define AIC3262_HP_CTL			AIC3XXX_MAKE_REG(0, 1, 9)
+#define AIC3262_HP_DEPOP		AIC3XXX_MAKE_REG(0, 1, 11)
+#define AIC3262_RECV_DEPOP		AIC3XXX_MAKE_REG(0, 1, 12)
+#define AIC3262_MA_CNTL			AIC3XXX_MAKE_REG(0, 1, 17)
+#define AIC3262_LADC_PGA_MAL_VOL	AIC3XXX_MAKE_REG(0, 1, 18)
+#define AIC3262_RADC_PGA_MAR_VOL	AIC3XXX_MAKE_REG(0, 1, 19)
+
+#define AIC3262_LINE_AMP_CNTL_R1	AIC3XXX_MAKE_REG(0, 1, 22)
+#define AIC3262_LINE_AMP_CNTL_R2	AIC3XXX_MAKE_REG(0, 1, 23)
+
+#define AIC3262_HP_AMP_CNTL_R1		AIC3XXX_MAKE_REG(0, 1, 27)
+#define AIC3262_HP_AMP_CNTL_R2		AIC3XXX_MAKE_REG(0, 1, 28)
+#define AIC3262_HP_AMP_CNTL_R3		AIC3XXX_MAKE_REG(0, 1, 29)
+
+#define AIC3262_HPL_VOL			AIC3XXX_MAKE_REG(0, 1, 31)
+#define AIC3262_HPR_VOL			AIC3XXX_MAKE_REG(0, 1, 32)
+#define AIC3262_INT1_SEL_L		AIC3XXX_MAKE_REG(0, 1, 34)
+#define AIC3262_CHARGE_PUMP_CNTL	AIC3XXX_MAKE_REG(0, 1, 35)
+#define AIC3262_RAMP_CNTL_R1		AIC3XXX_MAKE_REG(0, 1, 36)
+#define AIC3262_RAMP_CNTL_R2		AIC3XXX_MAKE_REG(0, 1, 37)
+#define AIC3262_IN1L_SEL_RM		AIC3XXX_MAKE_REG(0, 1, 38)
+#define AIC3262_IN1R_SEL_RM		AIC3XXX_MAKE_REG(0, 1, 39)
+#define AIC3262_REC_AMP_CNTL_R5		AIC3XXX_MAKE_REG(0, 1, 40)
+#define AIC3262_RAMPR_VOL		AIC3XXX_MAKE_REG(0, 1, 41)
+#define AIC3262_RAMP_TIME_CNTL		AIC3XXX_MAKE_REG(0, 1, 42)
+#define AIC3262_SPK_AMP_CNTL_R1		AIC3XXX_MAKE_REG(0, 1, 45)
+#define AIC3262_SPK_AMP_CNTL_R2		AIC3XXX_MAKE_REG(0, 1, 46)
+#define AIC3262_SPK_AMP_CNTL_R3		AIC3XXX_MAKE_REG(0, 1, 47)
+#define AIC3262_SPK_AMP_CNTL_R4		AIC3XXX_MAKE_REG(0, 1, 48)
+#define AIC3262_MIC_BIAS_CNTL		AIC3XXX_MAKE_REG(0, 1, 51)
+
+#define AIC3262_LMIC_PGA_PIN		AIC3XXX_MAKE_REG(0, 1, 52)
+#define AIC3262_LMIC_PGA_PM_IN4		AIC3XXX_MAKE_REG(0, 1, 53)
+#define AIC3262_LMIC_PGA_MIN		AIC3XXX_MAKE_REG(0, 1, 54)
+#define AIC3262_RMIC_PGA_PIN		AIC3XXX_MAKE_REG(0, 1, 55)
+#define AIC3262_RMIC_PGA_PM_IN4		AIC3XXX_MAKE_REG(0, 1, 56)
+#define AIC3262_RMIC_PGA_MIN		AIC3XXX_MAKE_REG(0, 1, 57)
+#define AIC3262_HP_FLAG		        AIC3XXX_MAKE_REG(0, 1, 66)
+#define AIC3262_SPKL_POWER_MASK		0x2
+#define AIC3262_SPKR_POWER_MASK		0x1
+#define AIC3262_HPL_POWER_MASK		(0b00000010)
+#define AIC3262_HPR_POWER_MASK		(0b00000001)
+#define AIC3262_SPKL_POWER_STATUS_MASK		0x2
+#define AIC3262_SPKR_POWER_STATUS_MASK		0x1
+#define AIC3262_HPL_POWER_STATUS_MASK		0x20
+#define AIC3262_HPR_POWER_STATUS_MASK		0x10
+
+#define AIC3262_HP_STAGE_MASK		(0b01100000)
+#define AIC3262_HP_STAGE_100		(0)
+#define AIC3262_HP_STAGE_75		(1)
+#define AIC3262_HP_STAGE_50		(2)
+#define AIC3262_HP_STAGE_25		(3)
+#define AIC3262_HP_STAGE_SHIFT		(5)
+#define AIC3262_DYNAMIC_OFFSET_CALIB_MASK	(0b00100000)
+#define AIC3262_DYNAMIC_OFFSET_CALIB		(0b00100000)
+
+/* MIC PGA Gain Registers */
+#define AIC3262_MICL_PGA		AIC3XXX_MAKE_REG(0, 1, 59)
+#define AIC3262_MICR_PGA		AIC3XXX_MAKE_REG(0, 1, 60)
+#define AIC3262_HEADSET_TUNING1_REG	AIC3XXX_MAKE_REG(0, 1, 119)
+#define AIC3262_HEADSET_DETECTOR_PULSE_MASK (0b11000000)
+#define AIC3262_HEADSET_DETECTOR_PULSE_RESET (0b10000000)
+#define AIC3262_MIC_PWR_DLY		AIC3XXX_MAKE_REG(0, 1, 121)
+#define AIC3262_REF_PWR_DLY		AIC3XXX_MAKE_REG(0, 1, 122)
+#define AIC3262_CHIP_REF_PWR_ON_MASK	0x4
+#define AIC3262_CHIP_REF_PWR_ON		0x4
+/* ****************** Page 4 Registers **************************************/
+#define AIC3262_PAGE_4			512
+#define AIC3262_ASI1_BUS_FMT		AIC3XXX_MAKE_REG(0, 4, 1)
+#define AIC3262_ASI_SELECTION_MASK	(0b11100000)
+#define AIC3262_ASI_DATA_WORD_LENGTH_MASK	(0b00011000)
+#define AIC3262_ASI1_BCLK_N_MASK	(0b01111111)
+#define AIC3262_ASI1_WCLK_N_MASK	(0b01111111)
+#define AIC3262_ASI1_CHNL_MASK		(0b11000000)
+#define AIC3262_ASI1_DAC_OUT_OFFSET	(0b00000001)
+#define AIC3262_ASI1_LCH_OFFSET		AIC3XXX_MAKE_REG(0, 4, 2)
+#define AIC3262_ASI1_RCH_OFFSET		AIC3XXX_MAKE_REG(0, 4, 3)
+#define AIC3262_ASI1_CHNL_SETUP		AIC3XXX_MAKE_REG(0, 4, 4)
+#define AIC3262_ASI1_MULTI_CH_SETUP_R1	AIC3XXX_MAKE_REG(0, 4, 5)
+#define AIC3262_ASI1_MULTI_CH_SETUP_R2	AIC3XXX_MAKE_REG(0, 4, 6)
+#define AIC3262_ASI1_ADC_INPUT_CNTL	AIC3XXX_MAKE_REG(0, 4, 7)
+#define AIC3262_ASI1_DAC_OUT_CNTL	AIC3XXX_MAKE_REG(0, 4, 8)
+#define AIC3262_ASI1_ADC_OUT_TRISTATE	AIC3XXX_MAKE_REG(0, 4, 9)
+#define AIC3262_ASI1_BWCLK_CNTL_REG	AIC3XXX_MAKE_REG(0, 4, 10)
+#define AIC3262_ASI1_BCLK_N_CNTL	AIC3XXX_MAKE_REG(0, 4, 11)
+#define AIC3262_ASI1_BCLK_N		AIC3XXX_MAKE_REG(0, 4, 12)
+#define AIC3262_ASI1_WCLK_N		AIC3XXX_MAKE_REG(0, 4, 13)
+#define AIC3262_ASI1_BWCLK_OUT_CNTL	AIC3XXX_MAKE_REG(0, 4, 14)
+#define AIC3262_ASI1_DOUT_CNTL		AIC3XXX_MAKE_REG(0, 4, 15)
+#define AIC3262_ASI2_BUS_FMT	        AIC3XXX_MAKE_REG(0, 4, 17)
+#define AIC3262_ASI2_LCH_OFFSET		AIC3XXX_MAKE_REG(0, 4, 18)
+#define AIC3262_ASI2_ADC_INPUT_CNTL	AIC3XXX_MAKE_REG(0, 4, 23)
+#define AIC3262_ASI2_DAC_OUT_CNTL	AIC3XXX_MAKE_REG(0, 4, 24)
+#define AIC3262_ASI2_BWCLK_CNTL_REG	AIC3XXX_MAKE_REG(0, 4, 26)
+#define AIC3262_ASI2_BCLK_N_CNTL	AIC3XXX_MAKE_REG(0, 4, 27)
+#define AIC3262_ASI2_BCLK_N		AIC3XXX_MAKE_REG(0, 4, 28)
+#define AIC3262_ASI2_WCLK_N		AIC3XXX_MAKE_REG(0, 4, 29)
+#define AIC3262_ASI2_BWCLK_OUT_CNTL	AIC3XXX_MAKE_REG(0, 4, 30)
+#define AIC3262_ASI2_DOUT_CNTL		AIC3XXX_MAKE_REG(0, 4, 31)
+#define AIC3262_ASI3_BUS_FMT		AIC3XXX_MAKE_REG(0, 4, 33)
+#define AIC3262_ASI3_LCH_OFFSET		AIC3XXX_MAKE_REG(0, 4, 34)
+#define AIC3262_ASI3_ADC_INPUT_CNTL	AIC3XXX_MAKE_REG(0, 4, 39)
+#define AIC3262_ASI3_DAC_OUT_CNTL	AIC3XXX_MAKE_REG(0, 4, 40)
+#define AIC3262_ASI3_BWCLK_CNTL_REG	AIC3XXX_MAKE_REG(0, 4, 42)
+#define AIC3262_ASI3_BCLK_N_CNTL	AIC3XXX_MAKE_REG(0, 4, 43)
+#define AIC3262_ASI3_BCLK_N		AIC3XXX_MAKE_REG(0, 4, 44)
+#define AIC3262_ASI3_WCLK_N		AIC3XXX_MAKE_REG(0, 4, 45)
+#define AIC3262_ASI3_BWCLK_OUT_CNTL	AIC3XXX_MAKE_REG(0, 4, 46)
+#define AIC3262_ASI3_DOUT_CNTL		AIC3XXX_MAKE_REG(0, 4, 47)
+#define AIC3262_DMIC_INPUT_CNTL		AIC3XXX_MAKE_REG(0, 4, 101)
+#define AIC3262_GPIO1_IO_CNTL		AIC3XXX_MAKE_REG(0, 4, 86)
+#define AIC3262_GPIO_D6_D2		(0b01111100)
+#define AIC3262_GPIO_D2_SHIFT		(2)
+#define AIC3262_GPIO_D1_SHIFT		(1)
+#define AIC3262_GPIO_D4_SHIFT		(4)
+#define AIC3262_GPIO2_IO_CNTL		AIC3XXX_MAKE_REG(0, 4, 87)
+#define AIC3262_GPI1_EN			AIC3XXX_MAKE_REG(0, 4, 91)
+#define AIC3262_GPI1_D2_D1		(0b00000110)
+#define AIC3262_GPI2_D5_D4		(0b00110000)
+#define AIC3262_GPI2_EN			AIC3XXX_MAKE_REG(0, 4, 92)
+#define AIC3262_GPO1_OUT_CNTL		AIC3XXX_MAKE_REG(0, 4, 96)
+#define AIC3262_GPO1_D4_D1		(0b00011110)
+#define AIC3262_DMIC_INPUT_CONTROL	AIC3XXX_MAKE_REG(0, 4, 101)
+#define AIC3262_DMIC_CONFIGURE_MASK	(0b00011111)
+#define AIC3262_DMIC_CONFIGURE_SHIFT	(0)
+#define AIC3262_DMIC_GPI2_LEFT_GPI2_RIGHT	(1)
+#define AIC3262_MINIDSP_DATA_PORT_CNTL	AIC3XXX_MAKE_REG(0, 4, 118)
+
+#define AIC3262_DAC_ASI_LR_UNMUTE_MASK	0x50
+#define AIC3262_DAC_ASI_LR_UNMUTE	0x50
+#define AIC3262_WCLK_BCLK_MASTER_MASK (0b00100110)
+#define AIC3262_WCLK_MASTER_MASK (0b00100000)
+#define AIC3262_BCLK_MASTER_MASK (0b00000100)
+#define AIC3262_ASI_WCLK_MASTER_MASK (0b10000000)
+#define AIC3262_ASI_BCLK_MASTER_MASK (0b10000000)
+#define AIC3262_BCLK_OFFSET_MASK (0b11111111)
+#define AIC3262_ASI_INTERFACE_MASK (0b11100000)
+#define AIC3262_WCLK_OUT_MASK (0b00100000)
+#define AIC3262_BCLK_OUT_MASK (0b00000100)
+#define AIC3262_BCLK_INV_MASK (0b00000010)
+
+#define AIC3262_ADC_ADAPTIVE_CRAM_REG    AIC3XXX_MAKE_REG(40, 0, 1)
+#define AIC3262_DAC_ADAPTIVE_BANK1_REG   AIC3XXX_MAKE_REG(80, 0, 1)
+#define AIC3262_DAC_ADAPTIVE_BANK2_REG   AIC3XXX_MAKE_REG(82, 0, 1)
+#define AIC3262_ADC_DATAPATH_SETUP      AIC3XXX_MAKE_REG(0, 0, 81)
+#define AIC3262_DAC_DATAPATH_SETUP      AIC3XXX_MAKE_REG(0, 0, 63)
+
+#endif
diff --git a/include/linux/mfd/tlv320aic3xxx-core.h b/include/linux/mfd/tlv320aic3xxx-core.h
new file mode 100644
index 0000000..d2f1d97
--- /dev/null
+++ b/include/linux/mfd/tlv320aic3xxx-core.h
@@ -0,0 +1,153 @@
+/*
+ * MFD driver for aic3262
+ *
+ * Author:      Mukund Navada <navada@ti.com>
+ *              Mehar Bajwa <mehar.bajwa@ti.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ *
+ */
+
+#ifndef __MFD_AIC3XXX_CORE_H__
+#define __MFD_AIC3XXX_CORE_H__
+
+#include <linux/interrupt.h>
+#include <linux/mfd/core.h>
+#include <linux/irqdomain.h>
+
+enum aic3xxx_type {
+	TLV320AIC3262 = 0,
+};
+
+#define AIC3XXX_IRQ_HEADSET_DETECT	0
+#define AIC3XXX_IRQ_BUTTON_PRESS	1
+#define AIC3XXX_IRQ_DAC_DRC		2
+#define AIC3XXX_IRQ_AGC_NOISE		3
+#define AIC3XXX_IRQ_OVER_CURRENT	4
+#define AIC3XXX_IRQ_OVERFLOW_EVENT	5
+#define AIC3XXX_IRQ_SPEAKER_OVER_TEMP	6
+
+
+union aic3xxx_reg_union {
+	struct aic3xxx_reg {
+		u8 offset;
+		u8 page;
+		u8 book;
+		u8 reserved;
+	} aic3xxx_register;
+	unsigned int aic3xxx_register_int;
+};
+
+/****************************             ************************************/
+
+struct aic3xxx_gpio_setup {
+	unsigned int reg;
+	u8 value;
+};
+
+/**
+ * Platform data for aic3xxx family device.
+ *
+ * @audio_mclk1: MCLK1 frequency in Hz
+ * @audio_mclk2: MCLK2 frequency in Hz
+ * @gpio_irq: whether AIC3262 interrupts the host AP on a GPIO pin
+ *		of AP
+ * @gpio_reset: is the codec being reset by a gpio [host] pin,
+ *		if yes provide the number.
+ * @num_gpios: number of gpio pins on this device
+ * @gpio_defaults: all gpio configuration
+ * @naudint_irq: audio interrupt number
+ * @irq_base: base of chained interrupt handler
+ */
+struct aic3xxx_pdata {
+	unsigned int audio_mclk1;
+	unsigned int audio_mclk2;
+	unsigned int gpio_irq;	/* whether AIC3262 interrupts the host AP on */
+				/* a GPIO pin of AP */
+	unsigned int gpio_reset;/* is the codec being reset by a gpio*/
+				/* [host] pin, if yes provide the number. */
+	int num_gpios;
+	/* all gpio configuration */
+	struct aic3xxx_gpio_setup *gpio_defaults;
+	int naudint_irq;	/* audio interrupt */
+	unsigned int irq_base;
+};
+
+struct aic3xxx {
+	struct mutex io_lock;
+	struct mutex irq_lock;
+	enum aic3xxx_type type;
+	struct device *dev;
+	struct regmap *regmap;
+	struct aic3xxx_pdata pdata;
+	void *control_data;
+	unsigned int irq;
+	unsigned int irq_base;
+	struct irq_domain *domain;
+	u8 irq_masks_cur;
+	u8 irq_masks_cache;
+	/* Used over suspend/resume */
+	bool suspended;
+	u8 book_no;
+	u8 page_no;
+};
+
+
+
+static inline int aic3xxx_request_irq(struct aic3xxx *aic3xxx, int irq,
+				      irq_handler_t handler,
+				      unsigned long irqflags, const char *name,
+				      void *data)
+{
+	irq = irq_create_mapping(aic3xxx->domain, irq);
+	if (irq < 0) {
+		dev_err(aic3xxx->dev,
+			"Mapping hardware interrupt failed %d\n", irq);
+		return irq;
+	}
+
+	return request_threaded_irq(irq, NULL, handler,
+				    irqflags, name, data);
+}
+
+static inline int aic3xxx_free_irq(struct aic3xxx *aic3xxx, int irq, void *data)
+{
+	if (!aic3xxx->irq_base)
+		return -EINVAL;
+
+	free_irq(aic3xxx->irq_base + irq, data);
+	return 0;
+}
+
+/* Device I/O API */
+int aic3xxx_reg_read(struct aic3xxx *aic3xxx, unsigned int reg);
+int aic3xxx_reg_write(struct aic3xxx *aic3xxx, unsigned int reg,
+		      unsigned char val);
+int aic3xxx_set_bits(struct aic3xxx *aic3xxx, unsigned int reg,
+		     unsigned char mask, unsigned char val);
+int aic3xxx_bulk_read(struct aic3xxx *aic3xxx, unsigned int reg,
+		      int count, u8 *buf);
+int aic3xxx_bulk_write(struct aic3xxx *aic3xxx, unsigned int reg,
+		       int count, const u8 *buf);
+int aic3xxx_wait_bits(struct aic3xxx *aic3xxx, unsigned int reg,
+		      unsigned char mask, unsigned char val, int delay,
+		      int counter);
+
+int aic3xxx_irq_init(struct aic3xxx *aic3xxx);
+void aic3xxx_irq_exit(struct aic3xxx *aic3xxx);
+int aic3xxx_device_init(struct aic3xxx *aic3xxx);
+void aic3xxx_device_exit(struct aic3xxx *aic3xxx);
+
+#endif /* End of __MFD_AIC3XXX_CORE_H__ */
diff --git a/include/linux/mfd/tlv320aic3xxx-registers.h b/include/linux/mfd/tlv320aic3xxx-registers.h
new file mode 100644
index 0000000..6fbcd7e
--- /dev/null
+++ b/include/linux/mfd/tlv320aic3xxx-registers.h
@@ -0,0 +1,75 @@
+
+/*
+ *tlv320aic3xxx-registers: Register bits for AIC3XXX codecs
+ *
+ *
+ * Author:      Mukund Navada <navada@ti.com>
+ *              Mehar Bajwa <mehar.bajwa@ti.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ *
+ */
+
+#ifndef __MFD_AIC3XXX_REGISTERS_H__
+#define __MFD_AIC3XXX_REGISTERS_H__
+#define AIC3XXX_MAKE_REG(book, page, offset)	(unsigned int)((book << 16) | \
+							(page << 8) | \
+							offset)
+
+#define AIC3XXX_RESET		AIC3XXX_MAKE_REG(0, 0, 1)
+#define AIC3XXX_REV_PG_ID		AIC3XXX_MAKE_REG(0, 0, 2)
+#define AIC3XXX_REV_M		(0b01110000)
+#define AIC3XXX_REV_S		4
+#define AIC3XXX_PG_M			(0b00000111)
+#define AIC3XXX_PG_S		0
+
+#define AIC3XXX_INT_STICKY_FLAG1		AIC3XXX_MAKE_REG(0, 0, 42)
+#define AIC3XXX_LEFT_DAC_OVERFLOW_INT	0x80
+#define AIC3XXX_RIGHT_DAC_OVERFLOW_INT	0x40
+#define AIC3XXX_MINIDSP_D_BARREL_SHIFT_OVERFLOW_INT	0x20
+#define AIC3XXX_LEFT_ADC_OVERFLOW_INT	0x08
+#define AIC3XXX_RIGHT_ADC_OVERFLOW_INT	0x04
+#define AIC3XXX_MINIDSP_A_BARREL_SHIFT_OVERFLOW_INT	0x02
+#define AIC3XXX_INT_STICKY_FLAG2		AIC3XXX_MAKE_REG(0, 0, 44)
+#define AIC3XXX_LEFT_OUTPUT_DRIVER_OVERCURRENT_INT	0x80
+#define AIC3XXX_RIGHT_OUTPUT_DRIVER_OVERCURRENT_INT	0x40
+#define AIC3XXX_BUTTON_PRESS_INT			0x20
+#define AIC3XXX_HEADSET_PLUG_UNPLUG_INT			0x10
+#define AIC3XXX_LEFT_DRC_THRES_INT			0x08
+#define AIC3XXX_RIGHT_DRC_THRES_INT			0x04
+#define AIC3XXX_MINIDSP_D_STD_INT			0x02
+#define AIC3XXX_MINIDSP_D_AUX_INT			0x01
+#define AIC3XXX_INT_STICKY_FLAG3		AIC3XXX_MAKE_REG(0, 0, 45)
+#define AIC3XXX_SPK_OVER_CURRENT_INT			0x80
+#define AIC3XXX_LEFT_AGC_NOISE_INT			0x40
+#define AIC3XXX_RIGHT_AGC_NOISE_INT			0x20
+#define AIC3XXX_MINIDSP_A_STD_INT			0x10
+#define AIC3XXX_MINIDSP_A_AUX_INT			0x08
+#define AIC3XXX_LEFT_ADC_DC_DATA_AVAILABLE_INT		0x04
+#define AIC3XXX_RIGHT_ADC_DC_DATA_AVAILABLE_INT		0x02
+#define AIC3XXX_CP_SHORT_CIRCUIT_INT			0x01
+#define AIC3XXX_INT1_CNTL		AIC3XXX_MAKE_REG(0, 0, 48)
+#define AIC3XXX_HEADSET_IN_M		0x80
+#define AIC3XXX_BUTTON_PRESS_M	0x40
+#define AIC3XXX_DAC_DRC_THRES_M	0x20
+#define AIC3XXX_AGC_NOISE_M		0x10
+#define AIC3XXX_OVER_CURRENT_M	0x08
+#define AIC3XXX_OVERFLOW_M		0x04
+#define AIC3XXX_SPK_OVERCURRENT_M	0x02
+#define AIC3XXX_CP_SHORT_CIRCUIT_M	0x02
+#define AIC3XXX_INT2_CNTL		AIC3XXX_MAKE_REG(0, 0, 49)
+#define AIC3XXX_INT_FMT			AIC3XXX_MAKE_REG(0, 0, 51)
+#define AIC3XXX_DEVICE_ID		AIC3XXX_MAKE_REG(0, 0, 125)
+#endif
-- 
1.7.0.4


^ permalink raw reply related	[flat|nested] 4+ messages in thread

* Re: [RESEND: PATCH]: Mfd-Initial-support-for-Texas-Instruments-AIC-family-Codecs
  2013-02-17  5:15 ` [RESEND: PATCH]: Mfd-Initial-support-for-Texas-Instruments-AIC-family-Codecs BAJWA, MEHAR
@ 2013-04-05 15:51   ` Samuel Ortiz
  2013-04-15 12:11     ` [PATCH: v2]: Mfd-Initial-support-for-Texas-Instruments-AIC-family-Codecs x0172184
  0 siblings, 1 reply; 4+ messages in thread
From: Samuel Ortiz @ 2013-04-05 15:51 UTC (permalink / raw)
  To: BAJWA, MEHAR
  Cc: linux-kernel@vger.kernel.org, Navada Kanyana, Mukund,
	liam.r.girdwood@intel.com

Hi Mehar,

On Sun, Feb 17, 2013 at 05:15:38AM +0000, BAJWA, MEHAR wrote:
> Hi Samuel,
>     I am re-sending the MFD support for AIC family of Audio CODECs
> from Texas Instruments. The patch was tested on mfd-for-linus-3.8-1.
> I would like to request that this be considered for inclusion in the kernel
> at the next available window. Could you please let me know if you have
> any comments/recommendations for this patch. We would like to work on it
> and get it up-streamed.
The patch looks good to me, but I'd like it to be splitted in several pieces.
You can probably place the core, the irq, the spi and the i2c pieces in their
own patches.

Cheers,
Samuel.

-- 
Intel Open Source Technology Centre
http://oss.intel.com/

^ permalink raw reply	[flat|nested] 4+ messages in thread

* Re: [PATCH: v2]: Mfd-Initial-support-for-Texas-Instruments-AIC-family-Codecs
  2013-04-05 15:51   ` Samuel Ortiz
@ 2013-04-15 12:11     ` x0172184
  2013-04-15 13:25       ` Samuel Ortiz
  0 siblings, 1 reply; 4+ messages in thread
From: x0172184 @ 2013-04-15 12:11 UTC (permalink / raw)
  To: Samuel Ortiz
  Cc: BAJWA, MEHAR, linux-kernel@vger.kernel.org,
	Navada Kanyana, Mukund, liam.r.girdwood@intel.com

[-- Attachment #1: Type: text/plain, Size: 869 bytes --]

On Friday 05 April 2013 09:21 PM, Samuel Ortiz wrote:
> Hi Mehar,
>
> On Sun, Feb 17, 2013 at 05:15:38AM +0000, BAJWA, MEHAR wrote:
>> Hi Samuel,
>>      I am re-sending the MFD support for AIC family of Audio CODECs
>> from Texas Instruments. The patch was tested on mfd-for-linus-3.8-1.
>> I would like to request that this be considered for inclusion in the kernel
>> at the next available window. Could you please let me know if you have
>> any comments/recommendations for this patch. We would like to work on it
>> and get it up-streamed.
> The patch looks good to me, but I'd like it to be splitted in several pieces.
> You can probably place the core, the irq, the spi and the i2c pieces in their
> own patches.
>
> Cheers,
> Samuel.
>
Hi Samuel,
     Thanks for your comments. As suggested by you, I am sending
individual patches as version 2.

Regards,
Mehar

[-- Attachment #2: 0001-mfd-Initial-support-for-Texas-Instruments-AIC-family.patch --]
[-- Type: text/x-patch, Size: 18444 bytes --]

>From 767b98b6a6ab08b2c0e22c4d0b16a17d1eeadf5d Mon Sep 17 00:00:00 2001
From: Mehar Bajwa <mehar.bajwa@ti.com>
Date: Fri, 12 Apr 2013 15:24:40 +0530
Subject: [PATCH 1/4] mfd: Initial support for Texas Instruments AIC family of CODECs

Initial support for Texas Instruments's AIC CODEC device.

The AIC platform provides common interface to series of low power audio CODECS.
This MFD core driver instantiates subdevices that help in supporting range
of features provided by AIC family of devices

Signed-off-by: Mehar Bajwa <mehar.bajwa@ti.com>
---
 drivers/mfd/Kconfig                     |   13 +
 drivers/mfd/Makefile                    |    1 +
 drivers/mfd/tlv320aic-core.c            |  462 +++++++++++++++++++++++++++++++
 include/linux/mfd/tlv320aic-core.h      |  112 ++++++++
 include/linux/mfd/tlv320aic-registers.h |   32 +++
 5 files changed, 620 insertions(+), 0 deletions(-)
 create mode 100644 drivers/mfd/tlv320aic-core.c
 create mode 100644 include/linux/mfd/tlv320aic-core.h
 create mode 100644 include/linux/mfd/tlv320aic-registers.h

diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig
index 671f5b1..629d374 100644
--- a/drivers/mfd/Kconfig
+++ b/drivers/mfd/Kconfig
@@ -45,6 +45,19 @@ config MFD_88PM805
 	  components like codec device, headset/Mic device under the
 	  corresponding menus.
 
+config MFD_AIC
+	bool "Support for Texas Instruments TLV320AIC platform"
+	select REGMAP
+	select MFD_CORE
+	help
+	  Say yes here if you want support for Texas Instruments AIC audio
+	  codec.
+	  You have to select individual I2C or SPI depending on
+	  AIC interfacing with platform. To enable IRQ handling
+	  facilities select IRQ component under corresponding menus.
+	  you have to select individual components like codec device
+	  to use AIC features.
+
 config MFD_SM501
 	tristate "Support for Silicon Motion SM501"
 	 ---help---
diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile
index b90409c..b975c94 100644
--- a/drivers/mfd/Makefile
+++ b/drivers/mfd/Makefile
@@ -6,6 +6,7 @@
 obj-$(CONFIG_MFD_88PM860X)	+= 88pm860x.o
 obj-$(CONFIG_MFD_88PM800)	+= 88pm800.o 88pm80x.o
 obj-$(CONFIG_MFD_88PM805)	+= 88pm805.o 88pm80x.o
+obj-$(CONFIG_MFD_AIC)		+= tlv320aic-core.o
 obj-$(CONFIG_MFD_SM501)		+= sm501.o
 obj-$(CONFIG_MFD_ASIC3)		+= asic3.o tmio_core.o
 
diff --git a/drivers/mfd/tlv320aic-core.c b/drivers/mfd/tlv320aic-core.c
new file mode 100644
index 0000000..4b8a424
--- /dev/null
+++ b/drivers/mfd/tlv320aic-core.c
@@ -0,0 +1,462 @@
+/*
+ * tlv320aic-core.c  -- driver for TLV320AIC
+ *
+ * Author:      Mukund Navada <navada@ti.com>
+ *              Mehar Bajwa <mehar.bajwa@ti.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ *
+ */
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/i2c.h>
+#include <linux/err.h>
+#include <linux/delay.h>
+#include <linux/regmap.h>
+#include <linux/mfd/core.h>
+#include <linux/pm_runtime.h>
+#include <linux/regulator/consumer.h>
+#include <linux/regulator/machine.h>
+#include <linux/gpio.h>
+
+#include <linux/mfd/tlv320aic-core.h>
+#include <linux/mfd/tlv320aic-registers.h>
+/**
+ * set_aic_book: change book which we have to write/read to.
+ *
+ * @aic: Device to write/read to.
+ * @book: Book to write/read to.
+ */
+int set_aic_book(struct aic *aic, int book)
+{
+	int ret = 0;
+	u8 page_buf[] = { 0x0, 0x0 };
+	u8 book_buf[] = { 0x7f, 0x0 };
+
+	ret = regmap_write(aic->regmap, page_buf[0], page_buf[1]);
+
+	if (ret < 0)
+		return ret;
+	book_buf[1] = book;
+	ret = regmap_write(aic->regmap, book_buf[0], book_buf[1]);
+
+	if (ret < 0)
+		return ret;
+	aic->book_no = book;
+	aic->page_no = 0;
+
+	return ret;
+}
+
+/**
+ * set_aic_page: change page which we have to write/read to.
+ *
+ * @aic: Device to write/read to.
+ * @page: Book to write/read to.
+ */
+int set_aic_page(struct aic *aic, int page)
+{
+	int ret = 0;
+	u8 page_buf[] = { 0x0, 0x0 };
+
+	page_buf[1] = page;
+	ret = regmap_write(aic->regmap, page_buf[0], page_buf[1]);
+
+	if (ret < 0)
+		return ret;
+	aic->page_no = page;
+	return ret;
+}
+/**
+ * aic_reg_read: Read a single TLV320AIC register.
+ *
+ * @aic: Device to read from.
+ * @reg: Register to read.
+ */
+int aic_reg_read(struct aic *aic, unsigned int reg)
+{
+	unsigned int val;
+	int ret;
+	union aic_reg_union *aic_reg = (union aic_reg_union *) &reg;
+	u8 book, page, offset;
+
+	page = aic_reg->aic_register.page;
+	book = aic_reg->aic_register.book;
+	offset = aic_reg->aic_register.offset;
+
+	mutex_lock(&aic->io_lock);
+	if (aic->book_no != book) {
+		ret = set_aic_book(aic, book);
+		if (ret < 0) {
+			mutex_unlock(&aic->io_lock);
+			return ret;
+		}
+	}
+
+	if (aic->page_no != page) {
+		ret = set_aic_page(aic, page);
+		if (ret < 0) {
+			mutex_unlock(&aic->io_lock);
+			return ret;
+		}
+	}
+	ret = regmap_read(aic->regmap, offset, &val);
+	mutex_unlock(&aic->io_lock);
+
+	if (ret < 0)
+		return ret;
+	else
+		return val;
+}
+EXPORT_SYMBOL_GPL(aic_reg_read);
+
+/**
+ * aic_bulk_read: Read multiple TLV320AIC registers
+ *
+ * @aic: Device to read from
+ * @reg: First register
+ * @count: Number of registers
+ * @buf: Buffer to fill.  The data will be returned big endian.
+ */
+int aic_bulk_read(struct aic *aic, unsigned int reg,
+		      int count, u8 *buf)
+{
+	int ret;
+	union aic_reg_union *aic_reg = (union aic_reg_union *) &reg;
+	u8 book, page, offset;
+
+	page = aic_reg->aic_register.page;
+	book = aic_reg->aic_register.book;
+	offset = aic_reg->aic_register.offset;
+
+	mutex_lock(&aic->io_lock);
+	if (aic->book_no != book) {
+		ret = set_aic_book(aic, book);
+		if (ret < 0) {
+			mutex_unlock(&aic->io_lock);
+			return ret;
+		}
+	}
+
+	if (aic->page_no != page) {
+		ret = set_aic_page(aic, page);
+		if (ret < 0) {
+			mutex_unlock(&aic->io_lock);
+			return ret;
+		}
+	}
+	ret = regmap_bulk_read(aic->regmap, offset, buf, count);
+	mutex_unlock(&aic->io_lock);
+		return ret;
+}
+EXPORT_SYMBOL_GPL(aic_bulk_read);
+
+/**
+ * aic_reg_write: Write a single TLV320AIC register.
+ *
+ * @aic: Device to write to.
+ * @reg: Register to write to.
+ * @val: Value to write.
+ */
+int aic_reg_write(struct aic *aic, unsigned int reg,
+		      unsigned char val)
+{
+	union aic_reg_union *aic_reg = (union aic_reg_union *) &reg;
+	int ret = 0;
+	u8 page, book, offset;
+
+	page = aic_reg->aic_register.page;
+	book = aic_reg->aic_register.book;
+	offset = aic_reg->aic_register.offset;
+
+	mutex_lock(&aic->io_lock);
+	if (book != aic->book_no) {
+		ret = set_aic_book(aic, book);
+		if (ret < 0) {
+			mutex_unlock(&aic->io_lock);
+			return ret;
+		}
+	}
+	if (page != aic->page_no) {
+		ret = set_aic_page(aic, page);
+		if (ret < 0) {
+			mutex_unlock(&aic->io_lock);
+			return ret;
+		}
+	}
+	ret = regmap_write(aic->regmap, offset, val);
+	mutex_unlock(&aic->io_lock);
+	return ret;
+
+}
+EXPORT_SYMBOL_GPL(aic_reg_write);
+
+/**
+ * aic_bulk_write: Write multiple TLV320AIC registers
+ *
+ * @aic: Device to write to
+ * @reg: First register
+ * @count: Number of registers
+ * @buf: Buffer to write from.  Data must be big-endian formatted.
+ */
+int aic_bulk_write(struct aic *aic, unsigned int reg,
+		       int count, const u8 *buf)
+{
+	union aic_reg_union *aic_reg = (union aic_reg_union *) &reg;
+	int ret = 0;
+	u8 page, book, offset;
+
+	page = aic_reg->aic_register.page;
+	book = aic_reg->aic_register.book;
+	offset = aic_reg->aic_register.offset;
+
+	mutex_lock(&aic->io_lock);
+	if (book != aic->book_no) {
+		ret = set_aic_book(aic, book);
+		if (ret < 0) {
+			mutex_unlock(&aic->io_lock);
+			return ret;
+		}
+	}
+	if (page != aic->page_no) {
+		ret = set_aic_page(aic, page);
+		if (ret < 0) {
+			mutex_unlock(&aic->io_lock);
+			return ret;
+		}
+	}
+	ret = regmap_raw_write(aic->regmap, offset, buf, count);
+	mutex_unlock(&aic->io_lock);
+	return ret;
+}
+EXPORT_SYMBOL_GPL(aic_bulk_write);
+
+/**
+ * aic_set_bits: Set the value of a bitfield in a TLV320AIC register
+ *
+ * @aic: Device to write to.
+ * @reg: Register to write to.
+ * @mask: Mask of bits to set.
+ * @val: Value to set (unshifted)
+ */
+int aic_set_bits(struct aic *aic, unsigned int reg,
+		     unsigned char mask, unsigned char val)
+{
+	union aic_reg_union *aic_reg = (union aic_reg_union *) &reg;
+	int ret = 0;
+	u8 page, book, offset;
+
+	page = aic_reg->aic_register.page;
+	book = aic_reg->aic_register.book;
+	offset = aic_reg->aic_register.offset;
+
+	mutex_lock(&aic->io_lock);
+	if (book != aic->book_no) {
+		ret = set_aic_book(aic, book);
+		if (ret < 0) {
+			mutex_unlock(&aic->io_lock);
+			return ret;
+		}
+	}
+	if (page != aic->page_no) {
+		ret = set_aic_page(aic, page);
+		if (ret < 0) {
+			mutex_unlock(&aic->io_lock);
+			return ret;
+		}
+	}
+	ret = regmap_update_bits(aic->regmap, offset, mask, val);
+	mutex_unlock(&aic->io_lock);
+	return ret;
+
+}
+EXPORT_SYMBOL_GPL(aic_set_bits);
+
+/**
+ * aic_wait_bits: wait for a value of a bitfield in a TLV320AIC register
+ *
+ * @aic: Device to write to.
+ * @reg: Register to write to.
+ * @mask: Mask of bits to set.
+ * @val: Value to set (unshifted)
+ * @sleep: delay value in each iteration in micro seconds
+ * @count: iteration count for timeout
+ */
+int aic_wait_bits(struct aic *aic, unsigned int reg,
+		      unsigned char mask, unsigned char val, int sleep,
+		      int counter)
+{
+	int status;
+	int timeout = sleep * counter;
+
+	status = aic_reg_read(aic, reg);
+	while (((status & mask) != val) && counter) {
+		usleep_range(sleep, sleep + 500);
+		status = aic_reg_read(aic, reg);
+		counter--;
+	};
+	if (!counter)
+		dev_err(aic->dev,
+			"wait_bits timedout (%d millisecs). lastval 0x%x\n",
+			timeout, status);
+	return counter;
+}
+EXPORT_SYMBOL_GPL(aic_wait_bits);
+
+static struct mfd_cell aic3262_devs[] = {
+	{
+	.name = "tlv320aic3262-codec",
+	},
+	{
+	.name = "tlv320aic3262-gpio",
+	},
+	{
+	.name = "tlv320aic3262-extcon",
+	}
+};
+
+
+/**
+ * Instantiate the generic non-control parts of the device.
+ */
+int aic_device_init(struct aic *aic)
+{
+	const char *devname;
+	int ret, irq_no;
+	u8 reset = 1;
+
+	mutex_init(&aic->io_lock);
+	dev_set_drvdata(aic->dev, aic);
+
+	if (dev_get_platdata(aic->dev))
+		memcpy(&aic->pdata, dev_get_platdata(aic->dev),
+			sizeof(aic->pdata));
+
+	/* GPIO reset for TLV320AIC codec */
+	if (gpio_is_valid(aic->pdata.gpio_reset)) {
+		ret = gpio_request_one(aic->pdata.gpio_reset,
+					GPIOF_DIR_OUT | GPIOF_INIT_LOW,
+					"aic-reset-pin");
+		if (ret != 0) {
+			dev_err(aic->dev, "not able to acquire gpio\n");
+			goto err_return;
+		}
+	}
+
+	/* run the codec through software reset */
+	ret = aic_reg_write(aic, AIC_RESET, reset);
+	if (ret < 0) {
+		dev_err(aic->dev, "Could not write to AIC register\n");
+		goto err_return;
+	}
+
+	usleep_range(10000, 10500);
+
+	ret = aic_reg_read(aic, AIC_DEVICE_ID);
+	if (ret < 0) {
+		dev_err(aic->dev, "Failed to read ID register\n");
+		goto err_return;
+	}
+
+	switch (ret) {
+	case 3:
+		devname = "TLV320AIC3262";
+		if (aic->type != TLV320AIC3262)
+			dev_warn(aic->dev, "Device registered as type %d\n",
+				 aic->type);
+		aic->type = TLV320AIC3262;
+		break;
+	default:
+		dev_err(aic->dev, "Device is not a TLV320AIC");
+		ret = -EINVAL;
+		goto err_return;
+	}
+
+	dev_info(aic->dev, "%s\n", devname);
+
+	/*If naudint is gpio convert it to irq number */
+	if (aic->pdata.gpio_irq == 1) {
+		aic->irq = gpio_to_irq(aic->pdata.naudint_irq);
+		gpio_request(aic->pdata.naudint_irq, "aic-gpio-irq");
+		gpio_direction_input(aic->pdata.naudint_irq);
+	} else {
+		aic->irq = aic->pdata.naudint_irq;
+	}
+
+	for (irq_no = 0; irq_no < aic->pdata.num_gpios; irq_no++) {
+		aic_reg_write(aic, aic->pdata.gpio_defaults[irq_no].reg,
+			aic->pdata.gpio_defaults[irq_no].value);
+	}
+#ifdef CONFIG_MFD_AIC_IRQ
+	if (aic->irq) {
+		ret = aic_irq_init(aic);
+		if (ret < 0)
+			goto err_irq;
+	}
+#endif
+	switch (aic->type) {
+	case TLV320AIC3262:
+		ret = mfd_add_devices(aic->dev, -1, aic3262_devs,
+				ARRAY_SIZE(aic3262_devs), NULL,
+				0, aic->domain);
+		break;
+	default:
+		dev_err(aic->dev, "unable to recognize codec\n");
+		break;
+	}
+	if (ret != 0) {
+		dev_err(aic->dev, "Failed to add children: %d\n", ret);
+		goto err_mfd;
+	}
+	dev_info(aic->dev, "aic_device_init added mfd devices\n");
+
+	return 0;
+
+err_mfd:
+#ifdef CONFIG_MFD_AIC_IRQ
+	aic_irq_exit(aic);
+err_irq:
+#endif
+	if (aic->pdata.gpio_irq)
+		gpio_free(aic->pdata.naudint_irq);
+err_return:
+
+	if (aic->pdata.gpio_reset)
+		gpio_free(aic->pdata.gpio_reset);
+
+	return ret;
+}
+EXPORT_SYMBOL_GPL(aic_device_init);
+
+void aic_device_exit(struct aic *aic)
+{
+
+	mfd_remove_devices(aic->dev);
+#ifdef CONFIG_MFD_AIC_IRQ
+	aic_irq_exit(aic);
+#endif
+	if (aic->pdata.gpio_irq)
+		gpio_free(aic->pdata.naudint_irq);
+	if (aic->pdata.gpio_reset)
+		gpio_free(aic->pdata.gpio_reset);
+
+}
+EXPORT_SYMBOL_GPL(aic_device_exit);
+
+MODULE_AUTHOR("Mukund Navada <navada@ti.comm>");
+MODULE_AUTHOR("Mehar Bajwa <mehar.bajwa@ti.com>");
+MODULE_DESCRIPTION("Core support for the TLV320AIC audio CODEC");
+MODULE_LICENSE("GPL");
diff --git a/include/linux/mfd/tlv320aic-core.h b/include/linux/mfd/tlv320aic-core.h
new file mode 100644
index 0000000..60d7146
--- /dev/null
+++ b/include/linux/mfd/tlv320aic-core.h
@@ -0,0 +1,112 @@
+/*
+ * MFD driver for AIC family
+ *
+ * Author:      Mukund Navada <navada@ti.com>
+ *              Mehar Bajwa <mehar.bajwa@ti.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ *
+ */
+
+#ifndef __MFD_AIC_CORE_H__
+#define __MFD_AIC_CORE_H__
+
+#include <linux/mfd/core.h>
+
+enum aic_type {
+	TLV320AIC3262 = 0,
+};
+
+union aic_reg_union {
+	struct aic_reg {
+		u8 offset;
+		u8 page;
+		u8 book;
+		u8 reserved;
+	} aic_register;
+	unsigned int aic_register_int;
+};
+
+/****************************             ************************************/
+
+struct aic_gpio_setup {
+	unsigned int reg;
+	u8 value;
+};
+
+/**
+ * Platform data for aic family device.
+ *
+ * @audio_mclk1: MCLK1 frequency in Hz
+ * @audio_mclk2: MCLK2 frequency in Hz
+ * @gpio_irq: whether AIC interrupts the host AP on a GPIO pin
+ *		of AP
+ * @gpio_reset: is the codec being reset by a gpio [host] pin,
+ *		if yes provide the number.
+ * @num_gpios: number of gpio pins on this device
+ * @gpio_defaults: all gpio configuration
+ * @naudint_irq: audio interrupt number
+ * @irq_base: base of chained interrupt handler
+ */
+struct aic_pdata {
+	unsigned int audio_mclk1;
+	unsigned int audio_mclk2;
+	unsigned int gpio_irq;	/* whether AIC interrupts the host AP on */
+				/* a GPIO pin of AP */
+	unsigned int gpio_reset;/* is the codec being reset by a gpio*/
+				/* [host] pin, if yes provide the number. */
+	int num_gpios;
+	/* all gpio configuration */
+	struct aic_gpio_setup *gpio_defaults;
+	int naudint_irq;	/* audio interrupt */
+	unsigned int irq_base;
+};
+
+struct aic {
+	struct mutex io_lock;
+	struct mutex irq_lock;
+	enum aic_type type;
+	struct device *dev;
+	struct regmap *regmap;
+	struct aic_pdata pdata;
+	void *control_data;
+	unsigned int irq;
+	unsigned int irq_base;
+	struct irq_domain *domain;
+	u8 irq_masks_cur;
+	u8 irq_masks_cache;
+	/* Used over suspend/resume */
+	bool suspended;
+	u8 book_no;
+	u8 page_no;
+};
+
+/* Device I/O API */
+int aic_reg_read(struct aic *aic, unsigned int reg);
+int aic_reg_write(struct aic *aic, unsigned int reg,
+		      unsigned char val);
+int aic_set_bits(struct aic *aic, unsigned int reg,
+		     unsigned char mask, unsigned char val);
+int aic_bulk_read(struct aic *aic, unsigned int reg,
+		      int count, u8 *buf);
+int aic_bulk_write(struct aic *aic, unsigned int reg,
+		       int count, const u8 *buf);
+int aic_wait_bits(struct aic *aic, unsigned int reg,
+		      unsigned char mask, unsigned char val, int delay,
+		      int counter);
+int aic_device_init(struct aic *aic);
+void aic_device_exit(struct aic *aic);
+
+#endif /* End of __MFD_AIC_CORE_H__ */
diff --git a/include/linux/mfd/tlv320aic-registers.h b/include/linux/mfd/tlv320aic-registers.h
new file mode 100644
index 0000000..8b56532
--- /dev/null
+++ b/include/linux/mfd/tlv320aic-registers.h
@@ -0,0 +1,32 @@
+/*
+ * tlv320aic-registers: Register bits for AIC codecs
+ *
+ *
+ * Author:      Mukund Navada <navada@ti.com>
+ *              Mehar Bajwa <mehar.bajwa@ti.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ *
+ */
+
+#ifndef __MFD_AIC_REGISTERS_H__
+#define __MFD_AIC_REGISTERS_H__
+#define AIC_MAKE_REG(book, page, offset)	(unsigned int)((book << 16) | \
+								(page << 8) | \
+								offset)
+
+#define AIC_RESET			AIC_MAKE_REG(0, 0, 1)
+#define AIC_DEVICE_ID			AIC_MAKE_REG(0, 0, 125)
+#endif
-- 
1.7.0.4


[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #3: 0002-mfd-Interrupt-handling-support-for-AIC-family.patch --]
[-- Type: text/x-patch; name="0002-mfd-Interrupt-handling-support-for-AIC-family.patch", Size: 13062 bytes --]

>From 64df2769d1a817d2c257a6a72fad820a0116abb0 Mon Sep 17 00:00:00 2001
From: Mehar Bajwa <mehar.bajwa@ti.com>
Date: Fri, 12 Apr 2013 15:38:45 +0530
Subject: [PATCH 2/4] mfd: Interrupt handling support for AIC family

This provides Interrupt handling features for common interface
to series of low power AIC audio CODECS.

Signed-off-by: Mehar Bajwa <mehar.bajwa@ti.com>
---
 drivers/mfd/Kconfig                     |   13 ++
 drivers/mfd/Makefile                    |    1 +
 drivers/mfd/tlv320aic-irq.c             |  234 +++++++++++++++++++++++++++++++
 include/linux/mfd/tlv320aic-core.h      |   49 ++++++-
 include/linux/mfd/tlv320aic-registers.h |   57 ++++++++
 5 files changed, 348 insertions(+), 6 deletions(-)
 create mode 100644 drivers/mfd/tlv320aic-irq.c

diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig
index 629d374..3019897 100644
--- a/drivers/mfd/Kconfig
+++ b/drivers/mfd/Kconfig
@@ -58,6 +58,19 @@ config MFD_AIC
 	  you have to select individual components like codec device
 	  to use AIC features.
 
+menu "AIC Interface Drivers"
+	depends on MFD_AIC
+
+config MFD_AIC_IRQ
+	bool "Support of IRQ for AIC"
+	depends on MFD_AIC
+	help
+	  Say yes here if you want support of IRQ for Texas Instruments
+	  AIC codec family.
+	  You have to select individual components like codec device
+	  under the corresponding menus.
+endmenu
+
 config MFD_SM501
 	tristate "Support for Silicon Motion SM501"
 	 ---help---
diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile
index b975c94..3b39454 100644
--- a/drivers/mfd/Makefile
+++ b/drivers/mfd/Makefile
@@ -7,6 +7,7 @@ obj-$(CONFIG_MFD_88PM860X)	+= 88pm860x.o
 obj-$(CONFIG_MFD_88PM800)	+= 88pm800.o 88pm80x.o
 obj-$(CONFIG_MFD_88PM805)	+= 88pm805.o 88pm80x.o
 obj-$(CONFIG_MFD_AIC)		+= tlv320aic-core.o
+obj-$(CONFIG_MFD_AIC_IRQ)	+= tlv320aic-irq.o
 obj-$(CONFIG_MFD_SM501)		+= sm501.o
 obj-$(CONFIG_MFD_ASIC3)		+= asic3.o tmio_core.o
 
diff --git a/drivers/mfd/tlv320aic-irq.c b/drivers/mfd/tlv320aic-irq.c
new file mode 100644
index 0000000..e299495
--- /dev/null
+++ b/drivers/mfd/tlv320aic-irq.c
@@ -0,0 +1,234 @@
+/*
+ * tlv320aic-irq.c  --  Interrupt controller support for
+ *			 TI TLV320AIC family
+ *
+ * Author:      Mukund Navada <navada@ti.com>
+ *              Mehar Bajwa <mehar.bajwa@ti.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/i2c.h>
+#include <linux/irq.h>
+#include <linux/mfd/core.h>
+#include <linux/interrupt.h>
+#include <linux/irqdomain.h>
+
+#include <linux/mfd/tlv320aic-core.h>
+#include <linux/mfd/tlv320aic-registers.h>
+
+#include <linux/delay.h>
+
+struct aic_irq_data {
+	int mask;
+	int status;
+};
+
+static struct aic_irq_data aic_irqs[] = {
+	{
+	 .mask = AIC_HEADSET_IN_M,
+	 .status = AIC_HEADSET_PLUG_UNPLUG_INT,
+	 },
+	{
+	 .mask = AIC_BUTTON_PRESS_M,
+	 .status = AIC_BUTTON_PRESS_INT,
+	 },
+	{
+	 .mask = AIC_DAC_DRC_THRES_M,
+	 .status = AIC_LEFT_DRC_THRES_INT | AIC_RIGHT_DRC_THRES_INT,
+	 },
+	{
+	 .mask = AIC_AGC_NOISE_M,
+	 .status = AIC_LEFT_AGC_NOISE_INT | AIC_RIGHT_AGC_NOISE_INT,
+	 },
+	{
+	 .mask = AIC_OVER_CURRENT_M,
+	 .status = AIC_LEFT_OUTPUT_DRIVER_OVERCURRENT_INT
+	 | AIC_RIGHT_OUTPUT_DRIVER_OVERCURRENT_INT,
+	 },
+	{
+	 .mask = AIC_OVERFLOW_M,
+	 .status =
+	 AIC_LEFT_DAC_OVERFLOW_INT | AIC_RIGHT_DAC_OVERFLOW_INT |
+	 AIC_MINIDSP_D_BARREL_SHIFT_OVERFLOW_INT |
+	 AIC_LEFT_ADC_OVERFLOW_INT | AIC_RIGHT_ADC_OVERFLOW_INT |
+	 AIC_MINIDSP_D_BARREL_SHIFT_OVERFLOW_INT,
+	 },
+	{
+	 .mask = AIC_SPK_OVERCURRENT_M,
+	 .status = AIC_SPK_OVER_CURRENT_INT,
+	 },
+
+};
+
+static void aic_irq_lock(struct irq_data *data)
+{
+	struct aic *aic = irq_data_get_irq_chip_data(data);
+
+	mutex_lock(&aic->irq_lock);
+}
+
+static void aic_irq_sync_unlock(struct irq_data *data)
+{
+	struct aic *aic = irq_data_get_irq_chip_data(data);
+
+	/* write back to hardware any change in irq mask */
+	if (aic->irq_masks_cur != aic->irq_masks_cache) {
+		aic->irq_masks_cache = aic->irq_masks_cur;
+		aic_reg_write(aic, AIC_INT1_CNTL,
+				  aic->irq_masks_cur);
+	}
+
+	mutex_unlock(&aic->irq_lock);
+}
+
+
+static void aic_irq_enable(struct irq_data *data)
+{
+	struct aic *aic = irq_data_get_irq_chip_data(data);
+	struct aic_irq_data *irq_data = &aic_irqs[data->hwirq];
+	aic->irq_masks_cur |= irq_data->mask;
+}
+
+static void aic_irq_disable(struct irq_data *data)
+{
+	struct aic *aic = irq_data_get_irq_chip_data(data);
+	struct aic_irq_data *irq_data = &aic_irqs[data->hwirq];
+
+	aic->irq_masks_cur &= ~irq_data->mask;
+}
+
+static struct irq_chip aic_irq_chip = {
+	.name = "tlv320aic",
+	.irq_bus_lock = aic_irq_lock,
+	.irq_bus_sync_unlock = aic_irq_sync_unlock,
+	.irq_enable = aic_irq_enable,
+	.irq_disable = aic_irq_disable
+};
+
+static irqreturn_t aic_irq_thread(int irq, void *data)
+{
+	struct aic *aic = data;
+	u8 status[4];
+
+	/* Reading sticky bit registers acknowledges
+		the interrupt to the device */
+	aic_bulk_read(aic, AIC_INT_STICKY_FLAG1, 4, status);
+
+	/* report  */
+	if (status[2] & aic_irqs[AIC_IRQ_HEADSET_DETECT].status)
+		handle_nested_irq(aic->irq_base);
+	if (status[2] & aic_irqs[AIC_IRQ_BUTTON_PRESS].status)
+		handle_nested_irq(aic->irq_base + 1);
+	if (status[2] & aic_irqs[AIC_IRQ_DAC_DRC].status)
+		handle_nested_irq(aic->irq_base + 2);
+	if (status[3] & aic_irqs[AIC_IRQ_AGC_NOISE].status)
+		handle_nested_irq(aic->irq_base + 3);
+	if (status[2] & aic_irqs[AIC_IRQ_OVER_CURRENT].status)
+		handle_nested_irq(aic->irq_base + 4);
+	if (status[0] & aic_irqs[AIC_IRQ_OVERFLOW_EVENT].status)
+		handle_nested_irq(aic->irq_base + 5);
+	if (status[3] & aic_irqs[AIC_IRQ_SPEAKER_OVER_TEMP].status)
+		handle_nested_irq(aic->irq_base + 6);
+
+	return IRQ_HANDLED;
+}
+
+static int aic_irq_map(struct irq_domain *h, unsigned int virq,
+				irq_hw_number_t hw)
+{
+	struct aic *aic = h->host_data;
+
+	irq_set_chip_data(virq, aic);
+	irq_set_chip_and_handler(virq, &aic_irq_chip, handle_edge_irq);
+	irq_set_nested_thread(virq, 1);
+
+	/* ARM needs us to explicitly flag the IRQ as valid
+	 * and will set them noprobe when we do so. */
+#ifdef CONFIG_ARM
+	set_irq_flags(virq, IRQF_VALID);
+#else
+	irq_set_noprobe(virq);
+#endif
+
+	return 0;
+}
+
+static const struct irq_domain_ops aic_domain_ops = {
+	.map    = aic_irq_map,
+	.xlate  = irq_domain_xlate_twocell,
+};
+
+int aic_irq_init(struct aic *aic)
+{
+	int ret;
+
+	mutex_init(&aic->irq_lock);
+
+	/* mask the individual interrupt sources */
+	aic->irq_masks_cur = 0x0;
+	aic->irq_masks_cache = 0x0;
+	aic_reg_write(aic, AIC_INT1_CNTL, 0x0);
+
+	if (!aic->irq) {
+		dev_warn(aic->dev, "no interrupt specified\n");
+		aic->irq_base = 0;
+		return 0;
+	}
+	if (aic->irq_base) {
+		aic->domain = irq_domain_add_legacy(aic->dev->of_node,
+					ARRAY_SIZE(aic_irqs),
+					aic->irq_base, 0,
+					&aic_domain_ops, aic);
+	} else {
+		aic->domain = irq_domain_add_linear(aic->dev->of_node,
+					ARRAY_SIZE(aic_irqs),
+					&aic_domain_ops, aic);
+		/* initiallizing irq_base from irq_domain*/
+	}
+	if (!aic->domain) {
+		dev_err(aic->dev, "Failed to create IRQ domain\n");
+		return -ENOMEM;
+	}
+
+	aic->irq_base = irq_create_mapping(aic->domain, 0);
+
+	ret = request_threaded_irq(aic->irq, NULL, aic_irq_thread,
+				   IRQF_ONESHOT,
+				   "tlv320aic", aic);
+	if (ret < 0) {
+		dev_err(aic->dev, "failed to request IRQ %d: %d\n",
+			aic->irq, ret);
+		return ret;
+	}
+	irq_set_irq_type(aic->irq, IRQF_TRIGGER_RISING);
+
+	return 0;
+}
+EXPORT_SYMBOL(aic_irq_init);
+
+void aic_irq_exit(struct aic *aic)
+{
+	if (aic->irq)
+		free_irq(aic->irq, aic);
+}
+EXPORT_SYMBOL(aic_irq_exit);
+MODULE_AUTHOR("Mukund navada <navada@ti.com>");
+MODULE_AUTHOR("Mehar Bajwa <mehar.bajwa@ti.com>");
+MODULE_DESCRIPTION("Interrupt controller support for TI TLV320AIC family");
+MODULE_LICENSE("GPL");
diff --git a/include/linux/mfd/tlv320aic-core.h b/include/linux/mfd/tlv320aic-core.h
index 60d7146..66a46fb 100644
--- a/include/linux/mfd/tlv320aic-core.h
+++ b/include/linux/mfd/tlv320aic-core.h
@@ -23,12 +23,22 @@
 #ifndef __MFD_AIC_CORE_H__
 #define __MFD_AIC_CORE_H__
 
+#include <linux/interrupt.h>
 #include <linux/mfd/core.h>
+#include <linux/irqdomain.h>
 
 enum aic_type {
 	TLV320AIC3262 = 0,
 };
 
+#define AIC_IRQ_HEADSET_DETECT		0
+#define AIC_IRQ_BUTTON_PRESS		1
+#define AIC_IRQ_DAC_DRC			2
+#define AIC_IRQ_AGC_NOISE		3
+#define AIC_IRQ_OVER_CURRENT		4
+#define AIC_IRQ_OVERFLOW_EVENT		5
+#define AIC_IRQ_SPEAKER_OVER_TEMP	6
+
 union aic_reg_union {
 	struct aic_reg {
 		u8 offset;
@@ -93,19 +103,46 @@ struct aic {
 	u8 page_no;
 };
 
+static inline int aic_request_irq(struct aic *aic, int irq,
+				      irq_handler_t handler,
+				      unsigned long irqflags, const char *name,
+				      void *data)
+{
+	irq = irq_create_mapping(aic->domain, irq);
+	if (irq < 0) {
+		dev_err(aic->dev,
+			"Mapping hardware interrupt failed %d\n", irq);
+		return irq;
+	}
+
+	return request_threaded_irq(irq, NULL, handler,
+				    irqflags, name, data);
+}
+
+static inline int aic_free_irq(struct aic *aic, int irq, void *data)
+{
+	if (!aic->irq_base)
+		return -EINVAL;
+
+	free_irq(aic->irq_base + irq, data);
+	return 0;
+}
+
 /* Device I/O API */
 int aic_reg_read(struct aic *aic, unsigned int reg);
 int aic_reg_write(struct aic *aic, unsigned int reg,
-		      unsigned char val);
+			unsigned char val);
 int aic_set_bits(struct aic *aic, unsigned int reg,
-		     unsigned char mask, unsigned char val);
+			unsigned char mask, unsigned char val);
 int aic_bulk_read(struct aic *aic, unsigned int reg,
-		      int count, u8 *buf);
+			int count, u8 *buf);
 int aic_bulk_write(struct aic *aic, unsigned int reg,
-		       int count, const u8 *buf);
+			int count, const u8 *buf);
 int aic_wait_bits(struct aic *aic, unsigned int reg,
-		      unsigned char mask, unsigned char val, int delay,
-		      int counter);
+			unsigned char mask, unsigned char val, int delay,
+			int counter);
+int aic_irq_init(struct aic *aic);
+void aic_irq_exit(struct aic *aic);
 int aic_device_init(struct aic *aic);
 void aic_device_exit(struct aic *aic);
 
diff --git a/include/linux/mfd/tlv320aic-registers.h b/include/linux/mfd/tlv320aic-registers.h
index 8b56532..c940fae 100644
--- a/include/linux/mfd/tlv320aic-registers.h
+++ b/include/linux/mfd/tlv320aic-registers.h
@@ -28,5 +28,62 @@
 								offset)
 
 #define AIC_RESET			AIC_MAKE_REG(0, 0, 1)
+#define AIC_REV_PG_ID			AIC_MAKE_REG(0, 0, 2)
+#define AIC_INT_STICKY_FLAG1		AIC_MAKE_REG(0, 0, 42)
+#define AIC_INT_STICKY_FLAG2		AIC_MAKE_REG(0, 0, 44)
+#define AIC_INT_STICKY_FLAG3		AIC_MAKE_REG(0, 0, 45)
+#define AIC_INT1_CNTL			AIC_MAKE_REG(0, 0, 48)
+#define AIC_INT2_CNTL			AIC_MAKE_REG(0, 0, 49)
+#define AIC_INT_FMT			AIC_MAKE_REG(0, 0, 51)
 #define AIC_DEVICE_ID			AIC_MAKE_REG(0, 0, 125)
+
+/*
+ *  B0_P0_R2 (0x000002) – Revision ID register.
+ */
+#define AIC_REV_M			(0b01110000)
+#define AIC_REV_S			(0b00000100)
+#define AIC_PG_M			(0b00000111)
+#define AIC_PG_S			(0b00000000)
+/*
+ * B0_P0_R42 (0x00002a) – Interrupt Status 1
+ */
+#define AIC_LEFT_DAC_OVERFLOW_INT		0x80
+#define AIC_RIGHT_DAC_OVERFLOW_INT		0x40
+#define AIC_MINIDSP_D_BARREL_SHIFT_OVERFLOW_INT	0x20
+#define AIC_LEFT_ADC_OVERFLOW_INT		0x08
+#define AIC_RIGHT_ADC_OVERFLOW_INT		0x04
+#define AIC_MINIDSP_A_BARREL_SHIFT_OVERFLOW_INT	0x02
+/*
+ * B0_P0_R44 (0x00002c) - Interrupt Status 2
+ */
+#define AIC_LEFT_OUTPUT_DRIVER_OVERCURRENT_INT	0x80
+#define AIC_RIGHT_OUTPUT_DRIVER_OVERCURRENT_INT	0x40
+#define AIC_BUTTON_PRESS_INT			0x20
+#define AIC_HEADSET_PLUG_UNPLUG_INT		0x10
+#define AIC_LEFT_DRC_THRES_INT			0x08
+#define AIC_RIGHT_DRC_THRES_INT			0x04
+#define AIC_MINIDSP_D_STD_INT			0x02
+#define AIC_MINIDSP_D_AUX_INT			0x01
+/*
+ * B0_P0_R45 (0x00002d) - Interrupt Status 3
+ */
+#define AIC_SPK_OVER_CURRENT_INT		0x80
+#define AIC_LEFT_AGC_NOISE_INT			0x40
+#define AIC_RIGHT_AGC_NOISE_INT			0x20
+#define AIC_MINIDSP_A_STD_INT			0x10
+#define AIC_MINIDSP_A_AUX_INT			0x08
+#define AIC_LEFT_ADC_DC_DATA_AVAILABLE_INT	0x04
+#define AIC_RIGHT_ADC_DC_DATA_AVAILABLE_INT	0x02
+#define AIC_CP_SHORT_CIRCUIT_INT		0x01
+/*
+ * B0_P0_R48 (0x000030) - Interrupt Control 1
+ */
+#define AIC_HEADSET_IN_M			0x80
+#define AIC_BUTTON_PRESS_M			0x40
+#define AIC_DAC_DRC_THRES_M			0x20
+#define AIC_AGC_NOISE_M				0x10
+#define AIC_OVER_CURRENT_M			0x08
+#define AIC_OVERFLOW_M				0x04
+#define AIC_SPK_OVERCURRENT_M			0x02
+#define AIC_CP_SHORT_CIRCUIT_M			0x02
 #endif
-- 
1.7.0.4


[-- Attachment #4: 0003-mfd-I2C-interface-with-AIC-platform.patch --]
[-- Type: text/x-patch, Size: 4756 bytes --]

>From cc75a5308791e50ae5cb9bc97168ecf81f0db795 Mon Sep 17 00:00:00 2001
From: Mehar Bajwa <mehar.bajwa@ti.com>
Date: Fri, 12 Apr 2013 15:42:15 +0530
Subject: [PATCH 3/4] mfd: I2C interface with AIC platform

Texas Instruments TLV320AIC family of audio SoC
core functionality controlled via I2C. This driver
provides common support for accessing the device.

Signed-off-by: Mehar Bajwa <mehar.bajwa@ti.com>
---
 drivers/mfd/Kconfig         |   12 +++++
 drivers/mfd/Makefile        |    1 +
 drivers/mfd/tlv320aic-i2c.c |   98 +++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 111 insertions(+), 0 deletions(-)
 create mode 100644 drivers/mfd/tlv320aic-i2c.c

diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig
index 3019897..40eb328 100644
--- a/drivers/mfd/Kconfig
+++ b/drivers/mfd/Kconfig
@@ -64,11 +64,23 @@ menu "AIC Interface Drivers"
 config MFD_AIC_IRQ
 	bool "Support of IRQ for AIC"
 	depends on MFD_AIC
+	default y
 	help
 	  Say yes here if you want support of IRQ for Texas Instruments
 	  AIC codec family.
 	  You have to select individual components like codec device
 	  under the corresponding menus.
+
+config MFD_AIC_I2C
+	bool "AIC I2C Interface"
+	select REGMAP_I2C
+	depends on MFD_AIC
+	depends on I2C
+	help
+	  Support for the Texas Instruments TLV320AIC family of audio SoC
+	  core functionality controlled via I2C. This driver provides common
+	  support for accessing the device, additional drivers must be enabled
+	  in order to use the functionality of the device.
 endmenu
 
 config MFD_SM501
diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile
index 3b39454..1e2c96a 100644
--- a/drivers/mfd/Makefile
+++ b/drivers/mfd/Makefile
@@ -8,6 +8,7 @@ obj-$(CONFIG_MFD_88PM800)	+= 88pm800.o 88pm80x.o
 obj-$(CONFIG_MFD_88PM805)	+= 88pm805.o 88pm80x.o
 obj-$(CONFIG_MFD_AIC)		+= tlv320aic-core.o
 obj-$(CONFIG_MFD_AIC_IRQ)	+= tlv320aic-irq.o
+obj-$(CONFIG_MFD_AIC_I2C)	+= tlv320aic-i2c.o
 obj-$(CONFIG_MFD_SM501)		+= sm501.o
 obj-$(CONFIG_MFD_ASIC3)		+= asic3.o tmio_core.o
 
diff --git a/drivers/mfd/tlv320aic-i2c.c b/drivers/mfd/tlv320aic-i2c.c
new file mode 100644
index 0000000..18987b1
--- /dev/null
+++ b/drivers/mfd/tlv320aic-i2c.c
@@ -0,0 +1,98 @@
+/*
+ * tlv320aic-i2c.c  -- driver for TLV320AIC Codecs Family
+ *
+ * Author:	Mukund Navada <navada@ti.com>
+ *		Mehar Bajwa <mehar.bajwa@ti.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/err.h>
+#include <linux/i2c.h>
+#include <linux/module.h>
+#include <linux/pm_runtime.h>
+#include <linux/regmap.h>
+#include <linux/regulator/consumer.h>
+#include <linux/slab.h>
+
+#include <linux/mfd/tlv320aic-core.h>
+
+struct regmap_config aic_i2c_regmap = {
+	.reg_bits = 8,
+	.val_bits = 8,
+	.cache_type = REGCACHE_NONE,
+};
+
+static int aic_i2c_probe(struct i2c_client *i2c,
+					  const struct i2c_device_id *id)
+{
+	struct aic *aic;
+	const struct regmap_config *regmap_config;
+	int ret;
+
+	regmap_config = &aic_i2c_regmap;
+
+	aic = devm_kzalloc(&i2c->dev, sizeof(*aic), GFP_KERNEL);
+	if (aic == NULL)
+		return -ENOMEM;
+
+	aic->regmap = devm_regmap_init_i2c(i2c, regmap_config);
+
+	if (IS_ERR(aic->regmap)) {
+		ret = PTR_ERR(aic->regmap);
+		dev_err(&i2c->dev, "Failed to allocate register map: %d\n",
+			ret);
+		return ret;
+	}
+
+	aic->type = id->driver_data;
+	aic->dev = &i2c->dev;
+	aic->irq = i2c->irq;
+
+	return aic_device_init(aic);
+}
+
+static int aic_i2c_remove(struct i2c_client *i2c)
+{
+	struct aic *aic = dev_get_drvdata(&i2c->dev);
+
+	aic_device_exit(aic);
+	return 0;
+}
+
+static const struct i2c_device_id aic_i2c_id[] = {
+	{"tlv320aic3262", TLV320AIC3262},
+	{ }
+};
+MODULE_DEVICE_TABLE(i2c, aic_i2c_id);
+
+static struct i2c_driver aic_i2c_driver = {
+	.driver = {
+		.name	= "tlv320aic",
+		.owner	= THIS_MODULE,
+	},
+	.probe		= aic_i2c_probe,
+	.remove		= aic_i2c_remove,
+	.id_table	= aic_i2c_id,
+};
+
+module_i2c_driver(aic_i2c_driver);
+
+MODULE_DESCRIPTION("TLV320AIC I2C bus interface");
+MODULE_AUTHOR("Mukund Navada <navada@ti.com>");
+MODULE_AUTHOR("Mehar Bajwa <mehar.bajwa@ti.com>");
+MODULE_LICENSE("GPL");
-- 
1.7.0.4


[-- Attachment #5: 0004-mfd-SPI-interface-with-AIC-platform.patch --]
[-- Type: text/x-patch, Size: 4738 bytes --]

>From 5ff43c8a4da33b481f6554efaae3f5de0c4b6693 Mon Sep 17 00:00:00 2001
From: Mehar Bajwa <mehar.bajwa@ti.com>
Date: Fri, 12 Apr 2013 15:44:24 +0530
Subject: [PATCH 4/4] mfd: SPI interface with AIC platform

Texas Instruments TLV320AIC family of audio SoC
core functionality controlled via SPI. This driver
provides common support for accessing the device.

Signed-off-by: Mehar Bajwa <mehar.bajwa@ti.com>
---
 drivers/mfd/Kconfig         |   11 +++++
 drivers/mfd/Makefile        |    1 +
 drivers/mfd/tlv320aic-spi.c |   97 +++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 109 insertions(+), 0 deletions(-)
 create mode 100644 drivers/mfd/tlv320aic-spi.c

diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig
index 40eb328..b1d6269 100644
--- a/drivers/mfd/Kconfig
+++ b/drivers/mfd/Kconfig
@@ -81,6 +81,17 @@ config MFD_AIC_I2C
 	  core functionality controlled via I2C. This driver provides common
 	  support for accessing the device, additional drivers must be enabled
 	  in order to use the functionality of the device.
+
+config MFD_AIC_SPI
+	bool "AIC SPI Interface"
+	select REGMAP_SPI
+	depends on MFD_AIC
+	depends on SPI_MASTER
+	help
+	  Support for the Texas Instruments TLV320AIC family of audio SoC
+	  core functionality controlled via SPI.  This driver provides common
+	  support for accessing the device, additional drivers must be enabled
+	  in order to use the functionality of the device.
 endmenu
 
 config MFD_SM501
diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile
index 1e2c96a..dfdcfa2 100644
--- a/drivers/mfd/Makefile
+++ b/drivers/mfd/Makefile
@@ -9,6 +9,7 @@ obj-$(CONFIG_MFD_88PM805)	+= 88pm805.o 88pm80x.o
 obj-$(CONFIG_MFD_AIC)		+= tlv320aic-core.o
 obj-$(CONFIG_MFD_AIC_IRQ)	+= tlv320aic-irq.o
 obj-$(CONFIG_MFD_AIC_I2C)	+= tlv320aic-i2c.o
+obj-$(CONFIG_MFD_AIC_SPI)	+= tlv320aic-spi.o
 obj-$(CONFIG_MFD_SM501)		+= sm501.o
 obj-$(CONFIG_MFD_ASIC3)		+= asic3.o tmio_core.o
 
diff --git a/drivers/mfd/tlv320aic-spi.c b/drivers/mfd/tlv320aic-spi.c
new file mode 100644
index 0000000..2faeb40
--- /dev/null
+++ b/drivers/mfd/tlv320aic-spi.c
@@ -0,0 +1,97 @@
+/*
+ * tlv320aic-spi.c  -- driver for TLV320AIC
+ *
+ * Author:      Mukund Navada <navada@ti.com>
+ *		Mehar Bajwa <mehar.bajwa@ti.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ *
+ */
+
+#include <linux/err.h>
+#include <linux/module.h>
+#include <linux/pm_runtime.h>
+#include <linux/regmap.h>
+#include <linux/regulator/consumer.h>
+#include <linux/slab.h>
+#include <linux/spi/spi.h>
+
+#include <linux/mfd/tlv320aic-core.h>
+
+struct regmap_config aic_spi_regmap = {
+	.reg_bits = 7,
+	.val_bits = 8,
+	.cache_type = REGCACHE_NONE,
+	.read_flag_mask = 0x1,
+	.pad_bits = 1,
+};
+
+static int tlv320aic_spi_probe(struct spi_device *spi)
+{
+	const struct spi_device_id *id = spi_get_device_id(spi);
+	struct aic *aic;
+	const struct regmap_config *regmap_config;
+	int ret;
+
+	regmap_config = &aic_spi_regmap;
+
+	aic = devm_kzalloc(&spi->dev, sizeof(struct aic), GFP_KERNEL);
+	if (aic == NULL)
+		return -ENOMEM;
+
+	aic->regmap = devm_regmap_init_spi(spi, regmap_config);
+	if (IS_ERR(aic->regmap)) {
+		ret = PTR_ERR(aic->regmap);
+		dev_err(&spi->dev, "Failed to allocate register map: %d\n",
+			ret);
+		return ret;
+	}
+
+	aic->type = id->driver_data;
+	aic->dev = &spi->dev;
+	aic->irq = spi->irq;
+
+	return aic_device_init(aic);
+}
+
+static int tlv320aic_spi_remove(struct spi_device *spi)
+{
+	struct aic *aic = dev_get_drvdata(&spi->dev);
+	aic_device_exit(aic);
+	return 0;
+}
+
+static const struct spi_device_id aic_spi_ids[] = {
+	{"tlv320aic3262", TLV320AIC3262},
+	{ }
+};
+MODULE_DEVICE_TABLE(spi, aic_spi_ids);
+
+static struct spi_driver tlv320aic_spi_driver = {
+	.driver = {
+		.name	= "tlv320aic",
+		.owner	= THIS_MODULE,
+	},
+	.probe		= tlv320aic_spi_probe,
+	.remove		= tlv320aic_spi_remove,
+	.id_table	= aic_spi_ids,
+};
+
+module_spi_driver(tlv320aic_spi_driver);
+
+MODULE_DESCRIPTION("TLV320AIC SPI bus interface");
+MODULE_AUTHOR("Mukund Navada <navada@ti.com>");
+MODULE_AUTHOR("Mehar Bajwa <mehar.bajwa@ti.com>");
+MODULE_LICENSE("GPL");
-- 
1.7.0.4


^ permalink raw reply related	[flat|nested] 4+ messages in thread

* Re: [PATCH: v2]: Mfd-Initial-support-for-Texas-Instruments-AIC-family-Codecs
  2013-04-15 12:11     ` [PATCH: v2]: Mfd-Initial-support-for-Texas-Instruments-AIC-family-Codecs x0172184
@ 2013-04-15 13:25       ` Samuel Ortiz
  0 siblings, 0 replies; 4+ messages in thread
From: Samuel Ortiz @ 2013-04-15 13:25 UTC (permalink / raw)
  To: x0172184
  Cc: BAJWA, MEHAR, linux-kernel@vger.kernel.org,
	Navada Kanyana, Mukund, liam.r.girdwood@intel.com

On Mon, Apr 15, 2013 at 05:41:55PM +0530, x0172184 wrote:
> On Friday 05 April 2013 09:21 PM, Samuel Ortiz wrote:
> >Hi Mehar,
> >
> >On Sun, Feb 17, 2013 at 05:15:38AM +0000, BAJWA, MEHAR wrote:
> >>Hi Samuel,
> >>     I am re-sending the MFD support for AIC family of Audio CODECs
> >>from Texas Instruments. The patch was tested on mfd-for-linus-3.8-1.
> >>I would like to request that this be considered for inclusion in the kernel
> >>at the next available window. Could you please let me know if you have
> >>any comments/recommendations for this patch. We would like to work on it
> >>and get it up-streamed.
> >The patch looks good to me, but I'd like it to be splitted in several pieces.
> >You can probably place the core, the irq, the spi and the i2c pieces in their
> >own patches.
> >
> >Cheers,
> >Samuel.
> >
> Hi Samuel,
>     Thanks for your comments. As suggested by you, I am sending
> individual patches as version 2.
Please do not send them as attached patches, but inlined. One patch per
email and threaded, please.

Cheers,
Samuel. 

-- 
Intel Open Source Technology Centre
http://oss.intel.com/

^ permalink raw reply	[flat|nested] 4+ messages in thread

end of thread, other threads:[~2013-04-15 13:25 UTC | newest]

Thread overview: 4+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
     [not found] <511DFF8A.1080801@ti.com>
2013-02-17  5:15 ` [RESEND: PATCH]: Mfd-Initial-support-for-Texas-Instruments-AIC-family-Codecs BAJWA, MEHAR
2013-04-05 15:51   ` Samuel Ortiz
2013-04-15 12:11     ` [PATCH: v2]: Mfd-Initial-support-for-Texas-Instruments-AIC-family-Codecs x0172184
2013-04-15 13:25       ` Samuel Ortiz

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox