From c6da2cfeb05178a11c6d062a06f8078150ee492f Mon Sep 17 00:00:00 2001 From: codeworkx Date: Sat, 2 Jun 2012 13:09:29 +0200 Subject: samsung update 1 --- drivers/misc/es305.c | 400 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 400 insertions(+) create mode 100644 drivers/misc/es305.c (limited to 'drivers/misc/es305.c') diff --git a/drivers/misc/es305.c b/drivers/misc/es305.c new file mode 100644 index 0000000..f3f3cd0 --- /dev/null +++ b/drivers/misc/es305.c @@ -0,0 +1,400 @@ +/* drivers/misc/es305.c - audience ES305 voice processor driver + * + * Copyright (C) 2012 Samsung 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef CONFIG_MACH_C1VZW +#define ES305_FIRMWARE_NAME "audience/es305_fw_c1vzw.bin" +#else +#define ES305_FIRMWARE_NAME "audience/es305_fw.bin" +#endif + +static struct i2c_client *this_client; +static struct es305_platform_data *pdata; +static struct task_struct *task; + +unsigned char es305_reset_cmd[] = { + 0x80, 0x02, 0x00, 0x00, +}; + +unsigned char es305_sync_cmd[] = { + 0x80, 0x00, 0x00, 0x00, +}; + +unsigned char es305_boot_cmd[] = { + 0x00, 0x01, +}; + +unsigned char es305_sleep_cmd[] = { + 0x80, 0x10, 0x00, 0x01, +}; + +unsigned char es305_bypass_data[] = { +#ifdef CONFIG_MACH_C1VZW + 0x80, 0x52, 0x00, 0x48, +#else + 0x80, 0x52, 0x00, 0x4C, +#endif + 0x80, 0x10, 0x00, 0x01, +}; + +static int es305_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; + } + return 0; +} + +static int es305_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; + } + + return 0; +} + +int es305_set_cmd(enum es305_cmd cmd) +{ +#if DEBUG + int i = 0; +#endif + int rc = 0, size = 0; + unsigned char *i2c_cmds = NULL; + + pr_info(MODULE_NAME "%s : cmd %d\n", __func__, cmd); + switch (cmd) { + case ES305_SW_RESET: + i2c_cmds = es305_reset_cmd; + size = sizeof(es305_reset_cmd); + break; + case ES305_SYNC: + i2c_cmds = es305_sync_cmd; + size = sizeof(es305_sync_cmd); + break; + case ES305_BOOT: + i2c_cmds = es305_boot_cmd; + size = sizeof(es305_boot_cmd); + break; + case ES305_SLEEP: + i2c_cmds = es305_sleep_cmd; + size = sizeof(es305_sleep_cmd); + break; + case ES305_BYPASS_DATA: + i2c_cmds = es305_bypass_data; + size = sizeof(es305_bypass_data); + break; + default: + pr_err(MODULE_NAME "%s : unknown cmd\n", __func__); + break; + } + +#if DEBUG + for (i = 0; i < size; i += 1) + pr_info(MODULE_NAME "%s : i2c_cmds[%d/%d] = 0x%x\n", + __func__, i, size, i2c_cmds[i]); +#endif + + rc = es305_i2c_write(i2c_cmds, size); + if (rc < 0) + pr_err(MODULE_NAME "%s failed %d\n", __func__, rc); + + return rc; +} + + +int es305_sw_reset(void) +{ + int rc = 0, size = 0; + unsigned char *i2c_cmds; + + pr_info(MODULE_NAME "%s\n", __func__); + + i2c_cmds = es305_reset_cmd; + size = sizeof(es305_reset_cmd); + + rc = es305_i2c_write(i2c_cmds, size); + if (rc < 0) + pr_err(MODULE_NAME "%s failed %d\n", __func__, rc); + + msleep(50); + + return rc; +} + +int es305_hw_reset(void) +{ + int rc = 0; + int retry = RETRY_CNT; + unsigned char msgbuf[4] = {0,}; + + pr_info(MODULE_NAME "%s\n", __func__); + + while (retry--) { + /* Reset ES305 chip */ + gpio_set_value(pdata->gpio_reset, 0); + + /* Enable ES305 clock */ + if (pdata->set_mclk != NULL) + pdata->set_mclk(true, false); + mdelay(1); + + gpio_set_value(pdata->gpio_reset, 1); + + msleep(50); /* Delay before send I2C command */ + + rc = es305_set_cmd(ES305_BOOT); + if (rc < 0) { + pr_err(MODULE_NAME "%s: set boot mode error\n", + __func__); + continue; + } + + rc = es305_i2c_read(msgbuf, 1); + if (rc < 0) { + pr_err(MODULE_NAME "%s: boot mode ack error (%d retries left)\n", + __func__, retry); + continue; + } + + mdelay(1); + rc = es305_load_firmware(); + if (rc < 0) { + pr_err(MODULE_NAME "%s: load firmware error\n", + __func__); + continue; + } + + msleep(20); /* Delay time before issue a Sync Cmd */ + + rc = es305_set_cmd(ES305_SYNC); + if (rc < 0) { + pr_err(MODULE_NAME "%s: set sync error\n", + __func__); + continue; + } + + rc = es305_i2c_read(msgbuf, 4); + if (rc < 0) { + pr_err(MODULE_NAME "%s: boot mode ack error (%d retries left)\n", + __func__, retry); + continue; + } + + break; + } + return rc; +} + +static int es305_bootup_init(void *dummy) +{ + int rc = 0; + + pr_info(MODULE_NAME "%s\n", __func__); + + rc = es305_hw_reset(); + if (rc < 0) { + pr_err(MODULE_NAME "%s: reset error %d\n", + __func__, rc); + return rc; + } + + rc = es305_set_cmd(ES305_BYPASS_DATA); + if (rc < 0) { + pr_err(MODULE_NAME "%s: set bypass error %d\n", + __func__, rc); + return rc; + } + return rc; +} + +static void es305_gpio_init(void) +{ + if (pdata->gpio_wakeup) { + gpio_request(pdata->gpio_wakeup, "ES305_WAKEUP"); + gpio_direction_output(pdata->gpio_wakeup, 1); + gpio_free(pdata->gpio_wakeup); + gpio_set_value(pdata->gpio_wakeup, 1); + } + + if (pdata->gpio_reset) { + gpio_request(pdata->gpio_reset, "ES305_RESET"); + gpio_direction_output(pdata->gpio_reset, 1); + gpio_free(pdata->gpio_reset); + gpio_set_value(pdata->gpio_reset, 1); + } +} + +int es305_load_firmware(void) +{ + int rc = 0, i = 0; + size_t size = 0; + const struct firmware *fw = NULL; + unsigned char *i2c_cmds; + + pr_info(MODULE_NAME "%s : start\n", __func__); + + rc = request_firmware(&fw, ES305_FIRMWARE_NAME, + &this_client->dev); + if (rc < 0) { + pr_err(MODULE_NAME "%s : unable to open firmware ret %d\n", + __func__, rc); + release_firmware(fw); + return rc; + } + i2c_cmds = (unsigned char *)fw->data; + size = fw->size; + + for (i = 0; i < (size/32); i++, i2c_cmds += 32) { + rc = es305_i2c_write(i2c_cmds, 32); + if (rc < 0) { + pr_err(MODULE_NAME "%s failed %d\n", __func__, rc); + break; + } + } + + if (size%32) { + rc = es305_i2c_write(i2c_cmds, size%32); + if (rc < 0) + pr_err(MODULE_NAME "%s failed %d\n", __func__, rc); + } + + release_firmware(fw); + + pr_info(MODULE_NAME "%s : end\n", __func__); + return rc; +} + + +static int es305_probe( + struct i2c_client *client, const struct i2c_device_id *id) +{ + int rc = 0; + pdata = client->dev.platform_data; + + pr_info(MODULE_NAME "%s : start\n", __func__); + + if (pdata == NULL) { + pdata = kzalloc(sizeof(*pdata), GFP_KERNEL); + if (pdata == NULL) { + rc = -ENOMEM; + pr_err(MODULE_NAME "%s: platform data is NULL\n", + __func__); + } + } + + this_client = client; + + es305_gpio_init(); + task = kthread_run(es305_bootup_init, NULL, "es305_bootup_init"); + if (IS_ERR(task)) { + rc = PTR_ERR(task); + task = NULL; + } + + pr_info(MODULE_NAME "%s : finish\n", __func__); + + return rc; +} + +static int es305_remove(struct i2c_client *client) +{ + struct es305_platform_data *pes305data = i2c_get_clientdata(client); + kfree(pes305data); + + return 0; +} + +static int es305_suspend(struct i2c_client *client, pm_message_t mesg) +{ + return 0; +} + +static int es305_resume(struct i2c_client *client) +{ + return 0; +} + +static const struct i2c_device_id es305_id[] = { + { "audience_es305", 0 }, + { } +}; + +static struct i2c_driver es305_driver = { + .probe = es305_probe, + .remove = es305_remove, + .suspend = es305_suspend, + .resume = es305_resume, + .id_table = es305_id, + .driver = { + .name = "audience_es305", + }, +}; + +static int __init es305_init(void) +{ + pr_info("%s\n", __func__); + + return i2c_add_driver(&es305_driver); +} + +static void __exit es305_exit(void) +{ + i2c_del_driver(&es305_driver); +} + +module_init(es305_init); +module_exit(es305_exit); + +MODULE_DESCRIPTION("audience es305 voice processor driver"); +MODULE_LICENSE("GPL"); -- cgit v1.1