185 lines
		
	
	
		
			4.1 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			185 lines
		
	
	
		
			4.1 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| /* driver/i2c/chip/tap6130.c
 | |
|  *
 | |
|  * TI TPA6130 Headset Amp
 | |
|  *
 | |
|  * 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/i2c.h>
 | |
| #include <linux/miscdevice.h>
 | |
| #include <asm/gpio.h>
 | |
| #include <asm/uaccess.h>
 | |
| #include <linux/delay.h>
 | |
| #include <linux/freezer.h>
 | |
| #include <mach/tpa6130.h>
 | |
| #include <linux/mutex.h>
 | |
| #include <mach/msm_rpcrouter.h>
 | |
| 
 | |
| #define HEADSET_MTOA_PROG                      0x30100003
 | |
| #define HEADSET_MTOA_VERS                      0
 | |
| #define HTC_HEADSET_NULL_PROC                  0
 | |
| #define HTC_HEADSET_CTL_PROC                   1
 | |
| 
 | |
| static struct i2c_client *this_client;
 | |
| struct mutex amp_mutex;
 | |
| static struct tpa6130_platform_data *pdata;
 | |
| 
 | |
| static int i2c_on;
 | |
| char buffer[2];
 | |
| 
 | |
| static int I2C_TxData(char *txData, int length)
 | |
| {
 | |
| 	struct i2c_msg msg[] = {
 | |
| 		{
 | |
| 		 .addr = this_client->addr,
 | |
| 		 .flags = 0,
 | |
| 		 .len = length,
 | |
| 		 .buf = txData,
 | |
| 		 },
 | |
| 	};
 | |
| 
 | |
| 	if (i2c_transfer(this_client->adapter, msg, 1) < 0) {
 | |
| 		pr_err("tpa6130 :I2C transfer error\n");
 | |
| 		return -EIO;
 | |
| 	} else
 | |
| 		return 0;
 | |
| }
 | |
| 
 | |
| void set_headset_amp(int on)
 | |
| {
 | |
| 	mutex_lock(&_mutex);
 | |
| 	if (on && !i2c_on) {
 | |
| 		buffer[0] = 0x01;
 | |
| 		buffer[1] = 0xC0;
 | |
| 		buffer[2] = 0x3E;
 | |
| 		if (I2C_TxData(buffer, 3) == 0) {
 | |
| 			i2c_on = 1;
 | |
| 			pr_err("tpa6130: turn on headset amp !\n");
 | |
| 		}
 | |
| 	} else if (!on && i2c_on) {
 | |
| 		buffer[0] = 0x01;
 | |
| 		buffer[1] = 0xC1;
 | |
| 		if (I2C_TxData(buffer, 2) == 0) {
 | |
| 			i2c_on = 0;
 | |
| 			pr_err("tpa6130: turn off headset amp !\n");
 | |
| 		}
 | |
| 	}
 | |
| 	mutex_unlock(&_mutex);
 | |
| }
 | |
| 
 | |
| static int handle_headset_call(struct msm_rpc_server *server,
 | |
| 			       struct rpc_request_hdr *req, unsigned len)
 | |
| {
 | |
| 	if (!pdata->enable_rpc_server)
 | |
| 		return 0;
 | |
| 
 | |
| 	struct rpc_headset_amp_ctl_args *args;
 | |
| 	switch (req->procedure) {
 | |
| 	case HTC_HEADSET_NULL_PROC:
 | |
| 		return 0;
 | |
| 	case HTC_HEADSET_CTL_PROC:
 | |
| 		args = (struct rpc_headset_amp_ctl_args *)(req + 1);
 | |
| 		args->on = be32_to_cpu(args->on);
 | |
| 		if (args->on) {
 | |
| 			gpio_set_value(pdata->gpio_hp_sd, 1);
 | |
| 			msleep(10);
 | |
| 			set_headset_amp(args->on);
 | |
| 		} else if (!args->on) {
 | |
| 			set_headset_amp(args->on);
 | |
| 			gpio_set_value(pdata->gpio_hp_sd, 0);
 | |
| 		}
 | |
| 		return 0;
 | |
| 	default:
 | |
| 		pr_err("tpa6130a: the wrong proc for headset server\n");
 | |
| 	}
 | |
| 	return -ENODEV;
 | |
| }
 | |
| 
 | |
| static struct msm_rpc_server headset_server = {
 | |
| 	.prog = HEADSET_MTOA_PROG,
 | |
| 	.vers = HEADSET_MTOA_VERS,
 | |
| 	.rpc_call = handle_headset_call
 | |
| };
 | |
| 
 | |
| int tpa6130_probe(struct i2c_client *client, const struct i2c_device_id *id)
 | |
| {
 | |
| 	int ret = 0;
 | |
| 
 | |
| 	pdata = client->dev.platform_data;
 | |
| 	if (pdata == NULL) {
 | |
| 		pr_err("tpa6130: platform data is NULL\n");
 | |
| 		goto fault;
 | |
| 	}
 | |
| 
 | |
| 	if (pdata->enable_rpc_server) {
 | |
| 		msm_rpc_create_server(&headset_server);
 | |
| 
 | |
| 		ret = gpio_request(pdata->gpio_hp_sd, "tpa6130");
 | |
| 		if (ret < 0) {
 | |
| 			pr_err("tap6130a : gpio request failed\n");
 | |
| 			goto fault;
 | |
| 		}
 | |
| 
 | |
| 		ret = gpio_direction_output(pdata->gpio_hp_sd, 1);
 | |
| 		if (ret < 0) {
 | |
| 			pr_err("tap6130a: request reset gpio failed\n");
 | |
| 			goto fault;
 | |
| 		}
 | |
| 		gpio_set_value(pdata->gpio_hp_sd, 0);
 | |
| 	}
 | |
| 
 | |
| 	this_client = client;
 | |
| 
 | |
| 	if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
 | |
| 		pr_err("tpa6130a: i2c check functionality error\n");
 | |
| 		goto fault;
 | |
| 	}
 | |
| 
 | |
| 	return 0;
 | |
| fault:
 | |
| 	return -ENODEV;
 | |
| }
 | |
| 
 | |
| static int tpa6130_remove(struct i2c_client *client)
 | |
| {
 | |
| 	return 0;
 | |
| }
 | |
| static const struct i2c_device_id tpa6130_id[] = {
 | |
| 	{ TPA6130_I2C_NAME, 0 },
 | |
| 	{ }
 | |
| };
 | |
| 
 | |
| static struct i2c_driver tpa6130_driver = {
 | |
| 	.probe 	        = tpa6130_probe,
 | |
| 	.remove 	= tpa6130_remove,
 | |
| 	.id_table	= tpa6130_id,
 | |
| 	.driver = {
 | |
| 		   .name = TPA6130_I2C_NAME,
 | |
| 		   },
 | |
| };
 | |
| 
 | |
| static int __init tpa6130_init(void)
 | |
| {
 | |
| 	pr_err("tpa6130 HP AMP: init\n");
 | |
| 	mutex_init(&_mutex);
 | |
| 	return i2c_add_driver(&tpa6130_driver);
 | |
| }
 | |
| 
 | |
| static void __exit tpa6130_exit(void)
 | |
| {
 | |
| 	i2c_del_driver(&tpa6130_driver);
 | |
| }
 | |
| 
 | |
| module_init(tpa6130_init);
 | |
| module_exit(tpa6130_exit);
 |