From e691eb848c2edfb47973959bb812eae3fbd1454a Mon Sep 17 00:00:00 2001 From: =?utf8?q?=E5=AE=8B=E7=A7=80=E6=9D=B0?= Date: Wed, 2 May 2012 18:21:14 +0800 Subject: [PATCH] rk30 phone: add TI aic3262 codec driver --- arch/arm/configs/rk30_phone_loquat_defconfig | 5 +- arch/arm/mach-rk30/board-rk30-phone-loquat.c | 9 + drivers/mfd/Kconfig | 5 + drivers/mfd/Makefile | 1 + drivers/mfd/tlv320aic3262-core.c | 680 +++++ drivers/mfd/tlv320aic3262-irq.c | 204 ++ include/linux/mfd/tlv320aic3262-core.h | 256 ++ include/linux/mfd/tlv320aic3262-registers.h | 301 ++ sound/soc/codecs/Kconfig | 3 + sound/soc/codecs/Makefile | 2 + sound/soc/codecs/aic3262_codec_ops.c | 407 +++ sound/soc/codecs/aic3262_codec_ops.h | 42 + sound/soc/codecs/aic326x_tiload.c | 349 +++ sound/soc/codecs/aic326x_tiload.h | 36 + sound/soc/codecs/aic3xxx_cfw.h | 425 +++ sound/soc/codecs/aic3xxx_cfw_ops.c | 447 +++ sound/soc/codecs/aic3xxx_cfw_ops.h | 69 + sound/soc/codecs/pickle.h | 7 + sound/soc/codecs/tlv320aic3262_default_fw.h | 320 ++ sound/soc/codecs/tlv320aic326x.c | 2869 ++++++++++++++++++ sound/soc/codecs/tlv320aic326x.h | 260 ++ sound/soc/rk29/Kconfig | 11 +- sound/soc/rk29/Makefile | 2 + sound/soc/rk29/rk29_aic3262.c | 412 +++ sound/soc/soc-core.c | 27 +- 25 files changed, 7146 insertions(+), 3 deletions(-) create mode 100644 drivers/mfd/tlv320aic3262-core.c create mode 100644 drivers/mfd/tlv320aic3262-irq.c create mode 100644 include/linux/mfd/tlv320aic3262-core.h create mode 100644 include/linux/mfd/tlv320aic3262-registers.h create mode 100644 sound/soc/codecs/aic3262_codec_ops.c create mode 100644 sound/soc/codecs/aic3262_codec_ops.h create mode 100644 sound/soc/codecs/aic326x_tiload.c create mode 100644 sound/soc/codecs/aic326x_tiload.h create mode 100644 sound/soc/codecs/aic3xxx_cfw.h create mode 100644 sound/soc/codecs/aic3xxx_cfw_ops.c create mode 100644 sound/soc/codecs/aic3xxx_cfw_ops.h create mode 100644 sound/soc/codecs/pickle.h create mode 100644 sound/soc/codecs/tlv320aic3262_default_fw.h create mode 100644 sound/soc/codecs/tlv320aic326x.c create mode 100644 sound/soc/codecs/tlv320aic326x.h create mode 100644 sound/soc/rk29/rk29_aic3262.c diff --git a/arch/arm/configs/rk30_phone_loquat_defconfig b/arch/arm/configs/rk30_phone_loquat_defconfig index d3f4bf7c53d0..9df0fa18f812 100755 --- a/arch/arm/configs/rk30_phone_loquat_defconfig +++ b/arch/arm/configs/rk30_phone_loquat_defconfig @@ -262,6 +262,7 @@ CONFIG_SPI_FPGA_GPIO_IRQ_NUM=0 CONFIG_POWER_SUPPLY=y CONFIG_TEST_POWER=y # CONFIG_HWMON is not set +CONFIG_AIC3262_CODEC=y CONFIG_REGULATOR=y CONFIG_MEDIA_SUPPORT=y CONFIG_VIDEO_DEV=y @@ -294,8 +295,10 @@ CONFIG_SND=y CONFIG_SND_SOC=y CONFIG_SND_RK29_SOC=y CONFIG_SND_I2S_DMA_EVENT_STATIC=y -CONFIG_SND_RK29_SOC_RT5631=y + +CONFIG_SND_RK29_SOC_AIC3262=y CONFIG_SND_RK29_CODEC_SOC_SLAVE=y +CONFIG_SND_SOC_TLV320AIC326X=y CONFIG_HID_A4TECH=y CONFIG_HID_ACRUX=y CONFIG_HID_ACRUX_FF=y diff --git a/arch/arm/mach-rk30/board-rk30-phone-loquat.c b/arch/arm/mach-rk30/board-rk30-phone-loquat.c index fd4b44f71931..7ba270c835dd 100755 --- a/arch/arm/mach-rk30/board-rk30-phone-loquat.c +++ b/arch/arm/mach-rk30/board-rk30-phone-loquat.c @@ -1319,6 +1319,15 @@ static struct i2c_board_info __initdata i2c0_info[] = { .flags = 0, }, #endif + +#if defined (CONFIG_SND_SOC_TLV320AIC326X) + { + .type = "tlv320aic3262", + .addr = 0x18, + .flags = 0, + }, +#endif + #if defined (CONFIG_SND_SOC_RT5631) { .type = "rt5631", diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig index af404a902e2f..2f102d7632f3 100755 --- a/drivers/mfd/Kconfig +++ b/drivers/mfd/Kconfig @@ -233,6 +233,11 @@ config TWL6030_PWM Say yes here if you want support for TWL6030 PWM. This is used to control charging LED brightness. +config AIC3262_CODEC + bool "Support TI Codec Aic3262" + 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 d0188cbcf8d3..829bbf1b42fa 100755 --- a/drivers/mfd/Makefile +++ b/drivers/mfd/Makefile @@ -43,6 +43,7 @@ obj-$(CONFIG_TWL4030_MADC) += twl4030-madc.o obj-$(CONFIG_TWL4030_POWER) += twl4030-power.o obj-$(CONFIG_TWL4030_CODEC) += twl4030-codec.o obj-$(CONFIG_TWL6030_PWM) += twl6030-pwm.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..4e058aca1fd0 --- /dev/null +++ b/drivers/mfd/tlv320aic3262-core.c @@ -0,0 +1,680 @@ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#define DEBUG 1 +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; + //u8 *buf = dest; + +// BUG_ON(bytes % 2); + BUG_ON(bytes <= 0); + + ret = aic3262->read_dev(aic3262, reg, bytes, dest); + if (ret < 0) + return ret; + +/* for (i = 0; i < bytes / 2; 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; + + +// BUG_ON(bytes % 2); + BUG_ON(bytes <= 0); + +/* for (i = 0; i < bytes / 2; 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; + +// val = cpu_to_be16(val); + + mutex_lock(&aic3262->io_lock); + + 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); + + ret = aic3262_write(aic3262, reg, 1, &r); + +out: + mutex_unlock(&aic3262->io_lock); + + return ret; +} +EXPORT_SYMBOL_GPL(aic3262_set_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); + + + /* Don't actually go through with the suspend if the CODEC is + * still active (eg, for audio passthrough from CP. */ +// ret = aic3262_reg_read(aic3262, 20AIC3262_POWER_MANAGEMENT_1); +/* if (ret < 0) { + dev_err(dev, "Failed to read power status: %d\n", ret); + } else if (ret & TLV320AIC3262_VMID_SEL_MASK) { + dev_dbg(dev, "CODEC still active, ignoring suspend\n"); + return 0; + } +*/ + /* GPIO configuration state is saved here since we may be configuring + * the GPIO alternate functions even if we're not using the gpiolib + * driver for them. + */ +// ret = aic3262_read(aic3262, TLV320AIC3262_GPIO_1, TLV320AIC3262_NUM_GPIO_REGS * 2, +// &aic3262->gpio_regs); +/* if (ret < 0) + dev_err(dev, "Failed to save GPIO registers: %d\n", ret);*/ + + + /* Explicitly put the device into reset in case regulators + * don't get disabled in order to ensure consistent restart. + */ +// aic3262_reg_write(aic3262, TLV320AIC3262_SOFTWARE_RESET, 0x8994); + + aic3262->suspended = true; + + return 0; +} + +static int aic3262_resume(struct device *dev) +{ + struct aic3262 *aic3262 = dev_get_drvdata(dev); + + + /* We may have lied to the PM core about suspending */ +/* if (!aic3262->suspended) + return 0; + + ret = aic3262_write(aic3262, TLV320AIC3262_INTERRUPT_STATUS_1_MASK, + TLV320AIC3262_NUM_IRQ_REGS * 2, &aic3262->irq_masks_cur); + if (ret < 0) + dev_err(dev, "Failed to restore interrupt masks: %d\n", ret); + + + ret = aic3262_write(aic3262, TLV320AIC3262_GPIO_1, TLV320AIC3262_NUM_GPIO_REGS * 2, + &aic3262->gpio_regs); + if (ret < 0) + dev_err(dev, "Failed to restore GPIO registers: %d\n", ret); +*/ + aic3262->suspended = false; + + return 0; +} +#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; + printk("aic3262_device_init beginning\n"); + + 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,"not able to acquire gpio %d for reseting the AIC3262 \n", pdata->gpio_reset); + goto err_return; + } + gpio_direction_output(pdata->gpio_reset, 1); + msleep(5); + gpio_direction_output(pdata->gpio_reset, 0); + msleep(5); + gpio_direction_output(pdata->gpio_reset, 1); +// gpio_set_value(pdata->gpio_reset, 0); + msleep(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; + } + + msleep(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; + 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); + + printk("aic3262_device_init %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) + { + if(pdata->gpio[i].in) // direction is input + { + // 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 is 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 // Disable the gpio/gpi/gpo + 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; + } + + printk("aic3262_device_init added mfd devices \n"); + pm_runtime_enable(aic3262->dev); + pm_runtime_resume(aic3262->dev); + + return 0; + +err_irq: + aic3262_irq_exit(aic3262); +//err: +// mfd_remove_devices(aic3262->dev); +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); +} + +static int aic3262_i2c_read_device(struct aic3262 *aic3262, unsigned int reg, + int bytes, void *dest) +{ + struct i2c_client *i2c = aic3262->control_data; + aic326x_reg_union *aic_reg = (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) // change in book required. + { + // 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;// To force a page change in the following if + } + + 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; + + //char *value; + aic326x_reg_union *aic_reg = (aic326x_reg_union *) ® + + //struct i2c_msg xfer[2]; + 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) // change in book required. + { + // 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;// To force a page change in the following if + } + + 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; + } +// value = (char *) src; + //ret = i2c_transfer(i2c->adapter, xfer, 2); + //printk("%s:write ret = %d\n",__func__, ret); + // send the offset as first message +/* xfer[0].addr = i2c->addr; + xfer[0].flags = i2c->flags & I2C_M_TEN; + xfer[0].len = 1; + xfer[0].buf = (char *)&offset; + // Send the values in bulk + xfer[1].addr = i2c->addr; + xfer[1].flags = i2c->flags & I2C_M_TEN; + xfer[1].len = bytes; + xfer[1].buf = (char *)src; + + ret = i2c_transfer(i2c->adapter, xfer, 2);*/ +/* buf[0] = offset; + memcpy(&buf[1], src, bytes);*/ +// ret = i2c_master_send(i2c, (unsigned char *)buf, 1); +// printk("%s:write offset ret = %d\n",__func__, ret); +// ret = i2c_master_send(i2c, (unsigned char *)src, bytes); +/* value = (char *) src; + buf[0] = offset; + buf[1] = *value; + ret = i2c_master_send(i2c, buf, 2); */ + 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; + + printk("%s: entered \n", __FUNCTION__); + aic3262 = kzalloc(sizeof(struct aic3262), GFP_KERNEL); + if (aic3262 == NULL) + return -ENOMEM; + + printk("%s: allocated memory \n", __FUNCTION__); + 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->irq = i2c->irq; + 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 UNIVERSAL_DEV_PM_OPS(aic3262_pm_ops, aic3262_suspend, aic3262_resume, + NULL); + +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; + printk("aic3262_mfd i2c_init \n"); + 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); + +MODULE_DESCRIPTION("Core support for the TLV320AIC3262 audio CODEC"); +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Mukund Navada "); diff --git a/drivers/mfd/tlv320aic3262-irq.c b/drivers/mfd/tlv320aic3262-irq.c new file mode 100644 index 000000000000..9a76f0f02e81 --- /dev/null +++ b/drivers/mfd/tlv320aic3262-irq.c @@ -0,0 +1,204 @@ + +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +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) // 0 + { + handle_nested_irq(aic3262->irq_base); + + } + if(status[2] & aic3262_irqs[AIC3262_IRQ_BUTTON_PRESS].status) // 1 + handle_nested_irq(aic3262->irq_base + 1); + if(status[2] & aic3262_irqs[AIC3262_IRQ_DAC_DRC].status) // 2 + handle_nested_irq(aic3262->irq_base + 2); + if(status[3] & aic3262_irqs[AIC3262_IRQ_AGC_NOISE].status) // 3 + handle_nested_irq(aic3262->irq_base + 3); + if(status[2] & aic3262_irqs[AIC3262_IRQ_OVER_CURRENT].status) // 4 + handle_nested_irq(aic3262->irq_base + 4); + if(status[0] & aic3262_irqs[AIC3262_IRQ_OVERFLOW_EVENT].status) // 5 + handle_nested_irq(aic3262->irq_base + 5); + if(status[3] & aic3262_irqs[AIC3262_IRQ_SPEAKER_OVER_TEMP].status) // 6 + 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..165f5e4c9cbf --- /dev/null +++ b/include/linux/mfd/tlv320aic3262-core.h @@ -0,0 +1,256 @@ +#ifndef __MFD_AIC3262_CORE_H__ +#define __MFD_AIC3262_CORE_H__ + +#include +#include +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 + +typedef union aic326x_reg_union { + struct aic326x_reg{ + u8 offset; + u8 page; + u8 book; + u8 reserved; + }aic326x_register; + unsigned int aic326x_register_int; +}aic326x_reg_union; + + +/**************************** ************************************/ + +/* + ***************************************************************************** + * 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; + +// int gpio_base; + + 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 to. + 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 void aic3262_free_irq(struct aic3262 *aic3262, int irq, + void *data) +{ + if (!aic3262->irq_base) + return; + + free_irq(aic3262->irq_base + irq, data); +} + +/* 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); + + +/* Helper to save on boilerplate */ +/*static inline int aic3262_request_irq(struct aic3262 *aic3262, int irq, + irq_handler_t handler, const char *name, + void *data) +{ + if (!aic3262->irq_base) + return -EINVAL; + return request_threaded_irq(aic3262->irq_base + irq, NULL, handler, + IRQF_TRIGGER_RISING, name, + data); +} +static inline void aic3262_free_irq(struct aic3262 *aic3262, int irq, void *data) +{ + if (!aic3262->irq_base) + return; + free_irq(aic3262->irq_base + irq, data); +} +*/ +int aic3262_irq_init(struct aic3262 *aic3262); +void aic3262_irq_exit(struct aic3262 *aic3262); + +#endif diff --git a/include/linux/mfd/tlv320aic3262-registers.h b/include/linux/mfd/tlv320aic3262-registers.h new file mode 100644 index 000000000000..3c268349f685 --- /dev/null +++ b/include/linux/mfd/tlv320aic3262-registers.h @@ -0,0 +1,301 @@ + +#ifndef __MFD_AIC3262_REGISTERS_H__ +#define __MFD_AIC3262_REGISTERS_H__ +/*typedef union aic326x_reg_union { + struct aic326x_reg{ + u8 reserved; + u8 book; + u8 page; + u8 offset; + }aic326x_register; + unsigned int aic326x_register_int; +}aic326x_reg_union;*/ +#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_FLAG MAKE_REG(0,0,37) +#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 (0b00001000) +#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 (0b1100000) +#define AIC3262_ASI_DATA_WORD_LENGTH_MASK (0b0011000) +#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_DMIC_INPUT_CNTL MAKE_REG(0,4, 101) +#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_CONTROL MAKE_REG(0, 4, 101) +#define AIC3262_DMIC_CONFIGURE_MASK (0b00011111) +#define AIC3262_DMIC_CONFIGURE_SHIFT (0) +#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 diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig index 594a1662839f..ab6dbb51b7c2 100644 --- a/sound/soc/codecs/Kconfig +++ b/sound/soc/codecs/Kconfig @@ -247,6 +247,9 @@ config SND_SOC_TLV320DAC33 config SND_SOC_TLV320AIC3111 tristate +config SND_SOC_TLV320AIC326X + tristate + config SND_SOC_TWL4030 select TWL4030_CODEC tristate diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile index 3720639b5912..ea82f0d8e08e 100644 --- a/sound/soc/codecs/Makefile +++ b/sound/soc/codecs/Makefile @@ -35,6 +35,7 @@ snd-soc-tlv320aic3x-objs := tlv320aic3x.o snd-soc-tlv320aic32x4-objs := tlv320aic32x4.o snd-soc-tlv320aic3111-objs := tlv320aic3111.o snd-soc-tlv320dac33-objs := tlv320dac33.o +snd-soc-tlv320aic326x-objs := tlv320aic326x.o aic326x_tiload.o aic3xxx_cfw_ops.o aic3262_codec_ops.o snd-soc-twl4030-objs := twl4030.o snd-soc-twl6040-objs := twl6040.o snd-soc-uda134x-objs := uda134x.o @@ -134,6 +135,7 @@ obj-$(CONFIG_SND_SOC_TLV320AIC3X) += snd-soc-tlv320aic3x.o obj-$(CONFIG_SND_SOC_TVL320AIC32X4) += snd-soc-tlv320aic32x4.o obj-$(CONFIG_SND_SOC_TLV320AIC3111) += snd-soc-tlv320aic3111.o obj-$(CONFIG_SND_SOC_TLV320DAC33) += snd-soc-tlv320dac33.o +obj-$(CONFIG_SND_SOC_TLV320AIC326X) += snd-soc-tlv320aic326x.o obj-$(CONFIG_SND_SOC_TWL4030) += snd-soc-twl4030.o obj-$(CONFIG_SND_SOC_TWL6040) += snd-soc-twl6040.o obj-$(CONFIG_SND_SOC_UDA134X) += snd-soc-uda134x.o diff --git a/sound/soc/codecs/aic3262_codec_ops.c b/sound/soc/codecs/aic3262_codec_ops.c new file mode 100644 index 000000000000..c9483036b60b --- /dev/null +++ b/sound/soc/codecs/aic3262_codec_ops.c @@ -0,0 +1,407 @@ +#include "aic3xxx_cfw.h" +#include "aic3xxx_cfw_ops.h" +#include +#include +#include +#include +#include "tlv320aic326x.h" +#include +#include "aic3262_codec_ops.h" + +#define DSP_STATUS(rs,adc_dac,rpos,rspos) rs |= ( ((adc_dac>>rpos) & 1) << rspos) + + + +int aic3262_ops_reg_read(void *p,unsigned int reg) +{ + struct aic3262_priv *ps = p; + cfw_register *c = (cfw_register *) ® + aic326x_reg_union mreg; + + mreg.aic326x_register.offset = c->offset; + mreg.aic326x_register.page = c->page; + mreg.aic326x_register.book = c->book; + mreg.aic326x_register.reserved = 0; + + return aic3262_reg_read(ps->codec->control_data, + mreg.aic326x_register_int); + +} +int aic3262_ops_reg_write(void *p,unsigned int reg,unsigned char mval) +{ + struct aic3262_priv *ps = p; + aic326x_reg_union mreg; + cfw_register *c = (cfw_register *) ® + + mreg.aic326x_register.offset = c->offset; + mreg.aic326x_register.page = c->page; + mreg.aic326x_register.book = c->book; + mreg.aic326x_register.reserved = 0; + mval = c->data; + DBG("reg_write:page %d book %d offset %d mval : %#x\n",mreg.aic326x_register.page,mreg.aic326x_register.book,mreg.aic326x_register.offset,mval); + + return aic3262_reg_write(ps->codec->control_data, mreg.aic326x_register_int, mval); +} + +int aic3262_ops_set_bits(void *p,unsigned int reg, + unsigned char mask, unsigned char val) +{ + struct aic3262_priv *ps = p; + + aic326x_reg_union mreg; + cfw_register *c = (cfw_register *) ® + mreg.aic326x_register.offset = c->offset; + mreg.aic326x_register.page = c->page; + mreg.aic326x_register.book = c->book; + mreg.aic326x_register.reserved = 0; + DBG("set_bits:page %d book %d offset %d mask %#x val %#x\n",mreg.aic326x_register.page,mreg.aic326x_register.book,mreg.aic326x_register.offset,mask,val); + + return aic3262_set_bits(ps->codec->control_data,mreg.aic326x_register_int,mask,val); + +} +int aic3262_ops_bulk_read(void *p,unsigned int reg,int count, u8 *buf) +{ + struct aic3262_priv *ps = p; + + aic326x_reg_union mreg; + cfw_register *c = (cfw_register *) ® + mreg.aic326x_register.offset = c->offset; + mreg.aic326x_register.page = c->page; + mreg.aic326x_register.book = c->book; + mreg.aic326x_register.reserved = 0; + + return aic3262_bulk_read(ps->codec->control_data,mreg.aic326x_register_int,count,buf); +} + +int aic3262_ops_bulk_write(void *p,unsigned int reg ,int count, const u8 *buf) +{ + struct aic3262_priv *ps = p; + aic326x_reg_union mreg; + cfw_register *c = (cfw_register *) ® + + mreg.aic326x_register.offset = c->offset; + mreg.aic326x_register.page = c->page; + mreg.aic326x_register.book = c->book; + mreg.aic326x_register.reserved = 0; + DBG("bulk_write: ncmd %d page %d book %d offset %d data[0] %d\n",count,mreg.aic326x_register.page,mreg.aic326x_register.book,mreg.aic326x_register.offset,buf[0]); + + aic3262_bulk_write(ps->codec->control_data,mreg.aic326x_register_int,count,buf); + return 0; +} +/***************************************************************************** + Function Name : aic3262_ops_dlock_lock +Argument : pointer argument to the codec +Return value : Integer +Purpose : To Read the run state of the DAC and ADC +by reading the codec and returning the run state + +Run state Bit format + +------------------------------------------------------ +D31|..........| D7 | D6| D5 | D4 | D3 | D2 | D1 | D0 | +R R R LADC RADC R R LDAC RDAC +------------------------------------------------------ + + *******************************************************************************/ + +int aic3262_ops_lock(void *pv) +{ + int run_state=0; + struct aic3262_priv *aic3262 = (struct aic3262_priv *) pv; + mutex_lock(&aic3262->codec->mutex); + + /* Reading the run state of adc and dac */ + run_state = get_runstate(aic3262->codec->control_data); + + return run_state; +} +/******************************************************************************** + Function name : aic3262_ops_dlock_unlock +Argument : pointer argument to the codec +Return Value : integer returning 0 +Purpose : To unlock the mutex acqiured for reading +run state of the codec + *********************************************************************************/ +int aic3262_ops_unlock(void *pv) +{ + /*Releasing the lock of mutex */ + struct aic3262_priv *aic3262 = (struct aic3262_priv *) pv; + + mutex_unlock(&aic3262->codec->mutex); + return 0; +} +/********************************************************************************* + Function Name : aic3262_ops_dlock_stop +Argument : pointer Argument to the codec +mask tells us the bit format of the +codec running state + +Bit Format: +------------------------------------------------------ +D31|..........| D7 | D6| D5 | D4 | D3 | D2 | D1 | D0 | +R R R AL AR R R DL DR +------------------------------------------------------ +R - Reserved +A - minidsp_A +D - minidsp_D + ***********************************************************************************/ + + + +int aic3262_ops_stop (void *pv, int mask) +{ + int run_state=0; + int cur_state=0; + int count = 100; + struct aic3262_priv *aic3262 = (struct aic3262_priv *) pv; + int limask=0; + + mutex_lock(&aic3262->codec->mutex); + run_state = get_runstate(aic3262->codec->control_data); + + if ( ( limask=(mask & AIC3XX_COPS_MDSP_A) ) ) + { + if((limask & 0x30) == 0x30) //Both ADC's needs to switchoff + aic3262_set_bits(aic3262->codec->control_data,AIC3262_ADC_DATAPATH_SETUP,0xC0,0); + else + { + if(limask & 0x10) //Right ADC is on, we need to switchoff + aic3262_set_bits(aic3262->codec->control_data,AIC3262_ADC_DATAPATH_SETUP,0x40,0); + + if(limask & 0x20) // Left ADC is on , we need to switchoff + aic3262_set_bits(aic3262->codec->control_data,AIC3262_ADC_DATAPATH_SETUP,0x80,0); + } + + } + + if( ( limask= (mask & AIC3XX_COPS_MDSP_D) ) ) + { + if( (limask & 0x03)== 0x03 ) + { + aic3262_set_bits(aic3262->codec->control_data,AIC3262_DAC_DATAPATH_SETUP,0xC0,0); + } + else + { + if(limask & 0x01) + aic3262_set_bits(aic3262->codec->control_data,AIC3262_DAC_DATAPATH_SETUP,0x40,0); + + if(limask & 0x02) + aic3262_set_bits(aic3262->codec->control_data,AIC3262_DAC_DATAPATH_SETUP,0x80,0); + } + } + // waiting for write to complete + + do + { + cur_state = get_runstate(aic3262->codec->control_data); + count--; + }while( ( (cur_state&mask&AIC3XX_COPS_MDSP_A) || (cur_state&mask&AIC3XX_COPS_MDSP_D) ) && count ); + + return run_state; + +} +/***************************************************************************** + Function name : aic3262_ops_dlock_restore +Argument : pointer argument to the codec,run_state +Return Value : integer returning 0 +Purpose : To unlock the mutex acqiured for reading +run state of the codec and to restore the states of the dsp + *******************************************************************************/ +int aic3262_ops_restore(void *pv, int run_state) +{ + int sync_state = 0; + int li_cur_state = 0; + struct aic3262_priv *aic3262 = (struct aic3262_priv *)pv; + /* This is for read the sync mode register state */ + sync_state = SYNC_STATE(aic3262); + /* Reading the current dsp state */ + li_cur_state = get_runstate(aic3262->codec->control_data); + + + //checking whether the sync mode has been set or not and checking the current state + if( ((run_state & 0x30) && (run_state & 0x03)) && (sync_state & 0x80) ) + aic3262_restart_dsps_sync(pv,run_state); + else + aic3262_dsp_pwrup(pv,run_state); + + + mutex_unlock(&aic3262->codec->mutex); + + return 0; +} + +/***************************************************************************** + Function name : aic3262_ops_adaptivebuffer_swap +Argument : pointer argument to the codec,mask tells us which dsp has to +be chosen for swapping +Return Value : integer returning 0 +Purpose : To swap the coefficient buffers of minidsp according to mask + *******************************************************************************/ + +int aic3262_ops_adaptivebuffer_swap(void *pv,int mask) +{ + int read_state=0; + struct aic3262_priv *aic3262 = (struct aic3262_priv *) pv; + + if(mask & AIC3XX_ABUF_MDSP_A) + { + + int count=0; + aic3262_set_bits(aic3262->codec->control_data,AIC3262_ADC_ADAPTIVE_CRAM_REG,0x1,0x1); + + do + { + read_state = aic3262_reg_read(aic3262->codec->control_data,AIC3262_ADC_ADAPTIVE_CRAM_REG); + count++; + }while( (read_state & 0x1) && (count <= 60) ); + } + + if(mask & AIC3XX_ABUF_MDSP_D1) + { + + int count=0; + + aic3262_set_bits(aic3262->codec->control_data,AIC3262_DAC_ADAPTIVE_BANK1_REG,0x1,0x1); + do + { + read_state = aic3262_reg_read(aic3262->codec->control_data, AIC3262_DAC_ADAPTIVE_BANK1_REG ); + count++; + }while( (read_state & 0x1) && (count <= 60) ); + } + + if(mask & AIC3XX_ABUF_MDSP_D2) + { + + int count=0; + aic3262_set_bits(aic3262->codec->control_data,AIC3262_DAC_ADAPTIVE_BANK2_REG,0x1,0x1); + do + { + read_state = aic3262_reg_read(aic3262->codec->control_data,AIC3262_DAC_ADAPTIVE_BANK2_REG); + count++; + }while( (read_state & 0x1) && (count <= 60) ); + } + + return 0; +} +/***************************************************************************** + Function name : get_runstate +Argument : pointer argument to the codec +Return Value : integer returning the runstate +Purpose : To read the current state of the dac's and adc's + *******************************************************************************/ + +int get_runstate(void *ps) +{ + struct aic3262 *pr = ps; + int run_state=0; + int DAC_state=0,ADC_state=0; + /* Read the run state */ + DAC_state = aic3262_reg_read(pr,AIC3262_DAC_FLAG); + ADC_state = aic3262_reg_read(pr,AIC3262_ADC_FLAG); + + DSP_STATUS(run_state,ADC_state,6,5); + DSP_STATUS(run_state,ADC_state,2,4); + DSP_STATUS(run_state,DAC_state,7,1); + DSP_STATUS(run_state,DAC_state,3,0); + + return run_state; + +} +/***************************************************************************** + Function name : aic3262_dsp_pwrdwn_status +Argument : pointer argument to the codec , cur_state of dac's and adc's +Return Value : integer returning 0 +Purpose : To read the status of dsp's + *******************************************************************************/ + +int aic3262_dsp_pwrdwn_status( + void *pv //ptr to the priv data structure + ) +{ + struct aic3262_priv *aic3262 = pv; + int count = 100; + int cur_state = 0; + + aic3262_set_bits(aic3262->codec->control_data,AIC3262_ADC_DATAPATH_SETUP,0XC0,0); + aic3262_set_bits(aic3262->codec->control_data,AIC3262_DAC_DATAPATH_SETUP,0XC0,0); + + do + { + cur_state = get_runstate(aic3262->codec->control_data); + }while(cur_state && count--); + + + return 0; +} +int aic3262_dsp_pwrup(void *pv,int state) +{ + int cur_state = 0; + int count =100; + struct aic3262_priv *aic3262 = (struct aic3262_priv *)pv; + + if(state & AIC3262_COPS_MDSP_A) + aic3262_set_bits(aic3262->codec->control_data,AIC3262_ADC_DATAPATH_SETUP,0XC0,0xC0); + else + { + if(state & AIC3262_COPS_MDSP_A_L) + aic3262_set_bits(aic3262->codec->control_data,AIC3262_ADC_DATAPATH_SETUP,0x80,0x80); + if(state & AIC3262_COPS_MDSP_A_R) + aic3262_set_bits(aic3262->codec->control_data,AIC3262_ADC_DATAPATH_SETUP,0x40,0x40); + } + + + if(state & AIC3262_COPS_MDSP_D) + aic3262_set_bits(aic3262->codec->control_data,AIC3262_DAC_DATAPATH_SETUP,0XC0,0xC0); + else + { + if(state & AIC3262_COPS_MDSP_D_L) + aic3262_set_bits(aic3262->codec->control_data,AIC3262_DAC_DATAPATH_SETUP,0x80,0x80); + if(state & AIC3262_COPS_MDSP_D_R) + aic3262_set_bits(aic3262->codec->control_data,AIC3262_DAC_DATAPATH_SETUP,0x40,0x40); + } + + //loop for waiting for the dsps to power up together + do + { + cur_state = get_runstate(aic3262->codec->control_data); + }while ((state == cur_state ) && count--); + + return 0; +} + +int aic3262_restart_dsps_sync(void *pv,int run_state) +{ + + aic3262_dsp_pwrdwn_status(pv); + //specific command sequence to be added later + /* + .................... + .................... + .................... + */ + + aic3262_dsp_pwrup(pv,run_state); + + //specific commands to be added later + /* + .................... + .................... + .................... + */ + return 0; +} + +const aic3xxx_codec_ops aic3262_cfw_codec_ops = { + .reg_read = aic3262_ops_reg_read, + .reg_write = aic3262_ops_reg_write, + .set_bits = aic3262_ops_set_bits, + .bulk_read = aic3262_ops_bulk_read, + .bulk_write = aic3262_ops_bulk_write, + .lock = aic3262_ops_lock, + .unlock = aic3262_ops_unlock, + .stop = aic3262_ops_stop , + .restore = aic3262_ops_restore, + .bswap = aic3262_ops_adaptivebuffer_swap, +}; + + diff --git a/sound/soc/codecs/aic3262_codec_ops.h b/sound/soc/codecs/aic3262_codec_ops.h new file mode 100644 index 000000000000..54d835b33adc --- /dev/null +++ b/sound/soc/codecs/aic3262_codec_ops.h @@ -0,0 +1,42 @@ +#define SYNC_STATE(p) aic3262_reg_read(p->codec->control_data,AIC3262_DAC_PRB) + +#define AIC3262_COPS_MDSP_A 0x30 +#define AIC3262_COPS_MDSP_A_L 0x20 +#define AIC3262_COPS_MDSP_A_R 0x10 + + + +#define AIC3262_COPS_MDSP_D 0x03 +#define AIC3262_COPS_MDSP_D_L 0x02 +#define AIC3262_COPS_MDSP_D_R 0x01 + + +int get_runstate(void *); + +int aic3262_dsp_pwrup(void *,int); + +int aic3262_pwr_down(void *,int ,int ,int ,int); + +int aic3262_dsp_pwrdwn_status(void *); + +int aic3262_ops_reg_read(void *p,unsigned int reg); + +int aic3262_ops_reg_write(void *p,unsigned int reg,unsigned char mval); + +int aic3262_ops_set_bits(void *p,unsigned int reg,unsigned char mask, unsigned char val); + +int aic3262_ops_bulk_read(void *p,unsigned int reg,int count, u8 *buf); + +int aic3262_ops_bulk_write(void *p, unsigned int reg,int count, const u8 *buf); + +int aic3262_ops_lock(void *pv); + +int aic3262_ops_unlock(void *pv); + +int aic3262_ops_stop (void *pv, int mask); + +int aic3262_ops_restore(void *pv, int run_state); + +int aic3262_ops_adaptivebuffer_swap(void *pv,int mask); + +int aic3262_restart_dsps_sync(void *pv,int run_state); diff --git a/sound/soc/codecs/aic326x_tiload.c b/sound/soc/codecs/aic326x_tiload.c new file mode 100644 index 000000000000..df1581e2a1f4 --- /dev/null +++ b/sound/soc/codecs/aic326x_tiload.c @@ -0,0 +1,349 @@ +/* + * linux/sound/soc/codecs/AIC3262_tiload.c + * + * + * Copyright (C) 2010 Texas Instruments, Inc. + * + * + * + * This package 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 PACKAGE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED + * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. + * + * History: + * + * Rev 0.1 Tiload support TI 16-09-2010 + * + * The Tiload programming support is added to AIC3262. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include "tlv320aic326x.h" +#include "aic326x_tiload.h" + +/* enable debug prints in the driver */ +//#define DEBUG +#undef DEBUG + +#ifdef DEBUG +#define dprintk(x...) printk(x) +#else +#define dprintk(x...) +#endif + +#ifdef AIC3262_TiLoad + +/* Function prototypes */ +#ifdef REG_DUMP_aic3262 +static void aic3262_dump_page(struct i2c_client *i2c, u8 page); +#endif + +/* externs */ +/*extern int aic3262_change_page(struct snd_soc_codec *codec, u8 new_page); +extern int aic3262_change_book(struct snd_soc_codec *codec, u8 new_book);*/ +extern int aic3262_write(struct snd_soc_codec *codec, unsigned int reg, + unsigned int value); + +int aic3262_driver_init(struct snd_soc_codec *codec); +/************** Dynamic aic3262 driver, TI LOAD support ***************/ + +static struct cdev *aic3262_cdev; +static aic326x_reg_union aic_reg; +static int aic3262_major = 0; /* Dynamic allocation of Mjr No. */ +static int aic3262_opened = 0; /* Dynamic allocation of Mjr No. */ +static struct snd_soc_codec *aic3262_codec; +struct class *tiload_class; +static unsigned int magic_num = 0xE0; + +/******************************** Debug section *****************************/ + +#ifdef REG_DUMP_aic3262 +/* + *---------------------------------------------------------------------------- + * Function : aic3262_dump_page + * Purpose : Read and display one codec register page, for debugging purpose + *---------------------------------------------------------------------------- + */ +static void aic3262_dump_page(struct i2c_client *i2c, u8 page) +{ + int i; + u8 data; + u8 test_page_array[8]; + + dprintk("TiLoad DRIVER : %s\n", __FUNCTION__); +// aic3262_change_page(codec, page); + + data = 0x0; + + i2c_master_send(i2c, data, 1); + i2c_master_recv(i2c, test_page_array, 8); + + dprintk("\n------- aic3262 PAGE %d DUMP --------\n", page); + for (i = 0; i < 8; i++) { + printk(" [ %d ] = 0x%x\n", i, test_page_array[i]); + } +} +#endif + +/* + *---------------------------------------------------------------------------- + * Function : tiload_open + * + * Purpose : open method for aic3262-tiload programming interface + *---------------------------------------------------------------------------- + */ +static int tiload_open(struct inode *in, struct file *filp) +{ + dprintk("TiLoad DRIVER : %s\n", __FUNCTION__); + if (aic3262_opened) { + dprintk("%s device is already opened\n", "aic3262"); + dprintk("%s: only one instance of driver is allowed\n", + "aic3262"); + return -1; + } + aic3262_opened++; + return 0; +} + +/* + *---------------------------------------------------------------------------- + * Function : tiload_release + * + * Purpose : close method for aic3262_tilaod programming interface + *---------------------------------------------------------------------------- + */ +static int tiload_release(struct inode *in, struct file *filp) +{ + dprintk("TiLoad DRIVER : %s\n", __FUNCTION__); + aic3262_opened--; + return 0; +} + +/* + *---------------------------------------------------------------------------- + * Function : tiload_read + * + * Purpose : read method for mini dsp programming interface + *---------------------------------------------------------------------------- + */ +static ssize_t tiload_read(struct file *file, char __user * buf, + size_t count, loff_t * offset) +{ + static char rd_data[8]; + char reg_addr; + size_t size; + #ifdef DEBUG + int i; + #endif + struct aic3262 *control = aic3262_codec->control_data; + + dprintk("TiLoad DRIVER : %s\n", __FUNCTION__); + if (count > 128) { + printk("Max 128 bytes can be read\n"); + count = 128; + } + + /* copy register address from user space */ + size = copy_from_user(®_addr, buf, 1); + if (size != 0) { + printk("read: copy_from_user failure\n"); + return -1; + } + /* Send the address to device thats is to be read */ + + aic_reg.aic326x_register.offset = reg_addr; + size = aic3262_bulk_read(control, aic_reg.aic326x_register_int, count, rd_data); +/* + if (i2c_master_send(i2c, ®_addr, 1) != 1) { + dprintk("Can not write register address\n"); + return -1; + } + size = i2c_master_recv(i2c, rd_data, count); +*/ +#ifdef DEBUG + printk(KERN_ERR "read size = %d, reg_addr= %x , count = %d\n", + (int)size, reg_addr, (int)count); + for (i = 0; i < (int)size; i++) { + dprintk(KERN_ERR "rd_data[%d]=%x\n", i, rd_data[i]); + } +#endif + if (size != count) { + dprintk("read %d registers from the codec\n", size); + } + + if (copy_to_user(buf, rd_data, size) != 0) { + dprintk("copy_to_user failed\n"); + return -1; + } + + return size; +} + +/* + *---------------------------------------------------------------------------- + * Function : tiload_write + * + * Purpose : write method for aic3262_tiload programming interface + *---------------------------------------------------------------------------- + */ +static ssize_t tiload_write(struct file *file, const char __user * buf, + size_t count, loff_t * offset) +{ + static char wr_data[8]; + u8 pg_no; + unsigned int reg; + #ifdef DEBUG + int i; + #endif + struct aic3262 *control = aic3262_codec->control_data; + + dprintk("TiLoad DRIVER : %s\n", __FUNCTION__); + /* copy buffer from user space */ + if (copy_from_user(wr_data, buf, count)) { + printk("copy_from_user failure\n"); + return -1; + } +#ifdef DEBUG + dprintk(KERN_ERR "write size = %d\n", (int)count); + for (i = 0; i < (int)count; i++) { + printk(KERN_INFO "\nwr_data[%d]=%x\n", i, wr_data[i]); + } +#endif + if(wr_data[0] == 0) + { + //change of page seen, but will only be registered + aic_reg.aic326x_register.page = wr_data[1]; + return count;// trick + + } + else + if(wr_data[0] == 127 /* && aic_reg.aic326x_register.page == 0*/) + { + //change of book seen, but will not be sent for I2C write + aic_reg.aic326x_register.book = wr_data[1]; + return count; //trick + + } + else + { + aic_reg.aic326x_register.offset = wr_data[0]; + aic3262_bulk_write(control, aic_reg.aic326x_register_int, count - 1,&wr_data[1]); + return count; + } +/* if (wr_data[0] == 0) { + aic3262_change_page(aic3262_codec, wr_data[1]); + return count; + } + pg_no = aic3262_private->page_no; + + if ((wr_data[0] == 127) && (pg_no == 0)) { + aic3262_change_book(aic3262_codec, wr_data[1]); + return count; + } + return i2c_master_send(i2c, wr_data, count);*/ + +} + +static int tiload_ioctl(struct file *filp, + unsigned int cmd, unsigned long arg) +{ + int num = 0; + void __user *argp = (void __user *)arg; + if (_IOC_TYPE(cmd) != aic3262_IOC_MAGIC) + return -ENOTTY; + + dprintk("TiLoad DRIVER : %s\n", __FUNCTION__); + switch (cmd) { + case aic3262_IOMAGICNUM_GET: + num = copy_to_user(argp, &magic_num, sizeof(int)); + break; + case aic3262_IOMAGICNUM_SET: + num = copy_from_user(&magic_num, argp, sizeof(int)); + break; + } + return num; +} + +/*********** File operations structure for aic3262-tiload programming *************/ +static struct file_operations aic3262_fops = { + .owner = THIS_MODULE, + .open = tiload_open, + .release = tiload_release, + .read = tiload_read, + .write = tiload_write, + .unlocked_ioctl = tiload_ioctl, +}; + +/* + *---------------------------------------------------------------------------- + * Function : aic3262_driver_init + * + * Purpose : Register a char driver for dynamic aic3262-tiload programming + *---------------------------------------------------------------------------- + */ +int aic3262_driver_init(struct snd_soc_codec *codec) +{ + int result; + + dev_t dev = MKDEV(aic3262_major, 0); + dprintk("TiLoad DRIVER : %s\n", __FUNCTION__); + aic3262_codec = codec; + + dprintk("allocating dynamic major number\n"); + + result = alloc_chrdev_region(&dev, 0, 1, DEVICE_NAME); + if (result < 0) { + dprintk("cannot allocate major number %d\n", aic3262_major); + return result; + } + + tiload_class = class_create(THIS_MODULE, DEVICE_NAME); + aic3262_major = MAJOR(dev); + dprintk("allocated Major Number: %d\n", aic3262_major); + + aic3262_cdev = cdev_alloc(); + cdev_init(aic3262_cdev, &aic3262_fops); + aic3262_cdev->owner = THIS_MODULE; + aic3262_cdev->ops = &aic3262_fops; + + aic_reg.aic326x_register.page = 0; + aic_reg.aic326x_register.book = 0; + + if (cdev_add(aic3262_cdev, dev, 1) < 0) { + dprintk("aic3262_driver: cdev_add failed \n"); + unregister_chrdev_region(dev, 1); + aic3262_cdev = NULL; + return 1; + } + dprintk("Registered aic3262 TiLoad driver, Major number: %d \n", + aic3262_major); + //class_device_create(tiload_class, NULL, dev, NULL, DEVICE_NAME, 0); + return 0; +} + +#endif diff --git a/sound/soc/codecs/aic326x_tiload.h b/sound/soc/codecs/aic326x_tiload.h new file mode 100644 index 000000000000..747bb200c29e --- /dev/null +++ b/sound/soc/codecs/aic326x_tiload.h @@ -0,0 +1,36 @@ +/* + * linux/sound/soc/codecs/aic3262_tiload.h + * + * + * Copyright (C) 2011 Texas Instruments Inc. + * + * + * + * This package 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 PACKAGE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED + * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. + * + * History: + * + * + * + * + */ + +#ifndef _AIC3262_TILOAD_H +#define _AIC3262_TILOAD_H + +/* typedefs required for the included header files */ +typedef char *string; + +/* defines */ +#define DEVICE_NAME "tiload_node" +#define aic3262_IOC_MAGIC 0xE0 +#define aic3262_IOMAGICNUM_GET _IOR(aic3262_IOC_MAGIC, 1, int) +#define aic3262_IOMAGICNUM_SET _IOW(aic3262_IOC_MAGIC, 2, int) + +#endif diff --git a/sound/soc/codecs/aic3xxx_cfw.h b/sound/soc/codecs/aic3xxx_cfw.h new file mode 100644 index 000000000000..7d9a6fbdbd42 --- /dev/null +++ b/sound/soc/codecs/aic3xxx_cfw.h @@ -0,0 +1,425 @@ +/** + * \file Codec Firmware Declarations + */ + +#ifndef CFW_FIRMWARE_H_ +#define CFW_FIRMWARE_H_ + +/** \defgroup bt Basic Types */ +/* @{ */ +#ifndef AIC3XXX_CFW_HOST_BLD +#include +#else +typedef unsigned char u8; +typedef unsigned short int u16; +typedef unsigned long int u32; +#endif +typedef signed char i8; +typedef signed short int i16; +typedef signed long int i32; + +#define CFW_FW_MAGIC 0xC0D1F1ED +/* @} */ + + +/** \defgroup pd Arbitrary Limitations */ +/* @{ */ +#ifndef CFW_MAX_ID +# define CFW_MAX_ID (64) /// +# include +# define warn(fmt, ...) printk(fmt "\n", ##__VA_ARGS__) +# define error(fmt, ...) printk(fmt "\n", ##__VA_ARGS__) +#else +# define _GNU_SOURCE +# include +# include "utils.h" +# include +#endif + +#include "aic3xxx_cfw.h" +#include "aic3xxx_cfw_ops.h" + + +/* + * Firmware version numbers are used to make sure that the + * host and target code stay in sync. It is _not_ recommended + * to provide this number from the outside (E.g., from a makefile) + * Instead, a set of automated tools are relied upon to keep the numbers + * in sync at the time of host testing. + */ +#define CFW_FW_VERSION 0x000100AF + + +static int aic3xxx_cfw_dlimage(void *pv, cfw_image *pim); +static int aic3xxx_cfw_dlcfg(void *pv, cfw_image *pim); +static void aic3xxx_cfw_dlcmds(void *pv, cfw_block *pb); +cfw_meta_register *aic3xxx_read_meta(cfw_block *pb, int i); +void aic3xxx_wait(void *p, unsigned int reg, u8 mask, u8 data); +static cfw_project *aic3xxx_cfw_unpickle(void *p, int n); + +#if defined(AIC3XXX_CFW_HOST_BLD) +// Host test only... +extern const aic3xxx_codec_ops dummy_codec_ops; +extern void *dummy_codec_ops_obj; + +void mdelay(int val) +{ + int i; + for (i=0; iops = &dummy_codec_ops; + ps->ops_obj = dummy_codec_ops_obj; + aic3xxx_cfw_reload(ps, pcfw, n); + return ps; +} +void *aic3xxx_cfw_getpjt(void *ps) +{ + return ((cfw_state *)ps)->pjt; +} +// ...till here +#endif + +int aic3xxx_cfw_reload(void *pv, void *pcfw, int n) +{ + cfw_state *ps = pv; + ps->pjt = aic3xxx_cfw_unpickle(pcfw, n); + ps->cur_mode = + ps->cur_pfw = + ps->cur_ovly = + ps->cur_cfg = -1; + return 0; +} +int aic3xxx_cfw_setmode(void *pv, int mode) +{ + cfw_state *ps = pv; + cfw_project *pjt = ps->pjt; + return aic3xxx_cfw_setmode_cfg(pv, mode, pjt->mode[mode]->cfg); +} +int aic3xxx_cfw_setcfg(void *pv, int cfg) +{ + cfw_state *ps = pv; + cfw_project *pjt = ps->pjt; + cfw_pfw *pfw; + if (ps->cur_pfw >= pjt->npfw) + return -1; // Non miniDSP + if (!(pjt->mode[ps->cur_mode]->supported_cfgs&(1 << cfg))) + return -1; + if (ps->cur_cfg == cfg) + return 0; + pfw = pjt->pfw[ps->cur_pfw]; + ps->cur_cfg = cfg; + return aic3xxx_cfw_dlcfg(pv, pfw->ovly_cfg[ps->cur_ovly][ps->cur_cfg]); +} +int aic3xxx_cfw_setmode_cfg(void *pv, int mode, int cfg) +{ + cfw_state *ps = pv; + cfw_project *pjt = ps->pjt; + int which = 0; + + if (mode >= pjt->nmode) + return -1; + if (pjt->mode[mode]->pfw < pjt->npfw) { // New mode uses miniDSP + // FIXME: Add support for entry and exit sequences + cfw_pfw *pfw = pjt->pfw[pjt->mode[mode]->pfw]; + // Make sure cfg is valid and supported in this mode + if (cfg >= pfw->ncfg || + !(pjt->mode[mode]->supported_cfgs&(1u<base; + if (im->block[CFW_BLOCK_A_INST]) + which |= AIC3XX_COPS_MDSP_A; + if (im->block[CFW_BLOCK_D_INST]) + which |= AIC3XX_COPS_MDSP_D; + + if (pjt->mode[mode]->pfw != ps->cur_pfw) { // New mode requires different PFW + ps->cur_pfw = pjt->mode[mode]->pfw; + ps->cur_ovly = 0; + ps->cur_cfg = 0; + + which = ps->ops->stop(ps->ops_obj, which); + aic3xxx_cfw_dlimage(pv, im); + if (pjt->mode[mode]->ovly && pjt->mode[mode]->ovly < pfw->novly) { + // New mode uses ovly + aic3xxx_cfw_dlimage(pv, pfw->ovly_cfg[pjt->mode[mode]->ovly][cfg]); + } else if (cfg) { + // new mode needs only a cfg change + aic3xxx_cfw_dlimage(pv, pfw->ovly_cfg[0][cfg]); + } + ps->ops->restore(ps->ops_obj, which); + + } else if (pjt->mode[mode]->ovly != ps->cur_ovly) { + // New mode requires only an ovly change + which = ps->ops->stop(ps->ops_obj, which); + aic3xxx_cfw_dlimage(pv, pfw->ovly_cfg[pjt->mode[mode]->ovly][cfg]); + ps->ops->restore(ps->ops_obj, which); + } else if (cfg != ps->cur_cfg) { // New mode requires only a cfg change + aic3xxx_cfw_dlcfg(pv, pfw->ovly_cfg[pjt->mode[mode]->ovly][cfg]); + } + ps->cur_ovly = pjt->mode[mode]->ovly; + ps->cur_cfg = cfg; + + // FIXME: Update PLL settings if present + // FIXME: This is hack and needs to go one way or another + ps->cur_mode = mode; + aic3xxx_cfw_set_pll(pv, 0); + + } else if (pjt->mode[mode]->pfw == 0xFF) { // Bypass mode + // FIXME + } else { // Error + warn("Bad pfw setting detected (%d). Max pfw=%d", pjt->mode[mode]->pfw, + pjt->npfw); + } + ps->cur_mode = mode; + warn("setmode_cfg: DONE (mode=%d pfw=%d ovly=%d cfg=%d)", ps->cur_mode, ps->cur_pfw, ps->cur_ovly, ps->cur_cfg); + return 0; +} +int aic3xxx_cfw_transition(void *pv, char *ttype) +{ + cfw_state *ps = pv; + int i; + for (i = 0; i < CFW_TRN_N; ++i) { + if (!strcasecmp(ttype, cfw_transition_id[i])) { + if (ps->pjt->transition[i]) { + DBG("Sending transition %s[%d]", ttype, i); + aic3xxx_cfw_dlcmds(pv, ps->pjt->transition[i]->block); + } + return 0; + } + } + warn("Transition %s not present or invalid", ttype); + return 0; +} +int aic3xxx_cfw_set_pll(void *pv, int asi) +{ + // FIXME: no error checks!! + cfw_state *ps = pv; + cfw_project *pjt = ps->pjt; + cfw_pfw *pfw = pjt->pfw[pjt->mode[ps->cur_mode]->pfw]; + if (pfw->pll) { + warn("Configuring PLL for ASI%d using PFW%d", asi, + pjt->mode[ps->cur_mode]->pfw); + aic3xxx_cfw_dlcmds(pv, pfw->pll); + } + return 0; +} +static void aic3xxx_cfw_dlcmds(void *pv, cfw_block *pb) +{ + cfw_state *ps = pv; + int i = 0, lock = 0; + + while (i < pb->ncmds) { + if (CFW_BLOCK_BURSTS(pb->type)) + ps->ops->bulk_write(ps->ops_obj, pb->cmd[i].burst->reg.bpod, + pb->cmd[i].burst->length, + pb->cmd[i].burst->data); + else { + cfw_meta_delay d = pb->cmd[i].reg.meta.delay; + cfw_meta_bitop b = pb->cmd[i].reg.meta.bitop; + switch (pb->cmd[i].reg.meta.mcmd) { + case CFW_META_DELAY: + mdelay(d.delay); + break; + case CFW_META_UPDTBITS: + ps->ops->set_bits(ps->ops_obj, pb->cmd[i+1].reg.bpod, + b.mask, pb->cmd[i+1].reg.data); + i++; + break; + case CFW_META_WAITBITS: + aic3xxx_wait(ps, pb->cmd[i+1].reg.bpod, + b.mask, pb->cmd[i+1].reg.data); + i++; + break; + case CFW_META_LOCK: + if (d.delay) { + ps->ops->lock(ps->ops_obj); + lock = 1; + } else { + if (!lock) + error("Attempt to unlock without first locking"); + ps->ops->unlock(ps->ops_obj); + lock = 0; + } + break; + default: + ps->ops->reg_write(ps->ops_obj, pb->cmd[i].reg.bpod, pb->cmd[i].reg.data); + } + } + ++i; + } + if (lock) + error("exiting blkcmds with lock ON"); +} + +void aic3xxx_wait(void *p, unsigned int reg, u8 mask, u8 data) +{ + cfw_state *ps = p; + while ((ps->ops->reg_read(ps->ops_obj, reg)&mask) != data) + mdelay(2); +} + +static int aic3xxx_cfw_dlcfg(void *pv, cfw_image *pim) +{ + cfw_state *ps = pv; + int i,run_state,swap; + struct { + u32 mdsp; + int buf_a, buf_b; + u32 swap; + } csecs[] = { + { + .mdsp=AIC3XX_COPS_MDSP_A, + .swap=AIC3XX_ABUF_MDSP_A, + .buf_a=CFW_BLOCK_A_A_COEF, + .buf_b=CFW_BLOCK_A_B_COEF + }, + { + .mdsp=AIC3XX_COPS_MDSP_D, + .swap=AIC3XX_ABUF_MDSP_D1, + .buf_a=CFW_BLOCK_D_A1_COEF, + .buf_b=CFW_BLOCK_D_B1_COEF + }, + { + .mdsp=AIC3XX_COPS_MDSP_D, + .swap=AIC3XX_ABUF_MDSP_D2, + .buf_a=CFW_BLOCK_D_A2_COEF, + .buf_b=CFW_BLOCK_D_B2_COEF + }, + }; + DBG("Download CFG %s", pim->name); + run_state = ps->ops->lock(ps->ops_obj); + swap = 0; + for (i = 0; i < sizeof(csecs)/sizeof(csecs[0]); ++i) { + if (pim->block[csecs[i].buf_a]) { + if (run_state & csecs[i].mdsp) { + aic3xxx_cfw_dlcmds(pv, pim->block[csecs[i].buf_a]); + swap |= csecs[i].swap; + aic3xxx_cfw_dlcmds(pv, pim->block[csecs[i].buf_b]); + } else { + aic3xxx_cfw_dlcmds(pv, pim->block[csecs[i].buf_a]); + aic3xxx_cfw_dlcmds(pv, pim->block[csecs[i].buf_b]); + } + } + } + if (swap) // Check needed? FIXME + ps->ops->bswap(ps->ops_obj, swap); + ps->ops->unlock(ps->ops_obj); + return 0; +} +static int aic3xxx_cfw_dlimage(void *pv, cfw_image *pim) +{ + //cfw_state *ps = pv; + int i; + DBG("Download IMAGE %s", pim->name); + for (i = 0; i < CFW_BLOCK_N; ++i) + if (pim->block[i]) + aic3xxx_cfw_dlcmds(pv, pim->block[i]); + return 0; +} + +# define FW_NDX2PTR(x, b) do { \ + x = (void *)((u8 *)(b) + ((int)(x))); \ + } while (0) +static void aic3xxx_cfw_unpickle_block(cfw_block *pb, void *p) +{ + int i; + if (CFW_BLOCK_BURSTS(pb->type)) { + for (i = 0; i < pb->ncmds; ++i) { + FW_NDX2PTR(pb->cmd[i].burst, p); + } + } +} +static void aic3xxx_cfw_unpickle_image(cfw_image *im, void *p) +{ + int i; + for (i = 0; i < CFW_BLOCK_N; ++i) + if (im->block[i]) { + FW_NDX2PTR(im->block[i], p); + aic3xxx_cfw_unpickle_block(im->block[i], p); + } +} +#ifndef AIC3XXX_CFW_HOST_BLD +static +#endif +unsigned int crc32(unsigned int *pdata, int n) +{ + u32 crc = 0, i, crc_poly = 0x04C11DB7; /* CRC - 32 */ + u32 msb; + u32 residue_value; + int bits; + + for (i = 0; i < (n >> 2); i++) { + bits = 32; + while (--bits >= 0) { + msb = crc & 0x80000000; + crc = (crc << 1) ^ ((*pdata >> bits) & 1); + if (msb) + crc = crc ^ crc_poly; + } + pdata++; + } + + switch (n & 3) { + case 0: + break; + case 1: + residue_value = (*pdata & 0xFF); + bits = 8; + break; + case 2: + residue_value = (*pdata & 0xFFFF); + bits = 16; + break; + case 3: + residue_value = (*pdata & 0xFFFFFF); + bits = 24; + break; + } + + if (n & 3) { + while (--bits >= 0) { + msb = crc & 0x80000000; + crc = (crc << 1) ^ ((residue_value >> bits) & 1); + if (msb) + crc = crc ^ crc_poly; + } + } + return (crc); +} +static int crc_chk(void *p, int n) +{ + cfw_project *pjt = (void *)p; + u32 crc = pjt->cksum, crc_comp; + pjt->cksum = 0; + DBG("Entering crc %d",n); + crc_comp = crc32(p, n); + if (crc_comp != crc) { + DBG("CRC mismatch 0x%08X != 0x%08X", crc, crc_comp); + return 0; // Dead code + } + DBG("CRC pass"); + pjt->cksum = crc; + return 1; +} +#ifndef AIC3XXX_CFW_HOST_BLD +static +#endif +cfw_project *aic3xxx_cfw_unpickle(void *p, int n) +{ + cfw_project *pjt = p; + int i, j, k; + if (pjt->magic != CFW_FW_MAGIC || + pjt->size != n || + pjt->bmagic != CFW_FW_VERSION || + !crc_chk(p, n)) { + error("magic:0x%08X!=0x%08X || size:%d!=%d || version:0x%08X!=0x%08X", + pjt->magic, CFW_FW_MAGIC, pjt->size, n, pjt->cksum, + CFW_FW_VERSION); + return NULL; + } + DBG("Loaded firmware inside unpickle\n"); + + for (i = 0; i < CFW_MAX_TRANSITIONS; i++) { + if (pjt->transition[i]) { + FW_NDX2PTR(pjt->transition[i], p); + FW_NDX2PTR(pjt->transition[i]->block, p); + aic3xxx_cfw_unpickle_block(pjt->transition[i]->block, p); + } + } + + for (i = 0; i < pjt->npfw; i++) { + DBG("loading pfw %d\n",i); + FW_NDX2PTR(pjt->pfw[i], p); + if (pjt->pfw[i]->base) { + FW_NDX2PTR(pjt->pfw[i]->base, p); + aic3xxx_cfw_unpickle_image(pjt->pfw[i]->base, p); + } + if (pjt->pfw[i]->pll) { + FW_NDX2PTR(pjt->pfw[i]->pll, p); + aic3xxx_cfw_unpickle_block(pjt->pfw[i]->pll, p); + } + for (j =0; j < pjt->pfw[i]->novly; ++j) + for (k =0; k < pjt->pfw[i]->ncfg; ++k) { + FW_NDX2PTR(pjt->pfw[i]->ovly_cfg[j][k], p); + aic3xxx_cfw_unpickle_image(pjt->pfw[i]->ovly_cfg[j][k], p); + } + } + + DBG("loaded pfw's\n"); + for (i = 0; i < pjt->nmode; i++) { + FW_NDX2PTR(pjt->mode[i], p); + if (pjt->mode[i]->entry) { + FW_NDX2PTR(pjt->mode[i]->entry, p); + aic3xxx_cfw_unpickle_block(pjt->mode[i]->entry, p); + } + if (pjt->mode[i]->exit) { + FW_NDX2PTR(pjt->mode[i]->exit, p); + aic3xxx_cfw_unpickle_block(pjt->mode[i]->exit, p); + } + } + DBG("loaded modes\n"); + return pjt; +} + + diff --git a/sound/soc/codecs/aic3xxx_cfw_ops.h b/sound/soc/codecs/aic3xxx_cfw_ops.h new file mode 100644 index 000000000000..82ca5f2a2527 --- /dev/null +++ b/sound/soc/codecs/aic3xxx_cfw_ops.h @@ -0,0 +1,69 @@ +#ifndef AIC3XXX_CFW_OPS_H_ +#define AIC3XXX_CFW_OPS_H_ + +#ifdef AIC3XXX_CFW_HOST_BLD +void *aic3xxx_cfw_init(void *pcfw, int n); +void *aic3xxx_cfw_getpjt(void *ps); +cfw_project *aic3xxx_cfw_unpickle(void *,int); +unsigned int crc32(unsigned int *pdata, int n); +#endif + +int aic3xxx_cfw_reload(void *pv, void *pcfw, int n); +int aic3xxx_cfw_setmode(void *pv, int mode); +int aic3xxx_cfw_setmode_cfg(void *pv, int mode, int cfg); +int aic3xxx_cfw_setcfg(void *pv, int cfg); +int aic3xxx_cfw_transition(void *pv, char *ttype); +int aic3xxx_cfw_set_pll(void *pv, int asi); + + +#define AIC3XX_COPS_MDSP_D (0x00000003u) +#define AIC3XX_COPS_MDSP_A (0x00000030u) +#define AIC3XX_COPS_MDSP_ALL (AIC3XX_COPS_MDSP_D|AIC3XX_COPS_MDSP_A) + +#define AIC3XX_ABUF_MDSP_D1 (0x00000001u) +#define AIC3XX_ABUF_MDSP_D2 (0x00000002u) +#define AIC3XX_ABUF_MDSP_A (0x00000010u) +#define AIC3XX_ABUF_MDSP_ALL \ + (AIC3XX_ABUF_MDSP_D1| AIC3XX_ABUF_MDSP_D2| AIC3XX_ABUF_MDSP_A) + +typedef struct aic3xxx_codec_ops { + int (*reg_read)(void *p, unsigned int reg); + int (*reg_write)(void *p, unsigned int reg, + unsigned int val); + int (*set_bits)(void *p, unsigned int reg, + unsigned char mask, unsigned char val); + int (*bulk_read)(void *p, unsigned int reg, + int count, u8 *buf); + int (*bulk_write)(void *p, unsigned int reg, + int count, const u8 *buf); + + int (*lock)(void *p); + int (*unlock)(void *p); + int (*stop)(void *p, int mask); + int (*restore)(void *p, int runstate); + int (*bswap)(void *p,int mask); +} aic3xxx_codec_ops; + +typedef struct cfw_state { + cfw_project *pjt; + const aic3xxx_codec_ops *ops; + void *ops_obj; + int cur_mode; + int cur_pfw; + int cur_ovly; + int cur_cfg; +} cfw_state; + +#ifndef AIC3XXX_CFW_HOST_BLD +#ifdef DEBUG + +# define DBG(fmt,...) printk("CFW[%s:%d]: " fmt "\n", \ + __FILE__, __LINE__, ##__VA_ARGS__) +#else + +# define DBG(fmt,...) + +#endif + +#endif +#endif diff --git a/sound/soc/codecs/pickle.h b/sound/soc/codecs/pickle.h new file mode 100644 index 000000000000..488012a603d3 --- /dev/null +++ b/sound/soc/codecs/pickle.h @@ -0,0 +1,7 @@ +#ifndef PICKLE_H_ +#define PICKLE_H_ + +void *pickle_pjt(cfw_project *p, int *n); +cfw_project *unpickle_pjt(void *p, int n); + +#endif diff --git a/sound/soc/codecs/tlv320aic3262_default_fw.h b/sound/soc/codecs/tlv320aic3262_default_fw.h new file mode 100644 index 000000000000..e51851b6c09b --- /dev/null +++ b/sound/soc/codecs/tlv320aic3262_default_fw.h @@ -0,0 +1,320 @@ +unsigned char default_firmware[] = { + 237,241,209,192,175, 0, 1, 0,219, 19, 0, 0, 28, 84, 36, 37, + 0, 0, 0, 0,161, 85,149, 79, 70,105,114,109,119, 97,114,101, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 2, 13, 0, 0, 8, 2, 0, 0, + 0, 0, 0, 0, 0,240, 73, 2,172, 3, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 32, 6, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,131, 17, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 105,110,105,116, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 73,110,105,116,105, 97,108,105,122, 97,116,105,111,110, 32,115, + 101,113,117,101,110, 99,101, 32, 40, 97,112,112,108,105,101,100, + 32,111,110,108,121, 32,111,110, 32,115,121,115,116,101,109, 32, + 98,111,111,116, 41, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 244, 5, 0, 0, 0, 0, 0, 0, 9, 0, 0, 0,100, 0,128, 0, + 0, 1, 1, 0, 0, 1,122, 1, 0, 0, 4, 51, 0, 0, 82, 0, + 0, 0, 67,151, 0, 1,119,127, 0, 0,129, 1, 0, 4, 11, 1, + 100,101,102, 97,117,108,116, 0, 98, 97,115,101, 95,109, 97,105, + 110, 95, 82, 97,116,101, 52, 56, 46, 99,102,103, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 2, 0, 0, 0, + 253, 11, 0, 0,120, 9, 0, 0,121, 12, 0, 0,254, 14, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0,100,101,102, 97,117,108,116, 47, + 98, 97,115,101, 95,109, 97,105,110, 95, 82, 97,116,101, 52, 56, + 46, 99,102,103, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 96, 8,115,247, + 0, 0, 0, 0,137, 55, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0,232, 11, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0,128, 0, 0, 0, 1, 0, 0, 0, + 244, 11, 0, 0, 2, 0, 0, 0, 0, 0, 60, 1, 1, 0, 0, 0, + 0, 29, 0, 0, 0, 0, 0,129,127, 0, 0, 6, 33, 0, 0,129, + 63, 0, 0, 7, 5, 0, 0, 8, 4, 0, 0, 9,176, 0, 0,129, + 127, 0, 0, 10, 1, 0, 0,129,127, 0, 0, 11, 2, 0, 0,129, + 127, 0, 0, 12, 8, 0, 0, 13, 0, 0, 0, 14,128, 0, 0, 18, + 130, 0, 0, 19,136, 0, 0, 20,128, 0, 0,129, 3, 0, 4, 11, + 0, 0, 0,129,127, 0, 4, 12, 4, 0, 0,129,127, 0, 4, 13, + 32, 0, 0,129,119, 0, 4, 14, 0, 0, 4,119,240, 0, 4,120, + 0,100, 0, 20,128,120, 0, 20, 0,100,101,102, 97,117,108,116, + 47, 80, 97,116, 99,104, 95, 98, 97,115,101, 95,109, 97,105,110, + 95, 82, 97,116,101, 52, 56, 46, 99,102,103, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0,233, 14, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0,128, 0, 0, 0, 1, 0, 0, + 0,245, 14, 0, 0, 2, 0, 0, 0, 0, 0, 60, 1, 1,100,101, + 102, 97,117,108,116, 47, 80, 97,116, 99,104, 95, 98, 97,115,101, + 95,115,112,107, 95, 82, 97,116,101, 52, 56, 46, 99,102,103, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 96, 8,115,247, 0, 0, 0, 0,233, 58, 1, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,110, 17, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,128, 36, + 115,247, 1, 0, 0, 0,122, 17, 0, 0, 2, 0, 0, 0, 0, 0, + 60, 1, 1, 0, 0, 0, 0,100,101,102, 97,117,108,116, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +}; diff --git a/sound/soc/codecs/tlv320aic326x.c b/sound/soc/codecs/tlv320aic326x.c new file mode 100644 index 000000000000..33d7cd8e45ee --- /dev/null +++ b/sound/soc/codecs/tlv320aic326x.c @@ -0,0 +1,2869 @@ +/* + * linux/sound/soc/codecs/tlv320aic326x.c + * + * Copyright (C) 2011 Texas Instruments Inc., + * + * This package 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 PACKAGE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED + * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. + * + * The TLV320AIC3262 is a flexible, low-power, low-voltage stereo audio + * codec with digital microphone inputs and programmable outputs. + * + * History: + * + * Rev 0.1 ASoC driver support TI 20-01-2011 + * + * The AIC325x ASoC driver is ported for the codec AIC3262. + * Rev 0.2 ASoC driver support TI 21-03-2011 + * The AIC326x ASoC driver is updated for linux 2.6.32 Kernel. + * Rev 0.3 ASoC driver support TI 20-04-2011 + * The AIC326x ASoC driver is ported to 2.6.35 omap4 kernel + */ + +/* + ***************************************************************************** + * INCLUDES + ***************************************************************************** + */ +//#define DEBUG 1 + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "aic3xxx_cfw.h" +#include "aic3xxx_cfw_ops.h" + +#include "tlv320aic326x.h" +#include "aic3262_codec_ops.h" +#include "tlv320aic3262_default_fw.h" + +#if 0 +#define DBG(x...) printk(KERN_INFO x) +#else +#define DBG(x...) +#endif + +#ifdef AIC3262_TiLoad +extern int aic3262_driver_init(struct snd_soc_codec *codec); +#endif + +#define AIC3262_PROC +#ifdef AIC3262_PROC +#include +#include +#include +char debug_write_read = 0; +#endif + +static struct snd_soc_codec *aic3262_codec; + +/* + ******************************************************************************* + * Global Variable * + ******************************************************************************* + */ +static u32 aic3262_reg_ctl; + +/* whenever aplay/arecord is run, aic3262_hw_params() function gets called. + * This function reprograms the clock dividers etc. this flag can be used to + * disable this when the clock dividers are programmed by pps config file + */ +static int soc_static_freq_config = 1; + +/****************************************************************************** + Macros +******************************************************************************/ + + +/* ASoC Widget Control definition for a single Register based Control */ +#define SOC_SINGLE_AIC3262(xname) \ +{\ + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \ + .info = __new_control_info, .get = __new_control_get,\ + .put = __new_control_put, \ + .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, \ +} +/* + ***************************************************************************** + * Function Prototype + ***************************************************************************** + */ +static int aic3262_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai); + +static int aic3262_mute(struct snd_soc_dai *dai, int mute); + +static int aic3262_set_dai_sysclk(struct snd_soc_dai *codec_dai, + int clk_id, unsigned int freq, int dir); + +static int aic3262_set_dai_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt); + +static int aic3262_dai_set_pll(struct snd_soc_dai *dai, int pll_id, int source, + unsigned int Fin, unsigned int Fout); + +static int aic3262_set_bias_level(struct snd_soc_codec *codec, enum snd_soc_bias_level level); + +unsigned int aic3262_codec_read(struct snd_soc_codec *codec, unsigned int reg); + + +static int __new_control_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo); + +static int __new_control_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol); + +static int __new_control_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol); +int aic3262_change_book(struct snd_soc_codec *codec, u8 new_book); + + +void aic3262_firmware_load(const struct firmware *fw, void *context); +static int aic3262_test_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol); +static int aic3262_test_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol); +static int aic3262_set_mode_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol); +static int aic3262_set_mode_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol); + +static int aic326x_adc_dsp_event(struct snd_soc_dapm_widget *w,struct snd_kcontrol *kcontrol, int event); +/*static int aic326x_adcl_event(struct snd_soc_dapm_widget *w,struct snd_kcontrol *kcontrol, int event); +static int aic326x_adcr_event(struct snd_soc_dapm_widget *w,struct snd_kcontrol *kcontrol, int event);*/ +static int __new_control_info(struct snd_kcontrol *kcontrol,struct snd_ctl_elem_info *uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = 1; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = 65535; + + return 0; +} + +static long debug_level = 0; +module_param(debug_level, int, 0); +MODULE_PARM_DESC(debug_level, "Debug level for printing"); + +/* + *---------------------------------------------------------------------------- + * Function : __new_control_get + * Purpose : This function is to read data of new control for + * program the AIC3262 registers. + * + *---------------------------------------------------------------------------- + */ +static int __new_control_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); + u32 val; + val = snd_soc_read(codec, aic3262_reg_ctl); + ucontrol->value.integer.value[0] = val; + return 0; +} + +/* + *---------------------------------------------------------------------------- + * Function : __new_control_put + * Purpose : new_control_put is called to pass data from user/application to + * the driver. + * + *---------------------------------------------------------------------------- + */ +static int __new_control_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); + u32 data_from_user = ucontrol->value.integer.value[0]; + u8 val = data_from_user & 0x00ff; + u32 reg = data_from_user >> 8;//MAKE_REG(book,page,offset) + snd_soc_write(codec, reg, val); + aic3262_reg_ctl = reg; + + return 0; +} + +static ssize_t debug_level_show(struct device *dev, + struct device_attribute *attr, + char *buf, size_t count) +{ + return sprintf(buf, "%ld\n", debug_level); +} + +static ssize_t debug_level_set(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + int ret; + ret = strict_strtol(buf, 10, &debug_level); + if(ret) + return ret; + return count; +} + +static DEVICE_ATTR(debug_level,0644, debug_level_show, debug_level_set); + +static const DECLARE_TLV_DB_SCALE(dac_vol_tlv, -6350, 50, 0); +static const DECLARE_TLV_DB_SCALE(adc_vol_tlv, -1200, 50, 0); +static const DECLARE_TLV_DB_SCALE(spk_gain_tlv, 600, 600, 0); +static const DECLARE_TLV_DB_SCALE(output_gain_tlv, -600, 100, 1); +static const DECLARE_TLV_DB_SCALE(micpga_gain_tlv, 0, 50, 0); +static const DECLARE_TLV_DB_SCALE(adc_fine_gain_tlv, -40, 10, 0); +static const DECLARE_TLV_DB_SCALE(beep_gen_volume_tlv, -6300, 100, 0); + +/* Chip-level Input and Output CM Mode Controls */ +static const char *input_common_mode_text[] = { + "0.9v", "0.75v" }; + +static const char *output_common_mode_text[] = { + "Input CM", "1.25v", "1.5v", "1.65v" }; + +static const struct soc_enum input_cm_mode = + SOC_ENUM_SINGLE(AIC3262_CM_REG, 2, 2, input_common_mode_text); + +static const struct soc_enum output_cm_mode = + SOC_ENUM_SINGLE(AIC3262_CM_REG, 0, 4, output_common_mode_text); +/* + ***************************************************************************** + * Structure Initialization + ***************************************************************************** + */ +static const struct snd_kcontrol_new aic3262_snd_controls[] = { + /* Output */ +#ifndef DAC_INDEPENDENT_VOL + /* sound new kcontrol for PCM Playback volume control */ + + SOC_DOUBLE_R_SX_TLV("PCM Playback Volume", + AIC3262_DAC_LVOL, AIC3262_DAC_RVOL, 8,0xffffff81, 0x30, dac_vol_tlv), +#endif + /*HP Driver Gain Control*/ + SOC_DOUBLE_R_SX_TLV("HeadPhone Driver Amplifier Volume", AIC3262_HPL_VOL, AIC3262_HPR_VOL, 6, 0xffffffb9, 0xffffffce, output_gain_tlv), + /*LO Driver Gain Control*/ + SOC_DOUBLE_TLV("Speaker Amplifier Volume", AIC3262_SPK_AMP_CNTL_R4, 4, 0, 5, 0, spk_gain_tlv), + + SOC_DOUBLE_R_SX_TLV("Receiver Amplifier Volume", AIC3262_REC_AMP_CNTL_R5, AIC3262_RAMPR_VOL, 6, 0xffffffb9, 0xffffffd6, output_gain_tlv), + + SOC_DOUBLE_R_SX_TLV("PCM Capture Volume", + AIC3262_LADC_VOL, AIC3262_RADC_VOL, 7,0xffffff68, 0xffffffa8, adc_vol_tlv), + + + SOC_DOUBLE_R_TLV ("MicPGA Volume Control", + AIC3262_MICL_PGA, AIC3262_MICR_PGA, 0, 0x5F, 0, micpga_gain_tlv), + SOC_DOUBLE_TLV("PCM Capture Fine Gain Volume", AIC3262_ADC_FINE_GAIN, 4, 0, 5, 1, adc_fine_gain_tlv), + SOC_DOUBLE("ADC channel mute", AIC3262_ADC_FINE_GAIN, 7, 3, 1, 0), + + SOC_DOUBLE("DAC MUTE", AIC3262_DAC_MVOL_CONF, 2, 3, 1, 1), + + /* sound new kcontrol for Programming the registers from user space */ + SOC_SINGLE_AIC3262("Program Registers"), + + SOC_SINGLE("RESET", AIC3262_RESET_REG, 0,1,0), + + SOC_SINGLE("DAC VOL SOFT STEPPING", AIC3262_DAC_MVOL_CONF, 0, 2, 0), + + + SOC_SINGLE("DAC AUTO MUTE CONTROL", AIC3262_DAC_MVOL_CONF, 4, 7, 0), + SOC_SINGLE("RIGHT MODULATOR SETUP", AIC3262_DAC_MVOL_CONF, 7, 1, 0), + + SOC_SINGLE("ADC Volume soft stepping", AIC3262_ADC_CHANNEL_POW, 0, 3, 0), + + SOC_SINGLE("Mic Bias ext independent enable", AIC3262_MIC_BIAS_CNTL, 7, 1, 0), + // SOC_SINGLE("MICBIAS_EXT ON", MIC_BIAS_CNTL, 6, 1, 0), + SOC_SINGLE("MICBIAS EXT Power Level", AIC3262_MIC_BIAS_CNTL, 4, 3, 0), + + // SOC_SINGLE("MICBIAS_INT ON", MIC_BIAS_CNTL, 2, 1, 0), + SOC_SINGLE("MICBIAS INT Power Level", AIC3262_MIC_BIAS_CNTL, 0, 3, 0), + + SOC_DOUBLE("DRC_EN_CTL", AIC3262_DRC_CNTL_R1, 6, 5, 1, 0), + SOC_SINGLE("DRC_THRESHOLD_LEVEL", AIC3262_DRC_CNTL_R1, 2, 7, 1), + SOC_SINGLE("DRC_HYSTERISIS_LEVEL", AIC3262_DRC_CNTL_R1, 0, 7, 0), + + SOC_SINGLE("DRC_HOLD_LEVEL", AIC3262_DRC_CNTL_R2, 3, 0x0F, 0), + SOC_SINGLE("DRC_GAIN_RATE", AIC3262_DRC_CNTL_R2, 0, 4, 0), + SOC_SINGLE("DRC_ATTACK_RATE", AIC3262_DRC_CNTL_R3, 4, 0x0F, 1), + SOC_SINGLE("DRC_DECAY_RATE", AIC3262_DRC_CNTL_R3, 0, 0x0F, 1), + + SOC_SINGLE("BEEP_GEN_EN", AIC3262_BEEP_CNTL_R1, 7, 1, 0), + SOC_DOUBLE_R("BEEP_VOL_CNTL", AIC3262_BEEP_CNTL_R1, AIC3262_BEEP_CNTL_R2, 0, 0x0F, 1), + SOC_SINGLE("BEEP_MAS_VOL", AIC3262_BEEP_CNTL_R2, 6, 3, 0), + + SOC_DOUBLE_R("AGC_EN", AIC3262_LAGC_CNTL, AIC3262_RAGC_CNTL, 7, 1, 0), + SOC_DOUBLE_R("AGC_TARGET_LEVEL", AIC3262_LAGC_CNTL, AIC3262_RAGC_CNTL, 4, 7, 1), + + SOC_DOUBLE_R("AGC_GAIN_HYSTERESIS", AIC3262_LAGC_CNTL, AIC3262_RAGC_CNTL, 0, 3, 0), + SOC_DOUBLE_R("AGC_HYSTERESIS", AIC3262_LAGC_CNTL_R2, AIC3262_RAGC_CNTL_R2, 6, 3, 0), + SOC_DOUBLE_R("AGC_NOISE_THRESHOLD", AIC3262_LAGC_CNTL_R2, AIC3262_RAGC_CNTL_R2, 1, 31, 1), + + SOC_DOUBLE_R("AGC_MAX_GAIN", AIC3262_LAGC_CNTL_R3, AIC3262_RAGC_CNTL_R3, 0, 116, 0), + SOC_DOUBLE_R("AGC_ATCK_TIME", AIC3262_LAGC_CNTL_R4, AIC3262_RAGC_CNTL_R4, 3, 31, 0), + SOC_DOUBLE_R("AGC_ATCK_SCALE_FACTOR", AIC3262_LAGC_CNTL_R4, AIC3262_RAGC_CNTL_R4, 0, 7, 0), + + SOC_DOUBLE_R("AGC_DECAY_TIME", AIC3262_LAGC_CNTL_R5, AIC3262_RAGC_CNTL_R5, 3, 31, 0), + SOC_DOUBLE_R("AGC_DECAY_SCALE_FACTOR", AIC3262_LAGC_CNTL_R5, AIC3262_RAGC_CNTL_R5, 0, 7, 0), + SOC_DOUBLE_R("AGC_NOISE_DEB_TIME", AIC3262_LAGC_CNTL_R6, AIC3262_RAGC_CNTL_R6, 0, 31, 0), + + SOC_DOUBLE_R("AGC_SGL_DEB_TIME", AIC3262_LAGC_CNTL_R7, AIC3262_RAGC_CNTL_R7, 0, 0x0F, 0), + + SOC_SINGLE("DAC PRB Selection",AIC3262_DAC_PRB, 0, 26, 0), + SOC_SINGLE("ADC PRB Selection",AIC3262_ADC_PRB, 0, 18, 0), + SOC_ENUM("Input CM mode", input_cm_mode), + SOC_ENUM("Output CM mode", output_cm_mode), + + SOC_SINGLE_EXT("FIRMWARE LOAD",SND_SOC_NOPM,0,0,0,aic3262_test_get,aic3262_test_put), + SOC_SINGLE_EXT("FIRMWARE SET MODE",SND_SOC_NOPM,0,0xffff,0,aic3262_set_mode_get,aic3262_set_mode_put), + + +}; +/* + *---------------------------------------------------------------------------- + * @struct snd_soc_codec_dai | + * It is SoC Codec DAI structure which has DAI capabilities viz., + * playback and capture, DAI runtime information viz. state of DAI + * and pop wait state, and DAI private data. + * The AIC3262 rates ranges from 8k to 192k + * The PCM bit format supported are 16, 20, 24 and 32 bits + *---------------------------------------------------------------------------- + */ +struct snd_soc_dai_ops aic3262_asi1_dai_ops = { + .hw_params = aic3262_hw_params, + .digital_mute = aic3262_mute, + .set_sysclk = aic3262_set_dai_sysclk, + .set_fmt = aic3262_set_dai_fmt, + .set_pll = aic3262_dai_set_pll, +}; +struct snd_soc_dai_ops aic3262_asi2_dai_ops = { + .hw_params = aic3262_hw_params, + .digital_mute = aic3262_mute, + .set_sysclk = aic3262_set_dai_sysclk, + .set_fmt = aic3262_set_dai_fmt, + .set_pll = aic3262_dai_set_pll, +}; +struct snd_soc_dai_ops aic3262_asi3_dai_ops = { + .hw_params = aic3262_hw_params, + .digital_mute = aic3262_mute, + .set_sysclk = aic3262_set_dai_sysclk, + .set_fmt = aic3262_set_dai_fmt, + .set_pll = aic3262_dai_set_pll, +}; + +struct snd_soc_dai_driver aic326x_dai_driver[] = { + { + .name = "aic326x-asi1", + .playback = { + .stream_name = "ASI1 Playback", + .channels_min = 1, + .channels_max = 2, + .rates = AIC3262_RATES, + .formats = AIC3262_FORMATS, + }, + .capture = { + .stream_name = "ASI1 Capture", + .channels_min = 1, + .channels_max = 2, + .rates = AIC3262_RATES, + .formats = AIC3262_FORMATS, + }, + .ops = &aic3262_asi1_dai_ops, + }, + { + .name = "aic326x-asi2", + .playback = { + .stream_name = "ASI2 Playback", + .channels_min = 1, + .channels_max = 2, + .rates = AIC3262_RATES, + .formats = AIC3262_FORMATS, + }, + .capture = { + .stream_name = "ASI2 Capture", + .channels_min = 1, + .channels_max = 2, + .rates = AIC3262_RATES, + .formats = AIC3262_FORMATS, + }, + .ops = &aic3262_asi2_dai_ops, + }, + { + .name = "aic326x-asi3", + .playback = { + .stream_name = "ASI3 Playback", + .channels_min = 1, + .channels_max = 2, + .rates = AIC3262_RATES, + .formats = AIC3262_FORMATS, + }, + .capture = { + .stream_name = "ASI3 Capture", + .channels_min = 1, + .channels_max = 2, + .rates = AIC3262_RATES, + .formats = AIC3262_FORMATS, + }, + .ops = &aic3262_asi3_dai_ops, + }, + +}; + +/* + ***************************************************************************** + * Initializations + ***************************************************************************** + */ +/* + * AIC3262 register cache + * We are caching the registers here. + * There is no point in caching the reset register. + * + * NOTE: In AIC3262, there are 127 registers supported in both page0 and page1 + * The following table contains the page0 and page 1 and page 3 + * registers values. + */ + +static const u8 aic3262_reg[AIC3262_CACHEREGNUM] = { + 0x00, 0x00, 0x10, 0x00, /* 0 */ + 0x03, 0x40, 0x11, 0x08, /* 4 */ + 0x00, 0x00, 0x00, 0x82, /* 8 */ + 0x88, 0x00, 0x80, 0x02, /* 12 */ + 0x00, 0x08, 0x01, 0x01, /* 16 */ + 0x80, 0x01, 0x00, 0x04, /* 20 */ + 0x00, 0x00, 0x01, 0x00, /* 24 */ + 0x00, 0x00, 0x01, 0x00, /* 28 */ + 0x00, 0x00, 0x00, 0x00, /* 32 */ + 0x00, 0x00, 0x00, 0x00, /* 36 */ + 0x00, 0x00, 0x00, 0x00, /* 40 */ + 0x00, 0x00, 0x00, 0x00, /* 44 */ + 0x00, 0x00, 0x00, 0x00, /* 48 */ + 0x00, 0x42, 0x02, 0x02, /* 52 */ + 0x42, 0x02, 0x02, 0x02, /* 56 */ + 0x00, 0x00, 0x00, 0x01, /* 60 */ + 0x01, 0x00, 0x14, 0x00, /* 64 */ + 0x0C, 0x00, 0x00, 0x00, /* 68 */ + 0x00, 0x00, 0x00, 0xEE, /* 72 */ + 0x10, 0xD8, 0x10, 0xD8, /* 76 */ + 0x00, 0x00, 0x88, 0x00, /* 80 */ + 0x00, 0x00, 0x00, 0x00, /* 84 */ + 0x7F, 0x00, 0x00, 0x00, /* 88 */ + 0x00, 0x00, 0x00, 0x00, /* 92 */ + 0x7F, 0x00, 0x00, 0x00, /* 96 */ + 0x00, 0x00, 0x00, 0x00, /* 100 */ + 0x00, 0x00, 0x00, 0x00, /* 104 */ + 0x00, 0x00, 0x00, 0x00, /* 108 */ + 0x00, 0x00, 0x00, 0x00, /* 112 */ + 0x00, 0x00, 0x00, 0x00, /* 116 */ + 0x00, 0x00, 0x00, 0x00, /* 120 */ + 0x00, 0x00, 0x00, 0x00, /* 124 - PAGE0 Registers(127) ends here */ + 0x01, 0x00, 0x08, 0x00, /* 128, PAGE1-0 */ + 0x00, 0x00, 0x00, 0x00, /* 132, PAGE1-4 */ + 0x00, 0x00, 0x00, 0x10, /* 136, PAGE1-8 */ + 0x00, 0x00, 0x00, 0x00, /* 140, PAGE1-12 */ + 0x40, 0x40, 0x40, 0x40, /* 144, PAGE1-16 */ + 0x00, 0x00, 0x00, 0x00, /* 148, PAGE1-20 */ + 0x00, 0x00, 0x00, 0x00, /* 152, PAGE1-24 */ + 0x00, 0x00, 0x00, 0x00, /* 156, PAGE1-28 */ + 0x00, 0x00, 0x00, 0x00, /* 160, PAGE1-32 */ + 0x00, 0x00, 0x00, 0x00, /* 164, PAGE1-36 */ + 0x00, 0x00, 0x00, 0x00, /* 168, PAGE1-40 */ + 0x00, 0x00, 0x00, 0x00, /* 172, PAGE1-44 */ + 0x00, 0x00, 0x00, 0x00, /* 176, PAGE1-48 */ + 0x00, 0x00, 0x00, 0x00, /* 180, PAGE1-52 */ + 0x00, 0x00, 0x00, 0x80, /* 184, PAGE1-56 */ + 0x80, 0x00, 0x00, 0x00, /* 188, PAGE1-60 */ + 0x00, 0x00, 0x00, 0x00, /* 192, PAGE1-64 */ + 0x00, 0x00, 0x00, 0x00, /* 196, PAGE1-68 */ + 0x00, 0x00, 0x00, 0x00, /* 200, PAGE1-72 */ + 0x00, 0x00, 0x00, 0x00, /* 204, PAGE1-76 */ + 0x00, 0x00, 0x00, 0x00, /* 208, PAGE1-80 */ + 0x00, 0x00, 0x00, 0x00, /* 212, PAGE1-84 */ + 0x00, 0x00, 0x00, 0x00, /* 216, PAGE1-88 */ + 0x00, 0x00, 0x00, 0x00, /* 220, PAGE1-92 */ + 0x00, 0x00, 0x00, 0x00, /* 224, PAGE1-96 */ + 0x00, 0x00, 0x00, 0x00, /* 228, PAGE1-100 */ + 0x00, 0x00, 0x00, 0x00, /* 232, PAGE1-104 */ + 0x00, 0x00, 0x00, 0x00, /* 236, PAGE1-108 */ + 0x00, 0x00, 0x00, 0x00, /* 240, PAGE1-112 */ + 0x00, 0x00, 0x00, 0x00, /* 244, PAGE1-116 */ + 0x00, 0x00, 0x00, 0x00, /* 248, PAGE1-120 */ + 0x00, 0x00, 0x00, 0x00, /* 252, PAGE1-124 Page 1 Registers Ends Here */ + 0x00, 0x00, 0x00, 0x00, /* 256, PAGE2-0 */ + 0x00, 0x00, 0x00, 0x00, /* 260, PAGE2-4 */ + 0x00, 0x00, 0x00, 0x00, /* 264, PAGE2-8 */ + 0x00, 0x00, 0x00, 0x00, /* 268, PAGE2-12 */ + 0x00, 0x00, 0x00, 0x00, /* 272, PAGE2-16 */ + 0x00, 0x00, 0x00, 0x00, /* 276, PAGE2-20 */ + 0x00, 0x00, 0x00, 0x00, /* 280, PAGE2-24 */ + 0x00, 0x00, 0x00, 0x00, /* 284, PAGE2-28 */ + 0x00, 0x00, 0x00, 0x00, /* 288, PAGE2-32 */ + 0x00, 0x00, 0x00, 0x00, /* 292, PAGE2-36 */ + 0x00, 0x00, 0x00, 0x00, /* 296, PAGE2-40 */ + 0x00, 0x00, 0x00, 0x00, /* 300, PAGE2-44 */ + 0x00, 0x00, 0x00, 0x00, /* 304, PAGE2-48 */ + 0x00, 0x00, 0x00, 0x00, /* 308, PAGE2-52 */ + 0x00, 0x00, 0x00, 0x00, /* 312, PAGE2-56 */ + 0x00, 0x00, 0x00, 0x00, /* 316, PAGE2-60 */ + 0x00, 0x00, 0x00, 0x00, /* 320, PAGE2-64 */ + 0x00, 0x00, 0x00, 0x00, /* 324, PAGE2-68 */ + 0x00, 0x00, 0x00, 0x00, /* 328, PAGE2-72 */ + 0x00, 0x00, 0x00, 0x00, /* 332, PAGE2-76 */ + 0x00, 0x00, 0x00, 0x00, /* 336, PAGE2-80 */ + 0x00, 0x00, 0x00, 0x00, /* 340, PAGE2-84 */ + 0x00, 0x00, 0x00, 0x00, /* 344, PAGE2-88 */ + 0x00, 0x00, 0x00, 0x00, /* 348, PAGE2-92 */ + 0x00, 0x00, 0x00, 0x00, /* 352, PAGE2-96 */ + 0x00, 0x00, 0x00, 0x00, /* 356, PAGE2-100 */ + 0x00, 0x00, 0x00, 0x00, /* 360, PAGE2-104 */ + 0x00, 0x00, 0x00, 0x00, /* 364, PAGE2-108 */ + 0x00, 0x00, 0x00, 0x00, /* 368, PAGE2-112*/ + 0x00, 0x00, 0x00, 0x00, /* 372, PAGE2-116*/ + 0x00, 0x00, 0x00, 0x00, /* 376, PAGE2-120*/ + 0x00, 0x00, 0x00, 0x00, /* 380, PAGE2-124 Page 2 Registers Ends Here */ + 0x00, 0x00, 0x00, 0x00, /* 384, PAGE3-0 */ + 0x00, 0x00, 0x00, 0x00, /* 388, PAGE3-4 */ + 0x00, 0x00, 0x00, 0x00, /* 392, PAGE3-8 */ + 0x00, 0x00, 0x00, 0x00, /* 392, PAGE3-12 */ + 0x00, 0x00, 0x00, 0x00, /* 392, PAGE3-16 */ + 0x00, 0x00, 0x00, 0x00, /* 392, PAGE3-20 */ + 0x00, 0x00, 0x00, 0x00, /* 392, PAGE3-24 */ + 0x00, 0x00, 0x00, 0x00, /* 392, PAGE3-28 */ + 0x00, 0x00, 0x00, 0x00, /* 392, PAGE3-32 */ + 0x00, 0x00, 0x00, 0x00, /* 392, PAGE3-36 */ + 0x00, 0x00, 0x00, 0x00, /* 392, PAGE3-40 */ + 0x00, 0x00, 0x00, 0x00, /* 392, PAGE3-44 */ + 0x00, 0x00, 0x00, 0x00, /* 392, PAGE3-48 */ + 0x00, 0x00, 0x00, 0x00, /* 392, PAGE3-52 */ + 0x00, 0x00, 0x00, 0x00, /* 392, PAGE3-56 */ + 0x00, 0x00, 0x00, 0x00, /* 392, PAGE3-60 */ + 0x00, 0x00, 0x00, 0x00, /* 392, PAGE3-64 */ + 0x00, 0x00, 0x00, 0x00, /* 392, PAGE3-68 */ + 0x00, 0x00, 0x00, 0x00, /* 328, PAGE3-72 */ + 0x00, 0x00, 0x00, 0x00, /* 332, PAGE3-76 */ + 0x00, 0x00, 0x00, 0x00, /* 336, PAGE3-80 */ + 0x00, 0x00, 0x00, 0x00, /* 340, PAGE3-84 */ + 0x00, 0x00, 0x00, 0x00, /* 344, PAGE3-88 */ + 0x00, 0x00, 0x00, 0x00, /* 348, PAGE3-92 */ + 0x00, 0x00, 0x00, 0x00, /* 352, PAGE3-96 */ + 0x00, 0x00, 0x00, 0x00, /* 356, PAGE3-100 */ + 0x00, 0x00, 0x00, 0x00, /* 360, PAGE3-104 */ + 0x00, 0x00, 0x00, 0x00, /* 364, PAGE3-108 */ + 0x00, 0x00, 0x00, 0x00, /* 368, PAGE3-112*/ + 0x00, 0x00, 0x00, 0x00, /* 372, PAGE3-116*/ + 0x00, 0x00, 0x00, 0x00, /* 376, PAGE3-120*/ + 0x00, 0x00, 0x00, 0x00, /* 380, PAGE3-124 Page 3 Registers Ends Here */ + 0x00, 0x00, 0x00, 0x00, /* 384, PAGE4-0 */ + 0x00, 0x00, 0x00, 0x00, /* 388, PAGE4-4 */ + 0x00, 0x00, 0x00, 0x00, /* 392, PAGE4-8 */ + 0x00, 0x00, 0x00, 0x00, /* 392, PAGE4-12 */ + 0x00, 0x00, 0x00, 0x00, /* 392, PAGE4-16 */ + 0x00, 0x00, 0x00, 0x00, /* 392, PAGE4-20 */ + 0x00, 0x00, 0x00, 0x00, /* 392, PAGE4-24 */ + 0x00, 0x00, 0x00, 0x00, /* 392, PAGE4-28 */ + 0x00, 0x00, 0x00, 0x00, /* 392, PAGE4-32 */ + 0x00, 0x00, 0x00, 0x00, /* 392, PAGE4-36 */ + 0x00, 0x00, 0x00, 0x00, /* 392, PAGE4-40 */ + 0x00, 0x00, 0x00, 0x00, /* 392, PAGE4-44 */ + 0x00, 0x00, 0x00, 0x00, /* 392, PAGE4-48 */ + 0x00, 0x00, 0x00, 0x00, /* 392, PAGE4-52 */ + 0x00, 0x00, 0x00, 0x00, /* 392, PAGE4-56 */ + 0x00, 0x00, 0x00, 0x00, /* 392, PAGE4-60 */ + 0x00, 0x00, 0x00, 0x00, /* 392, PAGE4-64 */ + 0x00, 0x00, 0x00, 0x00, /* 392, PAGE4-68 */ + 0x00, 0x00, 0x00, 0x00, /* 328, PAGE4-72 */ + 0x00, 0x00, 0x00, 0x00, /* 332, PAGE4-76 */ + 0x00, 0x00, 0x00, 0x00, /* 336, PAGE4-80 */ + 0x00, 0x00, 0x00, 0x00, /* 340, PAGE4-84 */ + 0x00, 0x00, 0x00, 0x00, /* 344, PAGE4-88 */ + 0x00, 0x00, 0x00, 0x00, /* 348, PAGE4-92 */ + 0x00, 0x00, 0x00, 0x00, /* 352, PAGE4-96 */ + 0x00, 0x00, 0x00, 0x00, /* 356, PAGE4-100 */ + 0x00, 0x00, 0x00, 0x00, /* 360, PAGE4-104 */ + 0x00, 0x00, 0x00, 0x00, /* 364, PAGE4-108 */ + 0x00, 0x00, 0x00, 0x00, /* 368, PAGE4-112*/ + 0x00, 0x00, 0x00, 0x00, /* 372, PAGE4-116*/ + 0x00, 0x00, 0x00, 0x00, /* 376, PAGE4-120*/ + 0x00, 0x00, 0x00, 0x00, /* 380, PAGE4-124 Page 2 Registers Ends Here */ +}; + +static const unsigned int adc_ma_tlv[] = { + TLV_DB_RANGE_HEAD(4), + 0, 29, TLV_DB_SCALE_ITEM(-1450, 500, 0), + 30, 35, TLV_DB_SCALE_ITEM(-2060, 1000, 0), + 36, 38, TLV_DB_SCALE_ITEM(-2660, 2000, 0), + 39, 40, TLV_DB_SCALE_ITEM(-3610, 5000, 0), +}; +static const DECLARE_TLV_DB_SCALE(lo_hp_tlv, -7830, 50, 0); +static const struct snd_kcontrol_new mal_pga_mixer_controls[] = { + SOC_DAPM_SINGLE("IN1L Switch", AIC3262_MA_CNTL, 5, 1, 0), + SOC_DAPM_SINGLE_TLV("Left MicPGA Volume", AIC3262_LADC_PGA_MAL_VOL, 0, 0x3f, 1, adc_ma_tlv), + +}; + +static const struct snd_kcontrol_new mar_pga_mixer_controls[] = { + SOC_DAPM_SINGLE("IN1R Switch", AIC3262_MA_CNTL, 4, 1, 0), + SOC_DAPM_SINGLE_TLV("Right MicPGA Volume", AIC3262_RADC_PGA_MAR_VOL, 0, 0x3f, 1, adc_ma_tlv), +}; + +/* Left HPL Mixer */ +static const struct snd_kcontrol_new hpl_output_mixer_controls[] = { + SOC_DAPM_SINGLE("MAL Switch", AIC3262_HP_AMP_CNTL_R1, 7, 1, 0), + SOC_DAPM_SINGLE("LDAC Switch", AIC3262_HP_AMP_CNTL_R1, 5, 1, 0), + SOC_DAPM_SINGLE_TLV("LOL-B1 Volume", AIC3262_HP_AMP_CNTL_R2, 0, 0x7f, 1, lo_hp_tlv), +}; + +/* Right HPR Mixer */ +static const struct snd_kcontrol_new hpr_output_mixer_controls[] = { + SOC_DAPM_SINGLE_TLV("LOR-B1 Volume", AIC3262_HP_AMP_CNTL_R3, 0, 0x7f, 1, lo_hp_tlv), + SOC_DAPM_SINGLE("LDAC Switch", AIC3262_HP_AMP_CNTL_R1, 2, 1, 0), + SOC_DAPM_SINGLE("RDAC Switch", AIC3262_HP_AMP_CNTL_R1, 4, 1, 0), + SOC_DAPM_SINGLE("MAR Switch", AIC3262_HP_AMP_CNTL_R1, 6, 1, 0), +}; + +/* Left LOL Mixer */ +static const struct snd_kcontrol_new lol_output_mixer_controls[] = { + SOC_DAPM_SINGLE("MAL Switch", AIC3262_LINE_AMP_CNTL_R2, 7, 1, 0), + SOC_DAPM_SINGLE("IN1L-B Switch", AIC3262_LINE_AMP_CNTL_R2, 3, 1,0), + SOC_DAPM_SINGLE("LDAC Switch", AIC3262_LINE_AMP_CNTL_R1, 7, 1, 0), + SOC_DAPM_SINGLE("RDAC Switch", AIC3262_LINE_AMP_CNTL_R1, 5, 1, 0), +}; + +/* Right LOR Mixer */ +static const struct snd_kcontrol_new lor_output_mixer_controls[] = { + SOC_DAPM_SINGLE("LOL Switch", AIC3262_LINE_AMP_CNTL_R1, 2, 1, 0), + SOC_DAPM_SINGLE("RDAC Switch", AIC3262_LINE_AMP_CNTL_R1, 6, 1, 0), + SOC_DAPM_SINGLE("MAR Switch", AIC3262_LINE_AMP_CNTL_R2, 6, 1, 0), + SOC_DAPM_SINGLE("IN1R-B Switch", AIC3262_LINE_AMP_CNTL_R2, 0, 1,0), +}; + +/* Left SPKL Mixer */ +static const struct snd_kcontrol_new spkl_output_mixer_controls[] = { + SOC_DAPM_SINGLE("MAL Switch", AIC3262_SPK_AMP_CNTL_R1, 7, 1, 0), + SOC_DAPM_SINGLE_TLV("LOL Volume", AIC3262_SPK_AMP_CNTL_R2, 0, 0x7f,1, lo_hp_tlv), + SOC_DAPM_SINGLE("SPR_IN Switch", AIC3262_SPK_AMP_CNTL_R1, 2, 1, 0), +}; + +/* Right SPKR Mixer */ +static const struct snd_kcontrol_new spkr_output_mixer_controls[] = { + SOC_DAPM_SINGLE_TLV("LOR Volume", AIC3262_SPK_AMP_CNTL_R3, 0, 0x7f, 1, lo_hp_tlv), + SOC_DAPM_SINGLE("MAR Switch", AIC3262_SPK_AMP_CNTL_R1, 6, 1, 0), +}; + +/* REC Mixer */ +static const struct snd_kcontrol_new rec_output_mixer_controls[] = { + SOC_DAPM_SINGLE_TLV("LOL-B2 Volume", AIC3262_RAMP_CNTL_R1, 0, 0x7f,1, lo_hp_tlv), + SOC_DAPM_SINGLE_TLV("IN1L Volume", AIC3262_IN1L_SEL_RM, 0, 0x7f, 1, lo_hp_tlv), + SOC_DAPM_SINGLE_TLV("IN1R Volume", AIC3262_IN1R_SEL_RM, 0, 0x7f, 1, lo_hp_tlv), + SOC_DAPM_SINGLE_TLV("LOR-B2 Volume", AIC3262_RAMP_CNTL_R2, 0,0x7f, 1,lo_hp_tlv), +}; + +/* Left Input Mixer */ +static const struct snd_kcontrol_new left_input_mixer_controls[] = { + SOC_DAPM_SINGLE("IN1L Switch", AIC3262_LMIC_PGA_PIN, 6, 3, 0), + SOC_DAPM_SINGLE("IN2L Switch", AIC3262_LMIC_PGA_PIN, 4, 3, 0), + SOC_DAPM_SINGLE("IN3L Switch", AIC3262_LMIC_PGA_PIN, 2, 3, 0), + SOC_DAPM_SINGLE("IN4L Switch", AIC3262_LMIC_PGA_PM_IN4, 5, 1, 0), + SOC_DAPM_SINGLE("IN1R Switch", AIC3262_LMIC_PGA_PIN, 0, 3, 0), + SOC_DAPM_SINGLE("IN2R Switch", AIC3262_LMIC_PGA_MIN, 4, 3, 0), + SOC_DAPM_SINGLE("IN3R Switch", AIC3262_LMIC_PGA_MIN, 2, 3, 0), + SOC_DAPM_SINGLE("IN4R Switch", AIC3262_LMIC_PGA_PM_IN4, 4, 1, 0), + SOC_DAPM_SINGLE("CM2L Switch", AIC3262_LMIC_PGA_MIN, 0, 3, 0), + SOC_DAPM_SINGLE("CM1L Switch", AIC3262_LMIC_PGA_MIN, 6, 3, 0), +}; + +/* Right Input Mixer */ +static const struct snd_kcontrol_new right_input_mixer_controls[] = { + SOC_DAPM_SINGLE("IN1R Switch", AIC3262_RMIC_PGA_PIN, 6, 3, 0), + SOC_DAPM_SINGLE("IN2R Switch", AIC3262_RMIC_PGA_PIN, 4, 3, 0), + SOC_DAPM_SINGLE("IN3R Switch", AIC3262_RMIC_PGA_PIN, 2, 3, 0), + SOC_DAPM_SINGLE("IN4R Switch", AIC3262_RMIC_PGA_PM_IN4, 5, 1, 0), + SOC_DAPM_SINGLE("IN2L Switch", AIC3262_RMIC_PGA_PIN, 0, 3, 0), + SOC_DAPM_SINGLE("IN1L Switch", AIC3262_RMIC_PGA_MIN, 4, 3, 0), + SOC_DAPM_SINGLE("IN3L Switch", AIC3262_RMIC_PGA_MIN, 2, 3, 0), + SOC_DAPM_SINGLE("IN4L Switch", AIC3262_RMIC_PGA_PM_IN4, 4, 1, 0), + SOC_DAPM_SINGLE("CM1R Switch", AIC3262_RMIC_PGA_MIN, 6, 3, 0), + SOC_DAPM_SINGLE("CM2R Switch", AIC3262_RMIC_PGA_MIN, 0, 3, 0), +}; + +static const char *asi1lin_text[] = { + "Off", "ASI1 Left In","ASI1 Right In","ASI1 MonoMix In" +}; + + +SOC_ENUM_SINGLE_DECL(asi1lin_enum, AIC3262_ASI1_DAC_OUT_CNTL, 6, asi1lin_text); + +static const struct snd_kcontrol_new asi1lin_control = +SOC_DAPM_ENUM("ASI1LIN Route", asi1lin_enum); + + +static const char *asi1rin_text[] = { + "Off", "ASI1 Right In","ASI1 Left In","ASI1 MonoMix In" +}; + +SOC_ENUM_SINGLE_DECL(asi1rin_enum, AIC3262_ASI1_DAC_OUT_CNTL, 4, asi1rin_text); + +static const struct snd_kcontrol_new asi1rin_control = +SOC_DAPM_ENUM("ASI1RIN Route", asi1rin_enum); + +static const char *asi2lin_text[] = { + "Off", "ASI2 Left In","ASI2 Right In","ASI2 MonoMix In" +}; + +SOC_ENUM_SINGLE_DECL(asi2lin_enum, AIC3262_ASI2_DAC_OUT_CNTL, 6, asi2lin_text); + +static const struct snd_kcontrol_new asi2lin_control = +SOC_DAPM_ENUM("ASI2LIN Route", asi2lin_enum); + +static const char *asi2rin_text[] = { + "Off", "ASI2 Right In","ASI2 Left In","ASI2 MonoMix In" +}; + +SOC_ENUM_SINGLE_DECL(asi2rin_enum, AIC3262_ASI2_DAC_OUT_CNTL, 4, asi2rin_text); + +static const struct snd_kcontrol_new asi2rin_control = +SOC_DAPM_ENUM("ASI2RIN Route", asi2rin_enum); + +static const char *asi3lin_text[] = { + "Off", "ASI3 Left In","ASI3 Right In","ASI3 MonoMix In" +}; + +SOC_ENUM_SINGLE_DECL(asi3lin_enum, AIC3262_ASI3_DAC_OUT_CNTL, 6, asi3lin_text); + +static const struct snd_kcontrol_new asi3lin_control = SOC_DAPM_ENUM("ASI3LIN Route", asi3lin_enum); + + +static const char *asi3rin_text[] = { + "Off", "ASI3 Right In","ASI3 Left In","ASI3 MonoMix In" +}; + +SOC_ENUM_SINGLE_DECL(asi3rin_enum, AIC3262_ASI3_DAC_OUT_CNTL, 4, asi3rin_text); + +static const struct snd_kcontrol_new asi3rin_control = +SOC_DAPM_ENUM("ASI3RIN Route", asi3rin_enum); + + +static const char *dacminidspin1_text[] = { + "ASI1 In", "ASI2 In","ASI3 In","ADC MiniDSP Out" +}; + +SOC_ENUM_SINGLE_DECL(dacminidspin1_enum, AIC3262_MINIDSP_DATA_PORT_CNTL, 4, dacminidspin1_text); + +static const struct snd_kcontrol_new dacminidspin1_control = +SOC_DAPM_ENUM("DAC MiniDSP IN1 Route", dacminidspin1_enum); + +static const char *dacminidspin2_text[] = { + "ASI1 In", "ASI2 In","ASI3 In" +}; + +SOC_ENUM_SINGLE_DECL(dacminidspin2_enum, AIC3262_MINIDSP_DATA_PORT_CNTL, 2, dacminidspin2_text); + +static const struct snd_kcontrol_new dacminidspin2_control = +SOC_DAPM_ENUM("DAC MiniDSP IN2 Route", dacminidspin2_enum); + +static const char *dacminidspin3_text[] = { + "ASI1 In", "ASI2 In","ASI3 In" +}; + +SOC_ENUM_SINGLE_DECL(dacminidspin3_enum, AIC3262_MINIDSP_DATA_PORT_CNTL, 0, dacminidspin3_text); + +static const struct snd_kcontrol_new dacminidspin3_control = +SOC_DAPM_ENUM("DAC MiniDSP IN3 Route", dacminidspin3_enum); + + +static const char *adcdac_route_text[] = { + "Off", + "On", +}; + +SOC_ENUM_SINGLE_DECL(adcdac_enum, 0, 2, adcdac_route_text); + +static const struct snd_kcontrol_new adcdacroute_control = + SOC_DAPM_ENUM_VIRT("ADC DAC Route", adcdac_enum); + +static const char *dout1_text[] = { + "ASI1 Out", + "DIN1 Bypass", + "DIN2 Bypass", + "DIN3 Bypass", +}; +SOC_ENUM_SINGLE_DECL(dout1_enum, AIC3262_ASI1_DOUT_CNTL, 0, dout1_text); +static const struct snd_kcontrol_new dout1_control = +SOC_DAPM_ENUM("DOUT1 Route", dout1_enum); + + +static const char *dout2_text[] = { + "ASI2 Out", + "DIN1 Bypass", + "DIN2 Bypass", + "DIN3 Bypass", +}; +SOC_ENUM_SINGLE_DECL(dout2_enum, AIC3262_ASI2_DOUT_CNTL, 0, dout2_text); +static const struct snd_kcontrol_new dout2_control = +SOC_DAPM_ENUM("DOUT2 Route", dout2_enum); + + +static const char *dout3_text[] = { + "ASI3 Out", + "DIN1 Bypass", + "DIN2 Bypass", + "DIN3 Bypass", +}; +SOC_ENUM_SINGLE_DECL(dout3_enum, AIC3262_ASI3_DOUT_CNTL, 0, dout3_text); +static const struct snd_kcontrol_new dout3_control = +SOC_DAPM_ENUM("DOUT3 Route", dout3_enum); + +static const char *asi1out_text[] = { + "Off", + "ADC MiniDSP Out1", + "ASI1In Bypass", + "ASI2In Bypass", + "ASI3In Bypass", +}; +SOC_ENUM_SINGLE_DECL(asi1out_enum, AIC3262_ASI1_ADC_INPUT_CNTL, 0, asi1out_text); +static const struct snd_kcontrol_new asi1out_control = +SOC_DAPM_ENUM("ASI1OUT Route", asi1out_enum); + +static const char *asi2out_text[] = { + "Off", + "ADC MiniDSP Out1", + "ASI1In Bypass", + "ASI2In Bypass", + "ASI3In Bypass", + "ADC MiniDSP Out2", +}; +SOC_ENUM_SINGLE_DECL(asi2out_enum, AIC3262_ASI2_ADC_INPUT_CNTL, 0, asi2out_text); +static const struct snd_kcontrol_new asi2out_control = +SOC_DAPM_ENUM("ASI2OUT Route", asi2out_enum); +static const char *asi3out_text[] = { + "Off", + "ADC MiniDSP Out1", + "ASI1In Bypass", + "ASI2In Bypass", + "ASI3In Bypass", + "Reserved", + "ADC MiniDSP Out3", +}; +SOC_ENUM_SINGLE_DECL(asi3out_enum, AIC3262_ASI3_ADC_INPUT_CNTL, 0, asi3out_text); +static const struct snd_kcontrol_new asi3out_control = +SOC_DAPM_ENUM("ASI3OUT Route", asi3out_enum); +static const char *asibclk_text[] = { + "DAC_CLK", + "DAC_MOD_CLK", + "ADC_CLK", + "ADC_MOD_CLK", +}; +SOC_ENUM_SINGLE_DECL(asi1bclk_enum, AIC3262_ASI1_BCLK_N_CNTL, 0, asibclk_text); +static const struct snd_kcontrol_new asi1bclk_control = +SOC_DAPM_ENUM("ASI1_BCLK Route", asi1bclk_enum); + +/*static const char *asi2bclk_text[] = { + "DAC_CLK", + "DAC_MOD_CLK", + "ADC_CLK", + "ADC_MOD_CLK", +}; */ +SOC_ENUM_SINGLE_DECL(asi2bclk_enum, AIC3262_ASI2_BCLK_N_CNTL, 0, asibclk_text); +static const struct snd_kcontrol_new asi2bclk_control = +SOC_DAPM_ENUM("ASI2_BCLK Route", asi2bclk_enum); +/*static const char *asi3bclk_text[] = { + "DAC_CLK", + "DAC_MOD_CLK", + "ADC_CLK", + "ADC_MOD_CLK", +};*/ +SOC_ENUM_SINGLE_DECL(asi3bclk_enum, AIC3262_ASI3_BCLK_N_CNTL, 0, asibclk_text); +static const struct snd_kcontrol_new asi3bclk_control = +SOC_DAPM_ENUM("ASI3_BCLK Route", asi3bclk_enum); + +static const char *adc_mux_text[] = { + "Analog", + "Digital", +}; + +SOC_ENUM_SINGLE_DECL(adcl_enum, AIC3262_ADC_CHANNEL_POW, 4, adc_mux_text); +SOC_ENUM_SINGLE_DECL(adcr_enum, AIC3262_ADC_CHANNEL_POW, 2, adc_mux_text); + +static const struct snd_kcontrol_new adcl_mux = + SOC_DAPM_ENUM("Left ADC Route", adcl_enum); + +static const struct snd_kcontrol_new adcr_mux = + SOC_DAPM_ENUM("Right ADC Route", adcr_enum); + +/*static const char *dmicinput_text[] = { + "GPI1", + "GPI2", + "DIN1", + "DIN2", + "GPIO1", + "GPIO2", + "MCLK2", +}; +SOC_ENUM_SINGLE_DECL(dmicinput_enum, AIC3262_DMIC_INPUT_CNTL, 0, dmicinput_text); + +static const struct snd_kcontrol_new dmicinput_control = + SOC_DAPM_ENUM("DMICDAT Input Route", dmicinput_enum); +*/ +static int aic326x_hp_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + int status; + int count=20; + int reg_mask = 0; + if(w->shift == 1) {// Left HPL + reg_mask = AIC3262_HPL_POWER_MASK; + } + if(w->shift == 0) { // Right HPR + reg_mask = AIC3262_HPR_POWER_MASK; + } + switch(event){ + case SND_SOC_DAPM_POST_PMU: + do + { + status = snd_soc_read(w->codec,AIC3262_HP_FLAG); + count--; + + }while(((status & reg_mask) == 0x00) && count != 0 ); //wait until hp powered up + break; + case SND_SOC_DAPM_POST_PMD: + do + { + status = snd_soc_read(w->codec,AIC3262_HP_FLAG); + count--; + + }while(((status & reg_mask) == reg_mask) && count != 0 ); //wait until hp powered down + break; + default: + BUG(); + return -EINVAL; + } + return 0; +} + +/*********************************************************************** +Arguments : pointer variable to dapm_widget, + pointer variable to sound control, + integer to event +Return value : 0 +Purpose : Headset popup reduction and powering up dsps together + when they are in sync mode +************************************************************************/ +static int aic326x_dac_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + int status; + int count=20; + int reg_mask = 0; + int run_state_mask; + struct aic3262_priv *aic3262 = snd_soc_codec_get_drvdata(w->codec); + int sync_needed = 0, non_sync_state =0; + int other_dsp = 0, run_state = 0; + + + if(w->shift == 7) {// Left DAC + reg_mask = AIC3262_LDAC_POWER_MASK; + run_state_mask = AIC3262_COPS_MDSP_D_L; + } + if (w->shift == 6) { // Right DAC + reg_mask = AIC3262_RDAC_POWER_MASK; + run_state_mask = AIC3262_COPS_MDSP_D_R; + } + switch(event){ + case SND_SOC_DAPM_POST_PMU: + do + { + status = snd_soc_read(w->codec, AIC3262_DAC_FLAG); + count--; + } while(((status & reg_mask) == 0)&& count != 0); + + sync_needed = SYNC_STATE(aic3262); + non_sync_state = DSP_NON_SYNC_MODE(aic3262->dsp_runstate); + other_dsp = aic3262->dsp_runstate & AIC3262_COPS_MDSP_A; + + if( sync_needed && non_sync_state && other_dsp ) + { + run_state = get_runstate(aic3262->codec->control_data); + aic3262_dsp_pwrdwn_status(aic3262); + aic3262_dsp_pwrup(aic3262,run_state); + } + aic3262->dsp_runstate |= run_state_mask; + break; + case SND_SOC_DAPM_POST_PMD: + do + { + status = snd_soc_read(w->codec, AIC3262_DAC_FLAG); + count--; + } while(((status & reg_mask) == reg_mask)&& count != 0); + + aic3262->dsp_runstate = (aic3262->dsp_runstate & ~run_state_mask); + break; + default: + BUG(); + return -EINVAL; + } + return 0; +} + + + +static int aic326x_spk_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + int status; + int count=20; + int reg_mask; + + if(w->shift == 1) {// Left SPK + reg_mask = AIC3262_SPKL_POWER_MASK; + } + if(w->shift == 0) { // Right SPK + reg_mask = AIC3262_SPKR_POWER_MASK; + } + switch(event){ + case SND_SOC_DAPM_POST_PMU: + do + { + status = snd_soc_read(w->codec,AIC3262_HP_FLAG); + count--; + + }while(((status & reg_mask) == 0x00) && count != 0 ); //wait until spk powered up + break; + case SND_SOC_DAPM_POST_PMD: + do + { + status = snd_soc_read(w->codec,AIC3262_HP_FLAG); + count--; + + }while(((status & reg_mask) == reg_mask) && count != 0 ); //wait until spk powered up + break; + default: + BUG(); + return -EINVAL; + } + return 0; +} +static int pll_power_on_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + if(event == SND_SOC_DAPM_POST_PMU) + { + mdelay(10); + } + return 0; +} +static int aic3262_test_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + + return 0; +} + +static int aic3262_test_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); + + request_firmware_nowait(THIS_MODULE, FW_ACTION_HOTPLUG, + "tlv320aic3262_fw_v1.bin", codec->dev, GFP_KERNEL, + codec, aic3262_firmware_load); + + return 0; +} + +static int aic3262_set_mode_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); + struct aic3262_priv *priv_ds = snd_soc_codec_get_drvdata(codec); + + return 0; +} + +static int aic3262_set_mode_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); + struct aic3262_priv *priv_ds = snd_soc_codec_get_drvdata(codec); + + int next_mode=0,next_cfg=0; + + next_mode = (ucontrol->value.integer.value[0]>>8); + next_cfg = (ucontrol->value.integer.value[0])&0xFF; + if (priv_ds==NULL) + { + dev_err(codec->dev,"\nFirmware not loaded, no mode switch can occur\n"); + } + else + { + mutex_lock(&priv_ds->cfw_mutex); + aic3xxx_cfw_setmode_cfg(priv_ds->cfw_p,next_mode,next_cfg); + mutex_unlock(&priv_ds->cfw_mutex); + } + + return 0; +} +static int aic326x_adc_dsp_event(struct snd_soc_dapm_widget *w,struct snd_kcontrol *kcontrol, int event) +{ + int run_state=0; + int non_sync_state = 0,sync_needed = 0; + int other_dsp = 0; + int run_state_mask = 0; + struct aic3262_priv *aic3262 = snd_soc_codec_get_drvdata(w->codec); + + if(w->shift == 7) {// Left ADC + run_state_mask = AIC3262_COPS_MDSP_A_L; + } + if (w->shift == 6) { // Right ADC + run_state_mask = AIC3262_COPS_MDSP_A_R; + } + switch(event){ + case SND_SOC_DAPM_POST_PMU: + sync_needed = SYNC_STATE(aic3262); + non_sync_state = DSP_NON_SYNC_MODE(aic3262->dsp_runstate); + other_dsp = aic3262->dsp_runstate & AIC3262_COPS_MDSP_D; + if( sync_needed && non_sync_state && other_dsp ){ + run_state = get_runstate(aic3262->codec->control_data); + aic3262_dsp_pwrdwn_status(aic3262); + aic3262_dsp_pwrup(aic3262,run_state); + } + aic3262->dsp_runstate |= run_state_mask; + break; + case SND_SOC_DAPM_POST_PMD: + aic3262->dsp_runstate = (aic3262->dsp_runstate & ~run_state_mask) ; + break; + default: + BUG(); + return -EINVAL; + } + return 0; +} + +static const struct snd_soc_dapm_widget aic3262_dapm_widgets[] = { + /* TODO: Can we switch these off ? */ + SND_SOC_DAPM_AIF_IN("DIN1", "ASI1 Playback", 0, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_IN("DIN2", "ASI2 Playback", 0, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_IN("DIN3", "ASI3 Playback", 0, SND_SOC_NOPM, 0, 0), + + SND_SOC_DAPM_DAC_E("Left DAC", NULL, AIC3262_PASI_DAC_DP_SETUP, 7, 0, + aic326x_dac_event, SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_DAC_E("Right DAC", NULL, AIC3262_PASI_DAC_DP_SETUP, 6, 0, + aic326x_dac_event, SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD), + + + /* dapm widget (path domain) for HPL Output Mixer */ + SND_SOC_DAPM_MIXER("HPL Output Mixer", SND_SOC_NOPM, 0, 0, + &hpl_output_mixer_controls[0], + ARRAY_SIZE(hpl_output_mixer_controls)), + + /* dapm widget (path domain) for HPR Output Mixer */ + SND_SOC_DAPM_MIXER("HPR Output Mixer", SND_SOC_NOPM, 0, 0, + &hpr_output_mixer_controls[0], + ARRAY_SIZE(hpr_output_mixer_controls)), + + + SND_SOC_DAPM_PGA_E("HPL Driver", AIC3262_HP_AMP_CNTL_R1, 1, 0, NULL, 0, aic326x_hp_event, SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_PGA_E("HPR Driver", AIC3262_HP_AMP_CNTL_R1, 0, 0, NULL, 0, aic326x_hp_event, SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD), + + + /* dapm widget (path domain) for LOL Output Mixer */ + SND_SOC_DAPM_MIXER("LOL Output Mixer", SND_SOC_NOPM, 0, 0, + &lol_output_mixer_controls[0], + ARRAY_SIZE(lol_output_mixer_controls)), + + /* dapm widget (path domain) for LOR Output Mixer mixer */ + SND_SOC_DAPM_MIXER("LOR Output Mixer", SND_SOC_NOPM, 0, 0, + &lor_output_mixer_controls[0], + ARRAY_SIZE(lor_output_mixer_controls)), + + SND_SOC_DAPM_PGA("LOL Driver", AIC3262_LINE_AMP_CNTL_R1, 1, 0, NULL, 0), + SND_SOC_DAPM_PGA("LOR Driver", AIC3262_LINE_AMP_CNTL_R1, 0, 0, NULL, 0), + + + /* dapm widget (path domain) for SPKL Output Mixer */ + SND_SOC_DAPM_MIXER("SPKL Output Mixer", SND_SOC_NOPM, 0, 0, + &spkl_output_mixer_controls[0], + ARRAY_SIZE(spkl_output_mixer_controls)), + + /* dapm widget (path domain) for SPKR Output Mixer */ + SND_SOC_DAPM_MIXER("SPKR Output Mixer", SND_SOC_NOPM, 0, 0, + &spkr_output_mixer_controls[0], + ARRAY_SIZE(spkr_output_mixer_controls)), + + SND_SOC_DAPM_PGA_E("SPKL Driver",AIC3262_SPK_AMP_CNTL_R1, 1, 0, NULL, 0, + aic326x_spk_event, SND_SOC_DAPM_POST_PMD | SND_SOC_DAPM_POST_PMU), + SND_SOC_DAPM_PGA_E("SPKR Driver", AIC3262_SPK_AMP_CNTL_R1, 0, 0, NULL, 0, + aic326x_spk_event, SND_SOC_DAPM_POST_PMD | SND_SOC_DAPM_POST_PMU), + + + /* dapm widget (path domain) for SPKR Output Mixer */ + SND_SOC_DAPM_MIXER("REC Output Mixer", SND_SOC_NOPM, 0, 0, + &rec_output_mixer_controls[0], + ARRAY_SIZE(rec_output_mixer_controls)), + + SND_SOC_DAPM_PGA("RECP Driver", AIC3262_REC_AMP_CNTL_R5, 7, 0, NULL, 0), + SND_SOC_DAPM_PGA("RECM Driver", AIC3262_REC_AMP_CNTL_R5, 6, 0, NULL, 0), + + + SND_SOC_DAPM_MUX("ASI1LIN Route", + SND_SOC_NOPM, 0, 0, &asi1lin_control), + SND_SOC_DAPM_MUX("ASI1RIN Route", + SND_SOC_NOPM, 0, 0, &asi1rin_control), + SND_SOC_DAPM_MUX("ASI2LIN Route", + SND_SOC_NOPM, 0, 0, &asi2lin_control), + SND_SOC_DAPM_MUX("ASI2RIN Route", + SND_SOC_NOPM, 0, 0, &asi2rin_control), + SND_SOC_DAPM_MUX("ASI3LIN Route", + SND_SOC_NOPM, 0, 0, &asi3lin_control), + SND_SOC_DAPM_MUX("ASI3RIN Route", + SND_SOC_NOPM, 0, 0, &asi3rin_control), + + SND_SOC_DAPM_PGA("ASI1LIN", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_PGA("ASI1RIN", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_PGA("ASI2LIN", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_PGA("ASI2RIN", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_PGA("ASI3LIN", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_PGA("ASI3RIN", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_PGA("ASI1MonoMixIN", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_PGA("ASI2MonoMixIN", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_PGA("ASI3MonoMixIN", SND_SOC_NOPM, 0, 0, NULL, 0), + /* TODO: Can we switch the ASIxIN off? */ + SND_SOC_DAPM_PGA("ASI1IN Port", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_PGA("ASI2IN Port", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_PGA("ASI3IN Port", SND_SOC_NOPM, 0, 0, NULL, 0), + + SND_SOC_DAPM_MUX("DAC MiniDSP IN1 Route", + SND_SOC_NOPM, 0, 0, &dacminidspin1_control), + SND_SOC_DAPM_MUX("DAC MiniDSP IN2 Route", + SND_SOC_NOPM, 0, 0, &dacminidspin2_control), + SND_SOC_DAPM_MUX("DAC MiniDSP IN3 Route", + SND_SOC_NOPM, 0, 0, &dacminidspin3_control), + + SND_SOC_DAPM_VIRT_MUX("ADC DAC Route", + SND_SOC_NOPM, 0, 0, &adcdacroute_control), + + SND_SOC_DAPM_PGA("CM", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_PGA("CM1L", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_PGA("CM2L", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_PGA("CM1R", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_PGA("CM2R", SND_SOC_NOPM, 0, 0, NULL, 0), + + /* TODO: Can we switch these off ? */ + SND_SOC_DAPM_AIF_OUT("DOUT1","ASI1 Capture", 0, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_OUT("DOUT2", "ASI2 Capture",0, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_OUT("DOUT3", "ASI3 Capture",0, SND_SOC_NOPM, 0, 0), + + SND_SOC_DAPM_MUX("DOUT1 Route", + SND_SOC_NOPM, 0, 0, &dout1_control), + SND_SOC_DAPM_MUX("DOUT2 Route", + SND_SOC_NOPM, 0, 0, &dout2_control), + SND_SOC_DAPM_MUX("DOUT3 Route", + SND_SOC_NOPM, 0, 0, &dout3_control), + + SND_SOC_DAPM_PGA("ASI1OUT", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_PGA("ASI2OUT", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_PGA("ASI3OUT", SND_SOC_NOPM, 0, 0, NULL, 0), + + + SND_SOC_DAPM_MUX("ASI1OUT Route", + SND_SOC_NOPM, 0, 0, &asi1out_control), + SND_SOC_DAPM_MUX("ASI2OUT Route", + SND_SOC_NOPM, 0, 0, &asi2out_control), + SND_SOC_DAPM_MUX("ASI3OUT Route", + SND_SOC_NOPM, 0, 0, &asi3out_control), + + /* TODO: Can we switch the ASI1 OUT1 off? */ + /* TODO: Can we switch them off? */ + SND_SOC_DAPM_PGA("ADC MiniDSP OUT1", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_PGA("ADC MiniDSP OUT2", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_PGA("ADC MiniDSP OUT3", SND_SOC_NOPM, 0, 0, NULL, 0), + + ///M SND_SOC_DAPM_PGA("GPI1 PIN", SND_SOC_NOPM, 0, 0, NULL, 0), + ///M SND_SOC_DAPM_PGA("GPI2 PIN", SND_SOC_NOPM, 0, 0, NULL, 0), + ///M SND_SOC_DAPM_PGA("DIN1 PIN", SND_SOC_NOPM, 0, 0, NULL, 0), + ///M SND_SOC_DAPM_PGA("DIN2 PIN", SND_SOC_NOPM, 0, 0, NULL, 0), + ///M SND_SOC_DAPM_PGA("GPIO1 PIN", SND_SOC_NOPM, 0, 0, NULL, 0), + ///M SND_SOC_DAPM_PGA("GPIO2 PIN", SND_SOC_NOPM, 0, 0, NULL, 0), + ///M SND_SOC_DAPM_PGA("MCLK2 PIN", SND_SOC_NOPM, 0, 0, NULL, 0), + +// SND_SOC_DAPM_MUX("DMICDAT Input Route", +// SND_SOC_NOPM, 0, 0, &dmicinput_control), + + SND_SOC_DAPM_MUX("Left ADC Route", SND_SOC_NOPM,0, 0, &adcl_mux), + SND_SOC_DAPM_MUX("Right ADC Route", SND_SOC_NOPM,0, 0, &adcr_mux), + + SND_SOC_DAPM_ADC_E("Left ADC", NULL, AIC3262_ADC_CHANNEL_POW, 7, 0, + aic326x_adc_dsp_event, SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_ADC_E("Right ADC", NULL, AIC3262_ADC_CHANNEL_POW, 6, 0, + aic326x_adc_dsp_event, SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD), + + SND_SOC_DAPM_PGA("Left MicPGA",AIC3262_MICL_PGA, 7, 1, NULL, 0), + SND_SOC_DAPM_PGA("Right MicPGA",AIC3262_MICR_PGA, 7, 1, NULL, 0), + + SND_SOC_DAPM_PGA("MAL PGA", AIC3262_MA_CNTL, 3, 0, NULL, 0), + SND_SOC_DAPM_PGA("MAR PGA", AIC3262_MA_CNTL, 2, 0, NULL, 0), + + /* dapm widget for MAL PGA Mixer*/ + SND_SOC_DAPM_MIXER("MAL PGA Mixer", SND_SOC_NOPM, 0, 0, + &mal_pga_mixer_controls[0], + ARRAY_SIZE(mal_pga_mixer_controls)), + + /* dapm widget for MAR PGA Mixer*/ + SND_SOC_DAPM_MIXER("MAR PGA Mixer", SND_SOC_NOPM, 0, 0, + &mar_pga_mixer_controls[0], + ARRAY_SIZE(mar_pga_mixer_controls)), + + /* dapm widget for Left Input Mixer*/ + SND_SOC_DAPM_MIXER("Left Input Mixer", SND_SOC_NOPM, 0, 0, + &left_input_mixer_controls[0], + ARRAY_SIZE(left_input_mixer_controls)), + + /* dapm widget for Right Input Mixer*/ + SND_SOC_DAPM_MIXER("Right Input Mixer", SND_SOC_NOPM, 0, 0, + &right_input_mixer_controls[0], + ARRAY_SIZE(right_input_mixer_controls)), + + SND_SOC_DAPM_OUTPUT("HPL"), + SND_SOC_DAPM_OUTPUT("HPR"), + SND_SOC_DAPM_OUTPUT("LOL"), + SND_SOC_DAPM_OUTPUT("LOR"), + SND_SOC_DAPM_OUTPUT("SPKL"), + SND_SOC_DAPM_OUTPUT("SPKR"), + SND_SOC_DAPM_OUTPUT("RECP"), + SND_SOC_DAPM_OUTPUT("RECM"), + + SND_SOC_DAPM_INPUT("IN1L"), + SND_SOC_DAPM_INPUT("IN2L"), + SND_SOC_DAPM_INPUT("IN3L"), + SND_SOC_DAPM_INPUT("IN4L"), + SND_SOC_DAPM_INPUT("IN1R"), + SND_SOC_DAPM_INPUT("IN2R"), + SND_SOC_DAPM_INPUT("IN3R"), + SND_SOC_DAPM_INPUT("IN4R"), +// SND_SOC_DAPM_INPUT("DMICDAT"), + SND_SOC_DAPM_INPUT("Left DMIC"), + SND_SOC_DAPM_INPUT("Right DMIC"), + + + SND_SOC_DAPM_MICBIAS("Mic Bias Ext", AIC3262_MIC_BIAS_CNTL, 6, 0), + SND_SOC_DAPM_MICBIAS("Mic Bias Int", AIC3262_MIC_BIAS_CNTL, 2, 0), + + + SND_SOC_DAPM_SUPPLY("PLLCLK",AIC3262_PLL_PR_POW_REG,7,0,pll_power_on_event,SND_SOC_DAPM_POST_PMU), + SND_SOC_DAPM_SUPPLY("DACCLK",AIC3262_NDAC_DIV_POW_REG,7,0, NULL, 0), + SND_SOC_DAPM_SUPPLY("CODEC_CLK_IN",SND_SOC_NOPM,0,0, NULL, 0), + SND_SOC_DAPM_SUPPLY("DAC_MOD_CLK",AIC3262_MDAC_DIV_POW_REG,7,0, NULL, 0), + SND_SOC_DAPM_SUPPLY("ADCCLK",AIC3262_NADC_DIV_POW_REG,7,0, NULL, 0), + SND_SOC_DAPM_SUPPLY("ADC_MOD_CLK",AIC3262_MADC_DIV_POW_REG,7,0, NULL, 0), + SND_SOC_DAPM_SUPPLY("ASI1_BCLK",AIC3262_ASI1_BCLK_N,7,0, NULL, 0), + SND_SOC_DAPM_SUPPLY("ASI1_WCLK",AIC3262_ASI1_WCLK_N,7,0, NULL, 0), + SND_SOC_DAPM_SUPPLY("ASI2_BCLK",AIC3262_ASI2_BCLK_N,7,0, NULL, 0), + SND_SOC_DAPM_SUPPLY("ASI2_WCLK",AIC3262_ASI2_WCLK_N,7,0, NULL, 0), + SND_SOC_DAPM_SUPPLY("ASI3_BCLK",AIC3262_ASI3_BCLK_N,7,0, NULL, 0), + SND_SOC_DAPM_SUPPLY("ASI3_WCLK",AIC3262_ASI3_WCLK_N,7,0, NULL, 0), + SND_SOC_DAPM_MUX("ASI1_BCLK Route", + SND_SOC_NOPM, 0, 0, &asi1bclk_control), + SND_SOC_DAPM_MUX("ASI2_BCLK Route", + SND_SOC_NOPM, 0, 0, &asi2bclk_control), + SND_SOC_DAPM_MUX("ASI3_BCLK Route", + SND_SOC_NOPM, 0, 0, &asi3bclk_control), +}; + +static const struct snd_soc_dapm_route aic3262_dapm_routes[] ={ + /* TODO: Do we need only DACCLK for ASIIN's and ADCCLK for ASIOUT??? */ + /* Clock portion */ + {"CODEC_CLK_IN", NULL, "PLLCLK"}, + {"DACCLK", NULL, "CODEC_CLK_IN"}, + {"ADCCLK", NULL, "CODEC_CLK_IN"}, + {"DAC_MOD_CLK", NULL, "DACCLK"}, +#ifdef AIC3262_SYNC_MODE + {"ADC_MOD_CLK", NULL,"DACCLK"}, +#else + {"ADC_MOD_CLK", NULL, "ADCCLK"}, +#endif + + {"ASI1_BCLK Route","DAC_CLK","DACCLK"}, + {"ASI1_BCLK Route","DAC_MOD_CLK","DAC_MOD_CLK"}, + {"ASI1_BCLK Route","ADC_CLK","ADCCLK"}, + {"ASI1_BCLK Route","ADC_MOD_CLK","ADC_MOD_CLK"}, + + {"ASI2_BCLK Route","DAC_CLK","DACCLK"}, + {"ASI2_BCLK Route","DAC_MOD_CLK","DAC_MOD_CLK"}, + {"ASI2_BCLK Route","ADC_CLK","ADCCLK"}, + {"ASI2_BCLK Route","ADC_MOD_CLK","ADC_MOD_CLK"}, + + {"ASI3_BCLK Route","DAC_CLK","DACCLK"}, + {"ASI3_BCLK Route","DAC_MOD_CLK","DAC_MOD_CLK"}, + {"ASI3_BCLK Route","ADC_CLK","ADCCLK"}, + {"ASI3_BCLK Route","ADC_MOD_CLK","ADC_MOD_CLK"}, + + {"ASI1_BCLK", NULL, "ASI1_BCLK Route"}, + {"ASI2_BCLK", NULL, "ASI2_BCLK Route"}, + {"ASI3_BCLK", NULL, "ASI3_BCLK Route"}, + + + {"DIN1", NULL , "PLLCLK"}, + {"DIN1", NULL , "DACCLK"}, + {"DIN1", NULL , "ADCCLK"}, + {"DIN1", NULL , "DAC_MOD_CLK"}, + {"DIN1", NULL , "ADC_MOD_CLK"}, + + {"DOUT1", NULL , "PLLCLK"}, + {"DOUT1", NULL , "DACCLK"}, + {"DOUT1", NULL , "ADCCLK"}, + {"DOUT1", NULL , "DAC_MOD_CLK"}, + {"DOUT1", NULL , "ADC_MOD_CLK"}, +#ifdef AIC3262_ASI1_MASTER + {"DIN1", NULL , "ASI1_BCLK"}, + {"DOUT1", NULL , "ASI1_BCLK"}, + {"DIN1", NULL , "ASI1_WCLK"}, + {"DOUT1", NULL , "ASI1_WCLK"}, +#else + +#endif + + {"DIN2", NULL , "PLLCLK"}, + {"DIN2", NULL , "DACCLK"}, + {"DIN2", NULL , "ADCCLK"}, + {"DIN2", NULL , "DAC_MOD_CLK"}, + {"DIN2", NULL , "ADC_MOD_CLK"}, + + {"DOUT2", NULL , "PLLCLK"}, + {"DOUT2", NULL , "DACCLK"}, + {"DOUT2", NULL , "ADCCLK"}, + {"DOUT2", NULL , "DAC_MOD_CLK"}, + {"DOUT2", NULL , "ADC_MOD_CLK"}, + +#ifdef AIC3262_ASI2_MASTER + {"DIN2", NULL , "ASI2_BCLK"}, + {"DOUT2", NULL , "ASI2_BCLK"}, + {"DIN2", NULL , "ASI2_WCLK"}, + {"DOUT2", NULL , "ASI2_WCLK"}, +#else + +#endif + {"DIN3", NULL , "PLLCLK"}, + {"DIN3", NULL , "DACCLK"}, + {"DIN3", NULL , "ADCCLK"}, + {"DIN3", NULL , "DAC_MOD_CLK"}, + {"DIN3", NULL , "ADC_MOD_CLK"}, + + + {"DOUT3", NULL , "PLLCLK"}, + {"DOUT3", NULL , "DACCLK"}, + {"DOUT3", NULL , "ADCCLK"}, + {"DOUT3", NULL , "DAC_MOD_CLK"}, + {"DOUT3", NULL , "ADC_MOD_CLK"}, + +#ifdef AIC3262_ASI3_MASTER + {"DIN3", NULL , "ASI3_BCLK"}, + {"DOUT3", NULL , "ASI3_BCLK"}, + {"DIN3", NULL , "ASI3_WCLK"}, + {"DOUT3", NULL , "ASI3_WCLK"}, +#else + +#endif + /* Playback (DAC) Portion */ + {"HPL Output Mixer","LDAC Switch","Left DAC"}, + {"HPL Output Mixer","MAL Switch","MAL PGA"}, + {"HPL Output Mixer","LOL-B1 Volume","LOL"}, + + {"HPR Output Mixer","LOR-B1 Volume","LOR"}, + {"HPR Output Mixer","LDAC Switch","Left DAC"}, + {"HPR Output Mixer","RDAC Switch","Right DAC"}, + {"HPR Output Mixer","MAR Switch","MAR PGA"}, + + {"HPL Driver",NULL,"HPL Output Mixer"}, + {"HPR Driver",NULL,"HPR Output Mixer"}, + + {"HPL",NULL,"HPL Driver"}, + {"HPR",NULL,"HPR Driver"}, + + {"LOL Output Mixer","MAL Switch","MAL PGA"}, + {"LOL Output Mixer","IN1L-B Switch","IN1L"}, + {"LOL Output Mixer","LDAC Switch","Left DAC"}, + {"LOL Output Mixer","RDAC Switch","Right DAC"}, + + {"LOR Output Mixer","LOL Switch","LOL"}, + {"LOR Output Mixer","RDAC Switch","Right DAC"}, + {"LOR Output Mixer","MAR Switch","MAR PGA"}, + {"LOR Output Mixer","IN1R-B Switch","IN1R"}, + + {"LOL Driver",NULL,"LOL Output Mixer"}, + {"LOR Driver",NULL,"LOR Output Mixer"}, + + {"LOL",NULL,"LOL Driver"}, + {"LOR",NULL,"LOR Driver"}, + + {"REC Output Mixer","LOL-B2 Volume","LOL"}, + {"REC Output Mixer","IN1L Volume","IN1L"}, + {"REC Output Mixer","IN1R Volume","IN1R"}, + {"REC Output Mixer","LOR-B2 Volume","LOR"}, + + {"RECP Driver",NULL,"REC Output Mixer"}, + {"RECM Driver",NULL,"REC Output Mixer"}, + + {"RECP",NULL,"RECP Driver"}, + {"RECM",NULL,"RECM Driver"}, + + {"SPKL Output Mixer","MAL Switch","MAL PGA"}, + {"SPKL Output Mixer","LOL Volume","LOL"}, + {"SPKL Output Mixer","SPR_IN Switch","SPKR Output Mixer"}, + + {"SPKR Output Mixer", "LOR Volume","LOR"}, + {"SPKR Output Mixer", "MAR Switch","MAR PGA"}, + + + {"SPKL Driver",NULL,"SPKL Output Mixer"}, + {"SPKR Driver",NULL,"SPKR Output Mixer"}, + + {"SPKL",NULL,"SPKL Driver"}, + {"SPKR",NULL,"SPKR Driver"}, + /* ASI Input routing */ + {"ASI1LIN", NULL, "DIN1"}, + {"ASI1RIN", NULL, "DIN1"}, + {"ASI1MonoMixIN", NULL, "DIN1"}, + {"ASI2LIN", NULL, "DIN2"}, + {"ASI2RIN", NULL, "DIN2"}, + {"ASI2MonoMixIN", NULL, "DIN2"}, + {"ASI3LIN", NULL, "DIN3"}, + {"ASI3RIN", NULL, "DIN3"}, + {"ASI3MonoMixIN", NULL, "DIN3"}, + + {"ASI1LIN Route","ASI1 Left In","ASI1LIN"}, + {"ASI1LIN Route","ASI1 Right In","ASI1RIN"}, + {"ASI1LIN Route","ASI1 MonoMix In","ASI1MonoMixIN"}, + + {"ASI1RIN Route","ASI1 Right In","ASI1RIN"}, + {"ASI1RIN Route","ASI1 Left In","ASI1LIN"}, + {"ASI1RIN Route","ASI1 MonoMix In","ASI1MonoMixIN"}, + + + {"ASI2LIN Route","ASI2 Left In","ASI2LIN"}, + {"ASI2LIN Route","ASI2 Right In","ASI2RIN"}, + {"ASI2LIN Route","ASI2 MonoMix In","ASI2MonoMixIN"}, + + {"ASI2RIN Route","ASI2 Right In","ASI2RIN"}, + {"ASI2RIN Route","ASI2 Left In","ASI2LIN"}, + {"ASI2RIN Route","ASI2 MonoMix In","ASI2MonoMixIN"}, + + + {"ASI3LIN Route","ASI3 Left In","ASI3LIN"}, + {"ASI3LIN Route","ASI3 Right In","ASI3RIN"}, + {"ASI3LIN Route","ASI3 MonoMix In","ASI3MonoMixIN"}, + + {"ASI3RIN Route","ASI3 Right In","ASI3RIN"}, + {"ASI3RIN Route","ASI3 Left In","ASI3LIN"}, + {"ASI3RIN Route","ASI3 MonoMix In","ASI3MonoMixIN"}, + + {"ASI1IN Port", NULL, "ASI1LIN Route"}, + {"ASI1IN Port", NULL, "ASI1RIN Route"}, + {"ASI2IN Port", NULL, "ASI2LIN Route"}, + {"ASI2IN Port", NULL, "ASI2RIN Route"}, + {"ASI3IN Port", NULL, "ASI3LIN Route"}, + {"ASI3IN Port", NULL, "ASI3RIN Route"}, + + {"DAC MiniDSP IN1 Route","ASI1 In","ASI1IN Port"}, + {"DAC MiniDSP IN1 Route","ASI2 In","ASI2IN Port"}, + {"DAC MiniDSP IN1 Route","ASI3 In","ASI3IN Port"}, + {"DAC MiniDSP IN1 Route","ADC MiniDSP Out","ADC MiniDSP OUT1"}, + + {"DAC MiniDSP IN2 Route","ASI1 In","ASI1IN Port"}, + {"DAC MiniDSP IN2 Route","ASI2 In","ASI2IN Port"}, + {"DAC MiniDSP IN2 Route","ASI3 In","ASI3IN Port"}, + + {"DAC MiniDSP IN3 Route","ASI1 In","ASI1IN Port"}, + {"DAC MiniDSP IN3 Route","ASI2 In","ASI2IN Port"}, + {"DAC MiniDSP IN3 Route","ASI3 In","ASI3IN Port"}, + + + {"Left DAC", "NULL","DAC MiniDSP IN1 Route"}, + {"Right DAC", "NULL","DAC MiniDSP IN1 Route"}, + {"Left DAC", "NULL","DAC MiniDSP IN2 Route"}, + {"Right DAC", "NULL","DAC MiniDSP IN2 Route"}, + {"Left DAC", "NULL","DAC MiniDSP IN3 Route"}, + {"Right DAC", "NULL","DAC MiniDSP IN3 Route"}, + + + + + /* Mixer Amplifier */ + + {"MAL PGA Mixer", "IN1L Switch","IN1L"}, + {"MAL PGA Mixer", "Left MicPGA Volume","Left MicPGA"}, + + {"MAL PGA", NULL, "MAL PGA Mixer"}, + + + {"MAR PGA Mixer", "IN1R Switch","IN1R"}, + {"MAR PGA Mixer", "Right MicPGA Volume","Right MicPGA"}, + + {"MAR PGA", NULL, "MAR PGA Mixer"}, + + + /* Virtual connection between DAC and ADC for miniDSP IPC */ + {"ADC DAC Route", "On", "Left ADC"}, + {"ADC DAC Route", "On", "Right ADC"}, + + {"Left DAC", NULL, "ADC DAC Route"}, + {"Right DAC", NULL, "ADC DAC Route"}, + + /* Capture (ADC) portions */ + /* Left Positive PGA input */ + {"Left Input Mixer","IN1L Switch","IN1L"}, + {"Left Input Mixer","IN2L Switch","IN2L"}, + {"Left Input Mixer","IN3L Switch","IN3L"}, + {"Left Input Mixer","IN4L Switch","IN4L"}, + {"Left Input Mixer","IN1R Switch","IN1R"}, + /* Left Negative PGA input */ + {"Left Input Mixer","IN2R Switch","IN2R"}, + {"Left Input Mixer","IN3R Switch","IN3R"}, + {"Left Input Mixer","IN4R Switch","IN4R"}, + {"Left Input Mixer","CM2L Switch","CM2L"}, + {"Left Input Mixer","CM1L Switch","CM1L"}, + + + /* Right Positive PGA Input */ + {"Right Input Mixer","IN1R Switch","IN1R"}, + {"Right Input Mixer","IN2R Switch","IN2R"}, + {"Right Input Mixer","IN3R Switch","IN3R"}, + {"Right Input Mixer","IN4R Switch","IN4R"}, + {"Right Input Mixer","IN2L Switch","IN2L"}, + /* Right Negative PGA Input */ + {"Right Input Mixer","IN1L Switch","IN1L"}, + {"Right Input Mixer","IN3L Switch","IN3L"}, + {"Right Input Mixer","IN4L Switch","IN4L"}, + {"Right Input Mixer","CM1R Switch","CM1R"}, + {"Right Input Mixer","CM2R Switch","CM2R"}, + + + {"CM1L", NULL, "CM"}, + {"CM2L", NULL, "CM"}, + {"CM1R", NULL, "CM"}, + {"CM1R", NULL, "CM"}, + + {"Left MicPGA",NULL,"Left Input Mixer"}, + {"Right MicPGA",NULL,"Right Input Mixer"}, + +/* {"DMICDAT Input Route","GPI1","GPI1 Pin"}, + {"DMICDAT Input Route","GPI2","GPI2 Pin"}, + {"DMICDAT Input Route","DIN1","DIN1 Pin"}, + {"DMICDAT Input Route","GPIO1","GPIO1 Pin"}, + {"DMICDAT Input Route","GPIO2","GPIO2 Pin"}, + {"DMICDAT Input Route","MCLK2","MCLK2 Pin"}, + + {"DMICDAT", NULL, "DMICDAT Input Route"}, + {"DMICDAT", NULL, "ADC_MOD_CLK"}, +*/ +// {"Left DMIC", NULL, "DMICDAT"}, +// {"Right DMIC", NULL, "DMICDAT"}, + + {"Left ADC Route", "Analog","Left MicPGA"}, + {"Left ADC Route", "Digital", "Left DMIC"}, + + {"Right ADC Route", "Analog","Right MicPGA"}, + {"Right ADC Route", "Digital", "Right DMIC"}, + + {"Left ADC", NULL, "Left ADC Route"}, + {"Right ADC", NULL, "Right ADC Route"}, + + + /* ASI Output Routing */ + {"ADC MiniDSP OUT1", NULL, "Left ADC"}, + {"ADC MiniDSP OUT1", NULL, "Right ADC"}, + {"ADC MiniDSP OUT2", NULL, "Left ADC"}, + {"ADC MiniDSP OUT2", NULL, "Right ADC"}, + {"ADC MiniDSP OUT3", NULL, "Left ADC"}, + {"ADC MiniDSP OUT3", NULL, "Right ADC"}, + + + {"ASI1OUT Route", "ADC MiniDSP Out1","ADC MiniDSP OUT1"},// Port 1 + {"ASI1OUT Route", "ASI1In Bypass","ASI1IN Port"}, + {"ASI1OUT Route", "ASI2In Bypass","ASI2IN Port"}, + {"ASI1OUT Route", "ASI3In Bypass","ASI3IN Port"}, + + {"ASI2OUT Route", "ADC MiniDSP Out1","ADC MiniDSP OUT1"},// Port 1 + {"ASI2OUT Route", "ASI1In Bypass","ASI1IN Port"}, + {"ASI2OUT Route", "ASI2In Bypass","ASI2IN Port"}, + {"ASI2OUT Route", "ASI3In Bypass","ASI3IN Port"}, + {"ASI2OUT Route", "ADC MiniDSP Out2","ADC MiniDSP OUT2"},// Port 2 + + {"ASI3OUT Route", "ADC MiniDSP Out1","ADC MiniDSP OUT1"},// Port 1 + {"ASI3OUT Route", "ASI1In Bypass","ASI1IN Port"}, + {"ASI3OUT Route", "ASI2In Bypass","ASI2IN Port"}, + {"ASI3OUT Route", "ASI3In Bypass","ASI3IN Port"}, + {"ASI3OUT Route", "ADC MiniDSP Out3","ADC MiniDSP OUT3"},// Port 3 + + {"ASI1OUT",NULL,"ASI1OUT Route"}, + {"ASI2OUT",NULL,"ASI2OUT Route"}, + {"ASI3OUT",NULL,"ASI3OUT Route"}, + + + {"DOUT1 Route", "ASI1 Out", "ASI1OUT"}, + {"DOUT1 Route", "DIN1 Bypass", "DIN1"}, + {"DOUT1 Route", "DIN2 Bypass", "DIN2"}, + {"DOUT1 Route", "DIN3 Bypass", "DIN3"}, + + {"DOUT2 Route", "ASI2 Out", "ASI2OUT"}, + {"DOUT2 Route", "DIN1 Bypass", "DIN1"}, + {"DOUT2 Route", "DIN2 Bypass", "DIN2"}, + {"DOUT2 Route", "DIN3 Bypass", "DIN3"}, + + {"DOUT3 Route", "ASI3 Out", "ASI3OUT"}, + {"DOUT3 Route", "DIN1 Bypass", "DIN1"}, + {"DOUT3 Route", "DIN2 Bypass", "DIN2"}, + {"DOUT3 Route", "DIN3 Bypass", "DIN3"}, + + {"DOUT1", NULL, "DOUT1 Route"}, + {"DOUT2", NULL, "DOUT2 Route"}, + {"DOUT3", NULL, "DOUT3 Route"}, + + +}; +#define AIC3262_DAPM_ROUTE_NUM (sizeof(aic3262_dapm_routes)/sizeof(struct snd_soc_dapm_route)) + + +/* aic3262_firmware_load + This function is called by the request_firmware_nowait function as soon + as the firmware has been loaded from the file. The firmware structure + contains the data and the size of the firmware loaded. + */ + +void aic3262_firmware_load(const struct firmware *fw, void *context) +{ + struct snd_soc_codec *codec = context; + struct aic3262_priv *private_ds = snd_soc_codec_get_drvdata(codec); + + mutex_lock(&private_ds->cfw_mutex); + if(private_ds->cur_fw != NULL) release_firmware(private_ds->cur_fw); + private_ds->cur_fw = NULL ; + if (fw==NULL) + { + dev_dbg(codec->dev,"Default firmware load\n"); + /*Request firmware failed due to non availbility of firmware file. Hence,Default firmware is getting loaded */ + if(!private_ds->isdefault_fw) // default firmware is already loaded + { + aic3xxx_cfw_reload( private_ds->cfw_p,default_firmware,sizeof(default_firmware) ); + private_ds->isdefault_fw = 1; + //init function for transition + aic3xxx_cfw_transition(private_ds->cfw_p,"INIT"); + } + } + else + { + dev_dbg(codec->dev,"Firmware load\n"); + private_ds->cur_fw = fw; + aic3xxx_cfw_reload(private_ds->cfw_p,(void*)fw->data,fw->size); + private_ds->isdefault_fw = 0; + //init function for transition + aic3xxx_cfw_transition(private_ds->cfw_p,"INIT"); + } + // when new firmware is loaded, mode is changed to zero and config is changed to zero + aic3xxx_cfw_setmode_cfg(private_ds->cfw_p,0,0); + mutex_unlock(&private_ds->cfw_mutex); +} + +/* + ***************************************************************************** + * Function Definitions + ***************************************************************************** + */ + +/* headset work and headphone/headset jack interrupt handlers */ + + +static void aic3262_hs_jack_report(struct snd_soc_codec *codec, + struct snd_soc_jack *jack, int report) +{ + struct aic3262_priv *aic3262 = snd_soc_codec_get_drvdata(codec); + int status, state = 0; + + mutex_lock(&aic3262->mutex); + + // Sync status + status = snd_soc_read(codec, AIC3262_DAC_FLAG); + // We will check only stereo MIC and headphone + if(status & AIC3262_JACK_WITH_STEREO_HS) + state |= SND_JACK_HEADPHONE; + if(status & AIC3262_JACK_WITH_MIC) + state |= SND_JACK_MICROPHONE; + + + mutex_unlock(&aic3262->mutex); + + snd_soc_jack_report(jack, state, report); + if (&aic3262->hs_jack.sdev) + switch_set_state(&aic3262->hs_jack.sdev, !!state); +} + +void aic3262_hs_jack_detect(struct snd_soc_codec *codec, + struct snd_soc_jack *jack, int report) +{ + struct aic3262_priv *aic3262 = snd_soc_codec_get_drvdata(codec); + struct aic3262_jack_data *hs_jack = &aic3262->hs_jack; + + hs_jack->jack = jack; + hs_jack->report = report; + + aic3262_hs_jack_report(codec, hs_jack->jack, hs_jack->report); +} +EXPORT_SYMBOL_GPL(aic3262_hs_jack_detect); + +static void aic3262_accessory_work(struct work_struct *work) +{ + struct aic3262_priv *aic3262 = container_of(work, + struct aic3262_priv, delayed_work.work); + struct snd_soc_codec *codec = aic3262->codec; + struct aic3262_jack_data *hs_jack = &aic3262->hs_jack; + + aic3262_hs_jack_report(codec, hs_jack->jack, hs_jack->report); +} + +/* audio interrupt handler */ +static irqreturn_t aic3262_audio_handler(int irq, void *data) +{ + struct snd_soc_codec *codec = data; + struct aic3262_priv *aic3262 = snd_soc_codec_get_drvdata(codec); + + queue_delayed_work(aic3262->workqueue, &aic3262->delayed_work, + msecs_to_jiffies(200)); + + return IRQ_HANDLED; +} + +/* + *---------------------------------------------------------------------------- + * Function : aic3262_write_reg_cache + * Purpose : This function is to write aic3262 register cache + * + *---------------------------------------------------------------------------- + */ +void aic3262_write_reg_cache(struct snd_soc_codec *codec, + u16 reg, u8 value) +{ + u8 *cache = codec->reg_cache; + + if (reg >= AIC3262_CACHEREGNUM) { + return; + } + cache[reg] = value; + return; +} + +/* + *---------------------------------------------------------------------------- + * Function : aic3262_codec_read + * Purpose : This function is to read the aic3262 register space. + * + *---------------------------------------------------------------------------- + */ +unsigned int aic3262_codec_read(struct snd_soc_codec *codec, unsigned int reg) +{ + + u8 value; + aic326x_reg_union *aic_reg = (aic326x_reg_union *)® + value = aic3262_reg_read(codec->control_data, reg); + dev_dbg(codec->dev,"p %d ,r 30 %x %x \n",aic_reg->aic326x_register.page,aic_reg->aic326x_register.offset,value); + return value; +} + +/* + *---------------------------------------------------------------------------- + * Function : aic3262_write + * Purpose : This function is to write to the aic3262 register space. + * + *---------------------------------------------------------------------------- + */ +int aic3262_codec_write(struct snd_soc_codec *codec, unsigned int reg, unsigned int value) +{ + aic326x_reg_union *aic_reg = (aic326x_reg_union *)® + dev_dbg(codec->dev,"p %d,w 30 %x %x \n",aic_reg->aic326x_register.page,aic_reg->aic326x_register.offset,value); + return aic3262_reg_write(codec->control_data, reg, value); +} + +/* + *---------------------------------------------------------------------------- + * Function : aic3262_reset_cache + * Purpose : This function is to reset the cache. + *---------------------------------------------------------------------------- + */ +int aic3262_reset_cache (struct snd_soc_codec *codec) +{ + dev_dbg(codec->dev,KERN_ALERT "codec: %s : started\n", __func__); + +#if defined(EN_REG_CACHE) + if (codec->reg_cache != NULL) { + memcpy(codec->reg_cache, aic3262_reg, sizeof (aic3262_reg)); + return 0; + } + + codec->reg_cache = kmemdup (aic3262_reg, sizeof (aic3262_reg), GFP_KERNEL); + if (codec->reg_cache == NULL) { + dev_err(codec->dev,"aic32x4: kmemdup failed\n"); + return -ENOMEM; + } +#endif + dev_dbg(codec->dev,KERN_ALERT "codec: %s : ended\n", __func__); + + return 0; +} + + +/* + *---------------------------------------------------------------------------- + * Function : aic3262_add_widgets + * Purpose : This function is to add the dapm widgets + * The following are the main widgets supported + * # Left DAC to Left Outputs + * # Right DAC to Right Outputs + * # Left Inputs to Left ADC + * # Right Inputs to Right ADC + * + *---------------------------------------------------------------------------- + */ +static int aic3262_add_widgets(struct snd_soc_codec *codec) +{ + + snd_soc_dapm_new_controls(&codec->dapm, aic3262_dapm_widgets, + ARRAY_SIZE(aic3262_dapm_widgets)); + /* set up audio path interconnects */ + dev_dbg(codec->dev,"#Completed adding new dapm widget controls size=%d\n",ARRAY_SIZE(aic3262_dapm_widgets)); + + snd_soc_dapm_add_routes(&codec->dapm, aic3262_dapm_routes, ARRAY_SIZE(aic3262_dapm_routes)); + dev_dbg(codec->dev,"#Completed adding DAPM routes\n"); + snd_soc_dapm_new_widgets(&codec->dapm); + dev_dbg(codec->dev,"#Completed updating dapm\n"); + + return 0; +} + /*---------------------------------------------------------------------------- + * Function : aic3262_hw_params + * Purpose : This function is to set the hardware parameters for AIC3262. + * The functions set the sample rate and audio serial data word + * length. + * + *---------------------------------------------------------------------------- + */ +int aic3262_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_codec *codec = rtd->codec; + struct aic3262_priv *aic3262 = snd_soc_codec_get_drvdata(codec); + int asi_reg, bclk_reg; + u8 data = 0; + + + if(substream->stream==SNDRV_PCM_STREAM_PLAYBACK) + aic3262->stream_status=1; + else + aic3262->stream_status=0; + + + switch(dai->id) + { + case 0: + asi_reg = AIC3262_ASI1_BUS_FMT; + bclk_reg = AIC3262_ASI1_BCLK_N; + break; + case 1: + asi_reg = AIC3262_ASI2_BUS_FMT; + bclk_reg = AIC3262_ASI2_BCLK_N; + break; + case 2: + asi_reg = AIC3262_ASI3_BUS_FMT; + bclk_reg = AIC3262_ASI3_BCLK_N; + break; + default: + return -EINVAL; + + + } + + switch (params_format(params)) { + case SNDRV_PCM_FORMAT_S16_LE: + data = data | 0x00; + break; + case SNDRV_PCM_FORMAT_S20_3LE: + data |= (0x08); + break; + case SNDRV_PCM_FORMAT_S24_LE: + data |= (0x10); + break; + case SNDRV_PCM_FORMAT_S32_LE: + data |= (0x18); + break; + } + + /* configure the respective Registers for the above configuration */ + snd_soc_update_bits(codec, asi_reg, AIC3262_ASI_DATA_WORD_LENGTH_MASK, data); + return 0; +} +/* + *---------------------------------------------------------------------------- + * Function : aic3262_mute + * Purpose : This function is to mute or unmute the left and right DAC + * + *---------------------------------------------------------------------------- + */ +static int aic3262_mute(struct snd_soc_dai *dai, int mute) +{ + struct snd_soc_codec *codec = dai->codec; + // int mute_reg; + + struct aic3262_priv *aic3262 = snd_soc_codec_get_drvdata(codec); + + dev_dbg(codec->dev, "codec : %s : started\n", __FUNCTION__ ); + if(dai->id > 2) + return -EINVAL; + if(mute) { + aic3262->mute_asi &= ~((0x1) << dai->id); + if(aic3262->mute_asi == 0)// Mute only when all asi's are muted + snd_soc_update_bits_locked(codec, AIC3262_DAC_MVOL_CONF, AIC3262_DAC_LR_MUTE_MASK,AIC3262_DAC_LR_MUTE); + + } else { // Unmute + if(aic3262->mute_asi == 0)// Unmute for the first asi that need to unmute. rest unmute will pass + snd_soc_update_bits_locked(codec, AIC3262_DAC_MVOL_CONF, AIC3262_DAC_LR_MUTE_MASK, 0x0); + aic3262->mute_asi |= ((0x1) << dai->id); + } + dev_dbg(codec->dev, "codec : %s : ended\n", __FUNCTION__ ); + return 0; +} + +/* + *---------------------------------------------------------------------------- + * Function : aic3262_set_dai_sysclk + * Purpose : This function is to set the DAI system clock + * + *---------------------------------------------------------------------------- + */ +static int aic3262_set_dai_sysclk(struct snd_soc_dai *codec_dai, + int clk_id, unsigned int freq, int dir) +{ + struct aic3262_priv *aic3262; + struct snd_soc_codec *codec; + codec = codec_dai->codec; + aic3262 = snd_soc_codec_get_drvdata(codec); + switch (freq) { + case AIC3262_FREQ_12000000: + aic3262->sysclk = freq; + return 0; + case AIC3262_FREQ_24000000: + aic3262->sysclk = freq; + return 0; + break; + case AIC3262_FREQ_19200000: + aic3262->sysclk = freq; + return 0; + break; + case AIC3262_FREQ_38400000: + aic3262->sysclk = freq; + dev_dbg(codec->dev,"codec: sysclk = %d\n", aic3262->sysclk); + return 0; + break; + + } + dev_err(codec->dev,"Invalid frequency to set DAI system clock\n"); + + return -EINVAL; +} + +/* + *---------------------------------------------------------------------------- + * Function : aic3262_set_dai_fmt + * Purpose : This function is to set the DAI format + * + *---------------------------------------------------------------------------- + */ +static int aic3262_set_dai_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt) +{ + // struct aic3262_priv *aic3262 = aic3262_priv_data; + struct aic3262_priv *aic3262; + struct snd_soc_codec *codec; + u8 iface_val, master,dsp_a_val ; + int aif_bclk_wclk_reg; + int aif_interface_reg; + int aif_bclk_offset_reg; + int iface_reg; + codec = codec_dai->codec; + aic3262 = snd_soc_codec_get_drvdata(codec); + iface_val = 0x00; + master = 0x0; + dsp_a_val = 0x0; + switch(codec_dai->id) + { + case 0: + aif_bclk_wclk_reg = AIC3262_ASI1_BWCLK_CNTL_REG; + aif_interface_reg = AIC3262_ASI1_BUS_FMT; + aif_bclk_offset_reg = AIC3262_ASI1_LCH_OFFSET; + break; + case 1: + aif_bclk_wclk_reg = AIC3262_ASI2_BWCLK_CNTL_REG; + aif_interface_reg = AIC3262_ASI2_BUS_FMT; + aif_bclk_offset_reg = AIC3262_ASI2_LCH_OFFSET; + break; + case 2: + aif_bclk_wclk_reg = AIC3262_ASI3_BWCLK_CNTL_REG; + aif_interface_reg = AIC3262_ASI3_BUS_FMT; + aif_bclk_offset_reg = AIC3262_ASI3_LCH_OFFSET; + break; + default: + return -EINVAL; + + } + /* set master/slave audio interface */ + switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { + case SND_SOC_DAIFMT_CBM_CFM: + dev_dbg(codec->dev, "setdai_fmt : SND_SOC_DAIFMT_CBM_CFM : master=1 \n"); + aic3262->master = 1; + master |= (AIC3262_WCLK_OUT_MASK | AIC3262_BCLK_OUT_MASK); + break; + case SND_SOC_DAIFMT_CBS_CFS: + dev_dbg(codec->dev, "setdai_fmt : SND_SOC_DAIFMT_CBS_CFS : master=0 \n"); + + aic3262->master = 0; + break; + case SND_SOC_DAIFMT_CBS_CFM: //new case..just for debugging + master |= (AIC3262_WCLK_OUT_MASK); + dev_dbg(codec->dev,"%s: SND_SOC_DAIFMT_CBS_CFM\n", __FUNCTION__); + aic3262->master = 0; + break; + case SND_SOC_DAIFMT_CBM_CFS: + master |= (AIC3262_BCLK_OUT_MASK); + aic3262->master = 0; + break; + + default: + dev_err(codec->dev, "Invalid DAI master/slave interface\n"); + + return -EINVAL; + } + + /* interface format */ + switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { + case SND_SOC_DAIFMT_I2S: + iface_val = (iface_reg & 0x1f); + break; + case SND_SOC_DAIFMT_DSP_A: + dsp_a_val = 0x1; /* Intentionally falling back to following case */ + case SND_SOC_DAIFMT_DSP_B: + iface_val = (iface_reg & 0x1f) | 0x20; + break; + case SND_SOC_DAIFMT_RIGHT_J: + iface_val = (iface_reg & 0x1f) | 0x40; + break; + case SND_SOC_DAIFMT_LEFT_J: + iface_val = (iface_reg & 0x1f) | 0x60; + break; + + dev_err(codec->dev,"Invalid DAI interface format\n"); + + return -EINVAL; + } + switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { + case SND_SOC_DAIFMT_DSP_A: + case SND_SOC_DAIFMT_DSP_B: + switch(fmt & SND_SOC_DAIFMT_INV_MASK) { + case SND_SOC_DAIFMT_NB_NF: + break; + case SND_SOC_DAIFMT_IB_NF: + master |= AIC3262_BCLK_INV_MASK; + break; + default: + return -EINVAL; + } + break; + case SND_SOC_DAIFMT_I2S: + case SND_SOC_DAIFMT_RIGHT_J: + case SND_SOC_DAIFMT_LEFT_J: + switch(fmt & SND_SOC_DAIFMT_INV_MASK) { + case SND_SOC_DAIFMT_NB_NF: + break; + case SND_SOC_DAIFMT_IB_NF: + master |= AIC3262_BCLK_INV_MASK; + break; + default: + return -EINVAL; + } + break; + default: + return -EINVAL; + } + snd_soc_update_bits(codec, aif_bclk_wclk_reg, AIC3262_WCLK_BCLK_MASTER_MASK ,master); + snd_soc_update_bits(codec, aif_interface_reg, AIC3262_ASI_INTERFACE_MASK ,iface_val); + snd_soc_update_bits(codec, aif_bclk_offset_reg, AIC3262_BCLK_OFFSET_MASK,dsp_a_val); + + return 0; +} + +static int aic3262_dai_set_pll(struct snd_soc_dai *dai, int pll_id, int source, + unsigned int Fin, unsigned int Fout) +{ + struct snd_soc_codec *codec = dai->codec; + struct aic3262_priv *aic3262 = snd_soc_codec_get_drvdata(codec); + + dev_dbg(codec->dev,"In aic3262: dai_set_pll\n"); + dev_dbg(codec->dev,"%d,%s,dai->id = %d\n", __LINE__ , __FUNCTION__ ,dai->id); + // select the PLL_CLKIN + snd_soc_update_bits(codec, AIC3262_PLL_CLKIN_REG, AIC3262_PLL_CLKIN_MASK, source << AIC3262_PLL_CLKIN_SHIFT); + // TODO: How to select low/high clock range? + + mutex_lock(&aic3262->cfw_mutex); + aic3xxx_cfw_set_pll(aic3262->cfw_p,dai->id); + mutex_unlock(&aic3262->cfw_mutex); + + return 0; + + +} +/* + *---------------------------------------------------------------------------- + * Function : aic3262_set_bias_level + * Purpose : This function is to get triggered when dapm events occurs. + * + *---------------------------------------------------------------------------- + */ +static int aic3262_set_bias_level(struct snd_soc_codec *codec, + enum snd_soc_bias_level level) +{ + + switch (level) { + /* full On */ + case SND_SOC_BIAS_ON: + + dev_dbg(codec->dev, "set_bias_on \n"); + break; + + /* partial On */ + case SND_SOC_BIAS_PREPARE: + dev_dbg(codec->dev, "set_bias_prepare \n"); + break; + + + /* Off, with power */ + case SND_SOC_BIAS_STANDBY: + /* + * all power is driven by DAPM system, + * so output power is safe if bypass was set + */ + dev_dbg(codec->dev, "set_bias_stby \n"); + if(codec->dapm.bias_level == SND_SOC_BIAS_OFF) + { + snd_soc_update_bits(codec, AIC3262_POWER_CONF, (AIC3262_AVDD_TO_DVDD_MASK | AIC3262_EXT_ANALOG_SUPPLY_MASK), 0x0); + + } + snd_soc_update_bits(codec, AIC3262_REF_PWR_DLY, AIC3262_CHIP_REF_PWR_ON_MASK, AIC3262_CHIP_REF_PWR_ON); + + break; + + + /* Off, without power */ + case SND_SOC_BIAS_OFF: + dev_dbg(codec->dev, "set_bias_off \n"); + /* force all power off */ + snd_soc_update_bits(codec, AIC3262_REF_PWR_DLY, AIC3262_CHIP_REF_PWR_ON_MASK, 0x0); + snd_soc_update_bits(codec, AIC3262_POWER_CONF, (AIC3262_AVDD_TO_DVDD_MASK | AIC3262_EXT_ANALOG_SUPPLY_MASK), + (AIC3262_AVDD_TO_DVDD | AIC3262_EXT_ANALOG_SUPPLY_OFF)); + break; + } + + return 0; +} + +/* + *---------------------------------------------------------------------------- + * Function : aic3262_suspend + * Purpose : This function is to suspend the AIC3262 driver. + * + *---------------------------------------------------------------------------- + */ +static int aic3262_suspend(struct snd_soc_codec *codec, pm_message_t state) +{ + aic3262_set_bias_level(codec, SND_SOC_BIAS_OFF); + return 0; +} + +/* + *---------------------------------------------------------------------------- + * Function : aic3262_resume + * Purpose : This function is to resume the AIC3262 driver + * + *---------------------------------------------------------------------------- + */ +static int aic3262_resume(struct snd_soc_codec *codec) +{ + aic3262_set_bias_level(codec, SND_SOC_BIAS_STANDBY); + + return 0; +} + +/* + *---------------------------------------------------------------------------- + * Function : aic3262_probe + * Purpose : This is first driver function called by the SoC core driver. + * + *---------------------------------------------------------------------------- + */ + +#ifdef AIC3262_PROC +static int aic3262_proc_init(void); +#endif + +static int aic3262_codec_probe(struct snd_soc_codec *codec) +{ + int ret = 0; + struct aic3262 *control; + struct aic3262_priv *aic3262; + struct aic3262_jack_data *jack; + if(codec == NULL) + dev_err(codec->dev,"codec pointer is NULL. \n"); + + DBG("aic3262_codec_probe is ok\n"); //sxj + + #ifdef AIC3262_PROC + aic3262_proc_init(); + #endif + + codec->control_data = dev_get_drvdata(codec->dev->parent); + control = codec->control_data; + + aic3262 = kzalloc(sizeof(struct aic3262_priv), GFP_KERNEL); + + if(aic3262 == NULL) + return -ENOMEM; + snd_soc_codec_set_drvdata( codec, aic3262); + + aic3262->pdata = dev_get_platdata(codec->dev->parent); + aic3262->codec = codec; + + aic3262->cur_fw = NULL; + aic3262->isdefault_fw= 0; + aic3262->cfw_p = &(aic3262->cfw_ps); + aic3262->cfw_p->ops = &aic3262_cfw_codec_ops; + aic3262->cfw_p->ops_obj = aic3262; + + aic3262->workqueue = create_singlethread_workqueue("aic3262-codec"); + if( !aic3262->workqueue) { + ret = -ENOMEM; + goto work_err; + } + ret = device_create_file(codec->dev, &dev_attr_debug_level); + if (ret) + dev_info(codec->dev, "Failed to add debug_level sysfs \n"); + INIT_DELAYED_WORK(&aic3262->delayed_work, aic3262_accessory_work); + + mutex_init(&aic3262->mutex); + mutex_init(&aic3262->cfw_mutex); + pm_runtime_enable(codec->dev); + pm_runtime_resume(codec->dev); + aic3262->dsp_runstate = 0; + /* use switch-class based headset reporting if platform requires it */ + jack = &aic3262->hs_jack; + jack->sdev.name = "h2w"; + ret = switch_dev_register(&jack->sdev); + if(ret) { + dev_err(codec->dev, "error registering switch device %d\n",ret); + goto reg_err; + } + if(control->irq) + { + ret = aic3262_request_irq(codec->control_data, AIC3262_IRQ_HEADSET_DETECT, + aic3262_audio_handler, IRQF_NO_SUSPEND,"aic3262_irq_headset", + codec); + + if(ret){ + dev_err(codec->dev, "HEADSET detect irq request failed:%d\n",ret); + goto irq_err; + } + } + + aic3262_set_bias_level(codec, SND_SOC_BIAS_STANDBY); + + aic3262->mute_asi = 0; + + snd_soc_add_controls(codec, aic3262_snd_controls, + ARRAY_SIZE(aic3262_snd_controls)); + mutex_init(&codec->mutex); + + aic3262_add_widgets(codec); + +#ifdef AIC3262_TiLoad + ret = aic3262_driver_init(codec); + if (ret < 0) + dev_err(codec->dev,"\nTiLoad Initialization failed\n"); +#endif + // force loading the default firmware + aic3262_firmware_load(NULL,codec); + dev_dbg(codec->dev,"%d,%s,Firmware test\n",__LINE__,__FUNCTION__); + request_firmware_nowait(THIS_MODULE, FW_ACTION_HOTPLUG,"tlv320aic3262_fw_v1.bin", codec->dev, GFP_KERNEL,codec, aic3262_firmware_load); + + aic3262_codec = codec; + DBG("%s end of aic3262_codec_probe\n",__FILE__); //sxj + return 0; +irq_err: + switch_dev_unregister(&jack->sdev); +reg_err: +work_err: + kfree(aic3262); + return 0; +} + +/* + *---------------------------------------------------------------------------- + * Function : aic3262_remove + * Purpose : to remove aic3262 soc device + * + *---------------------------------------------------------------------------- + */ +static int aic3262_codec_remove(struct snd_soc_codec *codec) +{ + /* power down chip */ + struct aic3262_priv *aic3262 = snd_soc_codec_get_drvdata(codec); + struct aic3262 *control = codec->control_data; + struct aic3262_jack_data *jack = &aic3262->hs_jack; + + aic3262_set_bias_level(codec, SND_SOC_BIAS_OFF); + + pm_runtime_disable(codec->dev); + /* free_irq if any */ + switch(control->type) { + case TLV320AIC3262: + aic3262_free_irq(control, AIC3262_IRQ_HEADSET_DETECT, codec); + break; + } + /* release firmware if any */ + if(aic3262->cur_fw != NULL) + { + release_firmware(aic3262->cur_fw); + } + /* destroy workqueue for jac dev */ + switch_dev_unregister(&jack->sdev); + destroy_workqueue(aic3262->workqueue); + + kfree(aic3262); + + return 0; +} + +static struct snd_soc_codec_driver soc_codec_driver_aic326x = { + .probe = aic3262_codec_probe, + .remove = aic3262_codec_remove, + .suspend = aic3262_suspend, + .resume = aic3262_resume, + .read = aic3262_codec_read, + .write = aic3262_codec_write, + .set_bias_level = aic3262_set_bias_level, + .reg_cache_size = ARRAY_SIZE(aic3262_reg), + .reg_word_size = sizeof(u8), + .reg_cache_default = aic3262_reg, +}; +static int aic326x_probe(struct platform_device *pdev) +{ + return snd_soc_register_codec(&pdev->dev, &soc_codec_driver_aic326x, + aic326x_dai_driver, ARRAY_SIZE(aic326x_dai_driver)); + +} + +static int aic326x_remove(struct platform_device *pdev) +{ + snd_soc_unregister_codec(&pdev->dev); + return 0; +} + +static struct platform_driver aic326x_codec_driver = { + .driver = { + .name = "tlv320aic3262-codec", + .owner = THIS_MODULE, + }, + .probe = aic326x_probe, + .remove = __devexit_p(aic326x_remove), +}; +/* + *---------------------------------------------------------------------------- + * Function : tlv320aic3262_modinit + * Purpose : module init function. First function to run. + * + *---------------------------------------------------------------------------- + */ +static int __init tlv320aic3262_modinit(void) +{ + return platform_driver_register(&aic326x_codec_driver); +} + +module_init(tlv320aic3262_modinit); + +/* + *---------------------------------------------------------------------------- + * Function : tlv320aic3262_exit + * Purpose : module init function. First function to run. + * + *---------------------------------------------------------------------------- + */ +static void __exit tlv320aic3262_exit(void) +{ + platform_driver_unregister(&aic326x_codec_driver); + +} + +module_exit(tlv320aic3262_exit); +MODULE_ALIAS("platform:tlv320aic3262-codec"); +MODULE_DESCRIPTION("ASoC TLV320AIC3262 codec driver"); +MODULE_AUTHOR("Y Preetam Sashank Reddy "); +MODULE_AUTHOR("Barani Prashanth "); +MODULE_AUTHOR("Mukund Navada K "); +MODULE_AUTHOR("Naren Vasanad "); +MODULE_LICENSE("GPL"); + + +#ifdef AIC3262_PROC + + +/*static void test_playback(void) +{ + int ret; + printk("test palyback start\n"); + + aic3262_write(aic3262_codec, 0x00, 0x00); + ret = aic3262_read(aic3262_codec,0x00); + printk("0x00 = %x\n",ret); + aic3262_write(aic3262_codec, 0x7f, 0x00); + ret = aic3262_read(aic3262_codec,0x7f); + printk("0x7f = %x\n",ret); + aic3262_write(aic3262_codec, 0x01, 0x01); + ret = aic3262_read(aic3262_codec,0x01); + printk("0x01 = %x\n",ret); + + aic3262_write(aic3262_codec, 0x04, 0x00); + aic3262_write(aic3262_codec, 0x0b, 0x81); + aic3262_write(aic3262_codec, 0x0c, 0x82); + aic3262_write(aic3262_codec, 0x0d, 0x00); + aic3262_write(aic3262_codec, 0x0e, 0x80); + + aic3262_write(aic3262_codec, 0x00, 0x01); + aic3262_write(aic3262_codec, 0x01+1*128, 0x00); + aic3262_write(aic3262_codec, 0x7a+1*128, 0x01); + + aic3262_write(aic3262_codec, 0x00, 0x04); + aic3262_write(aic3262_codec, 0x01+4*128, 0x00); + aic3262_write(aic3262_codec, 0x0a+4*128, 0x00); + + aic3262_write(aic3262_codec, 0x00, 0x00); + aic3262_write(aic3262_codec, 0x3c, 0x01); + + aic3262_write(aic3262_codec, 0x00, 0x01); + aic3262_write(aic3262_codec, 0x03+1*128, 0x00); + aic3262_write(aic3262_codec, 0x04+1*128, 0x00); + aic3262_write(aic3262_codec, 0x1f+1*128, 0x80); + aic3262_write(aic3262_codec, 0x20+1*128, 0x00); + aic3262_write(aic3262_codec, 0x21+1*128, 0x28); + aic3262_write(aic3262_codec, 0x23+1*128, 0x10); + aic3262_write(aic3262_codec, 0x1b+1*128, 0x33); + aic3262_write(aic3262_codec, 0x00, 0x00); + aic3262_write(aic3262_codec, 0x3f, 0xc0); + aic3262_write(aic3262_codec, 0x40, 0x00); + + aic3262_write(aic3262_codec, 0x00, 0x01); + aic3262_write(aic3262_codec, 0x16+1*128, 0xc3); + ret = aic3262_read(aic3262_codec,0x16); + printk("0x16 = %x\n",ret); + aic3262_write(aic3262_codec, 0xae, 0x00); + aic3262_write(aic3262_codec, 0x2f+1*128, 0x00); + aic3262_write(aic3262_codec, 0x30+1*128, 0x11); + aic3262_write(aic3262_codec, 0x52+1*128, 0x75); + aic3262_write(aic3262_codec, 0x53+1*128, 0x03); + aic3262_write(aic3262_codec, 0x2d+1*128, 0x03); + aic3262_write(aic3262_codec, 0x00, 0x00); + aic3262_write(aic3262_codec, 0x3f, 0xc0); + aic3262_write(aic3262_codec, 0x40, 0x00); + +}*/ + + +static void AP_to_speaker(void) +{ + printk("AP_to_speaker\n"); + + aic3262_codec_write(aic3262_codec, AIC3262_PAGE_SEL_REG, 0x00); + aic3262_codec_write(aic3262_codec, MAKE_REG(0,0,127), 0x00); + aic3262_codec_write(aic3262_codec, AIC3262_RESET_REG, 0x01); + + aic3262_codec_write(aic3262_codec, AIC3262_DAC_ADC_CLKIN_REG, 0x00); + aic3262_codec_write(aic3262_codec, AIC3262_NDAC_DIV_POW_REG, 0x81); + aic3262_codec_write(aic3262_codec, AIC3262_MDAC_DIV_POW_REG, 0x82); + aic3262_codec_write(aic3262_codec, AIC3262_DOSR_MSB_REG, 0x00); + aic3262_codec_write(aic3262_codec, AIC3262_DOSR_LSB_REG, 0x80); + + aic3262_codec_write(aic3262_codec, AIC3262_PAGE_SEL_REG, 0x01); + aic3262_codec_write(aic3262_codec, AIC3262_POWER_CONF, 0x00); + aic3262_codec_write(aic3262_codec, AIC3262_REF_PWR_DLY, 0x01); + + aic3262_codec_write(aic3262_codec, AIC3262_PAGE_SEL_REG, 0x04); + aic3262_codec_write(aic3262_codec, AIC3262_ASI1_BUS_FMT, 0x00); + aic3262_codec_write(aic3262_codec, AIC3262_ASI1_BWCLK_CNTL_REG, 0x00); + + aic3262_codec_write(aic3262_codec, AIC3262_PAGE_SEL_REG, 0x00); + aic3262_codec_write(aic3262_codec, AIC3262_DAC_PRB, 0x01); + + aic3262_codec_write(aic3262_codec, AIC3262_PAGE_SEL_REG, 0x01); + aic3262_codec_write(aic3262_codec, AIC3262_LDAC_PTM, 0x00); + aic3262_codec_write(aic3262_codec, AIC3262_RDAC_PTM, 0x00); + aic3262_codec_write(aic3262_codec, AIC3262_LINE_AMP_CNTL_R1, 0xC3); + aic3262_codec_write(aic3262_codec, AIC3262_SPK_AMP_CNTL_R2, 0x00); + aic3262_codec_write(aic3262_codec, AIC3262_SPK_AMP_CNTL_R3, 0x00); + aic3262_codec_write(aic3262_codec, AIC3262_SPK_AMP_CNTL_R4, 0x11); + aic3262_codec_write(aic3262_codec, MAKE_REG(0,1, 82), 0x75); + aic3262_codec_write(aic3262_codec, MAKE_REG(0,1, 83), 0x03); + aic3262_codec_write(aic3262_codec, AIC3262_SPK_AMP_CNTL_R1, 0x03); + + aic3262_codec_write(aic3262_codec, AIC3262_PAGE_SEL_REG, 0x00); + aic3262_codec_write(aic3262_codec, AIC3262_PASI_DAC_DP_SETUP, 0xc0); + aic3262_codec_write(aic3262_codec, AIC3262_DAC_MVOL_CONF, 0x00); + +} + + +static void AP_to_headphone(void) +{ + printk("AP_to_headphone\n"); + + aic3262_codec_write(aic3262_codec, AIC3262_PAGE_SEL_REG, 0x00); + aic3262_codec_write(aic3262_codec, MAKE_REG(0,0,127), 0x00); + aic3262_codec_write(aic3262_codec, AIC3262_RESET_REG, 0x01); + + aic3262_codec_write(aic3262_codec, AIC3262_DAC_ADC_CLKIN_REG, 0x00); + aic3262_codec_write(aic3262_codec, AIC3262_NDAC_DIV_POW_REG, 0x81); + aic3262_codec_write(aic3262_codec, AIC3262_MDAC_DIV_POW_REG, 0x82); + aic3262_codec_write(aic3262_codec, AIC3262_DOSR_MSB_REG, 0x00); + aic3262_codec_write(aic3262_codec, AIC3262_DOSR_LSB_REG, 0x80); + + aic3262_codec_write(aic3262_codec, AIC3262_PAGE_SEL_REG, 0x01); + aic3262_codec_write(aic3262_codec, AIC3262_POWER_CONF, 0x00); + aic3262_codec_write(aic3262_codec, AIC3262_REF_PWR_DLY, 0x01); + + aic3262_codec_write(aic3262_codec, AIC3262_PAGE_SEL_REG, 0x04); + aic3262_codec_write(aic3262_codec, AIC3262_ASI1_BUS_FMT, 0x00); + aic3262_codec_write(aic3262_codec, AIC3262_ASI1_BWCLK_CNTL_REG, 0x00); + + aic3262_codec_write(aic3262_codec, AIC3262_PAGE_SEL_REG, 0x00); + aic3262_codec_write(aic3262_codec, AIC3262_DAC_PRB, 0x01); + + aic3262_codec_write(aic3262_codec, AIC3262_PAGE_SEL_REG, 0x01); + aic3262_codec_write(aic3262_codec, AIC3262_LDAC_PTM, 0x00); + aic3262_codec_write(aic3262_codec, AIC3262_RDAC_PTM, 0x00); + aic3262_codec_write(aic3262_codec, AIC3262_HPL_VOL, 0x80); + aic3262_codec_write(aic3262_codec, AIC3262_HPR_VOL, 0x80); + aic3262_codec_write(aic3262_codec, MAKE_REG(0,1, 33), 0x28); + aic3262_codec_write(aic3262_codec, AIC3262_CHARGE_PUMP_CNTL, 0x10); + aic3262_codec_write(aic3262_codec, AIC3262_HP_AMP_CNTL_R1, 0x33); + aic3262_codec_write(aic3262_codec, AIC3262_PAGE_SEL_REG, 0x00); + aic3262_codec_write(aic3262_codec, AIC3262_PASI_DAC_DP_SETUP, 0xc0); + aic3262_codec_write(aic3262_codec, AIC3262_DAC_MVOL_CONF, 0x00); + +} + +static void record_in1lr(void) +{ + printk("record in1lr\n"); + + aic3262_codec_write(aic3262_codec, AIC3262_PAGE_SEL_REG, 0x00); + aic3262_codec_write(aic3262_codec, MAKE_REG(0,0,127), 0x00); + aic3262_codec_write(aic3262_codec, AIC3262_RESET_REG, 0x01); + + aic3262_codec_write(aic3262_codec, AIC3262_DAC_ADC_CLKIN_REG, 0x00); + aic3262_codec_write(aic3262_codec, AIC3262_NADC_DIV_POW_REG, 0x81); + aic3262_codec_write(aic3262_codec, AIC3262_MADC_DIV_POW_REG, 0x82); + aic3262_codec_write(aic3262_codec, AIC3262_AOSR_REG, 0x80); + + aic3262_codec_write(aic3262_codec, AIC3262_PAGE_SEL_REG, 0x01); + aic3262_codec_write(aic3262_codec, AIC3262_POWER_CONF, 0x00); + aic3262_codec_write(aic3262_codec, AIC3262_REF_PWR_DLY, 0x01); + aic3262_codec_write(aic3262_codec, AIC3262_MIC_PWR_DLY, 0x33); + + aic3262_codec_write(aic3262_codec, AIC3262_PAGE_SEL_REG, 0x04); + aic3262_codec_write(aic3262_codec, AIC3262_ASI1_BUS_FMT, 0x00); + aic3262_codec_write(aic3262_codec, AIC3262_ASI1_BWCLK_CNTL_REG, 0x00); + + aic3262_codec_write(aic3262_codec, AIC3262_PAGE_SEL_REG, 0x00); + aic3262_codec_write(aic3262_codec, AIC3262_ADC_PRB, 0x01); + + aic3262_codec_write(aic3262_codec, AIC3262_PAGE_SEL_REG, 0x01); + aic3262_codec_write(aic3262_codec, AIC3262_MIC_BIAS_CNTL, 0x40); + aic3262_codec_write(aic3262_codec, AIC3262_LMIC_PGA_PIN, 0x80); + aic3262_codec_write(aic3262_codec, AIC3262_LMIC_PGA_MIN, 0x80); + aic3262_codec_write(aic3262_codec, AIC3262_RMIC_PGA_PIN, 0x80); + aic3262_codec_write(aic3262_codec, AIC3262_RMIC_PGA_MIN, 0x80); + + aic3262_codec_write(aic3262_codec, AIC3262_MICL_PGA, 0x3c); + aic3262_codec_write(aic3262_codec, AIC3262_MICR_PGA, 0x3c); + aic3262_codec_write(aic3262_codec, MAKE_REG(0,1, 61), 0x00); + + aic3262_codec_write(aic3262_codec, AIC3262_PAGE_SEL_REG, 0x00); + aic3262_codec_write(aic3262_codec, AIC3262_PASI_DAC_DP_SETUP, 0xc0); + aic3262_codec_write(aic3262_codec, AIC3262_DAC_MVOL_CONF, 0x00); +} + +static void record_in2lr(void) +{ + printk("record in2lr\n"); + + aic3262_codec_write(aic3262_codec, AIC3262_PAGE_SEL_REG, 0x00); + aic3262_codec_write(aic3262_codec, MAKE_REG(0,0,127), 0x00); + aic3262_codec_write(aic3262_codec, AIC3262_RESET_REG, 0x01); + +} + +static void test_playback(void) +{ + int ret; + printk("test palyback start\n"); + + record_in1lr( ); + +} + + +static ssize_t aic3262_proc_write(struct file *file, const char __user *buffer, + unsigned long len, void *data) +{ + char *cookie_pot; + char *p; + int reg; + int value; + + cookie_pot = (char *)vmalloc( len ); + if (!cookie_pot) + { + return -ENOMEM; + } + else + { + if (copy_from_user( cookie_pot, buffer, len )) + return -EFAULT; + } + + switch(cookie_pot[0]) + { + case 'd': + case 'D': + debug_write_read ++; + debug_write_read %= 2; + if(debug_write_read != 0) + printk("Debug read and write reg on\n"); + else + printk("Debug read and write reg off\n"); + break; + case 'r': + case 'R': + printk("Read reg debug\n"); + if(cookie_pot[1] ==':') + { + debug_write_read = 1; + strsep(&cookie_pot,":"); + while((p=strsep(&cookie_pot,","))) + { + reg = simple_strtol(p,NULL,16); + value = aic3262_codec_read(aic3262_codec,reg); + printk("aic3262_codec_read:0x%04x = 0x%04x\n",reg,value); + } + debug_write_read = 0; + printk("\n"); + } + else + { + printk("Error Read reg debug.\n"); + printk("For example: echo r:22,23,24,25>aic3262_ts\n"); + } + break; + case 'w': + case 'W': + printk("Write reg debug\n"); + if(cookie_pot[1] ==':') + { + debug_write_read = 1; + strsep(&cookie_pot,":"); + while((p=strsep(&cookie_pot,"="))) + { + reg = simple_strtol(p,NULL,16); + p=strsep(&cookie_pot,","); + value = simple_strtol(p,NULL,16); + aic3262_codec_write(aic3262_codec,reg,value); + printk("aic3262_codec_write:0x%04x = 0x%04x\n",reg,value); + } + debug_write_read = 0; + printk("\n"); + } + else + { + printk("Error Write reg debug.\n"); + printk("For example: w:22=0,23=0,24=0,25=0>aic3262_ts\n"); + } + break; + case 'f': + case 'F': + test_playback( ); + break; + case 'a': + printk("Dump reg \n"); + + for(reg = 0; reg < 0x6e; reg+=2) + { + value = aic3262_codec_read(aic3262_codec,reg); + printk("aic3262_codec_read:0x%04x = 0x%04x\n",reg,value); + } + + break; + default: + printk("Help for aic3262_ts .\n-->The Cmd list: \n"); + printk("-->'d&&D' Open or Off the debug\n"); + printk("-->'r&&R' Read reg debug,Example: echo 'r:22,23,24,25'>aic3262_ts\n"); + printk("-->'w&&W' Write reg debug,Example: echo 'w:22=0,23=0,24=0,25=0'>aic3262_ts\n"); + break; + } + + return len; +} + +static const struct file_operations aic3262_proc_fops = { + .owner = THIS_MODULE, +}; + +static int aic3262_proc_init(void) +{ + struct proc_dir_entry *aic3262_proc_entry; + aic3262_proc_entry = create_proc_entry("driver/aic3262_ts", 0777, NULL); + if(aic3262_proc_entry != NULL) + { + aic3262_proc_entry->write_proc = aic3262_proc_write; + return 0; + } + else + { + printk("create proc error !\n"); + return -1; + } +} +#endif diff --git a/sound/soc/codecs/tlv320aic326x.h b/sound/soc/codecs/tlv320aic326x.h new file mode 100644 index 000000000000..c07950d97fd7 --- /dev/null +++ b/sound/soc/codecs/tlv320aic326x.h @@ -0,0 +1,260 @@ +/* + * linux/sound/soc/codecs/tlv320aic326x.h + * + * Copyright (C) 2011 TI Solutions Pvt Ltd. + * + * Based on sound/soc/codecs/tlv320aic3262.c + * + * This package 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 PACKAGE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED + * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. + * + * The TLV320AIC3262 is a flexible, low-power, low-voltage stereo audio + * codec with digital microphone inputs and programmable outputs. + * + * History: + * + * Rev 0.1 ASoC driver support TI 20-01-2011 + * + * The AIC325x ASoC driver is ported for the codec AIC3262. + * Rev 0.2 ASoC driver support TI 21-03-2011 + * The AIC326x ASoC driver is updated for linux 2.6.32 Kernel. + * Rev 0.3 ASoC driver support TI 20-04-2011 + * The AIC326x ASoC driver is ported to 2.6.35 omap4 kernel + */ + +#ifndef _TLV320AIC3262_H +#define _TLV320AIC3262_H +#include "aic3xxx_cfw.h" +#include "aic3xxx_cfw_ops.h" +#include + +/*#ifdef DEBUG + #define dprintk(x...) printk(x) + #define DBG(x...) printk(x) +#else + #define dprintk(x...) + #define DBG(x...) +#endif +*/ + +#define AUDIO_NAME "aic3262" +#define AIC3262_VERSION "1.1" +/* Macro to enable the inclusion of tiload kernel driver */ +#define AIC3262_TiLoad + + + +//#define AIC3262_SYNC_MODE +#undef AIC3262_SYNC_MODE + +#define AIC3262_ASI1_MASTER +//#undef AIC3262_ASI1_MASTER +//#define AIC3262_ASI2_MASTER +#undef AIC3262_ASI2_MASTER +//#define AIC3262_ASI3_MASTER +#undef AIC3262_ASI3_MASTER +/* Macro for McBsp master / slave configuration */ +#define AIC3262_MCBSP_SLAVE /*3262 master*/ +//#undef AIC3262_MCBSP_SLAVE + +/* Enable this macro allow for different ASI formats */ +//#define ASI_MULTI_FMT +#undef ASI_MULTI_FMT +/* Enable register caching on write */ +//#define EN_REG_CACHE + + +/* Enable or disable controls to have Input routing*/ +//#define FULL_IN_CNTL +#undef FULL_IN_CNTL +/* AIC3262 supported sample rate are 8k to 192k */ +#define AIC3262_RATES SNDRV_PCM_RATE_8000_192000 + +/* AIC3262 supports the word formats 16bits, 20bits, 24bits and 32 bits */ +#define AIC3262_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE \ + | SNDRV_PCM_FMTBIT_S24_3LE | SNDRV_PCM_FMTBIT_S32_LE) + +#define AIC3262_FREQ_12000000 12000000 +#define AIC3262_FREQ_19200000 19200000 +#define AIC3262_FREQ_24000000 24000000 +#define AIC3262_FREQ_38400000 38400000 +/* Audio data word length = 16-bits (default setting) */ +#define AIC3262_WORD_LEN_16BITS 0x00 +#define AIC3262_WORD_LEN_20BITS 0x01 +#define AIC3262_WORD_LEN_24BITS 0x02 +#define AIC3262_WORD_LEN_32BITS 0x03 + +/* sink: name of target widget */ +#define AIC3262_WIDGET_NAME 0 +/* control: mixer control name */ +#define AIC3262_CONTROL_NAME 1 +/* source: name of source name */ +#define AIC3262_SOURCE_NAME 2 + +/* D15..D8 aic3262 register offset */ +#define AIC3262_REG_OFFSET_INDEX 0 +/* D7...D0 register data */ +#define AIC3262_REG_DATA_INDEX 1 + +/* Serial data bus uses I2S mode (Default mode) */ +#define AIC3262_I2S_MODE 0x00 +#define AIC3262_DSP_MODE 0x01 +#define AIC3262_RIGHT_JUSTIFIED_MODE 0x02 +#define AIC3262_LEFT_JUSTIFIED_MODE 0x03 + +/* 8 bit mask value */ +#define AIC3262_8BITS_MASK 0xFF + +/* shift value for CLK_REG_3 register */ +#define CLK_REG_3_SHIFT 6 +/* shift value for DAC_OSR_MSB register */ +#define DAC_OSR_MSB_SHIFT 4 + +/* number of codec specific register for configuration */ +#define NO_FEATURE_REGS 2 + +/* AIC3262 register space */ +#define AIC3262_CACHEREGNUM 1024 /* Updated from 256 to support Page 3 registers */ +#define DAC_FLAG 37 +#define ADC_FLAG 36 + +#define DSP_NON_SYNC_MODE(state) (!( (state & 0x03) && (state & 0x30) )) +/* + *---------------------------------------------------------------------------- + * @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; +};*/ +struct aic3262_jack_data { + struct snd_soc_jack *jack; + int report; + struct switch_dev sdev; +}; +/*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_priv { + u32 sysclk; + s32 master; + u8 book_no; + u8 page_no; + u8 process_flow; + u8 mute_codec; + u8 stream_status; + int current_dac_config[2]; + int current_adc_config[2]; + struct aic3262_jack_data hs_jack; + struct workqueue_struct *workqueue; + struct delayed_work delayed_work; + struct snd_soc_codec *codec; + struct mutex mutex; + struct mutex cfw_mutex; + struct cfw_state cfw_ps; + struct cfw_state *cfw_p; + struct aic3262_pdata *pdata; + int mute_asi; // Bit 0 -> ASI1, Bit 1-> ASI2, Bit 2 -> ASI3 + int dsp_runstate; + struct firmware *cur_fw; + int isdefault_fw; +}; +/*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 + * @field u32 | aic3262_configs | + * configurations for aic3262 register value + *---------------------------------------------------------------------------- + */ +struct aic3262_rate_divs { + u32 mclk; + u32 rate; + u8 p_val; + u8 pll_j; + u16 pll_d; + u16 dosr; + u8 ndac; + u8 mdac; + u8 aosr; + u8 nadc; + u8 madc; + u8 blck_N; +// struct aic3262_configs codec_specific_regs[NO_FEATURE_REGS]; +}; + +/* + *---------------------------------------------------------------------------- + * @struct snd_soc_codec_dai | + * It is SoC Codec DAI structure which has DAI capabilities viz., + * playback and capture, DAI runtime information viz. state of DAI + * and pop wait state, and DAI private data. + *---------------------------------------------------------------------------- + */ +extern struct snd_soc_dai tlv320aic3262_dai; + +/* + *---------------------------------------------------------------------------- +nt aic3262_write(struct snd_soc_codec *codec, unsigned int reg, unsigned int value) + * @struct snd_soc_codec_device | + * This structure is soc audio codec device sturecute which pointer + * to basic functions aic3262_probe(), aic3262_remove(), + * aic3262_suspend() and aic3262_resume() + * + */ +extern struct snd_soc_codec_device soc_codec_dev_aic3262; +extern const aic3xxx_codec_ops aic3262_cfw_codec_ops; +void aic3262_hs_jack_detect(struct snd_soc_codec *codec, + struct snd_soc_jack *jack, int report); + +unsigned int aic3262_read(struct snd_soc_codec *codec, unsigned int reg); + int aic3262_write(struct snd_soc_codec *codec, unsigned int reg, unsigned int value); +#endif /* _TLV320AIC3262_H */ + diff --git a/sound/soc/rk29/Kconfig b/sound/soc/rk29/Kconfig index 5b7508b91456..1bc84a597cdf 100755 --- a/sound/soc/rk29/Kconfig +++ b/sound/soc/rk29/Kconfig @@ -134,6 +134,15 @@ config SND_RK29_SOC_AIC3111 Say Y if you want to add support for SoC audio on rockchip with the AIC3111. +config SND_RK29_SOC_AIC3262 + tristate "SoC I2S Audio support for rockchip - AIC3262" + depends on SND_RK29_SOC + select SND_RK29_SOC_I2S + select SND_SOC_TLV320AIC326X + help + Say Y if you want to add support for SoC audio on rockchip + with the AIC3262. + config SND_RK29_SOC_RK1000 tristate "SoC I2S Audio support for rockchip - RK1000" depends on SND_RK29_SOC @@ -160,7 +169,7 @@ config SND_RK29_SOC_RK610 Say Y if you want to add support for SoC audio on rockchip with the RK610(JETTA). -if SND_RK29_SOC_WM8988 || SND_RK29_SOC_RK1000 || SND_RK29_SOC_WM8994 || SND_RK29_SOC_WM8900 || SND_RK29_SOC_RT5621 || SND_RK29_SOC_RT5631 || SND_RK29_SOC_RT5625 || SND_RK29_SOC_CS42L52 || SND_RK29_SOC_AIC3111 || SND_RK29_SOC_HDMI || SND_RK29_SOC_RK610 +if SND_RK29_SOC_WM8988 || SND_RK29_SOC_RK1000 || SND_RK29_SOC_WM8994 || SND_RK29_SOC_WM8900 || SND_RK29_SOC_RT5621 || SND_RK29_SOC_RT5631 || SND_RK29_SOC_RT5625 || SND_RK29_SOC_CS42L52 || SND_RK29_SOC_AIC3111 || SND_RK29_SOC_HDMI || SND_RK29_SOC_RK610 || SND_RK29_SOC_AIC3262 choice bool "Set i2s type" default SND_RK29_CODEC_SOC_SLAVE diff --git a/sound/soc/rk29/Makefile b/sound/soc/rk29/Makefile index 81759578ebfb..a1f0de123846 100644 --- a/sound/soc/rk29/Makefile +++ b/sound/soc/rk29/Makefile @@ -24,6 +24,7 @@ snd-soc-rk1000-objs := rk29_rk1000codec.o snd-soc-wm8994-objs := rk29_wm8994.o snd-soc-hdmi-objs := rk29_hdmi.o snd-soc-rk610-objs := rk29_jetta_codec.o +snd-soc-aic3262-objs := rk29_aic3262.o obj-$(CONFIG_SND_RK29_SOC_WM8994) += snd-soc-wm8994.o obj-$(CONFIG_SND_RK29_SOC_WM8988) += snd-soc-wm8988.o @@ -34,5 +35,6 @@ obj-$(CONFIG_SND_RK29_SOC_RT5625) += snd-soc-rt5625.o obj-$(CONFIG_SND_RK29_SOC_RK1000) += snd-soc-rk1000.o obj-$(CONFIG_SND_RK29_SOC_CS42L52) += snd-soc-cs42l52.o obj-$(CONFIG_SND_RK29_SOC_AIC3111) += snd-soc-aic3111.o +obj-$(CONFIG_SND_RK29_SOC_AIC3262) += snd-soc-aic3262.o obj-$(CONFIG_SND_RK29_SOC_HDMI) += snd-soc-hdmi.o obj-$(CONFIG_SND_RK29_SOC_RK610) += snd-soc-rk610.o \ No newline at end of file diff --git a/sound/soc/rk29/rk29_aic3262.c b/sound/soc/rk29/rk29_aic3262.c new file mode 100644 index 000000000000..bca93054c40b --- /dev/null +++ b/sound/soc/rk29/rk29_aic3262.c @@ -0,0 +1,412 @@ +/* + * rk29_aic3262.c -- SoC audio for rockchip + * + * Driver for rockchip aic3262 audio + * Copyright (C) 2009 lhh + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * + */ + +#define DEBUG 1 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include +#include "../codecs/wm8994.h" +#include "rk29_pcm.h" +#include "rk29_i2s.h" +#include + +#include "../codecs/tlv320aic326x.h" + +#if 0 +#define DBG(x...) printk(KERN_INFO x) +#else +#define DBG(x...) +#endif + +struct regulator *vddhf_reg=NULL; + +/* Headset jack */ +static struct snd_soc_jack hs_jack; + +/*Headset jack detection DAPM pins */ +static struct snd_soc_jack_pin hs_jack_pins[] = { + { + .pin = "Headset Mic", + .mask = SND_JACK_MICROPHONE, + }, + { + .pin = "Headset Stereophone", + .mask = SND_JACK_HEADPHONE, + }, +}; + +static int spk_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + //struct snd_soc_codec *codec = w->codec; + int ret; + if (SND_SOC_DAPM_EVENT_ON(event)) { + + printk(" I am NULL is %d event is %d\n",vddhf_reg,event); + + if (vddhf_reg) { + ret = regulator_enable(vddhf_reg); + if(ret) { + printk("failed to enable vddhf \n"); + return ret; + } + } + } + else { + + if (vddhf_reg) { + ret = regulator_disable(vddhf_reg); + if (ret) { + printk("failed to disable " + "VDDHF regulator %d\n", ret); + return ret; + } + } + } + return 0; +} + + + +/* rk29 machine DAPM */ +static const struct snd_soc_dapm_widget rk29_aic3262_dapm_widgets[] = { + SND_SOC_DAPM_MIC("Ext Mic", NULL), + SND_SOC_DAPM_SPK("Ext Spk", NULL), + SND_SOC_DAPM_MIC("Headset Mic", NULL), + SND_SOC_DAPM_HP("Headset Stereophone", NULL), + SND_SOC_DAPM_SPK("Earphone Spk", NULL), + SND_SOC_DAPM_INPUT("FM Stereo In"), + SND_SOC_DAPM_LINE("FM Stereo Out",NULL), +}; + +static const struct snd_soc_dapm_route audio_map[] = { + /* External Mics: MAINMIC, SUBMIC with bias*/ + {"IN2L", NULL, "Mic Bias Int"}, + {"IN2R", NULL, "Mic Bias Int"}, + {"IN3L", NULL, "Mic Bias Int"}, + {"IN3R", NULL, "Mic Bias Int"}, + {"Mic Bias Int", NULL, "Ext Mic"}, + + /* External Speakers: HFL, HFR */ + {"Ext Spk", NULL, "SPKL"}, + {"Ext Spk", NULL, "SPKR"}, + + /* Headset Mic: HSMIC with bias */ + {"IN1L", NULL, "Mic Bias Ext"}, + {"IN1R", NULL, "Mic Bias Ext"}, + {"Mic Bias Ext", NULL, "Headset Mic"}, + + /* Headset Stereophone (Headphone): HPL, HPR */ + {"Headset Stereophone", NULL, "HPL"}, + {"Headset Stereophone", NULL, "HPR"}, + + /* Earphone speaker */ + {"Earphone Spk", NULL, "RECP"}, + {"Earphone Spk", NULL, "RECM"}, + + /* Aux/FM Stereo In: IN4L, IN4R */ + {"IN4L", NULL, "FM Stereo In"}, + {"IN4R", NULL, "FM Stereo In"}, + + /* Aux/FM Stereo Out: LOL, LOR */ + {"FM Stereo Out", NULL, "LOL"}, + {"FM Stereo Out", NULL, "LOR"}, +}; + +static const struct snd_kcontrol_new rk29_aic326x_controls[] = { + SOC_DAPM_PIN_SWITCH("Ext Mic"), + SOC_DAPM_PIN_SWITCH("Ext Spk"), + SOC_DAPM_PIN_SWITCH("Headset Mic"), + SOC_DAPM_PIN_SWITCH("Headset Stereophone"), + SOC_DAPM_PIN_SWITCH("Earphone Spk"), + SOC_DAPM_PIN_SWITCH("FM Stereo In"), + SOC_DAPM_PIN_SWITCH("FM Stereo Out"), +}; + +static int rk29_aic3262_init(struct snd_soc_pcm_runtime *rtd) +{ + struct snd_soc_codec *codec = rtd->codec; + struct snd_soc_dapm_context *dapm = &codec->dapm; + int ret; + + DBG("rk29_aic3262_init\n"); + + ret = snd_soc_add_controls(codec, rk29_aic326x_controls, + ARRAY_SIZE(rk29_aic326x_controls)); + + if (ret < 0) { + printk("rk29_aic3262: Err snd_soc_add_controls ret: %d\n", ret ); + return ret; + } + + /* Add rk29 specific widgets */ + ret = snd_soc_dapm_new_controls(dapm, rk29_aic3262_dapm_widgets, + ARRAY_SIZE(rk29_aic3262_dapm_widgets)); + if (ret) + return ret; + + /* Set up rk29 specific audio path audio_map */ + snd_soc_dapm_add_routes(dapm, audio_map, ARRAY_SIZE(audio_map)); + + + ret = snd_soc_dapm_sync(dapm); + if (ret) + return ret; + + /* Headset jack detection */ + ret = snd_soc_jack_new(codec, "Headset Jack", + SND_JACK_HEADSET, &hs_jack); + if (ret) + return ret; + + ret = snd_soc_jack_add_pins(&hs_jack, ARRAY_SIZE(hs_jack_pins), + hs_jack_pins); + aic3262_hs_jack_detect(codec, &hs_jack, SND_JACK_HEADSET); + + /* don't wait before switching of HS power */ + rtd->pmdown_time = 0; + return ret; +} + +static int rk29_aif1_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_dai *codec_dai = rtd->codec_dai; + struct snd_soc_dai *cpu_dai = rtd->cpu_dai; + unsigned int pll_out = 0; + int div_bclk,div_mclk; + int ret; + struct clk *general_pll; + + + printk("Enter::%s----%d\n",__FUNCTION__,__LINE__); + + /* set codec DAI configuration */ +#if defined (CONFIG_SND_RK29_CODEC_SOC_SLAVE) + DBG("Set codec_dai slave\n"); + ret = snd_soc_dai_set_fmt(codec_dai, SND_SOC_DAIFMT_I2S | + SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBS_CFS); +#endif +#if defined (CONFIG_SND_RK29_CODEC_SOC_MASTER) + ret = snd_soc_dai_set_fmt(codec_dai, SND_SOC_DAIFMT_I2S | + SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBM_CFM); + DBG("Set codec_dai master\n"); +#endif + if (ret < 0) + return ret; + + /* set cpu DAI configuration */ +#if defined (CONFIG_SND_RK29_CODEC_SOC_SLAVE) + DBG("Set cpu_dai master\n"); + ret = snd_soc_dai_set_fmt(cpu_dai, SND_SOC_DAIFMT_I2S | + SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBM_CFM); +#endif +#if defined (CONFIG_SND_RK29_CODEC_SOC_MASTER) + ret = snd_soc_dai_set_fmt(cpu_dai, SND_SOC_DAIFMT_I2S | + SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBS_CFS); + DBG("Set cpu_dai slave\n"); +#endif + if (ret < 0) + return ret; + + switch(params_rate(params)) { + case 8000: + case 16000: + case 24000: + case 32000: + case 48000: + pll_out = 12288000; + break; + case 11025: + case 22050: + case 44100: + pll_out = 11289600; + break; + default: + DBG("Enter:%s, %d, Error rate=%d\n",__FUNCTION__,__LINE__,params_rate(params)); + return -EINVAL; + } + + general_pll=clk_get(NULL, "general_pll"); + if(clk_get_rate(general_pll)>260000000) + {//288m + div_bclk=(pll_out/4)/params_rate(params)-1; + div_mclk=3; + } + + DBG("func is%s,gpll=%ld,pll_out=%d,div_mclk=%d\n",__FUNCTION__,clk_get_rate(general_pll),pll_out,div_mclk); + ret = snd_soc_dai_set_sysclk(cpu_dai, 0, 12000000, 0); + if(ret < 0) + { + DBG("rk29_hw_params_aic3262:failed to set the cpu sysclk for codec side\n"); + return ret; + } + snd_soc_dai_set_clkdiv(cpu_dai, ROCKCHIP_DIV_BCLK,div_bclk); + snd_soc_dai_set_clkdiv(cpu_dai, ROCKCHIP_DIV_MCLK, div_mclk); + DBG("Enter:%s, %d, LRCK=%d\n",__FUNCTION__,__LINE__,(pll_out/4)/params_rate(params)); + + //MCLK == 11289600 or 12288000 + ret = snd_soc_dai_set_sysclk(codec_dai, 0, 12000000, 0); + if (ret < 0) { + DBG("rk29_hw_params_aic3262:failed to set the sysclk for codec side\n"); + return ret; + } + + return 0; +} + +static int rk29_aif2_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params) +{ + return 0; +} + +static int rk29_aif3_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params) +{ + return 0; +} + +static struct snd_soc_ops rk29_aif1_ops = { + .hw_params = rk29_aif1_hw_params, +}; + +static struct snd_soc_ops rk29_aif2_ops = { + .hw_params = rk29_aif2_hw_params, +}; + +static struct snd_soc_ops rk29_aif3_ops = { + .hw_params = rk29_aif3_hw_params, +}; + +static struct snd_soc_dai_link rk29_dai[] = { + + { + .name = "AIC3262 I2S1", + .stream_name = "AIC3262 PCM", + .codec_name = "tlv320aic3262-codec", + .platform_name = "rockchip-audio", +#if defined(CONFIG_SND_RK29_SOC_I2S_8CH) + .cpu_dai_name = "rk29_i2s.0", +#elif defined(CONFIG_SND_RK29_SOC_I2S_2CH) + .cpu_dai_name = "rk29_i2s.1", +#else + .cpu_dai_name = "rk29_i2s.2", +#endif + .codec_dai_name = "aic326x-asi1", + .ops = &rk29_aif1_ops, + .init = rk29_aic3262_init, + }, + + { + .name = "AIC3262 I2S2", + .stream_name = "AIC3262 PCM", + .codec_name = "tlv320aic3262-codec", + .platform_name = "rockchip-audio", +#if defined(CONFIG_SND_RK29_SOC_I2S_8CH) + .cpu_dai_name = "rk29_i2s.0", +#elif defined(CONFIG_SND_RK29_SOC_I2S_2CH) + .cpu_dai_name = "rk29_i2s.1", +#else + .cpu_dai_name = "rk29_i2s.2", +#endif + .codec_dai_name = "aic326x-asi2", + .ops = &rk29_aif2_ops, + }, + + + { + .name = "AIC3262 I2S3", + .stream_name = "AIC3262 PCM", + .codec_name = "tlv320aic3262-codec", + .platform_name = "rockchip-audio", +#if defined(CONFIG_SND_RK29_SOC_I2S_8CH) + .cpu_dai_name = "rk29_i2s.0", +#elif defined(CONFIG_SND_RK29_SOC_I2S_2CH) + .cpu_dai_name = "rk29_i2s.1", +#else + .cpu_dai_name = "rk29_i2s.2", +#endif + .codec_dai_name = "aic326x-asi3", + .ops = &rk29_aif3_ops, + }, + +}; + + +static struct snd_soc_card snd_soc_card_rk29 = { + .name = "RK29_AIC3262", + .dai_link = rk29_dai, + .num_links = ARRAY_SIZE(rk29_dai), +}; + +static struct platform_device *rk29_snd_device; + +static int __init audio_card_init(void) +{ + int ret =0; + + DBG("Enter::%s----%d\n",__FUNCTION__,__LINE__); + + rk29_snd_device = platform_device_alloc("soc-audio", -1); + if (!rk29_snd_device) { + printk("platform device allocation failed\n"); + return -ENOMEM; + } + + platform_set_drvdata(rk29_snd_device, &snd_soc_card_rk29); + ret = platform_device_add(rk29_snd_device); + if (ret) { + printk("platform device add failed\n"); + // snd_soc_unregister_dai(&rk29_snd_device->dev); + platform_device_put(rk29_snd_device); + return ret; + } + + return ret; +} + +static void __exit audio_card_exit(void) +{ + platform_device_unregister(rk29_snd_device); +} + +module_init(audio_card_init); +module_exit(audio_card_exit); + +/* Module information */ +MODULE_AUTHOR("rockchip"); +MODULE_DESCRIPTION("ROCKCHIP i2s ASoC Interface"); +MODULE_LICENSE("GPL"); + diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c index 4610963ae48f..668897314ba5 100644 --- a/sound/soc/soc-core.c +++ b/sound/soc/soc-core.c @@ -3147,7 +3147,7 @@ EXPORT_SYMBOL_GPL(snd_soc_get_volsw_2r_sx); int snd_soc_put_volsw_2r_sx(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { - struct soc_mixer_control *mc = + /*struct soc_mixer_control *mc = (struct soc_mixer_control *)kcontrol->private_value; struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); unsigned int mask = (1<shift)-1; @@ -3175,6 +3175,31 @@ int snd_soc_put_volsw_2r_sx(struct snd_kcontrol *kcontrol, return ret; } + return 0;*/ //sxj modify, this function have bug + + struct soc_mixer_control *mc = + (struct soc_mixer_control *)kcontrol->private_value; + struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); + unsigned int mask = (1<shift)-1; + int min = mc->min; + int ret; + unsigned int val, valr, oval, ovalr; + + val = ((ucontrol->value.integer.value[0]+min) & 0xff); + val &= mask; + valr = ((ucontrol->value.integer.value[1]+min) & 0xff); + valr &= mask; + + ret = 0; + ret = snd_soc_update_bits_locked(codec, mc->reg, mask, val); + if(ret < 0) + return ret; + + ret = snd_soc_update_bits_locked(codec, mc->rreg, mask, valr); + + if(ret < 0) + return ret; + return 0; } EXPORT_SYMBOL_GPL(snd_soc_put_volsw_2r_sx); -- 2.34.1