misc: a1028: Add HTC VP A1028 driver
HTC kernel version: evitaul-jb-crc-3.4.10-ec474a3
Change-Id: I9dee52ad86d3ffef4054cfc81dda82a8e5abcf42
diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig
index 87a7d26..f310dac 100644
--- a/drivers/misc/Kconfig
+++ b/drivers/misc/Kconfig
@@ -695,6 +695,12 @@
boolean "Cable detect accessory by PMIC ADC"
default n
+config VP_A1028
+ tristate "A1028 Voice Processor driver"
+ depends on I2C
+ help
+ A1028 Voice Processor 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 a38df5e..a72ff1b 100644
--- a/drivers/misc/Makefile
+++ b/drivers/misc/Makefile
@@ -76,3 +76,4 @@
obj-$(CONFIG_BU52031NVX) += pm8xxx-cradle.o
obj-$(CONFIG_SLIMPORT_ANX7808) += slimport_anx7808/
obj-$(CONFIG_CABLE_DETECT_8XXX) += cable_detect_8xxx.o
+obj-$(CONFIG_VP_A1028) += a1028.o
diff --git a/drivers/misc/a1028.c b/drivers/misc/a1028.c
new file mode 100644
index 0000000..07a4092
--- /dev/null
+++ b/drivers/misc/a1028.c
@@ -0,0 +1,1343 @@
+/* drivers/i2c/chips/a1028.c - a1028 voice processor driver
+ *
+ * Copyright (C) 2009 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/module.h>
+#include <linux/i2c.h>
+#include <linux/slab.h>
+#include <linux/irq.h>
+#include <linux/miscdevice.h>
+#include <linux/gpio.h>
+#include <linux/uaccess.h>
+#include <linux/delay.h>
+#include <linux/input.h>
+#include <linux/workqueue.h>
+#include <linux/freezer.h>
+#include <linux/a1028.h>
+#include <mach/msm_xo.h>
+#define DEBUG (0)
+#define ENABLE_DIAG_IOCTLS (1)
+
+#define PARAM_MAX (sizeof(char) * 6 * 1024)
+
+static struct i2c_client *this_client;
+static struct a1028_platform_data *pdata;
+
+static int execute_cmdmsg(unsigned int);
+
+static struct mutex a1028_lock;
+static int a1028_opened;
+static int a1028_suspended;
+static int control_a1028_clk;
+static int control_a1028_xo_clk;
+static int control_a1028_micsel;
+static int a1028_NS_state = A1028_NS_STATE_AUTO;
+static int a1028_current_config = A1028_PATH_SUSPEND;
+static int a1028_param_ID;
+static char *config_data;
+static int a1028_cmds_len;
+
+struct vp_ctxt {
+ unsigned char *data;
+ unsigned int img_size;
+};
+
+struct vp_ctxt the_vp;
+
+static struct msm_xo_voter *a1028_clock;
+static const char *cid = "A1028";
+static int a1028_xo_clk_enable(int en)
+{
+ int rc = 0;
+ if (a1028_clock != NULL) {
+ if (en)
+ rc = msm_xo_mode_vote(a1028_clock, MSM_XO_MODE_ON);
+ else
+ rc = msm_xo_mode_vote(a1028_clock, MSM_XO_MODE_OFF);
+ if (rc < 0) {
+ pr_err("Configuring MSM_XO_MODE_ON failed"
+ " (%d)\n", rc);
+ goto fail;
+ }
+ } else
+ pr_err("a1028_clock is null\n");
+ return rc;
+fail:
+ msm_xo_put(a1028_clock);
+ return rc;
+}
+
+static void a1028_gpio_set_value(int gpio, int value)
+{
+ if (gpio > 0)
+ gpio_set_value(gpio, value);
+}
+
+static void a1028_pmic_set_value(int gpio, int value)
+{
+}
+
+static int a1028_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("%s: rx[%d] = %2x\n", \
+ __func__, i, rxData[i]);
+ }
+#endif
+
+ return 0;
+}
+
+static int a1028_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 a1028_open(struct inode *inode, struct file *file)
+{
+ int rc = 0;
+ struct vp_ctxt *vp = &the_vp;
+
+ mutex_lock(&a1028_lock);
+
+ if (a1028_opened) {
+ pr_err("%s: busy\n", __func__);
+ rc = -EBUSY;
+ goto done;
+ }
+
+ file->private_data = vp;
+ vp->img_size = 0;
+ a1028_opened = 1;
+done:
+ mutex_unlock(&a1028_lock);
+ return rc;
+}
+
+static int a1028_release(struct inode *inode, struct file *file)
+{
+ mutex_lock(&a1028_lock);
+ a1028_opened = 0;
+ mutex_unlock(&a1028_lock);
+
+ return 0;
+}
+
+static void a1028_i2c_sw_reset(unsigned int reset_cmd)
+{
+ int rc = 0;
+ unsigned char msgbuf[4];
+
+ msgbuf[0] = (reset_cmd >> 24) & 0xFF;
+ msgbuf[1] = (reset_cmd >> 16) & 0xFF;
+ msgbuf[2] = (reset_cmd >> 8) & 0xFF;
+ msgbuf[3] = reset_cmd & 0xFF;
+
+ pr_info("%s: %08x\n", __func__, reset_cmd);
+
+ rc = a1028_i2c_write(msgbuf, 4);
+ if (!rc)
+ msleep(20);
+}
+
+static ssize_t a1028_bootup_init(struct file *file, struct a1028img *img)
+{
+ struct vp_ctxt *vp = file->private_data;
+ int rc, pass = 0;
+ int remaining;
+ int retry = RETRY_CNT;
+ unsigned char *index;
+ char buf[2];
+
+ if (img->img_size > A1028_MAX_FW_SIZE) {
+ pr_err("%s: invalid a1028 image size %d\n", __func__,
+ img->img_size);
+ return -EINVAL;
+ }
+
+ vp->data = kmalloc(img->img_size, GFP_KERNEL);
+ if (!vp->data) {
+ pr_err("%s: out of memory\n", __func__);
+ return -ENOMEM;
+ }
+ vp->img_size = img->img_size;
+ if (copy_from_user(vp->data, img->buf, img->img_size)) {
+ pr_err("%s: copy from user failed\n", __func__);
+ kfree(vp->data);
+ return -EFAULT;
+ }
+
+ while (retry--) {
+
+ a1028_gpio_set_value(pdata->gpio_a1028_reset, 0);
+
+
+ if (control_a1028_clk) {
+ a1028_gpio_set_value(pdata->gpio_a1028_clk, 1);
+ mdelay(1);
+ }
+ if (control_a1028_xo_clk) {
+ a1028_xo_clk_enable(1);
+ mdelay(1);
+ }
+
+ a1028_gpio_set_value(pdata->gpio_a1028_reset, 1);
+
+ msleep(50);
+
+
+ buf[0] = A1028_msg_BOOT >> 8;
+ buf[1] = A1028_msg_BOOT & 0xff;
+
+ rc = a1028_i2c_write(buf, 2);
+ if (rc < 0) {
+ pr_err("%s: set boot mode error \
+ (%d retries left)\n", __func__, retry);
+ continue;
+ }
+
+ mdelay(1);
+ rc = a1028_i2c_read(buf, 1);
+ if (rc < 0) {
+ pr_err("%s: boot mode ack error \
+ (%d retries left)\n", __func__, retry);
+ continue;
+ }
+
+ if (buf[0] != A1028_msg_BOOT_ACK) {
+ pr_err("%s: not a boot-mode ack \
+ (%d retries left)\n", __func__, retry);
+ continue;
+ }
+
+ remaining = vp->img_size / 32;
+ index = vp->data;
+
+ pr_info("%s: starting to load image (%d passes)...\n",
+ __func__,
+ remaining + !!(vp->img_size % 32));
+
+ for (; remaining; remaining--, index += 32) {
+ rc = a1028_i2c_write(index, 32);
+ if (rc < 0)
+ break;
+ }
+
+ if (rc >= 0 && vp->img_size % 32)
+ rc = a1028_i2c_write(index, vp->img_size % 32);
+
+ if (rc < 0) {
+ pr_err("%s: fw load error %d (%d retries left)\n",
+ __func__, rc, retry);
+ continue;
+ }
+
+ msleep(20);
+
+ pr_info("%s: firmware loaded successfully\n", __func__);
+
+ rc = execute_cmdmsg(A100_msg_Sync);
+ if (rc < 0) {
+ pr_err("%s: sync command error %d \
+ (%d retries left)\n", __func__, rc, retry);
+ continue;
+ }
+
+ pass = 1;
+ break;
+ }
+
+
+ rc = execute_cmdmsg(A100_msg_Sleep);
+ if (rc < 0) {
+ pr_err("%s: suspend error\n", __func__);
+ goto set_suspend_err;
+ }
+
+ a1028_suspended = 1;
+ a1028_current_config = A1028_PATH_SUSPEND;
+
+ msleep(120);
+
+ if (control_a1028_clk)
+ a1028_gpio_set_value(pdata->gpio_a1028_clk, 0);
+ if (control_a1028_xo_clk)
+ a1028_xo_clk_enable(0);
+
+set_suspend_err:
+ if (pass && !rc)
+ pr_info("%s: initialized!\n", __func__);
+ else
+ pr_err("%s: initialization failed\n", __func__);
+
+ kfree(vp->data);
+ return rc;
+}
+
+unsigned char phonecall_receiver[] = {
+ 0x80, 0x1C, 0x00, 0x01,
+ 0x80, 0x17, 0x00, 0x02,
+ 0x80, 0x18, 0x00, 0x00,
+ 0x80, 0x17, 0x00, 0x00,
+ 0x80, 0x18, 0x00, 0x04,
+ 0x80, 0x1B, 0x00, 0x0C,
+ 0x80, 0x1B, 0x01, 0x0C,
+ 0x80, 0x17, 0x00, 0x04,
+ 0x80, 0x18, 0x00, 0x00,
+};
+
+unsigned char phonecall_headset[] = {
+ 0x80, 0x1C, 0x00, 0x01,
+ 0x80, 0x17, 0x00, 0x02,
+ 0x80, 0x18, 0x00, 0x03,
+ 0x80, 0x26, 0x00, 0x15,
+ 0x80, 0x17, 0x00, 0x00,
+ 0x80, 0x18, 0x00, 0x04,
+ 0x80, 0x1B, 0x00, 0x0D,
+ 0x80, 0x17, 0x00, 0x04,
+ 0x80, 0x18, 0x00, 0x00,
+};
+
+unsigned char phonecall_TTY_headset[] = {
+ 0x80, 0x17, 0x00, 0x02,
+ 0x80, 0x18, 0x00, 0x03,
+ 0x80, 0x26, 0x00, 0x15,
+ 0x80, 0x1C, 0x00, 0x00,
+ 0x80, 0x1B, 0x00, 0x00,
+ 0x80, 0x1B, 0x01, 0x00,
+ 0x80, 0x15, 0x00, 0x00,
+};
+
+unsigned char phonecall_speaker[] = {
+ 0x80, 0x1C, 0x00, 0x01,
+ 0x80, 0x17, 0x00, 0x02,
+ 0x80, 0x18, 0x00, 0x02,
+ 0x80, 0x17, 0x00, 0x00,
+ 0x80, 0x18, 0x00, 0x04,
+ 0x80, 0x1B, 0x00, 0x14,
+ 0x80, 0x17, 0x00, 0x04,
+ 0x80, 0x18, 0x00, 0x00,
+};
+
+unsigned char phonecall_bt[] = {
+ 0x80, 0x17, 0x00, 0x02,
+ 0x80, 0x18, 0x00, 0x03,
+ 0x80, 0x26, 0x00, 0x06,
+ 0x80, 0x1C, 0x00, 0x00,
+ 0x80, 0x1B, 0x00, 0x00,
+ 0x80, 0x15, 0x00, 0x00,
+};
+
+unsigned char INT_MIC_recording_receiver[] = {
+ 0x80, 0x17, 0x00, 0x02,
+ 0x80, 0x18, 0x00, 0x00,
+ 0x80, 0x26, 0x00, 0x01,
+ 0x80, 0x1B, 0x00, 0x0D,
+ 0x80, 0x1B, 0x01, 0x0D,
+ 0x80, 0x15, 0x00, 0x00,
+ 0x80, 0x1C, 0x00, 0x03,
+};
+
+unsigned char INT_MIC_stereo_recording_receiver[] = {
+ 0x80, 0x17, 0x00, 0x02,
+ 0x80, 0x18, 0x00, 0x03,
+ 0x80, 0x26, 0x00, 0x07,
+ 0x80, 0x1B, 0x00, 0xFA,
+ 0x80, 0x15, 0x00, 0x00,
+ 0x80, 0x1C, 0x00, 0x03,
+};
+
+unsigned char EXT_MIC_recording[] = {
+ 0x80, 0x17, 0x00, 0x02,
+ 0x80, 0x18, 0x00, 0x03,
+ 0x80, 0x26, 0x00, 0x15,
+ 0x80, 0x1B, 0x00, 0x0D,
+ 0x80, 0x1B, 0x01, 0x00,
+ 0x80, 0x15, 0x00, 0x00,
+ 0x80, 0x1C, 0x00, 0x03,
+};
+
+unsigned char INT_MIC_recording_speaker[] = {
+ 0x80, 0x17, 0x00, 0x02,
+ 0x80, 0x18, 0x00, 0x00,
+ 0x80, 0x26, 0x00, 0x01,
+ 0x80, 0x1B, 0x00, 0x0D,
+ 0x80, 0x1B, 0x01, 0x0D,
+ 0x80, 0x15, 0x00, 0x00,
+ 0x80, 0x1C, 0x00, 0x03,
+};
+
+unsigned char INT_MIC_stereo_recording_speaker[] = {
+ 0x80, 0x17, 0x00, 0x02,
+ 0x80, 0x18, 0x00, 0x03,
+ 0x80, 0x26, 0x00, 0x07,
+ 0x80, 0x1B, 0x00, 0xFA,
+ 0x80, 0x15, 0x00, 0x00,
+ 0x80, 0x1C, 0x00, 0x03,
+};
+
+unsigned char BACK_MIC_recording[] = {
+ 0x80, 0x17, 0x00, 0x02,
+ 0x80, 0x18, 0x00, 0x00,
+ 0x80, 0x26, 0x00, 0x15,
+ 0x80, 0x1B, 0x00, 0x0D,
+ 0x80, 0x15, 0x00, 0x00,
+ 0x80, 0x1C, 0x00, 0x03,
+};
+
+unsigned char vr_no_ns_receiver[] = {
+ 0x80, 0x17, 0x00, 0x02,
+ 0x80, 0x18, 0x00, 0x00,
+ 0x80, 0x1C, 0x00, 0x00,
+ 0x80, 0x1B, 0x00, 0x0C,
+ 0x80, 0x1B, 0x01, 0x0C,
+ 0x80, 0x15, 0x00, 0x00,
+};
+
+unsigned char vr_no_ns_headset[] = {
+ 0x80, 0x17, 0x00, 0x02,
+ 0x80, 0x18, 0x00, 0x03,
+ 0x80, 0x26, 0x00, 0x15,
+ 0x80, 0x1C, 0x00, 0x00,
+ 0x80, 0x1B, 0x00, 0x12,
+ 0x80, 0x15, 0x00, 0x00,
+};
+
+unsigned char vr_no_ns_speaker[] = {
+ 0x80, 0x17, 0x00, 0x02,
+ 0x80, 0x18, 0x00, 0x02,
+ 0x80, 0x1C, 0x00, 0x00,
+ 0x80, 0x1B, 0x00, 0x0C,
+ 0x80, 0x15, 0x00, 0x00,
+};
+
+unsigned char vr_no_ns_bt[] = {
+ 0x80, 0x26, 0x00, 0x06,
+ 0x80, 0x1C, 0x00, 0x00,
+ 0x80, 0x1B, 0x00, 0x00,
+ 0x80, 0x15, 0x00, 0x00,
+};
+
+unsigned char vr_ns_receiver[] = {
+ 0x80, 0x17, 0x00, 0x02,
+ 0x80, 0x18, 0x00, 0x00,
+ 0x80, 0x1C, 0x00, 0x01,
+ 0x80, 0x17, 0x00, 0x1A,
+ 0x80, 0x18, 0x00, 0x00,
+ 0x80, 0x17, 0x00, 0x04,
+ 0x80, 0x18, 0x00, 0x00,
+ 0x80, 0x17, 0x00, 0x00,
+ 0x80, 0x18, 0x00, 0x04,
+ 0x80, 0x1B, 0x00, 0x0C,
+ 0x80, 0x1B, 0x01, 0x0C,
+ 0x80, 0x15, 0x00, 0x00,
+};
+
+unsigned char vr_ns_headset[] = {
+ 0x80, 0x17, 0x00, 0x02,
+ 0x80, 0x18, 0x00, 0x03,
+ 0x80, 0x26, 0x00, 0x15,
+ 0x80, 0x1C, 0x00, 0x01,
+ 0x80, 0x17, 0x00, 0x00,
+ 0x80, 0x18, 0x00, 0x02,
+ 0x80, 0x17, 0x00, 0x1A,
+ 0x80, 0x18, 0x00, 0x00,
+ 0x80, 0x17, 0x00, 0x04,
+ 0x80, 0x18, 0x00, 0x00,
+ 0x80, 0x1B, 0x00, 0x12,
+ 0x80, 0x15, 0x00, 0x00,
+};
+
+unsigned char vr_ns_speaker[] = {
+ 0x80, 0x17, 0x00, 0x02,
+ 0x80, 0x18, 0x00, 0x02,
+ 0x80, 0x1C, 0x00, 0x01,
+ 0x80, 0x17, 0x00, 0x00,
+ 0x80, 0x18, 0x00, 0x04,
+ 0x80, 0x17, 0x00, 0x04,
+ 0x80, 0x18, 0x00, 0x00,
+ 0x80, 0x17, 0x00, 0x1A,
+ 0x80, 0x18, 0x00, 0x00,
+ 0x80, 0x1B, 0x00, 0x0C,
+ 0x80, 0x15, 0x00, 0x00,
+};
+
+unsigned char vr_ns_bt[] = {
+ 0x80, 0x26, 0x00, 0x06,
+ 0x80, 0x1C, 0x00, 0x01,
+ 0x80, 0x17, 0x00, 0x00,
+ 0x80, 0x18, 0x00, 0x02,
+ 0x80, 0x17, 0x00, 0x04,
+ 0x80, 0x18, 0x00, 0x00,
+ 0x80, 0x17, 0x00, 0x1A,
+ 0x80, 0x18, 0x00, 0x00,
+ 0x80, 0x1B, 0x00, 0x00,
+ 0x80, 0x15, 0x00, 0x00,
+};
+
+unsigned char suspend_mode[] = {
+ 0x80, 0x10, 0x00, 0x01
+};
+
+unsigned char mfg_loopback[] = {
+ 0x80, 0x1C, 0x00, 0x00,
+ 0x80, 0x17, 0x00, 0x02,
+ 0x80, 0x18, 0x00, 0x00,
+ 0x80, 0x17, 0x00, 0x00,
+ 0x80, 0x18, 0x00, 0x04,
+ 0x80, 0x1B, 0x00, 0x00,
+ 0x80, 0x1B, 0x01, 0x00,
+ 0x80, 0x17, 0x00, 0x04,
+ 0x80, 0x18, 0x00, 0x00,
+ 0x80, 0x17, 0x00, 0x0E,
+ 0x80, 0x18, 0x00, 0x00,
+ 0x80, 0x17, 0x00, 0x15,
+ 0x80, 0x18, 0x00, 0x01,
+};
+
+static ssize_t chk_wakeup_a1028(void)
+{
+ int rc = 0, retry = 3;
+
+ if (a1028_suspended == 1) {
+
+ if (control_a1028_clk) {
+ a1028_gpio_set_value(pdata->gpio_a1028_clk, 1);
+ mdelay(1);
+ }
+ if (control_a1028_xo_clk) {
+ a1028_xo_clk_enable(1);
+ mdelay(1);
+ }
+
+ a1028_gpio_set_value(pdata->gpio_a1028_wakeup, 0);
+ msleep(120);
+
+ do {
+ rc = execute_cmdmsg(A100_msg_Sync);
+ } while ((rc < 0) && --retry);
+
+ a1028_gpio_set_value(pdata->gpio_a1028_wakeup, 1);
+ if (rc < 0) {
+ pr_err("%s: failed (%d)\n", __func__, rc);
+ goto wakeup_sync_err;
+ }
+
+ a1028_suspended = 0;
+ }
+wakeup_sync_err:
+ return rc;
+}
+
+int a1028_filter_vp_cmd(int cmd, int mode)
+{
+ int msg = (cmd >> 16) & 0xFFFF;
+ int filtered_cmd = cmd;
+
+ if (a1028_NS_state == A1028_NS_STATE_AUTO)
+ return cmd;
+
+ switch (msg) {
+ case A100_msg_Bypass:
+ if (a1028_NS_state == A1028_NS_STATE_OFF)
+ filtered_cmd = A1028_msg_VP_OFF;
+ else
+ filtered_cmd = A1028_msg_VP_ON;
+ break;
+ case A100_msg_SetAlgorithmParmID:
+ a1028_param_ID = cmd & 0xFFFF;
+ break;
+ case A100_msg_SetAlgorithmParm:
+ if (a1028_param_ID == Mic_Config) {
+ if (a1028_NS_state == A1028_NS_STATE_CT)
+ filtered_cmd = (msg << 16);
+ else if (a1028_NS_state == A1028_NS_STATE_FT)
+ filtered_cmd = (msg << 16) | 0x0002;
+ }
+ break;
+ default:
+ if (mode == A1028_CONFIG_VP)
+ filtered_cmd = -1;
+ break;
+ }
+
+ pr_info("%s: %x filtered = %x, a1028_NS_state %d, mode %d\n",\
+ __func__, cmd, filtered_cmd, a1028_NS_state, mode);
+
+ return filtered_cmd;
+}
+int build_cmds(char *cmds, int newid)
+{
+ int i = 0;
+ int offset = 0;
+ for (i = 0; i < a1028_cmds_len; i += 6)
+ if (config_data[i] == newid) {
+ cmds[offset++] = config_data[i + 2];
+ cmds[offset++] = config_data[i + 3];
+ cmds[offset++] = config_data[i + 4];
+ cmds[offset++] = config_data[i + 5];
+ }
+ if (offset > 128) {
+ pr_err("Wrong i2c commands\n");
+ return 0;
+ }
+ return offset;
+}
+int a1028_set_config(int newid, int mode)
+{
+ int rc = 0, size = 0;
+ int number_of_cmd_sets, rd_retry_cnt;
+ unsigned int sw_reset = 0;
+ unsigned char *i2c_cmds;
+ unsigned char *index = 0;
+ unsigned char ack_buf[A1028_CMD_FIFO_DEPTH * 4];
+ unsigned char rdbuf[4];
+ unsigned char custom_cmds[128] = {0};
+
+ if ((a1028_suspended) && (newid == A1028_PATH_SUSPEND))
+ return rc;
+
+ rc = chk_wakeup_a1028();
+ if (rc < 0)
+ return rc;
+
+ sw_reset = ((A100_msg_Reset << 16) | RESET_IMMEDIATE);
+
+ switch (newid) {
+ case A1028_PATH_INCALL_RECEIVER:
+ if (control_a1028_micsel)
+ a1028_gpio_set_value(pdata->gpio_a1028_micsel, 0);
+ i2c_cmds = phonecall_receiver;
+ size = sizeof(phonecall_receiver);
+ break;
+ case A1028_PATH_INCALL_HEADSET:
+ if (control_a1028_micsel)
+ a1028_gpio_set_value(pdata->gpio_a1028_micsel, 1);
+ i2c_cmds = phonecall_headset;
+ size = sizeof(phonecall_headset);
+ break;
+ case A1028_PATH_INCALL_SPEAKER:
+ if (control_a1028_micsel)
+ a1028_gpio_set_value(pdata->gpio_a1028_micsel, 0);
+ i2c_cmds = phonecall_speaker;
+ size = sizeof(phonecall_speaker);
+ break;
+ case A1028_PATH_INCALL_BT:
+ if (control_a1028_micsel)
+ a1028_gpio_set_value(pdata->gpio_a1028_micsel, 0);
+ i2c_cmds = phonecall_bt;
+ size = sizeof(phonecall_bt);
+ break;
+ case A1028_PATH_VR_NO_NS_RECEIVER:
+ if (control_a1028_micsel)
+ a1028_gpio_set_value(pdata->gpio_a1028_micsel, 0);
+ i2c_cmds = vr_no_ns_receiver;
+ size = sizeof(vr_no_ns_receiver);
+ break;
+ case A1028_PATH_VR_NO_NS_HEADSET:
+ if (control_a1028_micsel)
+ a1028_gpio_set_value(pdata->gpio_a1028_micsel, 1);
+ i2c_cmds = vr_no_ns_headset;
+ size = sizeof(vr_no_ns_headset);
+ break;
+ case A1028_PATH_VR_NO_NS_SPEAKER:
+ if (control_a1028_micsel)
+ a1028_gpio_set_value(pdata->gpio_a1028_micsel, 0);
+ i2c_cmds = vr_no_ns_speaker;
+ size = sizeof(vr_no_ns_speaker);
+ break;
+ case A1028_PATH_VR_NO_NS_BT:
+ if (control_a1028_micsel)
+ a1028_gpio_set_value(pdata->gpio_a1028_micsel, 0);
+ i2c_cmds = vr_no_ns_bt;
+ size = sizeof(vr_no_ns_bt);
+ break;
+ case A1028_PATH_VR_NS_RECEIVER:
+ if (control_a1028_micsel)
+ a1028_gpio_set_value(pdata->gpio_a1028_micsel, 0);
+ i2c_cmds = vr_ns_receiver;
+ size = sizeof(vr_ns_receiver);
+ break;
+ case A1028_PATH_VR_NS_HEADSET:
+ if (control_a1028_micsel)
+ a1028_gpio_set_value(pdata->gpio_a1028_micsel, 1);
+ i2c_cmds = vr_ns_headset;
+ size = sizeof(vr_ns_headset);
+ break;
+ case A1028_PATH_VR_NS_SPEAKER:
+ if (control_a1028_micsel)
+ a1028_gpio_set_value(pdata->gpio_a1028_micsel, 0);
+ i2c_cmds = vr_ns_speaker;
+ size = sizeof(vr_ns_speaker);
+ break;
+ case A1028_PATH_VR_NS_BT:
+ if (control_a1028_micsel)
+ a1028_gpio_set_value(pdata->gpio_a1028_micsel, 0);
+ i2c_cmds = vr_ns_bt;
+ size = sizeof(vr_ns_bt);
+ break;
+ case A1028_PATH_RECORD_RECEIVER:
+ if (control_a1028_micsel)
+ a1028_gpio_set_value(pdata->gpio_a1028_micsel, 0);
+ i2c_cmds = INT_MIC_recording_receiver;
+ size = sizeof(INT_MIC_recording_receiver);
+ break;
+ case A1028_PATH_RECORD_HEADSET:
+ if (control_a1028_micsel)
+ a1028_gpio_set_value(pdata->gpio_a1028_micsel, 1);
+ i2c_cmds = EXT_MIC_recording;
+ size = sizeof(EXT_MIC_recording);
+ break;
+ case A1028_PATH_RECORD_SPEAKER:
+ if (control_a1028_micsel)
+ a1028_gpio_set_value(pdata->gpio_a1028_micsel, 0);
+ i2c_cmds = INT_MIC_recording_speaker;
+ size = sizeof(INT_MIC_recording_speaker);
+ break;
+ case A1028_PATH_RECORD_BT:
+ if (control_a1028_micsel)
+ a1028_gpio_set_value(pdata->gpio_a1028_micsel, 0);
+ i2c_cmds = phonecall_bt;
+ size = sizeof(phonecall_bt);
+ break;
+ case A1028_PATH_SUSPEND:
+ if (control_a1028_micsel)
+ a1028_gpio_set_value(pdata->gpio_a1028_micsel, 0);
+ i2c_cmds = (unsigned char *)suspend_mode;
+ size = sizeof(suspend_mode);
+ break;
+ case A1028_PATH_CAMCORDER:
+ if (control_a1028_micsel)
+ a1028_gpio_set_value(pdata->gpio_a1028_micsel, 0);
+ i2c_cmds = BACK_MIC_recording;
+ size = sizeof(BACK_MIC_recording);
+ break;
+ case A1028_PATH_MFG_LOOPBACK:
+ a1028_pmic_set_value(pdata->gpio_a1028_micswitch, 0);
+ i2c_cmds = mfg_loopback;
+ size = sizeof(mfg_loopback);
+ break;
+ default:
+ pr_err("%s: invalid cmd %d\n", __func__, newid);
+ rc = -1;
+ goto input_err;
+ break;
+ }
+
+ a1028_current_config = newid;
+
+ if (a1028_cmds_len > 0) {
+ int cmd_size = 0;
+ cmd_size = build_cmds(custom_cmds, newid);
+ if (cmd_size > 0)
+ i2c_cmds = custom_cmds;
+ size = cmd_size;
+ pr_info("use customize command\n");
+ }
+
+ pr_info("%s: change to mode %d\n", __func__, newid);
+
+ pr_info("%s: block write start (size = %d)\n", __func__, size);
+
+#if DEBUG
+ {
+ int i = 0;
+ for (i = 1; i <= size; i++) {
+ pr_info("%x ", *(i2c_cmds + i - 1));
+ if (!(i % 4))
+ pr_info("\n");
+ }
+ }
+#endif
+
+ rc = a1028_i2c_write(i2c_cmds, size);
+ if (rc < 0) {
+ pr_err("A1028 CMD block write error!\n");
+ a1028_i2c_sw_reset(sw_reset);
+ return rc;
+ }
+ pr_info("%s: block write end\n", __func__);
+
+
+ if (*i2c_cmds == 0x80 && *(i2c_cmds + 1) == 0x10
+ && *(i2c_cmds + 2) == 0x00 && *(i2c_cmds + 3) == 0x01) {
+ a1028_suspended = 1;
+
+ msleep(120);
+ if (control_a1028_clk)
+ a1028_gpio_set_value(pdata->gpio_a1028_clk, 0);
+ if (control_a1028_xo_clk)
+ a1028_xo_clk_enable(0);
+ return rc;
+ }
+
+ memset(ack_buf, 0, sizeof(ack_buf));
+ msleep(20);
+ pr_info("%s: CMD ACK block read start\n", __func__);
+ rc = a1028_i2c_read(ack_buf, size);
+ if (rc < 0) {
+ pr_err("%s: CMD ACK block read error\n", __func__);
+ a1028_i2c_sw_reset(sw_reset);
+ return rc;
+ } else {
+ pr_info("%s: CMD ACK block read end\n", __func__);
+#if DEBUG
+ {
+ int i = 0;
+ for (i = 1; i <= size; i++) {
+ pr_info("%x ", ack_buf[i-1]);
+ if (!(i % 4))
+ pr_info("\n");
+ }
+ }
+#endif
+ index = ack_buf;
+ number_of_cmd_sets = size / 4;
+ do {
+ if (*index == 0x00) {
+ rd_retry_cnt = POLLING_RETRY_CNT;
+rd_retry:
+ if (rd_retry_cnt--) {
+ memset(rdbuf, 0, sizeof(rdbuf));
+ rc = a1028_i2c_read(rdbuf, 4);
+ if (rc < 0)
+ return rc;
+#if DEBUG
+ {
+ int i = 0;
+ for (i = 0; i < sizeof(rdbuf); i++)
+ pr_info("0x%x\n", rdbuf[i]);
+ pr_info("-----------------\n");
+ }
+#endif
+ if (rdbuf[0] == 0x00) {
+ msleep(20);
+ goto rd_retry;
+ }
+ } else {
+ pr_err("%s: CMD ACK Not Ready\n",
+ __func__);
+ return -EBUSY;
+ }
+ } else if (*index == 0xff) {
+ return -ENOEXEC;
+ } else if (*index == 0x80) {
+ index += 4;
+ }
+ } while (--number_of_cmd_sets);
+ }
+input_err:
+ return rc;
+}
+
+int execute_cmdmsg(unsigned int msg)
+{
+ int rc = 0;
+ int retries, pass = 0;
+ unsigned char msgbuf[4];
+ unsigned char chkbuf[4];
+ unsigned int sw_reset = 0;
+
+ sw_reset = ((A100_msg_Reset << 16) | RESET_IMMEDIATE);
+
+ msgbuf[0] = (msg >> 24) & 0xFF;
+ msgbuf[1] = (msg >> 16) & 0xFF;
+ msgbuf[2] = (msg >> 8) & 0xFF;
+ msgbuf[3] = msg & 0xFF;
+
+ memcpy(chkbuf, msgbuf, 4);
+
+ rc = a1028_i2c_write(msgbuf, 4);
+ if (rc < 0) {
+ pr_err("%s: error %d\n", __func__, rc);
+ a1028_i2c_sw_reset(sw_reset);
+ return rc;
+ }
+
+
+ if (msg == A100_msg_Sleep)
+ return rc;
+
+ retries = POLLING_RETRY_CNT;
+ while (retries--) {
+ rc = 0;
+
+ msleep(20);
+ memset(msgbuf, 0, sizeof(msgbuf));
+ rc = a1028_i2c_read(msgbuf, 4);
+ if (rc < 0) {
+ pr_err("%s: ack-read error %d (%d retries)\n",\
+ __func__, rc, retries);
+ continue;
+ }
+
+ if (msgbuf[0] == 0x80 && msgbuf[1] == chkbuf[1]) {
+ pass = 1;
+ break;
+ } else if (msgbuf[0] == 0xff && msgbuf[1] == 0xff) {
+ pr_err("%s: illegal cmd %08x\n", __func__, msg);
+ rc = -EINVAL;
+ break;
+ } else if (msgbuf[0] == 0x00 && msgbuf[1] == 0x00) {
+ pr_info("%s: not ready (%d retries)\n", __func__,
+ retries);
+ rc = -EBUSY;
+ } else {
+ pr_info("%s: cmd/ack mismatch: (%d retries left)\n",
+ __func__,
+ retries);
+#if DEBUG
+ pr_info("%s: msgbuf[0] = %x\n", __func__, msgbuf[0]);
+ pr_info("%s: msgbuf[1] = %x\n", __func__, msgbuf[1]);
+ pr_info("%s: msgbuf[2] = %x\n", __func__, msgbuf[2]);
+ pr_info("%s: msgbuf[3] = %x\n", __func__, msgbuf[3]);
+#endif
+ rc = -EBUSY;
+ }
+ }
+
+ if (!pass) {
+ pr_err("%s: failed execute cmd %08x (%d)\n", __func__,
+ msg, rc);
+ a1028_i2c_sw_reset(sw_reset);
+ }
+ return rc;
+}
+
+#if ENABLE_DIAG_IOCTLS
+static int a1028_set_mic_state(char miccase)
+{
+ int rc = 0;
+ unsigned int cmd_msg = 0;
+
+ switch (miccase) {
+ case 1:
+ cmd_msg = 0x80260007;
+ break;
+ case 2:
+ cmd_msg = 0x80260015;
+ break;
+ case 3:
+ cmd_msg = 0x80260001;
+ break;
+ case 4:
+ cmd_msg = 0x80260006;
+ break;
+ default:
+ pr_info("%s: invalid input %d\n", __func__, miccase);
+ rc = -EINVAL;
+ break;
+ }
+ rc = execute_cmdmsg(cmd_msg);
+ return rc;
+}
+
+static int exe_cmd_in_file(unsigned char *incmd)
+{
+ int rc = 0;
+ int i = 0;
+ unsigned int cmd_msg = 0;
+ unsigned char tmp = 0;
+
+ for (i = 0; i < 4; i++) {
+ tmp = *(incmd + i);
+ cmd_msg |= (unsigned int)tmp;
+ if (i != 3)
+ cmd_msg = cmd_msg << 8;
+ }
+ rc = execute_cmdmsg(cmd_msg);
+ if (rc < 0)
+ pr_err("%s: cmd %08x error %d\n", __func__, cmd_msg, rc);
+ return rc;
+}
+#endif
+
+static long
+a1028_ioctl(struct file *file, unsigned int cmd,
+ unsigned long arg)
+{
+ void __user *argp = (void __user *)arg;
+ struct a1028img img;
+ struct A1028_config_data cfg;
+ int rc = 0;
+#if ENABLE_DIAG_IOCTLS
+ char msg[4];
+ int mic_cases = 0;
+ int mic_sel = 0;
+#endif
+ int pathid = 0;
+ int ns_state;
+
+ switch (cmd) {
+ case A1028_BOOTUP_INIT:
+ img.buf = 0;
+ img.img_size = 0;
+ if (copy_from_user(&img, argp, sizeof(img)))
+ return -EFAULT;
+ rc = a1028_bootup_init(file, &img);
+ break;
+ case A1028_SET_CONFIG:
+ if (copy_from_user(&pathid, argp, sizeof(pathid)))
+ return -EFAULT;
+ if (pathid < 0 || pathid >= A1028_PATH_MAX)
+ return -EINVAL;
+ rc = a1028_set_config(pathid, A1028_CONFIG_FULL);
+ if (rc < 0)
+ pr_err("%s: A1028_SET_CONFIG (%d) error %d!\n",
+ __func__, pathid, rc);
+ break;
+ case A1028_SET_NS_STATE:
+ if (copy_from_user(&ns_state, argp, sizeof(ns_state)))
+ return -EFAULT;
+ pr_info("%s: set noise suppression %d\n",\
+ __func__, ns_state);
+ if (ns_state < 0 || ns_state >= A1028_NS_NUM_STATES)
+ return -EINVAL;
+ a1028_NS_state = ns_state;
+ if (!a1028_suspended)
+ a1028_set_config(a1028_current_config,
+ A1028_CONFIG_VP);
+ break;
+ case A1028_SET_PARAM:
+ a1028_cmds_len = 0;
+ cfg.cmd_data = 0;
+ if (copy_from_user(&cfg, argp, sizeof(cfg))) {
+ pr_err("%s: copy from user failed.\n", __func__);
+ return -EFAULT;
+ }
+
+ if (cfg.data_len <= 0 || cfg.data_len > PARAM_MAX) {
+ pr_err("%s: invalid data length %d\n", \
+ __func__, cfg.data_len);
+ return -EINVAL;
+ }
+
+ if (cfg.cmd_data == NULL) {
+ pr_err("%s: invalid data\n", __func__);
+ return -EINVAL;
+ }
+
+ if (config_data == NULL)
+ config_data = kmalloc(cfg.data_len, GFP_KERNEL);
+ if (!config_data) {
+ pr_err("%s: out of memory\n", __func__);
+ return -ENOMEM;
+ }
+ if (copy_from_user(config_data, cfg.cmd_data, cfg.data_len)) {
+ pr_err("%s: copy data from user failed.\n",\
+ __func__);
+ kfree(config_data);
+ config_data = NULL;
+ return -EFAULT;
+ }
+ a1028_cmds_len = cfg.data_len;
+ pr_info("%s: update a1028 i2c commands success.\n",\
+ __func__);
+ rc = 0;
+ break;
+#if ENABLE_DIAG_IOCTLS
+ case A1028_SET_MIC_ONOFF:
+ rc = chk_wakeup_a1028();
+ if (rc < 0)
+ return rc;
+ if (copy_from_user(&mic_cases, argp, sizeof(mic_cases)))
+ return -EFAULT;
+ rc = a1028_set_mic_state(mic_cases);
+ if (rc < 0)
+ pr_err("%s: A1028_SET_MIC_ONOFF %d error %d!\n",
+ __func__, mic_cases, rc);
+ break;
+ case A1028_SET_MICSEL_ONOFF:
+ rc = chk_wakeup_a1028();
+ if (rc < 0)
+ return rc;
+ if (copy_from_user(&mic_sel, argp, sizeof(mic_sel)))
+ return -EFAULT;
+ if (control_a1028_micsel)
+ a1028_gpio_set_value(pdata->gpio_a1028_micsel, \
+ !!mic_sel);
+ rc = 0;
+ break;
+ case A1028_READ_DATA:
+ rc = chk_wakeup_a1028();
+ if (rc < 0)
+ return rc;
+ rc = a1028_i2c_read(msg, 4);
+ if (copy_to_user(argp, &msg, 4))
+ return -EFAULT;
+ break;
+ case A1028_WRITE_MSG:
+ rc = chk_wakeup_a1028();
+ if (rc < 0)
+ return rc;
+ if (copy_from_user(msg, argp, sizeof(msg)))
+ return -EFAULT;
+ rc = a1028_i2c_write(msg, 4);
+ break;
+ case A1028_SYNC_CMD:
+ rc = chk_wakeup_a1028();
+ if (rc < 0)
+ return rc;
+ msg[0] = 0x80;
+ msg[1] = 0x00;
+ msg[2] = 0x00;
+ msg[3] = 0x00;
+ rc = a1028_i2c_write(msg, 4);
+ break;
+ case A1028_SET_CMD_FILE:
+ rc = chk_wakeup_a1028();
+ if (rc < 0)
+ return rc;
+ if (copy_from_user(msg, argp, sizeof(msg)))
+ return -EFAULT;
+ rc = exe_cmd_in_file(msg);
+ break;
+#endif
+ default:
+ pr_err("%s: invalid command %d\n", __func__, _IOC_NR(cmd));
+ rc = -EINVAL;
+ break;
+ }
+
+ return rc;
+}
+
+static const struct file_operations a1028_fops = {
+ .owner = THIS_MODULE,
+ .open = a1028_open,
+ .release = a1028_release,
+ .unlocked_ioctl = a1028_ioctl,
+};
+
+static struct miscdevice a1028_device = {
+ .minor = MISC_DYNAMIC_MINOR,
+ .name = "audience_a1028",
+ .fops = &a1028_fops,
+};
+
+static int a1028_probe(
+ struct i2c_client *client, const struct i2c_device_id *id)
+{
+ int rc = 0;
+ control_a1028_xo_clk = 0;
+ pdata = client->dev.platform_data;
+
+ if (pdata == NULL) {
+ pdata = kzalloc(sizeof(*pdata), GFP_KERNEL);
+ if (pdata == NULL) {
+ rc = -ENOMEM;
+ pr_err("%s: platform data is NULL\n", __func__);
+ goto err_alloc_data_failed;
+ }
+ }
+
+ this_client = client;
+#ifdef CONFIG_ARCH_MSM8960
+ a1028_clock = msm_xo_get(MSM_XO_TCXO_D1, cid);
+ if (IS_ERR(a1028_clock)) {
+ control_a1028_xo_clk = 0;
+ msm_xo_put(a1028_clock);
+ } else {
+ control_a1028_xo_clk = 1;
+ pr_info("use tcxo_d1 clk");
+ }
+ control_a1028_clk = 0;
+ control_a1028_micsel = 0;
+#else
+ rc = gpio_request(pdata->gpio_a1028_clk, "a1028");
+ if (rc < 0) {
+ control_a1028_clk = 0;
+ goto chk_gpio_micsel;
+ }
+ control_a1028_clk = 1;
+
+ rc = gpio_direction_output(pdata->gpio_a1028_clk, 1);
+ if (rc < 0) {
+ pr_err("%s: request clk gpio direction failed\n", __func__);
+ goto err_free_gpio_clk;
+ }
+
+chk_gpio_micsel:
+ rc = gpio_request(pdata->gpio_a1028_micsel, "a1028");
+ if (rc < 0) {
+ pr_info("%s: gpio request mic_sel pin failed\n", __func__);
+ control_a1028_micsel = 0;
+ } else {
+ rc = gpio_direction_output(pdata->gpio_a1028_micsel, 1);
+ if (rc < 0) {
+ pr_err("%s: request mic_sel gpio direction \
+ failed\n", __func__);
+ goto err_free_gpio_micsel;
+ }
+ }
+#endif
+
+ rc = gpio_request(pdata->gpio_a1028_wakeup, "a1028");
+ if (rc < 0) {
+ pr_err("%s: gpio request wakeup pin failed\n", __func__);
+ goto err_free_gpio;
+ }
+
+ rc = gpio_direction_output(pdata->gpio_a1028_wakeup, 1);
+ if (rc < 0) {
+ pr_err("%s: request wakeup gpio direction failed\n",\
+ __func__);
+ goto err_free_gpio;
+ }
+
+ rc = gpio_request(pdata->gpio_a1028_reset, "a1028");
+ if (rc < 0) {
+ pr_err("%s: gpio request reset pin failed\n", __func__);
+ goto err_free_gpio;
+ }
+
+ rc = gpio_direction_output(pdata->gpio_a1028_reset, 1);
+ if (rc < 0) {
+ pr_err("%s: request reset gpio direction failed\n",\
+ __func__);
+ goto err_free_gpio_all;
+ }
+
+ if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
+ pr_err("%s: i2c check functionality error\n", __func__);
+ rc = -ENODEV;
+ goto err_free_gpio_all;
+ }
+
+ if (control_a1028_clk)
+ a1028_gpio_set_value(pdata->gpio_a1028_clk, 1);
+ if (control_a1028_micsel)
+ a1028_gpio_set_value(pdata->gpio_a1028_micsel, 0);
+ a1028_gpio_set_value(pdata->gpio_a1028_wakeup, 1);
+ a1028_gpio_set_value(pdata->gpio_a1028_reset, 1);
+
+ rc = misc_register(&a1028_device);
+ if (rc) {
+ pr_err("%s: a1028_device register failed\n", __func__);
+ goto err_free_gpio_all;
+ }
+ pr_info("%s ok", __func__);
+ return 0;
+
+err_free_gpio_all:
+ gpio_free(pdata->gpio_a1028_reset);
+err_free_gpio:
+ gpio_free(pdata->gpio_a1028_wakeup);
+#ifndef CONFIG_ARCH_MSM8960
+err_free_gpio_micsel:
+ gpio_free(pdata->gpio_a1028_micsel);
+err_free_gpio_clk:
+ gpio_free(pdata->gpio_a1028_clk);
+#endif
+err_alloc_data_failed:
+ return rc;
+}
+
+static int a1028_remove(struct i2c_client *client)
+{
+ struct a1028_platform_data *p1028data = i2c_get_clientdata(client);
+ kfree(p1028data);
+
+ return 0;
+}
+
+static int a1028_suspend(struct i2c_client *client, pm_message_t mesg)
+{
+ return 0;
+}
+
+static int a1028_resume(struct i2c_client *client)
+{
+ return 0;
+}
+
+static const struct i2c_device_id a1028_id[] = {
+ { "audience_a1028", 0 },
+ { }
+};
+
+static struct i2c_driver a1028_driver = {
+ .probe = a1028_probe,
+ .remove = a1028_remove,
+ .suspend = a1028_suspend,
+ .resume = a1028_resume,
+ .id_table = a1028_id,
+ .driver = {
+ .name = "audience_a1028",
+ },
+};
+
+static int __init a1028_init(void)
+{
+ pr_info("%s\n", __func__);
+ mutex_init(&a1028_lock);
+
+ return i2c_add_driver(&a1028_driver);
+}
+
+static void __exit a1028_exit(void)
+{
+ i2c_del_driver(&a1028_driver);
+}
+
+module_init(a1028_init);
+module_exit(a1028_exit);
+
+MODULE_DESCRIPTION("A1028 voice processor driver");
+MODULE_LICENSE("GPL");
diff --git a/include/linux/a1028.h b/include/linux/a1028.h
new file mode 100644
index 0000000..7c6e295
--- /dev/null
+++ b/include/linux/a1028.h
@@ -0,0 +1,218 @@
+/* include/linux/a1028.h - a1028 voice processor driver
+ *
+ * Copyright (C) 2009 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.
+ *
+ */
+
+#ifndef __LINUX_A1028_H
+#define __LINUX_A1028_H
+
+#include <linux/ioctl.h>
+
+#define A1028_I2C_NAME "audience_a1028"
+#define A1028_MAX_FW_SIZE (32*1024)
+struct a1028img {
+ unsigned char *buf;
+ unsigned img_size;
+};
+
+enum A1028_PathID {
+ A1028_PATH_SUSPEND,
+ A1028_PATH_INCALL_RECEIVER,
+ A1028_PATH_INCALL_HEADSET,
+ A1028_PATH_INCALL_SPEAKER,
+ A1028_PATH_INCALL_BT,
+ A1028_PATH_VR_NO_NS_RECEIVER,
+ A1028_PATH_VR_NO_NS_HEADSET,
+ A1028_PATH_VR_NO_NS_SPEAKER,
+ A1028_PATH_VR_NO_NS_BT,
+ A1028_PATH_VR_NS_RECEIVER,
+ A1028_PATH_VR_NS_HEADSET,
+ A1028_PATH_VR_NS_SPEAKER,
+ A1028_PATH_VR_NS_BT,
+ A1028_PATH_RECORD_RECEIVER,
+ A1028_PATH_RECORD_HEADSET,
+ A1028_PATH_RECORD_SPEAKER,
+ A1028_PATH_RECORD_BT,
+ A1028_PATH_CAMCORDER,
+ A1028_PATH_INCALL_TTY,
+ A1028_PATH_MFG_LOOPBACK,
+ A1028_PATH_MAX
+};
+
+struct A1028_config_data {
+ unsigned int data_len;
+ unsigned int mode_num;
+ unsigned char *cmd_data;
+};
+
+enum A1028_NS_states {
+ A1028_NS_STATE_AUTO,
+ A1028_NS_STATE_OFF,
+ A1028_NS_STATE_CT,
+ A1028_NS_STATE_FT,
+ A1028_NS_NUM_STATES
+};
+
+#define A1028_IOCTL_MAGIC 'u'
+
+#define A1028_BOOTUP_INIT _IOW(A1028_IOCTL_MAGIC, 0x01, struct a1028img *)
+#define A1028_SET_CONFIG _IOW(A1028_IOCTL_MAGIC, 0x02, enum A1028_PathID)
+#define A1028_SET_NS_STATE _IOW(A1028_IOCTL_MAGIC, 0x03, enum A1028_NS_states)
+#define A1028_SET_PARAM _IOW(A1028_IOCTL_MAGIC, 0x04, unsigned)
+#define A1028_SET_MIC_ONOFF _IOW(A1028_IOCTL_MAGIC, 0x50, unsigned)
+#define A1028_SET_MICSEL_ONOFF _IOW(A1028_IOCTL_MAGIC, 0x51, unsigned)
+#define A1028_READ_DATA _IOR(A1028_IOCTL_MAGIC, 0x52, unsigned)
+#define A1028_WRITE_MSG _IOW(A1028_IOCTL_MAGIC, 0x53, unsigned)
+#define A1028_SYNC_CMD _IO(A1028_IOCTL_MAGIC, 0x54)
+#define A1028_SET_CMD_FILE _IOW(A1028_IOCTL_MAGIC, 0x55, unsigned)
+
+#ifdef __KERNEL__
+
+#define CtrlMode_LAL 0x0001
+#define CtrlMode_LAH 0x0002
+#define CtrlMode_FE 0x0003
+#define CtrlMode_RE 0x0004
+#define A100_msg_Sync 0x80000000
+#define A100_msg_Sync_Ack 0x80000000
+
+#define A100_msg_Reset 0x8002
+#define RESET_IMMEDIATE 0x0000
+#define RESET_DELAYED 0x0001
+
+#define A100_msg_BootloadInitiate 0x8003
+#define A100_msg_GetDeviceParm 0x800B
+#define A100_msg_SetDeviceParmID 0x800C
+#define A100_msg_SetDeviceParm 0x800D
+
+#define PCM0WordLength 0x0100
+#define PCM0DelFromFsTx 0x0101
+#define PCM0DelFromFsRx 0x0102
+#define PCM0LatchEdge 0x0103
+#define PCM0Endianness 0x0105
+#define PCM0TristateEnable 0x0107
+
+#define PCM1WordLength 0x0200
+#define PCM1DelFromFsTx 0x0201
+#define PCM1DelFromFsRx 0x0202
+#define PCM1LatchEdge 0x0203
+#define PCM1Endianness 0x0205
+#define PCM1TristateEnable 0x0207
+
+#define PCMWordLength_16bit 0x10
+#define PCMWordLength_24bit 0x18
+#define PCMWordLength_32bit 0x20
+#define PCMLatchEdge_Tx_F_Rx_R 0x00
+#define PCMLatchEdge_Tx_R_Rx_F 0x03
+#define PCMEndianness_Little 0x00
+#define PCMEndianness_Big 0x01
+#define PCMTristate_Disable 0x00
+#define PCMTristate_Enable 0x01
+
+#define ADC0Gain 0x0300
+#define ADC0Rate 0x0301
+#define ADC0CutoffFreq 0x0302
+
+#define ADC1Gain 0x0400
+#define ADC1Rate 0x0401
+#define ADC1CutoffFreq 0x0402
+
+#define ADC_Gain_0db 0x00
+#define ADC_Gain_6db 0x01
+#define ADC_Gain_12db 0x02
+#define ADC_Gain_18db 0x03
+#define ADC_Gain_24db 0x04
+#define ADC_Gain_30db 0x05
+#define ADC_Rate_8kHz 0x00
+#define ADC_Rate_16kHz 0x01
+#define ADC_CutoffFreq_NO_DC_Filter 0x00
+#define ADC_CutoffFreq_59p68Hz 0x01
+#define ADC_CutoffFreq_7p46Hz 0x02
+#define ADC_CutoffFreq_3p73Hz 0x03
+
+#define A100_msg_Sleep 0x80100001
+
+#define A100_msg_GetAlgorithmParm 0x8016
+#define A100_msg_SetAlgorithmParmID 0x8017
+#define A100_msg_SetAlgorithmParm 0x8018
+
+#define AIS_Global_Supression_Level 0x0000
+#define Mic_Config 0x0002
+#define AEC_Mode 0x0003
+#define AEC_CNG 0x0023
+#define Output_AGC 0x0004
+#define Output_AGC_Target_Level 0x0005
+#define Output_AGC_Noise_Floor 0x0006
+#define Output_AGC_SNR_Improvement 0x0007
+#define Comfort_Noise 0x001A
+#define Comfort_Noise_Level 0x001B
+
+#define Speaker_Volume 0x0012
+#define VEQ_Mode 0x0009
+#define VEQ_Max_FarEnd_Limiter_Level 0x000D
+#define VEQ_Noise_Estimation_Adj 0x0025
+#define Receive_NS 0x000E
+#define Receive_NS_Level 0x000F
+#define SideTone 0x0015
+#define SideTone_Gain 0x0016
+
+#define A100_msg_GetTxDigitalInputGain 0x801A
+#define A100_msg_SetTxDigitalInputGain 0x801B
+
+#define A100_msg_GetRcvDigitalInputGain 0x8022
+#define A100_msg_SetRcvDigitalInputGain 0x8023
+
+#define A100_msg_GetTxDigitalOutputGain 0x801D
+#define A100_msg_SetTxDigitalOutputGain 0x8015
+
+#define A100_msg_Bypass 0x801C
+#define A1028_msg_VP_ON 0x801C0001
+#define A1028_msg_VP_OFF 0x801C0000
+
+#define A100_msg_GetMicRMS 0x8013
+#define A100_msg_GetMicPeak 0x8014
+#define DiagPath_Pri_Input_Mic 0x0000
+#define DiagPath_Sec_Input_Mic 0x0001
+#define DiagPath_Output_Mic 0x0002
+#define DiagPath_Far_End_Input 0x0003
+#define DiagPath_Far_End_Output 0x0004
+#define A100_msg_SwapInputCh 0x8019
+#define A100_msg_OutputKnownSig 0x801E
+
+#define A1028_msg_BOOT 0x0001
+#define A1028_msg_BOOT_ACK 0x01
+
+#define TIMEOUT 20
+#define RETRY_CNT 5
+#define POLLING_RETRY_CNT 3
+#define A1028_ERROR_CODE 0xffff
+#define A1028_SLEEP 0
+#define A1028_ACTIVE 1
+#define A1028_CMD_FIFO_DEPTH 64
+
+enum A1028_config_mode {
+ A1028_CONFIG_FULL,
+ A1028_CONFIG_VP
+};
+
+struct a1028_platform_data {
+ uint32_t gpio_a1028_micsel;
+ uint32_t gpio_a1028_wakeup;
+ uint32_t gpio_a1028_reset;
+ uint32_t gpio_a1028_int;
+ uint32_t gpio_a1028_clk;
+ uint32_t gpio_a1028_micswitch;
+};
+
+
+#endif
+#endif