669 lines · 15.0 KB · C
RawDownload
/* * Intel MID Resistive Touch Screen Driver * * Copyright (C) 2008 Intel Corp * * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; version 2 of the License. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.	See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. * * Questions/Comments/Bug fixes to Sreedhara (sreedhara.ds@intel.com) *			    Ramesh Agarwal (ramesh.agarwal@intel.com) * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ * * TODO: *	review conversion of r/m/w sequences */ #include <linux/module.h>#include <linux/init.h>#include <linux/input.h>#include <linux/interrupt.h>#include <linux/err.h>#include <linux/param.h>#include <linux/slab.h>#include <linux/platform_device.h>#include <linux/irq.h>#include <linux/delay.h>#include <asm/intel_scu_ipc.h> /* PMIC Interrupt registers */#define PMIC_REG_ID1		0x00 /* PMIC ID1 register */ /* PMIC Interrupt registers */#define PMIC_REG_INT		0x04 /* PMIC interrupt register */#define PMIC_REG_MINT		0x05 /* PMIC interrupt mask register */ /* ADC Interrupt registers */#define PMIC_REG_ADCINT		0x5F /* ADC interrupt register */#define PMIC_REG_MADCINT	0x60 /* ADC interrupt mask register */ /* ADC Control registers */#define PMIC_REG_ADCCNTL1	0x61 /* ADC control register */ /* ADC Channel Selection registers */#define PMICADDR0		0xA4#define END_OF_CHANNEL		0x1F /* ADC Result register */#define PMIC_REG_ADCSNS0H	0x64 /* ADC channels for touch screen */#define MRST_TS_CHAN10		0xA /* Touch screen X+ connection */#define MRST_TS_CHAN11		0xB /* Touch screen X- connection */#define MRST_TS_CHAN12		0xC /* Touch screen Y+ connection */#define MRST_TS_CHAN13		0xD /* Touch screen Y- connection */ /* Touch screen channel BIAS constants */#define MRST_XBIAS		0x20#define MRST_YBIAS		0x40#define MRST_ZBIAS		0x80 /* Touch screen coordinates */#define MRST_X_MIN		10#define MRST_X_MAX		1024#define MRST_X_FUZZ		5#define MRST_Y_MIN		10#define MRST_Y_MAX		1024#define MRST_Y_FUZZ		5#define MRST_PRESSURE_MIN	0#define MRST_PRESSURE_NOMINAL	50#define MRST_PRESSURE_MAX	100 #define WAIT_ADC_COMPLETION	10 /* msec */ /* PMIC ADC round robin delays */#define ADC_LOOP_DELAY0		0x0 /* Continuous loop */#define ADC_LOOP_DELAY1		0x1 /* 4.5  ms approximate */ /* PMIC Vendor Identifiers */#define PMIC_VENDOR_FS		0 /* PMIC vendor FreeScale */#define PMIC_VENDOR_MAXIM	1 /* PMIC vendor MAXIM */#define PMIC_VENDOR_NEC		2 /* PMIC vendor NEC */#define MRSTOUCH_MAX_CHANNELS	32 /* Maximum ADC channels */ /* Touch screen device structure */struct mrstouch_dev {	struct device *dev; /* device associated with touch screen */	struct input_dev *input;	char phys[32];	u16 asr;		/* Address selection register */	int irq;	unsigned int vendor;	/* PMIC vendor */	unsigned int rev;	/* PMIC revision */ 	int (*read_prepare)(struct mrstouch_dev *tsdev);	int (*read)(struct mrstouch_dev *tsdev, u16 *x, u16 *y, u16 *z);	int (*read_finish)(struct mrstouch_dev *tsdev);};  /*************************** NEC and Maxim Interface ************************/ static int mrstouch_nec_adc_read_prepare(struct mrstouch_dev *tsdev){	return intel_scu_ipc_update_register(PMIC_REG_MADCINT, 0, 0x20);} /* * Enables PENDET interrupt. */static int mrstouch_nec_adc_read_finish(struct mrstouch_dev *tsdev){	int err; 	err = intel_scu_ipc_update_register(PMIC_REG_MADCINT, 0x20, 0x20);	if (!err)		err = intel_scu_ipc_update_register(PMIC_REG_ADCCNTL1, 0, 0x05); 	return err;} /* * Reads PMIC ADC touch screen result * Reads ADC storage registers for higher 7 and lower 3 bits and * converts the two readings into a single value and turns off gain bit */static int mrstouch_ts_chan_read(u16 offset, u16 chan, u16 *vp, u16 *vm){	int err;	u16 result;	u32 res; 	result = PMIC_REG_ADCSNS0H + offset; 	if (chan == MRST_TS_CHAN12)		result += 4; 	err = intel_scu_ipc_ioread32(result, &res);	if (err)		return err; 	/* Mash the bits up */ 	*vp = (res & 0xFF) << 3;	/* Highest 7 bits */	*vp |= (res >> 8) & 0x07;	/* Lower 3 bits */	*vp &= 0x3FF; 	res >>= 16; 	*vm = (res & 0xFF) << 3;	/* Highest 7 bits */	*vm |= (res >> 8) & 0x07;	/* Lower 3 bits */	*vm &= 0x3FF; 	return 0;} /* * Enables X, Y and Z bias values * Enables YPYM for X channels and XPXM for Y channels */static int mrstouch_ts_bias_set(uint offset, uint bias){	int count;	u16 chan, start;	u16 reg[4];	u8 data[4]; 	chan = PMICADDR0 + offset;	start = MRST_TS_CHAN10; 	for (count = 0; count <= 3; count++) {		reg[count] = chan++;		data[count] = bias | (start + count);	} 	return intel_scu_ipc_writev(reg, data, 4);} /* To read touch screen channel values */static int mrstouch_nec_adc_read(struct mrstouch_dev *tsdev,				 u16 *x, u16 *y, u16 *z){	int err;	u16 xm, ym, zm; 	/* configure Y bias for X channels */	err = mrstouch_ts_bias_set(tsdev->asr, MRST_YBIAS);	if (err)		goto ipc_error; 	msleep(WAIT_ADC_COMPLETION); 	/* read x+ and x- channels */	err = mrstouch_ts_chan_read(tsdev->asr, MRST_TS_CHAN10, x, &xm);	if (err)		goto ipc_error; 	/* configure x bias for y channels */	err = mrstouch_ts_bias_set(tsdev->asr, MRST_XBIAS);	if (err)		goto ipc_error; 	msleep(WAIT_ADC_COMPLETION); 	/* read y+ and y- channels */	err = mrstouch_ts_chan_read(tsdev->asr, MRST_TS_CHAN12, y, &ym);	if (err)		goto ipc_error; 	/* configure z bias for x and y channels */	err = mrstouch_ts_bias_set(tsdev->asr, MRST_ZBIAS);	if (err)		goto ipc_error; 	msleep(WAIT_ADC_COMPLETION); 	/* read z+ and z- channels */	err = mrstouch_ts_chan_read(tsdev->asr, MRST_TS_CHAN10, z, &zm);	if (err)		goto ipc_error; 	return 0; ipc_error:	dev_err(tsdev->dev, "ipc error during adc read\n");	return err;}  /*************************** Freescale Interface ************************/ static int mrstouch_fs_adc_read_prepare(struct mrstouch_dev *tsdev){	int err, count;	u16 chan;	u16 reg[5];	u8 data[5]; 	/* Stop the ADC */	err = intel_scu_ipc_update_register(PMIC_REG_MADCINT, 0x00, 0x02);	if (err)		goto ipc_error; 	chan = PMICADDR0 + tsdev->asr; 	/* Set X BIAS */	for (count = 0; count <= 3; count++) {		reg[count] = chan++;		data[count] = 0x2A;	}	reg[count] =  chan++; /* Dummy */	data[count] = 0; 	err = intel_scu_ipc_writev(reg, data, 5);	if (err)		goto ipc_error; 	msleep(WAIT_ADC_COMPLETION); 	/* Set Y BIAS */	for (count = 0; count <= 3; count++) {		reg[count] = chan++;		data[count] = 0x4A;	}	reg[count] = chan++; /* Dummy */	data[count] = 0; 	err = intel_scu_ipc_writev(reg, data, 5);	if (err)		goto ipc_error; 	msleep(WAIT_ADC_COMPLETION); 	/* Set Z BIAS */	err = intel_scu_ipc_iowrite32(chan + 2, 0x8A8A8A8A);	if (err)		goto ipc_error; 	msleep(WAIT_ADC_COMPLETION); 	return 0; ipc_error:	dev_err(tsdev->dev, "ipc error during %s\n", __func__);	return err;} static int mrstouch_fs_adc_read(struct mrstouch_dev *tsdev,				u16 *x, u16 *y, u16 *z){	int err;	u16 result;	u16 reg[4];	u8 data[4]; 	result = PMIC_REG_ADCSNS0H + tsdev->asr; 	reg[0] = result + 4;	reg[1] = result + 5;	reg[2] = result + 16;	reg[3] = result + 17; 	err = intel_scu_ipc_readv(reg, data, 4);	if (err)		goto ipc_error; 	*x = data[0] << 3; /* Higher 7 bits */	*x |= data[1] & 0x7; /* Lower 3 bits */	*x &= 0x3FF; 	*y = data[2] << 3; /* Higher 7 bits */	*y |= data[3] & 0x7; /* Lower 3 bits */	*y &= 0x3FF; 	/* Read Z value */	reg[0] = result + 28;	reg[1] = result + 29; 	err = intel_scu_ipc_readv(reg, data, 4);	if (err)		goto ipc_error; 	*z = data[0] << 3; /* Higher 7 bits */	*z |= data[1] & 0x7; /* Lower 3 bits */	*z &= 0x3FF; 	return 0; ipc_error:	dev_err(tsdev->dev, "ipc error during %s\n", __func__);	return err;} static int mrstouch_fs_adc_read_finish(struct mrstouch_dev *tsdev){	int err, count;	u16 chan;	u16 reg[5];	u8 data[5]; 	/* Clear all TS channels */	chan = PMICADDR0 + tsdev->asr;	for (count = 0; count <= 4; count++) {		reg[count] = chan++;		data[count] = 0;	}	err = intel_scu_ipc_writev(reg, data, 5);	if (err)		goto ipc_error; 	for (count = 0; count <= 4; count++) {		reg[count] = chan++;		data[count] = 0;	}	err = intel_scu_ipc_writev(reg, data, 5);	if (err)		goto ipc_error; 	err = intel_scu_ipc_iowrite32(chan + 2, 0x00000000);	if (err)		goto ipc_error; 	/* Start ADC */	err = intel_scu_ipc_update_register(PMIC_REG_MADCINT, 0x02, 0x02);	if (err)		goto ipc_error; 	return 0; ipc_error:	dev_err(tsdev->dev, "ipc error during %s\n", __func__);	return err;} static void mrstouch_report_event(struct input_dev *input,			unsigned int x, unsigned int y, unsigned int z){	if (z > MRST_PRESSURE_NOMINAL) {		/* Pen touched, report button touch and coordinates */		input_report_key(input, BTN_TOUCH, 1);		input_report_abs(input, ABS_X, x);		input_report_abs(input, ABS_Y, y);	} else {		input_report_key(input, BTN_TOUCH, 0);	} 	input_report_abs(input, ABS_PRESSURE, z);	input_sync(input);} /* PENDET interrupt handler */static irqreturn_t mrstouch_pendet_irq(int irq, void *dev_id){	struct mrstouch_dev *tsdev = dev_id;	u16 x, y, z; 	/*	 * Should we lower thread priority? Probably not, since we are	 * not spinning but sleeping...	 */ 	if (tsdev->read_prepare(tsdev))		goto out; 	do {		if (tsdev->read(tsdev, &x, &y, &z))			break; 		mrstouch_report_event(tsdev->input, x, y, z);	} while (z > MRST_PRESSURE_NOMINAL); 	tsdev->read_finish(tsdev); out:	return IRQ_HANDLED;} /* Utility to read PMIC ID */static int mrstouch_read_pmic_id(uint *vendor, uint *rev){	int err;	u8 r; 	err = intel_scu_ipc_ioread8(PMIC_REG_ID1, &r);	if (err)		return err; 	*vendor = r & 0x7;	*rev = (r >> 3) & 0x7; 	return 0;} /* * Parse ADC channels to find end of the channel configured by other ADC user * NEC and MAXIM requires 4 channels and FreeScale needs 18 channels */static int mrstouch_chan_parse(struct mrstouch_dev *tsdev){	int found = 0;	int err, i;	u8 r8; 	for (i = 0; i < MRSTOUCH_MAX_CHANNELS; i++) {		err = intel_scu_ipc_ioread8(PMICADDR0 + i, &r8);		if (err)			return err; 		if (r8 == END_OF_CHANNEL) {			found = i;			break;		}	} 	if (tsdev->vendor == PMIC_VENDOR_FS) {		if (found > MRSTOUCH_MAX_CHANNELS - 18)			return -ENOSPC;	} else {		if (found > MRSTOUCH_MAX_CHANNELS - 4)			return -ENOSPC;	} 	return found;}  /* * Writes touch screen channels to ADC address selection registers */static int mrstouch_ts_chan_set(uint offset){	u16 chan; 	int ret, count; 	chan = PMICADDR0 + offset;	for (count = 0; count <= 3; count++) {		ret = intel_scu_ipc_iowrite8(chan++, MRST_TS_CHAN10 + count);		if (ret)			return ret;	}	return intel_scu_ipc_iowrite8(chan++, END_OF_CHANNEL);} /* Initialize ADC */static int mrstouch_adc_init(struct mrstouch_dev *tsdev){	int err, start;	u8 ra, rm;

Lines 1–500 of 669.