1. 程式人生 > >TSC2007 移植到android 6.0核心 (使用裝置樹)

TSC2007 移植到android 6.0核心 (使用裝置樹)

    最近接手了一塊電阻屏,用了TI的TSC2007進行訊號轉換。

    介面是 I2C, 還有一箇中斷屏,工作原理比較簡單:傳送一個8BIT命令(例如讀X),然後接收16BIT的返回值。

    整個驅動參考了TI的原始碼 以及 TI社群的討論。

/*
 * drivers/input/touchscreen/tsc2007.c
 *
 * Copyright (c) 2018 Melo
 *	Melo Fang <[email protected]>
 * 
 * Modified the TSC2007 driver from:
 * Copyright (c) 2014 Aprilaire (Research Products Corporation)
 *	Santhosh Ramani <
[email protected]
> * * 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 <linux/uaccess.h> #include <linux/module.h> #include <linux/slab.h> #include <linux/input.h> #include <linux/interrupt.h> #include <linux/i2c.h> #include <linux/i2c/tsc2007.h> #include <linux/of_device.h> #include <linux/of.h> #include <linux/of_gpio.h> #include <linux/delay.h> #include <linux/proc_fs.h> #include <linux/fs.h> #include <linux/time.h> #include <linux/regulator/consumer.h> #include <linux/pinctrl/consumer.h> #include <linux/pm_wakeup.h> #include <linux/notifier.h> #include <linux/fb.h> #include <linux/err.h> #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 <
[email protected]
>"); MODULE_DESCRIPTION("TSC2007 TouchScreen Driver Using I2C Interface"); MODULE_LICENSE("GPL");