最近接手了一块电阻屏,用了TI的TSC2007进行信号转换。
接口是 I2C, 还有一个中断屏,工作原理比较简单:发送一个8BIT命令(例如读X),然后接收16BIT的返回值。
整个驱动参考了TI的源码 以及 TI社区的讨论。
/*
* drivers/input/touchscreen/tsc2007.c
*
* Copyright (c) 2018 Melo
* Melo Fang
*
* Modified the TSC2007 driver from:
* Copyright (c) 2014 Aprilaire (Research Products Corporation)
* Santhosh Ramani
*
* Using code from:
* - ads7846.c
* Copyright (c) 2005 David Brownell
* Copyright (c) 2006 Nokia Corporation
* - corgi_ts.c
* Copyright (C) 2004-2005 Richard Purdie
* - omap_ts.[hc], ads7846.h, ts_osk.c
* Copyright (C) 2002 MontaVista Software
* Copyright (C) 2004 Texas Instruments
* Copyright (C) 2005 Dirk Behme
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
/*
* This code has been modified to pickup falling and rising edge of
* the PENIRQ. Once the falling edge is detected,
* Interrupt is disabled
* Touch is sampled
* And upon PENIRQ release (high) interrupt is re-enabled
tsc2007_xfer(ts, PWRDOWN);
*
* Modified poll-period: to sample delay (because is delays sampling)
*
* Added report-delay: this prevents reporting incorrect touch events
* (picked up during the pen-up event at the time of sampling)
*
* Added initial-delay: adds some delay before the first sample
*
* Technically, polling period is now (report_delay + sample_delay)
*/
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define PINCTRL_STATE_ACTIVE "pmx_rtp_active"
#define PINCTRL_STATE_SUSPEND "pmx_rtp_suspend"
#define I2C_VTG_MIN_UV **00000
#define I2C_VTG_MAX_UV **00000
#define VDD_MIN_UV **00000
#define VDD_MAX_UV **00000
#define TSC_DRIVER_NAME "tsc2007"
extern struct kobject *rtp_kobj;
static int rtp_cal[7] = {1,0,0,0,1,0,1};
struct tsc2007_dev *misc_ts = NULL;
static bool vcci2c_enabled = false;
static bool vdd_enabled = false;
static bool isSuspend = 0;
static bool cal_mode = 1;
enum _tsc2007_power_control {
POWER_OFF,
POWER_ON,
};
static unsigned int pointx[5] = {*, **, **, **, **};
static unsigned int pointy[12] = {*, ..};
//Use pointx pointy to convert the coord to the key
static int old_key = 0;
#define MAX_RTP_BUTTON_NUM 60
static u32 RTP_BUTTON_MAPPING_KEY[MAX_RTP_BUTTON_NUM] = {
60 个 KEY
};
#define RTP_DEBUG 1
static int m_rtp_debug_mode = RTP_DEBUG;
#define tsc2007_debug_msg(fmt, args...) \
if (m_rtp_debug_mode) \
printk(KERN_WARNING "tsc2007 : [%-18s:%5d]" fmt, \
__func__, __LINE__, ## args);
#define tsc2007_printk(fmt, args...) \
printk(KERN_WARNING "tsc2007 : [%-18s:%5d]" fmt, \
__func__, __LINE__, ## args);
#define tsc2007_err(fmt, args...) \
printk(KERN_ERR "tsc2007 : [%-18s:%5d]" fmt, \
__func__, __LINE__, ## args);
static ssize_t tsc2007_vendor_show(struct device *dev, struct device_attribute *attr,char *buf);
static ssize_t tsc2007_chipvendor_show(struct device *dev, struct device_attribute *attr,char *buf);
static ssize_t tsc2007_chip_show(struct device *dev, struct device_attribute *attr,char *buf);
static ssize_t tsc2007_interface_show(struct device *dev, struct device_attribute *attr,char *buf);
static ssize_t tsc2007_power_show(struct device *dev, struct device_attribute *attr,char *buf);
static ssize_t tsc2007_calibration_show(struct device *dev, struct device_attribute *attr,char *buf);
static ssize_t tsc2007_enable_show(struct device *dev, struct device_attribute *attr,char *buf);
static ssize_t tsc2007_enable_store(struct device *dev, struct device_attribute *attr,const char *buf,size_t count);
static ssize_t tsc2007_calmode_show(struct device *dev, struct device_attribute *attr,char *buf);
static ssize_t tsc2007_calmode_store(struct device *dev, struct device_attribute *attr,const char *buf,size_t count);
static ssize_t tsc2007_debug_show(struct device *dev, struct device_attribute *attr,char *buf);
static ssize_t tsc2007_debug_store(struct device *dev, struct device_attribute *attr,const char *buf,size_t count);
static DEVICE_ATTR(vendor, S_IRUGO, tsc2007_vendor_show, NULL);
static DEVICE_ATTR(chipvendor, S_IRUGO, tsc2007_chipvendor_show, NULL);
static DEVICE_ATTR(chip, S_IRUGO, tsc2007_chip_show, NULL);
static DEVICE_ATTR(interface, S_IRUGO, tsc2007_interface_show, NULL);
static DEVICE_ATTR(power, S_IRUGO, tsc2007_power_show, NULL);
static DEVICE_ATTR(calibration, S_IRUGO, tsc2007_calibration_show, NULL);
static DEVICE_ATTR(enable, S_IRUGO|S_IWUGO, tsc2007_enable_show, tsc2007_enable_store);
static DEVICE_ATTR(calmode, S_IRUGO|S_IWUGO, tsc2007_calmode_show, tsc2007_calmode_store);
static DEVICE_ATTR(debug, S_IRUGO|S_IWUGO, tsc2007_debug_show, tsc2007_debug_store);
static struct attribute * tsc2007_touch_prop_attrs[] = {
&dev_attr_vendor.attr,
&dev_attr_chipvendor.attr,
&dev_attr_chip.attr,
&dev_attr_interface.attr,
&dev_attr_power.attr,
&dev_attr_calibration.attr,
&dev_attr_enable.attr,
&dev_attr_calmode.attr,
&dev_attr_debug.attr,
NULL
};
static struct attribute_group tsc2007_touch_prop_attr_group = {
.attrs = tsc2007_touch_prop_attrs,
};
struct ts_event {
u16 x;
u16 y;
u16 z1, z2;
};
struct tsc2007_dev {
struct input_dev *input;
char phys[32];
struct i2c_client *client;
u16 model;
u16 x_plate_ohms;
u16 max_rt;
unsigned long sample_delay;
unsigned long report_delay;
unsigned long initial_delay;
int fuzzx;
int fuzzy;
int fuzzz;
unsigned gpio;
int irq;
wait_queue_head_t wait;
bool stopped;
int (*get_pendown_state)(struct device *);
void (*clear_penirq)(void);
struct notifier_block fb_notif;
struct regulator *vcc_i2c;
struct regulator *vdd;
struct pinctrl *rtp_pinctrl;
struct pinctrl_state *pinctrl_state_active;
struct pinctrl_state *pinctrl_state_suspend;
};
/*
//
// _ooOoo_
// o8888888o
// 88" . "88
// (| -_- |)
// O\ = /O
// ____/`---'\____
// .' \\| |// `.
// / \\||| : |||// \
// / _||||| -:- |||||- \
// | | \\\ - /// | |
// | \_| ''\---/'' | |
// \ .-\__ `-` ___/-. /
// ___`. .' /--.--\ `. . __
// ."" '< `.___\_<|>_/___.' >'"".
// | | : `- \`.;`\ _ /`;.`/ - ` : | |
// \ \ `-. \_ __\ /__ _/ .-` / /
// ======`-.____`-.___\_____/___.-`____.-'======
// `=---='
// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
// Buddha Bless, No Bug !
**************************************************************/
static inline int tsc2007_xfer(struct tsc2007_dev *ts, u8 cmd)
{
u16 data;
u16 val;
data = i2c_smbus_read_word_data(ts->client, cmd);
if (data < 0) {
tsc2007_debug_msg("i2c io error: %d\n\n\n\n", data);
return data;
}
/* The protocol and raw data format from i2c interface:
* S Addr Wr [A] Comm [A] S Addr Rd [A] [DataLow] A [DataHigh] NA P
* Where DataLow has [D11-D4], DataHigh has [D3-D0 << 4 | Dummy 4bit].
*/
val = swab16(data) >> 4;
udelay(50);
tsc2007_debug_msg("data: 0x%x, val: 0x%x\n", data, val);
return val;
}
static void tsc2007_read_values(struct tsc2007_dev *tsc, struct ts_event *tc)
{
int x,y;
/* y- still on; turn on only y+ (and ADC) */
tc->y = tsc2007_xfer(tsc, READ_Y);
/* turn y- off, x+ on, then leave in lowpower */
tc->x = tsc2007_xfer(tsc, READ_X);
/* turn y+ off, x- on; we'll use formula #1 */
tc->z1 = tsc2007_xfer(tsc, READ_Z1);
tc->z2 = tsc2007_xfer(tsc, READ_Z2);
tsc2007_debug_msg("[old]x = %d y = %d z1 = %d z2 = %d\n", tc->x, tc->y, tc->z1, tc->z2);
/* calibration coord */
if(rtp_cal[6])
{
x = (tc->x * rtp_cal[0] + tc->y * rtp_cal[1] + rtp_cal[2])/rtp_cal[6];
if(x < 0)
x = 0;
y = (tc->x * rtp_cal[3] + tc->y * rtp_cal[4] + rtp_cal[5])/rtp_cal[6];
if(y < 0)
y = 0;
tc->x = x;
tc->y = y;
}
/* Prepare for next touch reading - power down ADC, enable PENIRQ */
tsc2007_xfer(tsc, PWRDOWN);
}
static u32 tsc2007_calculate_pressure(struct tsc2007_dev *tsc, struct ts_event *tc)
{
u32 rt = 0;
/* range filtering */
if (tc->x == MAX_12BIT)
tc->x = 0;
if (likely(tc->x && tc->z1)) {
/* compute touch pressure resistance using equation #1 */
rt = tc->z2 - tc->z1;
rt *= tc->x;
rt *= tsc->x_plate_ohms;
rt /= tc->z1;
rt = (rt + 2047) >> 12;
}
return rt;
}
static bool tsc2007_is_pen_down(struct tsc2007_dev *ts)
{
/*
* NOTE: We can't rely on the pressure to determine the pen down
* state, even though this controller has a pressure sensor.
* The pressure value can fluctuate for quite a while after
* lifting the pen and in some cases may not even settle at the
* expected value.
*
* The only safe way to check for the pen up condition is in the
* work function by reading the pen signal state (it's a GPIO
* and IRQ). Unfortunately such callback is not always available,
* in that case we assume that the pen is down and expect caller
* to fall back on the pressure reading.
*/
if (!ts->get_pendown_state)
return true;
return ts->get_pendown_state(&ts->client->dev);
}
static void input_convert_to_key(struct tsc2007_dev *ts, struct ts_event *tc)
{
struct input_dev *input = ts->input;
int fx,fy,i;
for(i=0; i<5; i++)
{
if(tc->x >= pointx[i] && tc->x <= pointx[i]+48)
fx = i;
}
for(i=0; i<12; i++)
{
if(tc->y >= pointy[i] && tc->y <= pointy[i]+48)
fy = i;
}
if(fx == 5 || fy == 12) {
tsc2007_debug_msg("invaild area\n");
if(old_key)
{
input_report_key(input, old_key, 0);
old_key = 0;
}
return;
}
i = RTP_BUTTON_MAPPING_KEY[fy *5 + fx];
tsc2007_debug_msg("coord[%d,%d] convert to key:%d \r\n",
tc->x, tc->y, i);
if(i == old_key)
return;
if(old_key)
input_report_key(input, old_key, 0);
input_report_key(input, i, 1);
input_sync(input);
old_key = i;
}
static irqreturn_t tsc2007_soft_irq(int irq, void *handle)
{
struct tsc2007_dev *ts = handle;
struct input_dev *input = ts->input;
struct ts_event tc;
u32 rt;
/*
* With some panels we need to wait a bit otherwise the first value
* is often wrong. (Allows the touch screen to settle)
*/
//pm_stay_awake(&(ts->client->dev));
if (ts->initial_delay > 0)
msleep(ts->initial_delay);
while (!ts->stopped && tsc2007_is_pen_down(ts)) {
if(isSuspend){
input_report_key(input, KEY_HOMEPAGE, 1);
input_sync(input);
input_report_key(input, KEY_HOMEPAGE, 0);
input_sync(input);
//isSuspend = 0;
msleep(10);
}
/* pen is down, continue with the measurement */
tsc2007_read_values(ts, &tc);
rt = tsc2007_calculate_pressure(ts, &tc);
if (!rt && !ts->get_pendown_state) {
/*
* If pressure reported is 0 and we don't have
* callback to check pendown state, we have to
* assume that pen was lifted up.
*/
break;
}
/*
Before reporting the new reading, check to make sure that
the pen is still down. This ensures that readings taken
during Pen-up event are ignored.
*/
wait_event_timeout(ts->wait, ts->stopped, msecs_to_jiffies(ts->report_delay));
if (ts->stopped || (!tsc2007_is_pen_down(ts)))
break;
if (rt <= ts->max_rt) {
tsc2007_printk("DOWN point(%4d,%4d), pressure (%4u)\n",
tc.x, tc.y, rt);
if(cal_mode) {
input_report_key(input, BTN_TOUCH, 1);
input_report_abs(input, ABS_X, tc.x);
input_report_abs(input, ABS_Y, tc.y);
input_report_abs(input, ABS_PRESSURE, rt);
input_sync(input);
} else {
input_convert_to_key(ts,&tc);
}
} else {
/*
* Sample found inconsistent by debouncing or pressure is
* beyond the maximum. Don't report it to user space,
* repeat at least once more the measurement.
*/
tsc2007_debug_msg("ignored pressure %d\n", rt);
}
wait_event_timeout(ts->wait, ts->stopped, msecs_to_jiffies(ts->sample_delay));
}
tsc2007_debug_msg("Finger UP\n");
if(cal_mode) {
input_report_key(input, BTN_TOUCH, 0);
input_report_abs(input, ABS_PRESSURE, 0);
input_sync(input);
} else {
if(old_key) {
input_report_key(input, old_key, 0);
input_sync(input);
old_key = 0;
}
}
if (ts->clear_penirq)
ts->clear_penirq();
/* Now that the touch has been handled, re-enabled the PENIRQ */
enable_irq(ts->irq);
// pm_relax(&(ts->client->dev));
return IRQ_HANDLED;
}
static irqreturn_t tsc2007_hard_irq(int irq, void *handle)
{
struct tsc2007_dev *ts = handle;
tsc2007_debug_msg("tsc2007 hard irq\n");
if (tsc2007_is_pen_down(ts)) {
/* Once the IRQ handling starts, disable the interrupt */
disable_irq_nosync(ts->irq);
return IRQ_WAKE_THREAD;
}
if (ts->clear_penirq) {
ts->clear_penirq();
}
return IRQ_HANDLED;
}
static void tsc2007_power_control(struct tsc2007_dev *ts, u8 ctl)
{
int ret;
if(ctl == POWER_OFF) {
if(vdd_enabled)
{
ret = regulator_disable(ts->vdd);
if(ret){
tsc2007_printk("Regulator vdd disable failed.\r\n");
}
else
{
vdd_enabled = false;
}
}
if(vcci2c_enabled)
{
ret = regulator_disable(ts->vcc_i2c);
if(ret){
tsc2007_printk("Regulator vcc_i2c disable failed.\r\n");
}
else
{
vcci2c_enabled = false;
}
}
} else if (ctl == POWER_ON) {
if(!vdd_enabled)
{
ret = regulator_enable(ts->vdd);
if(ret){
tsc2007_printk("Regulator vdd enable failed.\r\n");
}
else
{
vdd_enabled = true;
}
}
if(!vcci2c_enabled)
{
ret = regulator_enable(ts->vcc_i2c);
if(ret){
tsc2007_printk("Regulator vcc_i2c enable failed.\r\n");
}
else
{
vcci2c_enabled = true;
}
}
}
}
static void tsc2007_stop(struct tsc2007_dev *ts)
{
if(ts->stopped == false) {
ts->stopped = true;
mb();
wake_up(&ts->wait);
disable_irq(ts->irq);
}
tsc2007_debug_msg("tsc2007_stop\n");
}
static int tsc2007_open(struct input_dev *input_dev)
{
struct tsc2007_dev *ts = input_get_drvdata(input_dev);
int err;
struct ts_event tc;
tsc2007_debug_msg("tsc2007_open\n");
if(ts->stopped == true) {
ts->stopped = false;
mb();
enable_irq(ts->irq);
/* Prepare for touch readings - power down ADC and enable PENIRQ */
err = tsc2007_xfer(ts, PWRDOWN);
if (err < 0) {
tsc2007_stop(ts);
tsc2007_err("tsc2007_open err: xfer PWRDOWN\n");
return err;
}
msleep(ts->initial_delay);
}
return 0;
}
static void tsc2007_close(struct input_dev *input_dev)
{
struct tsc2007_dev *ts = input_get_drvdata(input_dev);
tsc2007_stop(ts);
}
static int tsc2007_resume(struct device *dev)
{
struct tsc2007_dev *ts = misc_ts;
if(ts == NULL)
return -1;
isSuspend = 0;
tsc2007_printk("tsc2007_resume\r\n");
tsc2007_open(ts->input);
return 0;
}
static int tsc2007_suspend(struct device *dev)
{
struct tsc2007_dev *ts = misc_ts;
if(ts == NULL)
return -1;
isSuspend = 1;
tsc2007_printk("ts2007_suspend\r\n");
tsc2007_stop(ts);
return 0;
}
static const struct dev_pm_ops tsc2007_pm_ops = {
#if (!defined(CONFIG_FB))
.suspend = tsc2007_suspend;
.resume = tsc2007_resume;
#endif
};
static int fb_notifier_callback(struct notifier_block *self,
unsigned long event, void *data)
{
struct fb_event *evdata = data;
int *blank;
struct tsc2007_dev *ts = container_of(self, struct tsc2007_dev, fb_notif);
tsc2007_debug_msg("fb_notifier_callback\r\n");
if(evdata && evdata->data && event == FB_EVENT_BLANK &&
ts && ts->client) {
blank = evdata->data;
if(*blank == FB_BLANK_UNBLANK)
tsc2007_resume(&ts->client->dev);
else if(*blank == FB_BLANK_POWERDOWN)
tsc2007_suspend(&ts->client->dev);
}
return 0;
}
//******************************************************************
//sysfs interface
static ssize_t tsc2007_vendor_show(struct device *dev, struct device_attribute *attr, char *buf)
{
return sprintf(buf,"YOUNG FAST\n");
}
static ssize_t tsc2007_chipvendor_show(struct device *dev, struct device_attribute *attr, char *buf)
{
return sprintf(buf,"TI\n");
}
static ssize_t tsc2007_chip_show(struct device *dev, struct device_attribute *attr, char *buf)
{
return sprintf(buf,"tsc2007\n");
}
static ssize_t tsc2007_interface_show(struct device *dev, struct device_attribute *attr, char *buf)
{
return sprintf(buf,"I2C\n");
}
static ssize_t tsc2007_power_show(struct device *dev, struct device_attribute *attr, char *buf)
{
return sprintf(buf,"I/O Power 1v8\nVDD Power3v3\n");
}
static ssize_t tsc2007_calibration_show(struct device *dev, struct device_attribute *attr, char *buf)
{
mm_segment_t old_fs = {0};
int nread = 0;
char buff[100];
struct file *rtp_filp = NULL;
int i=0,j=0,flag =0;
old_fs = get_fs();
set_fs(get_fs());
rtp_filp = filp_open("data/etc/pointercal", O_RDONLY, 0);
if(IS_ERR(rtp_filp)) {
tsc2007_err("rtp calibration file open error[%d]\r\n",(int)rtp_filp);
rtp_cal[0] = 1;
rtp_cal[1] = 0;
rtp_cal[2] = 0;
rtp_cal[3] = 0;
rtp_cal[4] = 1;
rtp_cal[5] = 0;
rtp_cal[6] = 1;
return -1;
}
nread = vfs_read(rtp_filp, (char __user *)buff, 100, &rtp_filp->f_pos);
filp_close(rtp_filp,current->files);
set_fs(old_fs);
while(i <= 6 && j < (nread -1))
{
if(buff[j] != ' ') {
if(buff[j] == '-')
flag = 1;
else
rtp_cal[i] = rtp_cal[i] * 10 + buff[j] - 48;
}else{
if(flag)
rtp_cal[i] = -rtp_cal[i];
flag = 0;
i++;
}
j++;
}
tsc2007_printk("rtp_cal{0-6}:%d %d %d %d %d %d %d\n",rtp_cal[0],rtp_cal[1],rtp_cal[2],rtp_cal[3],rtp_cal[4],rtp_cal[5],rtp_cal[6]);
return sprintf(buf,"RTP CALIBRATION INFO READ success\n");
}
static ssize_t tsc2007_enable_show(struct device *dev, struct device_attribute *attr, char *buf)
{
return sprintf(buf,"%s\n", !misc_ts->stopped ? "Tsc2007 enable" : "Tsc2007 disable");
}
static ssize_t tsc2007_enable_store(struct device *dev,
struct device_attribute *attr, const char *buf, size_t count)
{
int error;
unsigned long val;
error = kstrtoul(buf, 10, &val);
if(error)
return error;
if((val != 0) && (val != 1)) {
tsc2007_debug_msg("enable tsc2007 = %ld\n",val);
return count;
}
if(val == 1) {
if(misc_ts->stopped) {
tsc2007_open(misc_ts->input);
}
tsc2007_printk("enable tsc2007\n");
} else {
if(!misc_ts->stopped) {
tsc2007_close(misc_ts->input);
}
tsc2007_printk("disable tsc2007\n");
}
return count;
}
static ssize_t tsc2007_calmode_show(struct device *dev, struct device_attribute *attr, char *buf)
{
return sprintf(buf,"%s\n",cal_mode ? "Tsc2007 cal mode" : "Tsc2007 normal mode");
}
static ssize_t tsc2007_calmode_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
{
int ret;
unsigned long val;
ret = kstrtoul(buf, 10, &val);
if(ret)
return ret;
if((val != 0) && (val != 1)) {
tsc2007_debug_msg("tsc2007 calmode store = %ld\n", val);
return count;
}
tsc2007_stop(misc_ts);
msleep(10);
tsc2007_open(misc_ts->input);
if(val == 1) {
cal_mode = 1;
} else {
cal_mode = 0;
}
return count;
}
static ssize_t tsc2007_debug_show(struct device *dev, struct device_attribute *attr, char *buf)
{
return sprintf(buf,"%d\n",m_rtp_debug_mode);
}
static ssize_t tsc2007_debug_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
{
int ret;
unsigned long val;
ret = kstrtoul(buf, 10, &val);
if(ret)
return ret;
if((val != 0) && (val != 1)) {
tsc2007_debug_msg("tsc2007 debug store = %ld\n", val);
return count;
}
if(val == 1) {
m_rtp_debug_mode = 1;
} else {
m_rtp_debug_mode = 0;
}
return count;
}
//******************************************************************
//******************************************************************
//******************************************************************
//******************************************************************
#ifdef CONFIG_OF
static int tsc2007_get_pendown_state_gpio(struct device *dev)
{
struct i2c_client *client = to_i2c_client(dev);
struct tsc2007_dev *ts = i2c_get_clientdata(client);
return !gpio_get_value(ts->gpio);
}
static int tsc2007_probe_dt(struct i2c_client *client, struct tsc2007_dev *ts)
{
struct device_node *np = client->dev.of_node;
u32 val32;
u64 val64;
if (!np) {
tsc2007_err("missing device tree data\n");
return -EINVAL;
}
if (!of_property_read_u32(np, "ti,max-rt", &val32))
ts->max_rt = val32;
else
ts->max_rt = MAX_12BIT;
if (!of_property_read_u32(np, "ti,fuzzx", &val32))
ts->fuzzx = val32;
if (!of_property_read_u32(np, "ti,fuzzy", &val32))
ts->fuzzy = val32;
if (!of_property_read_u32(np, "ti,fuzzz", &val32))
ts->fuzzz = val32;
if (!of_property_read_u64(np, "ti,sample-delay", &val64))
ts->sample_delay = val64;
else
ts->sample_delay = 1;
if (!of_property_read_u64(np, "ti,report-delay", &val64))
ts->report_delay = val64;
else
ts->report_delay = 1;
if (!of_property_read_u64(np, "ti,initial-delay", &val64))
ts->initial_delay = val64;
else
ts->initial_delay = 1;
if (!of_property_read_u32(np, "ti,x-plate-ohms", &val32)) {
ts->x_plate_ohms = val32;
} else {
tsc2007_err("missing ti,x-plate-ohms devicetree property.");
return -EINVAL;
}
ts->gpio = of_get_named_gpio(np,"ti,gpio", 0);
return 0;
}
#else
static int tsc2007_probe_dt(struct i2c_client *client, struct tsc2007_dev *ts)
{
dev_err(&client->dev, "platform data is required!\n");
return -EINVAL;
}
#endif
static int tsc2007_probe_pdev(struct i2c_client *client, struct tsc2007_dev *ts,
const struct tsc2007_platform_data *pdata,
const struct i2c_device_id *id)
{
ts->model = pdata->model;
ts->x_plate_ohms = pdata->x_plate_ohms;
ts->max_rt = pdata->max_rt ? : MAX_12BIT;
ts->sample_delay = pdata->sample_delay ? : 1;
ts->report_delay = pdata->report_delay ? : 1;
ts->initial_delay = pdata->initial_delay;
ts->get_pendown_state = pdata->get_pendown_state;
ts->clear_penirq = pdata->clear_penirq;
ts->fuzzx = pdata->fuzzx;
ts->fuzzy = pdata->fuzzy;
ts->fuzzz = pdata->fuzzz;
if (pdata->x_plate_ohms == 0) {
dev_err(&client->dev, "x_plate_ohms is not set up in platform data");
return -EINVAL;
}
return 0;
}
static int tsc2007_power_init(struct tsc2007_dev *ts)
{
int rc;
ts->vdd = regulator_get(&ts->client->dev, "vdd");
if (IS_ERR(ts->vdd)) {
rc =PTR_ERR(ts->vdd);
tsc2007_err("Regulator get failed vdd rc=%d\n",rc);
return rc;
}
if(regulator_count_voltages(ts->vdd) > 0) {
rc = regulator_set_voltage(ts->vdd, VDD_MIN_UV, VDD_MAX_UV);
if(rc) {
tsc2007_err("Regulator get failed vdd rc=%d\n",rc);
regulator_put(ts->vdd);
return rc;
}
}
ts->vcc_i2c = regulator_get(&ts->client->dev, "vcc_i2c");
if (IS_ERR(ts->vcc_i2c)) {
rc =PTR_ERR(ts->vcc_i2c);
tsc2007_err("Regulator get failed vcc_i2c rc=%d\n",rc);
return rc;
}
if(regulator_count_voltages(ts->vcc_i2c) > 0) {
rc = regulator_set_voltage(ts->vcc_i2c, I2C_VTG_MIN_UV, I2C_VTG_MAX_UV);
if(rc) {
tsc2007_err("Regulator get failed vcc_i2c rc=%d\n",rc);
regulator_put(ts->vcc_i2c);
return rc;
}
}
tsc2007_debug_msg("vdd %d vcci2c %d\n",regulator_get_voltage(ts->vdd),regulator_get_voltage(ts->vcc_i2c));
return 0;
}
static int tsc2007_pinctrl_init(struct tsc2007_dev *ts)
{
int ret;
ts->rtp_pinctrl = devm_pinctrl_get(&ts->client->dev);
if(IS_ERR_OR_NULL(ts->rtp_pinctrl)) {
ret = PTR_ERR(ts->rtp_pinctrl);
tsc2007_err("Failed to get pinctrl %d\n", ret);
ts->rtp_pinctrl = NULL;
return ret;
}
ts->pinctrl_state_active =
pinctrl_lookup_state(ts->rtp_pinctrl, PINCTRL_STATE_ACTIVE);
if(IS_ERR_OR_NULL(ts->pinctrl_state_active)) {
ret = PTR_ERR(ts->pinctrl_state_active);
tsc2007_err("Can not lookup %s pinstate %d\n", PINCTRL_STATE_ACTIVE, ret);
devm_pinctrl_put(ts->rtp_pinctrl);
ts->rtp_pinctrl = NULL;
return ret;
}
ts->pinctrl_state_suspend =
pinctrl_lookup_state(ts->rtp_pinctrl, PINCTRL_STATE_SUSPEND);
if(IS_ERR_OR_NULL(ts->pinctrl_state_suspend)) {
ret = PTR_ERR(ts->pinctrl_state_suspend);
tsc2007_err("Can not lookup %s pinstate %d\n", PINCTRL_STATE_SUSPEND, ret);
devm_pinctrl_put(ts->rtp_pinctrl);
ts->rtp_pinctrl = NULL;
return ret;
}
return 0;
}
static void tsc2007_call_exit_platform_hw(void *data)
{
struct device *dev = data;
const struct tsc2007_platform_data *pdata = dev_get_platdata(dev);
pdata->exit_platform_hw();
}
static int tsc2007_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
const struct tsc2007_platform_data *pdata = dev_get_platdata(&client->dev);
struct tsc2007_dev *ts;
struct input_dev *input_dev;
int err,i;
if(client->dev.of_node) {
ts = devm_kzalloc(&client->dev,
sizeof(struct tsc2007_dev), GFP_KERNEL);
if (!ts)
{
tsc2007_err("Failed to allocate memory\n");
return -ENOMEM;
}
err = tsc2007_probe_dt(client, ts);
} else
err = tsc2007_probe_pdev(client, ts, pdata, id);
if (err)
{
tsc2007_err("Invalid data\n");
return err;
}
tsc2007_printk("driver version = %s\r\n",TSC_DRIVER_VERSION);
tsc2007_debug_msg("debug test\r\n");
if(strcmp(client->name,TSC_DRIVER_NAME) != 0) {
tsc2007_err("not tsc2007 driver.(%s)\r\n",client->name);
}
if (!i2c_check_functionality(client->adapter,I2C_FUNC_SMBUS_READ_WORD_DATA)) {
tsc2007_err("error : not compatiable isc function \r\n");
ts = NULL;
return -ENODEV;
}
i2c_set_clientdata(client, ts);
ts->client = client;
misc_ts = ts;
/****************************************************/
input_dev = devm_input_allocate_device(&client->dev);
if (!input_dev)
{
tsc2007_err("Unable to allocate input device \r\n");
return -ENOMEM;
}
/* map GPIO numbers to IRQ numbers [GPIO edge change triggers isr] */
if (gpio_is_valid(ts->gpio)) {
ts->get_pendown_state = tsc2007_get_pendown_state_gpio;
/* If GPIO is valid, then request the GPIO for use */
if (gpio_request(ts->gpio, "nPENIRQ")) {
tsc2007_err("Requesting GPIO failed \r\n");
return -ENOMEM;
}
/* Set the direction to be input */
if (gpio_direction_input(ts->gpio)) {
gpio_free(ts->gpio);
return -ENOMEM;
}
}
tsc2007_printk("tsc2007 int-pin[%d] :%d\n ",ts->gpio,gpio_get_value(ts->gpio));
ts->irq = gpio_to_irq(ts->gpio);
if(ts->irq < 0)
tsc2007_err("error.gpio_to_irq function is not \
supported ? you should define GPIO_TOUCH_IRQ.\r\n");
client->irq = ts->irq;
err = tsc2007_power_init(ts);
if(err) {
tsc2007_err("power init failed\r\n");
return err;
}
err = tsc2007_pinctrl_init(ts);
if(!err && ts->rtp_pinctrl) {
err = pinctrl_select_state(ts->rtp_pinctrl, ts->pinctrl_state_active);
if(err < 0)
tsc2007_err("failed to select pin to active state");
}
tsc2007_power_control(ts, POWER_OFF);
msleep(50);
tsc2007_power_control(ts, POWER_ON);
ts->input = input_dev;
init_waitqueue_head(&ts->wait);
sprintf(ts->phys, "input(rtp)");
input_dev->name = "TSC2007 Touchscreen";
input_dev->phys = ts->phys;
input_dev->id.bustype = BUS_I2C;
input_dev->open = tsc2007_open;
input_dev->close = tsc2007_close;
input_set_drvdata(input_dev, ts);
set_bit(EV_SYN, input_dev->evbit);
set_bit(EV_KEY, input_dev->evbit);
set_bit(EV_ABS, input_dev->evbit);
for(i=0; i < MAX_RTP_BUTTON_NUM; i++)
set_bit(RTP_BUTTON_MAPPING_KEY[i], input_dev->keybit);
set_bit(KEY_HOMEPAGE, input_dev->keybit);
set_bit(BTN_TOUCH, input_dev->keybit);
input_set_abs_params(input_dev, ABS_X, 0, MAX_12BIT, ts->fuzzx, 0);
input_set_abs_params(input_dev, ABS_Y, 0, MAX_12BIT, ts->fuzzy, 0);
input_set_abs_params(input_dev, ABS_PRESSURE, 0, MAX_12BIT,
ts->fuzzz, 0);
if (pdata) {
if (pdata->exit_platform_hw) {
err = devm_add_action(&client->dev,
tsc2007_call_exit_platform_hw,
&client->dev);
if (err) {
dev_err(&client->dev,
"Failed to register exit_platform_hw action, %d\n",
err);
return err;
}
}
if (pdata->init_platform_hw)
pdata->init_platform_hw();
}
err = devm_request_threaded_irq(&client->dev, ts->irq,
tsc2007_hard_irq, tsc2007_soft_irq,
IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING,
client->dev.driver->name, ts);
if (err) {
dev_err(&client->dev, "Failed to request irq %d: %d\n",
ts->irq, err);
return err;
}
ts->fb_notif.notifier_call = fb_notifier_callback;
err = fb_register_client(&ts->fb_notif);
if(err)
tsc2007_err("Unable to register fb_notifier: %d\r\n",err);
tsc2007_stop(ts);
err = input_register_device(input_dev);
if (err) {
dev_err(&client->dev,
"Failed to register input device: %d\n", err);
return err;
}
err = sysfs_create_group(rtp_kobj, &tsc2007_touch_prop_attr_group);
if(err)
tsc2007_err("Failed to create touch info sysfs\r\n");
tsc2007_printk("Tsc2007 touch is ready\r\n");
return 0;
}
static int tsc2007_remove(struct i2c_client *client)
{
struct tsc2007_dev *ts = i2c_get_clientdata(client);
if(ts == NULL)
return 0;
tsc2007_debug_msg("tsc2007_remove++\r\n");
tsc2007_stop(ts);
if(ts->irq)
free_irq(ts->irq, ts);
if(fb_unregister_client(&ts->fb_notif))
tsc2007_err("Error occurred while unregistering fb_notifier.\r\n");
if(gpio_is_valid(ts->gpio))
gpio_free(ts->gpio);
input_unregister_device(ts->input);
input_free_device(ts->input);
kfree(ts);
tsc2007_debug_msg("tsc2007 remove--\r\n");
return 0;
}
static const struct i2c_device_id tsc2007_idtable[] = {
{ "tsc2007", 0 },
{ }
};
#ifdef CONFIG_OF
static const struct of_device_id tsc2007_match_table[] = {
{ .compatible = "ti,tsc2007" },
{ /* sentinel */ }
};
#endif
static struct i2c_driver tsc2007_driver = {
.driver = {
.owner = THIS_MODULE,
.name = TSC_DRIVER_NAME,
.of_match_table = tsc2007_match_table,
.pm = &tsc2007_pm_ops,
},
.id_table = tsc2007_idtable,
.probe = tsc2007_probe,
.remove = tsc2007_remove,
};
static int __init tsc2007_init(void)
{
return i2c_add_driver(&tsc2007_driver);
}
static void __exit tsc2007_exit(void)
{
i2c_del_driver(&tsc2007_driver);
}
module_init(tsc2007_init);
module_exit(tsc2007_exit);
MODULE_AUTHOR("Melo Fang ");
MODULE_DESCRIPTION("TSC2007 TouchScreen Driver Using I2C Interface");
MODULE_LICENSE("GPL");