| /* drivers/i2c/chips/a2220.c - a2220 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/kernel.h> |
| #include <linux/init.h> |
| #include <linux/err.h> |
| #include <linux/module.h> |
| #include <linux/interrupt.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/a2220.h> |
| #include <linux/a2220_fw.h> |
| #include <linux/kthread.h> |
| #include <linux/clk.h> |
| |
| #include <mach/iomap.h> |
| #include <linux/io.h> |
| |
| |
| #define PMC_CLK_OUT 0x1a8 |
| #define CLK3_SRC_SEL (0x3 << 22) |
| #define CLK3_FORCE_EN (0x1 << 18) |
| |
| #define MODULE_NAME "audience_a2220" |
| #define DEBUG (0) |
| #define ENABLE_DIAG_IOCTLS (0) |
| #define WAKEUP_GPIO_NUM_HERCULES_REV01 33 |
| #define WAKEUP_GPIO_NUM_CELOX_ATT_REV05 33 |
| |
| /* MAGIC NUMBERS! Fixme */ |
| #define VP_RESET 118 |
| #define AUDIO_LD0_EN 60 |
| #define AMP_SHUTDOWN_N 139 |
| |
| static struct i2c_client *this_client; |
| static struct a2220_platform_data *pdata; |
| static struct task_struct *task; |
| static int execute_cmdmsg(unsigned int); |
| |
| static struct mutex a2220_lock; |
| static int a2220_opened; |
| static int a2220_suspended; |
| static int control_a2220_clk = 0; |
| struct clk *extern3_clk; |
| static unsigned int a2220_NS_state = A2220_NS_STATE_AUTO; |
| static int a2220_current_config = A2220_PATH_SUSPEND; |
| static int a2220_param_ID; |
| |
| struct vp_ctxt { |
| unsigned char *data; |
| unsigned int img_size; |
| }; |
| |
| struct vp_ctxt the_vp; |
| |
| unsigned int get_hw_rev(void) |
| { |
| return 0x05; |
| } |
| |
| static int a2220_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) { |
| printk(KERN_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 a2220_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) { |
| printk(KERN_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 a2220_open(struct inode *inode, struct file *file) |
| { |
| int rc = 0; |
| struct vp_ctxt *vp = &the_vp; |
| |
| mutex_lock(&a2220_lock); |
| |
| if (a2220_opened) { |
| printk(KERN_ERR "%s: busy\n", __func__); |
| rc = -EBUSY; |
| goto done; |
| } |
| |
| file->private_data = vp; |
| vp->img_size = 0; |
| a2220_opened = 1; |
| done: |
| mutex_unlock(&a2220_lock); |
| return rc; |
| } |
| |
| static int a2220_release(struct inode *inode, struct file *file) |
| { |
| mutex_lock(&a2220_lock); |
| a2220_opened = 0; |
| mutex_unlock(&a2220_lock); |
| |
| return 0; |
| } |
| |
| #ifdef AUDIENCE_BYPASS /*(+)dragonball Multimedia mode */ |
| #define A100_msg_mutimedia1 0x801C0000 /*VoiceProcessingOn, 0x0000:off */ |
| #define A100_msg_mutimedia2 0x8026001F /*SelectRouting, 0x001A:(26) */ |
| #define A100_msg_mutimedia3 0x800C0B03 /* ; PCM B Din delay 1bit */ |
| #define A100_msg_mutimedia4 0x800D0001 |
| #define A100_msg_mutimedia5 0x800C0A03 /* ; PCM A Din delay 1bit */ |
| #define A100_msg_mutimedia6 0x800D0001 |
| #endif |
| |
| static void a2220_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 = a2220_i2c_write(msgbuf, 4); |
| if (!rc) |
| msleep(20); |
| } |
| |
| static ssize_t a2220_hw_reset(struct a2220img *img) |
| { |
| struct a2220img *vp = img; |
| int rc, i, pass = 0; |
| int remaining; |
| int retry = RETRY_CNT; |
| unsigned char *index; |
| char buf[2]; |
| |
| while (retry--) { |
| /* Reset A2220 chip */ |
| if (pdata->gpio_a2220_reset) |
| gpio_set_value(pdata->gpio_a2220_reset, 0); |
| else |
| gpio_set_value(VP_RESET, 1); |
| |
| /* Enable A2220 clock */ |
| if (control_a2220_clk) |
| gpio_set_value(pdata->gpio_a2220_clk, 1); |
| mdelay(1); |
| |
| /* Take out of reset */ |
| if (pdata->gpio_a2220_reset) |
| gpio_set_value(pdata->gpio_a2220_reset, 1); |
| else |
| gpio_set_value(VP_RESET, 0); |
| |
| msleep(50); /* Delay before send I2C command */ |
| |
| /* Boot Cmd to A2220 */ |
| buf[0] = A2220_msg_BOOT >> 8; |
| buf[1] = A2220_msg_BOOT & 0xff; |
| |
| rc = a2220_i2c_write(buf, 2); |
| if (rc < 0) { |
| printk(KERN_ERR "%s: set boot mode error (%d retries left)\n", |
| __func__, retry); |
| continue; |
| } |
| |
| mdelay(1); |
| rc = a2220_i2c_read(buf, 1); |
| |
| if (rc < 0) { |
| printk(KERN_ERR "%s: boot mode ack error (%d retries left)\n", |
| __func__, retry); |
| continue; |
| } |
| |
| remaining = vp->img_size / 32; |
| index = vp->buf; |
| |
| for (; remaining; remaining--, index += 32) { |
| rc = a2220_i2c_write(index, 32); |
| if (rc < 0) |
| break; |
| } |
| |
| if (rc >= 0 && vp->img_size % 32) |
| rc = a2220_i2c_write(index, vp->img_size % 32); |
| |
| if (rc < 0) { |
| printk(KERN_ERR "%s: fw load error %d (%d retries left)\n", |
| __func__, rc, retry); |
| continue; |
| } |
| |
| msleep(20); /* Delay time before issue a Sync Cmd */ |
| |
| for (i = 0; i < 10; i++) |
| msleep(20); |
| |
| rc = execute_cmdmsg(A100_msg_Sync); |
| if (rc < 0) { |
| printk(KERN_ERR "%s: sync command error %d (%d retries left)\n", |
| __func__, rc, retry); |
| continue; |
| } |
| |
| pass = 1; |
| break; |
| } |
| return rc; |
| } |
| |
| /* eS305B HPT */ |
| #ifdef CONFIG_USA_MODEL_SGH_I717 |
| static int hpt_longCmd_execute(unsigned char *i2c_cmds, int size) |
| { |
| |
| int i = 0, rc = 0; |
| int retry = 4; |
| /* unsigned int sw_reset = 0; */ |
| unsigned int msg; |
| unsigned char *pMsg; |
| |
| pMsg = (unsigned char *)&msg; |
| |
| for (i = 0; i < size; i += 4) { |
| pMsg[3] = i2c_cmds[i]; |
| pMsg[2] = i2c_cmds[i + 1]; |
| pMsg[1] = i2c_cmds[i + 2]; |
| pMsg[0] = i2c_cmds[i + 3]; |
| |
| do { |
| rc = execute_cmdmsg(msg); |
| } while ((rc < 0) && --retry); |
| |
| } |
| return 0; |
| } |
| #endif |
| |
| static int a2220_set_boot_mode() |
| { |
| int rc; |
| int retry = RETRY_CNT; |
| char buf[2]; |
| |
| mdelay(100); |
| |
| while (retry--) { |
| /* Reset A2220 chip */ |
| gpio_set_value(VP_RESET, 1); |
| |
| /* Enable A2220 clock */ |
| if (control_a2220_clk) |
| gpio_set_value(pdata->gpio_a2220_clk, 1); |
| mdelay(1); |
| |
| /* Take out of reset */ |
| gpio_set_value(VP_RESET, 0); |
| |
| msleep(150); /* Delay before send I2C command */ |
| |
| /* Boot Cmd to A2220 */ |
| buf[0] = A2220_msg_BOOT >> 8; |
| buf[1] = A2220_msg_BOOT & 0xff; |
| rc = a2220_i2c_write(buf, 2); |
| if (rc < 0) { |
| printk(KERN_ERR "%s: write error (%d retries left)\n", |
| __func__, retry); |
| if (retry > 0) |
| continue; |
| else |
| return rc; |
| } |
| |
| mdelay(1); |
| rc = a2220_i2c_read(buf, 1); |
| |
| if (rc < 0) { |
| printk(KERN_ERR "%s: ack error (%d retries left)\n", |
| __func__, retry); |
| continue; |
| } |
| } |
| |
| return rc; |
| } |
| |
| static ssize_t a2220_bootup_init(struct a2220img *pImg) |
| { |
| struct a2220img *vp = pImg; |
| int rc, pass = 0; |
| int remaining; |
| int retry = RETRY_CNT; |
| unsigned char *index; |
| char buf[2]; |
| |
| mdelay(10); |
| |
| while (retry--) { |
| remaining = vp->img_size / 32; |
| index = vp->buf; |
| pr_info("%s: starting to load image (%d passes)...\n", |
| __func__, remaining + !!(vp->img_size % 32)); |
| |
| for (; remaining; remaining--, index += 32) { |
| rc = a2220_i2c_write(index, 32); |
| if (rc < 0) |
| break; |
| } |
| |
| if (rc >= 0 && vp->img_size % 32) |
| rc = a2220_i2c_write(index, vp->img_size % 32); |
| |
| if (rc < 0) { |
| printk(KERN_ERR "%s: fw load error %d (%d retries left)\n", |
| __func__, rc, retry); |
| continue; |
| } |
| |
| msleep(150); /* Delay time before issue a Sync Cmd */ |
| |
| rc = execute_cmdmsg(A100_msg_Sync); |
| if (rc < 0) { |
| printk(KERN_ERR "%s: sync command error %d (%d retries left)\n", |
| __func__, rc, retry); |
| continue; |
| } |
| |
| pass = 1; |
| break; |
| } |
| |
| rc = execute_cmdmsg(A100_msg_ReadPortA); |
| if (rc < 0) |
| printk(KERN_ERR "%s: suspend error\n", __func__); |
| |
| rc = execute_cmdmsg(A100_msg_PortD_C_PASS); |
| if (rc < 0) |
| printk(KERN_ERR "%s: suspend error\n", __func__); |
| |
| rc = execute_cmdmsg(A100_msg_PortB_A_PASS); |
| if (rc < 0) |
| printk(KERN_ERR "%s: suspend error\n", __func__); |
| |
| a2220_suspended = 0; |
| rc = execute_cmdmsg(A100_msg_Sleep); |
| if (rc < 0) |
| printk(KERN_ERR "%s: suspend error\n", __func__); |
| |
| msleep(30); |
| |
| if (control_a2220_clk) |
| clk_disable(extern3_clk); |
| |
| return rc; |
| } |
| |
| |
| static ssize_t chk_wakeup_a2220(void) |
| { |
| int i, rc = 0, retry = 4; |
| |
| if (a2220_suspended == 1) { |
| /* Enable A2220 clock */ |
| if (control_a2220_clk) { |
| gpio_set_value(pdata->gpio_a2220_clk, 1); |
| mdelay(1); |
| } |
| |
| if (pdata->gpio_a2220_wakeup) { |
| printk(MODULE_NAME |
| "%s : chk_wakeup_a2220 --> get_hw_rev of Target = %d\n", |
| __func__, get_hw_rev()); |
| #ifdef CONFIG_USA_MODEL_SGH_T989 |
| if (get_hw_rev() >= 0x05) |
| gpio_set_value(WAKEUP_GPIO_NUM_HERCULES_REV01, |
| 0); |
| else |
| gpio_set_value(pdata->gpio_a2220_wakeup, 0); |
| #elif CONFIG_USA_MODEL_SGH_I727 |
| qweqwewq if (get_hw_rev() >= 0x05) |
| gpio_set_value(WAKEUP_GPIO_NUM_CELOX_ATT_REV05, |
| 0); |
| else |
| gpio_set_value(pdata->gpio_a2220_wakeup, 0); |
| #elif CONFIG_USA_MODEL_SGH_I717 |
| gpio_set_value(WAKEUP_GPIO_NUM_CELOX_ATT_REV05, 0); |
| #else |
| gpio_set_value(pdata->gpio_a2220_wakeup, 0); |
| #endif |
| } |
| #ifdef CONFIG_USA_MODEL_SGH_I717 |
| for (i = 0; i < 5; i++) |
| msleep(20); |
| #else |
| msleep(30); |
| #endif |
| |
| do { |
| rc = execute_cmdmsg(A100_msg_Sync); |
| } while ((rc < 0) && --retry); |
| |
| /* Audience not responding to execute_cmdmsg , |
| * doing HW reset of the chipset */ |
| if ((retry == 0) && (rc < 0)) { |
| struct a2220img img; |
| img.buf = a2220_firmware_buf; |
| img.img_size = sizeof(a2220_firmware_buf); |
| rc = a2220_hw_reset(&img); /* Call if the Audience |
| chipset is not responding after retrying 12 times */ |
| } |
| if (rc < 0) |
| printk(MODULE_NAME "%s :: Audience HW Reset Failed\n", |
| __func__); |
| |
| #ifdef CONFIG_USA_MODEL_SGH_I717 |
| rc = hpt_longCmd_execute(hpt_init_macro, |
| sizeof(hpt_init_macro)); |
| if (rc < 0) |
| printk(MODULE_NAME "%s: htp init error\n", __func__); |
| #endif |
| |
| if (pdata->gpio_a2220_wakeup) { |
| #ifdef CONFIG_USA_MODEL_SGH_T989 |
| if (get_hw_rev() >= 0x05) |
| gpio_set_value(WAKEUP_GPIO_NUM_HERCULES_REV01, |
| 1); |
| else |
| gpio_set_value(pdata->gpio_a2220_wakeup, 1); |
| #elif CONFIG_USA_MODEL_SGH_I727 |
| if (get_hw_rev() >= 0x05) |
| gpio_set_value(WAKEUP_GPIO_NUM_CELOX_ATT_REV05, |
| 1); |
| else |
| gpio_set_value(pdata->gpio_a2220_wakeup, 1); |
| #elif CONFIG_USA_MODEL_SGH_I717 |
| gpio_set_value(WAKEUP_GPIO_NUM_CELOX_ATT_REV05, 1); |
| #else |
| gpio_set_value(pdata->gpio_a2220_wakeup, 1); |
| #endif |
| } |
| |
| if (rc < 0) { |
| printk(KERN_ERR "%s: failed (%d)\n", __func__, rc); |
| goto wakeup_sync_err; |
| } |
| |
| a2220_suspended = 0; |
| } |
| wakeup_sync_err: |
| return rc; |
| } |
| |
| /* Filter commands according to noise suppression state forced by |
| * A2220_SET_NS_STATE ioctl. |
| * |
| * For this function to operate properly, all configurations must include |
| * both A100_msg_Bypass and Mic_Config commands even if default values |
| * are selected or if Mic_Config is useless because VP is off |
| */ |
| int a2220_filter_vp_cmd(int cmd, int mode) |
| { |
| int msg = (cmd >> 16) & 0xFFFF; |
| int filtered_cmd = cmd; |
| |
| if (a2220_NS_state == A2220_NS_STATE_AUTO) |
| return cmd; |
| |
| switch (msg) { |
| case A100_msg_Bypass: |
| if (a2220_NS_state == A2220_NS_STATE_OFF) |
| filtered_cmd = A2220_msg_VP_OFF; |
| else |
| filtered_cmd = A2220_msg_VP_ON; |
| break; |
| case A100_msg_SetAlgorithmParmID: |
| a2220_param_ID = cmd & 0xFFFF; |
| break; |
| case A100_msg_SetAlgorithmParm: |
| if (a2220_param_ID == Mic_Config) { |
| if (a2220_NS_state == A2220_NS_STATE_CT) |
| filtered_cmd = (msg << 16); |
| else if (a2220_NS_state == A2220_NS_STATE_FT) |
| filtered_cmd = (msg << 16) | 0x0002; |
| } |
| break; |
| default: |
| if (mode == A2220_CONFIG_VP) |
| filtered_cmd = -1; |
| break; |
| } |
| |
| pr_info("%s: %x filtered = %x, a2220_NS_state %d, mode %d\n", __func__, |
| cmd, filtered_cmd, a2220_NS_state, mode); |
| |
| return filtered_cmd; |
| } |
| |
| int a2220_set_config(char newid, int mode) |
| { |
| int i = 0, rc = 0, size = 0; |
| int retry = 4; |
| unsigned int sw_reset = 0; |
| unsigned char *i2c_cmds; |
| unsigned int msg; |
| unsigned char *pMsg; |
| |
| if ((a2220_suspended) && (newid == A2220_PATH_SUSPEND)) |
| return rc; |
| |
| #if defined(CONFIG_USA_MODEL_SGH_T989) || defined(CONFIG_USA_MODEL_SGH_I727)\ |
| || defined(CONFIG_USA_MODEL_SGH_I717) |
| if (a2220_current_config == newid) { |
| printk(KERN_DEBUG "already configured this path!!!\n"); |
| return rc; |
| } |
| #endif |
| |
| rc = chk_wakeup_a2220(); |
| if (rc < 0) |
| return rc; |
| |
| sw_reset = ((A100_msg_Reset << 16) | RESET_IMMEDIATE); |
| |
| switch (newid) { |
| case A2220_PATH_INCALL_RECEIVER_NSON: |
| i2c_cmds = phonecall_receiver_nson; |
| size = sizeof(phonecall_receiver_nson); |
| break; |
| |
| case A2220_PATH_INCALL_RECEIVER_NSOFF: |
| i2c_cmds = phonecall_receiver_nsoff; |
| size = sizeof(phonecall_receiver_nsoff); |
| break; |
| |
| /* (+) ysseo 20110420 : to use a2220 bypass mode */ |
| #ifdef AUDIENCE_BYPASS /*(+)dragonball Multimedia bypass mode */ |
| case A2220_PATH_BYPASS_MULTIMEDIA: |
| printk(KERN_DEBUG "%s : setting A2220_PATH_BYPASS_MULTIMEDIA\n", |
| __func__); |
| i2c_cmds = bypass_multimedia; |
| size = sizeof(bypass_multimedia); |
| break; |
| #endif |
| case A2220_PATH_INCALL_HEADSET: |
| i2c_cmds = phonecall_headset; |
| size = sizeof(phonecall_headset); |
| break; |
| case A2220_PATH_INCALL_SPEAKER: |
| i2c_cmds = phonecall_speaker; |
| size = sizeof(phonecall_speaker); |
| break; |
| case A2220_PATH_INCALL_BT: |
| i2c_cmds = phonecall_bt; |
| size = sizeof(phonecall_bt); |
| break; |
| case A2220_PATH_INCALL_TTY: |
| i2c_cmds = phonecall_tty; |
| size = sizeof(phonecall_tty); |
| break; |
| case A2220_PATH_VR_NO_NS_RECEIVER: |
| i2c_cmds = vr_no_ns_receiver; |
| size = sizeof(vr_no_ns_receiver); |
| break; |
| case A2220_PATH_VR_NO_NS_HEADSET: |
| i2c_cmds = vr_no_ns_headset; |
| size = sizeof(vr_no_ns_headset); |
| break; |
| case A2220_PATH_VR_NO_NS_SPEAKER: |
| i2c_cmds = vr_no_ns_speaker; |
| size = sizeof(vr_no_ns_speaker); |
| break; |
| case A2220_PATH_VR_NO_NS_BT: |
| i2c_cmds = vr_no_ns_bt; |
| size = sizeof(vr_no_ns_bt); |
| break; |
| case A2220_PATH_VR_NS_RECEIVER: |
| i2c_cmds = vr_ns_receiver; |
| size = sizeof(vr_ns_receiver); |
| break; |
| case A2220_PATH_VR_NS_HEADSET: |
| i2c_cmds = vr_ns_headset; |
| size = sizeof(vr_ns_headset); |
| break; |
| case A2220_PATH_VR_NS_SPEAKER: |
| i2c_cmds = vr_ns_speaker; |
| size = sizeof(vr_ns_speaker); |
| break; |
| case A2220_PATH_VR_NS_BT: |
| i2c_cmds = vr_ns_bt; |
| size = sizeof(vr_ns_bt); |
| break; |
| case A2220_PATH_RECORD_RECEIVER: |
| i2c_cmds = INT_MIC_recording_receiver; |
| size = sizeof(INT_MIC_recording_receiver); |
| break; |
| case A2220_PATH_RECORD_HEADSET: |
| i2c_cmds = EXT_MIC_recording; |
| size = sizeof(EXT_MIC_recording); |
| break; |
| case A2220_PATH_RECORD_SPEAKER: |
| i2c_cmds = INT_MIC_recording_speaker; |
| size = sizeof(INT_MIC_recording_speaker); |
| break; |
| case A2220_PATH_RECORD_BT: |
| i2c_cmds = phonecall_bt; |
| size = sizeof(phonecall_bt); |
| break; |
| case A2220_PATH_SUSPEND: |
| i2c_cmds = (unsigned char *)suspend_mode; |
| size = sizeof(suspend_mode); |
| break; |
| case A2220_PATH_CAMCORDER: |
| i2c_cmds = BACK_MIC_recording; |
| size = sizeof(BACK_MIC_recording); |
| break; |
| default: |
| printk(KERN_ERR "%s: invalid cmd %d\n", __func__, newid); |
| rc = -1; |
| goto input_err; |
| break; |
| } |
| |
| a2220_current_config = newid; |
| |
| #if DEBUG |
| pr_info("%s: change to mode %d\n", __func__, newid); |
| pr_info("%s: block write start (size = %d)\n", __func__, size); |
| for (i = 1; i <= size; i++) { |
| pr_info("%x ", *(i2c_cmds + i - 1)); |
| if (!(i % 4)) |
| pr_info("\n"); |
| } |
| #endif |
| |
| #if 1 |
| |
| pMsg = (unsigned char *)&msg; |
| |
| for (i = 0; i < size; i += 4) { |
| pMsg[3] = i2c_cmds[i]; |
| pMsg[2] = i2c_cmds[i + 1]; |
| pMsg[1] = i2c_cmds[i + 2]; |
| pMsg[0] = i2c_cmds[i + 3]; |
| |
| do { |
| rc = execute_cmdmsg(msg); |
| } while ((rc < 0) && --retry); |
| |
| /* Audience not responding to execute_cmdmsg , |
| * doing HW reset of the chipset */ |
| if ((retry == 0) && (rc < 0)) { |
| struct a2220img img; |
| img.buf = a2220_firmware_buf; |
| img.img_size = sizeof(a2220_firmware_buf); |
| rc = a2220_hw_reset(&img); /* Call if the Audience |
| chipset is not responding after retrying 12 times */ |
| if (rc < 0) { |
| printk(MODULE_NAME |
| "%s :: Audience HW Reset Failed\n", |
| __func__); |
| return rc; |
| } |
| } |
| |
| } |
| |
| #else |
| rc = a2220_i2c_write(i2c_cmds, size); |
| if (rc < 0) { |
| printk(KERN_ERR "A2220 CMD block write error!\n"); |
| a2220_i2c_sw_reset(sw_reset); |
| return rc; |
| } |
| pr_info("%s: block write end\n", __func__); |
| |
| /* Don't need to get Ack after sending out a suspend command */ |
| if (*i2c_cmds == 0x80 && *(i2c_cmds + 1) == 0x10 |
| && *(i2c_cmds + 2) == 0x00 && *(i2c_cmds + 3) == 0x01) { |
| a2220_suspended = 1; |
| /* Disable A2220 clock */ |
| msleep(120); |
| if (control_a2220_clk) |
| gpio_set_value(pdata->gpio_a2220_clk, 0); |
| return rc; |
| } |
| |
| memset(ack_buf, 0, sizeof(ack_buf)); |
| msleep(20); |
| pr_info("%s: CMD ACK block read start\n", __func__); |
| rc = a2220_i2c_read(ack_buf, size); |
| if (rc < 0) { |
| printk(KERN_ERR "%s: CMD ACK block read error\n", __func__); |
| a2220_i2c_sw_reset(sw_reset); |
| return rc; |
| } else { |
| pr_info("%s: CMD ACK block read end\n", __func__); |
| #if DEBUG |
| 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 = a2220_i2c_read(rdbuf, 4); |
| if (rc < 0) |
| return rc; |
| #if DEBUG |
| 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 { |
| printk(KERN_ERR "%s: CMD ACK Not Ready\n", |
| __func__); |
| return -EBUSY; |
| } |
| } else if (*index == 0xff) { /* illegal cmd */ |
| return -ENOEXEC; |
| } else if (*index == 0x80) { |
| index += 4; |
| } |
| } while (--number_of_cmd_sets); |
| } |
| #endif |
| |
| 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; |
| |
| #if DEBUG |
| printk(KERN_DEBUG "%s : execute_cmdmsg :: %x %x %x %x\n", |
| __func__, msgbuf[0], msgbuf[1], msgbuf[2], msgbuf[3]); |
| #endif |
| memcpy(chkbuf, msgbuf, 4); |
| |
| rc = a2220_i2c_write(msgbuf, 4); |
| if (rc < 0) { |
| a2220_i2c_sw_reset(sw_reset); |
| |
| if (msg == A100_msg_Sleep) { |
| printk(MODULE_NAME |
| "%s : execute_cmdmsg ...go to suspend first\n", |
| __func__); |
| a2220_suspended = 1;/*(+)dragonball test for audience */ |
| msleep(120); |
| |
| } |
| return rc; |
| } |
| |
| /* We don't need to get Ack after sending out a suspend command */ |
| if (msg == A100_msg_Sleep) { |
| printk(MODULE_NAME "%s : ...go to suspend first\n", __func__); |
| a2220_suspended = 1; /*(+)dragonball test for audience */ |
| |
| return rc; |
| } |
| |
| retries = POLLING_RETRY_CNT; |
| while (retries--) { |
| rc = 0; |
| memset(msgbuf, 0, sizeof(msgbuf)); |
| rc = a2220_i2c_read(msgbuf, 4); |
| if (rc < 0) { |
| printk(KERN_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) { |
| printk(KERN_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; |
| } |
| msleep(20); /* use polling */ |
| } |
| |
| if (!pass) { |
| printk(KERN_ERR "%s: failed execute cmd %08x (%d)\n", |
| __func__, msg, rc); |
| a2220_i2c_sw_reset(sw_reset); |
| } |
| |
| return rc; |
| } |
| |
| #if ENABLE_DIAG_IOCTLS |
| static int a2220_set_mic_state(char miccase) |
| { |
| int rc = 0; |
| unsigned int cmd_msg = 0; |
| |
| switch (miccase) { |
| case 1: /* Mic-1 ON / Mic-2 OFF */ |
| cmd_msg = 0x80260007; |
| break; |
| case 2: /* Mic-1 OFF / Mic-2 ON */ |
| cmd_msg = 0x80260015; |
| break; |
| case 3: /* both ON */ |
| cmd_msg = 0x80260001; |
| break; |
| case 4: /* both OFF */ |
| 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) |
| printk(KERN_ERR "%s: cmd %08x error %d\n", |
| __func__, cmd_msg, rc); |
| return rc; |
| } |
| #endif /* ENABLE_DIAG_IOCTLS */ |
| |
| /* Thread does the init process of Audience Chip */ |
| static int a2220_init_thread(void *data) |
| { |
| int rc = 0; |
| struct a2220img img; |
| img.buf = a2220_firmware_buf; |
| img.img_size = sizeof(a2220_firmware_buf); |
| rc = a2220_bootup_init(&img); |
| return rc; |
| } |
| |
| static int |
| a2220_ioctl(struct inode *inode, struct file *file, unsigned int cmd, |
| unsigned long arg) |
| { |
| void __user *argp = (void __user *)arg; |
| struct a2220img img; |
| int rc = 0; |
| #if ENABLE_DIAG_IOCTLS |
| char msg[4]; |
| int mic_cases = 0; |
| int mic_sel = 0; |
| #endif |
| unsigned int ns_state; |
| |
| switch (cmd) { |
| case A2220_BOOTUP_INIT: |
| img.buf = a2220_firmware_buf; |
| img.img_size = sizeof(a2220_firmware_buf); |
| printk(MODULE_NAME "%s : a2220_firmware_buf = %d\n", __func__, |
| sizeof(a2220_firmware_buf)); |
| task = |
| kthread_run(a2220_init_thread, NULL, "a2220_init_thread"); |
| if (IS_ERR(task)) { |
| rc = PTR_ERR(task); |
| task = NULL; |
| } |
| break; |
| case A2220_SET_CONFIG: |
| rc = a2220_set_config(arg, A2220_CONFIG_FULL); |
| if (rc < 0) |
| printk(KERN_ERR "%s: A2220_SET_CONFIG (%lu) error %d!\n", |
| __func__, arg, rc); |
| break; |
| case A2220_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 >= A2220_NS_NUM_STATES) |
| return -EINVAL; |
| a2220_NS_state = ns_state; |
| if (!a2220_suspended) |
| a2220_set_config(a2220_current_config, A2220_CONFIG_VP); |
| break; |
| #if ENABLE_DIAG_IOCTLS |
| case A2220_SET_MIC_ONOFF: |
| rc = chk_wakeup_a2220(); |
| if (rc < 0) |
| return rc; |
| if (copy_from_user(&mic_cases, argp, sizeof(mic_cases))) |
| return -EFAULT; |
| rc = a2220_set_mic_state(mic_cases); |
| if (rc < 0) |
| printk(KERN_ERR "%s: A2220_SET_MIC_ONOFF %d error %d!\n", |
| __func__, mic_cases, rc); |
| break; |
| case A2220_SET_MICSEL_ONOFF: |
| rc = chk_wakeup_a2220(); |
| if (rc < 0) |
| return rc; |
| if (copy_from_user(&mic_sel, argp, sizeof(mic_sel))) |
| return -EFAULT; |
| rc = 0; |
| break; |
| case A2220_READ_DATA: |
| rc = chk_wakeup_a2220(); |
| if (rc < 0) |
| return rc; |
| rc = a2220_i2c_read(msg, 4); |
| if (copy_to_user(argp, &msg, 4)) |
| return -EFAULT; |
| break; |
| case A2220_WRITE_MSG: |
| rc = chk_wakeup_a2220(); |
| if (rc < 0) |
| return rc; |
| if (copy_from_user(msg, argp, sizeof(msg))) |
| return -EFAULT; |
| rc = a2220_i2c_write(msg, 4); |
| break; |
| case A2220_SYNC_CMD: |
| rc = chk_wakeup_a2220(); |
| if (rc < 0) |
| return rc; |
| msg[0] = 0x80; |
| msg[1] = 0x00; |
| msg[2] = 0x00; |
| msg[3] = 0x00; |
| rc = a2220_i2c_write(msg, 4); |
| break; |
| case A2220_SET_CMD_FILE: |
| rc = chk_wakeup_a2220(); |
| if (rc < 0) |
| return rc; |
| if (copy_from_user(msg, argp, sizeof(msg))) |
| return -EFAULT; |
| rc = exe_cmd_in_file(msg); |
| break; |
| #endif /* ENABLE_DIAG_IOCTLS */ |
| default: |
| printk(KERN_ERR "%s: invalid command %d\n", |
| __func__, _IOC_NR(cmd)); |
| rc = -EINVAL; |
| break; |
| } |
| |
| return rc; |
| } |
| |
| int a2220_ioctl2(unsigned int cmd, unsigned long arg) |
| { |
| a2220_ioctl(NULL, NULL, cmd, arg); |
| return 0; |
| } |
| EXPORT_SYMBOL(a2220_ioctl2); |
| |
| int a2220_port_path_change(unsigned int msg) |
| { |
| switch (msg) { |
| case A100_msg_PortC_D_PASS: |
| case A100_msg_PortD_C_PASS: |
| case A100_msg_PortB_A_PASS: |
| case A100_msg_PortA_B_PASS: |
| case A100_msg_PortC_A_PASS: |
| case A100_msg_PortA_C_PASS: |
| break; |
| default: |
| printk(KERN_ERR "Not support [0x%X] for port change\n", msg); |
| return -EINVAL; |
| } |
| /* Default set to PORTD -> PORTC and |
| PORTB -> PORTA in pass through) */ |
| /* return execute_cmdmsg(msg); */ |
| return 0; |
| } |
| EXPORT_SYMBOL(a2220_port_path_change); |
| |
| static const struct file_operations a2220_fops = { |
| .owner = THIS_MODULE, |
| .open = a2220_open, |
| .release = a2220_release, |
| /* .ioctl = a2220_ioctl, */ |
| }; |
| |
| static struct miscdevice a2220_device = { |
| .minor = MISC_DYNAMIC_MINOR, |
| .name = "audience_a2220", |
| .fops = &a2220_fops, |
| }; |
| |
| static int a2220_probe(struct i2c_client *client, |
| const struct i2c_device_id *id) |
| { |
| int rc = 0, ret; |
| unsigned long val; |
| void __iomem *pmc_base = IO_ADDRESS(TEGRA_PMC_BASE); |
| |
| extern3_clk = clk_get_sys("extern3", NULL); |
| if (IS_ERR(extern3_clk)) { |
| printk(KERN_ERR "%s: Can't retrieve extern3\n", __func__); |
| goto err_clk_get_failed; |
| } |
| |
| ret = clk_enable(extern3_clk); |
| if (ret) { |
| printk(KERN_ERR "Can't enable clk extern3"); |
| goto err_clk_enable_failed; |
| } |
| |
| control_a2220_clk = 1; |
| /* disable master enable in PMC */ |
| val = readl(pmc_base + PMC_CLK_OUT); |
| val |= CLK3_SRC_SEL; |
| |
| writel(val, pmc_base + PMC_CLK_OUT); |
| |
| val = readl(pmc_base + PMC_CLK_OUT); |
| val |= CLK3_FORCE_EN; |
| writel(val, pmc_base + PMC_CLK_OUT); |
| |
| pdata = client->dev.platform_data; |
| |
| if (pdata == NULL) { |
| pdata = kzalloc(sizeof(*pdata), GFP_KERNEL); |
| printk(KERN_ERR "%s : a2220_probe - pdata NULL so allocating ...\n", |
| __func__); |
| if (pdata == NULL) { |
| rc = -ENOMEM; |
| printk(KERN_ERR "%s: platform data is NULL\n", |
| __func__); |
| |
| goto err_alloc_data_failed; |
| } |
| } |
| |
| #ifdef CONFIG_USA_MODEL_SGH_T989 |
| if (get_hw_rev() >= 0x05) |
| gpio_tlmm_config(GPIO_CFG(33, 0, GPIO_CFG_OUTPUT, |
| GPIO_CFG_NO_PULL, GPIO_CFG_2MA), |
| GPIO_CFG_ENABLE);/* 2MIC_PWDN */ |
| else |
| gpio_tlmm_config(GPIO_CFG(34, 0, GPIO_CFG_OUTPUT, |
| GPIO_CFG_NO_PULL, GPIO_CFG_2MA), |
| GPIO_CFG_ENABLE);/* 2MIC_PWDN */ |
| #elif CONFIG_USA_MODEL_SGH_I727 |
| if (get_hw_rev() >= 0x05) { |
| printk(KERN_DEBUG " %s : GPIO 33\n", __func__); |
| gpio_tlmm_config(GPIO_CFG(33, 0, GPIO_CFG_OUTPUT, |
| GPIO_CFG_NO_PULL, GPIO_CFG_2MA), |
| GPIO_CFG_ENABLE);/* 2MIC_PWDN */ |
| } else { |
| printk(KERN_DEBUG "%s : get_hw_rev() == %d\n", |
| __func__, get_hw_rev()); |
| |
| gpio_tlmm_config(GPIO_CFG(34, 0, GPIO_CFG_OUTPUT, |
| GPIO_CFG_NO_PULL, GPIO_CFG_2MA), |
| GPIO_CFG_ENABLE);/* 2MIC_PWDN */ |
| } |
| #elif CONFIG_USA_MODEL_SGH_I717 |
| |
| #else |
| gpio_tlmm_config(GPIO_CFG(34, 0, GPIO_CFG_OUTPUT, |
| GPIO_CFG_NO_PULL, GPIO_CFG_2MA), |
| GPIO_CFG_ENABLE); /* 2MIC_PWDN */ |
| #endif |
| |
| #if !defined(CONFIG_USA_MODEL_SGH_I727) && !defined(CONFIG_USA_MODEL_SGH_T989)\ |
| && !defined(CONFIG_USA_MODEL_SGH_I717) /*qup_a2220 */ |
| gpio_tlmm_config(GPIO_CFG(35, 0, GPIO_CFG_OUTPUT, GPIO_CFG_NO_PULL, |
| GPIO_CFG_2MA), GPIO_CFG_ENABLE);/* 2MIC_SDA_1.8V */ |
| gpio_tlmm_config(GPIO_CFG(36, 0, GPIO_CFG_OUTPUT, GPIO_CFG_NO_PULL, |
| GPIO_CFG_2MA), GPIO_CFG_ENABLE);/* 2MIC_SCL_1.8V */ |
| #endif |
| |
| /*VP reset */ |
| gpio_set_value(VP_RESET, 1); |
| gpio_set_value(AUDIO_LD0_EN, 0); |
| |
| this_client = client; |
| |
| gpio_set_value(VP_RESET, 0); |
| |
| if (pdata->gpio_a2220_clk) { |
| rc = gpio_request(pdata->gpio_a2220_clk, "a2220"); |
| if (rc < 0) { |
| control_a2220_clk = 0; |
| goto chk_gpio_micsel; |
| } |
| control_a2220_clk = 1; |
| |
| rc = gpio_direction_output(pdata->gpio_a2220_clk, 1); |
| if (rc < 0) { |
| printk(KERN_ERR "%s: request clk gpio direction failed\n", |
| __func__); |
| goto err_free_gpio_clk; |
| } |
| } |
| chk_gpio_micsel: |
| if (pdata->gpio_a2220_micsel) { |
| rc = gpio_request(pdata->gpio_a2220_micsel, "a2220"); |
| if (rc < 0) { |
| printk(KERN_ERR "%s: gpio request mic_sel pin failed\n", |
| __func__); |
| goto err_free_gpio_micsel; |
| } |
| |
| rc = gpio_direction_output(pdata->gpio_a2220_micsel, 1); |
| if (rc < 0) { |
| printk(KERN_ERR "%s: request mic_sel gpio direction failed\n", |
| __func__); |
| goto err_free_gpio_micsel; |
| } |
| } |
| |
| if (pdata->gpio_a2220_wakeup) { |
| #ifdef CONFIG_USA_MODEL_SGH_T989 |
| if (get_hw_rev() >= 0x05) |
| rc = gpio_request(WAKEUP_GPIO_NUM_HERCULES_REV01, |
| "a2220"); |
| else |
| rc = gpio_request(pdata->gpio_a2220_wakeup, "a2220"); |
| #elif CONFIG_USA_MODEL_SGH_I727 |
| if (get_hw_rev() >= 0x05) |
| rc = gpio_request(WAKEUP_GPIO_NUM_CELOX_ATT_REV05, |
| "a2220"); |
| else |
| rc = gpio_request(pdata->gpio_a2220_wakeup, "a2220"); |
| #elif CONFIG_USA_MODEL_SGH_I717 |
| rc = gpio_request(WAKEUP_GPIO_NUM_CELOX_ATT_REV05, "a2220"); |
| #else |
| rc = gpio_request(pdata->gpio_a2220_wakeup, "a2220"); |
| #endif |
| if (rc < 0) { |
| printk(KERN_ERR "%s: gpio request wakeup pin failed\n", |
| __func__); |
| goto err_free_gpio; |
| } |
| #ifdef CONFIG_USA_MODEL_SGH_T989 |
| if (get_hw_rev() >= 0x05) |
| rc = gpio_direction_output |
| (WAKEUP_GPIO_NUM_HERCULES_REV01, 1); |
| else |
| rc = gpio_direction_output(pdata->gpio_a2220_wakeup, 1); |
| #elif CONFIG_USA_MODEL_SGH_I727 |
| if (get_hw_rev() >= 0x05) |
| rc = gpio_direction_output |
| (WAKEUP_GPIO_NUM_CELOX_ATT_REV05, 1); |
| else |
| rc = gpio_direction_output(pdata->gpio_a2220_wakeup, 1); |
| #elif CONFIG_USA_MODEL_SGH_I717 |
| rc = gpio_direction_output(WAKEUP_GPIO_NUM_CELOX_ATT_REV05, 1); |
| #else |
| rc = gpio_direction_output(pdata->gpio_a2220_wakeup, 1); |
| #endif |
| |
| |
| if (rc < 0) { |
| printk(KERN_ERR "%s: request wakeup gpio direction failed\n", |
| __func__); |
| goto err_free_gpio; |
| } |
| } |
| |
| if (pdata->gpio_a2220_reset) { |
| rc = gpio_request(pdata->gpio_a2220_reset, "a2220"); |
| if (rc < 0) { |
| printk(KERN_ERR "%s: gpio request reset pin failed\n", |
| __func__); |
| goto err_free_gpio; |
| } |
| |
| rc = gpio_direction_output(pdata->gpio_a2220_reset, 1); |
| if (rc < 0) { |
| printk(KERN_ERR "%s: request reset gpio direction failed\n", |
| __func__); |
| goto err_free_gpio_all; |
| } |
| } |
| |
| if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) { |
| printk(KERN_ERR "%s: i2c check functionality error\n", |
| __func__); |
| rc = -ENODEV; |
| goto err_free_gpio_all; |
| } |
| |
| if (control_a2220_clk) |
| gpio_set_value(pdata->gpio_a2220_clk, 1); |
| if (pdata->gpio_a2220_micsel) |
| gpio_set_value(pdata->gpio_a2220_micsel, 0); |
| |
| if (pdata->gpio_a2220_wakeup) { |
| #ifdef CONFIG_USA_MODEL_SGH_T989 |
| if (get_hw_rev() >= 0x05) |
| gpio_set_value(WAKEUP_GPIO_NUM_HERCULES_REV01, 1); |
| else |
| gpio_set_value(pdata->gpio_a2220_wakeup, 1); |
| #elif CONFIG_USA_MODEL_SGH_I727 |
| if (get_hw_rev() >= 0x05) |
| gpio_set_value(WAKEUP_GPIO_NUM_CELOX_ATT_REV05, 1); |
| else |
| gpio_set_value(pdata->gpio_a2220_wakeup, 1); |
| #elif CONFIG_USA_MODEL_SGH_I717 |
| gpio_set_value(WAKEUP_GPIO_NUM_CELOX_ATT_REV05, 1); |
| #else |
| gpio_set_value(pdata->gpio_a2220_wakeup, 1); |
| #endif |
| } |
| |
| if (pdata->gpio_a2220_reset) |
| gpio_set_value(pdata->gpio_a2220_reset, 1); |
| |
| if (pdata->gpio_a2220_audience_chip_sel) |
| gpio_set_value(pdata->gpio_a2220_audience_chip_sel, 1); |
| |
| rc = misc_register(&a2220_device); |
| if (rc) { |
| printk(KERN_ERR "%s: a2220_device register failed\n", __func__); |
| goto err_free_gpio_all; |
| } |
| |
| /* send boot msg */ |
| rc = a2220_set_boot_mode(); |
| if (rc < 0) { |
| printk(KERN_ERR "%s: failed %d\n", __func__, rc); |
| goto err_free_gpio_all; |
| } |
| |
| /* A2220 firmware download start .. */ |
| a2220_ioctl2(A2220_BOOTUP_INIT, 0); |
| |
| return 0; |
| |
| err_free_gpio_all: |
| if (pdata->gpio_a2220_reset) |
| gpio_free(pdata->gpio_a2220_reset); |
| err_free_gpio: |
| if (pdata->gpio_a2220_wakeup) { |
| #ifdef CONFIG_USA_MODEL_SGH_T989 |
| if (get_hw_rev() >= 0x05) |
| gpio_free(WAKEUP_GPIO_NUM_HERCULES_REV01); |
| else |
| gpio_free(pdata->gpio_a2220_wakeup); |
| #elif CONFIG_USA_MODEL_SGH_I727 |
| if (get_hw_rev() >= 0x05) |
| gpio_free(WAKEUP_GPIO_NUM_CELOX_ATT_REV05); |
| else |
| gpio_free(pdata->gpio_a2220_wakeup); |
| #elif CONFIG_USA_MODEL_SGH_I717 |
| gpio_free(WAKEUP_GPIO_NUM_CELOX_ATT_REV05); |
| #else |
| gpio_free(pdata->gpio_a2220_wakeup); |
| #endif |
| } |
| err_free_gpio_micsel: |
| if (pdata->gpio_a2220_micsel) |
| gpio_free(pdata->gpio_a2220_micsel); |
| err_free_gpio_clk: |
| if (pdata->gpio_a2220_clk) |
| gpio_free(pdata->gpio_a2220_clk); |
| err_alloc_data_failed: |
| clk_disable(extern3_clk); |
| err_clk_enable_failed: |
| clk_put(extern3_clk); |
| err_clk_get_failed: |
| |
| return rc; |
| } |
| |
| static int a2220_remove(struct i2c_client *client) |
| { |
| struct a2220_platform_data *p1026data = i2c_get_clientdata(client); |
| kfree(p1026data); |
| |
| return 0; |
| } |
| |
| static int a2220_suspend(struct i2c_client *client, pm_message_t mesg) |
| { |
| return 0; |
| } |
| |
| static int a2220_resume(struct i2c_client *client) |
| { |
| return 0; |
| } |
| |
| static const struct i2c_device_id a2220_id[] = { |
| {"audience_a2220", 0}, |
| {} |
| }; |
| |
| MODULE_DEVICE_TABLE(i2c, a2220_id); |
| |
| static struct i2c_driver a2220_driver = { |
| .probe = a2220_probe, |
| .remove = a2220_remove, |
| .suspend = a2220_suspend, |
| .resume = a2220_resume, |
| .id_table = a2220_id, |
| .driver = { |
| .name = "audience_a2220", |
| }, |
| }; |
| |
| |
| static int __init a2220_init(void) |
| { |
| #ifdef CONFIG_BATTERY_SEC |
| if (is_lpcharging_state()) { |
| pr_info("%s : LPM Charging Mode! return 0\n", __func__); |
| return 0; |
| } |
| #endif |
| mutex_init(&a2220_lock); |
| |
| return i2c_add_driver(&a2220_driver); |
| } |
| |
| static void __exit a2220_exit(void) |
| { |
| i2c_del_driver(&a2220_driver); |
| } |
| |
| module_init(a2220_init); |
| module_exit(a2220_exit); |
| |
| MODULE_DESCRIPTION("A2220 voice processor driver"); |
| MODULE_LICENSE("GPL"); |