misc: tfa9887: Add HTC TFA9887 Amp driver
* HTC kernel version: m7-kk-3.4.10-17db3b4
Change-Id: I8fbeff195ff11497f9fef8b1d8196d4c76eaaa6b
diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig
index 786395e..ed18acb 100644
--- a/drivers/misc/Kconfig
+++ b/drivers/misc/Kconfig
@@ -715,6 +715,18 @@
help
PN544 NFC driver.
+config AMP_TFA9887
+ tristate "NXP TFA9887 Speaker AMP Driver"
+ depends on I2C=y
+ help
+ NXP TFA9887 Speaker AMP Driver implemented by HTC.
+
+config AMP_TFA9887L
+ tristate "NXP TFA9887 (L) Speaker AMP Driver"
+ depends on I2C=y
+ help
+ NXP TFA9887 Speaker AMP Driver implemented by HTC.
+
source "drivers/misc/c2port/Kconfig"
source "drivers/misc/eeprom/Kconfig"
source "drivers/misc/cb710/Kconfig"
diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile
index 59f4fcd..861097a 100644
--- a/drivers/misc/Makefile
+++ b/drivers/misc/Makefile
@@ -79,3 +79,5 @@
obj-$(CONFIG_CABLE_DETECT_8XXX) += cable_detect_8xxx.o
obj-$(CONFIG_VP_A1028) += a1028.o
obj-$(CONFIG_SENSORS_NFC_PN544) += pn544.o
+obj-$(CONFIG_AMP_TFA9887) += tfa9887.o
+obj-$(CONFIG_AMP_TFA9887L) += tfa9887l.o
diff --git a/drivers/misc/tfa9887.c b/drivers/misc/tfa9887.c
new file mode 100644
index 0000000..c27bbb0
--- /dev/null
+++ b/drivers/misc/tfa9887.c
@@ -0,0 +1,540 @@
+/* driver/i2c/chip/tfa9887.c
+ *
+ * NXP tfa9887 Speaker Amp
+ *
+ * Copyright (C) 2012 HTC Corporation
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <linux/interrupt.h>
+#include <linux/i2c.h>
+#include <linux/slab.h>
+#include <linux/irq.h>
+#include <linux/miscdevice.h>
+#include <asm/uaccess.h>
+#include <linux/delay.h>
+#include <linux/input.h>
+#include <linux/workqueue.h>
+#include <linux/freezer.h>
+#include <linux/tfa9887.h>
+#include <linux/mutex.h>
+#include <linux/debugfs.h>
+#include <linux/gpio.h>
+#include <linux/module.h>
+#include <linux/mfd/pm8xxx/pm8921.h>
+
+#define TPA9887_IOCTL_MAGIC 'a'
+#define TPA9887_WRITE_CONFIG _IOW(TPA9887_IOCTL_MAGIC, 0x01, unsigned int)
+#define TPA9887_READ_CONFIG _IOW(TPA9887_IOCTL_MAGIC, 0x02, unsigned int)
+#define TPA9887_ENABLE_DSP _IOW(TPA9887_IOCTL_MAGIC, 0x03, unsigned int)
+#define TPA9887_WRITE_L_CONFIG _IOW(TPA9887_IOCTL_MAGIC, 0x04, unsigned int)
+#define TPA9887_READ_L_CONFIG _IOW(TPA9887_IOCTL_MAGIC, 0x05, unsigned int)
+#define TPA9887_KERNEL_LOCK _IOW(TPA9887_IOCTL_MAGIC, 0x06, unsigned int)
+#define DEBUG (0)
+
+static struct i2c_client *this_client;
+static struct tfa9887_platform_data *pdata;
+struct mutex spk_amp_lock;
+static int tfa9887_opened;
+static int last_spkamp_state;
+static int dsp_enabled;
+static int tfa9887_i2c_write(char *txData, int length);
+static int tfa9887_i2c_read(char *rxData, int length);
+#ifdef CONFIG_DEBUG_FS
+static struct dentry *debugfs_tpa_dent;
+static struct dentry *debugfs_peek;
+static struct dentry *debugfs_poke;
+static unsigned char read_data;
+
+static int get_parameters(char *buf, long int *param1, int num_of_par)
+{
+ char *token;
+ int base, cnt;
+
+ token = strsep(&buf, " ");
+
+ for (cnt = 0; cnt < num_of_par; cnt++) {
+ if (token != NULL) {
+ if ((token[1] == 'x') || (token[1] == 'X'))
+ base = 16;
+ else
+ base = 10;
+
+ if (strict_strtoul(token, base, ¶m1[cnt]) != 0)
+ return -EINVAL;
+
+ token = strsep(&buf, " ");
+ }
+ else
+ return -EINVAL;
+ }
+ return 0;
+}
+
+static int codec_debug_open(struct inode *inode, struct file *file)
+{
+ file->private_data = inode->i_private;
+ return 0;
+}
+
+static ssize_t codec_debug_read(struct file *file, char __user *ubuf,
+ size_t count, loff_t *ppos)
+{
+ char lbuf[8];
+
+ snprintf(lbuf, sizeof(lbuf), "0x%x\n", read_data);
+ return simple_read_from_buffer(ubuf, count, ppos, lbuf, strlen(lbuf));
+}
+
+static ssize_t codec_debug_write(struct file *filp,
+ const char __user *ubuf, size_t cnt, loff_t *ppos)
+{
+ char *access_str = filp->private_data;
+ char lbuf[32];
+ unsigned char reg_idx[2] = {0x00, 0x00};
+ int rc;
+ long int param[5];
+
+ if (cnt > sizeof(lbuf) - 1)
+ return -EINVAL;
+
+ rc = copy_from_user(lbuf, ubuf, cnt);
+ if (rc)
+ return -EFAULT;
+
+ lbuf[cnt] = '\0';
+
+ if (!strcmp(access_str, "poke")) {
+
+ rc = get_parameters(lbuf, param, 2);
+ if ((param[0] <= 0xFF) && (param[1] <= 0xFF) &&
+ (rc == 0)) {
+ reg_idx[0] = param[0];
+ reg_idx[1] = param[1];
+ tfa9887_i2c_write(reg_idx, 2);
+ } else
+ rc = -EINVAL;
+ } else if (!strcmp(access_str, "peek")) {
+
+ rc = get_parameters(lbuf, param, 1);
+ if ((param[0] <= 0xFF) && (rc == 0)) {
+ reg_idx[0] = param[0];
+ tfa9887_i2c_read(&read_data, 1);
+ } else
+ rc = -EINVAL;
+ }
+
+ if (rc == 0)
+ rc = cnt;
+ else
+ pr_err("%s: rc = %d\n", __func__, rc);
+
+ return rc;
+}
+
+static const struct file_operations codec_debug_ops = {
+ .open = codec_debug_open,
+ .write = codec_debug_write,
+ .read = codec_debug_read
+};
+#endif
+#ifdef CONFIG_AMP_TFA9887L
+unsigned char cf_dsp_bypass[3][3] = {
+ {0x04, 0x88, 0x53},
+ {0x09, 0x06, 0x19},
+ {0x09, 0x06, 0x18}
+};
+#else
+unsigned char cf_dsp_bypass[3][3] = {
+ {0x04, 0x88, 0x0B},
+ {0x09, 0x06, 0x19},
+ {0x09, 0x06, 0x18}
+};
+#endif
+unsigned char amp_off[1][3] = {
+ {0x09, 0x06, 0x19}
+};
+
+static int tfa9887_i2c_write(char *txData, int length)
+{
+ int rc;
+ struct i2c_msg msg[] = {
+ {
+ .addr = this_client->addr,
+ .flags = 0,
+ .len = length,
+ .buf = txData,
+ },
+ };
+
+ rc = i2c_transfer(this_client->adapter, msg, 1);
+ if (rc < 0) {
+ pr_err("%s: transfer error %d\n", __func__, rc);
+ return rc;
+ }
+
+#if DEBUG
+ {
+ int i = 0;
+ for (i = 0; i < length; i++)
+ pr_info("%s: tx[%d] = %2x\n", \
+ __func__, i, txData[i]);
+ }
+#endif
+
+ return 0;
+}
+
+static int tfa9887_i2c_read(char *rxData, int length)
+{
+ int rc;
+ struct i2c_msg msgs[] = {
+ {
+ .addr = this_client->addr,
+ .flags = I2C_M_RD,
+ .len = length,
+ .buf = rxData,
+ },
+ };
+
+ rc = i2c_transfer(this_client->adapter, msgs, 1);
+ if (rc < 0) {
+ pr_err("%s: transfer error %d\n", __func__, rc);
+ return rc;
+ }
+
+#if DEBUG
+ {
+ int i = 0;
+ for (i = 0; i < length; i++)
+ pr_info("i2c_read %s: rx[%d] = %2x\n", __func__, i, \
+ rxData[i]);
+ }
+#endif
+
+ return 0;
+}
+
+static int tfa9887_open(struct inode *inode, struct file *file)
+{
+ int rc = 0;
+
+ if (tfa9887_opened) {
+ pr_info("%s: busy\n", __func__);
+ }
+ tfa9887_opened = 1;
+
+ return rc;
+}
+
+static int tfa9887_release(struct inode *inode, struct file *file)
+{
+ tfa9887_opened = 0;
+
+ return 0;
+}
+
+void set_tfa9887_spkamp(int en, int dsp_mode)
+{
+ int i =0;
+
+
+ unsigned char mute_reg[1] = {0x06};
+ unsigned char mute_data[3] = {0, 0, 0};
+ unsigned char power_reg[1] = {0x09};
+ unsigned char power_data[3] = {0, 0, 0};
+ unsigned char SPK_CR[3] = {0x8, 0x8, 0};
+
+ pr_info("%s: en = %d dsp_enabled = %d\n", __func__, en, dsp_enabled);
+ mutex_lock(&spk_amp_lock);
+ if (en && !last_spkamp_state) {
+ last_spkamp_state = 1;
+
+ if (dsp_enabled == 0) {
+ for (i=0; i <3 ; i++)
+ tfa9887_i2c_write(cf_dsp_bypass[i], 3);
+
+ tfa9887_i2c_write(SPK_CR,1);
+ tfa9887_i2c_read(SPK_CR+1,2);
+ SPK_CR[1] |= 0x4;
+ tfa9887_i2c_write(SPK_CR,3);
+ } else {
+
+
+ tfa9887_i2c_write(power_reg, 1);
+ tfa9887_i2c_read(power_data + 1, 2);
+ tfa9887_i2c_write(mute_reg, 1);
+ tfa9887_i2c_read(mute_data + 1, 2);
+ mute_data[0] = 0x6;
+ mute_data[2] &= 0xdf;
+ power_data[0] = 0x9;
+ power_data[2] &= 0xfe;
+
+ tfa9887_i2c_write(power_data, 3);
+
+ tfa9887_i2c_write(mute_data, 3);
+ power_data[2] |= 0x8;
+ tfa9887_i2c_write(power_data, 3);
+ }
+ } else if (!en && last_spkamp_state) {
+ last_spkamp_state = 0;
+ if (dsp_enabled == 0) {
+ tfa9887_i2c_write(amp_off[0], 3);
+ } else {
+
+
+ tfa9887_i2c_write(power_reg, 1);
+ tfa9887_i2c_read(power_data + 1, 2);
+ tfa9887_i2c_write(mute_reg, 1);
+ tfa9887_i2c_read(mute_data + 1, 2);
+ mute_data[0] = 0x6;
+ mute_data[2] |= 0x20;
+
+ tfa9887_i2c_write(mute_data, 3);
+ tfa9887_i2c_write(power_reg, 1);
+ tfa9887_i2c_read(power_data + 1, 2);
+ power_data[0] = 0x9;
+ power_data[2] &= 0xf7;
+ tfa9887_i2c_write(power_data, 3);
+
+
+ power_data[2] |= 0x1;
+ tfa9887_i2c_write(power_data, 3);
+ }
+ }
+ mutex_unlock(&spk_amp_lock);
+}
+
+static long tfa9887_ioctl(struct file *file, unsigned int cmd,
+ unsigned long arg)
+{
+ int rc = 0;
+ unsigned int reg_value[2];
+ unsigned int len = 0;
+ char *addr;
+ void __user *argp = (void __user *)arg;
+
+ switch (cmd) {
+ case TPA9887_WRITE_CONFIG:
+ pr_debug("%s: TPA9887_WRITE_CONFIG\n", __func__);
+ rc = copy_from_user(reg_value, argp, sizeof(reg_value));
+ if (rc) {
+ pr_err("%s: copy from user failed.\n", __func__);
+ goto err;
+ }
+
+ len = reg_value[0];
+ addr = (char *)reg_value[1];
+
+ tfa9887_i2c_write(addr+1, len -1);
+
+ break;
+ case TPA9887_READ_CONFIG:
+ pr_debug("%s: TPA9887_READ_CONFIG\n", __func__);
+ rc = copy_from_user(reg_value, argp, sizeof(reg_value));;
+ if (rc) {
+ pr_err("%s: copy from user failed.\n", __func__);
+ goto err;
+ }
+
+ len = reg_value[0];
+ addr = (char *)reg_value[1];
+ tfa9887_i2c_read(addr, len);
+
+ rc = copy_to_user(argp, reg_value, sizeof(reg_value));
+ if (rc) {
+ pr_err("%s: copy to user failed.\n", __func__);
+ goto err;
+ }
+ break;
+#ifdef CONFIG_AMP_TFA9887L
+ case TPA9887_WRITE_L_CONFIG:
+ pr_debug("%s: TPA9887_WRITE_CONFIG_L\n", __func__);
+ rc = copy_from_user(reg_value, argp, sizeof(reg_value));
+ if (rc) {
+ pr_err("%s: copy from user failed.\n", __func__);
+ goto err;
+ }
+
+ len = reg_value[0];
+ addr = (char *)reg_value[1];
+ tfa9887_l_write(addr+1, len -1);
+ break;
+ case TPA9887_READ_L_CONFIG:
+ pr_debug("%s: TPA9887_READ_CONFIG_L\n", __func__);
+ rc = copy_from_user(reg_value, argp, sizeof(reg_value));;
+ if (rc) {
+ pr_err("%s: copy from user failed.\n", __func__);
+ goto err;
+ }
+
+ len = reg_value[0];
+ addr = (char *)reg_value[1];
+ tfa9887_l_read(addr, len);
+
+ rc = copy_to_user(argp, reg_value, sizeof(reg_value));
+ if (rc) {
+ pr_err("%s: copy to user failed.\n", __func__);
+ goto err;
+ }
+ break;
+#endif
+ case TPA9887_ENABLE_DSP:
+ pr_info("%s: TPA9887_ENABLE_DSP\n", __func__);
+ rc = copy_from_user(reg_value, argp, sizeof(reg_value));;
+ if (rc) {
+ pr_err("%s: copy from user failed.\n", __func__);
+ goto err;
+ }
+
+ len = reg_value[0];
+ dsp_enabled = reg_value[1];
+ break;
+ case TPA9887_KERNEL_LOCK:
+ rc = copy_from_user(reg_value, argp, sizeof(reg_value));;
+ if (rc) {
+ pr_err("%s: copy from user failed.\n", __func__);
+ goto err;
+ }
+
+ len = reg_value[0];
+
+ pr_debug("TPA9887_KLOCK1 %d\n", reg_value[1]);
+ if (reg_value[1])
+ mutex_lock(&spk_amp_lock);
+ else
+ mutex_unlock(&spk_amp_lock);
+ break;
+ }
+err:
+ return rc;
+}
+
+static struct file_operations tfa9887_fops = {
+ .owner = THIS_MODULE,
+ .open = tfa9887_open,
+ .release = tfa9887_release,
+ .unlocked_ioctl = tfa9887_ioctl,
+};
+
+static struct miscdevice tfa9887_device = {
+ .minor = MISC_DYNAMIC_MINOR,
+ .name = "tfa9887",
+ .fops = &tfa9887_fops,
+};
+
+int tfa9887_probe(struct i2c_client *client, const struct i2c_device_id *id)
+{
+ int ret = 0;
+
+ pdata = client->dev.platform_data;
+
+ if (pdata == NULL) {
+ pdata = kzalloc(sizeof(*pdata), GFP_KERNEL);
+ if (pdata == NULL) {
+ ret = -ENOMEM;
+ pr_err("%s: platform data is NULL\n", __func__);
+ goto err_alloc_data_failed;
+ }
+ }
+
+ this_client = client;
+
+ if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
+ pr_err("%s: i2c check functionality error\n", __func__);
+ ret = -ENODEV;
+ goto err_free_gpio_all;
+ }
+
+ ret = misc_register(&tfa9887_device);
+ if (ret) {
+ pr_err("%s: tfa9887_device register failed\n", __func__);
+ goto err_free_gpio_all;
+ }
+#ifdef CONFIG_DEBUG_FS
+ debugfs_tpa_dent = debugfs_create_dir("tfa9887", 0);
+ if (!IS_ERR(debugfs_tpa_dent)) {
+ debugfs_peek = debugfs_create_file("peek",
+ S_IFREG | S_IRUGO, debugfs_tpa_dent,
+ (void *) "peek", &codec_debug_ops);
+
+ debugfs_poke = debugfs_create_file("poke",
+ S_IFREG | S_IRUGO, debugfs_tpa_dent,
+ (void *) "poke", &codec_debug_ops);
+
+ }
+#endif
+ return 0;
+
+err_free_gpio_all:
+ return ret;
+err_alloc_data_failed:
+ return ret;
+}
+
+static int tfa9887_remove(struct i2c_client *client)
+{
+ struct tfa9887_platform_data *p9887data = i2c_get_clientdata(client);
+ kfree(p9887data);
+
+ return 0;
+}
+
+static int tfa9887_suspend(struct i2c_client *client, pm_message_t mesg)
+{
+ return 0;
+}
+
+static int tfa9887_resume(struct i2c_client *client)
+{
+ return 0;
+}
+
+static const struct i2c_device_id tfa9887_id[] = {
+ { TFA9887_I2C_NAME, 0 },
+ { }
+};
+
+static struct i2c_driver tfa9887_driver = {
+ .probe = tfa9887_probe,
+ .remove = tfa9887_remove,
+ .suspend = tfa9887_suspend,
+ .resume = tfa9887_resume,
+ .id_table = tfa9887_id,
+ .driver = {
+ .name = TFA9887_I2C_NAME,
+ },
+};
+
+static int __init tfa9887_init(void)
+{
+ pr_info("%s\n", __func__);
+ mutex_init(&spk_amp_lock);
+ dsp_enabled = 0;
+ return i2c_add_driver(&tfa9887_driver);
+}
+
+static void __exit tfa9887_exit(void)
+{
+#ifdef CONFIG_DEBUG_FS
+ debugfs_remove(debugfs_peek);
+ debugfs_remove(debugfs_poke);
+ debugfs_remove(debugfs_tpa_dent);
+#endif
+ i2c_del_driver(&tfa9887_driver);
+}
+
+module_init(tfa9887_init);
+module_exit(tfa9887_exit);
+
+MODULE_DESCRIPTION("tfa9887 Speaker Amp driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/misc/tfa9887l.c b/drivers/misc/tfa9887l.c
new file mode 100644
index 0000000..40370af
--- /dev/null
+++ b/drivers/misc/tfa9887l.c
@@ -0,0 +1,509 @@
+/* driver/i2c/chip/tfa9887.c
+ *
+ * NXP tfa9887 Speaker Amp
+ *
+ * Copyright (C) 2012 HTC Corporation
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <linux/interrupt.h>
+#include <linux/i2c.h>
+#include <linux/slab.h>
+#include <linux/irq.h>
+#include <linux/miscdevice.h>
+#include <asm/uaccess.h>
+#include <linux/delay.h>
+#include <linux/input.h>
+#include <linux/workqueue.h>
+#include <linux/freezer.h>
+#include <linux/tfa9887.h>
+#include <linux/mutex.h>
+#include <linux/debugfs.h>
+#include <linux/gpio.h>
+#include <linux/module.h>
+#include <linux/mfd/pm8xxx/pm8921.h>
+
+#define TPA9887_IOCTL_MAGIC 'a'
+#define TPA9887_WRITE_CONFIG _IOW(TPA9887_IOCTL_MAGIC, 0x01, unsigned int)
+#define TPA9887_READ_CONFIG _IOW(TPA9887_IOCTL_MAGIC, 0x02, unsigned int)
+#define TPA9887_ENABLE_DSP _IOW(TPA9887_IOCTL_MAGIC, 0x03, unsigned int)
+#define TPA9887_KERNEL_LOCK _IOW(TPA9887_IOCTL_MAGIC, 0x06, unsigned int)
+#define DEBUG (0)
+
+static struct i2c_client *this_client;
+static struct tfa9887_platform_data *pdata;
+struct mutex spk_ampl_lock;
+static int tfa9887l_opened;
+static int last_spkampl_state;
+static int dspl_enabled;
+static int tfa9887_i2c_write(char *txData, int length);
+static int tfa9887_i2c_read(char *rxData, int length);
+#ifdef CONFIG_DEBUG_FS
+static struct dentry *debugfs_tpa_dent;
+static struct dentry *debugfs_peek;
+static struct dentry *debugfs_poke;
+static unsigned char read_data;
+
+static int get_parameters(char *buf, long int *param1, int num_of_par)
+{
+ char *token;
+ int base, cnt;
+
+ token = strsep(&buf, " ");
+
+ for (cnt = 0; cnt < num_of_par; cnt++) {
+ if (token != NULL) {
+ if ((token[1] == 'x') || (token[1] == 'X'))
+ base = 16;
+ else
+ base = 10;
+
+ if (strict_strtoul(token, base, ¶m1[cnt]) != 0)
+ return -EINVAL;
+
+ token = strsep(&buf, " ");
+ }
+ else
+ return -EINVAL;
+ }
+ return 0;
+}
+
+static int codec_debug_open(struct inode *inode, struct file *file)
+{
+ file->private_data = inode->i_private;
+ return 0;
+}
+
+static ssize_t codec_debug_read(struct file *file, char __user *ubuf,
+ size_t count, loff_t *ppos)
+{
+ char lbuf[8];
+
+ snprintf(lbuf, sizeof(lbuf), "0x%x\n", read_data);
+ return simple_read_from_buffer(ubuf, count, ppos, lbuf, strlen(lbuf));
+}
+
+static ssize_t codec_debug_write(struct file *filp,
+ const char __user *ubuf, size_t cnt, loff_t *ppos)
+{
+ char *access_str = filp->private_data;
+ char lbuf[32];
+ unsigned char reg_idx[2] = {0x00, 0x00};
+ int rc;
+ long int param[5];
+
+ if (cnt > sizeof(lbuf) - 1)
+ return -EINVAL;
+
+ rc = copy_from_user(lbuf, ubuf, cnt);
+ if (rc)
+ return -EFAULT;
+
+ lbuf[cnt] = '\0';
+
+ if (!strcmp(access_str, "poke")) {
+
+ rc = get_parameters(lbuf, param, 2);
+ if ((param[0] <= 0xFF) && (param[1] <= 0xFF) &&
+ (rc == 0)) {
+ reg_idx[0] = param[0];
+ reg_idx[1] = param[1];
+ tfa9887_i2c_write(reg_idx, 2);
+ } else
+ rc = -EINVAL;
+ } else if (!strcmp(access_str, "peek")) {
+
+ rc = get_parameters(lbuf, param, 1);
+ if ((param[0] <= 0xFF) && (rc == 0)) {
+ reg_idx[0] = param[0];
+ tfa9887_i2c_read(&read_data, 1);
+ } else
+ rc = -EINVAL;
+ }
+
+ if (rc == 0)
+ rc = cnt;
+ else
+ pr_err("%s: rc = %d\n", __func__, rc);
+
+ return rc;
+}
+
+static const struct file_operations codec_debug_ops = {
+ .open = codec_debug_open,
+ .write = codec_debug_write,
+ .read = codec_debug_read
+};
+#endif
+
+unsigned char cf_dspl_bypass[3][3] = {
+ {0x04, 0x88, 0x0B},
+ {0x09, 0x06, 0x19},
+ {0x09, 0x06, 0x18}
+};
+
+unsigned char ampl_off[1][3] = {
+ {0x09, 0x06, 0x19}
+};
+
+static int tfa9887_i2c_write(char *txData, int length)
+{
+ int rc;
+ struct i2c_msg msg[] = {
+ {
+ .addr = this_client->addr,
+ .flags = 0,
+ .len = length,
+ .buf = txData,
+ },
+ };
+
+ rc = i2c_transfer(this_client->adapter, msg, 1);
+ if (rc < 0) {
+ pr_err("%s: transfer error %d\n", __func__, rc);
+ return rc;
+ }
+
+#if DEBUG
+ {
+ int i = 0;
+ for (i = 0; i < length; i++)
+ pr_info("%s: tx[%d] = %2x\n", \
+ __func__, i, txData[i]);
+ }
+#endif
+
+ return 0;
+}
+
+static int tfa9887_i2c_read(char *rxData, int length)
+{
+ int rc;
+ struct i2c_msg msgs[] = {
+ {
+ .addr = this_client->addr,
+ .flags = I2C_M_RD,
+ .len = length,
+ .buf = rxData,
+ },
+ };
+
+ rc = i2c_transfer(this_client->adapter, msgs, 1);
+ if (rc < 0) {
+ pr_err("%s: transfer error %d\n", __func__, rc);
+ return rc;
+ }
+
+#if DEBUG
+ {
+ int i = 0;
+ for (i = 0; i < length; i++)
+ pr_info("i2c_read %s: rx[%d] = %2x\n", __func__, i, \
+ rxData[i]);
+ }
+#endif
+
+ return 0;
+}
+
+static int tfa9887l_open(struct inode *inode, struct file *file)
+{
+ int rc = 0;
+
+ if (tfa9887l_opened) {
+ pr_info("%s: busy\n", __func__);
+ }
+ tfa9887l_opened = 1;
+
+ return rc;
+}
+
+static int tfa9887l_release(struct inode *inode, struct file *file)
+{
+ tfa9887l_opened = 0;
+
+ return 0;
+}
+
+
+int tfa9887_l_write(char *txData, int length) {
+ return tfa9887_i2c_write(txData, length);
+}
+
+int tfa9887_l_read(char *rxData, int length) {
+ return tfa9887_i2c_read(rxData, length);
+}
+
+void set_tfa9887l_spkamp(int en, int dsp_mode)
+{
+ int i =0;
+
+
+ unsigned char mute_reg[1] = {0x06};
+ unsigned char mute_data[3] = {0, 0, 0};
+ unsigned char power_reg[1] = {0x09};
+ unsigned char power_data[3] = {0, 0, 0};
+ unsigned char SPK_CR[3] = {0x8, 0x8, 0};
+
+ pr_info("%s: en = %d dsp_enabled = %d\n", __func__, en, dspl_enabled);
+ mutex_lock(&spk_ampl_lock);
+ if (en && !last_spkampl_state) {
+ last_spkampl_state = 1;
+
+ if (dspl_enabled == 0) {
+ for (i=0; i <3 ; i++)
+ tfa9887_i2c_write(cf_dspl_bypass[i], 3);
+
+ tfa9887_i2c_write(SPK_CR,1);
+ tfa9887_i2c_read(SPK_CR+1,2);
+ SPK_CR[1] |= 0x4;
+ tfa9887_i2c_write(SPK_CR,3);
+ } else {
+
+
+ tfa9887_i2c_write(power_reg, 1);
+ tfa9887_i2c_read(power_data + 1, 2);
+ tfa9887_i2c_write(mute_reg, 1);
+ tfa9887_i2c_read(mute_data + 1, 2);
+ mute_data[0] = 0x6;
+ mute_data[2] &= 0xdf;
+ power_data[0] = 0x9;
+ power_data[2] &= 0xfe;
+
+ tfa9887_i2c_write(power_data, 3);
+
+ tfa9887_i2c_write(mute_data, 3);
+ power_data[2] |= 0x8;
+ tfa9887_i2c_write(power_data, 3);
+ }
+ } else if (!en && last_spkampl_state) {
+ last_spkampl_state = 0;
+ if (dspl_enabled == 0) {
+ tfa9887_i2c_write(ampl_off[0], 3);
+ } else {
+
+
+ tfa9887_i2c_write(power_reg, 1);
+ tfa9887_i2c_read(power_data + 1, 2);
+ tfa9887_i2c_write(mute_reg, 1);
+ tfa9887_i2c_read(mute_data + 1, 2);
+ mute_data[0] = 0x6;
+ mute_data[2] |= 0x20;
+
+ tfa9887_i2c_write(mute_data, 3);
+ tfa9887_i2c_write(power_reg, 1);
+ tfa9887_i2c_read(power_data + 1, 2);
+ power_data[0] = 0x9;
+ power_data[2] &= 0xf7;
+ tfa9887_i2c_write(power_data, 3);
+
+
+ power_data[2] |= 0x1;
+ tfa9887_i2c_write(power_data, 3);
+ }
+ }
+ mutex_unlock(&spk_ampl_lock);
+}
+
+static long tfa9887l_ioctl(struct file *file, unsigned int cmd,
+ unsigned long arg)
+{
+ int rc = 0;
+ unsigned int reg_value[2];
+ unsigned int len = 0;
+ char *addr;
+ void __user *argp = (void __user *)arg;
+
+ switch (cmd) {
+ case TPA9887_WRITE_CONFIG:
+ pr_debug("%s: TPA9887_WRITE_CONFIG\n", __func__);
+ rc = copy_from_user(reg_value, argp, sizeof(reg_value));
+ if (rc) {
+ pr_err("%s: copy from user failed.\n", __func__);
+ goto err;
+ }
+
+ len = reg_value[0];
+ addr = (char *)reg_value[1];
+
+ tfa9887_i2c_write(addr+1, len -1);
+
+ break;
+ case TPA9887_READ_CONFIG:
+ pr_debug("%s: TPA9887_READ_CONFIG\n", __func__);
+ rc = copy_from_user(reg_value, argp, sizeof(reg_value));;
+ if (rc) {
+ pr_err("%s: copy from user failed.\n", __func__);
+ goto err;
+ }
+
+ len = reg_value[0];
+ addr = (char *)reg_value[1];
+ tfa9887_i2c_read(addr, len);
+
+ rc = copy_to_user(argp, reg_value, sizeof(reg_value));
+ if (rc) {
+ pr_err("%s: copy to user failed.\n", __func__);
+ goto err;
+ }
+ break;
+ case TPA9887_ENABLE_DSP:
+ pr_info("%s: TPA9887_ENABLE_DSP\n", __func__);
+ rc = copy_from_user(reg_value, argp, sizeof(reg_value));;
+ if (rc) {
+ pr_err("%s: copy from user failed.\n", __func__);
+ goto err;
+ }
+
+ len = reg_value[0];
+ dspl_enabled = reg_value[1];
+ break;
+ case TPA9887_KERNEL_LOCK:
+ rc = copy_from_user(reg_value, argp, sizeof(reg_value));;
+ if (rc) {
+ pr_err("%s: copy from user failed.\n", __func__);
+ goto err;
+ }
+
+ len = reg_value[0];
+
+ pr_debug("TPA9887_KLOCK2 %d\n", reg_value[1]);
+ if (reg_value[1])
+ mutex_lock(&spk_ampl_lock);
+ else
+ mutex_unlock(&spk_ampl_lock);
+ break;
+ }
+err:
+ return rc;
+}
+
+static struct file_operations tfa9887l_fops = {
+ .owner = THIS_MODULE,
+ .open = tfa9887l_open,
+ .release = tfa9887l_release,
+ .unlocked_ioctl = tfa9887l_ioctl,
+};
+
+static struct miscdevice tfa9887l_device = {
+ .minor = MISC_DYNAMIC_MINOR,
+ .name = "tfa9887l",
+ .fops = &tfa9887l_fops,
+};
+
+int tfa9887l_probe(struct i2c_client *client, const struct i2c_device_id *id)
+{
+ int ret = 0;
+
+ pdata = client->dev.platform_data;
+
+ if (pdata == NULL) {
+ pdata = kzalloc(sizeof(*pdata), GFP_KERNEL);
+ if (pdata == NULL) {
+ ret = -ENOMEM;
+ pr_err("%s: platform data is NULL\n", __func__);
+ goto err_alloc_data_failed;
+ }
+ }
+
+ this_client = client;
+
+ if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
+ pr_err("%s: i2c check functionality error\n", __func__);
+ ret = -ENODEV;
+ goto err_free_gpio_all;
+ }
+
+ ret = misc_register(&tfa9887l_device);
+ if (ret) {
+ pr_err("%s: tfa9887l_device register failed\n", __func__);
+ goto err_free_gpio_all;
+ }
+#ifdef CONFIG_DEBUG_FS
+ debugfs_tpa_dent = debugfs_create_dir("tfa9887", 0);
+ if (!IS_ERR(debugfs_tpa_dent)) {
+ debugfs_peek = debugfs_create_file("peek",
+ S_IFREG | S_IRUGO, debugfs_tpa_dent,
+ (void *) "peek", &codec_debug_ops);
+
+ debugfs_poke = debugfs_create_file("poke",
+ S_IFREG | S_IRUGO, debugfs_tpa_dent,
+ (void *) "poke", &codec_debug_ops);
+
+ }
+#endif
+ return 0;
+
+err_free_gpio_all:
+ return ret;
+err_alloc_data_failed:
+ return ret;
+}
+
+static int tfa9887l_remove(struct i2c_client *client)
+{
+ struct tfa9887_platform_data *p9887data = i2c_get_clientdata(client);
+ kfree(p9887data);
+
+ return 0;
+}
+
+static int tfa9887l_suspend(struct i2c_client *client, pm_message_t mesg)
+{
+ return 0;
+}
+
+static int tfa9887l_resume(struct i2c_client *client)
+{
+ return 0;
+}
+
+static const struct i2c_device_id tfa9887l_id[] = {
+ { TFA9887L_I2C_NAME, 0 },
+ { }
+};
+
+static struct i2c_driver tfa9887l_driver = {
+ .probe = tfa9887l_probe,
+ .remove = tfa9887l_remove,
+ .suspend = tfa9887l_suspend,
+ .resume = tfa9887l_resume,
+ .id_table = tfa9887l_id,
+ .driver = {
+ .name = TFA9887L_I2C_NAME,
+ },
+};
+
+static int __init tfa9887l_init(void)
+{
+ pr_info("%s\n", __func__);
+ mutex_init(&spk_ampl_lock);
+ dspl_enabled = 0;
+ return i2c_add_driver(&tfa9887l_driver);
+}
+
+static void __exit tfa9887l_exit(void)
+{
+#ifdef CONFIG_DEBUG_FS
+ debugfs_remove(debugfs_peek);
+ debugfs_remove(debugfs_poke);
+ debugfs_remove(debugfs_tpa_dent);
+#endif
+ i2c_del_driver(&tfa9887l_driver);
+}
+
+module_init(tfa9887l_init);
+module_exit(tfa9887l_exit);
+
+MODULE_DESCRIPTION("tfa9887 L Speaker Amp driver");
+MODULE_LICENSE("GPL");
diff --git a/include/linux/tfa9887.h b/include/linux/tfa9887.h
new file mode 100644
index 0000000..68012d5
--- /dev/null
+++ b/include/linux/tfa9887.h
@@ -0,0 +1,18 @@
+#ifndef TFA9887_H
+#define TFA9887_H
+
+#include <linux/ioctl.h>
+
+#define TFA9887_I2C_NAME "tfa9887"
+#define TFA9887L_I2C_NAME "tfa9887l"
+struct tfa9887_platform_data {
+ uint32_t gpio_tfa9887_spk_en;
+
+};
+
+void set_tfa9887_spkamp(int en, int dsp_mode);
+void set_tfa9887l_spkamp(int en, int dsp_mode);
+int tfa9887_l_write(char *txData, int length);
+int tfa9887_l_read(char *rxData, int length);
+#endif
+