泛泰A860 PIZEO /TS5000协议/TI drv2665芯片 Android内核震动驱动源码

来源:互联网 发布:流体计算软件app 编辑:程序博客网 时间:2024/06/06 08:47
/* *  ti_drv2665.c - Texas Instruments DRV2665 piezo haptic driver * *  Copyright (C) 2011 Texas Instruments * *  Contact: Ameya Palande <ameya.palande@ti.com> * *  This program is free software; you can redistribute it and/or modify *  it under the terms of the GNU General Public License as published by *  the Free Software Foundation; either version 2 of the License, or *  (at your option) any later version. * *  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. * *  You should have received a copy of the GNU General Public License *  along with this program; if not, write to the Free Software *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */#include <linux/module.h>#include <linux/slab.h>#include <linux/i2c.h>#include <linux/pm.h>#include <linux/delay.h>#include <linux/regulator/consumer.h>#include <linux/gpio.h>#include <linux/fs.h>#include "../staging/android/timed_output.h"#ifdef CONFIG_HAS_EARLYSUSPEND#include <linux/earlysuspend.h>#define DRV2665_SUS_LEVEL1#endif#ifdef CONFIG_PIEZO_ECO#define DEVICE_BUS 5#else#define DEVICE_BUS  19#endif#if 0/*** I2C FW info*/typedef struct _FWINFO{    char cMajorVersion;    char cMinorVersion;    char cInterfaceType;    /* 3 = I2C */    char cAmpType[9];       /* 1 = differential amp */    char cAmpUsage;         /* 1 = single boost, 2 = dual boost */    char cClockSource;      /* 2 = external 18MHz clock */} FWINFO;FWINFO g_FWInfo;#define VIBE_MAX_DEVICE_NAME_LENGTH64static char g_szFWVersion[VIBE_MAX_DEVICE_NAME_LENGTH];#endif#define DRV2665_DRV_NAME"ti_drv2665"#define DRV2665_I2C_ADDRESS0x59#define DRV2665_CHIP_ID0x05#define DRV2665_AUTOSUSPEND_DELAY5000#define DRV2665_FIFO_DEPTH100#define DRV2665_MSG_BUFFER_SIZEPAGE_SIZE#define DRV2665_STATUS_REG0x00#defineDRV2665_STATUS_MASK0x03#define DRV2665_CONTROL_REG0x01#defineDRV2665_ID_MASK0x78#defineDRV2665_ID_SHIFT0x03#defineDRV2665_CONTROL_WRITE_MASK0x07#define DRV2665_CONTROL2_REG0x02#defineDRV2665_DEV_RST_SHIFT0x07#defineDRV2665_DEV_RST(1 << DRV2665_DEV_RST_SHIFT)#defineDRV2665_STANDBY_SHIFT0x06#defineDRV2665_STANDBY(1 << DRV2665_STANDBY_SHIFT)#defineDRV2665_TIMEOUT_SHIFT0x02#defineDRV2665_05MS_TIMEOUT0x00#defineDRV2665_10MS_TIMEOUT0x01#defineDRV2665_15MS_TIMEOUT0x02#defineDRV2665_20MS_TIMEOUT0x03#define DRV2665_FIFO_REG0x0B/*Following  code is for setting gain for DRV2665  in register 1 bit 0 and bit 10x00 :output  is set for +/-25V :For AAc, this is more useful  for +/-30V actuator0x01 :output  is set for +/-50V0x02 :output  is set for +/-75V :we use this for SEMCO0x03 :output  is set for +/-100V*/#define DRV2665_GAIN_50 0x00#define DRV2665_GAIN_100 0x01#define DRV2665_GAIN_150 0x02#define DRV2665_GAIN_200 0x03struct drv2665_data {//--------Timer ---------//struct i2c_client *client;struct timed_output_dev timed_dev;struct hrtimer timer;struct work_struct work;u32 max_runtime_ms;u32 time_chunk_ms;u32 runtime_left;#ifdef CONFIG_HAS_EARLYSUSPENDstruct early_suspend es;#endif//----------------------//struct mutex lock;/* DRV2665 cached registers */u8 control;u8 control2;/* DRV2665_FIFO_DEPTH + additional 1 byte for fifo cmd register */u8 fifo_buff[DRV2665_FIFO_DEPTH + 1];};static int drv2665_read_byte(struct device *dev, u8 reg, const char *reg_name){int val;val = i2c_smbus_read_byte_data(to_i2c_client(dev), reg);if (val < 0)dev_err(dev, "error reading %s register\n", reg_name);return val;}static int drv2665_write_byte(struct device *dev, u8 reg, u8 data,const char *reg_name){int status;status = i2c_smbus_write_byte_data(to_i2c_client(dev), reg, data);if (status < 0)dev_err(dev, "error writing %s register\n", reg_name);return status;}static ssize_t status_show(struct device *dev, struct device_attribute *attr,char *buf){int val;val = drv2665_read_byte(dev, DRV2665_STATUS_REG, "status");if (val < 0)return val;return sprintf(buf, "%x\n", val & DRV2665_STATUS_MASK);}static DEVICE_ATTR(status, S_IRUGO, status_show, NULL);static ssize_t fifo_store(struct device *dev, struct device_attribute *attr,const char *buf, size_t count){int status;struct i2c_client *client;struct drv2665_data *data;struct i2c_msg msg;/* * make sure that count is between 1 and DRV2665_FIFO_DEPTH both * inclusive */if (count < 1)return -EINVAL;if (count > DRV2665_FIFO_DEPTH)return -ENOSPC;memset(&msg, 0x00, sizeof(msg));client = to_i2c_client(dev);data = i2c_get_clientdata(client);mutex_lock(&data->lock);/* DRV2665 FIFO Register Address */data->fifo_buff[0] = DRV2665_FIFO_REG;/* fill remaining fifo_buff with data from user space */memcpy(data->fifo_buff + 1, buf, count);msg.addr = client->addr;msg.flags = 0;msg.buf = data->fifo_buff;msg.len = count + 1;//msg.transferred = 0;status = i2c_transfer(client->adapter, &msg, 1);mutex_unlock(&data->lock);/* * subtract DRV2665_FIFO_REG byte from successfully * transferred bytes *///return msg.transferred - 1;return count;}static ssize_t fifo_show(struct device *dev,struct device_attribute *attr, char *buf){struct i2c_client *client;struct drv2665_data *data;int i, count = 0, count_total = 0;char * buf_tmp;client = to_i2c_client(dev);data = i2c_get_clientdata(client);mutex_lock(&data->lock);buf_tmp = buf;for(i=0;i<DRV2665_FIFO_DEPTH;i++){count = sprintf(buf_tmp, "%d\n", data->fifo_buff[i]);buf_tmp += count;count_total += count;}mutex_unlock(&data->lock);return count_total;}static DEVICE_ATTR(fifo, S_IRUGO | S_IWUSR, fifo_show, fifo_store);static ssize_t control_show(struct device *dev,struct device_attribute *attr, char *buf){int val;val = drv2665_read_byte(dev, DRV2665_CONTROL_REG, "control");if (val < 0)return val;return sprintf(buf, "%x\n", val);}static ssize_t control_store(struct device *dev,struct device_attribute *attr, const char *buf, size_t count){u8 val;int status;struct drv2665_data *data;ssize_t ret_value;data = dev_get_drvdata(dev);status = kstrtou8(buf, 16, &val);if (status)return status;mutex_lock(&data->lock);status = drv2665_write_byte(dev, DRV2665_CONTROL_REG,val & DRV2665_CONTROL_WRITE_MASK, "control");if (status < 0) {ret_value = status;} else {/* cache DRV2665_CONTROL_REG value */data->control = val & DRV2665_CONTROL_WRITE_MASK;ret_value = 1;}mutex_unlock(&data->lock);return ret_value;}static DEVICE_ATTR(control, S_IRUGO | S_IWUSR, control_show, control_store);static ssize_t control2_show(struct device *dev,struct device_attribute *attr, char *buf){int val;val = drv2665_read_byte(dev, DRV2665_CONTROL2_REG, "control2");if (val < 0)return val;return sprintf(buf, "%x\n", val);}static ssize_t control2_store(struct device *dev,struct device_attribute *attr, const char *buf, size_t count){u8 val;int status;struct drv2665_data *data;ssize_t ret_value;data = dev_get_drvdata(dev);status = kstrtou8(buf, 16, &val);if (status)return status;mutex_lock(&data->lock);status = drv2665_write_byte(dev, DRV2665_CONTROL2_REG, val, "control2");if (status < 0) {ret_value = status;} else {/* cache DRV2665_CONTROL2_REG value */data->control2 = val;ret_value = 1;}mutex_unlock(&data->lock);return ret_value;}static DEVICE_ATTR(control2, S_IRUGO | S_IWUSR, control2_show, control2_store);static ssize_t reset_store(struct device *dev,struct device_attribute *attr, const char *buf, size_t count){u8 val;int status;struct drv2665_data *data;data = dev_get_drvdata(dev);status = kstrtou8(buf, 16, &val);if (status)return status;if (val != 1)return -EINVAL;mutex_lock(&data->lock);status = drv2665_write_byte(dev, DRV2665_CONTROL2_REG,DRV2665_DEV_RST, "control2");mutex_unlock(&data->lock);if (status < 0)return status;return 1;}static DEVICE_ATTR(reset, S_IWUSR, NULL, reset_store);static struct attribute *drv2665_attributes[] = {&dev_attr_reset.attr,&dev_attr_control2.attr,&dev_attr_control.attr,&dev_attr_status.attr,&dev_attr_fifo.attr,NULL};static const struct attribute_group drv2665_attr_group = {.attrs = drv2665_attributes,};//-------------------------------------------Timer ------------------------------------------//#define DRV2665_BYTES_PER_MS8 //8K HZ#define DRV2665_VIB_DEFAULT_VAL0x7F#define DRV2665_VIB_MAX_TIME5000#define DRV2665_FIFO_CHUNK_MS10#define DRV2665_GAIN DRV2665_GAIN_150#define DRV2665_TIMEOUT (DRV2665_20MS_TIMEOUT << DRV2665_TIMEOUT_SHIFT)u8 sin_buf[]={0,7,15,23,30,38,45,53,60,68,75,82,90,97,104,111,118,124,131,138,144,150,156,162,168,174,180,185,190,195,200,205,209,214,218,222,225,229,232,235,238,241,243,245,247,249,251,252,253,254,255,255,255,255,255,255,254,253,252,250,249,247,245,243,240,237,234,231,228,224,220,217,212,208,203,199,194,189,183,178,172,167,161,155,149,142,136,129,123,116,109,102,95,88,80,73,66,58,51,43,36,28,20,13,5,};static int drv2665_read_reg(struct i2c_client *client, u32 reg){int rc;rc = i2c_smbus_read_byte_data(client, reg);if (rc < 0)dev_err(&client->dev, "i2c reg read for 0x%x failed\n", reg);return rc;}static int drv2665_write_reg(struct i2c_client *client, u32 reg, u8 val){int rc;rc = i2c_smbus_write_byte_data(client, reg, val);if (rc < 0)dev_err(&client->dev, "i2c reg write for 0x%xfailed\n", reg);return rc;}static void drv2665_dump_regs(struct drv2665_data *data, char *label){dev_dbg(&data->client->dev,"%s: reg0x00 = 0x%x, reg0x01 = 0x%x reg0x02 = 0x%x", label,drv2665_read_reg(data->client, DRV2665_STATUS_REG),drv2665_read_reg(data->client, DRV2665_CONTROL_REG),drv2665_read_reg(data->client, DRV2665_CONTROL2_REG));}static void drv2665_worker(struct work_struct *work){struct drv2665_data *data;int rc = 0;u8 val;data = container_of(work, struct drv2665_data, work);/* data is played at 8khz */if (data->runtime_left < data->time_chunk_ms)val = data->runtime_left * DRV2665_BYTES_PER_MS;elseval = data->time_chunk_ms * DRV2665_BYTES_PER_MS;rc = i2c_master_send(data->client, data->fifo_buff, val + 1);if (rc < 0)dev_err(&data->client->dev, "i2c send message failed\n");}static void drv2665_enable(struct timed_output_dev *dev, int runtime){struct drv2665_data *data = container_of(dev, struct drv2665_data, timed_dev);unsigned long time_ms;if (runtime > data->max_runtime_ms) {dev_dbg(&data->client->dev, "Invalid runtime\n");runtime = data->max_runtime_ms;}mutex_lock(&data->lock);hrtimer_cancel(&data->timer);data->runtime_left = runtime;if (data->runtime_left < data->time_chunk_ms)time_ms = runtime * NSEC_PER_MSEC;elsetime_ms = data->time_chunk_ms * NSEC_PER_MSEC;hrtimer_start(&data->timer, ktime_set(0, time_ms), HRTIMER_MODE_REL);schedule_work(&data->work);mutex_unlock(&data->lock);}static int drv2665_get_time(struct timed_output_dev *dev){struct drv2665_data *data = container_of(dev, struct drv2665_data, timed_dev);if (hrtimer_active(&data->timer))returndata->runtime_left +ktime_to_ms(hrtimer_get_remaining(&data->timer));return 0;}static enum hrtimer_restart drv2665_timer(struct hrtimer *timer){struct drv2665_data *data;int time_ms;data = container_of(timer, struct drv2665_data, timer);if (data->runtime_left <= data->time_chunk_ms) {data->runtime_left = 0;schedule_work(&data->work);return HRTIMER_NORESTART;}data->runtime_left -= data->time_chunk_ms;if (data->runtime_left < data->time_chunk_ms)time_ms = data->runtime_left * NSEC_PER_MSEC;elsetime_ms = data->time_chunk_ms * NSEC_PER_MSEC;hrtimer_forward_now(&data->timer, ktime_set(0, time_ms));schedule_work(&data->work);return HRTIMER_RESTART;}static int drv2665_pm(bool enable){    int nRet;    struct regulator *vreg_lvs4_1p8;    vreg_lvs4_1p8 = regulator_get(NULL, "8921_lvs4");    if(IS_ERR(vreg_lvs4_1p8)) {        nRet = PTR_ERR(vreg_lvs4_1p8);        return -EIO;    }    if(enable)        nRet = regulator_enable(vreg_lvs4_1p8);    else        nRet = regulator_disable(vreg_lvs4_1p8);    msleep(50);    if(nRet<0) {        return -EIO;    }    regulator_put(vreg_lvs4_1p8);    return nRet;}#ifdef CONFIG_PMstatic int drv2665_suspend(struct device *dev){struct drv2665_data *data = dev_get_drvdata(dev);u8 val;int rc;hrtimer_cancel(&data->timer);cancel_work_sync(&data->work);/* set standby */val = data->control2 | DRV2665_STANDBY;rc = drv2665_write_reg(data->client, DRV2665_CONTROL2_REG, val);if (rc < 0)dev_err(dev, "unable to set standby\n");/* turn regulators off */drv2665_pm(false);return 0;}static int drv2665_resume(struct device *dev){struct drv2665_data *data = dev_get_drvdata(dev);int rc;/* turn regulators on */drv2665_pm(true);if (rc < 0) {dev_err(dev, "unable to turn regulators on\n");return rc;}/* clear standby */rc = drv2665_write_reg(data->client,DRV2665_CONTROL2_REG, data->control2);if (rc < 0) {dev_err(dev, "unable to clear standby\n");goto vreg_off;}return 0;vreg_off:drv2665_pm(false);return rc;}#ifdef CONFIG_HAS_EARLYSUSPENDstatic void drv2665_early_suspend(struct early_suspend *es){struct drv2665_data *data = container_of(es, struct drv2665_data, es);drv2665_suspend(&data->client->dev);}static void drv2665_late_resume(struct early_suspend *es){struct drv2665_data *data = container_of(es, struct drv2665_data, es);drv2665_resume(&data->client->dev);}#endifstatic const struct dev_pm_ops drv2665_pm_ops = {#ifndef CONFIG_HAS_EARLYSUSPEND.suspend = drv2665_suspend,.resume = drv2665_resume,#endif};#endif//-----------------------------------------Timer end-----------------------------------------//static int __devinit drv2665_probe(struct i2c_client *client,const struct i2c_device_id *id){int status;struct drv2665_data *data;int rc;//char buf[1] = {0};drv2665_pm(true);data = kzalloc(sizeof(*data), GFP_KERNEL);if (!data) {status = -ENOMEM;dev_err(&client->dev, "%s: kzalloc failed\n", __func__);goto exit;}/* register sysfs hooks */status = sysfs_create_group(&client->dev.kobj, &drv2665_attr_group);if (status) {dev_err(&client->dev, "sysfs_create_group failed\n");goto exit_free;}/* * turn on the chip with max timeout of 20ms * save this configuration in dev->control2 */data->control2 = DRV2665_20MS_TIMEOUT << DRV2665_TIMEOUT_SHIFT;status = drv2665_write_byte(&client->dev, DRV2665_CONTROL2_REG,data->control2, "control2");if (status < 0)goto exit_sysfs;status = drv2665_write_byte(&client->dev, DRV2665_CONTROL_REG,DRV2665_GAIN, "control");if (status < 0)goto exit_sysfs;mutex_init(&data->lock);i2c_set_clientdata(client, data);#if 0if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C))    {             dev_err(&client->dev, "i2c_check_functionality failed.\n");        return -ENODEV;    }    else    {        dev_info(&client->dev, "addr=0x%02x name=%s\n",client->addr, client->name);        /* Retrieve the FW version for information purposes */        dev_info(&client->dev, "send to master, buf=%s\n",buf);        i2c_master_send(client, (char*)(&buf), 1);        dev_info(&client->dev, "recv from master, buf=%s\n",buf);        if ((!client) || (i2c_master_recv(client, (char*)(&g_FWInfo), sizeof(FWINFO)) != sizeof(FWINFO)))        {            /* Could not retrieve the FW info. Writing ? as an indication */            dev_err(&client->dev, "Could not retrieve the FW info. Writing ? as an indication\n");            sprintf(g_szFWVersion, "[FW: ?]");        }        else        {            sprintf(g_szFWVersion, "[FW: v%d.%d]", g_FWInfo.cMajorVersion, g_FWInfo.cMinorVersion);        }    }    dev_info(&client->dev, "drv2605_probe end. %s\n",g_szFWVersion);#endif//------------------Timer ------------//data->client = client;data->max_runtime_ms = DRV2665_VIB_MAX_TIME;INIT_WORK(&data->work, drv2665_worker);hrtimer_init(&data->timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);data->timer.function = drv2665_timer;data->fifo_buff[0] = DRV2665_FIFO_REG;memcpy(data->fifo_buff+1, sin_buf, DRV2665_FIFO_DEPTH-1);data->time_chunk_ms = DRV2665_FIFO_CHUNK_MS;drv2665_dump_regs(data, "new");/* register with timed output class */data->timed_dev.name = "vibrator";data->timed_dev.get_time = drv2665_get_time;data->timed_dev.enable = drv2665_enable;rc = timed_output_dev_register(&data->timed_dev);if (rc) {dev_err(&client->dev, "unable to register with timed_output\n");goto exit_sysfs;}#ifdef CONFIG_HAS_EARLYSUSPENDdata->es.level = EARLY_SUSPEND_LEVEL_BLANK_SCREEN + DRV2665_SUS_LEVEL;data->es.suspend = drv2665_early_suspend;data->es.resume = drv2665_late_resume;register_early_suspend(&data->es);#endifdev_info(&client->dev, "initialized successfully\n");return 0;exit_sysfs:sysfs_remove_group(&client->dev.kobj, &drv2665_attr_group);exit_free:kfree(data);exit:return status;}static int __devexit drv2665_remove(struct i2c_client *client){struct drv2665_data *data;data = i2c_get_clientdata(client);/* unregister sysfs hooks */sysfs_remove_group(&client->dev.kobj, &drv2665_attr_group);#ifdef CONFIG_HAS_EARLYSUSPENDunregister_early_suspend(&data->es);#endifmutex_destroy(&data->lock);timed_output_dev_unregister(&data->timed_dev);hrtimer_cancel(&data->timer);cancel_work_sync(&data->work);kfree(data);return 0;}#if 0static int drv2665_detect(struct i2c_client *client,struct i2c_board_info *info){int val;if (!i2c_check_functionality(client->adapter,I2C_FUNC_SMBUS_BYTE_DATA))return -ENODEV;val = drv2665_read_byte(&client->dev, DRV2665_CONTROL_REG, "control");if (val < 0){dev_info(&client->dev, "control error\n");return -ENODEV;}val = (val & DRV2665_ID_MASK) >> DRV2665_ID_SHIFT;if (val != DRV2665_CHIP_ID){dev_info(&client->dev, "DRV2665_CHIP_ID error\n");return -ENODEV;}elsestrlcpy(info->type, DRV2665_DRV_NAME, I2C_NAME_SIZE);return 0;}#endifstatic const struct i2c_device_id drv2665_id[] = {{ DRV2665_DRV_NAME, 0 },{ }};MODULE_DEVICE_TABLE(i2c, drv2665_id);static const unsigned short normal_i2c[] = { DRV2665_I2C_ADDRESS,I2C_CLIENT_END };static struct i2c_driver drv2665_driver = {.driver = {.name= DRV2665_DRV_NAME,.owner  = THIS_MODULE,#ifdef CONFIG_PM.pm = &drv2665_pm_ops,#endif},.id_table= drv2665_id,.probe= drv2665_probe,//.detect= drv2665_detect,.address_list= normal_i2c,.remove= __devexit_p(drv2665_remove),};static struct i2c_board_info info = {  I2C_BOARD_INFO(DRV2665_DRV_NAME, DRV2665_I2C_ADDRESS),};static int __init drv2665_init(void){    struct i2c_adapter* adapter; /* Initialized below. */    struct i2c_client* client; /* Initialized below. */        adapter = i2c_get_adapter(DEVICE_BUS);    if (adapter) {        pr_info("found device bus %d\n",DEVICE_BUS);        client = i2c_new_device(adapter, &info);        if (client) {            int retVal = i2c_add_driver(&drv2665_driver);            if (retVal) {                pr_err("drv2665_driver: Cannot add driver.\n");                return -ENODEV;            }            pr_err("added driver. retVal=%d addr=0x%02x name=%s\n",retVal, client->addr, client->name);            return retVal;        } else {            pr_err("drv2665_driver: Cannot create new device.\n");            return -ENODEV;        }    } else {        pr_err("drv2665_driver: I2C Adapter not found.\n");        return -ENODEV;    }    return -ENODEV;}static void __exit drv2665_exit(void){i2c_del_driver(&drv2665_driver);}module_init(drv2665_init);module_exit(drv2665_exit);MODULE_AUTHOR("syhost <soyudesign@gmail.com>");MODULE_DESCRIPTION("DRV2665 Piezo Haptic Driver");MODULE_LICENSE("GPL v2");




将此代码保存成ti_drv2665.c 放到drivers/misc下, 同时修改该目录下的Makefile:

#obj-$(CONFIG_PIEZO) += tspdrv/obj-$(CONFIG_PIEZO) += ti_drv2665.o

同时还需要将标准的震动驱动的config禁止掉, 在config文件里:

CONFIG_PMIC8XXX_VIBRATOR=n



此震动驱动跟泛泰A860官方的完全不一样, 可以兼容Andorid标准的震动控制路径:

/sys/class/timed_output/vibrator/enable


默认的PIEZO的震动驱动模型为(sin函数):


0 0
原创粉丝点击