diff options
-rw-r--r-- | drivers/mfd/Kconfig | 5 | ||||
-rw-r--r-- | drivers/mfd/Makefile | 1 | ||||
-rw-r--r-- | drivers/mfd/tlv320aic3262-core.c | 885 | ||||
-rw-r--r-- | drivers/mfd/tlv320aic3262-irq.c | 204 | ||||
-rw-r--r-- | include/linux/mfd/tlv320aic3262-core.h | 237 | ||||
-rw-r--r-- | include/linux/mfd/tlv320aic3262-registers.h | 323 |
6 files changed, 1655 insertions, 0 deletions
diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig index e31f7710b1e2..10dba0cbda97 100644 --- a/drivers/mfd/Kconfig +++ b/drivers/mfd/Kconfig @@ -270,6 +270,11 @@ config TWL6040_CORE select MFD_CORE default n +config AIC3262_CODEC + bool + select MFD_CORE + default n + config MFD_STMPE bool "Support STMicroelectronics STMPE" depends on I2C=y && GENERIC_HARDIRQS diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile index 482cd278cb8d..c8dc50450219 100644 --- a/drivers/mfd/Makefile +++ b/drivers/mfd/Makefile @@ -49,6 +49,7 @@ obj-$(CONFIG_TWL4030_POWER) += twl4030-power.o obj-$(CONFIG_MFD_TWL4030_AUDIO) += twl4030-audio.o obj-$(CONFIG_TWL6030_PWM) += twl6030-pwm.o obj-$(CONFIG_TWL6040_CORE) += twl6040-core.o twl6040-irq.o +obj-$(CONFIG_AIC3262_CODEC) += tlv320aic3262-core.o tlv320aic3262-irq.o obj-$(CONFIG_MFD_MC13XXX) += mc13xxx-core.o diff --git a/drivers/mfd/tlv320aic3262-core.c b/drivers/mfd/tlv320aic3262-core.c new file mode 100644 index 000000000000..7b61c7497a45 --- /dev/null +++ b/drivers/mfd/tlv320aic3262-core.c @@ -0,0 +1,885 @@ + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/slab.h> +#include <linux/i2c.h> +#include <linux/spi/spi.h> +#include <linux/delay.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/tlv320aic3262-core.h> +#include <linux/mfd/tlv320aic3262-registers.h> +#define DEBUG +struct aic3262_gpio { + unsigned int reg; + u8 mask; + u8 shift; +}; +struct aic3262_gpio aic3262_gpio_control[] = { + { + .reg = AIC3262_GPIO1_IO_CNTL, + .mask = AIC3262_GPIO_D6_D2, + .shift = AIC3262_GPIO_D2_SHIFT, + }, + { + .reg = AIC3262_GPIO2_IO_CNTL, + .mask = AIC3262_GPIO_D6_D2, + .shift = AIC3262_GPIO_D2_SHIFT, + }, + { + .reg = AIC3262_GPI1_EN, + .mask = AIC3262_GPI1_D2_D1, + .shift = AIC3262_GPIO_D1_SHIFT, + }, + { + .reg = AIC3262_GPI2_EN, + .mask = AIC3262_GPI2_D5_D4, + .shift = AIC3262_GPIO_D4_SHIFT, + }, + { + .reg = AIC3262_GPO1_OUT_CNTL, + .mask = AIC3262_GPO1_D4_D1, + .shift = AIC3262_GPIO_D1_SHIFT, + }, +}; +static int aic3262_read(struct aic3262 *aic3262, unsigned int reg, + int bytes, void *dest) +{ + int ret; + int i; + u8 *buf = dest; + + BUG_ON(bytes <= 0); + + ret = aic3262->read_dev(aic3262, reg, bytes, dest); + if (ret < 0) + return ret; + + for (i = 0; i < bytes ; i++) { + dev_vdbg(aic3262->dev, "Read %04x from R%d(0x%x)\n", + buf[i], reg + i, reg + i); + } + + return ret; +} + +/** + * aic3262_reg_read: Read a single TLV320AIC3262 register. + * + * @aic3262: Device to read from. + * @reg: Register to read. + */ +int aic3262_reg_read(struct aic3262 *aic3262, unsigned int reg) +{ + unsigned char val; + int ret; + + mutex_lock(&aic3262->io_lock); + + ret = aic3262_read(aic3262, reg, 1, &val); + + mutex_unlock(&aic3262->io_lock); + + if (ret < 0) + return ret; + else + return val; +} +EXPORT_SYMBOL_GPL(aic3262_reg_read); + +/** + * aic3262_bulk_read: Read multiple TLV320AIC3262 registers + * + * @aic3262: Device to read from + * @reg: First register + * @count: Number of registers + * @buf: Buffer to fill. The data will be returned big endian. + */ +int aic3262_bulk_read(struct aic3262 *aic3262, unsigned int reg, + int count, u8 *buf) +{ + int ret; + + mutex_lock(&aic3262->io_lock); + + ret = aic3262_read(aic3262, reg, count, buf); + + mutex_unlock(&aic3262->io_lock); + + + return ret; +} +EXPORT_SYMBOL_GPL(aic3262_bulk_read); + +static int aic3262_write(struct aic3262 *aic3262, unsigned int reg, + int bytes, const void *src) +{ + const u8 *buf = src; + int i; + + BUG_ON(bytes <= 0); + + for (i = 0; i < bytes ; i++) { + dev_vdbg(aic3262->dev, "Write %04x to R%d(0x%x)\n", + buf[i], reg + i, reg + i); + } + + return aic3262->write_dev(aic3262, reg, bytes, src); +} + +/** + * aic3262_reg_write: Write a single TLV320AIC3262 register. + * + * @aic3262: Device to write to. + * @reg: Register to write to. + * @val: Value to write. + */ +int aic3262_reg_write(struct aic3262 *aic3262, unsigned int reg, + unsigned char val) +{ + int ret; + + + mutex_lock(&aic3262->io_lock); + + dev_dbg(aic3262->dev, "w 30 %x %x", reg, val); + ret = aic3262_write(aic3262, reg, 1, &val); + + mutex_unlock(&aic3262->io_lock); + + return ret; +} +EXPORT_SYMBOL_GPL(aic3262_reg_write); + +/** + * aic3262_bulk_write: Write multiple TLV320AIC3262 registers + * + * @aic3262: Device to write to + * @reg: First register + * @count: Number of registers + * @buf: Buffer to write from. Data must be big-endian formatted. + */ +int aic3262_bulk_write(struct aic3262 *aic3262, unsigned int reg, + int count, const u8 *buf) +{ + int ret; + + mutex_lock(&aic3262->io_lock); + + ret = aic3262_write(aic3262, reg, count, buf); + + mutex_unlock(&aic3262->io_lock); + + return ret; +} +EXPORT_SYMBOL_GPL(aic3262_bulk_write); + +/** + * aic3262_set_bits: Set the value of a bitfield in a TLV320AIC3262 register + * + * @aic3262: Device to write to. + * @reg: Register to write to. + * @mask: Mask of bits to set. + * @val: Value to set (unshifted) + */ +int aic3262_set_bits(struct aic3262 *aic3262, unsigned int reg, + unsigned char mask, unsigned char val) +{ + int ret; + u8 r; + + mutex_lock(&aic3262->io_lock); + + ret = aic3262_read(aic3262, reg, 1, &r); + if (ret < 0) + goto out; + + + r &= ~mask; + r |= (val & mask); + + dev_dbg(aic3262->dev, "w 30 %x %x", reg, r); + ret = aic3262_write(aic3262, reg, 1, &r); + +out: + mutex_unlock(&aic3262->io_lock); + + return ret; +} +EXPORT_SYMBOL_GPL(aic3262_set_bits); + +/** + * aic3262_wait_bits: wait for a value of a bitfield in a TLV320AIC3262 register + * + * @aic3262: Device to write to. + * @reg: Register to write to. + * @mask: Mask of bits to set. + * @val: Value to set (unshifted) + * @sleep: mdelay value in each iteration in milliseconds + * @count: iteration count for timeout + */ +int aic3262_wait_bits(struct aic3262 *aic3262, unsigned int reg, + unsigned char mask, unsigned char val, int sleep, int counter) +{ + int status; + int timeout = sleep*counter; + + status = aic3262_reg_read(aic3262, reg); + while (((status & mask) != val) && counter) { + mdelay(sleep); + status = aic3262_reg_read(aic3262, reg); + counter--; + }; + if (!counter) + dev_err(aic3262->dev, + "wait_bits timedout (%d millisecs). lastval 0x%x\n", + timeout, status); + return counter; +} +EXPORT_SYMBOL_GPL(aic3262_wait_bits); + +/* to be changed -- Mukund*/ +static struct resource aic3262_codec_resources[] = { + { + .start = AIC3262_IRQ_HEADSET_DETECT, + .end = AIC3262_IRQ_SPEAKER_OVER_TEMP, + .flags = IORESOURCE_IRQ, + }, +}; + +static struct resource aic3262_gpio_resources[] = { + { + .start = AIC3262_GPIO1, + .end = AIC3262_GPO1, + .flags = IORESOURCE_IRQ, + }, +}; + +static struct mfd_cell aic3262_devs[] = { + { + .name = "tlv320aic3262-codec", + .num_resources = ARRAY_SIZE(aic3262_codec_resources), + .resources = aic3262_codec_resources, + }, + + { + .name = "tlv320aic3262-gpio", + .num_resources = ARRAY_SIZE(aic3262_gpio_resources), + .resources = aic3262_gpio_resources, + .pm_runtime_no_callbacks = true, + }, +}; + + +#ifdef CONFIG_PM +static int aic3262_suspend(struct device *dev) +{ + struct aic3262 *aic3262 = dev_get_drvdata(dev); + + aic3262->suspended = true; + + return 0; +} + +static int aic3262_resume(struct device *dev) +{ + struct aic3262 *aic3262 = dev_get_drvdata(dev); + + + aic3262->suspended = false; + + return 0; +} + +static UNIVERSAL_DEV_PM_OPS(aic3262_pm_ops, aic3262_suspend, aic3262_resume, + NULL); +#endif + + +/* + * Instantiate the generic non-control parts of the device. + */ +static int aic3262_device_init(struct aic3262 *aic3262, int irq) +{ + struct aic3262_pdata *pdata = aic3262->dev->platform_data; + const char *devname; + int ret, i; + u8 revID, pgID; + unsigned int naudint = 0; + u8 resetVal = 1; + + mutex_init(&aic3262->io_lock); + dev_set_drvdata(aic3262->dev, aic3262); + if (pdata) { + if (pdata->gpio_reset) { + ret = gpio_request(pdata->gpio_reset, + "aic3262-reset-pin"); + if (ret != 0) { + dev_err(aic3262->dev, + "Failed to reset aic3262 using gpio %d\n", + pdata->gpio_reset); + goto err_return; + } + gpio_direction_output(pdata->gpio_reset, 1); + mdelay(5); + gpio_direction_output(pdata->gpio_reset, 0); + mdelay(5); + gpio_direction_output(pdata->gpio_reset, 1); + mdelay(5); + } + } + + + /* run the codec through software reset */ + ret = aic3262_reg_write(aic3262, AIC3262_RESET_REG, resetVal); + if (ret < 0) { + dev_err(aic3262->dev, "Could not write to AIC3262 register\n"); + goto err_return; + } + + mdelay(10); + + ret = aic3262_reg_read(aic3262, AIC3262_REV_PG_ID); + if (ret < 0) { + dev_err(aic3262->dev, "Failed to read ID register\n"); + goto err_return; + } + revID = (ret & AIC3262_REV_MASK) >> AIC3262_REV_SHIFT; + pgID = (ret & AIC3262_PG_MASK) >> AIC3262_PG_SHIFT; + switch (revID) { + case 3: + devname = "TLV320AIC3262"; + if (aic3262->type != TLV320AIC3262) + dev_warn(aic3262->dev, "Device registered as type %d\n", + aic3262->type); + aic3262->type = TLV320AIC3262; + break; + case 1: + devname = "TLV320AIC3262"; + if (aic3262->type != TLV320AIC3262) + dev_warn(aic3262->dev, "Device registered as type %d\n", + aic3262->type); + aic3262->type = TLV320AIC3262; + break; + + default: + dev_err(aic3262->dev, "Device is not a TLV320AIC3262, ID is %x\n", + ret); + ret = -EINVAL; + goto err_return; + + } + + dev_info(aic3262->dev, "%s revision %c\n", devname, 'D' + ret); + + + if (pdata) { + if (pdata->gpio_irq == 1) { + naudint = gpio_to_irq(pdata->naudint_irq); + gpio_request(pdata->naudint_irq, "aic3262-gpio-irq"); + gpio_direction_input(pdata->naudint_irq); + } else + naudint = pdata->naudint_irq; + + aic3262->irq = naudint; + aic3262->irq_base = pdata->irq_base; + for (i = 0; i < AIC3262_NUM_GPIO; i++) { + if (pdata->gpio[i].used) { + /* Direction is input */ + if (pdata->gpio[i].in) { + /* set direction to input for GPIO, + and enable for GPI */ + aic3262_set_bits(aic3262, + aic3262_gpio_control[i].reg, + aic3262_gpio_control[i].mask, + 0x1 << + aic3262_gpio_control[i].shift); + + if (pdata->gpio[i].in_reg) + /* Some input modes, does not + need extra registers to be + written */ + aic3262_set_bits(aic3262, + pdata->gpio[i].in_reg, + pdata->gpio[i]. + in_reg_bitmask, + pdata->gpio[i].value << + pdata->gpio[i]. + in_reg_shift); + } else { + /* Direction si output */ + aic3262_set_bits(aic3262, + aic3262_gpio_control[i].reg, + aic3262_gpio_control[i].mask, + pdata->gpio[i].value << + aic3262_gpio_control[i].shift); + } + } else + aic3262_set_bits(aic3262, + aic3262_gpio_control[i].reg, + aic3262_gpio_control[i].mask, 0x0); + } + } + + if (naudint) { + /* codec interrupt */ + ret = aic3262_irq_init(aic3262); + if (ret) + goto err_irq; + } + + ret = mfd_add_devices(aic3262->dev, -1, + aic3262_devs, ARRAY_SIZE(aic3262_devs), + NULL, 0); + if (ret != 0) { + dev_err(aic3262->dev, "Failed to add children: %d\n", ret); + goto err_irq; + } + + pm_runtime_enable(aic3262->dev); + pm_runtime_resume(aic3262->dev); + + return 0; + +err_irq: + aic3262_irq_exit(aic3262); +err_return: + kfree(aic3262); + return ret; +} + +static void aic3262_device_exit(struct aic3262 *aic3262) +{ + pm_runtime_disable(aic3262->dev); + mfd_remove_devices(aic3262->dev); + aic3262_irq_exit(aic3262); + kfree(aic3262); +} + +#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE) + + +static int aic3262_i2c_read_device(struct aic3262 *aic3262, unsigned int reg, + int bytes, void *dest) +{ + struct i2c_client *i2c = aic3262->control_data; + union aic326x_reg_union *aic_reg = (union aic326x_reg_union *) ® + char *value; + int ret; + u8 buf[2]; + u8 page, book, offset; + page = aic_reg->aic326x_register.page; + book = aic_reg->aic326x_register.book; + offset = aic_reg->aic326x_register.offset; + if (aic3262->book_no != book) { + /* We should change to page 0. + Change the book by writing to offset 127 of page 0 + Change the page back to whatever was set before change page */ + buf[0] = 0x0; + buf[1] = 0x0; + ret = i2c_master_send(i2c, (unsigned char *)buf, 2); + if (ret < 0) + return ret; + if (ret != 2) + return -EIO; + buf[0] = 127; + buf[1] = book; + ret = i2c_master_send(i2c, (unsigned char *)buf, 2); + if (ret < 0) + return ret; + if (ret != 2) + return -EIO; + aic3262->book_no = book; + aic3262->page_no = 0x0; + } + + if (aic3262->page_no != page) { + buf[0] = 0x0; + buf[1] = page; + ret = i2c_master_send(i2c, (unsigned char *) buf, 2); + + if (ret < 0) + return ret; + if (ret != 2) + return -EIO; + aic3262->page_no = page; + } + + /* Send the required offset */ + buf[0] = offset ; + ret = i2c_master_send(i2c, (unsigned char *)buf, 1); + if (ret < 0) + return ret; + if (ret != 1) + return -EIO; + + ret = i2c_master_recv(i2c, dest, bytes); + value = dest; + if (ret < 0) + return ret; + if (ret != bytes) + return -EIO; + return ret; +} + +static int aic3262_i2c_write_device(struct aic3262 *aic3262, unsigned int reg, + int bytes, const void *src) +{ + struct i2c_client *i2c = aic3262->control_data; + int ret; + + union aic326x_reg_union *aic_reg = (union aic326x_reg_union *) ® + + u8 buf[2]; + u8 write_buf[bytes + 1]; + u8 page, book, offset; + page = aic_reg->aic326x_register.page; + book = aic_reg->aic326x_register.book; + offset = aic_reg->aic326x_register.offset; + if (aic3262->book_no != book) { + /* We should change to page 0. + Change the book by writing to offset 127 of page 0 + Change the page back to whatever was set before change page*/ + buf[0] = 0x0; + buf[1] = 0x0; + ret = i2c_master_send(i2c, (unsigned char *)buf, 2); + + if (ret < 0) + return ret; + if (ret != 2) + return -EIO; + buf[0] = 127; + buf[1] = book; + ret = i2c_master_send(i2c, (unsigned char *)buf, 2); + if (ret < 0) + return ret; + if (ret != 2) + return -EIO; + aic3262->book_no = book; + aic3262->page_no = 0x0; + } + + if (aic3262->page_no != page) { + buf[0] = 0x0; + buf[1] = page; + ret = i2c_master_send(i2c, (unsigned char *) buf, 2); + if (ret < 0) + return ret; + if (ret != 2) + return -EIO; + aic3262->page_no = page; + } + write_buf[0] = offset; + memcpy(&write_buf[1], src, bytes); + ret = i2c_master_send(i2c, write_buf, bytes + 1); + if (ret < 0) + return ret; + if (ret != (bytes + 1)) + return -EIO; + + return 0; +} + +static int aic3262_i2c_probe(struct i2c_client *i2c, + const struct i2c_device_id *id) +{ + struct aic3262 *aic3262; + + aic3262 = kzalloc(sizeof(struct aic3262), GFP_KERNEL); + if (aic3262 == NULL) + return -ENOMEM; + + i2c_set_clientdata(i2c, aic3262); + aic3262->dev = &i2c->dev; + aic3262->control_data = i2c; + aic3262->read_dev = aic3262_i2c_read_device; + aic3262->write_dev = aic3262_i2c_write_device; + aic3262->type = id->driver_data; + aic3262->book_no = 255; + aic3262->page_no = 255; + + return aic3262_device_init(aic3262, i2c->irq); +} + +static int aic3262_i2c_remove(struct i2c_client *i2c) +{ + struct aic3262 *aic3262 = i2c_get_clientdata(i2c); + + aic3262_device_exit(aic3262); + + return 0; +} + +static const struct i2c_device_id aic3262_i2c_id[] = { + { "tlv320aic3262", TLV320AIC3262 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, aic3262_i2c_id); + + +static struct i2c_driver aic3262_i2c_driver = { + .driver = { + .name = "tlv320aic3262", + .owner = THIS_MODULE, + .pm = &aic3262_pm_ops, + }, + .probe = aic3262_i2c_probe, + .remove = aic3262_i2c_remove, + .id_table = aic3262_i2c_id, +}; + +static int __init aic3262_i2c_init(void) +{ + int ret; + ret = i2c_add_driver(&aic3262_i2c_driver); + if (ret != 0) + pr_err("Failed to register aic3262 I2C driver: %d\n", ret); + + return ret; +} +module_init(aic3262_i2c_init); + +static void __exit aic3262_i2c_exit(void) +{ + i2c_del_driver(&aic3262_i2c_driver); +} +module_exit(aic3262_i2c_exit); +#endif +#if defined(CONFIG_SPI_MASTER) +/* TODO: UGLY + * NVidia's CS differs from what TI requires on the SPI bus. So before + * we do any write/read we pull down the CS gpio :( + * The problem is in spi_read. + * Can we set the flag spi_transfer.cs_change during read so that CS is + * pulled low until the next transaction occurs + * (spi_read requires a spi_write followed by spi_read) + */ +#include <linux/gpio.h> +#include "../../../arch/arm/mach-tegra/gpio-names.h" +#include <linux/delay.h> +#define SPI_CS TEGRA_GPIO_PX3 +#define CS(a) gpio_set_value(SPI_CS, a) +void nvidia_spi_cs_en(bool stop) +{ + if (stop) { + CS(1); + udelay(1); + } else { + CS(0); + udelay(1); + } + return; +} +static int aic3262_spi_read_device(struct aic3262 *aic3262, unsigned int reg, + int bytes, void *dest) +{ + struct spi_device *spi = aic3262->control_data; + union aic326x_reg_union *aic_reg = (union aic326x_reg_union *) ® + u8 *write_read_buf; + unsigned int i; + unsigned int time; + unsigned int last_count; + unsigned int spi_read_bufsize = max(32, SMP_CACHE_BYTES)-1; + struct spi_message message; + struct spi_transfer x[2]; + int ret; + u8 buf[2]; + u8 page, book, offset; + page = aic_reg->aic326x_register.page; + book = aic_reg->aic326x_register.book; + offset = aic_reg->aic326x_register.offset; + if (aic3262->book_no != book) { + /* We should change to page 0. + Change the book by writing to offset 127 of page 0 + Change the page back to whatever was set before change page */ + + buf[0] = 0x0; + buf[1] = 0x0; + + nvidia_spi_cs_en(0); + ret = spi_write(spi, (unsigned char *)buf, 2); + nvidia_spi_cs_en(1); + + if (ret < 0) + return ret; + buf[0] = (127 << 1) ; + buf[1] = book; + + nvidia_spi_cs_en(0); + ret = spi_write(spi, (unsigned char *)buf, 2); + nvidia_spi_cs_en(1); + + if (ret < 0) + return ret; + aic3262->book_no = book; + aic3262->page_no = 0x0; + } + + if (aic3262->page_no != page) { + buf[0] = 0x0; + buf[1] = page; + + nvidia_spi_cs_en(0); + ret = spi_write(spi, (unsigned char *)buf, 2); + nvidia_spi_cs_en(1); + + if (ret < 0) + return ret; + aic3262->page_no = page; + } + + buf[0] = (offset << 1) | (0x01) ; + memset(x, 0, sizeof x); + spi_message_init(&message); + x[0].len = 1; + x[0].tx_buf = buf; + x[1].len = bytes; + x[1].rx_buf = dest ; + + spi_message_add_tail(&x[0], &message); + spi_message_add_tail(&x[1], &message); + + nvidia_spi_cs_en(0); + ret = spi_sync(spi, &message); + nvidia_spi_cs_en(1); + if (ret < 0) + return ret; + + return bytes; + +} +/* NVidia's CS differs from what TI requires on the SPI bus. So before + * we do any write/read we pull down the CS gpio :( + */ +static int aic3262_spi_write_device(struct aic3262 *aic3262, unsigned int reg, + int bytes, const void *src) +{ + struct spi_device *spi = aic3262->control_data; + int ret; + + union aic326x_reg_union *aic_reg = (union aic326x_reg_union *) ® + + u8 buf[2]; + u8 write_buf[bytes + 1]; + u8 page, book, offset; + page = aic_reg->aic326x_register.page; + book = aic_reg->aic326x_register.book; + offset = aic_reg->aic326x_register.offset; + if (aic3262->book_no != book) { + /* We should change to page 0. + Change the book by writing to offset 127 of page 0 + Change the page back to whatever was set before change page */ + + buf[0] = 0x0; + buf[1] = 0x0; + + nvidia_spi_cs_en(0); + ret = spi_write(spi, (unsigned char *)buf, 2); + nvidia_spi_cs_en(1); + + if (ret < 0) + return ret; + buf[0] = (127 << 1) ; + buf[1] = book; + + nvidia_spi_cs_en(0); + ret = spi_write(spi, (unsigned char *)buf, 2); + nvidia_spi_cs_en(1); + + if (ret < 0) + return ret; + aic3262->book_no = book; + aic3262->page_no = 0x0; + } + + if (aic3262->page_no != page) { + buf[0] = 0x0; + buf[1] = page; + nvidia_spi_cs_en(0); + ret = spi_write(spi, (unsigned char *) buf, 2); + nvidia_spi_cs_en(1); + if (ret < 0) + return ret; + aic3262->page_no = page; + } + write_buf[0] = offset << 1 ; + memcpy(&write_buf[1], src, bytes); + nvidia_spi_cs_en(0); + ret = spi_write(spi, write_buf, bytes + 1); + nvidia_spi_cs_en(1); + if (ret < 0) + return ret; + + return bytes; +} + +static int aic3262_spi_probe(struct spi_device *spi) +{ + struct aic3262 *aic3262; + + aic3262 = kzalloc(sizeof(struct aic3262), GFP_KERNEL); + if (aic3262 == NULL) + return -ENOMEM; + + spi_set_drvdata(spi, aic3262); + aic3262->dev = &spi->dev; + aic3262->control_data = spi; + aic3262->read_dev = aic3262_spi_read_device; + aic3262->write_dev = aic3262_spi_write_device; + spi->bits_per_word = 8; + spi->mode = SPI_MODE_1; + spi->max_speed_hz = 4000*1000; + spi_setup(spi); + + if (strcmp(spi->modalias, "tlv320aic3262") == 0) + aic3262->type = TLV320AIC3262; + aic3262->book_no = 255; + aic3262->page_no = 255; + + return aic3262_device_init(aic3262, spi->irq); +} + +static int aic3262_spi_remove(struct spi_device *spi) +{ + struct aic3262 *aic3262 = spi_get_drvdata(spi); + + aic3262_device_exit(aic3262); + + return 0; +} + +static struct spi_driver aic3262_spi_driver = { + .driver = { + .name = "tlv320aic3262", + .owner = THIS_MODULE, + .pm = &aic3262_pm_ops, + }, + .probe = aic3262_spi_probe, + .remove = aic3262_spi_remove, +}; + +static int __init aic3262_spi_init(void) +{ + int ret; + ret = spi_register_driver(&aic3262_spi_driver); + if (ret != 0) + pr_err("Failed to register aic3262 SPI driver: %d\n", ret); + + return ret; +} +module_init(aic3262_spi_init); + +static void __exit aic3262_spi_exit(void) +{ + spi_unregister_driver(&aic3262_spi_driver); +} +module_exit(aic3262_spi_exit); +#endif + +MODULE_DESCRIPTION("Core support for the TLV320AIC3262 audio CODEC"); +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Mukund Navada <navada@ti.com>"); diff --git a/drivers/mfd/tlv320aic3262-irq.c b/drivers/mfd/tlv320aic3262-irq.c new file mode 100644 index 000000000000..7e7a5499f3e5 --- /dev/null +++ b/drivers/mfd/tlv320aic3262-irq.c @@ -0,0 +1,204 @@ + +#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/mfd/tlv320aic3262-core.h> +#include <linux/mfd/tlv320aic3262-registers.h> + +#include <linux/delay.h> +struct aic3262_irq_data { + int mask; + int status; +}; + +static struct aic3262_irq_data aic3262_irqs[] = { + { + .mask = AIC3262_HEADSET_IN_MASK, + .status = AIC3262_HEADSET_PLUG_UNPLUG_INT, + }, + { + .mask = AIC3262_BUTTON_PRESS_MASK, + .status = AIC3262_BUTTON_PRESS_INT, + }, + { + .mask = AIC3262_DAC_DRC_THRES_MASK, + .status = AIC3262_LEFT_DRC_THRES_INT | AIC3262_RIGHT_DRC_THRES_INT, + }, + { + .mask = AIC3262_AGC_NOISE_MASK, + .status = AIC3262_LEFT_AGC_NOISE_INT | AIC3262_RIGHT_AGC_NOISE_INT, + }, + { + .mask = AIC3262_OVER_CURRENT_MASK, + .status = AIC3262_LEFT_OUTPUT_DRIVER_OVERCURRENT_INT + | AIC3262_RIGHT_OUTPUT_DRIVER_OVERCURRENT_INT, + }, + { + .mask = AIC3262_OVERFLOW_MASK, + .status = + AIC3262_LEFT_DAC_OVERFLOW_INT | AIC3262_RIGHT_DAC_OVERFLOW_INT | + AIC3262_MINIDSP_D_BARREL_SHIFT_OVERFLOW_INT | + AIC3262_LEFT_ADC_OVERFLOW_INT | AIC3262_RIGHT_ADC_OVERFLOW_INT | + AIC3262_MINIDSP_D_BARREL_SHIFT_OVERFLOW_INT, + }, + { + .mask = AIC3262_SPK_OVERCURRENT_MASK, + .status = AIC3262_SPK_OVER_CURRENT_INT, + }, + +}; + +struct aic3262_gpio_data { + +}; + +static inline struct aic3262_irq_data *irq_to_aic3262_irq(struct aic3262 + *aic3262, int irq) +{ + return &aic3262_irqs[irq - aic3262->irq_base]; +} + +static void aic3262_irq_lock(struct irq_data *data) +{ + struct aic3262 *aic3262 = irq_data_get_irq_chip_data(data); + + mutex_lock(&aic3262->irq_lock); +} + +static void aic3262_irq_sync_unlock(struct irq_data *data) +{ + struct aic3262 *aic3262 = irq_data_get_irq_chip_data(data); + + /* write back to hardware any change in irq mask */ + if (aic3262->irq_masks_cur != aic3262->irq_masks_cache) { + aic3262->irq_masks_cache = aic3262->irq_masks_cur; + aic3262_reg_write(aic3262, AIC3262_INT1_CNTL, + aic3262->irq_masks_cur); + } + + mutex_unlock(&aic3262->irq_lock); +} + +static void aic3262_irq_unmask(struct irq_data *data) +{ + struct aic3262 *aic3262 = irq_data_get_irq_chip_data(data); + struct aic3262_irq_data *irq_data = + irq_to_aic3262_irq(aic3262, data->irq); + + aic3262->irq_masks_cur |= irq_data->mask; +} + +static void aic3262_irq_mask(struct irq_data *data) +{ + struct aic3262 *aic3262 = irq_data_get_irq_chip_data(data); + struct aic3262_irq_data *irq_data = + irq_to_aic3262_irq(aic3262, data->irq); + + aic3262->irq_masks_cur &= ~irq_data->mask; +} + +static struct irq_chip aic3262_irq_chip = { + .name = "tlv320aic3262", + .irq_bus_lock = aic3262_irq_lock, + .irq_bus_sync_unlock = aic3262_irq_sync_unlock, + .irq_mask = aic3262_irq_mask, + .irq_unmask = aic3262_irq_unmask, +}; + +static irqreturn_t aic3262_irq_thread(int irq, void *data) +{ + struct aic3262 *aic3262 = data; + u8 status[4]; + int i = 0; + /* Reading the sticky bit registers acknowledges + the interrupt to the device */ + aic3262_bulk_read(aic3262, AIC3262_INT_STICKY_FLAG1, 4, status); + + /* report */ + if (status[2] & aic3262_irqs[AIC3262_IRQ_HEADSET_DETECT].status) + handle_nested_irq(aic3262->irq_base); + + if (status[2] & aic3262_irqs[AIC3262_IRQ_BUTTON_PRESS].status) + handle_nested_irq(aic3262->irq_base + 1); + if (status[2] & aic3262_irqs[AIC3262_IRQ_DAC_DRC].status) + handle_nested_irq(aic3262->irq_base + 2); + if (status[3] & aic3262_irqs[AIC3262_IRQ_AGC_NOISE].status) + handle_nested_irq(aic3262->irq_base + 3); + if (status[2] & aic3262_irqs[AIC3262_IRQ_OVER_CURRENT].status) + handle_nested_irq(aic3262->irq_base + 4); + if (status[0] & aic3262_irqs[AIC3262_IRQ_OVERFLOW_EVENT].status) + handle_nested_irq(aic3262->irq_base + 5); + if (status[3] & aic3262_irqs[AIC3262_IRQ_SPEAKER_OVER_TEMP].status) + handle_nested_irq(aic3262->irq_base + 6); + + /* ack unmasked irqs */ + /* No need to acknowledge the interrupt on AIC3262 */ + + return IRQ_HANDLED; +} + +int aic3262_irq_init(struct aic3262 *aic3262) +{ + int cur_irq, ret; + + mutex_init(&aic3262->irq_lock); + + /* mask the individual interrupt sources */ + aic3262->irq_masks_cur = 0x0; + aic3262->irq_masks_cache = 0x0; + aic3262_reg_write(aic3262, AIC3262_INT1_CNTL, 0x0); + + if (!aic3262->irq) { + dev_warn(aic3262->dev, + "no interrupt specified, no interrupts\n"); + aic3262->irq_base = 0; + return 0; + } + + if (!aic3262->irq_base) { + dev_err(aic3262->dev, + "no interrupt base specified, no interrupts\n"); + return 0; + } + + /* Register them with genirq */ + for (cur_irq = aic3262->irq_base; + cur_irq < aic3262->irq_base + ARRAY_SIZE(aic3262_irqs); + cur_irq++) { + irq_set_chip_data(cur_irq, aic3262); + irq_set_chip_and_handler(cur_irq, &aic3262_irq_chip, + handle_edge_irq); + irq_set_nested_thread(cur_irq, 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(cur_irq, IRQF_VALID); +#else + set_irq_noprobe(cur_irq); +#endif + } + + ret = request_threaded_irq(aic3262->irq, NULL, aic3262_irq_thread, + IRQF_TRIGGER_RISING, + "tlv320aic3262", aic3262); + if (ret) { + dev_err(aic3262->dev, "failed to request IRQ %d: %d\n", + aic3262->irq, ret); + return ret; + } + + return 0; +} +EXPORT_SYMBOL(aic3262_irq_init); + +void aic3262_irq_exit(struct aic3262 *aic3262) +{ + if (aic3262->irq) + free_irq(aic3262->irq, aic3262); +} +EXPORT_SYMBOL(aic3262_irq_exit); diff --git a/include/linux/mfd/tlv320aic3262-core.h b/include/linux/mfd/tlv320aic3262-core.h new file mode 100644 index 000000000000..3c79ae98f860 --- /dev/null +++ b/include/linux/mfd/tlv320aic3262-core.h @@ -0,0 +1,237 @@ +#ifndef __MFD_AIC3262_CORE_H__ +#define __MFD_AIC3262_CORE_H__ + +#include <linux/interrupt.h> +#include <linux/mfd/core.h> +enum aic3262_type { + TLV320AIC3262 = 0, +}; + +#define AIC3262_IRQ_HEADSET_DETECT 0 +#define AIC3262_IRQ_BUTTON_PRESS 1 +#define AIC3262_IRQ_DAC_DRC 2 +#define AIC3262_IRQ_AGC_NOISE 3 +#define AIC3262_IRQ_OVER_CURRENT 4 +#define AIC3262_IRQ_OVERFLOW_EVENT 5 +#define AIC3262_IRQ_SPEAKER_OVER_TEMP 6 + +#define AIC3262_GPIO1 7 +#define AIC3262_GPIO2 8 +#define AIC3262_GPI1 9 +#define AIC3262_GPI2 10 +#define AIC3262_GPO1 11 + +union aic326x_reg_union { + struct aic326x_reg { + u8 offset; + u8 page; + u8 book; + u8 reserved; + } aic326x_register; + unsigned int aic326x_register_int; +}; + +/**************************** ************************************/ + +/* + ***************************************************************************** + * Structures Definitions + ***************************************************************************** + */ +/* + *---------------------------------------------------------------------------- + * @struct aic3262_setup_data | + * i2c specific data setup for AIC3262. + * @field unsigned short |i2c_address | + * Unsigned short for i2c address. + *---------------------------------------------------------------------------- + */ +struct aic3262_setup_data { + unsigned short i2c_address; +}; + +/* GPIO API */ +#define AIC3262_NUM_GPIO 5 /* include 2 GPI and 1 GPO pins */ +enum { + AIC3262_GPIO1_FUNC_DISABLED = 0, + AIC3262_GPIO1_FUNC_INPUT = 1, + AIC3262_GPIO1_FUNC_OUTPUT = 3, + AIC3262_GPIO1_FUNC_CLOCK_OUTPUT = 4, + AIC3262_GPIO1_FUNC_INT1_OUTPUT = 5, + AIC3262_GPIO1_FUNC_INT2_OUTPUT = 6, + AIC3262_GPIO1_FUNC_ADC_MOD_CLK_OUTPUT = 10, + AIC3262_GPIO1_FUNC_SAR_ADC_INTERRUPT = 12, + AIC3262_GPIO1_FUNC_ASI1_DATA_OUTPUT = 15, + AIC3262_GPIO1_FUNC_ASI1_WCLK = 16, + AIC3262_GPIO1_FUNC_ASI1_BCLK = 17, + AIC3262_GPIO1_FUNC_ASI2_WCLK = 18, + AIC3262_GPIO1_FUNC_ASI2_BCLK = 19, + AIC3262_GPIO1_FUNC_ASI3_WCLK = 20, + AIC3262_GPIO1_FUNC_ASI3_BCLK = 21 +}; + +enum { + AIC3262_GPIO2_FUNC_DISABLED = 0, + AIC3262_GPIO2_FUNC_INPUT = 1, + AIC3262_GPIO2_FUNC_OUTPUT = 3, + AIC3262_GPIO2_FUNC_CLOCK_OUTPUT = 4, + AIC3262_GPIO2_FUNC_INT1_OUTPUT = 5, + AIC3262_GPIO2_FUNC_INT2_OUTPUT = 6, + AIC3262_GPIO2_FUNC_ADC_MOD_CLK_OUTPUT = 10, + AIC3262_GPIO2_FUNC_SAR_ADC_INTERRUPT = 12, + AIC3262_GPIO2_FUNC_ASI1_DATA_OUTPUT = 15, + AIC3262_GPIO2_FUNC_ASI1_WCLK = 16, + AIC3262_GPIO2_FUNC_ASI1_BCLK = 17, + AIC3262_GPIO2_FUNC_ASI2_WCLK = 18, + AIC3262_GPIO2_FUNC_ASI2_BCLK = 19, + AIC3262_GPIO2_FUNC_ASI3_WCLK = 20, + AIC3262_GPIO2_FUNC_ASI3_BCLK = 21 +}; +enum { + AIC3262_GPO1_FUNC_DISABLED = 0, + AIC3262_GPO1_FUNC_MSO_OUTPUT_FOR_SPI = 1, + AIC3262_GPO1_FUNC_GENERAL_PURPOSE_OUTPUT = 2, + AIC3262_GPO1_FUNC_CLOCK_OUTPUT = 3, + AIC3262_GPO1_FUNC_INT1_OUTPUT = 4, + AIC3262_GPO1_FUNC_INT2_OUTPUT = 5, + AIC3262_GPO1_FUNC_ADC_MOD_CLK_OUTPUT = 7, + AIC3262_GPO1_FUNC_SAR_ADC_INTERRUPT = 12, + AIC3262_GPO1_FUNC_ASI1_DATA_OUTPUT = 15, +}; +/* + *---------------------------------------------------------------------------- + * @struct aic3262_configs | + * AIC3262 initialization data which has register offset and register + * value. + * @field u8 | book_no | + * AIC3262 Book Number Offsets required for initialization.. + * @field u16 | reg_offset | + * AIC3262 Register offsets required for initialization.. + * @field u8 | reg_val | + * value to set the AIC3262 register to initialize the AIC3262. + *--------------------------------------------------------------------------- + */ +struct aic3262_configs { + u8 book_no; + u16 reg_offset; + u8 reg_val; +}; + +/* + *---------------------------------------------------------------------------- + * @struct aic3262_rate_divs | + * Setting up the values to get different freqencies + * + * @field u32 | mclk | + * Master clock + * @field u32 | rate | + * sample rate + * @field u8 | p_val | + * value of p in PLL + * @field u32 | pll_j | + * value for pll_j + * @field u32 | pll_d | + * value for pll_d + * @field u32 | dosr | + * value to store dosr + * @field u32 | ndac | + * value for ndac + * @field u32 | mdac | + * value for mdac + * @field u32 | aosr | + * value for aosr + * @field u32 | nadc | + * value for nadc + * @field u32 | madc | + * value for madc + * @field u32 | blck_N | + * value for block N + */ +struct aic3262 { + struct mutex io_lock; + struct mutex irq_lock; + enum aic3262_type type; + struct device *dev; + int (*read_dev)(struct aic3262 *aic3262, unsigned int reg, + int bytes, void *dest); + int (*write_dev)(struct aic3262 *aic3262, unsigned int reg, + int bytes, const void *src); + + void *control_data; + unsigned int irq; + unsigned int irq_base; + u8 irq_masks_cur; + u8 irq_masks_cache; + /* Used over suspend/resume */ + bool suspended; + u8 book_no; + u8 page_no; +}; + +struct aic3262_gpio_setup { + u8 used; /* GPIO, GPI and GPO is used in the board, */ + /* used = 1 else 0 */ + u8 in; /* GPIO is used as input, in = 1 else in = 0 */ + /* GPI in = 1, GPO in = 0 */ + unsigned int in_reg; /* if GPIO is input, + register to write the mask. */ + u8 in_reg_bitmask; /* bitmask for 'value' to be + written into in_reg */ + u8 in_reg_shift; /* bits to shift to write 'value' + into in_reg */ + u8 value; /* value to be written + gpio_control_reg if GPIO */ + /* is output, in_reg if its input */ +}; + +struct aic3262_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. */ + struct aic3262_gpio_setup *gpio;/* all gpio configuration */ + int naudint_irq; /* audio interrupt */ + unsigned int irq_base; +}; + +static inline int aic3262_request_irq(struct aic3262 *aic3262, int irq, + irq_handler_t handler, + unsigned long irqflags, const char *name, + void *data) +{ + if (!aic3262->irq_base) + return -EINVAL; + + return request_threaded_irq(aic3262->irq_base + irq, NULL, handler, + irqflags, name, data); +} + +static inline int aic3262_free_irq(struct aic3262 *aic3262, int irq, void *data) +{ + if (!aic3262->irq_base) + return -EINVAL; + + free_irq(aic3262->irq_base + irq, data); + return 0; +} + +/* Device I/O API */ +int aic3262_reg_read(struct aic3262 *aic3262, unsigned int reg); +int aic3262_reg_write(struct aic3262 *aic3262, unsigned int reg, + unsigned char val); +int aic3262_set_bits(struct aic3262 *aic3262, unsigned int reg, + unsigned char mask, unsigned char val); +int aic3262_bulk_read(struct aic3262 *aic3262, unsigned int reg, + int count, u8 *buf); +int aic3262_bulk_write(struct aic3262 *aic3262, unsigned int reg, + int count, const u8 *buf); +int aic3262_wait_bits(struct aic3262 *aic3262, unsigned int reg, + unsigned char mask, unsigned char val, int delay, + int counter); + +int aic3262_irq_init(struct aic3262 *aic3262); +void aic3262_irq_exit(struct aic3262 *aic3262); + +#endif /* End of __MFD_AIC3262_CORE_H__ */ diff --git a/include/linux/mfd/tlv320aic3262-registers.h b/include/linux/mfd/tlv320aic3262-registers.h new file mode 100644 index 000000000000..bdd7dfed80cd --- /dev/null +++ b/include/linux/mfd/tlv320aic3262-registers.h @@ -0,0 +1,323 @@ + +#ifndef __MFD_AIC3262_REGISTERS_H__ +#define __MFD_AIC3262_REGISTERS_H__ + +#define MAKE_REG(book, page, offset) \ + (unsigned int)((book << 16)|(page << 8)|offset) + +/* ****************** Book 0 Registers **************************************/ + +/* ****************** Page 0 Registers **************************************/ +#define AIC3262_PAGE_SEL_REG MAKE_REG(0, 0, 0) +#define AIC3262_RESET_REG MAKE_REG(0, 0, 1) +#define AIC3262_REV_PG_ID 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 MAKE_REG(0, 0, 4) +#define AIC3262_PLL_CLKIN_REG 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 MAKE_REG(0, 0, 5) +#define AIC3262_PLL_PR_POW_REG 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 MAKE_REG(0, 0, 7) +#define AIC3262_JVAL_MASK 0x3f +#define AIC3262_PLL_D_MSB MAKE_REG(0, 0, 8) +#define AIC3262_DVAL_MSB_MASK 0xf +#define AIC3262_DVAL_LSB_MASK 0xff +#define AIC3262_PLL_D_LSB MAKE_REG(0, 0, 9) +#define AIC3262_PLL_CKIN_DIV MAKE_REG(0, 0, 10) + +#define AIC3262_NDAC_DIV_POW_REG MAKE_REG(0, 0, 11) +#define AIC3262_MDAC_DIV_POW_REG MAKE_REG(0, 0, 12) +#define AIC3262_DOSR_MSB_REG MAKE_REG(0, 0, 13) +#define AIC3262_DOSR_MSB_MASK 0x3 +#define AIC3262_DOSR_LSB_REG MAKE_REG(0, 0, 14) +#define AIC3262_DOSR_LSB_MASK 0xFF + +#define AIC3262_NADC_DIV_POW_REG MAKE_REG(0, 0, 18) +#define AIC3262_MADC_DIV_POW_REG MAKE_REG(0, 0, 19) +#define AIC3262_AOSR_REG MAKE_REG(0, 0, 20) +#define AIC3262_CLKOUT_MUX MAKE_REG(0, 0, 21) +#define AIC3262_CLKOUT_MDIV_VAL MAKE_REG(0, 0, 22) +#define AIC3262_TIMER_REG MAKE_REG(0, 0, 23) + +#define AIC3262_LF_CLK_CNTL MAKE_REG(0, 0, 24) +#define AIC3262_HF_CLK_CNTL_R1 MAKE_REG(0, 0, 25) +#define AIC3262_HF_CLK_CNTL_R2 MAKE_REG(0, 0, 26) +#define AIC3262_HF_CLK_CNTL_R3 MAKE_REG(0, 0, 27) +#define AIC3262_HF_CLK_CNTL_R4 MAKE_REG(0, 0, 28) +#define AIC3262_HF_CLK_TRIM_R1 MAKE_REG(0, 0, 29) +#define AIC3262_HF_CLK_TRIM_R2 MAKE_REG(0, 0, 30) +#define AIC3262_HF_CLK_TRIM_R3 MAKE_REG(0, 0, 31) +#define AIC3262_HF_CLK_TRIM_R4 MAKE_REG(0, 0, 32) +#define AIC3262_LDAC_POWER_MASK 0x80 +#define AIC3262_RDAC_POWER_MASK 0x08 +#define AIC3262_DAC_POWER_MASK 0x88 +#define AIC3262_DAC_FLAG 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 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_INT_STICKY_FLAG1 MAKE_REG(0, 0, 42) +#define AIC3262_LEFT_DAC_OVERFLOW_INT 0x80 +#define AIC3262_RIGHT_DAC_OVERFLOW_INT 0x40 +#define AIC3262_MINIDSP_D_BARREL_SHIFT_OVERFLOW_INT 0x20 +#define AIC3262_LEFT_ADC_OVERFLOW_INT 0x08 +#define AIC3262_RIGHT_ADC_OVERFLOW_INT 0x04 +#define AIC3262_MINIDSP_A_BARREL_SHIFT_OVERFLOW_INT 0x02 +#define AIC3262_INT_STICKY_FLAG2 MAKE_REG(0, 0, 44) +#define AIC3262_LEFT_OUTPUT_DRIVER_OVERCURRENT_INT 0x80 +#define AIC3262_RIGHT_OUTPUT_DRIVER_OVERCURRENT_INT 0x40 +#define AIC3262_BUTTON_PRESS_INT 0x20 +#define AIC3262_HEADSET_PLUG_UNPLUG_INT 0x10 +#define AIC3262_LEFT_DRC_THRES_INT 0x08 +#define AIC3262_RIGHT_DRC_THRES_INT 0x04 +#define AIC3262_MINIDSP_D_STD_INT 0x02 +#define AIC3262_RIGHT_DRC_AUX_INT 0x01 +#define AIC3262_INT_STICKY_FLAG3 MAKE_REG(0, 0, 45) +#define AIC3262_SPK_OVER_CURRENT_INT 0x80 +#define AIC3262_LEFT_AGC_NOISE_INT 0x40 +#define AIC3262_RIGHT_AGC_NOISE_INT 0x20 +#define AIC3262_INT1_CNTL MAKE_REG(0, 0, 48) +#define AIC3262_HEADSET_IN_MASK 0x80 +#define AIC3262_BUTTON_PRESS_MASK 0x40 +#define AIC3262_DAC_DRC_THRES_MASK 0x20 +#define AIC3262_AGC_NOISE_MASK 0x10 +#define AIC3262_OVER_CURRENT_MASK 0x08 +#define AIC3262_OVERFLOW_MASK 0x04 +#define AIC3262_SPK_OVERCURRENT_MASK 0x02 +#define AIC3262_INT2_CNTL MAKE_REG(0, 0, 49) +#define AIC3262_INT_FMT MAKE_REG(0, 0, 51) + +#define AIC3262_DAC_PRB MAKE_REG(0, 0, 60) +#define AIC3262_ADC_PRB MAKE_REG(0, 0, 61) +#define AIC3262_PASI_DAC_DP_SETUP MAKE_REG(0, 0, 63) + +#define AIC3262_DAC_MVOL_CONF MAKE_REG(0, 0, 64) +#define AIC3262_DAC_LR_MUTE_MASK 0xc +#define AIC3262_DAC_LR_MUTE 0xc + +#define AIC3262_DAC_LVOL MAKE_REG(0, 0, 65) +#define AIC3262_DAC_RVOL MAKE_REG(0, 0, 66) +#define AIC3262_HP_DETECT MAKE_REG(0, 0, 67) +#define AIC3262_DRC_CNTL_R1 MAKE_REG(0, 0, 68) +#define AIC3262_DRC_CNTL_R2 MAKE_REG(0, 0, 69) +#define AIC3262_DRC_CNTL_R3 MAKE_REG(0, 0, 70) +#define AIC3262_BEEP_CNTL_R1 MAKE_REG(0, 0, 71) +#define AIC3262_BEEP_CNTL_R2 MAKE_REG(0, 0, 72) + +#define AIC3262_ADC_CHANNEL_POW MAKE_REG(0, 0, 81) +#define AIC3262_ADC_FINE_GAIN MAKE_REG(0, 0, 82) +#define AIC3262_LADC_VOL MAKE_REG(0, 0, 83) +#define AIC3262_RADC_VOL MAKE_REG(0, 0, 84) +#define AIC3262_ADC_PHASE MAKE_REG(0, 0, 85) + +#define AIC3262_LAGC_CNTL MAKE_REG(0, 0, 86) +#define AIC3262_LAGC_CNTL_R2 MAKE_REG(0, 0, 87) +#define AIC3262_LAGC_CNTL_R3 MAKE_REG(0, 0, 88) +#define AIC3262_LAGC_CNTL_R4 MAKE_REG(0, 0, 89) +#define AIC3262_LAGC_CNTL_R5 MAKE_REG(0, 0, 90) +#define AIC3262_LAGC_CNTL_R6 MAKE_REG(0, 0, 91) +#define AIC3262_LAGC_CNTL_R7 MAKE_REG(0, 0, 92) +#define AIC3262_LAGC_CNTL_R8 MAKE_REG(0, 0, 93) + +#define AIC3262_RAGC_CNTL MAKE_REG(0, 0, 94) +#define AIC3262_RAGC_CNTL_R2 MAKE_REG(0, 0, 95) +#define AIC3262_RAGC_CNTL_R3 MAKE_REG(0, 0, 96) +#define AIC3262_RAGC_CNTL_R4 MAKE_REG(0, 0, 97) +#define AIC3262_RAGC_CNTL_R5 MAKE_REG(0, 0, 98) +#define AIC3262_RAGC_CNTL_R6 MAKE_REG(0, 0, 99) +#define AIC3262_RAGC_CNTL_R7 MAKE_REG(0, 0, 100) +#define AIC3262_RAGC_CNTL_R8 MAKE_REG(0, 0, 101) +#define AIC3262_MINIDSP_ACCESS_CTRL MAKE_REG(0, 0, 121) +/* ****************** Page 1 Registers **************************************/ +#define AIC3262_PAGE_1 128 + +#define AIC3262_POWER_CONF 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 MAKE_REG(0, 1, 3) +#define AIC3262_RDAC_PTM MAKE_REG(0, 1, 4) +#define AIC3262_CM_REG MAKE_REG(0, 1, 8) +#define AIC3262_HP_CTL MAKE_REG(0, 1, 9) +#define AIC3262_HP_DEPOP MAKE_REG(0, 1, 11) +#define AIC3262_RECV_DEPOP MAKE_REG(0, 1, 12) +#define AIC3262_MA_CNTL MAKE_REG(0, 1, 17) +#define AIC3262_LADC_PGA_MAL_VOL MAKE_REG(0, 1, 18) +#define AIC3262_RADC_PGA_MAR_VOL MAKE_REG(0, 1, 19) + +#define AIC3262_LINE_AMP_CNTL_R1 MAKE_REG(0, 1, 22) +#define AIC3262_LINE_AMP_CNTL_R2 MAKE_REG(0, 1, 23) + +#define AIC3262_HP_AMP_CNTL_R1 MAKE_REG(0, 1, 27) +#define AIC3262_HP_AMP_CNTL_R2 MAKE_REG(0, 1, 28) +#define AIC3262_HP_AMP_CNTL_R3 MAKE_REG(0, 1, 29) + +#define AIC3262_HPL_VOL MAKE_REG(0, 1, 31) +#define AIC3262_HPR_VOL MAKE_REG(0, 1, 32) +#define AIC3262_INT1_SEL_L MAKE_REG(0, 1, 34) +#define AIC3262_CHARGE_PUMP_CNTL MAKE_REG(0, 1, 35) +#define AIC3262_RAMP_CNTL_R1 MAKE_REG(0, 1, 36) +#define AIC3262_RAMP_CNTL_R2 MAKE_REG(0, 1, 37) +#define AIC3262_IN1L_SEL_RM MAKE_REG(0, 1, 38) +#define AIC3262_IN1R_SEL_RM MAKE_REG(0, 1, 39) +#define AIC3262_REC_AMP_CNTL_R5 MAKE_REG(0, 1, 40) +#define AIC3262_RAMPR_VOL MAKE_REG(0, 1, 41) +#define AIC3262_RAMP_TIME_CNTL MAKE_REG(0, 1, 42) +#define AIC3262_SPK_AMP_CNTL_R1 MAKE_REG(0, 1, 45) +#define AIC3262_SPK_AMP_CNTL_R2 MAKE_REG(0, 1, 46) +#define AIC3262_SPK_AMP_CNTL_R3 MAKE_REG(0, 1, 47) +#define AIC3262_SPK_AMP_CNTL_R4 MAKE_REG(0, 1, 48) +#define AIC3262_MIC_BIAS_CNTL MAKE_REG(0, 1, 51) + +#define AIC3262_LMIC_PGA_PIN MAKE_REG(0, 1, 52) +#define AIC3262_LMIC_PGA_PM_IN4 MAKE_REG(0, 1, 53) +#define AIC3262_LMIC_PGA_MIN MAKE_REG(0, 1, 54) +#define AIC3262_RMIC_PGA_PIN MAKE_REG(0, 1, 55) +#define AIC3262_RMIC_PGA_PM_IN4 MAKE_REG(0, 1, 56) +#define AIC3262_RMIC_PGA_MIN MAKE_REG(0, 1, 57) +#define AIC3262_HP_FLAG MAKE_REG(0, 1, 66) +#define AIC3262_SPKL_POWER_MASK 0x2 +#define AIC3262_SPKR_POWER_MASK 0x1 +#define AIC3262_HPL_POWER_MASK 0x20 +#define AIC3262_HPR_POWER_MASK 0x10 +/* MIC PGA Gain Registers */ +#define AIC3262_MICL_PGA MAKE_REG(0, 1, 59) +#define AIC3262_MICR_PGA MAKE_REG(0, 1, 60) +#define AIC3262_HEADSET_TUNING1_REG MAKE_REG(0, 1, 119) +#define AIC3262_HEADSET_DETECTOR_PULSE_MASK (0b11000000) +#define AIC3262_HEADSET_DETECTOR_PULSE_RESET (0b10000000) +#define AIC3262_MIC_PWR_DLY MAKE_REG(0, 1, 121) +#define AIC3262_REF_PWR_DLY 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 MAKE_REG(0, 4, 1) +#define AIC3262_ASI_SELECTION_MASK (0b11100000) +#define AIC3262_ASI_DATA_WORD_LENGTH_MASK (0b00011000) +#define AIC3262_ASI_BCLK_N_MASK (0b01111111) +#define AIC3262_ASI1_LCH_OFFSET MAKE_REG(0, 4, 2) +#define AIC3262_ASI1_RCH_OFFSET MAKE_REG(0, 4, 3) +#define AIC3262_ASI1_CHNL_SETUP MAKE_REG(0, 4, 4) +#define AIC3262_ASI1_MULTI_CH_SETUP_R1 MAKE_REG(0, 4, 5) +#define AIC3262_ASI1_MULTI_CH_SETUP_R2 MAKE_REG(0, 4, 6) +#define AIC3262_ASI1_ADC_INPUT_CNTL MAKE_REG(0, 4, 7) +#define AIC3262_ASI1_DAC_OUT_CNTL MAKE_REG(0, 4, 8) +#define AIC3262_ASI1_ADC_OUT_TRISTATE MAKE_REG(0, 4, 9) +#define AIC3262_ASI1_BWCLK_CNTL_REG MAKE_REG(0, 4, 10) +#define AIC3262_ASI1_BCLK_N_CNTL MAKE_REG(0, 4, 11) +#define AIC3262_ASI1_BCLK_N MAKE_REG(0, 4, 12) +#define AIC3262_ASI1_WCLK_N MAKE_REG(0, 4, 13) +#define AIC3262_ASI1_BWCLK_OUT_CNTL MAKE_REG(0, 4, 14) +#define AIC3262_ASI1_DOUT_CNTL MAKE_REG(0, 4, 15) +#define AIC3262_ASI2_BUS_FMT MAKE_REG(0, 4, 17) +#define AIC3262_ASI2_LCH_OFFSET MAKE_REG(0, 4, 18) +#define AIC3262_ASI2_ADC_INPUT_CNTL MAKE_REG(0, 4, 23) +#define AIC3262_ASI2_DAC_OUT_CNTL MAKE_REG(0, 4, 24) +#define AIC3262_ASI2_BWCLK_CNTL_REG MAKE_REG(0, 4, 26) +#define AIC3262_ASI2_BCLK_N_CNTL MAKE_REG(0, 4, 27) +#define AIC3262_ASI2_BCLK_N MAKE_REG(0, 4, 28) +#define AIC3262_ASI2_WCLK_N MAKE_REG(0, 4, 29) +#define AIC3262_ASI2_BWCLK_OUT_CNTL MAKE_REG(0, 4, 30) +#define AIC3262_ASI2_DOUT_CNTL MAKE_REG(0, 4, 31) +#define AIC3262_ASI3_BUS_FMT MAKE_REG(0, 4, 33) +#define AIC3262_ASI3_LCH_OFFSET MAKE_REG(0, 4, 34) +#define AIC3262_ASI3_ADC_INPUT_CNTL MAKE_REG(0, 4, 39) +#define AIC3262_ASI3_DAC_OUT_CNTL MAKE_REG(0, 4, 40) +#define AIC3262_ASI3_BWCLK_CNTL_REG MAKE_REG(0, 4, 42) +#define AIC3262_ASI3_BCLK_N_CNTL MAKE_REG(0, 4, 43) +#define AIC3262_ASI3_BCLK_N MAKE_REG(0, 4, 44) +#define AIC3262_ASI3_WCLK_N MAKE_REG(0, 4, 45) +#define AIC3262_ASI3_BWCLK_OUT_CNTL MAKE_REG(0, 4, 46) +#define AIC3262_ASI3_DOUT_CNTL MAKE_REG(0, 4, 47) +#define AIC3262_GPIO1_IO_CNTL 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 MAKE_REG(0, 4, 87) +#define AIC3262_GPI1_EN MAKE_REG(0, 4, 91) +#define AIC3262_GPI1_D2_D1 (0b00000110) +#define AIC3262_GPI2_D5_D4 (0b00110000) +#define AIC3262_GPI2_EN MAKE_REG(0, 4, 92) +#define AIC3262_GPO1_OUT_CNTL MAKE_REG(0, 4, 96) +#define AIC3262_GPO1_D4_D1 (0b00011110) +#define AIC3262_DMIC_INPUT_CNTL MAKE_REG(0, 4, 101) +#define AIC3262_DMIC_CONFIGURE_MASK (0b00011111) +#define AIC3262_DMIC_CONFIGURE_SHIFT (0) +#define AIC3262_DMIC_GPI1_LEFT_GPI1_RIGHT (0) +#define AIC3262_DMIC_GPI2_LEFT_GPI2_RIGHT (1) +#define AIC3262_DMIC_DIN1_LEFT_DIN1_RIGHT (2) +#define AIC3262_DMIC_DIN2_LEFT_DIN2_RIGHT (3) +#define AIC3262_DMIC_GPI01_LEFT_GPI01_RIGHT (4) +#define AIC3262_DMIC_GPI02_LEFT_GPI02_RIGHT (5) +#define AIC3262_DMIC_MCLK2_LEFT_MCLK2_RIGHT (6) +#define AIC3262_DMIC_GPI1_LEFT_GPI2_RIGHT (7) +#define AIC3262_DMIC_GPI1_LEFT_DIN1_RIGHT (8) +#define AIC3262_DMIC_GPI1_LEFT_DIN2_RIGHT (9) +#define AIC3262_DMIC_GPI1_LEFT_GPIO1_RIGHT (10) +#define AIC3262_DMIC_GPI1_LEFT_GPIO2_RIGHT (11) +#define AIC3262_DMIC_GPI1_LEFT_MCLK2_RIGHT (12) +#define AIC3262_DMIC_GPI2_LEFT_DIN1_RIGHT (13) +#define AIC3262_DMIC_GPI2_LEFT_DIN2_RIGHT (14) +#define AIC3262_DMIC_GPI2_LEFT_GPIO1_RIGHT (15) +#define AIC3262_DMIC_GPI2_LEFT_GPIO2_RIGHT (16) +#define AIC3262_DMIC_GPI2_LEFT_MCLK2_RIGHT (17) +#define AIC3262_DMIC_DIN1_LEFT_DIN2_RIGHT (18) +#define AIC3262_DMIC_DIN1_LEFT_GPIO1_RIGHT (19) +#define AIC3262_DMIC_DIN1_LEFT_GPIO2_RIGHT (20) +#define AIC3262_DMIC_DIN1_LEFT_MCLK2_RIGHT (21) +#define AIC3262_DMIC_DIN2_LEFT_GPIO1_RIGHT (22) +#define AIC3262_DMIC_DIN2_LEFT_GPIO2_RIGHT (23) +#define AIC3262_DMIC_DIN2_LEFT_MCLK2_RIGHT (24) +#define AIC3262_DMIC_GPI01_LEFT_GPI02_RIGHT (25) +#define AIC3262_DMIC_GPI01_LEFT_MCLK2_RIGHT (26) +#define AIC3262_DMIC_GPI02_LEFT_MCLK2_RIGHT (27) +#define AIC3262_MINIDSP_DATA_PORT_CNTL 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_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 MAKE_REG(40, 0, 1) +#define AIC3262_DAC_ADAPTIVE_BANK1_REG MAKE_REG(80, 0, 1) +#define AIC3262_DAC_ADAPTIVE_BANK2_REG MAKE_REG(82, 0, 1) +#define AIC3262_ADC_DATAPATH_SETUP MAKE_REG(0, 0, 81) +#define AIC3262_DAC_DATAPATH_SETUP MAKE_REG(0, 0, 63) + +#endif |