TSC2007 移植到android 6.0内核 (使用设备树)

    最近接手了一块电阻屏,用了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");

你可能感兴趣的:(kernel,驱动,Linux)