OSHW-DEIMOS/SOFTWARE/A64-TERES/linux-a64/drivers/input/touchscreen/bcd/tu_ts.c
Dimitar Gamishev f9b0e7a283 linux
2017-10-13 14:07:04 +03:00

678 lines
17 KiB
C
Executable File

/* capacivite multi-touch device driver.*/
#include <linux/i2c.h>
#include <linux/input.h>
#include "tu_ts.h"
#include <linux/interrupt.h>
#include <linux/delay.h>
#include <linux/interrupt.h>
#include <linux/errno.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/init.h>
#include <linux/ioport.h>
#include <asm/irq.h>
#include <asm/io.h>
#include <linux/miscdevice.h>
#include <linux/cdev.h>
#include <linux/uaccess.h>
#include <linux/kthread.h>
#include <mach/irqs.h>
#include <mach/hardware.h>
#include <mach/sys_config.h>
#include <linux/init-input.h>
#include <linux/gpio.h>
//#define REG_DATA_DEBUG
#ifdef CONFIG_HAS_EARLYSUSPEND
#include <linux/pm.h>
#include <linux/earlysuspend.h>
#endif
#define BUF_SIZE REPORT_BUF_SIZE
#define COORD_INTERPRET(MSB_BYTE, LSB_BYTE) \
(MSB_BYTE << 8 | LSB_BYTE)
static void tu_early_suspend(struct early_suspend *h);
static void tu_late_resume(struct early_suspend *h);
static struct i2c_client *touch_i2c_client;
static struct ctp_config_info config_info = {
.input_type = CTP_TYPE,
};
static u32 debug_mask = 0;
enum{
DEBUG_INIT = 1U << 0,
DEBUG_SUSPEND = 1U << 1,
DEBUG_INT_INFO = 1U << 2,
DEBUG_X_Y_INFO = 1U << 3,
DEBUG_KEY_INFO = 1U << 4,
DEBUG_WAKEUP_INFO = 1U << 5,
DEBUG_OTHERS_INFO = 1U << 6,
};
#define dprintk(level_mask,fmt,arg...) if(unlikely(debug_mask & level_mask)) \
printk("[CTP]: "fmt, ## arg)
module_param_named(debug_mask,debug_mask,int,S_IRUGO | S_IWUSR | S_IWGRP);
static const unsigned short normal_i2c[2] = {0x5F,I2C_CLIENT_END};
// --------------------------------------------------------------
struct tu_data{
u16 x, y, w, p, id;
struct i2c_client *client;
/* capacivite device*/
struct input_dev *dev;
/* digitizer */
struct timer_list timer;
struct input_dev *dig_dev;
struct mutex lock;
struct work_struct work;
#ifdef CONFIG_HAS_EARLYSUSPEND
struct early_suspend early_suspend;
#endif
struct workqueue_struct *tu_wq;
int fw_ver;
struct miscdevice firmware;
};
///////////////////////////////////////////////
//specific tp related macro: need be configured for specific tp
//#define CTP_IRQ_NO (gpio_int_info[0].port_num)
//#define CTP_IRQ_MODE (NEGATIVE_EDGE)
#define CTP_IRQ_NUMBER (config_info.int_number)
#define CTP_IRQ_MODE (IRQF_TRIGGER_FALLING)
#define CTP_NAME TU_I2C_NAME
#define TS_RESET_LOW_PERIOD (15)
#define TS_INITIAL_HIGH_PERIOD (15)
#define TS_WAKEUP_LOW_PERIOD (100)
#define TS_WAKEUP_HIGH_PERIOD (100)
#define TS_POLL_DELAY (10) /* ms delay between samples */
#define TS_POLL_PERIOD (10) /* ms delay between samples */
#define SCREEN_MAX_X (screen_max_x)
#define SCREEN_MAX_Y (screen_max_y)
#define PRESS_MAX (255)
static struct workqueue_struct *tu_wq;
#define READ_TOUCH_ADDR_H 0x0F
#define READ_TOUCH_ADDR_L 0x40
static int screen_max_x = 0;
static int screen_max_y = 0;
static int revert_x_flag = 0;
static int revert_y_flag = 0;
static int exchange_x_y_flag = 0;
static __u32 twi_id = 0;
static int i2c_write_bytes(struct i2c_client *client, uint8_t *data, uint16_t len)
{
struct i2c_msg msg;
int ret=-1;
msg.flags = !I2C_M_RD;
msg.addr = client->addr;
msg.len = len;
msg.buf = data;
ret=i2c_transfer(client->adapter, &msg,1);
return ret;
}
static bool i2c_test(struct i2c_client * client)
{
int ret,retry;
uint8_t test_data[1] = { 0 }; //only write a data address.
for(retry=0; retry < 5; retry++)
{
ret = i2c_write_bytes(client, test_data, 1); //Test i2c.
if (ret == 1)
break;
msleep(10);
}
return ret==1 ? true : false;
}
/**
* ctp_detect - Device detection callback for automatic device creation
* return value:
* = 0; success;
* < 0; err
*/
int ctp_detect(struct i2c_client *client, struct i2c_board_info *info)
{
int ret = 0;
struct i2c_adapter *adapter = client->adapter;
if(twi_id == adapter->nr)
{
ret = i2c_test(client);
if(!ret){
printk("%s:I2C connection might be something wrong \n",__func__);
return -ENODEV;
}else{
dprintk(DEBUG_INIT,"***CTP*** I2C connection sucess !\n");
strlcpy(info->type, CTP_NAME, I2C_NAME_SIZE);
return 0;
}
}else{
return -ENODEV;
}
}
/**
* ctp_wakeup - function
*
*/
int ctp_wakeup(int status,int ms)
{
dprintk(DEBUG_INIT,"***CTP*** %s:status:%d,ms = %d\n",__func__,status,ms);
if (status == 0) {
if(ms == 0) {
__gpio_set_value(config_info.wakeup_gpio.gpio, 0);
}else {
__gpio_set_value(config_info.wakeup_gpio.gpio, 0);
msleep(ms);
__gpio_set_value(config_info.wakeup_gpio.gpio, 1);
}
}
if (status == 1) {
if(ms == 0) {
__gpio_set_value(config_info.wakeup_gpio.gpio, 1);
}else {
__gpio_set_value(config_info.wakeup_gpio.gpio, 1);
msleep(ms);
__gpio_set_value(config_info.wakeup_gpio.gpio, 0);
}
}
msleep(5);
return 0;
}
/**
* ctp_print_info - sysconfig print function
* return value:
*
*/
void ctp_print_info(struct ctp_config_info info,int debug_level)
{
if(debug_level == DEBUG_INIT)
{
printk("info.ctp_used:%d\n",info.ctp_used);
printk("info.twi_id:%d\n",info.twi_id);
printk("info.screen_max_x:%d\n",info.screen_max_x);
printk("info.screen_max_y:%d\n",info.screen_max_y);
printk("info.revert_x_flag:%d\n",info.revert_x_flag);
printk("info.revert_y_flag:%d\n",info.revert_y_flag);
printk("info.exchange_x_y_flag:%d\n",info.exchange_x_y_flag);
printk("info.irq_gpio_number:%d\n",info.irq_gpio.gpio);
printk("info.wakeup_gpio_number:%d\n",info.wakeup_gpio.gpio);
}
}
unsigned int kbc_i2c_read_reg(unsigned int reg);
unsigned int kbc_i2c_write_reg(unsigned int reg,unsigned int value);
unsigned int kbc_i2c_read_reg(unsigned int reg)
{
return i2c_smbus_read_byte_data(touch_i2c_client, reg);
}
unsigned int kbc_i2c_write_reg(unsigned int reg,unsigned int value)
{
return i2c_smbus_write_byte_data(touch_i2c_client, reg, value);
}
irqreturn_t tu_irq(int irq, void *dev_id)
{
int ret = 0;
struct tu_data *tu = (struct tu_data *)dev_id;
dprintk(DEBUG_INT_INFO,"==========tu_ts Interrupt============\n");
ret = input_set_int_enable(&(config_info.input_type), 0);
if (ret < 0)
dprintk(DEBUG_INT_INFO,"%s irq disable failed\n", __func__);
queue_work(tu_wq, &tu->work);
return 0;
}
static inline void tu_report(struct tu_data *tu)
{
dprintk(DEBUG_X_Y_INFO,"source data:ID:%d,x=%d,y=%d,w=%d\n", tu->id,tu->x,tu->y,tu->w);
if(1 == exchange_x_y_flag){
swap(tu->x, tu->y);
}
if(1 == revert_x_flag){
tu->x = SCREEN_MAX_X - tu->x;
}
if(1 == revert_y_flag){
tu->y= SCREEN_MAX_Y - tu->y;
}
dprintk(DEBUG_X_Y_INFO," report data:ID:%d,x=%d,y=%d,w=%d\n",tu->id,tu->x,tu->y,tu->w);
input_report_abs(tu->dev, ABS_MT_POSITION_X, tu->x);
input_report_abs(tu->dev, ABS_MT_POSITION_Y, tu->y);
input_report_abs(tu->dev, ABS_MT_TOUCH_MAJOR, tu->w);
input_report_abs(tu->dev, ABS_MT_WIDTH_MAJOR, tu->w);
input_report_abs(tu->dev, ABS_MT_TRACKING_ID, tu->id);
input_mt_sync(tu->dev);
}
static void tu_i2c_work(struct work_struct *work)
{
int i = 0;
u_int8_t ret = 0;
u_int8_t idx_x_low;
u_int8_t idx_x_hi;
u_int8_t idx_y_low;
u_int8_t idx_y_hi;
u_int8_t idx_id_st;
u_int8_t touchcnt;
u_int8_t touchstatus;
u8 read_buf[REPORT_BUF_SIZE+1]={0};
#ifdef HAVE_TOUCH_KEY
static unsigned int prev_key = 0;
#endif
struct tu_data *tu = container_of(work, struct tu_data, work);
ret = i2c_smbus_read_i2c_block_data(tu->client, 0, BUF_SIZE,read_buf);
if(ret < 0)
{
printk("Read error!!!!!\n");
goto report_work_fail;
}
#ifdef REG_DATA_DEBUG
printk("\nRead: ");
for(i = 0; i < BUF_SIZE; i ++)
{
printk("%4x", read_buf[i]);
}
printk("\n");
#endif
if (read_buf[TU_RMOD] == 0xb1) //Point Report
{
touchcnt = read_buf[TU_POINTS] & 0x0f;
dprintk(DEBUG_OTHERS_INFO,"touchcnt = %d !!\n",touchcnt);
if(touchcnt>5)
goto report_work_fail;
touchstatus = read_buf[TU_1_ID_STATUS];
if( touchstatus==0x00)
{
dprintk(DEBUG_OTHERS_INFO,"touch up !!\n");
input_report_abs(tu->dev, ABS_MT_TOUCH_MAJOR, 0);
input_report_abs(tu->dev, ABS_MT_WIDTH_MAJOR, 0);
input_mt_sync(tu->dev);
}
else
{
idx_x_low = TU_1_POS_X_LOW;
idx_x_hi = TU_1_POS_X_HI;
idx_y_low = TU_1_POS_Y_LOW;
idx_y_hi = TU_1_POS_Y_HI;
idx_id_st = TU_1_ID_STATUS;
for( i=0; i<touchcnt; i++ )
{
tu->x = COORD_INTERPRET(read_buf[idx_x_hi], read_buf[idx_x_low]);
tu->y = COORD_INTERPRET(read_buf[idx_y_hi], read_buf[idx_y_low]);
//tu->w = (read_buf[idx_id_st]&0x0f);
tu->w = 10;
tu->id = (read_buf[idx_id_st]>>4)&0x0f;
tu->x = (tu->x * SCREEN_MAX_X) /AA_X_SIZE;
tu->y = (tu->y * SCREEN_MAX_Y ) /AA_Y_SIZE;
tu_report(tu);
idx_x_low += MAX_POINT_SIZE;
idx_x_hi += MAX_POINT_SIZE;
idx_y_low += MAX_POINT_SIZE;
idx_y_hi += MAX_POINT_SIZE;
idx_id_st += MAX_POINT_SIZE;
}
}
} else if(read_buf[TU_RMOD] == 0x00){
dprintk(DEBUG_OTHERS_INFO,"touch up !!\n");
input_report_abs(tu->dev, ABS_MT_TOUCH_MAJOR, 0);
input_report_abs(tu->dev, ABS_MT_WIDTH_MAJOR, 0);
input_mt_sync(tu->dev);
}
#ifdef HAVE_TOUCH_KEY
else if (read_buf[TU_RMOD] == 0xb2) //Key Detect
{
dprintk(DEBUG_KEY_INFO,"tu: have touch key !\n");
switch (read_buf[TU_KEY_CODE])
{
case TOUCH_KEY_HOME:
input_event(tu->dev, EV_KEY, KEY_HOME, !!read_buf[TU_KEY_CODE]);
prev_key = KEY_HOME;
break;
case TOUCH_KEY_BACK:
input_event(tu->dev, EV_KEY, KEY_BACK, !!read_buf[TU_KEY_CODE]);
prev_key = KEY_BACK;
break;
case TOUCH_KEY_MENU:
input_event(tu->dev, EV_KEY, KEY_MENU, !!read_buf[TU_KEY_CODE]);
prev_key = KEY_MENU;
break;
case TOUCH_KEY_REL:
input_event(tu->dev, EV_KEY, prev_key, !!read_buf[TU_KEY_CODE]);
break;
case TOUCH_KEY_VOL_UP:
input_event(tu->dev, EV_KEY, KEY_VOLUMEUP, !!read_buf[TU_KEY_CODE]);
prev_key = KEY_VOLUMEUP;
break;
case TOUCH_KEY_VOL_DOWN:
input_event(tu->dev, EV_KEY, KEY_VOLUMEDOWN, !!read_buf[TU_KEY_CODE]);
prev_key = KEY_VOLUMEDOWN;
break;
case TOUCH_KEY_CALL:
input_event(tu->dev, EV_KEY, KEY_SEND, !!read_buf[TU_KEY_CODE]);
prev_key = KEY_SEND;
break;
default:
dev_dbg(&tu->client->dev, "Unknown Android Key %02x", read_buf[TU_KEY_CODE]);
break;
}
}
#endif
input_sync(tu->dev);
dprintk(DEBUG_OTHERS_INFO,"in tu_i2c_work!!!!!\n");
report_work_fail:
ret = input_set_int_enable(&(config_info.input_type), 1);
if (ret < 0)
dprintk(DEBUG_SUSPEND,"%s irq enable failed\n", __func__);
}
static int tu_probe(struct i2c_client *client, const struct i2c_device_id *ids)
{
int err = 0;
struct tu_data *tu;
struct input_dev *input_dev;
dprintk(DEBUG_INIT,"====%s begin=====. \n", __func__);
tu = kzalloc(sizeof(struct tu_data), GFP_KERNEL);
if (!tu)
return -ENOMEM;
tu->client = client;
touch_i2c_client=client;
dev_info(&tu->client->dev, "device probing\n");
i2c_set_clientdata(client, tu);
mutex_init(&tu->lock);
//Open Device Node for APK Update
tu->firmware.minor = MISC_DYNAMIC_MINOR;
tu->firmware.name = "TU_Update";
tu->firmware.mode = S_IRWXUGO;
if (misc_register(&tu->firmware) < 0)
printk("[tu]misc_register failed!!");
else
printk("[tu]misc_register finished!!");
/* allocate input device for capacitive */
input_dev = input_allocate_device();
if (!input_dev) {
dev_err(&tu->client->dev, "failed to allocate input device \n");
goto exit_kfree;
}
//Initial Device Parameters
input_dev->name = CTP_NAME;
input_dev->phys = "input/tu-ts";
input_dev->id.bustype = BUS_I2C;
input_dev->id.vendor = 0x0EFF;
input_dev->id.product = 0x0020;
tu->dev = input_dev;
//Key bit initialize
input_dev->evbit[0] = BIT_MASK(EV_SYN) | BIT_MASK(EV_ABS) |BIT_MASK(EV_KEY);
input_dev->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH);
#ifdef HAVE_TOUCH_KEY
set_bit(KEY_BACK, input_dev->keybit);
set_bit(KEY_MENU, input_dev->keybit);
set_bit(KEY_HOME, input_dev->keybit);
set_bit(KEY_VOLUMEUP, input_dev->keybit);
set_bit(KEY_VOLUMEDOWN, input_dev->keybit);
set_bit(KEY_SEND, input_dev->keybit);
#endif
set_bit(INPUT_PROP_DIRECT, input_dev->propbit);
//Set Coordinate
input_set_abs_params(input_dev, ABS_MT_TOUCH_MAJOR, 0, 8, 0, 0);
input_set_abs_params(input_dev, ABS_MT_WIDTH_MAJOR, 0, 8, 0, 0);
input_set_abs_params(input_dev, ABS_MT_TRACKING_ID, 0, 4, 0, 0);
input_set_abs_params(input_dev, ABS_PRESSURE, 0, 256, 0, 0);
input_set_abs_params(input_dev, ABS_MT_POSITION_X, 0, SCREEN_MAX_X, 0, 0); //896
input_set_abs_params(input_dev, ABS_MT_POSITION_Y, 0, SCREEN_MAX_Y, 0, 0); //576
//Register Device Object
err = input_register_device(input_dev);
if (err)
goto exit_input;
//initialize I2C Work
INIT_WORK(&tu->work, tu_i2c_work);
tu_wq = create_singlethread_workqueue("tu_wq");
if (!tu_wq) {
printk(KERN_ALERT "Creat %s workqueue failed.\n", __func__);
return -ENOMEM;
}
flush_workqueue(tu_wq);
config_info.dev = &(tu->dev->dev);
err = input_request_int(&(config_info.input_type), tu_irq,CTP_IRQ_MODE, tu);
if (err) {
pr_info( "tu_probe: request irq failed\n");
goto exit_irq_request_failed;
}
#ifdef CONFIG_HAS_EARLYSUSPEND
tu->early_suspend.level = EARLY_SUSPEND_LEVEL_BLANK_SCREEN + 1;
tu->early_suspend.suspend = tu_early_suspend;
tu->early_suspend.resume = tu_late_resume;
register_early_suspend(&tu->early_suspend);
#endif
return 0;
exit_irq_request_failed:
input_free_int(&(config_info.input_type), tu);
exit_input:
input_unregister_device(tu->dev);
exit_kfree:
kfree(tu);
return err;
}
static int __devexit tu_remove(struct i2c_client *client)
{
struct tu_data *tu = i2c_get_clientdata(client);
#ifdef CONFIG_HAS_EARLYSUSPEND
unregister_early_suspend(&tu->early_suspend);
#endif
input_free_int(&(config_info.input_type), tu);
flush_workqueue(tu_wq);
if (tu_wq)
destroy_workqueue(tu_wq);
misc_deregister(&tu->firmware);
input_unregister_device(tu->dev);
kfree(tu);
return 0;
}
static int tu_suspend(struct i2c_client *client, pm_message_t state)
{
int ret;
struct tu_data *ts = i2c_get_clientdata(touch_i2c_client);
printk("[CTP]: %s suspend!\n",__func__);
ret = input_set_int_enable(&(config_info.input_type), 0);
if (ret < 0)
dprintk(DEBUG_SUSPEND,"%s irq disable failed\n", __func__);
ret = cancel_work_sync(&ts->work);
ret = i2c_smbus_write_i2c_block_data(touch_i2c_client, 0, 4, command_list[0]);
if (ret < 0)
printk(KERN_ERR "tu_suspend: send i2c data failed\n");
return 0;
}
static int tu_resume(struct i2c_client *client)
{
int ret;
printk("[CTP]: %s resume!\n",__func__);
ret = input_set_int_enable(&(config_info.input_type), 1);
if (ret < 0)
dprintk(DEBUG_SUSPEND,"%s irq enable failed\n", __func__);
ret = i2c_smbus_write_i2c_block_data(touch_i2c_client, 0, 4, command_list[1]);
if (ret < 0)
printk(KERN_ERR "tu_resume: send i2c data failed\n");
return 0;
}
#ifdef CONFIG_HAS_EARLYSUSPEND
static void tu_early_suspend(struct early_suspend *h)
{
dprintk(DEBUG_SUSPEND,"\ntu_early_suspend\n");
tu_suspend(touch_i2c_client, PMSG_SUSPEND);
}
static void tu_late_resume(struct early_suspend *h)
{
dprintk(DEBUG_WAKEUP_INFO,"\tu_late_resume\n");
tu_resume(touch_i2c_client);
}
#endif
static struct i2c_device_id tu_id_table[] = {
{CTP_NAME,0 },
{ }
};
static struct i2c_driver tu_driver = {
.class = I2C_CLASS_HWMON,
.probe = tu_probe,
.remove = tu_remove,
#ifdef CONFIG_HAS_EARLYSUSPEND
#else
#ifdef CONFIG_PM
.suspend = tu_suspend,
.resume = tu_resume,
#endif
#endif
.id_table = tu_id_table,
.driver = {
.name = CTP_NAME,
.owner = THIS_MODULE,
},
.address_list = normal_i2c,
};
static int ctp_get_system_config(void)
{
ctp_print_info(config_info,DEBUG_INIT);
twi_id = config_info.twi_id;
screen_max_x = config_info.screen_max_x;
screen_max_y = config_info.screen_max_y;
revert_x_flag = config_info.revert_x_flag;
revert_y_flag = config_info.revert_y_flag;
exchange_x_y_flag = config_info.exchange_x_y_flag;
if((screen_max_x == 0) || (screen_max_y == 0)){
printk("%s:read config error!\n",__func__);
return 0;
}
return 1;
}
static int __init tu_init(void)
{
int ret = -1;
dprintk(DEBUG_INIT,"***************************init begin*************************************\n");
if (input_fetch_sysconfig_para(&(config_info.input_type))) {
printk("%s: ctp_fetch_sysconfig_para err.\n", __func__);
return 0;
} else {
ret = input_init_platform_resource(&(config_info.input_type));
if (0 != ret) {
printk("%s:init_platform_resource err. \n", __func__);
}
}
if(config_info.ctp_used == 0){
printk("*** ctp_used set to 0 !\n");
printk("*** if use ctp,please put the sys_config.fex ctp_used set to 1. \n");
return 0;
}
if(!ctp_get_system_config()){
printk("%s:read config fail!\n",__func__);
return ret;
}
ctp_wakeup(0, 50);
tu_driver.detect = ctp_detect,
ret = i2c_add_driver(&tu_driver);
return ret;
}
static void tu_exit(void)
{
printk("[CTP]: ctp driver exit !! %s\n",__func__);
i2c_del_driver(&tu_driver);
input_free_platform_resource(&(config_info.input_type));
}
late_initcall(tu_init);
module_exit(tu_exit);
MODULE_DESCRIPTION("TU Touchscreen Driver");
MODULE_LICENSE("GPL v2");