| /* | 
 |  * Texas Instruments TNETV107X Touchscreen Driver | 
 |  * | 
 |  * Copyright (C) 2010 Texas Instruments | 
 |  * | 
 |  * 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. | 
 |  * | 
 |  * This program is distributed "as is" WITHOUT ANY WARRANTY of any | 
 |  * kind, whether express or implied; without even the implied warranty | 
 |  * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | 
 |  * GNU General Public License for more details. | 
 |  */ | 
 |  | 
 | #include <linux/kernel.h> | 
 | #include <linux/errno.h> | 
 | #include <linux/input.h> | 
 | #include <linux/platform_device.h> | 
 | #include <linux/interrupt.h> | 
 | #include <linux/slab.h> | 
 | #include <linux/delay.h> | 
 | #include <linux/ctype.h> | 
 | #include <linux/io.h> | 
 | #include <linux/clk.h> | 
 |  | 
 | #include <mach/tnetv107x.h> | 
 |  | 
 | #define TSC_PENUP_POLL		(HZ / 5) | 
 | #define IDLE_TIMEOUT		100 /* msec */ | 
 |  | 
 | /* | 
 |  * The first and last samples of a touch interval are usually garbage and need | 
 |  * to be filtered out with these devices.  The following definitions control | 
 |  * the number of samples skipped. | 
 |  */ | 
 | #define TSC_HEAD_SKIP		1 | 
 | #define TSC_TAIL_SKIP		1 | 
 | #define TSC_SKIP		(TSC_HEAD_SKIP + TSC_TAIL_SKIP + 1) | 
 | #define TSC_SAMPLES		(TSC_SKIP + 1) | 
 |  | 
 | /* Register Offsets */ | 
 | struct tsc_regs { | 
 | 	u32	rev; | 
 | 	u32	tscm; | 
 | 	u32	bwcm; | 
 | 	u32	swc; | 
 | 	u32	adcchnl; | 
 | 	u32	adcdata; | 
 | 	u32	chval[4]; | 
 | }; | 
 |  | 
 | /* TSC Mode Configuration Register (tscm) bits */ | 
 | #define WMODE		BIT(0) | 
 | #define TSKIND		BIT(1) | 
 | #define ZMEASURE_EN	BIT(2) | 
 | #define IDLE		BIT(3) | 
 | #define TSC_EN		BIT(4) | 
 | #define STOP		BIT(5) | 
 | #define ONE_SHOT	BIT(6) | 
 | #define SINGLE		BIT(7) | 
 | #define AVG		BIT(8) | 
 | #define AVGNUM(x)	(((x) & 0x03) <<  9) | 
 | #define PVSTC(x)	(((x) & 0x07) << 11) | 
 | #define PON		BIT(14) | 
 | #define PONBG		BIT(15) | 
 | #define AFERST		BIT(16) | 
 |  | 
 | /* ADC DATA Capture Register bits */ | 
 | #define DATA_VALID	BIT(16) | 
 |  | 
 | /* Register Access Macros */ | 
 | #define tsc_read(ts, reg)		__raw_readl(&(ts)->regs->reg) | 
 | #define tsc_write(ts, reg, val)		__raw_writel(val, &(ts)->regs->reg); | 
 | #define tsc_set_bits(ts, reg, val)	\ | 
 | 	tsc_write(ts, reg, tsc_read(ts, reg) | (val)) | 
 | #define tsc_clr_bits(ts, reg, val)	\ | 
 | 	tsc_write(ts, reg, tsc_read(ts, reg) & ~(val)) | 
 |  | 
 | struct sample { | 
 | 	int x, y, p; | 
 | }; | 
 |  | 
 | struct tsc_data { | 
 | 	struct input_dev		*input_dev; | 
 | 	struct resource			*res; | 
 | 	struct tsc_regs __iomem		*regs; | 
 | 	struct timer_list		timer; | 
 | 	spinlock_t			lock; | 
 | 	struct clk			*clk; | 
 | 	struct device			*dev; | 
 | 	int				sample_count; | 
 | 	struct sample			samples[TSC_SAMPLES]; | 
 | 	int				tsc_irq; | 
 | }; | 
 |  | 
 | static int tsc_read_sample(struct tsc_data *ts, struct sample* sample) | 
 | { | 
 | 	int	x, y, z1, z2, t, p = 0; | 
 | 	u32	val; | 
 |  | 
 | 	val = tsc_read(ts, chval[0]); | 
 | 	if (val & DATA_VALID) | 
 | 		x = val & 0xffff; | 
 | 	else | 
 | 		return -EINVAL; | 
 |  | 
 | 	y  = tsc_read(ts, chval[1]) & 0xffff; | 
 | 	z1 = tsc_read(ts, chval[2]) & 0xffff; | 
 | 	z2 = tsc_read(ts, chval[3]) & 0xffff; | 
 |  | 
 | 	if (z1) { | 
 | 		t = ((600 * x) * (z2 - z1)); | 
 | 		p = t / (u32) (z1 << 12); | 
 | 		if (p < 0) | 
 | 			p = 0; | 
 | 	} | 
 |  | 
 | 	sample->x  = x; | 
 | 	sample->y  = y; | 
 | 	sample->p  = p; | 
 |  | 
 | 	return 0; | 
 | } | 
 |  | 
 | static void tsc_poll(unsigned long data) | 
 | { | 
 | 	struct tsc_data *ts = (struct tsc_data *)data; | 
 | 	unsigned long flags; | 
 | 	int i, val, x, y, p; | 
 |  | 
 | 	spin_lock_irqsave(&ts->lock, flags); | 
 |  | 
 | 	if (ts->sample_count >= TSC_SKIP) { | 
 | 		input_report_abs(ts->input_dev, ABS_PRESSURE, 0); | 
 | 		input_report_key(ts->input_dev, BTN_TOUCH, 0); | 
 | 		input_sync(ts->input_dev); | 
 | 	} else if (ts->sample_count > 0) { | 
 | 		/* | 
 | 		 * A touch event lasted less than our skip count.  Salvage and | 
 | 		 * report anyway. | 
 | 		 */ | 
 | 		for (i = 0, val = 0; i < ts->sample_count; i++) | 
 | 			val += ts->samples[i].x; | 
 | 		x = val / ts->sample_count; | 
 |  | 
 | 		for (i = 0, val = 0; i < ts->sample_count; i++) | 
 | 			val += ts->samples[i].y; | 
 | 		y = val / ts->sample_count; | 
 |  | 
 | 		for (i = 0, val = 0; i < ts->sample_count; i++) | 
 | 			val += ts->samples[i].p; | 
 | 		p = val / ts->sample_count; | 
 |  | 
 | 		input_report_abs(ts->input_dev, ABS_X, x); | 
 | 		input_report_abs(ts->input_dev, ABS_Y, y); | 
 | 		input_report_abs(ts->input_dev, ABS_PRESSURE, p); | 
 | 		input_report_key(ts->input_dev, BTN_TOUCH, 1); | 
 | 		input_sync(ts->input_dev); | 
 | 	} | 
 |  | 
 | 	ts->sample_count = 0; | 
 |  | 
 | 	spin_unlock_irqrestore(&ts->lock, flags); | 
 | } | 
 |  | 
 | static irqreturn_t tsc_irq(int irq, void *dev_id) | 
 | { | 
 | 	struct tsc_data *ts = (struct tsc_data *)dev_id; | 
 | 	struct sample *sample; | 
 | 	int index; | 
 |  | 
 | 	spin_lock(&ts->lock); | 
 |  | 
 | 	index = ts->sample_count % TSC_SAMPLES; | 
 | 	sample = &ts->samples[index]; | 
 | 	if (tsc_read_sample(ts, sample) < 0) | 
 | 		goto out; | 
 |  | 
 | 	if (++ts->sample_count >= TSC_SKIP) { | 
 | 		index = (ts->sample_count - TSC_TAIL_SKIP - 1) % TSC_SAMPLES; | 
 | 		sample = &ts->samples[index]; | 
 |  | 
 | 		input_report_abs(ts->input_dev, ABS_X, sample->x); | 
 | 		input_report_abs(ts->input_dev, ABS_Y, sample->y); | 
 | 		input_report_abs(ts->input_dev, ABS_PRESSURE, sample->p); | 
 | 		if (ts->sample_count == TSC_SKIP) | 
 | 			input_report_key(ts->input_dev, BTN_TOUCH, 1); | 
 | 		input_sync(ts->input_dev); | 
 | 	} | 
 | 	mod_timer(&ts->timer, jiffies + TSC_PENUP_POLL); | 
 | out: | 
 | 	spin_unlock(&ts->lock); | 
 | 	return IRQ_HANDLED; | 
 | } | 
 |  | 
 | static int tsc_start(struct input_dev *dev) | 
 | { | 
 | 	struct tsc_data *ts = input_get_drvdata(dev); | 
 | 	unsigned long timeout = jiffies + msecs_to_jiffies(IDLE_TIMEOUT); | 
 | 	u32 val; | 
 |  | 
 | 	clk_enable(ts->clk); | 
 |  | 
 | 	/* Go to idle mode, before any initialization */ | 
 | 	while (time_after(timeout, jiffies)) { | 
 | 		if (tsc_read(ts, tscm) & IDLE) | 
 | 			break; | 
 | 	} | 
 |  | 
 | 	if (time_before(timeout, jiffies)) { | 
 | 		dev_warn(ts->dev, "timeout waiting for idle\n"); | 
 | 		clk_disable(ts->clk); | 
 | 		return -EIO; | 
 | 	} | 
 |  | 
 | 	/* Configure TSC Control register*/ | 
 | 	val = (PONBG | PON | PVSTC(4) | ONE_SHOT | ZMEASURE_EN); | 
 | 	tsc_write(ts, tscm, val); | 
 |  | 
 | 	/* Bring TSC out of reset: Clear AFE reset bit */ | 
 | 	val &= ~(AFERST); | 
 | 	tsc_write(ts, tscm, val); | 
 |  | 
 | 	/* Configure all pins for hardware control*/ | 
 | 	tsc_write(ts, bwcm, 0); | 
 |  | 
 | 	/* Finally enable the TSC */ | 
 | 	tsc_set_bits(ts, tscm, TSC_EN); | 
 |  | 
 | 	return 0; | 
 | } | 
 |  | 
 | static void tsc_stop(struct input_dev *dev) | 
 | { | 
 | 	struct tsc_data *ts = input_get_drvdata(dev); | 
 |  | 
 | 	tsc_clr_bits(ts, tscm, TSC_EN); | 
 | 	synchronize_irq(ts->tsc_irq); | 
 | 	del_timer_sync(&ts->timer); | 
 | 	clk_disable(ts->clk); | 
 | } | 
 |  | 
 | static int __devinit tsc_probe(struct platform_device *pdev) | 
 | { | 
 | 	struct device *dev = &pdev->dev; | 
 | 	struct tsc_data *ts; | 
 | 	int error = 0; | 
 | 	u32 rev = 0; | 
 |  | 
 | 	ts = kzalloc(sizeof(struct tsc_data), GFP_KERNEL); | 
 | 	if (!ts) { | 
 | 		dev_err(dev, "cannot allocate device info\n"); | 
 | 		return -ENOMEM; | 
 | 	} | 
 |  | 
 | 	ts->dev = dev; | 
 | 	spin_lock_init(&ts->lock); | 
 | 	setup_timer(&ts->timer, tsc_poll, (unsigned long)ts); | 
 | 	platform_set_drvdata(pdev, ts); | 
 |  | 
 | 	ts->tsc_irq = platform_get_irq(pdev, 0); | 
 | 	if (ts->tsc_irq < 0) { | 
 | 		dev_err(dev, "cannot determine device interrupt\n"); | 
 | 		error = -ENODEV; | 
 | 		goto error_res; | 
 | 	} | 
 |  | 
 | 	ts->res = platform_get_resource(pdev, IORESOURCE_MEM, 0); | 
 | 	if (!ts->res) { | 
 | 		dev_err(dev, "cannot determine register area\n"); | 
 | 		error = -ENODEV; | 
 | 		goto error_res; | 
 | 	} | 
 |  | 
 | 	if (!request_mem_region(ts->res->start, resource_size(ts->res), | 
 | 				pdev->name)) { | 
 | 		dev_err(dev, "cannot claim register memory\n"); | 
 | 		ts->res = NULL; | 
 | 		error = -EINVAL; | 
 | 		goto error_res; | 
 | 	} | 
 |  | 
 | 	ts->regs = ioremap(ts->res->start, resource_size(ts->res)); | 
 | 	if (!ts->regs) { | 
 | 		dev_err(dev, "cannot map register memory\n"); | 
 | 		error = -ENOMEM; | 
 | 		goto error_map; | 
 | 	} | 
 |  | 
 | 	ts->clk = clk_get(dev, NULL); | 
 | 	if (!ts->clk) { | 
 | 		dev_err(dev, "cannot claim device clock\n"); | 
 | 		error = -EINVAL; | 
 | 		goto error_clk; | 
 | 	} | 
 |  | 
 | 	error = request_threaded_irq(ts->tsc_irq, NULL, tsc_irq, 0, | 
 | 				     dev_name(dev), ts); | 
 | 	if (error < 0) { | 
 | 		dev_err(ts->dev, "Could not allocate ts irq\n"); | 
 | 		goto error_irq; | 
 | 	} | 
 |  | 
 | 	ts->input_dev = input_allocate_device(); | 
 | 	if (!ts->input_dev) { | 
 | 		dev_err(dev, "cannot allocate input device\n"); | 
 | 		error = -ENOMEM; | 
 | 		goto error_input; | 
 | 	} | 
 | 	input_set_drvdata(ts->input_dev, ts); | 
 |  | 
 | 	ts->input_dev->name       = pdev->name; | 
 | 	ts->input_dev->id.bustype = BUS_HOST; | 
 | 	ts->input_dev->dev.parent = &pdev->dev; | 
 | 	ts->input_dev->open	  = tsc_start; | 
 | 	ts->input_dev->close	  = tsc_stop; | 
 |  | 
 | 	clk_enable(ts->clk); | 
 | 	rev = tsc_read(ts, rev); | 
 | 	ts->input_dev->id.product = ((rev >>  8) & 0x07); | 
 | 	ts->input_dev->id.version = ((rev >> 16) & 0xfff); | 
 | 	clk_disable(ts->clk); | 
 |  | 
 | 	__set_bit(EV_KEY,    ts->input_dev->evbit); | 
 | 	__set_bit(EV_ABS,    ts->input_dev->evbit); | 
 | 	__set_bit(BTN_TOUCH, ts->input_dev->keybit); | 
 |  | 
 | 	input_set_abs_params(ts->input_dev, ABS_X, 0, 0xffff, 5, 0); | 
 | 	input_set_abs_params(ts->input_dev, ABS_Y, 0, 0xffff, 5, 0); | 
 | 	input_set_abs_params(ts->input_dev, ABS_PRESSURE, 0, 4095, 128, 0); | 
 |  | 
 | 	error = input_register_device(ts->input_dev); | 
 | 	if (error < 0) { | 
 | 		dev_err(dev, "failed input device registration\n"); | 
 | 		goto error_reg; | 
 | 	} | 
 |  | 
 | 	return 0; | 
 |  | 
 | error_reg: | 
 | 	input_free_device(ts->input_dev); | 
 | error_input: | 
 | 	free_irq(ts->tsc_irq, ts); | 
 | error_irq: | 
 | 	clk_put(ts->clk); | 
 | error_clk: | 
 | 	iounmap(ts->regs); | 
 | error_map: | 
 | 	release_mem_region(ts->res->start, resource_size(ts->res)); | 
 | error_res: | 
 | 	platform_set_drvdata(pdev, NULL); | 
 | 	kfree(ts); | 
 |  | 
 | 	return error; | 
 | } | 
 |  | 
 | static int __devexit tsc_remove(struct platform_device *pdev) | 
 | { | 
 | 	struct tsc_data *ts = platform_get_drvdata(pdev); | 
 |  | 
 | 	input_unregister_device(ts->input_dev); | 
 | 	free_irq(ts->tsc_irq, ts); | 
 | 	clk_put(ts->clk); | 
 | 	iounmap(ts->regs); | 
 | 	release_mem_region(ts->res->start, resource_size(ts->res)); | 
 | 	platform_set_drvdata(pdev, NULL); | 
 | 	kfree(ts); | 
 |  | 
 | 	return 0; | 
 | } | 
 |  | 
 | static struct platform_driver tsc_driver = { | 
 | 	.probe		= tsc_probe, | 
 | 	.remove		= __devexit_p(tsc_remove), | 
 | 	.driver.name	= "tnetv107x-ts", | 
 | 	.driver.owner	= THIS_MODULE, | 
 | }; | 
 |  | 
 | static int __init tsc_init(void) | 
 | { | 
 | 	return platform_driver_register(&tsc_driver); | 
 | } | 
 |  | 
 | static void __exit tsc_exit(void) | 
 | { | 
 | 	platform_driver_unregister(&tsc_driver); | 
 | } | 
 |  | 
 | module_init(tsc_init); | 
 | module_exit(tsc_exit); | 
 |  | 
 | MODULE_AUTHOR("Cyril Chemparathy"); | 
 | MODULE_DESCRIPTION("TNETV107X Touchscreen Driver"); | 
 | MODULE_ALIAS("platform: tnetv107x-ts"); | 
 | MODULE_LICENSE("GPL"); |