This thread has been locked.

If you have a related question, please click the "Ask a related question" button in the top right corner. The newly created question will be automatically linked to this question.

TSC2004 X and Y not correct

Other Parts Discussed in Thread: TSC2004

Dears ,

   My customer is using TSC2004 for one resistance touch panel . The commnication  is OK through I2C. But the X/Y data is not correct.

X data read back is 60-255 , which should be 0-480.

X data read back is 120-255, which should be 0-800.

It seems in 8 bit mode.

Can you give me some adivce?

We use the code as the attached file.

Thanks

 

Bruce.

/*
 * drivers/input/touchscreen/tsc2004.c
 *
 * Copyright (C) 2009 Texas Instruments Inc
 * Author: Vaibhav Hiremath <hvaibhav@ti.com>
 *
 * Using code from:
 *  - tsc2007.c
 *
 * This package 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 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., 675 Mass Ave, Cambridge, MA 02139, USA.
 *
 */

#include <linux/module.h>
#include <linux/slab.h>
#include <linux/input.h>
#include <linux/interrupt.h>
#include <linux/i2c.h>
#include <linux/i2c/tsc2004.h>


#define TS_POLL_DELAY			1 /* ms delay between samples */
#define TS_POLL_PERIOD			1 /* ms delay between samples */

/* Control byte 0 */
#define TSC2004_CMD0(addr, pnd, rw) ((addr<<3)|(pnd<<1)|rw)
/* Control byte 1 */
#define TSC2004_CMD1(cmd, mode, rst) ((1<<7)|(cmd<<4)|(mode<<2)|(rst<<1))

/* Command Bits */
#define READ_REG	1
#define WRITE_REG	0
#define SWRST_TRUE	1
#define SWRST_FALSE	0
#define PND0_TRUE	1
#define PND0_FALSE	0

#ifdef CONFIG_MACH_OMAP3517EVM

#define OMAP3517EVM_XMIN	9
#define OMAP3517EVM_XMAX	245
#define OMAP3517EVM_YMIN	14
#define OMAP3517EVM_YMAX	235
#define OMAP3517EVM_XRES	480
#define OMAP3517EVM_YRES	272

#endif

/* Converter function mapping */
enum convertor_function {
	MEAS_X_Y_Z1_Z2,	/* Measure X,Y,z1 and Z2:	0x0 */
	MEAS_X_Y,	/* Measure X and Y only:	0x1 */
	MEAS_X,		/* Measure X only:		0x2 */
	MEAS_Y,		/* Measure Y only:		0x3 */
	MEAS_Z1_Z2,	/* Measure Z1 and Z2 only:	0x4 */
	MEAS_AUX,	/* Measure Auxillary input:	0x5 */
	MEAS_TEMP1,	/* Measure Temparature1:	0x6 */
	MEAS_TEMP2,	/* Measure Temparature2:	0x7 */
	MEAS_AUX_CONT,	/* Continuously measure Auxillary input: 0x8 */
	X_DRV_TEST,	/* X-Axis drivers tested 	0x9 */
	Y_DRV_TEST,	/* Y-Axis drivers tested 	0xA */
	/*Command Reserved*/
	SHORT_CKT_TST = 0xC,	/* Short circuit test:	0xC */
	XP_XN_DRV_STAT,	/* X+,Y- drivers status:	0xD */
	YP_YN_DRV_STAT,	/* X+,Y- drivers status:	0xE */
	YP_XN_DRV_STAT	/* Y+,X- drivers status:	0xF */
};

/* Register address mapping */
enum register_address {
	X_REG,		/* X register:		0x0 */
	Y_REG,		/* Y register:		0x1 */
	Z1_REG,		/* Z1 register:		0x2 */
	Z2_REG,		/* Z2 register:		0x3 */
	AUX_REG,	/* AUX register:	0x4 */
	TEMP1_REG,	/* Temp1 register:	0x5 */
	TEMP2_REG,	/* Temp2 register:	0x6 */
	STAT_REG,	/* Status Register:	0x7 */
	AUX_HGH_TH_REG,	/* AUX high threshold register:	0x8 */
	AUX_LOW_TH_REG,	/* AUX low threshold register:	0x9 */
	TMP_HGH_TH_REG,	/* Temp high threshold register:0xA */
	TMP_LOW_TH_REG,	/* Temp low threshold register:	0xB */
	CFR0_REG,	/* Configuration register 0:	0xC */
	CFR1_REG,	/* Configuration register 1:	0xD */
	CFR2_REG,	/* Configuration register 2:	0xE */
	CONV_FN_SEL_STAT	/* Convertor function select register:	0xF */
};

/* Supported Resolution modes */
enum resolution_mode {
	MODE_10BIT,	/* 10 bit resolution */
	MODE_12BIT		/* 12 bit resolution */
};

/* Configuraton register bit fields */
/* CFR0 */
#define PEN_STS_CTRL_MODE	(1<<15)
#define ADC_STS			(1<<14)
#define RES_CTRL		(1<<13)
#define ADC_CLK_4MHZ		(0<<11)
#define ADC_CLK_2MHZ		(1<<11)
#define ADC_CLK_1MHZ		(2<<11)
#define PANEL_VLTG_STB_TIME_0US		(0<<8)
#define PANEL_VLTG_STB_TIME_100US	(1<<8)
#define PANEL_VLTG_STB_TIME_500US	(2<<8)
#define PANEL_VLTG_STB_TIME_1MS		(3<<8)
#define PANEL_VLTG_STB_TIME_5MS		(4<<8)
#define PANEL_VLTG_STB_TIME_10MS	(5<<8)
#define PANEL_VLTG_STB_TIME_50MS	(6<<8)
#define PANEL_VLTG_STB_TIME_100MS	(7<<8)

/* CFR2 */
#define PINTS1			(1<<15)
#define PINTS0			(1<<14)
#define MEDIAN_VAL_FLTR_SIZE_1	(0<<12)
#define MEDIAN_VAL_FLTR_SIZE_3	(1<<12)
#define MEDIAN_VAL_FLTR_SIZE_7	(2<<12)
#define MEDIAN_VAL_FLTR_SIZE_15	(3<<12)
#define AVRG_VAL_FLTR_SIZE_1	(0<<10)
#define AVRG_VAL_FLTR_SIZE_3_4	(1<<10)
#define AVRG_VAL_FLTR_SIZE_7_8	(2<<10)
#define AVRG_VAL_FLTR_SIZE_16	(3<<10)
#define MAV_FLTR_EN_X		(1<<4)
#define MAV_FLTR_EN_Y		(1<<3)
#define MAV_FLTR_EN_Z		(1<<2)

#define	MAX_12BIT		((1 << 12) - 1)
#define MEAS_MASK		0xFFF

struct ts_event {
	u16	x;
	u16	y;
	u16	z1, z2;
};

struct tsc2004 {
	struct input_dev	*input;
	char			phys[32];
	struct delayed_work	work;

	struct i2c_client	*client;

	u16			model;
	u16			x_plate_ohms;

	bool			pendown;
	int			irq;

	int			(*get_pendown_state)(void);
	void			(*clear_penirq)(void);
};

static inline int tsc2004_read_word_data(struct tsc2004 *tsc, u8 cmd)
{
	s32 data;
	u16 val;

	data = i2c_smbus_read_word_data(tsc->client, cmd);
	if (data < 0) {
		dev_err(&tsc->client->dev, "i2c io (read) error: %d\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;

	dev_dbg(&tsc->client->dev, "data: 0x%x, val: 0x%x\n", data, val);

	return val;
}

static inline int tsc2004_write_word_data(struct tsc2004 *tsc, u8 cmd, u16 data)
{
	u16 val;

	val = swab16(data);
	return i2c_smbus_write_word_data(tsc->client, cmd, val);
}

static inline int tsc2004_write_cmd(struct tsc2004 *tsc, u8 value)
{
	return i2c_smbus_write_byte(tsc->client, value);
}

static int tsc2004_prepare_for_reading(struct tsc2004 *ts)
{
	int err;
	int cmd, data;

	/* Reset the TSC, configure for 12 bit */
	cmd = TSC2004_CMD1(MEAS_X_Y_Z1_Z2, MODE_12BIT, SWRST_TRUE);
	err = tsc2004_write_cmd(ts, cmd);
	if (err < 0)
		return err;

	/* Enable interrupt for PENIRQ and DAV */
	cmd = TSC2004_CMD0(CFR2_REG, PND0_FALSE, WRITE_REG);
	data = PINTS1 | PINTS0 | MEDIAN_VAL_FLTR_SIZE_15 |
		AVRG_VAL_FLTR_SIZE_7_8 | MAV_FLTR_EN_X | MAV_FLTR_EN_Y |
		MAV_FLTR_EN_Z;
	err = tsc2004_write_word_data(ts, cmd, data);
	if (err < 0)
		return err;

	/* Configure the TSC in TSMode 1 */
	cmd = TSC2004_CMD0(CFR0_REG, PND0_FALSE, WRITE_REG);
	data = PEN_STS_CTRL_MODE | ADC_CLK_2MHZ | PANEL_VLTG_STB_TIME_1MS;
	err = tsc2004_write_word_data(ts, cmd, data);
	if (err < 0)
		return err;

	/* Enable x, y, z1 and z2 conversion functions */
	cmd = TSC2004_CMD1(MEAS_X_Y_Z1_Z2, MODE_12BIT, SWRST_FALSE);
	err = tsc2004_write_cmd(ts, cmd);
	if (err < 0)
		return err;

	return 0;
}

static void tsc2004_read_values(struct tsc2004 *tsc, struct ts_event *tc)
{
	int cmd;

	/* Read X Measurement */
	cmd = TSC2004_CMD0(X_REG, PND0_FALSE, READ_REG);
	tc->x = tsc2004_read_word_data(tsc, cmd);

	/* Read Y Measurement */
	cmd = TSC2004_CMD0(Y_REG, PND0_FALSE, READ_REG);
	tc->y = tsc2004_read_word_data(tsc, cmd);

	/* Read Z1 Measurement */
	cmd = TSC2004_CMD0(Z1_REG, PND0_FALSE, READ_REG);
	tc->z1 = tsc2004_read_word_data(tsc, cmd);

	/* Read Z2 Measurement */
	cmd = TSC2004_CMD0(Z2_REG, PND0_FALSE, READ_REG);
	tc->z2 = tsc2004_read_word_data(tsc, cmd);


	tc->x &= MEAS_MASK;
	tc->y &= MEAS_MASK;
	tc->z1 &= MEAS_MASK;
	tc->z2 &= MEAS_MASK;

	/* Prepare for touch readings */
	if (tsc2004_prepare_for_reading(tsc) < 0)
		dev_dbg(&tsc->client->dev, "Failed to prepare TSC for next"
				"reading\n");
}

static u32 tsc2004_calculate_pressure(struct tsc2004 *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 void tsc2004_send_up_event(struct tsc2004 *tsc)
{
	struct input_dev *input = tsc->input;

	dev_dbg(&tsc->client->dev, "UP\n");

	input_report_key(input, BTN_TOUCH, 0);
	input_report_abs(input, ABS_PRESSURE, 0);
	input_sync(input);
}

static void tsc2004_work(struct work_struct *work)
{
	struct tsc2004 *ts =
		container_of(to_delayed_work(work), struct tsc2004, work);
	struct ts_event tc;
	u32 rt;

	/*
	 * 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 have rely on the pressure anyway.
	 */
	if (ts->get_pendown_state) {
		if (unlikely(!ts->get_pendown_state())) {
			tsc2004_send_up_event(ts);
			ts->pendown = false;
			goto out;
		}

		dev_dbg(&ts->client->dev, "pen is still down\n");
	}

	tsc2004_read_values(ts, &tc);

	rt = tsc2004_calculate_pressure(ts, &tc);
	if (rt > MAX_12BIT) {
		/*
		 * 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.
		 */
		dev_dbg(&ts->client->dev, "ignored pressure %d\n", rt);
		goto out;

	}

	if (rt) {
		struct input_dev *input = ts->input;

		if (!ts->pendown) {
			dev_dbg(&ts->client->dev, "DOWN\n");

			input_report_key(input, BTN_TOUCH, 1);
			ts->pendown = true;
		}

#ifdef CONFIG_MACH_OMAP3517EVM
		tc.x =(OMAP3517EVM_XRES * (tc.x - OMAP3517EVM_XMIN)) /
		      (OMAP3517EVM_XMAX - OMAP3517EVM_XMIN);
		tc.y = OMAP3517EVM_YRES -
			((OMAP3517EVM_YRES * (tc.y - OMAP3517EVM_YMIN)) /
			(OMAP3517EVM_YMAX - OMAP3517EVM_YMIN));
#endif

		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);

		dev_dbg(&ts->client->dev, "point(%4d,%4d), pressure (%4u)\n",
			tc.x, tc.y, rt);

	} else if (!ts->get_pendown_state && ts->pendown) {
		/*
		 * We don't have callback to check pendown state, so we
		 * have to assume that since pressure reported is 0 the
		 * pen was lifted up.
		 */
		tsc2004_send_up_event(ts);
		ts->pendown = false;
	}

 out:
	if (ts->pendown)
		schedule_delayed_work(&ts->work,
				      msecs_to_jiffies(TS_POLL_PERIOD));
	else
		enable_irq(ts->irq);
}

static irqreturn_t tsc2004_irq(int irq, void *handle)
{
	struct tsc2004 *ts = handle;

	if (!ts->get_pendown_state || likely(ts->get_pendown_state())) {
		disable_irq_nosync(ts->irq);
		schedule_delayed_work(&ts->work,
				      msecs_to_jiffies(TS_POLL_DELAY));
	}

	if (ts->clear_penirq)
		ts->clear_penirq();

	return IRQ_HANDLED;
}

static void tsc2004_free_irq(struct tsc2004 *ts)
{
	free_irq(ts->irq, ts);
	if (cancel_delayed_work_sync(&ts->work)) {
		/*
		 * Work was pending, therefore we need to enable
		 * IRQ here to balance the disable_irq() done in the
		 * interrupt handler.
		 */
		enable_irq(ts->irq);
	}
}

static int __devinit tsc2004_probe(struct i2c_client *client,
				   const struct i2c_device_id *id)
{
	struct tsc2004 *ts;
	struct tsc2004_platform_data *pdata = pdata = client->dev.platform_data;
	struct input_dev *input_dev;
	int err;

	if (!pdata) {
		dev_err(&client->dev, "platform data is required!\n");
		return -EINVAL;
	}

	if (!i2c_check_functionality(client->adapter,
				     I2C_FUNC_SMBUS_READ_WORD_DATA))
		return -EIO;

	ts = kzalloc(sizeof(struct tsc2004), GFP_KERNEL);
	input_dev = input_allocate_device();
	if (!ts || !input_dev) {
		err = -ENOMEM;
		goto err_free_mem;
	}

	ts->client = client;
	ts->irq = client->irq;
	ts->input = input_dev;
	INIT_DELAYED_WORK(&ts->work, tsc2004_work);

	ts->model             = pdata->model;
	ts->x_plate_ohms      = pdata->x_plate_ohms;
	ts->get_pendown_state = pdata->get_pendown_state;
	ts->clear_penirq      = pdata->clear_penirq;

	snprintf(ts->phys, sizeof(ts->phys),
		 "%s/input0", dev_name(&client->dev));

	input_dev->name = "TSC2004 Touchscreen";
	input_dev->phys = ts->phys;
	input_dev->id.bustype = BUS_I2C;

	input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS);
	input_dev->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH);

#ifdef CONFIG_MACH_OMAP3517EVM
	input_set_abs_params(input_dev, ABS_X, 0, OMAP3517EVM_XRES, 0, 0);
	input_set_abs_params(input_dev, ABS_Y, 0, OMAP3517EVM_YRES, 0, 0);
#else
	input_set_abs_params(input_dev, ABS_X, 0, MAX_12BIT, 0, 0);
	input_set_abs_params(input_dev, ABS_Y, 0, MAX_12BIT, 0, 0);
#endif
	input_set_abs_params(input_dev, ABS_PRESSURE, 0, MAX_12BIT, 0, 0);

	if (pdata->init_platform_hw)
		pdata->init_platform_hw();

	err = request_irq(ts->irq, tsc2004_irq, IRQF_TRIGGER_FALLING,
			client->dev.driver->name, ts);
	if (err < 0) {
		dev_err(&client->dev, "irq %d busy?\n", ts->irq);
		goto err_free_mem;
	}

	/* Prepare for touch readings */
	err = tsc2004_prepare_for_reading(ts);
	if (err < 0)
		goto err_free_irq;

	err = input_register_device(input_dev);
	if (err)
		goto err_free_irq;

	i2c_set_clientdata(client, ts);

	return 0;

 err_free_irq:
	tsc2004_free_irq(ts);
	if (pdata->exit_platform_hw)
		pdata->exit_platform_hw();
 err_free_mem:
	input_free_device(input_dev);
	kfree(ts);
	return err;
}

static int __devexit tsc2004_remove(struct i2c_client *client)
{
	struct tsc2004	*ts = i2c_get_clientdata(client);
	struct tsc2004_platform_data *pdata = client->dev.platform_data;

	tsc2004_free_irq(ts);

	if (pdata->exit_platform_hw)
		pdata->exit_platform_hw();

	input_unregister_device(ts->input);
	kfree(ts);

	return 0;
}

static struct i2c_device_id tsc2004_idtable[] = {
	{ "tsc2004", 0 },
	{ }
};

MODULE_DEVICE_TABLE(i2c, tsc2004_idtable);

static struct i2c_driver tsc2004_driver = {
	.driver = {
		.owner	= THIS_MODULE,
		.name	= "tsc2004"
	},
	.id_table	= tsc2004_idtable,
	.probe		= tsc2004_probe,
	.remove		= __devexit_p(tsc2004_remove),
};

static int __init tsc2004_init(void)
{
	return i2c_add_driver(&tsc2004_driver);
}

static void __exit tsc2004_exit(void)
{
	i2c_del_driver(&tsc2004_driver);
}

module_init(tsc2004_init);
module_exit(tsc2004_exit);

MODULE_AUTHOR("Vaibhav Hiremath <hvaibhav@ti.com>");
MODULE_DESCRIPTION("TSC2004 TouchScreen Driver");
MODULE_LICENSE("GPL");