diff options
Diffstat (limited to 'drivers')
| -rw-r--r-- | drivers/input/touchscreen/ads7846.c | 135 | 
1 files changed, 95 insertions, 40 deletions
diff --git a/drivers/input/touchscreen/ads7846.c b/drivers/input/touchscreen/ads7846.c index c6164b6f476a..03e527c16c23 100644 --- a/drivers/input/touchscreen/ads7846.c +++ b/drivers/input/touchscreen/ads7846.c @@ -63,11 +63,11 @@ struct ts_event {  	/* For portability, we can't read 12 bit values using SPI (which  	 * would make the controller deliver them as native byteorder u16  	 * with msbs zeroed).  Instead, we read them as two 8-bit values, -	 * which need byteswapping then range adjustment. +	 * *** WHICH NEED BYTESWAPPING *** and range adjustment.  	 */ -	__be16 x; -	__be16 y; -	__be16 z1, z2; +	u16	x; +	u16	y; +	u16	z1, z2;  	int    ignore;  }; @@ -106,6 +106,9 @@ struct ads7846 {  	unsigned		irq_disabled:1;	/* P: lock */  	unsigned		disabled:1; +	int			(*filter)(void *data, int data_idx, int *val); +	void			*filter_data; +	void			(*filter_cleanup)(void *data);  	int			(*get_pendown_state)(void);  }; @@ -379,13 +382,13 @@ static void ads7846_rx(void *ads)  	u16			x, y, z1, z2;  	unsigned long		flags; -	/* adjust:  on-wire is a must-ignore bit, a BE12 value, then padding; -	 * built from two 8 bit values written msb-first. +	/* ads7846_rx_val() did in-place conversion (including byteswap) from +	 * on-the-wire format as part of debouncing to get stable readings.  	 */ -	x = (be16_to_cpu(ts->tc.x) >> 3) & 0x0fff; -	y = (be16_to_cpu(ts->tc.y) >> 3) & 0x0fff; -	z1 = (be16_to_cpu(ts->tc.z1) >> 3) & 0x0fff; -	z2 = (be16_to_cpu(ts->tc.z2) >> 3) & 0x0fff; +	x = ts->tc.x; +	y = ts->tc.y; +	z1 = ts->tc.z1; +	z2 = ts->tc.z2;  	/* range filtering */  	if (x == MAX_12BIT) @@ -453,50 +456,85 @@ static void ads7846_rx(void *ads)  	spin_unlock_irqrestore(&ts->lock, flags);  } -static void ads7846_debounce(void *ads) +static int ads7846_debounce(void *ads, int data_idx, int *val)  {  	struct ads7846		*ts = ads; -	struct spi_message	*m; -	struct spi_transfer	*t; -	int			val; -	int			status; -	m = &ts->msg[ts->msg_idx]; -	t = list_entry(m->transfers.prev, struct spi_transfer, transfer_list); -	val = (be16_to_cpu(*(__be16 *)t->rx_buf) >> 3) & 0x0fff; -	if (!ts->read_cnt || (abs(ts->last_read - val) > ts->debounce_tol)) { +	if (!ts->read_cnt || (abs(ts->last_read - *val) > ts->debounce_tol)) { +		/* Start over collecting consistent readings. */ +		ts->read_rep = 0;  		/* Repeat it, if this was the first read or the read  		 * wasn't consistent enough. */  		if (ts->read_cnt < ts->debounce_max) { -			ts->last_read = val; +			ts->last_read = *val;  			ts->read_cnt++; +			return ADS7846_FILTER_REPEAT;  		} else {  			/* Maximum number of debouncing reached and still  			 * not enough number of consistent readings. Abort  			 * the whole sample, repeat it in the next sampling  			 * period.  			 */ -			ts->tc.ignore = 1;  			ts->read_cnt = 0; -			/* Last message will contain ads7846_rx() as the -			 * completion function. -			 */ -			m = ts->last_msg; +			return ADS7846_FILTER_IGNORE;  		} -		/* Start over collecting consistent readings. */ -		ts->read_rep = 0;  	} else {  		if (++ts->read_rep > ts->debounce_rep) {  			/* Got a good reading for this coordinate,  			 * go for the next one. */ -			ts->tc.ignore = 0; -			ts->msg_idx++;  			ts->read_cnt = 0;  			ts->read_rep = 0; -			m++; -		} else +			return ADS7846_FILTER_OK; +		} else {  			/* Read more values that are consistent. */  			ts->read_cnt++; +			return ADS7846_FILTER_REPEAT; +		} +	} +} + +static int ads7846_no_filter(void *ads, int data_idx, int *val) +{ +	return ADS7846_FILTER_OK; +} + +static void ads7846_rx_val(void *ads) +{ +	struct ads7846 *ts = ads; +	struct spi_message *m; +	struct spi_transfer *t; +	u16 *rx_val; +	int val; +	int action; +	int status; + +	m = &ts->msg[ts->msg_idx]; +	t = list_entry(m->transfers.prev, struct spi_transfer, transfer_list); +	rx_val = t->rx_buf; + +	/* adjust:  on-wire is a must-ignore bit, a BE12 value, then padding; +	 * built from two 8 bit values written msb-first. +	 */ +	val = be16_to_cpu(*rx_val) >> 3; + +	action = ts->filter(ts->filter_data, ts->msg_idx, &val); +	switch (action) { +	case ADS7846_FILTER_REPEAT: +		break; +	case ADS7846_FILTER_IGNORE: +		ts->tc.ignore = 1; +		/* Last message will contain ads7846_rx() as the +		 * completion function. +		 */ +		m = ts->last_msg; +		break; +	case ADS7846_FILTER_OK: +		*rx_val = val; +		ts->tc.ignore = 0; +		m = &ts->msg[++ts->msg_idx]; +		break; +	default: +		BUG();  	}  	status = spi_async(ts->spi, m);  	if (status) @@ -689,14 +727,25 @@ static int __devinit ads7846_probe(struct spi_device *spi)  	ts->vref_delay_usecs = pdata->vref_delay_usecs ? : 100;  	ts->x_plate_ohms = pdata->x_plate_ohms ? : 400;  	ts->pressure_max = pdata->pressure_max ? : ~0; -	if (pdata->debounce_max) { + +	if (pdata->filter != NULL) { +		if (pdata->filter_init != NULL) { +			err = pdata->filter_init(pdata, &ts->filter_data); +			if (err < 0) +				goto err_free_mem; +		} +		ts->filter = pdata->filter; +		ts->filter_cleanup = pdata->filter_cleanup; +	} else if (pdata->debounce_max) {  		ts->debounce_max = pdata->debounce_max; +		if (ts->debounce_max < 2) +			ts->debounce_max = 2;  		ts->debounce_tol = pdata->debounce_tol;  		ts->debounce_rep = pdata->debounce_rep; -		if (ts->debounce_rep > ts->debounce_max + 1) -			ts->debounce_rep = ts->debounce_max - 1; +		ts->filter = ads7846_debounce; +		ts->filter_data = ts;  	} else -		ts->debounce_tol = ~0; +		ts->filter = ads7846_no_filter;  	ts->get_pendown_state = pdata->get_pendown_state;  	snprintf(ts->phys, sizeof(ts->phys), "%s/input0", spi->dev.bus_id); @@ -737,7 +786,7 @@ static int __devinit ads7846_probe(struct spi_device *spi)  	x->len = 2;  	spi_message_add_tail(x, m); -	m->complete = ads7846_debounce; +	m->complete = ads7846_rx_val;  	m->context = ts;  	m++; @@ -755,7 +804,7 @@ static int __devinit ads7846_probe(struct spi_device *spi)  	x->len = 2;  	spi_message_add_tail(x, m); -	m->complete = ads7846_debounce; +	m->complete = ads7846_rx_val;  	m->context = ts;  	/* turn y+ off, x- on; we'll use formula #2 */ @@ -774,7 +823,7 @@ static int __devinit ads7846_probe(struct spi_device *spi)  		x->len = 2;  		spi_message_add_tail(x, m); -		m->complete = ads7846_debounce; +		m->complete = ads7846_rx_val;  		m->context = ts;  		m++; @@ -791,7 +840,7 @@ static int __devinit ads7846_probe(struct spi_device *spi)  		x->len = 2;  		spi_message_add_tail(x, m); -		m->complete = ads7846_debounce; +		m->complete = ads7846_rx_val;  		m->context = ts;  	} @@ -820,7 +869,7 @@ static int __devinit ads7846_probe(struct spi_device *spi)  			spi->dev.driver->name, ts)) {  		dev_dbg(&spi->dev, "irq %d busy?\n", spi->irq);  		err = -EBUSY; -		goto err_free_mem; +		goto err_cleanup_filter;  	}  	dev_info(&spi->dev, "touchscreen, irq %d\n", spi->irq); @@ -856,6 +905,9 @@ static int __devinit ads7846_probe(struct spi_device *spi)  	sysfs_remove_group(&spi->dev.kobj, ts->attr_group);   err_free_irq:  	free_irq(spi->irq, ts); + err_cleanup_filter: +	if (ts->filter_cleanup) +		ts->filter_cleanup(ts->filter_data);   err_free_mem:  	input_free_device(input_dev);  	kfree(ts); @@ -876,6 +928,9 @@ static int __devexit ads7846_remove(struct spi_device *spi)  	/* suspend left the IRQ disabled */  	enable_irq(ts->spi->irq); +	if (ts->filter_cleanup) +		ts->filter_cleanup(ts->filter_data); +  	kfree(ts);  	dev_dbg(&spi->dev, "unregistered touchscreen\n");  |