TSC 2007 android 6.0カーネルへの移植(デバイスツリー使用)


最近抵抗スクリーンを受け取り、TIのTSC 2007で信号変換を行った.
インタフェースはI 2 Cであり、割り込み画面もあり、動作原理は比較的簡単である:8 BITコマンド(例えばXを読む)を送信し、16 BITの戻り値を受信する.
駆動全体は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



", 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
", 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
", 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
"); 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
", 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)
", 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
", rt); } wait_event_timeout(ts->wait, ts->stopped, msecs_to_jiffies(ts->sample_delay)); } tsc2007_debug_msg("Finger UP
"); 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
"); 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
"); } else { vdd_enabled = false; } } if(vcci2c_enabled) { ret = regulator_disable(ts->vcc_i2c); if(ret){ tsc2007_printk("Regulator vcc_i2c disable failed.\r
"); } 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
"); } else { vdd_enabled = true; } } if(!vcci2c_enabled) { ret = regulator_enable(ts->vcc_i2c); if(ret){ tsc2007_printk("Regulator vcc_i2c enable failed.\r
"); } 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
"); } 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
"); 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
"); 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
"); 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
"); 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
"); 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
"); } static ssize_t tsc2007_chipvendor_show(struct device *dev, struct device_attribute *attr, char *buf) { return sprintf(buf,"TI
"); } static ssize_t tsc2007_chip_show(struct device *dev, struct device_attribute *attr, char *buf) { return sprintf(buf,"tsc2007
"); } static ssize_t tsc2007_interface_show(struct device *dev, struct device_attribute *attr, char *buf) { return sprintf(buf,"I2C
"); } static ssize_t tsc2007_power_show(struct device *dev, struct device_attribute *attr, char *buf) { return sprintf(buf,"I/O Power 1v8
VDD Power3v3
"); } 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
",(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
",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
"); } static ssize_t tsc2007_enable_show(struct device *dev, struct device_attribute *attr, char *buf) { return sprintf(buf,"%s
", !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
",val); return count; } if(val == 1) { if(misc_ts->stopped) { tsc2007_open(misc_ts->input); } tsc2007_printk("enable tsc2007
"); } else { if(!misc_ts->stopped) { tsc2007_close(misc_ts->input); } tsc2007_printk("disable tsc2007
"); } return count; } static ssize_t tsc2007_calmode_show(struct device *dev, struct device_attribute *attr, char *buf) { return sprintf(buf,"%s
",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
", 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
",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
", 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
"); 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!
"); 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
",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
",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
",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
",rc); regulator_put(ts->vcc_i2c); return rc; } } tsc2007_debug_msg("vdd %d vcci2c %d
",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
", 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
", 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
", 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
"); return -ENOMEM; } err = tsc2007_probe_dt(client, ts); } else err = tsc2007_probe_pdev(client, ts, pdata, id); if (err) { tsc2007_err("Invalid data
"); return err; } tsc2007_printk("driver version = %s\r
",TSC_DRIVER_VERSION); tsc2007_debug_msg("debug test\r
"); if(strcmp(client->name,TSC_DRIVER_NAME) != 0) { tsc2007_err("not tsc2007 driver.(%s)\r
",client->name); } if (!i2c_check_functionality(client->adapter,I2C_FUNC_SMBUS_READ_WORD_DATA)) { tsc2007_err("error : not compatiable isc function \r
"); 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
"); 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
"); 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
",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
"); client->irq = ts->irq; err = tsc2007_power_init(ts); if(err) { tsc2007_err("power init failed\r
"); 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
", 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
", 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
",err); tsc2007_stop(ts); err = input_register_device(input_dev); if (err) { dev_err(&client->dev, "Failed to register input device: %d
", 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
"); tsc2007_printk("Tsc2007 touch is ready\r
"); 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
"); 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
"); 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
"); 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");