⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 ads7846.c

📁 Touchright serial touchscreen driver
💻 C
📖 第 1 页 / 共 2 页
字号:
/* * ADS7846 based touchscreen and sensor driver * * Copyright (c) 2005 David Brownell * Copyright (c) 2006 Nokia Corporation * Various changes: Imre Deak <imre.deak@nokia.com> * Ads7843 support: Atmel, Nicolas Ferre <nicolas.ferre@rfo.atmel.com> * * Using code from: *  - 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. */#include <linux/device.h>#include <linux/init.h>#include <linux/delay.h>#include <linux/input.h>#include <linux/interrupt.h>#include <linux/slab.h>#include <linux/spi/spi.h>#include <linux/spi/ads7846.h>#include <asm/irq.h>#ifdef	CONFIG_ARM#include <asm/mach-types.h>#ifdef	CONFIG_ARCH_OMAP#include <asm/arch/gpio.h>#endif#endif/* * This code has been heavily tested on a Nokia 770, and lightly * tested on other ads7846 devices (OSK/Mistral, Lubbock). * Support for ads7843 tested on Atmel at91sam9261-EK. * Support for ads7845 has only been stubbed in. * * IRQ handling needs a workaround because of a shortcoming in handling * edge triggered IRQs on some platforms like the OMAP1/2. These * platforms don't handle the ARM lazy IRQ disabling properly, thus we * have to maintain our own SW IRQ disabled status. This should be * removed as soon as the affected platform's IRQ handling is fixed. * * app note sbaa036 talks in more detail about accurate sampling... * that ought to help in situations like LCDs inducing noise (which * can also be helped by using synch signals) and more generally. * This driver tries to utilize the measures described in the app * note. The strength of filtering can be set in the board-* specific * files. */#define	TS_POLL_PERIOD	msecs_to_jiffies(10)/* this driver doesn't aim at the peak continuous sample rate */#define	SAMPLE_BITS	(8 /*cmd*/ + 16 /*sample*/ + 2 /* before, after */)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.	 */	__be16 x;	__be16 y;	__be16 z1, z2;	int    ignore;};struct ads7846 {	struct input_dev	*input;	char			phys[32];	struct spi_device	*spi;	u16			model;	u16			vref_delay_usecs;	u16			x_plate_ohms;	u16			pressure_max;	u8			read_x, read_y, read_z1, read_z2, pwrdown;	u16			zerro;		/* to send zerros while receiving */	u16			dummy;		/* for the pwrdown read */	struct ts_event		tc;	struct spi_transfer	xfer[10];	struct spi_message	msg[5];	struct spi_message	*last_msg;	int			msg_idx;	int			read_cnt;	int			read_rep;	int			last_read;	u16			debounce_max;	u16			debounce_tol;	u16			debounce_rep;	spinlock_t		lock;	struct timer_list	timer;		/* P: lock */	unsigned		pendown:1;	/* P: lock */	unsigned		pending:1;	/* P: lock */// FIXME remove "irq_disabled"	unsigned		irq_disabled:1;	/* P: lock */	unsigned		disabled:1;	int			(*get_pendown_state)(void);};/* leave chip selected when we're done, for quicker re-select? */#if	0#define	CS_CHANGE(xfer)	((xfer).cs_change = 1)#else#define	CS_CHANGE(xfer)	((xfer).cs_change = 0)#endif/*--------------------------------------------------------------------------*//* The ADS7846 has touchscreen and other sensors. * Earlier ads784x chips are somewhat compatible. */#define	ADS_START		(1 << 7)#define	ADS_A2A1A0_d_y		(1 << 4)	/* differential */#define	ADS_A2A1A0_d_z1		(3 << 4)	/* differential */#define	ADS_A2A1A0_d_z2		(4 << 4)	/* differential */#define	ADS_A2A1A0_d_x		(5 << 4)	/* differential */#define	ADS_A2A1A0_temp0	(0 << 4)	/* non-differential */#define	ADS_A2A1A0_vbatt	(2 << 4)	/* non-differential */#define	ADS_A2A1A0_vaux		(6 << 4)	/* non-differential */#define	ADS_A2A1A0_temp1	(7 << 4)	/* non-differential */#define	ADS_8_BIT		(1 << 3)#define	ADS_12_BIT		(0 << 3)#define	ADS_SER			(1 << 2)	/* non-differential */#define	ADS_DFR			(0 << 2)	/* differential */#define	ADS_PD10_PDOWN		(0 << 0)	/* lowpower mode + penirq */#define	ADS_PD10_ADC_ON		(1 << 0)	/* ADC on */#define	ADS_PD10_REF_ON		(2 << 0)	/* vREF on + penirq */#define	ADS_PD10_ALL_ON		(3 << 0)	/* ADC + vREF on */#define	MAX_12BIT	((1<<12)-1)/* leave ADC powered up (disables penirq) between differential samples */#define	READ_12BIT_DFR(x) (ADS_START | ADS_A2A1A0_d_ ## x \	| ADS_12_BIT | ADS_DFR)#define	READ_Y	(READ_12BIT_DFR(y)  | ADS_PD10_ADC_ON)#define	READ_Z1	(READ_12BIT_DFR(z1) | ADS_PD10_ADC_ON)#define	READ_Z2	(READ_12BIT_DFR(z2) | ADS_PD10_ADC_ON)#define	READ_X	(READ_12BIT_DFR(x)  | ADS_PD10_ADC_ON)#define	PWRDOWN	(READ_12BIT_DFR(y)  | ADS_PD10_PDOWN)	/* LAST *//* alternate ads7843 commands */#define	ALT_READ_Y	(READ_12BIT_DFR(y)  | ADS_PD10_ALL_ON)#define	ALT_READ_X	(READ_12BIT_DFR(x)  | ADS_PD10_ALL_ON)/* single-ended samples need to first power up reference voltage; * we leave both ADC and VREF powered */#define	READ_12BIT_SER(x) (ADS_START | ADS_A2A1A0_ ## x \	| ADS_12_BIT | ADS_SER)#define	REF_ON	(READ_12BIT_DFR(x) | ADS_PD10_ALL_ON)#define	REF_OFF	(READ_12BIT_DFR(y) | ADS_PD10_PDOWN)/*--------------------------------------------------------------------------*//* * Non-touchscreen sensors only use single-ended conversions. */struct ser_req {	u8			ref_on;	u8			command;	u8			ref_off;	u16			scratch;	u16			zerro;	__be16			sample;	struct spi_message	msg;	struct spi_transfer	xfer[6];};static void ads7846_enable(struct ads7846 *ts);static void ads7846_disable(struct ads7846 *ts);static int device_suspended(struct device *dev){	struct ads7846 *ts = dev_get_drvdata(dev);	return dev->power.power_state.event != PM_EVENT_ON || ts->disabled;}static int ads7846_read12_ser(struct device *dev, unsigned command){	struct spi_device	*spi = to_spi_device(dev);	struct ads7846		*ts = dev_get_drvdata(dev);	struct ser_req		*req = kzalloc(sizeof *req, SLAB_KERNEL);	int			status;	int			sample;	int			i;	if (!req)		return -ENOMEM;	spi_message_init(&req->msg);	/* activate reference, so it has time to settle; */	req->ref_on = REF_ON;	req->xfer[0].tx_buf = &req->ref_on;	req->xfer[0].len = 1;	req->xfer[1].tx_buf = &req->zerro;	req->xfer[1].rx_buf = &req->scratch;	req->xfer[1].len = 2;	/*	 * for external VREF, 0 usec (and assume it's always on);	 * for 1uF, use 800 usec;	 * no cap, 100 usec.	 */	req->xfer[1].delay_usecs = ts->vref_delay_usecs;	/* take sample */	req->command = (u8) command;	req->xfer[2].tx_buf = &req->command;	req->xfer[2].len = 1;	req->xfer[3].tx_buf = &req->zerro;	req->xfer[3].rx_buf = &req->sample;	req->xfer[3].len = 2;	/* REVISIT:  take a few more samples, and compare ... */	/* turn off reference */	req->ref_off = REF_OFF;	req->xfer[4].tx_buf = &req->ref_off;	req->xfer[4].len = 1;	req->xfer[3].tx_buf = &req->zerro;	req->xfer[5].rx_buf = &req->scratch;	req->xfer[5].len = 2;	CS_CHANGE(req->xfer[5]);	/* group all the transfers together, so we can't interfere with	 * reading touchscreen state; disable penirq while sampling	 */	for (i = 0; i < 6; i++)		spi_message_add_tail(&req->xfer[i], &req->msg);	ts->irq_disabled = 1;	disable_irq(spi->irq);	status = spi_sync(spi, &req->msg);	ts->irq_disabled = 0;	enable_irq(spi->irq);	if (req->msg.status)		status = req->msg.status;	/* on-wire is a must-ignore bit, a BE12 value, then padding */	sample = be16_to_cpu(req->sample);	sample = sample >> 3;	sample &= 0x0fff;	kfree(req);	return status ? status : sample;}#define SHOW(name) static ssize_t \name ## _show(struct device *dev, struct device_attribute *attr, char *buf) \{ \	ssize_t v = ads7846_read12_ser(dev, \			READ_12BIT_SER(name) | ADS_PD10_ALL_ON); \	if (v < 0) \		return v; \	return sprintf(buf, "%u\n", (unsigned) v); \} \static DEVICE_ATTR(name, S_IRUGO, name ## _show, NULL);SHOW(temp0)SHOW(temp1)SHOW(vaux)SHOW(vbatt)static int is_pen_down(struct device *dev){	struct ads7846		*ts = dev_get_drvdata(dev);	return ts->pendown;}static ssize_t ads7846_pen_down_show(struct device *dev,				     struct device_attribute *attr, char *buf){	return sprintf(buf, "%u\n", is_pen_down(dev));}static DEVICE_ATTR(pen_down, S_IRUGO, ads7846_pen_down_show, NULL);static ssize_t ads7846_disable_show(struct device *dev,				     struct device_attribute *attr, char *buf){	struct ads7846	*ts = dev_get_drvdata(dev);	return sprintf(buf, "%u\n", ts->disabled);}static ssize_t ads7846_disable_store(struct device *dev,				     struct device_attribute *attr,				     const char *buf, size_t count){	struct ads7846 *ts = dev_get_drvdata(dev);	char *endp;	int i;	i = simple_strtoul(buf, &endp, 10);	spin_lock_irq(&ts->lock);	if (i)		ads7846_disable(ts);	else		ads7846_enable(ts);	spin_unlock_irq(&ts->lock);	return count;}static DEVICE_ATTR(disable, 0664, ads7846_disable_show, ads7846_disable_store);/*--------------------------------------------------------------------------*//* * PENIRQ only kicks the timer.  The timer only reissues the SPI transfer, * to retrieve touchscreen status. * * The SPI transfer completion callback does the real work.  It reports * touchscreen events and reactivates the timer (or IRQ) as appropriate. */static void ads7846_rx(void *ads){	struct ads7846		*ts = ads;	struct input_dev	*input_dev = ts->input;	unsigned		Rt;	unsigned		sync = 0;	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.	 */	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;	/* range filtering */	if (x == MAX_12BIT)		x = 0;	if (likely(x && z1 && !device_suspended(&ts->spi->dev))) {		/* compute touch pressure resistance using equation #2 */		Rt = z2;		Rt -= z1;		Rt *= x;		Rt *= ts->x_plate_ohms;		Rt /= z1;		Rt = (Rt + 2047) >> 12;	} else		Rt = 0;	if (ts->model == 7843)		Rt = ts->pressure_max / 2;	/* 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 */	if (ts->tc.ignore || Rt > ts->pressure_max) {		mod_timer(&ts->timer, jiffies + TS_POLL_PERIOD);		return;	}	/* NOTE:  "pendown" is inferred from pressure; we don't rely on	 * being able to check nPENIRQ status, or "friendly" trigger modes	 * (both-edges is much better than just-falling or low-level).	 *	 * REVISIT:  some boards may require reading nPENIRQ; it's	 * needed on 7843.  and 7845 reads pressure differently...	 *	 * REVISIT:  the touchscreen might not be connected; this code	 * won't notice that, even if nPENIRQ never fires ...	 */	if (!ts->pendown && Rt != 0) {		input_report_key(input_dev, BTN_TOUCH, 1);		sync = 1;	} else if (ts->pendown && Rt == 0) {		input_report_key(input_dev, BTN_TOUCH, 0);		sync = 1;	}	if (Rt) {		input_report_abs(input_dev, ABS_X, x);		input_report_abs(input_dev, ABS_Y, y);		sync = 1;	}	if (sync) {		input_report_abs(input_dev, ABS_PRESSURE, Rt);		input_sync(input_dev);	}#ifdef	VERBOSE	if (Rt || ts->pendown)		pr_debug("%s: %d/%d/%d%s\n", ts->spi->dev.bus_id,			x, y, Rt, Rt ? "" : " UP");#endif	spin_lock_irqsave(&ts->lock, flags);	ts->pendown = (Rt != 0);	mod_timer(&ts->timer, jiffies + TS_POLL_PERIOD);	spin_unlock_irqrestore(&ts->lock, flags);}static void ads7843_rx(void *ads){	struct ads7846		*ts = ads;	struct input_dev	*input_dev = ts->input;	u16			x, y;	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.	 */	x = (be16_to_cpu(ts->tc.x) >> 3) & 0x0fff;	y = (be16_to_cpu(ts->tc.y) >> 3) & 0x0fff;	/* range filtering */	if (x == MAX_12BIT)		x = 0;	if (ts->pendown) {		input_report_key(input_dev, BTN_TOUCH, 1);		input_report_abs(input_dev, ABS_PRESSURE, ts->pressure_max / 2);		input_report_abs(input_dev, ABS_X, x);		input_report_abs(input_dev, ABS_Y, y);	} else {		input_report_key(input_dev, BTN_TOUCH, 0);		input_report_abs(input_dev, ABS_PRESSURE, 0);	}	input_sync(input_dev);#ifdef	VERBOSE	pr_debug("%s: %d/%d%s\n", ts->spi->dev.bus_id,		x, y, ts->pendown ? "" : " UP");#endif	if (ts->pendown) {		spin_lock_irqsave(&ts->lock, flags);		mod_timer(&ts->timer, jiffies + TS_POLL_PERIOD);		spin_unlock_irqrestore(&ts->lock, flags);	}}static void ads7846_debounce(void *ads){	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)) {		/* 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->read_cnt++;		} 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;		}		/* 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;

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -