All of lore.kernel.org
 help / color / mirror / Atom feed
From: Shine Liu <shinel@foxmail.com>
To: linux-input@vger.kernel.org, dtor@mail.ru, dmitry.torokhov@gmail.com
Cc: linux-arm-kernel@lists.infradead.org
Subject: [PATCH] input/touchscreen: add S3C24XX SoC touchscreen input driver
Date: Mon, 19 Oct 2009 18:54:21 +0800	[thread overview]
Message-ID: <1255949661.2060.29.camel@shinel> (raw)


This touchscreen driver is for touchscreen controller on Samsung
S3C2410/S3C2440 SoC chip. S3C2410/S3C2440 has the on chip touchscreen
controller based on it's analog to digital converter(8-channel analog
inputs, touchscreen uses 4 of them). This driver uses the exsiting S3C
ADC driver to make the touchscreen controller of S3C2410/S3C2440 work
well together with other ADC devices connected to the S3C24XX SoC chip.

The patch is created against kernel 2.6.32-rc4 and tested with S3C2440
SoC and Samsung LTE430WQ-F0C touchscreen.


Signed-off-by: Shine Liu <liuxian@redflag-linux.com>
Signed-off-by: Shine Liu <shinel@foxmail.com>
--------------------------------------------------------

--- a/drivers/input/touchscreen/Makefile	2009-10-12 05:43:56.000000000 +0800
+++ b/drivers/input/touchscreen/Makefile	2009-10-13 20:35:02.000000000 +0800
@@ -10,6 +10,7 @@
 obj-$(CONFIG_TOUCHSCREEN_AD7879)	+= ad7879.o
 obj-$(CONFIG_TOUCHSCREEN_ADS7846)	+= ads7846.o
 obj-$(CONFIG_TOUCHSCREEN_ATMEL_TSADCC)	+= atmel_tsadcc.o
+obj-$(CONFIG_TOUCHSCREEN_S3C24XX_TSADCC)       += s3c24xx_tsadcc.o
 obj-$(CONFIG_TOUCHSCREEN_BITSY)		+= h3600_ts_input.o
 obj-$(CONFIG_TOUCHSCREEN_CORGI)		+= corgi_ts.o
 obj-$(CONFIG_TOUCHSCREEN_GUNZE)		+= gunze.o
--- a/drivers/input/touchscreen/Kconfig	2009-10-12 05:43:56.000000000 +0800
+++ b/drivers/input/touchscreen/Kconfig	2009-10-13 20:37:51.000000000 +0800
@@ -307,6 +307,19 @@
 	  To compile this driver as a module, choose M here: the
 	  module will be called atmel_tsadcc.
 
+config TOUCHSCREEN_S3C24XX_TSADCC
+	tristate "Samsung S3C24XX touchscreen input driver"
+	depends on ARCH_S3C2410 && S3C24XX_ADC && INPUT && INPUT_TOUCHSCREEN
+	select SERIO
+	help
+	  Say Y here if you have a touchscreen connected to the ADC Controller
+	  on your s3c2410/s3c2440 SoC.
+
+	  If unsure, say N.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called s3c24xx_tsadcc.
+
 config TOUCHSCREEN_UCB1400
 	tristate "Philips UCB1400 touchscreen"
 	depends on AC97_BUS
--- /dev/null	2009-10-09 10:57:12.360986601 +0800
+++ b/drivers/input/touchscreen/s3c24xx_tsadcc.c	2009-10-19 17:16:57.000000000 +0800
@@ -0,0 +1,432 @@
+/*
+ * 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; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * 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
+ *
+ * Part of the code come from openmoko project.
+ *
+ * Shine Liu <shinel@foxmail.com>
+ */
+
+#include <linux/errno.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/input.h>
+#include <linux/init.h>
+#include <linux/serio.h>
+#include <linux/timer.h>
+#include <linux/delay.h>
+#include <linux/platform_device.h>
+#include <linux/clk.h>
+#include <linux/io.h>
+#include <asm/irq.h>
+
+#include <mach/gpio.h>
+#include <mach/regs-gpio.h>
+#include <mach/hardware.h>
+#include <plat/regs-adc.h>
+#include <plat/adc.h>
+
+
+MODULE_AUTHOR("Shine Liu <shinel@foxmail.com>");
+MODULE_DESCRIPTION("Samsung s3c24xx touchscreen driver");
+MODULE_LICENSE("GPL");
+
+
+#define S3C24XX_TS_VERSION	0x0101
+
+#define TSC_SLEEP (S3C2410_ADCTSC_PULL_UP_DISABLE | \
+	S3C2410_ADCTSC_XY_PST(0))
+
+#define WAIT4INT(x)	(((x)<<8) | \
+	S3C2410_ADCTSC_YM_SEN | \
+	S3C2410_ADCTSC_YP_SEN | \
+	S3C2410_ADCTSC_XP_SEN | \
+	S3C2410_ADCTSC_XY_PST(3))
+
+#define AUTO_XY	(S3C2410_ADCTSC_PULL_UP_DISABLE | \
+	S3C2410_ADCTSC_AUTO_PST | \
+	S3C2410_ADCTSC_XY_PST(0))
+
+#define TS_STATE_STANDBY 0 /* initial state */
+#define TS_STATE_PRESSED 1
+#define TS_STATE_RELEASE_PENDING 2 /* wait to confirm the up event */
+#define TS_STATE_RELEASE 3
+
+#define TIME_RELEASE_PENDING (HZ / 20) /* about 50ms */
+#define TIME_NEXT_CONV ((HZ < 51)? 1 : HZ / 50) /* about 20ms */
+
+static char *s3c24xx_ts_name = "Samsung s3c24xx TouchScreen";
+static void __iomem *base_addr;
+
+
+/*
+ * Per-touchscreen data.
+ */
+
+struct s3c24xx_ts {
+	struct input_dev *dev;
+	struct s3c_adc_client *adc_client;
+	unsigned char is_down;
+	unsigned char state;
+	unsigned adc_selected;
+	unsigned int delay;	/* register value for delay */
+	unsigned int presc;	/* register value for prescaler */
+};
+
+static struct s3c24xx_ts ts;
+
+
+/*
+ * Low level functions to config registers.
+ */
+static inline void s3c24xx_ts_connect(void)
+{
+	s3c2410_gpio_cfgpin(S3C2410_GPG(12), S3C2410_GPG12_XMON);
+	s3c2410_gpio_cfgpin(S3C2410_GPG(13), S3C2410_GPG13_nXPON);
+	s3c2410_gpio_cfgpin(S3C2410_GPG(14), S3C2410_GPG14_YMON);
+	s3c2410_gpio_cfgpin(S3C2410_GPG(15), S3C2410_GPG15_nYPON);
+}
+
+static void s3c24xx_ts_start_adc_conversion(void)
+{
+	pr_debug("start_adc_conv ADCTSC: 0x%x", 
+		readl(base_addr + S3C2410_ADCTSC));
+	writel(AUTO_XY, base_addr + S3C2410_ADCTSC);
+	pr_debug(" ---> 0x%x", 
+		readl(base_addr + S3C2410_ADCTSC));
+	s3c_adc_start(ts.adc_client, 0, 1);
+	pr_debug(" ---> 0x%x\n", 
+		readl(base_addr + S3C2410_ADCTSC));
+}
+
+
+/*
+ * Event handling
+ */
+
+enum ts_input_event {IE_UP = 0, IE_DOWN};
+
+static void ts_input_report(int event, int coords[])
+{
+
+	if (event == IE_DOWN) {
+		input_report_abs(ts.dev, ABS_X, coords[0]);
+		input_report_abs(ts.dev, ABS_Y, coords[1]);
+		input_report_key(ts.dev, BTN_TOUCH, 1);
+		input_report_abs(ts.dev, ABS_PRESSURE, 1);
+
+		pr_debug("down (X:%03d, Y:%03d)\n", coords[0], coords[1]);
+	} else {
+		input_report_key(ts.dev, BTN_TOUCH, 0);
+		input_report_abs(ts.dev, ABS_PRESSURE, 0);
+		pr_debug("up\n");
+	}
+
+	input_sync(ts.dev);
+}
+
+
+/*
+ * Timer to process the UP event or to report the postion of DRAG
+ */
+
+static void ts_drag_pull_timer_f(unsigned long data);
+static struct timer_list ts_drag_pull_timer = 
+	TIMER_INITIALIZER(ts_drag_pull_timer_f, 0, 0);
+
+static void ts_drag_pull_timer_f(unsigned long data)
+{
+	unsigned long flags;
+	/* delay reporting the UP event to avoid jitter */
+	static unsigned release_pending_counter = 0;
+	pr_debug("Timer called: is_down[%d] status[%d]\n", ts.is_down, ts.state);
+
+	local_irq_save(flags);
+	if(ts.state == TS_STATE_RELEASE_PENDING) {
+		if(release_pending_counter++ < 1) {
+			/* jitter avoidance window: delay TIME_RELEASE_PENDING jfs */
+			mod_timer(&ts_drag_pull_timer, jiffies + TIME_RELEASE_PENDING);
+		} else {
+			/* no down event occurd during last delay */
+			release_pending_counter = 0;
+			ts_input_report(IE_UP, NULL);
+			ts.state = TS_STATE_RELEASE;
+			writel(WAIT4INT(0), base_addr + S3C2410_ADCTSC);
+		}
+		local_irq_restore(flags);
+		return;
+	}
+
+	/* ts.is_down should be true here */
+	s3c24xx_ts_start_adc_conversion();
+	local_irq_restore(flags);
+}
+
+
+/*
+ * ISR for the IRQ_TS
+ */
+
+static irqreturn_t stylus_updown(int irq, void *dev_id)
+{
+	unsigned long data0;
+	unsigned long data1;
+	unsigned long adctsc;
+	int event_type;		/* 1 for down, 0 for up */
+
+	/* 
+	 * According to s3c2440 manual chap16: After Touch Screen 
+	 * Controller generates INT_TC, XY_PST must be set to [00]
+	 */
+	adctsc = readl(base_addr + S3C2410_ADCTSC);
+	writel(adctsc & ~(S3C2410_ADCTSC_XY_PST(3)), base_addr + S3C2410_ADCTSC);
+	data0 = readl(base_addr + S3C2410_ADCDAT0);
+	data1 = readl(base_addr + S3C2410_ADCDAT1);
+	event_type = (!(data0 & S3C2410_ADCDAT0_UPDOWN)) &&
+					    (!(data1 & S3C2410_ADCDAT0_UPDOWN));
+	pr_debug("event_type: %d, DATA0 0x%lx, DATA1 0x%lx, ADCTSC 0x%lx, "
+		"ADCUPDN 0x%x\n", event_type, data0, data1,
+		adctsc, readl(base_addr + 0x14));
+
+	/* ignore sequential same event */
+	if(ts.is_down == event_type) {
+		pr_debug("###Ignore same event: %d\n", event_type);
+		/* restore XY_PST */
+		writel(adctsc, base_addr + S3C2410_ADCTSC);
+		return IRQ_HANDLED;
+	}
+
+	ts.is_down = event_type;
+	if (ts.is_down) {
+		s3c24xx_ts_start_adc_conversion();
+	} else {
+		ts.state = TS_STATE_RELEASE_PENDING;
+		/*
+		 * Delay to report the up event for a while to avoid jitter.
+		 * This state will be checked after (0, TIME_NEXT_CONV)
+		 * jiffies depended on when the interrupt occured.
+		 */
+	}
+
+	return IRQ_HANDLED;
+}
+
+/* 
+ * Called in ISR of IRQ_ADC
+ */
+
+static void stylus_adc_action(struct s3c_adc_client *client,
+		unsigned p0, unsigned p1, unsigned *conv_left)
+{
+	int buf[2];
+	unsigned long flags;
+
+	local_irq_save(flags);
+	/* 
+	 * IRQ_TS may rise just in the time window between AUTO_XY mode
+	 * set and this pointer */
+	if(ts.state == TS_STATE_RELEASE_PENDING)
+	{
+		local_irq_restore(flags);
+		return;
+	}
+
+	/* Grab the ADC results. */
+	buf[0] = p0;
+	buf[1] = p1;
+
+	ts_input_report(IE_DOWN, buf);
+	pr_debug("[adc_selected: %d] ADCTSC: 0x%x", 
+		ts.adc_selected, readl(base_addr + S3C2410_ADCTSC));
+
+	writel(WAIT4INT(1), base_addr + S3C2410_ADCTSC);
+	printk(" ---> 0x%x\n", 
+		readl(base_addr + S3C2410_ADCTSC));
+
+	ts.state = TS_STATE_PRESSED;
+	mod_timer(&ts_drag_pull_timer, jiffies + TIME_NEXT_CONV);
+	local_irq_restore(flags);
+}
+
+
+/* 
+ * callback function for s3c-adc
+ */
+
+void adc_selected_f(struct s3c_adc_client *client, unsigned selected)
+{
+	ts.adc_selected = selected;
+}
+
+
+/*
+ * The functions for inserting/removing us as a module.
+ */
+
+static int __init s3c24xx_ts_probe(struct platform_device *pdev)
+{
+	int rc;
+	struct input_dev *input_dev;
+	int ret = 0;
+
+	dev_info(&pdev->dev, "Starting\n");
+	pr_debug("Entering s3c24xx_ts_probe\n");
+
+	base_addr = ioremap(S3C2410_PA_ADC, 0x20);
+	if (base_addr == NULL) {
+		dev_err(&pdev->dev, "Failed to remap register block\n");
+		ret = -ENOMEM;
+		goto fail0;
+	}
+
+	/* Configure GPIOs */
+	s3c24xx_ts_connect();
+
+	writel(WAIT4INT(0), base_addr + S3C2410_ADCTSC);
+
+	/* Initialise input stuff */
+	memset(&ts, 0, sizeof(struct s3c24xx_ts));
+	ts.adc_client =
+		s3c_adc_register(pdev, adc_selected_f, stylus_adc_action, 1);
+	if (!ts.adc_client) {
+			dev_err(&pdev->dev,
+					"Unable to register s3c24xx_ts as s3c_adc client\n");
+			ret = -EIO;
+			goto fail1;
+	}
+
+	/* save prescale EN and VAL in the S3C2410_ADCCON register */
+	ts.presc = readl(base_addr + S3C2410_ADCCON);
+	/* save value of S3C2410_ADCDLY register */
+	ts.delay = readl(base_addr + S3C2410_ADCDLY);
+	pr_debug("platform data: ADCCON=%08x ADCDLY=%08x\n", ts.presc, ts.delay);
+
+	input_dev = input_allocate_device();
+	if (!input_dev) {
+		dev_err(&pdev->dev, "Unable to allocate the input device\n");
+		ret = -ENOMEM;
+		goto fail2;
+	}
+
+	ts.dev = input_dev;
+	ts.dev->evbit[0] = BIT_MASK(EV_SYN) | BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS);
+	ts.dev->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH);
+	input_set_abs_params(ts.dev, ABS_X, 0, 0x3FF, 0, 0);
+	input_set_abs_params(ts.dev, ABS_Y, 0, 0x3FF, 0, 0);
+	input_set_abs_params(ts.dev, ABS_PRESSURE, 0, 1, 0, 0);
+
+	ts.dev->name = s3c24xx_ts_name;
+	ts.dev->id.bustype = BUS_RS232;
+	ts.dev->id.vendor = 0xBAAD;
+	ts.dev->id.product = 0xBEEF;
+	ts.dev->id.version = S3C24XX_TS_VERSION;
+	ts.state = TS_STATE_STANDBY;
+
+	/* Get IRQ */
+	if (request_irq(IRQ_TC, stylus_updown, IRQF_SAMPLE_RANDOM,
+			"s3c24xx_ts_action", ts.dev)) {
+		dev_err(&pdev->dev, "Could not allocate ts IRQ_TC !\n");
+		ret = -EIO;
+		goto fail3;
+	}
+
+	dev_info(&pdev->dev, "Successfully loaded\n");
+
+	/* All went ok, so register to the input system */
+	rc = input_register_device(ts.dev);
+	if (rc) {
+		ret = -EIO;
+		goto fail4;
+	}
+	return 0;
+
+fail4:
+	disable_irq(IRQ_TC);
+	free_irq(IRQ_TC, ts.dev);
+fail3:
+	input_free_device(ts.dev);
+fail2:	
+	s3c_adc_release(ts.adc_client);
+fail1:
+	iounmap(base_addr);
+fail0:
+	return ret;
+}
+
+static int s3c24xx_ts_remove(struct platform_device *pdev)
+{
+	disable_irq(IRQ_TC);
+	free_irq(IRQ_TC, ts.dev);
+	input_unregister_device(ts.dev);
+	input_free_device(ts.dev);
+	s3c_adc_release(ts.adc_client);
+	iounmap(base_addr);
+	return 0;
+}
+
+#ifdef CONFIG_PM
+static int s3c24xx_ts_suspend(struct platform_device *pdev, pm_message_t state)
+{
+	disable_irq(IRQ_TC);
+	ts.presc = readl(base_addr + S3C2410_ADCCON);
+	ts.delay = readl(base_addr + S3C2410_ADCDLY);
+
+	writel(TSC_SLEEP, base_addr + S3C2410_ADCTSC);
+	writel(ts.presc | S3C2410_ADCCON_STDBM, base_addr + S3C2410_ADCCON);
+	return 0;
+}
+
+static int s3c24xx_ts_resume(struct platform_device *pdev)
+{
+	/* Restore registers */
+	writel(ts.presc, base_addr + S3C2410_ADCCON);
+	writel(ts.delay, base_addr + S3C2410_ADCDLY);
+	writel(WAIT4INT(0), base_addr + S3C2410_ADCTSC);
+	enable_irq(IRQ_TC);
+	return 0;
+}
+
+#else
+#define s3c24xx_ts_suspend NULL
+#define s3c24xx_ts_resume  NULL
+#endif
+
+static struct platform_driver s3c24xx_ts_driver = {
+	.driver		= {
+		.name	= "s3c24xx-ts",
+		.owner	= THIS_MODULE,
+	},
+	.probe		= s3c24xx_ts_probe,
+	.remove		= s3c24xx_ts_remove,
+	.suspend 	= s3c24xx_ts_suspend,
+	.resume		= s3c24xx_ts_resume,
+};
+
+static int __init s3c24xx_ts_init(void)
+{
+	int rc;
+
+	rc = platform_driver_register(&s3c24xx_ts_driver);
+	return rc;
+}
+
+static void __exit s3c24xx_ts_exit(void)
+{
+	platform_driver_unregister(&s3c24xx_ts_driver);
+}
+
+module_init(s3c24xx_ts_init);
+module_exit(s3c24xx_ts_exit);
+

WARNING: multiple messages have this Message-ID (diff)
From: shinel@foxmail.com (Shine Liu)
To: linux-arm-kernel@lists.infradead.org
Subject: [PATCH] input/touchscreen: add S3C24XX SoC touchscreen input driver
Date: Mon, 19 Oct 2009 18:54:21 +0800	[thread overview]
Message-ID: <1255949661.2060.29.camel@shinel> (raw)


This touchscreen driver is for touchscreen controller on Samsung
S3C2410/S3C2440 SoC chip. S3C2410/S3C2440 has the on chip touchscreen
controller based on it's analog to digital converter(8-channel analog
inputs, touchscreen uses 4 of them). This driver uses the exsiting S3C
ADC driver to make the touchscreen controller of S3C2410/S3C2440 work
well together with other ADC devices connected to the S3C24XX SoC chip.

The patch is created against kernel 2.6.32-rc4 and tested with S3C2440
SoC and Samsung LTE430WQ-F0C touchscreen.


Signed-off-by: Shine Liu <liuxian@redflag-linux.com>
Signed-off-by: Shine Liu <shinel@foxmail.com>
--------------------------------------------------------

--- a/drivers/input/touchscreen/Makefile	2009-10-12 05:43:56.000000000 +0800
+++ b/drivers/input/touchscreen/Makefile	2009-10-13 20:35:02.000000000 +0800
@@ -10,6 +10,7 @@
 obj-$(CONFIG_TOUCHSCREEN_AD7879)	+= ad7879.o
 obj-$(CONFIG_TOUCHSCREEN_ADS7846)	+= ads7846.o
 obj-$(CONFIG_TOUCHSCREEN_ATMEL_TSADCC)	+= atmel_tsadcc.o
+obj-$(CONFIG_TOUCHSCREEN_S3C24XX_TSADCC)       += s3c24xx_tsadcc.o
 obj-$(CONFIG_TOUCHSCREEN_BITSY)		+= h3600_ts_input.o
 obj-$(CONFIG_TOUCHSCREEN_CORGI)		+= corgi_ts.o
 obj-$(CONFIG_TOUCHSCREEN_GUNZE)		+= gunze.o
--- a/drivers/input/touchscreen/Kconfig	2009-10-12 05:43:56.000000000 +0800
+++ b/drivers/input/touchscreen/Kconfig	2009-10-13 20:37:51.000000000 +0800
@@ -307,6 +307,19 @@
 	  To compile this driver as a module, choose M here: the
 	  module will be called atmel_tsadcc.
 
+config TOUCHSCREEN_S3C24XX_TSADCC
+	tristate "Samsung S3C24XX touchscreen input driver"
+	depends on ARCH_S3C2410 && S3C24XX_ADC && INPUT && INPUT_TOUCHSCREEN
+	select SERIO
+	help
+	  Say Y here if you have a touchscreen connected to the ADC Controller
+	  on your s3c2410/s3c2440 SoC.
+
+	  If unsure, say N.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called s3c24xx_tsadcc.
+
 config TOUCHSCREEN_UCB1400
 	tristate "Philips UCB1400 touchscreen"
 	depends on AC97_BUS
--- /dev/null	2009-10-09 10:57:12.360986601 +0800
+++ b/drivers/input/touchscreen/s3c24xx_tsadcc.c	2009-10-19 17:16:57.000000000 +0800
@@ -0,0 +1,432 @@
+/*
+ * 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; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * 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
+ *
+ * Part of the code come from openmoko project.
+ *
+ * Shine Liu <shinel@foxmail.com>
+ */
+
+#include <linux/errno.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/input.h>
+#include <linux/init.h>
+#include <linux/serio.h>
+#include <linux/timer.h>
+#include <linux/delay.h>
+#include <linux/platform_device.h>
+#include <linux/clk.h>
+#include <linux/io.h>
+#include <asm/irq.h>
+
+#include <mach/gpio.h>
+#include <mach/regs-gpio.h>
+#include <mach/hardware.h>
+#include <plat/regs-adc.h>
+#include <plat/adc.h>
+
+
+MODULE_AUTHOR("Shine Liu <shinel@foxmail.com>");
+MODULE_DESCRIPTION("Samsung s3c24xx touchscreen driver");
+MODULE_LICENSE("GPL");
+
+
+#define S3C24XX_TS_VERSION	0x0101
+
+#define TSC_SLEEP (S3C2410_ADCTSC_PULL_UP_DISABLE | \
+	S3C2410_ADCTSC_XY_PST(0))
+
+#define WAIT4INT(x)	(((x)<<8) | \
+	S3C2410_ADCTSC_YM_SEN | \
+	S3C2410_ADCTSC_YP_SEN | \
+	S3C2410_ADCTSC_XP_SEN | \
+	S3C2410_ADCTSC_XY_PST(3))
+
+#define AUTO_XY	(S3C2410_ADCTSC_PULL_UP_DISABLE | \
+	S3C2410_ADCTSC_AUTO_PST | \
+	S3C2410_ADCTSC_XY_PST(0))
+
+#define TS_STATE_STANDBY 0 /* initial state */
+#define TS_STATE_PRESSED 1
+#define TS_STATE_RELEASE_PENDING 2 /* wait to confirm the up event */
+#define TS_STATE_RELEASE 3
+
+#define TIME_RELEASE_PENDING (HZ / 20) /* about 50ms */
+#define TIME_NEXT_CONV ((HZ < 51)? 1 : HZ / 50) /* about 20ms */
+
+static char *s3c24xx_ts_name = "Samsung s3c24xx TouchScreen";
+static void __iomem *base_addr;
+
+
+/*
+ * Per-touchscreen data.
+ */
+
+struct s3c24xx_ts {
+	struct input_dev *dev;
+	struct s3c_adc_client *adc_client;
+	unsigned char is_down;
+	unsigned char state;
+	unsigned adc_selected;
+	unsigned int delay;	/* register value for delay */
+	unsigned int presc;	/* register value for prescaler */
+};
+
+static struct s3c24xx_ts ts;
+
+
+/*
+ * Low level functions to config registers.
+ */
+static inline void s3c24xx_ts_connect(void)
+{
+	s3c2410_gpio_cfgpin(S3C2410_GPG(12), S3C2410_GPG12_XMON);
+	s3c2410_gpio_cfgpin(S3C2410_GPG(13), S3C2410_GPG13_nXPON);
+	s3c2410_gpio_cfgpin(S3C2410_GPG(14), S3C2410_GPG14_YMON);
+	s3c2410_gpio_cfgpin(S3C2410_GPG(15), S3C2410_GPG15_nYPON);
+}
+
+static void s3c24xx_ts_start_adc_conversion(void)
+{
+	pr_debug("start_adc_conv ADCTSC: 0x%x", 
+		readl(base_addr + S3C2410_ADCTSC));
+	writel(AUTO_XY, base_addr + S3C2410_ADCTSC);
+	pr_debug(" ---> 0x%x", 
+		readl(base_addr + S3C2410_ADCTSC));
+	s3c_adc_start(ts.adc_client, 0, 1);
+	pr_debug(" ---> 0x%x\n", 
+		readl(base_addr + S3C2410_ADCTSC));
+}
+
+
+/*
+ * Event handling
+ */
+
+enum ts_input_event {IE_UP = 0, IE_DOWN};
+
+static void ts_input_report(int event, int coords[])
+{
+
+	if (event == IE_DOWN) {
+		input_report_abs(ts.dev, ABS_X, coords[0]);
+		input_report_abs(ts.dev, ABS_Y, coords[1]);
+		input_report_key(ts.dev, BTN_TOUCH, 1);
+		input_report_abs(ts.dev, ABS_PRESSURE, 1);
+
+		pr_debug("down (X:%03d, Y:%03d)\n", coords[0], coords[1]);
+	} else {
+		input_report_key(ts.dev, BTN_TOUCH, 0);
+		input_report_abs(ts.dev, ABS_PRESSURE, 0);
+		pr_debug("up\n");
+	}
+
+	input_sync(ts.dev);
+}
+
+
+/*
+ * Timer to process the UP event or to report the postion of DRAG
+ */
+
+static void ts_drag_pull_timer_f(unsigned long data);
+static struct timer_list ts_drag_pull_timer = 
+	TIMER_INITIALIZER(ts_drag_pull_timer_f, 0, 0);
+
+static void ts_drag_pull_timer_f(unsigned long data)
+{
+	unsigned long flags;
+	/* delay reporting the UP event to avoid jitter */
+	static unsigned release_pending_counter = 0;
+	pr_debug("Timer called: is_down[%d] status[%d]\n", ts.is_down, ts.state);
+
+	local_irq_save(flags);
+	if(ts.state == TS_STATE_RELEASE_PENDING) {
+		if(release_pending_counter++ < 1) {
+			/* jitter avoidance window: delay TIME_RELEASE_PENDING jfs */
+			mod_timer(&ts_drag_pull_timer, jiffies + TIME_RELEASE_PENDING);
+		} else {
+			/* no down event occurd during last delay */
+			release_pending_counter = 0;
+			ts_input_report(IE_UP, NULL);
+			ts.state = TS_STATE_RELEASE;
+			writel(WAIT4INT(0), base_addr + S3C2410_ADCTSC);
+		}
+		local_irq_restore(flags);
+		return;
+	}
+
+	/* ts.is_down should be true here */
+	s3c24xx_ts_start_adc_conversion();
+	local_irq_restore(flags);
+}
+
+
+/*
+ * ISR for the IRQ_TS
+ */
+
+static irqreturn_t stylus_updown(int irq, void *dev_id)
+{
+	unsigned long data0;
+	unsigned long data1;
+	unsigned long adctsc;
+	int event_type;		/* 1 for down, 0 for up */
+
+	/* 
+	 * According to s3c2440 manual chap16: After Touch Screen 
+	 * Controller generates INT_TC, XY_PST must be set to [00]
+	 */
+	adctsc = readl(base_addr + S3C2410_ADCTSC);
+	writel(adctsc & ~(S3C2410_ADCTSC_XY_PST(3)), base_addr + S3C2410_ADCTSC);
+	data0 = readl(base_addr + S3C2410_ADCDAT0);
+	data1 = readl(base_addr + S3C2410_ADCDAT1);
+	event_type = (!(data0 & S3C2410_ADCDAT0_UPDOWN)) &&
+					    (!(data1 & S3C2410_ADCDAT0_UPDOWN));
+	pr_debug("event_type: %d, DATA0 0x%lx, DATA1 0x%lx, ADCTSC 0x%lx, "
+		"ADCUPDN 0x%x\n", event_type, data0, data1,
+		adctsc, readl(base_addr + 0x14));
+
+	/* ignore sequential same event */
+	if(ts.is_down == event_type) {
+		pr_debug("###Ignore same event: %d\n", event_type);
+		/* restore XY_PST */
+		writel(adctsc, base_addr + S3C2410_ADCTSC);
+		return IRQ_HANDLED;
+	}
+
+	ts.is_down = event_type;
+	if (ts.is_down) {
+		s3c24xx_ts_start_adc_conversion();
+	} else {
+		ts.state = TS_STATE_RELEASE_PENDING;
+		/*
+		 * Delay to report the up event for a while to avoid jitter.
+		 * This state will be checked after (0, TIME_NEXT_CONV)
+		 * jiffies depended on when the interrupt occured.
+		 */
+	}
+
+	return IRQ_HANDLED;
+}
+
+/* 
+ * Called in ISR of IRQ_ADC
+ */
+
+static void stylus_adc_action(struct s3c_adc_client *client,
+		unsigned p0, unsigned p1, unsigned *conv_left)
+{
+	int buf[2];
+	unsigned long flags;
+
+	local_irq_save(flags);
+	/* 
+	 * IRQ_TS may rise just in the time window between AUTO_XY mode
+	 * set and this pointer */
+	if(ts.state == TS_STATE_RELEASE_PENDING)
+	{
+		local_irq_restore(flags);
+		return;
+	}
+
+	/* Grab the ADC results. */
+	buf[0] = p0;
+	buf[1] = p1;
+
+	ts_input_report(IE_DOWN, buf);
+	pr_debug("[adc_selected: %d] ADCTSC: 0x%x", 
+		ts.adc_selected, readl(base_addr + S3C2410_ADCTSC));
+
+	writel(WAIT4INT(1), base_addr + S3C2410_ADCTSC);
+	printk(" ---> 0x%x\n", 
+		readl(base_addr + S3C2410_ADCTSC));
+
+	ts.state = TS_STATE_PRESSED;
+	mod_timer(&ts_drag_pull_timer, jiffies + TIME_NEXT_CONV);
+	local_irq_restore(flags);
+}
+
+
+/* 
+ * callback function for s3c-adc
+ */
+
+void adc_selected_f(struct s3c_adc_client *client, unsigned selected)
+{
+	ts.adc_selected = selected;
+}
+
+
+/*
+ * The functions for inserting/removing us as a module.
+ */
+
+static int __init s3c24xx_ts_probe(struct platform_device *pdev)
+{
+	int rc;
+	struct input_dev *input_dev;
+	int ret = 0;
+
+	dev_info(&pdev->dev, "Starting\n");
+	pr_debug("Entering s3c24xx_ts_probe\n");
+
+	base_addr = ioremap(S3C2410_PA_ADC, 0x20);
+	if (base_addr == NULL) {
+		dev_err(&pdev->dev, "Failed to remap register block\n");
+		ret = -ENOMEM;
+		goto fail0;
+	}
+
+	/* Configure GPIOs */
+	s3c24xx_ts_connect();
+
+	writel(WAIT4INT(0), base_addr + S3C2410_ADCTSC);
+
+	/* Initialise input stuff */
+	memset(&ts, 0, sizeof(struct s3c24xx_ts));
+	ts.adc_client =
+		s3c_adc_register(pdev, adc_selected_f, stylus_adc_action, 1);
+	if (!ts.adc_client) {
+			dev_err(&pdev->dev,
+					"Unable to register s3c24xx_ts as s3c_adc client\n");
+			ret = -EIO;
+			goto fail1;
+	}
+
+	/* save prescale EN and VAL in the S3C2410_ADCCON register */
+	ts.presc = readl(base_addr + S3C2410_ADCCON);
+	/* save value of S3C2410_ADCDLY register */
+	ts.delay = readl(base_addr + S3C2410_ADCDLY);
+	pr_debug("platform data: ADCCON=%08x ADCDLY=%08x\n", ts.presc, ts.delay);
+
+	input_dev = input_allocate_device();
+	if (!input_dev) {
+		dev_err(&pdev->dev, "Unable to allocate the input device\n");
+		ret = -ENOMEM;
+		goto fail2;
+	}
+
+	ts.dev = input_dev;
+	ts.dev->evbit[0] = BIT_MASK(EV_SYN) | BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS);
+	ts.dev->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH);
+	input_set_abs_params(ts.dev, ABS_X, 0, 0x3FF, 0, 0);
+	input_set_abs_params(ts.dev, ABS_Y, 0, 0x3FF, 0, 0);
+	input_set_abs_params(ts.dev, ABS_PRESSURE, 0, 1, 0, 0);
+
+	ts.dev->name = s3c24xx_ts_name;
+	ts.dev->id.bustype = BUS_RS232;
+	ts.dev->id.vendor = 0xBAAD;
+	ts.dev->id.product = 0xBEEF;
+	ts.dev->id.version = S3C24XX_TS_VERSION;
+	ts.state = TS_STATE_STANDBY;
+
+	/* Get IRQ */
+	if (request_irq(IRQ_TC, stylus_updown, IRQF_SAMPLE_RANDOM,
+			"s3c24xx_ts_action", ts.dev)) {
+		dev_err(&pdev->dev, "Could not allocate ts IRQ_TC !\n");
+		ret = -EIO;
+		goto fail3;
+	}
+
+	dev_info(&pdev->dev, "Successfully loaded\n");
+
+	/* All went ok, so register to the input system */
+	rc = input_register_device(ts.dev);
+	if (rc) {
+		ret = -EIO;
+		goto fail4;
+	}
+	return 0;
+
+fail4:
+	disable_irq(IRQ_TC);
+	free_irq(IRQ_TC, ts.dev);
+fail3:
+	input_free_device(ts.dev);
+fail2:	
+	s3c_adc_release(ts.adc_client);
+fail1:
+	iounmap(base_addr);
+fail0:
+	return ret;
+}
+
+static int s3c24xx_ts_remove(struct platform_device *pdev)
+{
+	disable_irq(IRQ_TC);
+	free_irq(IRQ_TC, ts.dev);
+	input_unregister_device(ts.dev);
+	input_free_device(ts.dev);
+	s3c_adc_release(ts.adc_client);
+	iounmap(base_addr);
+	return 0;
+}
+
+#ifdef CONFIG_PM
+static int s3c24xx_ts_suspend(struct platform_device *pdev, pm_message_t state)
+{
+	disable_irq(IRQ_TC);
+	ts.presc = readl(base_addr + S3C2410_ADCCON);
+	ts.delay = readl(base_addr + S3C2410_ADCDLY);
+
+	writel(TSC_SLEEP, base_addr + S3C2410_ADCTSC);
+	writel(ts.presc | S3C2410_ADCCON_STDBM, base_addr + S3C2410_ADCCON);
+	return 0;
+}
+
+static int s3c24xx_ts_resume(struct platform_device *pdev)
+{
+	/* Restore registers */
+	writel(ts.presc, base_addr + S3C2410_ADCCON);
+	writel(ts.delay, base_addr + S3C2410_ADCDLY);
+	writel(WAIT4INT(0), base_addr + S3C2410_ADCTSC);
+	enable_irq(IRQ_TC);
+	return 0;
+}
+
+#else
+#define s3c24xx_ts_suspend NULL
+#define s3c24xx_ts_resume  NULL
+#endif
+
+static struct platform_driver s3c24xx_ts_driver = {
+	.driver		= {
+		.name	= "s3c24xx-ts",
+		.owner	= THIS_MODULE,
+	},
+	.probe		= s3c24xx_ts_probe,
+	.remove		= s3c24xx_ts_remove,
+	.suspend 	= s3c24xx_ts_suspend,
+	.resume		= s3c24xx_ts_resume,
+};
+
+static int __init s3c24xx_ts_init(void)
+{
+	int rc;
+
+	rc = platform_driver_register(&s3c24xx_ts_driver);
+	return rc;
+}
+
+static void __exit s3c24xx_ts_exit(void)
+{
+	platform_driver_unregister(&s3c24xx_ts_driver);
+}
+
+module_init(s3c24xx_ts_init);
+module_exit(s3c24xx_ts_exit);
+

             reply	other threads:[~2009-10-19 10:54 UTC|newest]

Thread overview: 28+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2009-10-19 10:54 Shine Liu [this message]
2009-10-19 10:54 ` [PATCH] input/touchscreen: add S3C24XX SoC touchscreen input driver Shine Liu
2009-10-20  1:38 ` Dmitry Torokhov
2009-10-20  1:38   ` Dmitry Torokhov
2009-10-20 12:16   ` [PATCH v2] " Shine Liu
2009-10-21  4:34   ` Shine Liu
     [not found] <mailman.4020.1255949705.2256.linux-arm-kernel@lists.infradead.org>
2009-10-19 11:28 ` [PATCH] " Maurus Cuelenaere
2009-10-19 11:28   ` Maurus Cuelenaere
2009-10-19 12:07   ` Russell King - ARM Linux
2009-10-19 12:07     ` Russell King - ARM Linux
2009-10-20  4:21     ` Nelson Castillo
2009-10-20  4:21       ` Nelson Castillo
2009-10-20  7:39       ` Russell King - ARM Linux
2009-10-20  7:39         ` Russell King - ARM Linux
2009-10-20  8:21         ` Nelson Castillo
2009-10-20  8:21           ` Nelson Castillo
2009-10-20  9:41           ` Mark Brown
2009-10-20  9:41             ` Mark Brown
2009-10-23  5:38             ` Nelson Castillo
2009-10-23  5:38               ` Nelson Castillo
2009-10-20 10:09           ` Andy Green
2009-10-20 10:09             ` Andy Green
2009-10-19 13:31   ` Shine Liu
2009-10-19 13:31     ` Shine Liu
2009-10-19 14:44     ` Arnaud Patard
2009-10-19 14:44       ` Arnaud Patard (Rtp)
2009-10-19 14:34   ` Juergen Beisert
2009-10-19 14:34     ` Juergen Beisert

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=1255949661.2060.29.camel@shinel \
    --to=shinel@foxmail.com \
    --cc=dmitry.torokhov@gmail.com \
    --cc=dtor@mail.ru \
    --cc=linux-arm-kernel@lists.infradead.org \
    --cc=linux-input@vger.kernel.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.