| Michael Hennerich | b4be468 | 2009-03-09 20:12:52 -0700 | [diff] [blame] | 1 | /* | 
| Mike Frysinger | 4397c98 | 2010-06-30 01:40:52 -0700 | [diff] [blame] | 2 |  * AD7879/AD7889 based touchscreen and GPIO driver | 
| Michael Hennerich | b4be468 | 2009-03-09 20:12:52 -0700 | [diff] [blame] | 3 |  * | 
| Mike Frysinger | 4397c98 | 2010-06-30 01:40:52 -0700 | [diff] [blame] | 4 |  * Copyright (C) 2008-2010 Michael Hennerich, Analog Devices Inc. | 
| Michael Hennerich | b4be468 | 2009-03-09 20:12:52 -0700 | [diff] [blame] | 5 |  * | 
| Mike Frysinger | 4397c98 | 2010-06-30 01:40:52 -0700 | [diff] [blame] | 6 |  * Licensed under the GPL-2 or later. | 
| Michael Hennerich | b4be468 | 2009-03-09 20:12:52 -0700 | [diff] [blame] | 7 |  * | 
 | 8 |  * History: | 
 | 9 |  * Copyright (c) 2005 David Brownell | 
 | 10 |  * Copyright (c) 2006 Nokia Corporation | 
 | 11 |  * Various changes: Imre Deak <imre.deak@nokia.com> | 
 | 12 |  * | 
 | 13 |  * Using code from: | 
 | 14 |  *  - corgi_ts.c | 
 | 15 |  *	Copyright (C) 2004-2005 Richard Purdie | 
 | 16 |  *  - omap_ts.[hc], ads7846.h, ts_osk.c | 
 | 17 |  *	Copyright (C) 2002 MontaVista Software | 
 | 18 |  *	Copyright (C) 2004 Texas Instruments | 
 | 19 |  *	Copyright (C) 2005 Dirk Behme | 
 | 20 |  *  - ad7877.c | 
 | 21 |  *	Copyright (C) 2006-2008 Analog Devices Inc. | 
 | 22 |  */ | 
 | 23 |  | 
 | 24 | #include <linux/device.h> | 
 | 25 | #include <linux/init.h> | 
 | 26 | #include <linux/delay.h> | 
 | 27 | #include <linux/input.h> | 
 | 28 | #include <linux/interrupt.h> | 
 | 29 | #include <linux/irq.h> | 
 | 30 | #include <linux/slab.h> | 
| Michael Hennerich | b4be468 | 2009-03-09 20:12:52 -0700 | [diff] [blame] | 31 | #include <linux/spi/spi.h> | 
 | 32 | #include <linux/i2c.h> | 
| Michael Hennerich | ec51b7f | 2010-01-19 00:27:58 -0800 | [diff] [blame] | 33 | #include <linux/gpio.h> | 
| Michael Hennerich | b4be468 | 2009-03-09 20:12:52 -0700 | [diff] [blame] | 34 |  | 
 | 35 | #include <linux/spi/ad7879.h> | 
| Paul Gortmaker | d2d8442 | 2011-07-03 13:53:48 -0400 | [diff] [blame] | 36 | #include <linux/module.h> | 
| Mike Frysinger | 4397c98 | 2010-06-30 01:40:52 -0700 | [diff] [blame] | 37 | #include "ad7879.h" | 
| Michael Hennerich | b4be468 | 2009-03-09 20:12:52 -0700 | [diff] [blame] | 38 |  | 
 | 39 | #define AD7879_REG_ZEROS		0 | 
 | 40 | #define AD7879_REG_CTRL1		1 | 
 | 41 | #define AD7879_REG_CTRL2		2 | 
 | 42 | #define AD7879_REG_CTRL3		3 | 
 | 43 | #define AD7879_REG_AUX1HIGH		4 | 
 | 44 | #define AD7879_REG_AUX1LOW		5 | 
 | 45 | #define AD7879_REG_TEMP1HIGH		6 | 
 | 46 | #define AD7879_REG_TEMP1LOW		7 | 
 | 47 | #define AD7879_REG_XPLUS		8 | 
 | 48 | #define AD7879_REG_YPLUS		9 | 
 | 49 | #define AD7879_REG_Z1			10 | 
 | 50 | #define AD7879_REG_Z2			11 | 
 | 51 | #define AD7879_REG_AUXVBAT		12 | 
 | 52 | #define AD7879_REG_TEMP			13 | 
 | 53 | #define AD7879_REG_REVID		14 | 
 | 54 |  | 
 | 55 | /* Control REG 1 */ | 
 | 56 | #define AD7879_TMR(x)			((x & 0xFF) << 0) | 
 | 57 | #define AD7879_ACQ(x)			((x & 0x3) << 8) | 
 | 58 | #define AD7879_MODE_NOC			(0 << 10)	/* Do not convert */ | 
 | 59 | #define AD7879_MODE_SCC			(1 << 10)	/* Single channel conversion */ | 
 | 60 | #define AD7879_MODE_SEQ0		(2 << 10)	/* Sequence 0 in Slave Mode */ | 
 | 61 | #define AD7879_MODE_SEQ1		(3 << 10)	/* Sequence 1 in Master Mode */ | 
 | 62 | #define AD7879_MODE_INT			(1 << 15)	/* PENIRQ disabled INT enabled */ | 
 | 63 |  | 
 | 64 | /* Control REG 2 */ | 
 | 65 | #define AD7879_FCD(x)			((x & 0x3) << 0) | 
 | 66 | #define AD7879_RESET			(1 << 4) | 
 | 67 | #define AD7879_MFS(x)			((x & 0x3) << 5) | 
 | 68 | #define AD7879_AVG(x)			((x & 0x3) << 7) | 
 | 69 | #define	AD7879_SER			(1 << 9)	/* non-differential */ | 
 | 70 | #define	AD7879_DFR			(0 << 9)	/* differential */ | 
 | 71 | #define AD7879_GPIOPOL			(1 << 10) | 
 | 72 | #define AD7879_GPIODIR			(1 << 11) | 
 | 73 | #define AD7879_GPIO_DATA		(1 << 12) | 
 | 74 | #define AD7879_GPIO_EN			(1 << 13) | 
 | 75 | #define AD7879_PM(x)			((x & 0x3) << 14) | 
 | 76 | #define AD7879_PM_SHUTDOWN		(0) | 
 | 77 | #define AD7879_PM_DYN			(1) | 
 | 78 | #define AD7879_PM_FULLON		(2) | 
 | 79 |  | 
 | 80 | /* Control REG 3 */ | 
 | 81 | #define AD7879_TEMPMASK_BIT		(1<<15) | 
 | 82 | #define AD7879_AUXVBATMASK_BIT		(1<<14) | 
 | 83 | #define AD7879_INTMODE_BIT		(1<<13) | 
 | 84 | #define AD7879_GPIOALERTMASK_BIT	(1<<12) | 
 | 85 | #define AD7879_AUXLOW_BIT		(1<<11) | 
 | 86 | #define AD7879_AUXHIGH_BIT		(1<<10) | 
 | 87 | #define AD7879_TEMPLOW_BIT		(1<<9) | 
 | 88 | #define AD7879_TEMPHIGH_BIT		(1<<8) | 
 | 89 | #define AD7879_YPLUS_BIT		(1<<7) | 
 | 90 | #define AD7879_XPLUS_BIT		(1<<6) | 
 | 91 | #define AD7879_Z1_BIT			(1<<5) | 
 | 92 | #define AD7879_Z2_BIT			(1<<4) | 
 | 93 | #define AD7879_AUX_BIT			(1<<3) | 
 | 94 | #define AD7879_VBAT_BIT			(1<<2) | 
 | 95 | #define AD7879_TEMP_BIT			(1<<1) | 
 | 96 |  | 
 | 97 | enum { | 
 | 98 | 	AD7879_SEQ_XPOS  = 0, | 
 | 99 | 	AD7879_SEQ_YPOS  = 1, | 
 | 100 | 	AD7879_SEQ_Z1    = 2, | 
 | 101 | 	AD7879_SEQ_Z2    = 3, | 
 | 102 | 	AD7879_NR_SENSE  = 4, | 
 | 103 | }; | 
 | 104 |  | 
 | 105 | #define	MAX_12BIT			((1<<12)-1) | 
 | 106 | #define	TS_PEN_UP_TIMEOUT		msecs_to_jiffies(50) | 
 | 107 |  | 
| Michael Hennerich | b4be468 | 2009-03-09 20:12:52 -0700 | [diff] [blame] | 108 | struct ad7879 { | 
| Mike Frysinger | 4397c98 | 2010-06-30 01:40:52 -0700 | [diff] [blame] | 109 | 	const struct ad7879_bus_ops *bops; | 
 | 110 |  | 
 | 111 | 	struct device		*dev; | 
| Michael Hennerich | b4be468 | 2009-03-09 20:12:52 -0700 | [diff] [blame] | 112 | 	struct input_dev	*input; | 
| Michael Hennerich | b4be468 | 2009-03-09 20:12:52 -0700 | [diff] [blame] | 113 | 	struct timer_list	timer; | 
| Michael Hennerich | ec51b7f | 2010-01-19 00:27:58 -0800 | [diff] [blame] | 114 | #ifdef CONFIG_GPIOLIB | 
 | 115 | 	struct gpio_chip	gc; | 
| Dmitry Torokhov | 14fbbc3 | 2010-06-30 14:50:51 -0700 | [diff] [blame] | 116 | 	struct mutex		mutex; | 
| Michael Hennerich | ec51b7f | 2010-01-19 00:27:58 -0800 | [diff] [blame] | 117 | #endif | 
| Mike Frysinger | 4397c98 | 2010-06-30 01:40:52 -0700 | [diff] [blame] | 118 | 	unsigned int		irq; | 
| Dmitry Torokhov | 14fbbc3 | 2010-06-30 14:50:51 -0700 | [diff] [blame] | 119 | 	bool			disabled;	/* P: input->mutex */ | 
 | 120 | 	bool			suspended;	/* P: input->mutex */ | 
| Michael Hennerich | b4be468 | 2009-03-09 20:12:52 -0700 | [diff] [blame] | 121 | 	u16			conversion_data[AD7879_NR_SENSE]; | 
 | 122 | 	char			phys[32]; | 
 | 123 | 	u8			first_conversion_delay; | 
 | 124 | 	u8			acquisition_time; | 
 | 125 | 	u8			averaging; | 
 | 126 | 	u8			pen_down_acc_interval; | 
 | 127 | 	u8			median; | 
 | 128 | 	u16			x_plate_ohms; | 
 | 129 | 	u16			pressure_max; | 
| Michael Hennerich | b4be468 | 2009-03-09 20:12:52 -0700 | [diff] [blame] | 130 | 	u16			cmd_crtl1; | 
 | 131 | 	u16			cmd_crtl2; | 
 | 132 | 	u16			cmd_crtl3; | 
| Michael Hennerich | b584efc | 2010-10-28 14:59:05 -0700 | [diff] [blame] | 133 | 	int			x; | 
 | 134 | 	int			y; | 
 | 135 | 	int			Rt; | 
| Michael Hennerich | b4be468 | 2009-03-09 20:12:52 -0700 | [diff] [blame] | 136 | }; | 
 | 137 |  | 
| Mike Frysinger | 4397c98 | 2010-06-30 01:40:52 -0700 | [diff] [blame] | 138 | static int ad7879_read(struct ad7879 *ts, u8 reg) | 
 | 139 | { | 
 | 140 | 	return ts->bops->read(ts->dev, reg); | 
 | 141 | } | 
 | 142 |  | 
 | 143 | static int ad7879_multi_read(struct ad7879 *ts, u8 first_reg, u8 count, u16 *buf) | 
 | 144 | { | 
 | 145 | 	return ts->bops->multi_read(ts->dev, first_reg, count, buf); | 
 | 146 | } | 
 | 147 |  | 
 | 148 | static int ad7879_write(struct ad7879 *ts, u8 reg, u16 val) | 
 | 149 | { | 
 | 150 | 	return ts->bops->write(ts->dev, reg, val); | 
 | 151 | } | 
| Michael Hennerich | b4be468 | 2009-03-09 20:12:52 -0700 | [diff] [blame] | 152 |  | 
| Michael Hennerich | 963ce8a | 2010-06-30 14:51:10 -0700 | [diff] [blame] | 153 | static int ad7879_report(struct ad7879 *ts) | 
| Michael Hennerich | b4be468 | 2009-03-09 20:12:52 -0700 | [diff] [blame] | 154 | { | 
 | 155 | 	struct input_dev *input_dev = ts->input; | 
 | 156 | 	unsigned Rt; | 
 | 157 | 	u16 x, y, z1, z2; | 
 | 158 |  | 
 | 159 | 	x = ts->conversion_data[AD7879_SEQ_XPOS] & MAX_12BIT; | 
 | 160 | 	y = ts->conversion_data[AD7879_SEQ_YPOS] & MAX_12BIT; | 
 | 161 | 	z1 = ts->conversion_data[AD7879_SEQ_Z1] & MAX_12BIT; | 
 | 162 | 	z2 = ts->conversion_data[AD7879_SEQ_Z2] & MAX_12BIT; | 
 | 163 |  | 
 | 164 | 	/* | 
 | 165 | 	 * The samples processed here are already preprocessed by the AD7879. | 
| Mike Frysinger | 4397c98 | 2010-06-30 01:40:52 -0700 | [diff] [blame] | 166 | 	 * The preprocessing function consists of a median and an averaging | 
 | 167 | 	 * filter.  The combination of these two techniques provides a robust | 
 | 168 | 	 * solution, discarding the spurious noise in the signal and keeping | 
 | 169 | 	 * only the data of interest.  The size of both filters is | 
 | 170 | 	 * programmable. (dev.platform_data, see linux/spi/ad7879.h) Other | 
 | 171 | 	 * user-programmable conversion controls include variable acquisition | 
 | 172 | 	 * time, and first conversion delay. Up to 16 averages can be taken | 
 | 173 | 	 * per conversion. | 
| Michael Hennerich | b4be468 | 2009-03-09 20:12:52 -0700 | [diff] [blame] | 174 | 	 */ | 
 | 175 |  | 
 | 176 | 	if (likely(x && z1)) { | 
 | 177 | 		/* compute touch pressure resistance using equation #1 */ | 
 | 178 | 		Rt = (z2 - z1) * x * ts->x_plate_ohms; | 
 | 179 | 		Rt /= z1; | 
 | 180 | 		Rt = (Rt + 2047) >> 12; | 
 | 181 |  | 
| Michael Hennerich | b584efc | 2010-10-28 14:59:05 -0700 | [diff] [blame] | 182 | 		/* | 
 | 183 | 		 * Sample found inconsistent, pressure is beyond | 
 | 184 | 		 * the maximum. Don't report it to user space. | 
 | 185 | 		 */ | 
 | 186 | 		if (Rt > ts->pressure_max) | 
 | 187 | 			return -EINVAL; | 
| Michael Hennerich | 963ce8a | 2010-06-30 14:51:10 -0700 | [diff] [blame] | 188 |  | 
| Michael Hennerich | b584efc | 2010-10-28 14:59:05 -0700 | [diff] [blame] | 189 | 		/* | 
 | 190 | 		 * Note that we delay reporting events by one sample. | 
 | 191 | 		 * This is done to avoid reporting last sample of the | 
 | 192 | 		 * touch sequence, which may be incomplete if finger | 
 | 193 | 		 * leaves the surface before last reading is taken. | 
 | 194 | 		 */ | 
 | 195 | 		if (timer_pending(&ts->timer)) { | 
 | 196 | 			/* Touch continues */ | 
 | 197 | 			input_report_key(input_dev, BTN_TOUCH, 1); | 
 | 198 | 			input_report_abs(input_dev, ABS_X, ts->x); | 
 | 199 | 			input_report_abs(input_dev, ABS_Y, ts->y); | 
 | 200 | 			input_report_abs(input_dev, ABS_PRESSURE, ts->Rt); | 
 | 201 | 			input_sync(input_dev); | 
 | 202 | 		} | 
 | 203 |  | 
 | 204 | 		ts->x = x; | 
 | 205 | 		ts->y = y; | 
 | 206 | 		ts->Rt = Rt; | 
 | 207 |  | 
| Michael Hennerich | 963ce8a | 2010-06-30 14:51:10 -0700 | [diff] [blame] | 208 | 		return 0; | 
| Michael Hennerich | b4be468 | 2009-03-09 20:12:52 -0700 | [diff] [blame] | 209 | 	} | 
| Michael Hennerich | 963ce8a | 2010-06-30 14:51:10 -0700 | [diff] [blame] | 210 |  | 
 | 211 | 	return -EINVAL; | 
| Michael Hennerich | b4be468 | 2009-03-09 20:12:52 -0700 | [diff] [blame] | 212 | } | 
 | 213 |  | 
| Michael Hennerich | b4be468 | 2009-03-09 20:12:52 -0700 | [diff] [blame] | 214 | static void ad7879_ts_event_release(struct ad7879 *ts) | 
 | 215 | { | 
 | 216 | 	struct input_dev *input_dev = ts->input; | 
 | 217 |  | 
 | 218 | 	input_report_abs(input_dev, ABS_PRESSURE, 0); | 
| Michael Hennerich | 963ce8a | 2010-06-30 14:51:10 -0700 | [diff] [blame] | 219 | 	input_report_key(input_dev, BTN_TOUCH, 0); | 
| Michael Hennerich | b4be468 | 2009-03-09 20:12:52 -0700 | [diff] [blame] | 220 | 	input_sync(input_dev); | 
 | 221 | } | 
 | 222 |  | 
 | 223 | static void ad7879_timer(unsigned long handle) | 
 | 224 | { | 
 | 225 | 	struct ad7879 *ts = (void *)handle; | 
 | 226 |  | 
 | 227 | 	ad7879_ts_event_release(ts); | 
 | 228 | } | 
 | 229 |  | 
 | 230 | static irqreturn_t ad7879_irq(int irq, void *handle) | 
 | 231 | { | 
 | 232 | 	struct ad7879 *ts = handle; | 
 | 233 |  | 
| Mike Frysinger | 4397c98 | 2010-06-30 01:40:52 -0700 | [diff] [blame] | 234 | 	ad7879_multi_read(ts, AD7879_REG_XPLUS, AD7879_NR_SENSE, ts->conversion_data); | 
| Michael Hennerich | b4be468 | 2009-03-09 20:12:52 -0700 | [diff] [blame] | 235 |  | 
| Michael Hennerich | 963ce8a | 2010-06-30 14:51:10 -0700 | [diff] [blame] | 236 | 	if (!ad7879_report(ts)) | 
 | 237 | 		mod_timer(&ts->timer, jiffies + TS_PEN_UP_TIMEOUT); | 
| Michael Hennerich | b4be468 | 2009-03-09 20:12:52 -0700 | [diff] [blame] | 238 |  | 
 | 239 | 	return IRQ_HANDLED; | 
 | 240 | } | 
 | 241 |  | 
| Dmitry Torokhov | 14fbbc3 | 2010-06-30 14:50:51 -0700 | [diff] [blame] | 242 | static void __ad7879_enable(struct ad7879 *ts) | 
| Michael Hennerich | b4be468 | 2009-03-09 20:12:52 -0700 | [diff] [blame] | 243 | { | 
| Mike Frysinger | 4397c98 | 2010-06-30 01:40:52 -0700 | [diff] [blame] | 244 | 	ad7879_write(ts, AD7879_REG_CTRL2, ts->cmd_crtl2); | 
 | 245 | 	ad7879_write(ts, AD7879_REG_CTRL3, ts->cmd_crtl3); | 
 | 246 | 	ad7879_write(ts, AD7879_REG_CTRL1, ts->cmd_crtl1); | 
| Dmitry Torokhov | 14fbbc3 | 2010-06-30 14:50:51 -0700 | [diff] [blame] | 247 |  | 
 | 248 | 	enable_irq(ts->irq); | 
| Michael Hennerich | b4be468 | 2009-03-09 20:12:52 -0700 | [diff] [blame] | 249 | } | 
 | 250 |  | 
| Dmitry Torokhov | 14fbbc3 | 2010-06-30 14:50:51 -0700 | [diff] [blame] | 251 | static void __ad7879_disable(struct ad7879 *ts) | 
| Michael Hennerich | b4be468 | 2009-03-09 20:12:52 -0700 | [diff] [blame] | 252 | { | 
| Michael Hennerich | 4fecc20 | 2011-08-02 15:41:37 -0700 | [diff] [blame] | 253 | 	u16 reg = (ts->cmd_crtl2 & ~AD7879_PM(-1)) | | 
 | 254 | 		AD7879_PM(AD7879_PM_SHUTDOWN); | 
| Dmitry Torokhov | 14fbbc3 | 2010-06-30 14:50:51 -0700 | [diff] [blame] | 255 | 	disable_irq(ts->irq); | 
| Michael Hennerich | b4be468 | 2009-03-09 20:12:52 -0700 | [diff] [blame] | 256 |  | 
| Dmitry Torokhov | 14fbbc3 | 2010-06-30 14:50:51 -0700 | [diff] [blame] | 257 | 	if (del_timer_sync(&ts->timer)) | 
 | 258 | 		ad7879_ts_event_release(ts); | 
| Michael Hennerich | b4be468 | 2009-03-09 20:12:52 -0700 | [diff] [blame] | 259 |  | 
| Michael Hennerich | 4fecc20 | 2011-08-02 15:41:37 -0700 | [diff] [blame] | 260 | 	ad7879_write(ts, AD7879_REG_CTRL2, reg); | 
| Dmitry Torokhov | 14fbbc3 | 2010-06-30 14:50:51 -0700 | [diff] [blame] | 261 | } | 
| Michael Hennerich | b4be468 | 2009-03-09 20:12:52 -0700 | [diff] [blame] | 262 |  | 
| Michael Hennerich | b4be468 | 2009-03-09 20:12:52 -0700 | [diff] [blame] | 263 |  | 
| Dmitry Torokhov | 14fbbc3 | 2010-06-30 14:50:51 -0700 | [diff] [blame] | 264 | static int ad7879_open(struct input_dev *input) | 
 | 265 | { | 
 | 266 | 	struct ad7879 *ts = input_get_drvdata(input); | 
 | 267 |  | 
 | 268 | 	/* protected by input->mutex */ | 
 | 269 | 	if (!ts->disabled && !ts->suspended) | 
 | 270 | 		__ad7879_enable(ts); | 
 | 271 |  | 
 | 272 | 	return 0; | 
 | 273 | } | 
 | 274 |  | 
 | 275 | static void ad7879_close(struct input_dev* input) | 
 | 276 | { | 
 | 277 | 	struct ad7879 *ts = input_get_drvdata(input); | 
 | 278 |  | 
 | 279 | 	/* protected by input->mutex */ | 
 | 280 | 	if (!ts->disabled && !ts->suspended) | 
 | 281 | 		__ad7879_disable(ts); | 
 | 282 | } | 
 | 283 |  | 
| Dmitry Torokhov | 8672bd9 | 2011-11-14 00:32:09 -0800 | [diff] [blame] | 284 | #ifdef CONFIG_PM_SLEEP | 
 | 285 | static int ad7879_suspend(struct device *dev) | 
| Dmitry Torokhov | 14fbbc3 | 2010-06-30 14:50:51 -0700 | [diff] [blame] | 286 | { | 
| Dmitry Torokhov | 8672bd9 | 2011-11-14 00:32:09 -0800 | [diff] [blame] | 287 | 	struct ad7879 *ts = dev_get_drvdata(dev); | 
 | 288 |  | 
| Dmitry Torokhov | 14fbbc3 | 2010-06-30 14:50:51 -0700 | [diff] [blame] | 289 | 	mutex_lock(&ts->input->mutex); | 
 | 290 |  | 
 | 291 | 	if (!ts->suspended && !ts->disabled && ts->input->users) | 
 | 292 | 		__ad7879_disable(ts); | 
 | 293 |  | 
 | 294 | 	ts->suspended = true; | 
 | 295 |  | 
 | 296 | 	mutex_unlock(&ts->input->mutex); | 
| Dmitry Torokhov | 14fbbc3 | 2010-06-30 14:50:51 -0700 | [diff] [blame] | 297 |  | 
| Dmitry Torokhov | 8672bd9 | 2011-11-14 00:32:09 -0800 | [diff] [blame] | 298 | 	return 0; | 
 | 299 | } | 
 | 300 |  | 
 | 301 | static int ad7879_resume(struct device *dev) | 
| Dmitry Torokhov | 14fbbc3 | 2010-06-30 14:50:51 -0700 | [diff] [blame] | 302 | { | 
| Dmitry Torokhov | 8672bd9 | 2011-11-14 00:32:09 -0800 | [diff] [blame] | 303 | 	struct ad7879 *ts = dev_get_drvdata(dev); | 
 | 304 |  | 
| Dmitry Torokhov | 14fbbc3 | 2010-06-30 14:50:51 -0700 | [diff] [blame] | 305 | 	mutex_lock(&ts->input->mutex); | 
 | 306 |  | 
 | 307 | 	if (ts->suspended && !ts->disabled && ts->input->users) | 
 | 308 | 		__ad7879_enable(ts); | 
 | 309 |  | 
 | 310 | 	ts->suspended = false; | 
 | 311 |  | 
 | 312 | 	mutex_unlock(&ts->input->mutex); | 
| Dmitry Torokhov | 8672bd9 | 2011-11-14 00:32:09 -0800 | [diff] [blame] | 313 |  | 
 | 314 | 	return 0; | 
| Dmitry Torokhov | 14fbbc3 | 2010-06-30 14:50:51 -0700 | [diff] [blame] | 315 | } | 
| Dmitry Torokhov | 8672bd9 | 2011-11-14 00:32:09 -0800 | [diff] [blame] | 316 | #endif | 
 | 317 |  | 
 | 318 | SIMPLE_DEV_PM_OPS(ad7879_pm_ops, ad7879_suspend, ad7879_resume); | 
 | 319 | EXPORT_SYMBOL(ad7879_pm_ops); | 
| Dmitry Torokhov | 14fbbc3 | 2010-06-30 14:50:51 -0700 | [diff] [blame] | 320 |  | 
 | 321 | static void ad7879_toggle(struct ad7879 *ts, bool disable) | 
 | 322 | { | 
 | 323 | 	mutex_lock(&ts->input->mutex); | 
 | 324 |  | 
 | 325 | 	if (!ts->suspended && ts->input->users != 0) { | 
 | 326 |  | 
 | 327 | 		if (disable) { | 
 | 328 | 			if (ts->disabled) | 
 | 329 | 				__ad7879_enable(ts); | 
 | 330 | 		} else { | 
 | 331 | 			if (!ts->disabled) | 
 | 332 | 				__ad7879_disable(ts); | 
 | 333 | 		} | 
| Michael Hennerich | b4be468 | 2009-03-09 20:12:52 -0700 | [diff] [blame] | 334 | 	} | 
 | 335 |  | 
| Dmitry Torokhov | 14fbbc3 | 2010-06-30 14:50:51 -0700 | [diff] [blame] | 336 | 	ts->disabled = disable; | 
 | 337 |  | 
 | 338 | 	mutex_unlock(&ts->input->mutex); | 
| Michael Hennerich | b4be468 | 2009-03-09 20:12:52 -0700 | [diff] [blame] | 339 | } | 
| Michael Hennerich | b4be468 | 2009-03-09 20:12:52 -0700 | [diff] [blame] | 340 |  | 
 | 341 | static ssize_t ad7879_disable_show(struct device *dev, | 
 | 342 | 				     struct device_attribute *attr, char *buf) | 
 | 343 | { | 
 | 344 | 	struct ad7879 *ts = dev_get_drvdata(dev); | 
 | 345 |  | 
 | 346 | 	return sprintf(buf, "%u\n", ts->disabled); | 
 | 347 | } | 
 | 348 |  | 
 | 349 | static ssize_t ad7879_disable_store(struct device *dev, | 
 | 350 | 				     struct device_attribute *attr, | 
 | 351 | 				     const char *buf, size_t count) | 
 | 352 | { | 
 | 353 | 	struct ad7879 *ts = dev_get_drvdata(dev); | 
| JJ Ding | 76496e7 | 2011-11-09 10:20:14 -0800 | [diff] [blame] | 354 | 	unsigned int val; | 
| Michael Hennerich | b4be468 | 2009-03-09 20:12:52 -0700 | [diff] [blame] | 355 | 	int error; | 
 | 356 |  | 
| JJ Ding | 76496e7 | 2011-11-09 10:20:14 -0800 | [diff] [blame] | 357 | 	error = kstrtouint(buf, 10, &val); | 
| Michael Hennerich | b4be468 | 2009-03-09 20:12:52 -0700 | [diff] [blame] | 358 | 	if (error) | 
 | 359 | 		return error; | 
 | 360 |  | 
| Dmitry Torokhov | 14fbbc3 | 2010-06-30 14:50:51 -0700 | [diff] [blame] | 361 | 	ad7879_toggle(ts, val); | 
| Michael Hennerich | b4be468 | 2009-03-09 20:12:52 -0700 | [diff] [blame] | 362 |  | 
 | 363 | 	return count; | 
 | 364 | } | 
 | 365 |  | 
 | 366 | static DEVICE_ATTR(disable, 0664, ad7879_disable_show, ad7879_disable_store); | 
 | 367 |  | 
| Michael Hennerich | b4be468 | 2009-03-09 20:12:52 -0700 | [diff] [blame] | 368 | static struct attribute *ad7879_attributes[] = { | 
 | 369 | 	&dev_attr_disable.attr, | 
| Michael Hennerich | b4be468 | 2009-03-09 20:12:52 -0700 | [diff] [blame] | 370 | 	NULL | 
 | 371 | }; | 
 | 372 |  | 
 | 373 | static const struct attribute_group ad7879_attr_group = { | 
 | 374 | 	.attrs = ad7879_attributes, | 
 | 375 | }; | 
 | 376 |  | 
| Michael Hennerich | ec51b7f | 2010-01-19 00:27:58 -0800 | [diff] [blame] | 377 | #ifdef CONFIG_GPIOLIB | 
 | 378 | static int ad7879_gpio_direction_input(struct gpio_chip *chip, | 
 | 379 | 					unsigned gpio) | 
 | 380 | { | 
 | 381 | 	struct ad7879 *ts = container_of(chip, struct ad7879, gc); | 
 | 382 | 	int err; | 
 | 383 |  | 
 | 384 | 	mutex_lock(&ts->mutex); | 
 | 385 | 	ts->cmd_crtl2 |= AD7879_GPIO_EN | AD7879_GPIODIR | AD7879_GPIOPOL; | 
| Mike Frysinger | 4397c98 | 2010-06-30 01:40:52 -0700 | [diff] [blame] | 386 | 	err = ad7879_write(ts, AD7879_REG_CTRL2, ts->cmd_crtl2); | 
| Michael Hennerich | ec51b7f | 2010-01-19 00:27:58 -0800 | [diff] [blame] | 387 | 	mutex_unlock(&ts->mutex); | 
 | 388 |  | 
 | 389 | 	return err; | 
 | 390 | } | 
 | 391 |  | 
 | 392 | static int ad7879_gpio_direction_output(struct gpio_chip *chip, | 
 | 393 | 					unsigned gpio, int level) | 
 | 394 | { | 
 | 395 | 	struct ad7879 *ts = container_of(chip, struct ad7879, gc); | 
 | 396 | 	int err; | 
 | 397 |  | 
 | 398 | 	mutex_lock(&ts->mutex); | 
 | 399 | 	ts->cmd_crtl2 &= ~AD7879_GPIODIR; | 
 | 400 | 	ts->cmd_crtl2 |= AD7879_GPIO_EN | AD7879_GPIOPOL; | 
 | 401 | 	if (level) | 
 | 402 | 		ts->cmd_crtl2 |= AD7879_GPIO_DATA; | 
 | 403 | 	else | 
 | 404 | 		ts->cmd_crtl2 &= ~AD7879_GPIO_DATA; | 
 | 405 |  | 
| Mike Frysinger | 4397c98 | 2010-06-30 01:40:52 -0700 | [diff] [blame] | 406 | 	err = ad7879_write(ts, AD7879_REG_CTRL2, ts->cmd_crtl2); | 
| Michael Hennerich | ec51b7f | 2010-01-19 00:27:58 -0800 | [diff] [blame] | 407 | 	mutex_unlock(&ts->mutex); | 
 | 408 |  | 
 | 409 | 	return err; | 
 | 410 | } | 
 | 411 |  | 
 | 412 | static int ad7879_gpio_get_value(struct gpio_chip *chip, unsigned gpio) | 
 | 413 | { | 
 | 414 | 	struct ad7879 *ts = container_of(chip, struct ad7879, gc); | 
 | 415 | 	u16 val; | 
 | 416 |  | 
 | 417 | 	mutex_lock(&ts->mutex); | 
| Mike Frysinger | 4397c98 | 2010-06-30 01:40:52 -0700 | [diff] [blame] | 418 | 	val = ad7879_read(ts, AD7879_REG_CTRL2); | 
| Michael Hennerich | ec51b7f | 2010-01-19 00:27:58 -0800 | [diff] [blame] | 419 | 	mutex_unlock(&ts->mutex); | 
 | 420 |  | 
 | 421 | 	return !!(val & AD7879_GPIO_DATA); | 
 | 422 | } | 
 | 423 |  | 
 | 424 | static void ad7879_gpio_set_value(struct gpio_chip *chip, | 
 | 425 | 				  unsigned gpio, int value) | 
 | 426 | { | 
 | 427 | 	struct ad7879 *ts = container_of(chip, struct ad7879, gc); | 
 | 428 |  | 
 | 429 | 	mutex_lock(&ts->mutex); | 
 | 430 | 	if (value) | 
 | 431 | 		ts->cmd_crtl2 |= AD7879_GPIO_DATA; | 
 | 432 | 	else | 
 | 433 | 		ts->cmd_crtl2 &= ~AD7879_GPIO_DATA; | 
 | 434 |  | 
| Mike Frysinger | 4397c98 | 2010-06-30 01:40:52 -0700 | [diff] [blame] | 435 | 	ad7879_write(ts, AD7879_REG_CTRL2, ts->cmd_crtl2); | 
| Michael Hennerich | ec51b7f | 2010-01-19 00:27:58 -0800 | [diff] [blame] | 436 | 	mutex_unlock(&ts->mutex); | 
 | 437 | } | 
 | 438 |  | 
| Mike Frysinger | 4397c98 | 2010-06-30 01:40:52 -0700 | [diff] [blame] | 439 | static int ad7879_gpio_add(struct ad7879 *ts, | 
 | 440 | 			   const struct ad7879_platform_data *pdata) | 
| Michael Hennerich | ec51b7f | 2010-01-19 00:27:58 -0800 | [diff] [blame] | 441 | { | 
| Michael Hennerich | ec51b7f | 2010-01-19 00:27:58 -0800 | [diff] [blame] | 442 | 	int ret = 0; | 
 | 443 |  | 
| Dmitry Torokhov | 14fbbc3 | 2010-06-30 14:50:51 -0700 | [diff] [blame] | 444 | 	mutex_init(&ts->mutex); | 
 | 445 |  | 
| Michael Hennerich | ec51b7f | 2010-01-19 00:27:58 -0800 | [diff] [blame] | 446 | 	if (pdata->gpio_export) { | 
 | 447 | 		ts->gc.direction_input = ad7879_gpio_direction_input; | 
 | 448 | 		ts->gc.direction_output = ad7879_gpio_direction_output; | 
 | 449 | 		ts->gc.get = ad7879_gpio_get_value; | 
 | 450 | 		ts->gc.set = ad7879_gpio_set_value; | 
 | 451 | 		ts->gc.can_sleep = 1; | 
 | 452 | 		ts->gc.base = pdata->gpio_base; | 
 | 453 | 		ts->gc.ngpio = 1; | 
 | 454 | 		ts->gc.label = "AD7879-GPIO"; | 
 | 455 | 		ts->gc.owner = THIS_MODULE; | 
| Mike Frysinger | 4397c98 | 2010-06-30 01:40:52 -0700 | [diff] [blame] | 456 | 		ts->gc.dev = ts->dev; | 
| Michael Hennerich | ec51b7f | 2010-01-19 00:27:58 -0800 | [diff] [blame] | 457 |  | 
 | 458 | 		ret = gpiochip_add(&ts->gc); | 
 | 459 | 		if (ret) | 
| Mike Frysinger | 4397c98 | 2010-06-30 01:40:52 -0700 | [diff] [blame] | 460 | 			dev_err(ts->dev, "failed to register gpio %d\n", | 
| Michael Hennerich | ec51b7f | 2010-01-19 00:27:58 -0800 | [diff] [blame] | 461 | 				ts->gc.base); | 
 | 462 | 	} | 
 | 463 |  | 
 | 464 | 	return ret; | 
 | 465 | } | 
 | 466 |  | 
| Mike Frysinger | 4397c98 | 2010-06-30 01:40:52 -0700 | [diff] [blame] | 467 | static void ad7879_gpio_remove(struct ad7879 *ts) | 
| Michael Hennerich | ec51b7f | 2010-01-19 00:27:58 -0800 | [diff] [blame] | 468 | { | 
| Mike Frysinger | 4397c98 | 2010-06-30 01:40:52 -0700 | [diff] [blame] | 469 | 	const struct ad7879_platform_data *pdata = ts->dev->platform_data; | 
| Michael Hennerich | ec51b7f | 2010-01-19 00:27:58 -0800 | [diff] [blame] | 470 | 	int ret; | 
 | 471 |  | 
 | 472 | 	if (pdata->gpio_export) { | 
 | 473 | 		ret = gpiochip_remove(&ts->gc); | 
 | 474 | 		if (ret) | 
| Mike Frysinger | 4397c98 | 2010-06-30 01:40:52 -0700 | [diff] [blame] | 475 | 			dev_err(ts->dev, "failed to remove gpio %d\n", | 
| Michael Hennerich | ec51b7f | 2010-01-19 00:27:58 -0800 | [diff] [blame] | 476 | 				ts->gc.base); | 
 | 477 | 	} | 
 | 478 | } | 
 | 479 | #else | 
| Mike Frysinger | 4397c98 | 2010-06-30 01:40:52 -0700 | [diff] [blame] | 480 | static inline int ad7879_gpio_add(struct ad7879 *ts, | 
 | 481 | 				  const struct ad7879_platform_data *pdata) | 
| Michael Hennerich | ec51b7f | 2010-01-19 00:27:58 -0800 | [diff] [blame] | 482 | { | 
 | 483 | 	return 0; | 
 | 484 | } | 
 | 485 |  | 
| Mike Frysinger | 4397c98 | 2010-06-30 01:40:52 -0700 | [diff] [blame] | 486 | static inline void ad7879_gpio_remove(struct ad7879 *ts) | 
| Michael Hennerich | ec51b7f | 2010-01-19 00:27:58 -0800 | [diff] [blame] | 487 | { | 
 | 488 | } | 
 | 489 | #endif | 
 | 490 |  | 
| Mike Frysinger | 4397c98 | 2010-06-30 01:40:52 -0700 | [diff] [blame] | 491 | struct ad7879 *ad7879_probe(struct device *dev, u8 devid, unsigned int irq, | 
 | 492 | 			    const struct ad7879_bus_ops *bops) | 
| Michael Hennerich | b4be468 | 2009-03-09 20:12:52 -0700 | [diff] [blame] | 493 | { | 
| Mike Frysinger | 4397c98 | 2010-06-30 01:40:52 -0700 | [diff] [blame] | 494 | 	struct ad7879_platform_data *pdata = dev->platform_data; | 
 | 495 | 	struct ad7879 *ts; | 
| Michael Hennerich | b4be468 | 2009-03-09 20:12:52 -0700 | [diff] [blame] | 496 | 	struct input_dev *input_dev; | 
| Michael Hennerich | b4be468 | 2009-03-09 20:12:52 -0700 | [diff] [blame] | 497 | 	int err; | 
 | 498 | 	u16 revid; | 
 | 499 |  | 
| Mike Frysinger | 4397c98 | 2010-06-30 01:40:52 -0700 | [diff] [blame] | 500 | 	if (!irq) { | 
 | 501 | 		dev_err(dev, "no IRQ?\n"); | 
 | 502 | 		err = -EINVAL; | 
 | 503 | 		goto err_out; | 
| Michael Hennerich | b4be468 | 2009-03-09 20:12:52 -0700 | [diff] [blame] | 504 | 	} | 
 | 505 |  | 
 | 506 | 	if (!pdata) { | 
| Mike Frysinger | 4397c98 | 2010-06-30 01:40:52 -0700 | [diff] [blame] | 507 | 		dev_err(dev, "no platform data?\n"); | 
 | 508 | 		err = -EINVAL; | 
 | 509 | 		goto err_out; | 
| Michael Hennerich | b4be468 | 2009-03-09 20:12:52 -0700 | [diff] [blame] | 510 | 	} | 
 | 511 |  | 
| Mike Frysinger | 4397c98 | 2010-06-30 01:40:52 -0700 | [diff] [blame] | 512 | 	ts = kzalloc(sizeof(*ts), GFP_KERNEL); | 
| Michael Hennerich | b4be468 | 2009-03-09 20:12:52 -0700 | [diff] [blame] | 513 | 	input_dev = input_allocate_device(); | 
| Mike Frysinger | 4397c98 | 2010-06-30 01:40:52 -0700 | [diff] [blame] | 514 | 	if (!ts || !input_dev) { | 
 | 515 | 		err = -ENOMEM; | 
 | 516 | 		goto err_free_mem; | 
 | 517 | 	} | 
| Michael Hennerich | b4be468 | 2009-03-09 20:12:52 -0700 | [diff] [blame] | 518 |  | 
| Mike Frysinger | 4397c98 | 2010-06-30 01:40:52 -0700 | [diff] [blame] | 519 | 	ts->bops = bops; | 
 | 520 | 	ts->dev = dev; | 
| Michael Hennerich | b4be468 | 2009-03-09 20:12:52 -0700 | [diff] [blame] | 521 | 	ts->input = input_dev; | 
| Dmitry Torokhov | 14fbbc3 | 2010-06-30 14:50:51 -0700 | [diff] [blame] | 522 | 	ts->irq = irq; | 
| Michael Hennerich | b4be468 | 2009-03-09 20:12:52 -0700 | [diff] [blame] | 523 |  | 
 | 524 | 	setup_timer(&ts->timer, ad7879_timer, (unsigned long) ts); | 
| Mike Frysinger | 4397c98 | 2010-06-30 01:40:52 -0700 | [diff] [blame] | 525 |  | 
| Michael Hennerich | b4be468 | 2009-03-09 20:12:52 -0700 | [diff] [blame] | 526 | 	ts->x_plate_ohms = pdata->x_plate_ohms ? : 400; | 
 | 527 | 	ts->pressure_max = pdata->pressure_max ? : ~0; | 
 | 528 |  | 
 | 529 | 	ts->first_conversion_delay = pdata->first_conversion_delay; | 
 | 530 | 	ts->acquisition_time = pdata->acquisition_time; | 
 | 531 | 	ts->averaging = pdata->averaging; | 
 | 532 | 	ts->pen_down_acc_interval = pdata->pen_down_acc_interval; | 
 | 533 | 	ts->median = pdata->median; | 
 | 534 |  | 
| Mike Frysinger | 4397c98 | 2010-06-30 01:40:52 -0700 | [diff] [blame] | 535 | 	snprintf(ts->phys, sizeof(ts->phys), "%s/input0", dev_name(dev)); | 
| Michael Hennerich | b4be468 | 2009-03-09 20:12:52 -0700 | [diff] [blame] | 536 |  | 
 | 537 | 	input_dev->name = "AD7879 Touchscreen"; | 
 | 538 | 	input_dev->phys = ts->phys; | 
| Mike Frysinger | 4397c98 | 2010-06-30 01:40:52 -0700 | [diff] [blame] | 539 | 	input_dev->dev.parent = dev; | 
 | 540 | 	input_dev->id.bustype = bops->bustype; | 
| Michael Hennerich | b4be468 | 2009-03-09 20:12:52 -0700 | [diff] [blame] | 541 |  | 
| Dmitry Torokhov | 14fbbc3 | 2010-06-30 14:50:51 -0700 | [diff] [blame] | 542 | 	input_dev->open = ad7879_open; | 
 | 543 | 	input_dev->close = ad7879_close; | 
 | 544 |  | 
 | 545 | 	input_set_drvdata(input_dev, ts); | 
 | 546 |  | 
| Michael Hennerich | b4be468 | 2009-03-09 20:12:52 -0700 | [diff] [blame] | 547 | 	__set_bit(EV_ABS, input_dev->evbit); | 
 | 548 | 	__set_bit(ABS_X, input_dev->absbit); | 
 | 549 | 	__set_bit(ABS_Y, input_dev->absbit); | 
 | 550 | 	__set_bit(ABS_PRESSURE, input_dev->absbit); | 
 | 551 |  | 
| Michael Hennerich | 963ce8a | 2010-06-30 14:51:10 -0700 | [diff] [blame] | 552 | 	__set_bit(EV_KEY, input_dev->evbit); | 
 | 553 | 	__set_bit(BTN_TOUCH, input_dev->keybit); | 
 | 554 |  | 
| Michael Hennerich | b4be468 | 2009-03-09 20:12:52 -0700 | [diff] [blame] | 555 | 	input_set_abs_params(input_dev, ABS_X, | 
 | 556 | 			pdata->x_min ? : 0, | 
 | 557 | 			pdata->x_max ? : MAX_12BIT, | 
 | 558 | 			0, 0); | 
 | 559 | 	input_set_abs_params(input_dev, ABS_Y, | 
 | 560 | 			pdata->y_min ? : 0, | 
 | 561 | 			pdata->y_max ? : MAX_12BIT, | 
 | 562 | 			0, 0); | 
 | 563 | 	input_set_abs_params(input_dev, ABS_PRESSURE, | 
 | 564 | 			pdata->pressure_min, pdata->pressure_max, 0, 0); | 
 | 565 |  | 
| Mike Frysinger | 4397c98 | 2010-06-30 01:40:52 -0700 | [diff] [blame] | 566 | 	err = ad7879_write(ts, AD7879_REG_CTRL2, AD7879_RESET); | 
| Michael Hennerich | b4be468 | 2009-03-09 20:12:52 -0700 | [diff] [blame] | 567 | 	if (err < 0) { | 
| Mike Frysinger | 4397c98 | 2010-06-30 01:40:52 -0700 | [diff] [blame] | 568 | 		dev_err(dev, "Failed to write %s\n", input_dev->name); | 
| Michael Hennerich | b4be468 | 2009-03-09 20:12:52 -0700 | [diff] [blame] | 569 | 		goto err_free_mem; | 
 | 570 | 	} | 
 | 571 |  | 
| Mike Frysinger | 4397c98 | 2010-06-30 01:40:52 -0700 | [diff] [blame] | 572 | 	revid = ad7879_read(ts, AD7879_REG_REVID); | 
 | 573 | 	input_dev->id.product = (revid & 0xff); | 
 | 574 | 	input_dev->id.version = revid >> 8; | 
 | 575 | 	if (input_dev->id.product != devid) { | 
 | 576 | 		dev_err(dev, "Failed to probe %s (%x vs %x)\n", | 
 | 577 | 			input_dev->name, devid, revid); | 
| Michael Hennerich | b4be468 | 2009-03-09 20:12:52 -0700 | [diff] [blame] | 578 | 		err = -ENODEV; | 
 | 579 | 		goto err_free_mem; | 
 | 580 | 	} | 
 | 581 |  | 
| Michael Hennerich | ec51b7f | 2010-01-19 00:27:58 -0800 | [diff] [blame] | 582 | 	ts->cmd_crtl3 = AD7879_YPLUS_BIT | | 
 | 583 | 			AD7879_XPLUS_BIT | | 
 | 584 | 			AD7879_Z2_BIT | | 
 | 585 | 			AD7879_Z1_BIT | | 
 | 586 | 			AD7879_TEMPMASK_BIT | | 
 | 587 | 			AD7879_AUXVBATMASK_BIT | | 
 | 588 | 			AD7879_GPIOALERTMASK_BIT; | 
 | 589 |  | 
 | 590 | 	ts->cmd_crtl2 = AD7879_PM(AD7879_PM_DYN) | AD7879_DFR | | 
 | 591 | 			AD7879_AVG(ts->averaging) | | 
 | 592 | 			AD7879_MFS(ts->median) | | 
 | 593 | 			AD7879_FCD(ts->first_conversion_delay); | 
 | 594 |  | 
 | 595 | 	ts->cmd_crtl1 = AD7879_MODE_INT | AD7879_MODE_SEQ1 | | 
 | 596 | 			AD7879_ACQ(ts->acquisition_time) | | 
 | 597 | 			AD7879_TMR(ts->pen_down_acc_interval); | 
 | 598 |  | 
| Mike Frysinger | 4397c98 | 2010-06-30 01:40:52 -0700 | [diff] [blame] | 599 | 	err = request_threaded_irq(ts->irq, NULL, ad7879_irq, | 
| Dmitry Torokhov | 7cd7a82 | 2010-06-30 01:40:52 -0700 | [diff] [blame] | 600 | 				   IRQF_TRIGGER_FALLING, | 
| Mike Frysinger | 4397c98 | 2010-06-30 01:40:52 -0700 | [diff] [blame] | 601 | 				   dev_name(dev), ts); | 
| Michael Hennerich | b4be468 | 2009-03-09 20:12:52 -0700 | [diff] [blame] | 602 | 	if (err) { | 
| Mike Frysinger | 4397c98 | 2010-06-30 01:40:52 -0700 | [diff] [blame] | 603 | 		dev_err(dev, "irq %d busy?\n", ts->irq); | 
| Michael Hennerich | b4be468 | 2009-03-09 20:12:52 -0700 | [diff] [blame] | 604 | 		goto err_free_mem; | 
 | 605 | 	} | 
 | 606 |  | 
| Dmitry Torokhov | 14fbbc3 | 2010-06-30 14:50:51 -0700 | [diff] [blame] | 607 | 	__ad7879_disable(ts); | 
 | 608 |  | 
| Mike Frysinger | 4397c98 | 2010-06-30 01:40:52 -0700 | [diff] [blame] | 609 | 	err = sysfs_create_group(&dev->kobj, &ad7879_attr_group); | 
| Michael Hennerich | b4be468 | 2009-03-09 20:12:52 -0700 | [diff] [blame] | 610 | 	if (err) | 
 | 611 | 		goto err_free_irq; | 
 | 612 |  | 
| Mike Frysinger | 4397c98 | 2010-06-30 01:40:52 -0700 | [diff] [blame] | 613 | 	err = ad7879_gpio_add(ts, pdata); | 
| Michael Hennerich | b4be468 | 2009-03-09 20:12:52 -0700 | [diff] [blame] | 614 | 	if (err) | 
 | 615 | 		goto err_remove_attr; | 
 | 616 |  | 
| Michael Hennerich | ec51b7f | 2010-01-19 00:27:58 -0800 | [diff] [blame] | 617 | 	err = input_register_device(input_dev); | 
 | 618 | 	if (err) | 
 | 619 | 		goto err_remove_gpio; | 
 | 620 |  | 
| Mike Frysinger | 4397c98 | 2010-06-30 01:40:52 -0700 | [diff] [blame] | 621 | 	return ts; | 
| Michael Hennerich | b4be468 | 2009-03-09 20:12:52 -0700 | [diff] [blame] | 622 |  | 
| Michael Hennerich | ec51b7f | 2010-01-19 00:27:58 -0800 | [diff] [blame] | 623 | err_remove_gpio: | 
| Mike Frysinger | 4397c98 | 2010-06-30 01:40:52 -0700 | [diff] [blame] | 624 | 	ad7879_gpio_remove(ts); | 
| Michael Hennerich | b4be468 | 2009-03-09 20:12:52 -0700 | [diff] [blame] | 625 | err_remove_attr: | 
| Mike Frysinger | 4397c98 | 2010-06-30 01:40:52 -0700 | [diff] [blame] | 626 | 	sysfs_remove_group(&dev->kobj, &ad7879_attr_group); | 
| Michael Hennerich | b4be468 | 2009-03-09 20:12:52 -0700 | [diff] [blame] | 627 | err_free_irq: | 
| Mike Frysinger | 4397c98 | 2010-06-30 01:40:52 -0700 | [diff] [blame] | 628 | 	free_irq(ts->irq, ts); | 
| Michael Hennerich | b4be468 | 2009-03-09 20:12:52 -0700 | [diff] [blame] | 629 | err_free_mem: | 
 | 630 | 	input_free_device(input_dev); | 
| Mike Frysinger | 4397c98 | 2010-06-30 01:40:52 -0700 | [diff] [blame] | 631 | 	kfree(ts); | 
 | 632 | err_out: | 
 | 633 | 	return ERR_PTR(err); | 
| Michael Hennerich | b4be468 | 2009-03-09 20:12:52 -0700 | [diff] [blame] | 634 | } | 
| Mike Frysinger | 4397c98 | 2010-06-30 01:40:52 -0700 | [diff] [blame] | 635 | EXPORT_SYMBOL(ad7879_probe); | 
| Michael Hennerich | b4be468 | 2009-03-09 20:12:52 -0700 | [diff] [blame] | 636 |  | 
| Mike Frysinger | 4397c98 | 2010-06-30 01:40:52 -0700 | [diff] [blame] | 637 | void ad7879_remove(struct ad7879 *ts) | 
| Michael Hennerich | b4be468 | 2009-03-09 20:12:52 -0700 | [diff] [blame] | 638 | { | 
| Mike Frysinger | 4397c98 | 2010-06-30 01:40:52 -0700 | [diff] [blame] | 639 | 	ad7879_gpio_remove(ts); | 
| Mike Frysinger | 4397c98 | 2010-06-30 01:40:52 -0700 | [diff] [blame] | 640 | 	sysfs_remove_group(&ts->dev->kobj, &ad7879_attr_group); | 
 | 641 | 	free_irq(ts->irq, ts); | 
| Michael Hennerich | b4be468 | 2009-03-09 20:12:52 -0700 | [diff] [blame] | 642 | 	input_unregister_device(ts->input); | 
| Michael Hennerich | b4be468 | 2009-03-09 20:12:52 -0700 | [diff] [blame] | 643 | 	kfree(ts); | 
| Michael Hennerich | b4be468 | 2009-03-09 20:12:52 -0700 | [diff] [blame] | 644 | } | 
| Mike Frysinger | 4397c98 | 2010-06-30 01:40:52 -0700 | [diff] [blame] | 645 | EXPORT_SYMBOL(ad7879_remove); | 
| Michael Hennerich | b4be468 | 2009-03-09 20:12:52 -0700 | [diff] [blame] | 646 |  | 
 | 647 | MODULE_AUTHOR("Michael Hennerich <hennerich@blackfin.uclinux.org>"); | 
 | 648 | MODULE_DESCRIPTION("AD7879(-1) touchscreen Driver"); | 
 | 649 | MODULE_LICENSE("GPL"); |