Re: [patch 1/2] Touchscreen support for sharp sl-5500

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



Hi!

> > This adds support for reading ADCs (etc), neccessary to operate touch
> > screen on Sharp Zaurus sl-5500.
> 
> I would like to know what the diffs are between my version (attached)
> and this version before they get applied.

Hmm, diff looks quite big (attached), and I got it from lenz for 99%
part.

I have made quite a lot of cleanups to touchscreen part, and it seems
to be acceptable by input people. I think it should go into
drivers/input/touchscreen/collie_ts.c... Also it looks to me like
mcp.h should go into asm/arch-sa1100, so that other drivers can use it...

> The only reason my version has not been submitted is because it lives
> in the drivers/misc directory, and mainline kernel folk don't like
> drivers which clutter up that directory.  In fact, I had been told
> that drivers/misc should remain completely empty - which makes this
> set of miscellaneous drivers homeless.

Could they simply live in arch/arm/mach-sa1100? Or is arch/arm/soc
better place?
							Pavel

--- linux-rmk/drivers/input/touchscreen/Kconfig	2005-07-14 00:41:02.000000000 +0200
+++ linux-z/drivers/input/touchscreen/Kconfig	2005-07-21 17:22:31.000000000 +0200
@@ -36,6 +36,15 @@
 	  To compile this driver as a module, choose M here: the
 	  module will be called ads7846_ts.
 
+config TOUCHSCREEN_COLLIE
+	tristate "Collie touchscreen (for Sharp SL-5500)"
+	depends on MCP_UCB1200
+	help
+	  Say Y here to enable the driver for the touchscreen on the 
+	  Sharp SL-5500 series of PDAs.
+
+	  If unsure, say N.
+
 config TOUCHSCREEN_GUNZE
 	tristate "Gunze AHL-51S touchscreen"
 	select SERIO
--- linux-rmk/drivers/input/touchscreen/Makefile	2005-07-14 00:41:02.000000000 +0200
+++ linux-z/drivers/input/touchscreen/Makefile	2005-07-21 06:39:52.000000000 +0200
@@ -6,6 +6,7 @@
 
 obj-$(CONFIG_TOUCHSCREEN_BITSY)	+= h3600_ts_input.o
 obj-$(CONFIG_TOUCHSCREEN_CORGI)	+= corgi_ts.o
+obj-$(CONFIG_TOUCHSCREEN_COLLIE)+= collie_ts.o
 obj-$(CONFIG_TOUCHSCREEN_GUNZE)	+= gunze.o
 obj-$(CONFIG_TOUCHSCREEN_ELO)	+= elo.o
 obj-$(CONFIG_TOUCHSCREEN_MTOUCH) += mtouch.o
--- linux-rmk/drivers/misc/Makefile	2005-07-25 05:17:11.000000000 +0200
+++ linux-z/drivers/misc/Makefile	2005-07-21 06:36:17.000000000 +0200
@@ -6,12 +6,15 @@
 obj-$(CONFIG_IBM_ASM)	+= ibmasm/
 obj-$(CONFIG_HDPU_FEATURES)	+= hdpuftrs/
 
-obj-$(CONFIG_MCP)		+= mcp-core.o
-obj-$(CONFIG_MCP_SA1100)	+= mcp-sa1100.o
-obj-$(CONFIG_MCP_UCB1200)	+= ucb1x00-core.o
-obj-$(CONFIG_MCP_UCB1200_AUDIO)	+= ucb1x00-audio.o
-obj-$(CONFIG_MCP_UCB1200_TS)	+= ucb1x00-ts.o
+obj-$(CONFIG_MCP)              += mcp-core.o
+obj-$(CONFIG_MCP_UCB1200)      += ucb1x00-core.o
+obj-$(CONFIG_MCP_UCB1200_AUDIO)        += ucb1x00-audio.o
 
 ifeq ($(CONFIG_SA1100_ASSABET),y)
-obj-$(CONFIG_MCP_UCB1200)	+= ucb1x00-assabet.o
+obj-$(CONFIG_MCP_UCB1200)      += ucb1x00-assabet.o
 endif
+
+obj-$(CONFIG_MCP_SA1100)       += mcp-sa1100.o
+
+ucb1400-core-y := ucb1x00-core.o mcp-ac97.o
+obj-$(CONFIG_UCB1400_TS) += ucb1400-core.o ucb1x00-ts.o
Only in linux-z/drivers/misc: mcp-ac97.c
--- linux-rmk/drivers/misc/mcp-core.c	2005-07-25 05:17:11.000000000 +0200
+++ linux-z/drivers/misc/mcp-core.c	2005-07-21 06:57:36.000000000 +0200
@@ -19,9 +19,9 @@
 #include <asm/dma.h>
 #include <asm/system.h>
 
-#include "mcp.h"
+#include <asm/arch-sa1100/mcp.h>
 
-#define to_mcp(d)		container_of(d, struct mcp, attached_device)
+#define to_mcp(d)		((struct mcp *)(d)->platform_data)
 #define to_mcp_driver(d)	container_of(d, struct mcp_driver, drv)
 
 static int mcp_bus_match(struct device *dev, struct device_driver *drv)
@@ -46,7 +46,7 @@
 	return 0;
 }
 
-static int mcp_bus_suspend(struct device *dev, pm_message_t state)
+static int mcp_bus_suspend(struct device *dev, u32 state)
 {
 	struct mcp *mcp = to_mcp(dev);
 	int ret = 0;
@@ -179,26 +179,40 @@
 	spin_unlock_irqrestore(&mcp->lock, flags);
 }
 
-static void mcp_release(struct device *dev)
-{
-	struct mcp *mcp = container_of(dev, struct mcp, attached_device);
-
-	kfree(mcp);
+static void mcp_host_release(struct device *dev) {
+	struct mcp *mcp = dev->platform_data;
+	complete(&mcp->attached_device_released);
 }
 
 int mcp_host_register(struct mcp *mcp, struct device *parent)
 {
-	mcp->attached_device.parent = parent;
-	mcp->attached_device.bus = &mcp_bus_type;
-	mcp->attached_device.dma_mask = parent->dma_mask;
-	mcp->attached_device.release = mcp_release;
-	strcpy(mcp->attached_device.bus_id, "mcp0");
-	return device_register(&mcp->attached_device);
+	int ret;
+	struct device *dev = kmalloc(sizeof(*dev), GFP_KERNEL);
+	if (!dev)
+		return -ENOMEM;
+	memset(dev, 0, sizeof(*dev));
+	dev->platform_data = mcp;
+	dev->parent = parent;
+	dev->bus = &mcp_bus_type;
+	dev->dma_mask = parent->dma_mask;
+	dev->release = mcp_host_release;
+	strcpy(dev->bus_id, "mcp0");
+	mcp->attached_device = dev;
+	ret = device_register(dev);
+	if (ret) {
+		mcp->attached_device = NULL;
+		kfree(dev);
+	}
+	return ret;
 }
 
 void mcp_host_unregister(struct mcp *mcp)
 {
-	device_unregister_wait(&mcp->attached_device);
+	init_completion(&mcp->attached_device_released);
+	device_unregister(mcp->attached_device);
+	wait_for_completion(&mcp->attached_device_released);
+	kfree(mcp->attached_device);
+	mcp->attached_device = NULL;
 }
 
 int mcp_driver_register(struct mcp_driver *mcpdrv)
--- linux-rmk/drivers/misc/mcp-sa1100.c	2005-07-25 05:17:11.000000000 +0200
+++ linux-z/drivers/misc/mcp-sa1100.c	2005-07-21 06:58:49.000000000 +0200
@@ -27,7 +27,8 @@
 
 #include <asm/arch/assabet.h>
 
-#include "mcp.h"
+#include <asm/arch-sa1100/mcp.h>
+
 
 static void
 mcp_sa1100_set_telecom_divisor(struct mcp *mcp, unsigned int divisor)
@@ -140,7 +141,7 @@
 static int mcp_sa1100_probe(struct device *dev)
 {
 	struct platform_device *pdev = to_platform_device(dev);
-	struct mcp *mcp;
+	struct mcp *mcp = &mcp_sa1100;
 	int ret;
 
 	if (!machine_is_adsbitsy()       && !machine_is_assabet()        &&
@@ -149,20 +150,16 @@
 	    !machine_is_graphicsmaster() && !machine_is_lart()           &&
 	    !machine_is_omnimeter()      && !machine_is_pfs168()         &&
 	    !machine_is_shannon()        && !machine_is_simpad()         &&
-	    !machine_is_yopy())
+	    !machine_is_yopy()		 && !machine_is_collie()) {
+		printk(KERN_WARNING "MCP-sa1100: machine is not supported\n");
 		return -ENODEV;
+	}
 
-	if (!request_mem_region(0x80060000, 0x60, "sa11x0-mcp"))
+	if (!request_mem_region(0x80060000, 0x60, "sa11x0-mcp")) {
+		printk(KERN_ERR "MCP-sa1100: Unable to request memory region\n");
 		return -EBUSY;
-
-	mcp = kmalloc(sizeof(struct mcp), GFP_KERNEL);
-	if (!mcp) {
-		ret = -ENOMEM;
-		goto release;
 	}
 
-	*mcp = mcp_sa1100;
-
 	mcp->me = dev;
 	dev_set_drvdata(dev, mcp);
 
@@ -170,6 +167,12 @@
 		ASSABET_BCR_set(ASSABET_BCR_CODEC_RST);
 	}
 
+	if (machine_is_collie()) {
+		GAFR &= ~(GPIO_GPIO(16));
+		GPDR |= GPIO_GPIO(16);
+		GPSR |= GPIO_GPIO(16);
+	}
+
 	/*
 	 * Setup the PPC unit correctly.
 	 */
@@ -181,7 +184,8 @@
 
 	Ser4MCSR = -1;
 	Ser4MCCR1 = 0;
-	Ser4MCCR0 = 0x00007f7f | MCCR0_ADM;
+	//Ser4MCCR0 = 0x00007f7f | MCCR0_ADM;
+	Ser4MCCR0 = MCCR0_ADM | MCCR0_ExtClk;
 
 	/*
 	 * Calculate the read/write timeout (us) from the bit clock
@@ -192,14 +196,11 @@
 			  mcp->sclk_rate;
 
 	ret = mcp_host_register(mcp, &pdev->dev);
-	if (ret == 0)
-		goto out;
-
- release:
-	release_mem_region(0x80060000, 0x60);
-	dev_set_drvdata(dev, NULL);
+	if (ret != 0) {
+		release_mem_region(0x80060000, 0x60);
+		dev_set_drvdata(dev, NULL);
+	}
 
- out:
 	return ret;
 }
 
@@ -208,6 +209,7 @@
 	struct mcp *mcp = dev_get_drvdata(dev);
 
 	dev_set_drvdata(dev, NULL);
+
 	mcp_host_unregister(mcp);
 	release_mem_region(0x80060000, 0x60);
 
Only in linux-rmk/drivers/misc: mcp.h
--- linux-rmk/drivers/misc/ucb1x00-assabet.c	2005-07-25 05:17:11.000000000 +0200
+++ linux-z/drivers/misc/ucb1x00-assabet.c	2005-07-21 06:55:31.000000000 +0200
@@ -35,34 +35,34 @@
 UCB1X00_ATTR(vcharger, UCB_ADC_INP_AD0);
 UCB1X00_ATTR(batt_temp, UCB_ADC_INP_AD2);
 
-static int ucb1x00_assabet_add(struct ucb1x00_dev *dev)
+static int ucb1x00_assabet_add(struct class_device *dev)
 {
-	class_device_create_file(&dev->ucb->cdev, &class_device_attr_vbatt);
-	class_device_create_file(&dev->ucb->cdev, &class_device_attr_vcharger);
-	class_device_create_file(&dev->ucb->cdev, &class_device_attr_batt_temp);
+	class_device_create_file(dev, &class_device_attr_vbatt);
+	class_device_create_file(dev, &class_device_attr_vcharger);
+	class_device_create_file(dev, &class_device_attr_batt_temp);
 	return 0;
 }
 
-static void ucb1x00_assabet_remove(struct ucb1x00_dev *dev)
+static void ucb1x00_assabet_remove(struct class_device *dev)
 {
-	class_device_remove_file(&dev->ucb->cdev, &class_device_attr_batt_temp);
-	class_device_remove_file(&dev->ucb->cdev, &class_device_attr_vcharger);
-	class_device_remove_file(&dev->ucb->cdev, &class_device_attr_vbatt);
+	class_device_remove_file(dev, &class_device_attr_batt_temp);
+	class_device_remove_file(dev, &class_device_attr_vcharger);
+	class_device_remove_file(dev, &class_device_attr_vbatt);
 }
 
-static struct ucb1x00_driver ucb1x00_assabet_driver = {
+static struct class_interface ucb1x00_assabet_interface = {
 	.add	= ucb1x00_assabet_add,
 	.remove	= ucb1x00_assabet_remove,
 };
 
 static int __init ucb1x00_assabet_init(void)
 {
-	return ucb1x00_register_driver(&ucb1x00_assabet_driver);
+	return ucb1x00_register_interface(&ucb1x00_assabet_interface);
 }
 
 static void __exit ucb1x00_assabet_exit(void)
 {
-	ucb1x00_unregister_driver(&ucb1x00_assabet_driver);
+	ucb1x00_unregister_interface(&ucb1x00_assabet_interface);
 }
 
 module_init(ucb1x00_assabet_init);
--- linux-rmk/drivers/misc/ucb1x00-audio.c	2005-07-25 05:17:11.000000000 +0200
+++ linux-z/drivers/misc/ucb1x00-audio.c	2005-07-21 06:55:31.000000000 +0200
@@ -50,11 +50,6 @@
 	unsigned short		input_level;
 };
 
-struct ucb1x00_devdata {
-	struct ucb1x00_audio	audio;
-	struct ucb1x00_audio	telecom;
-};
-
 #define REC_MASK	(SOUND_MASK_VOLUME | SOUND_MASK_MIC)
 #define DEV_MASK	REC_MASK
 
@@ -285,122 +280,134 @@
 	return sa1100_audio_attach(inode, file, &ucba->state);
 }
 
-static int
-ucb1x00_audio_add_one(struct ucb1x00 *ucb, struct ucb1x00_audio *a, int telecom)
+static struct ucb1x00_audio *ucb1x00_audio_alloc(struct ucb1x00 *ucb)
 {
-	memset(a, 0, sizeof(*a));
+	struct ucb1x00_audio *ucba;
 
-	a->magic = MAGIC;
-	a->ucb = ucb;
-	a->fops.owner = THIS_MODULE;
-	a->fops.open  = ucb1x00_audio_open;
-	a->mops.owner = THIS_MODULE;
-	a->mops.ioctl = ucb1x00_mixer_ioctl;
-	a->state.output_stream = &a->output_stream;
-	a->state.input_stream = &a->input_stream;
-	a->state.data = a;
-	a->state.hw_init = ucb1x00_audio_startup;
-	a->state.hw_shutdown = ucb1x00_audio_shutdown;
-	a->state.client_ioctl = ucb1x00_audio_ioctl;
-
-	/* There is a bug in the StrongARM causes corrupt MCP data to be sent to
-	 * the codec when the FIFOs are empty and writes are made to the OS timer
-	 * match register 0. To avoid this we must make sure that data is always
-	 * sent to the codec.
-	 */
-	a->state.need_tx_for_rx = 1;
+	ucba = kmalloc(sizeof(*ucba), GFP_KERNEL);
+	if (ucba) {
+		memset(ucba, 0, sizeof(*ucba));
+
+		ucba->magic = MAGIC;
+		ucba->ucb = ucb;
+		ucba->fops.owner = THIS_MODULE;
+		ucba->fops.open  = ucb1x00_audio_open;
+		ucba->mops.owner = THIS_MODULE;
+		ucba->mops.ioctl = ucb1x00_mixer_ioctl;
+		ucba->state.output_stream = &ucba->output_stream;
+		ucba->state.input_stream = &ucba->input_stream;
+		ucba->state.data = ucba;
+		ucba->state.hw_init = ucb1x00_audio_startup;
+		ucba->state.hw_shutdown = ucb1x00_audio_shutdown;
+		ucba->state.client_ioctl = ucb1x00_audio_ioctl;
+
+		/* There is a bug in the StrongARM causes corrupt MCP data to be sent to
+		 * the codec when the FIFOs are empty and writes are made to the OS timer
+		 * match register 0. To avoid this we must make sure that data is always
+		 * sent to the codec.
+		 */
+		ucba->state.need_tx_for_rx = 1;
+
+		init_MUTEX(&ucba->state.sem);
+		ucba->rate = 8000;
+	}
+	return ucba;
+}
+
+static struct ucb1x00_audio *ucb1x00_audio_add_one(struct ucb1x00 *ucb, int telecom)
+{
+	struct ucb1x00_audio *a;
 
-	init_MUTEX(&a->state.sem);
-	a->rate = 8000;
-	a->telecom = telecom;
-	a->input_stream.dev = ucb->cdev.dev;
-	a->output_stream.dev = ucb->cdev.dev;
-	a->ctrl_a = 0;
-
-	if (a->telecom) {
-		a->input_stream.dma_dev = ucb->mcp->dma_telco_rd;
-		a->input_stream.id = "UCB1x00 telco in";
-		a->output_stream.dma_dev = ucb->mcp->dma_telco_wr;
-		a->output_stream.id = "UCB1x00 telco out";
-		a->ctrl_b = UCB_TC_B_IN_ENA|UCB_TC_B_OUT_ENA;
+	a = ucb1x00_audio_alloc(ucb);
+	if (a) {
+		a->telecom = telecom;
+
+		a->input_stream.dev = ucb->cdev.dev;
+		a->output_stream.dev = ucb->cdev.dev;
+		a->ctrl_a = 0;
+
+		if (a->telecom) {
+			a->input_stream.dma_dev = ucb->mcp->dma_telco_rd;
+			a->input_stream.id = "UCB1x00 telco in";
+			a->output_stream.dma_dev = ucb->mcp->dma_telco_wr;
+			a->output_stream.id = "UCB1x00 telco out";
+			a->ctrl_b = UCB_TC_B_IN_ENA|UCB_TC_B_OUT_ENA;
 #if 0
-		a->daa_oh_bit = UCB_IO_8;
+			a->daa_oh_bit = UCB_IO_8;
 
-		ucb1x00_enable(ucb);
-		ucb1x00_io_write(ucb, a->daa_oh_bit, 0);
-		ucb1x00_io_set_dir(ucb, UCB_IO_7 | UCB_IO_6, a->daa_oh_bit);
-		ucb1x00_disable(ucb);
+			ucb1x00_enable(ucb);
+			ucb1x00_io_write(ucb, a->daa_oh_bit, 0);
+			ucb1x00_io_set_dir(ucb, UCB_IO_7 | UCB_IO_6, a->daa_oh_bit);
+			ucb1x00_disable(ucb);
 #endif
-	} else {
-		a->input_stream.dma_dev  = ucb->mcp->dma_audio_rd;
-		a->input_stream.id = "UCB1x00 audio in";
-		a->output_stream.dma_dev = ucb->mcp->dma_audio_wr;
-		a->output_stream.id = "UCB1x00 audio out";
-		a->ctrl_b = UCB_AC_B_IN_ENA|UCB_AC_B_OUT_ENA;
-	}
+		} else {
+			a->input_stream.dma_dev  = ucb->mcp->dma_audio_rd;
+			a->input_stream.id = "UCB1x00 audio in";
+			a->output_stream.dma_dev = ucb->mcp->dma_audio_wr;
+			a->output_stream.id = "UCB1x00 audio out";
+			a->ctrl_b = UCB_AC_B_IN_ENA|UCB_AC_B_OUT_ENA;
+		}
 
-	a->dev_id = register_sound_dsp(&a->fops, -1);
-	a->mix_id = register_sound_mixer(&a->mops, -1);
+		a->dev_id = register_sound_dsp(&a->fops, -1);
+		a->mix_id = register_sound_mixer(&a->mops, -1);
 
-	printk("Sound: UCB1x00 %s: dsp id %d mixer id %d\n",
-		a->telecom ? "telecom" : "audio",
-		a->dev_id, a->mix_id);
+		printk("Sound: UCB1x00 %s: dsp id %d mixer id %d\n",
+			a->telecom ? "telecom" : "audio",
+			a->dev_id, a->mix_id);
+	}
 
-	return 0;
+	return a;
 }
 
 static void ucb1x00_audio_remove_one(struct ucb1x00_audio *a)
 {
 	unregister_sound_dsp(a->dev_id);
 	unregister_sound_mixer(a->mix_id);
+	kfree(a);
 }
 
-static int ucb1x00_audio_add(struct ucb1x00_dev *dev)
+static int ucb1x00_audio_add(struct class_device *cdev)
 {
-	struct ucb1x00_devdata *dd;
-	struct ucb1x00 *ucb = dev->ucb;
+	struct ucb1x00 *ucb = classdev_to_ucb1x00(cdev);
 
 	if (ucb->cdev.dev == NULL || ucb->cdev.dev->dma_mask == NULL)
 		return -ENXIO;
 
-	dd = kmalloc(sizeof(struct ucb1x00_devdata), GFP_KERNEL);
-	if (!dd)
-		return -ENOMEM;
-
-	ucb1x00_audio_add_one(ucb, &dd->audio, 0);
-	ucb1x00_audio_add_one(ucb, &dd->telecom, 1);
-
-	dev->priv = dd;
+	ucb->audio_data = ucb1x00_audio_add_one(ucb, 0);
+	ucb->telecom_data = ucb1x00_audio_add_one(ucb, 1);
 
 	return 0;
 }
 
-static void ucb1x00_audio_remove(struct ucb1x00_dev *dev)
+static void ucb1x00_audio_remove(struct class_device *cdev)
 {
-	struct ucb1x00_devdata *dd = dev->priv;
+	struct ucb1x00 *ucb = classdev_to_ucb1x00(cdev);
 
-	ucb1x00_audio_remove_one(&dd->audio);
-	ucb1x00_audio_remove_one(&dd->telecom);
-	kfree(dd);
+	ucb1x00_audio_remove_one(ucb->audio_data);
+	ucb1x00_audio_remove_one(ucb->telecom_data);
 }
 
-#ifdef CONFIG_PM
-static int ucb1x00_audio_suspend(struct ucb1x00_dev *dev, pm_message_t state)
+#if 0 //def CONFIG_PM
+static int ucb1x00_audio_suspend(struct ucb1x00 *ucb, u32 state)
 {
-	struct ucb1x00_devdata *dd = dev->priv;
+	struct ucb1x00_audio *a;
 
-	sa1100_audio_suspend(&dd->audio.state, state);
-	sa1100_audio_suspend(&dd->telecom.state, state);
+	a = ucb->audio_data;
+	sa1100_audio_suspend(&a->state, state);
+	a = ucb->telecom_data;
+	sa1100_audio_suspend(&a->state, state);
 
 	return 0;
 }
 
-static int ucb1x00_audio_resume(struct ucb1x00_dev *dev)
+static int ucb1x00_audio_resume(struct ucb1x00 *ucb)
 {
-	struct ucb1x00_devdata *dd = dev->priv;
+	struct ucb1x00_audio *a;
 
-	sa1100_audio_resume(&dd->audio.state);
-	sa1100_audio_resume(&dd->telecom.state);
+	a = ucb->audio_data;
+	sa1100_audio_resume(&a->state);
+	a = ucb->telecom_data;
+	sa1100_audio_resume(&a->state);
 
 	return 0;
 }
@@ -409,21 +416,19 @@
 #define ucb1x00_audio_resume	NULL
 #endif
 
-static struct ucb1x00_driver ucb1x00_audio_driver = {
+static struct class_interface ucb1x00_audio_interface = {
 	.add		= ucb1x00_audio_add,
 	.remove		= ucb1x00_audio_remove,
-	.suspend	= ucb1x00_audio_suspend,
-	.resume		= ucb1x00_audio_resume,
 };
 
 static int __init ucb1x00_audio_init(void)
 {
-	return ucb1x00_register_driver(&ucb1x00_audio_driver);
+	return ucb1x00_register_interface(&ucb1x00_audio_interface);
 }
 
 static void __exit ucb1x00_audio_exit(void)
 {
-	ucb1x00_unregister_driver(&ucb1x00_audio_driver);
+	ucb1x00_unregister_interface(&ucb1x00_audio_interface);
 }
 
 module_init(ucb1x00_audio_init);
--- linux-rmk/drivers/misc/ucb1x00-core.c	2005-07-25 05:17:11.000000000 +0200
+++ linux-z/drivers/misc/ucb1x00-core.c	2005-07-22 03:28:07.000000000 +0200
@@ -29,11 +29,7 @@
 #include <asm/hardware.h>
 #include <asm/irq.h>
 
-#include "ucb1x00.h"
-
-static DECLARE_MUTEX(ucb1x00_sem);
-static LIST_HEAD(ucb1x00_drivers);
-static LIST_HEAD(ucb1x00_devices);
+#include <asm/arch-sa1100/ucb1x00.h>
 
 /**
  *	ucb1x00_io_set_dir - set IO direction
@@ -58,9 +54,9 @@
 	spin_lock_irqsave(&ucb->io_lock, flags);
 	ucb->io_dir |= out;
 	ucb->io_dir &= ~in;
+	spin_unlock_irqrestore(&ucb->io_lock, flags);
 
 	ucb1x00_reg_write(ucb, UCB_IO_DIR, ucb->io_dir);
-	spin_unlock_irqrestore(&ucb->io_lock, flags);
 }
 
 /**
@@ -86,9 +82,9 @@
 	spin_lock_irqsave(&ucb->io_lock, flags);
 	ucb->io_out |= set;
 	ucb->io_out &= ~clear;
+	spin_unlock_irqrestore(&ucb->io_lock, flags);
 
 	ucb1x00_reg_write(ucb, UCB_IO_DATA, ucb->io_out);
-	spin_unlock_irqrestore(&ucb->io_lock, flags);
 }
 
 /**
@@ -174,7 +170,7 @@
 		if (val & UCB_ADC_DAT_VAL)
 			break;
 		/* yield to other processes */
-		set_current_state(TASK_INTERRUPTIBLE);
+		set_current_state(TASK_UNINTERRUPTIBLE);
 		schedule_timeout(1);
 	}
 
@@ -223,6 +219,57 @@
 	return IRQ_HANDLED;
 }
 
+/*
+ * A restriction with interrupts exists when using the ucb1400, as
+ * the codec read/write routines may sleep while waiting for codec
+ * access completion and uses semaphores for access control to the
+ * AC97 bus.  A complete codec read cycle could take  anywhere from
+ * 60 to 100uSec so we *definitely* don't want to spin inside the
+ * interrupt handler waiting for codec access.  So, we handle the
+ * interrupt by scheduling a RT kernel thread to run in process
+ * context instead of interrupt context.
+ */
+static int ucb1x00_thread(void *_ucb)
+{
+	struct task_struct *tsk = current;
+	DECLARE_WAITQUEUE(wait, tsk);
+	struct ucb1x00 *ucb = _ucb;
+
+	ucb->irq_task = tsk;
+	daemonize("kUCB1x00d");
+	allow_signal(SIGKILL);
+	tsk->policy = SCHED_FIFO;
+	tsk->rt_priority = 1;
+
+	add_wait_queue(&ucb->irq_wait, &wait);
+	set_task_state(tsk, TASK_INTERRUPTIBLE);
+	complete(&ucb->complete);
+
+	for (;;) {
+		if (signal_pending(tsk))
+			break;
+		schedule();
+		ucb1x00_irq(-1, ucb, NULL);
+		set_task_state(tsk, TASK_INTERRUPTIBLE);
+		enable_irq(ucb->irq);
+	}
+
+	remove_wait_queue(&ucb->irq_wait, &wait);
+	ucb->irq_task = NULL;
+	complete_and_exit(&ucb->complete, 0);
+}
+
+static irqreturn_t ucb1x00_threaded_irq(int irqnr, void *devid, struct pt_regs *regs)
+{
+	struct ucb1x00 *ucb = devid;
+	if (irqnr == ucb->irq) {
+		disable_irq(ucb->irq);
+		wake_up(&ucb->irq_wait);
+		return IRQ_HANDLED;
+	}
+	return IRQ_NONE;
+}
+
 /**
  *	ucb1x00_hook_irq - hook a UCB1x00 interrupt
  *	@ucb:   UCB1x00 structure describing chip
@@ -276,18 +323,22 @@
 
 	if (idx < 16) {
 		spin_lock_irqsave(&ucb->lock, flags);
-
-		ucb1x00_enable(ucb);
-		if (edges & UCB_RISING) {
+		if (edges & UCB_RISING)
 			ucb->irq_ris_enbl |= 1 << idx;
-			ucb1x00_reg_write(ucb, UCB_IE_RIS, ucb->irq_ris_enbl);
-		}
-		if (edges & UCB_FALLING) {
+		if (edges & UCB_FALLING)
 			ucb->irq_fal_enbl |= 1 << idx;
-			ucb1x00_reg_write(ucb, UCB_IE_FAL, ucb->irq_fal_enbl);
-		}
-		ucb1x00_disable(ucb);
 		spin_unlock_irqrestore(&ucb->lock, flags);
+
+		ucb1x00_enable(ucb);
+
+		/* This prevents spurious interrupts on the UCB1400 */
+		ucb1x00_reg_write(ucb, UCB_IE_CLEAR, 1 << idx);
+		ucb1x00_reg_write(ucb, UCB_IE_CLEAR, 0);
+
+		ucb1x00_reg_write(ucb, UCB_IE_RIS, ucb->irq_ris_enbl);
+		ucb1x00_reg_write(ucb, UCB_IE_FAL, ucb->irq_fal_enbl);
+
+		ucb1x00_disable(ucb);
 	}
 }
 
@@ -305,18 +356,16 @@
 
 	if (idx < 16) {
 		spin_lock_irqsave(&ucb->lock, flags);
-
-		ucb1x00_enable(ucb);
-		if (edges & UCB_RISING) {
+		if (edges & UCB_RISING)
 			ucb->irq_ris_enbl &= ~(1 << idx);
-			ucb1x00_reg_write(ucb, UCB_IE_RIS, ucb->irq_ris_enbl);
-		}
-		if (edges & UCB_FALLING) {
+		if (edges & UCB_FALLING)
 			ucb->irq_fal_enbl &= ~(1 << idx);
-			ucb1x00_reg_write(ucb, UCB_IE_FAL, ucb->irq_fal_enbl);
-		}
-		ucb1x00_disable(ucb);
 		spin_unlock_irqrestore(&ucb->lock, flags);
+
+		ucb1x00_enable(ucb);
+		ucb1x00_reg_write(ucb, UCB_IE_RIS, ucb->irq_ris_enbl);
+		ucb1x00_reg_write(ucb, UCB_IE_FAL, ucb->irq_fal_enbl);
+		ucb1x00_disable(ucb);
 	}
 }
 
@@ -349,16 +398,17 @@
 		ucb->irq_ris_enbl &= ~(1 << idx);
 		ucb->irq_fal_enbl &= ~(1 << idx);
 
-		ucb1x00_enable(ucb);
-		ucb1x00_reg_write(ucb, UCB_IE_RIS, ucb->irq_ris_enbl);
-		ucb1x00_reg_write(ucb, UCB_IE_FAL, ucb->irq_fal_enbl);
-		ucb1x00_disable(ucb);
-
 		irq->fn = NULL;
 		irq->devid = NULL;
 		ret = 0;
 	}
 	spin_unlock_irq(&ucb->lock);
+
+	ucb1x00_enable(ucb);
+	ucb1x00_reg_write(ucb, UCB_IE_RIS, ucb->irq_ris_enbl);
+	ucb1x00_reg_write(ucb, UCB_IE_FAL, ucb->irq_fal_enbl);
+	ucb1x00_disable(ucb);
+
 	return ret;
 
 bad:
@@ -366,36 +416,6 @@
 	return -EINVAL;
 }
 
-static int ucb1x00_add_dev(struct ucb1x00 *ucb, struct ucb1x00_driver *drv)
-{
-	struct ucb1x00_dev *dev;
-	int ret = -ENOMEM;
-
-	dev = kmalloc(sizeof(struct ucb1x00_dev), GFP_KERNEL);
-	if (dev) {
-		dev->ucb = ucb;
-		dev->drv = drv;
-
-		ret = drv->add(dev);
-
-		if (ret == 0) {
-			list_add(&dev->dev_node, &ucb->devs);
-			list_add(&dev->drv_node, &drv->devs);
-		} else {
-			kfree(dev);
-		}
-	}
-	return ret;
-}
-
-static void ucb1x00_remove_dev(struct ucb1x00_dev *dev)
-{
-	dev->drv->remove(dev);
-	list_del(&dev->dev_node);
-	list_del(&dev->drv_node);
-	kfree(dev);
-}
-
 /*
  * Try to probe our interrupt, rather than relying on lots of
  * hard-coded machine dependencies.  For reference, the expected
@@ -460,17 +480,16 @@
 static int ucb1x00_probe(struct mcp *mcp)
 {
 	struct ucb1x00 *ucb;
-	struct ucb1x00_driver *drv;
 	unsigned int id;
 	int ret = -ENODEV;
 
 	mcp_enable(mcp);
 	id = mcp_reg_read(mcp, UCB_ID);
 
-	if (id != UCB_ID_1200 && id != UCB_ID_1300) {
+	/*if (id != UCB_ID_1200 && id != UCB_ID_1300 && id != UCB_ID_1400) {
 		printk(KERN_WARNING "UCB1x00 ID not found: %04x\n", id);
 		goto err_disable;
-	}
+	}*/
 
 	ucb = kmalloc(sizeof(struct ucb1x00), GFP_KERNEL);
 	ret = -ENOMEM;
@@ -480,15 +499,20 @@
 	memset(ucb, 0, sizeof(struct ucb1x00));
 
 	ucb->cdev.class = &ucb1x00_class;
-	ucb->cdev.dev = &mcp->attached_device;
+	ucb->cdev.dev = mcp->attached_device;
 	strlcpy(ucb->cdev.class_id, "ucb1x00", sizeof(ucb->cdev.class_id));
 
 	spin_lock_init(&ucb->lock);
 	spin_lock_init(&ucb->io_lock);
 	sema_init(&ucb->adc_sem, 1);
+	init_waitqueue_head(&ucb->irq_wait);
 
-	ucb->id  = id;
 	ucb->mcp = mcp;
+	ucb->id  = id;
+	/* distinguish between UCB1400 revs 1B and 2A */
+	if (id == UCB_ID_1400 && mcp_reg_read(mcp, 0x00) == 0x002a)
+		ucb->id = UCB_ID_1400_BUGGY;
+
 	ucb->irq = ucb1x00_detect_irq(ucb);
 	if (ucb->irq == NO_IRQ) {
 		printk(KERN_ERR "UCB1x00: IRQ probe failed\n");
@@ -496,7 +520,9 @@
 		goto err_free;
 	}
 
-	ret = request_irq(ucb->irq, ucb1x00_irq, 0, "UCB1x00", ucb);
+	ret = request_irq(ucb->irq,
+			  id != UCB_ID_1400 ? ucb1x00_irq : ucb1x00_threaded_irq,
+			  0, "UCB1x00", ucb);
 	if (ret) {
 		printk(KERN_ERR "ucb1x00: unable to grab irq%d: %d\n",
 			ucb->irq, ret);
@@ -507,43 +533,36 @@
 	mcp_set_drvdata(mcp, ucb);
 
 	ret = class_device_register(&ucb->cdev);
-	if (ret)
-		goto err_irq;
 
-	INIT_LIST_HEAD(&ucb->devs);
-	down(&ucb1x00_sem);
-	list_add(&ucb->node, &ucb1x00_devices);
-	list_for_each_entry(drv, &ucb1x00_drivers, node) {
-		ucb1x00_add_dev(ucb, drv);
+	if (!ret && id == UCB_ID_1400) {
+		init_completion(&ucb->complete);
+		ret = kernel_thread(ucb1x00_thread, ucb, CLONE_KERNEL);
+		if (ret >= 0) {
+			wait_for_completion(&ucb->complete);
+			ret = 0;
+		}
 	}
-	up(&ucb1x00_sem);
-	goto out;
 
- err_irq:
-	free_irq(ucb->irq, ucb);
+	if (ret) {
+		free_irq(ucb->irq, ucb);
  err_free:
-	kfree(ucb);
+		kfree(ucb);
+	}
  err_disable:
 	mcp_disable(mcp);
- out:
 	return ret;
 }
 
 static void ucb1x00_remove(struct mcp *mcp)
 {
 	struct ucb1x00 *ucb = mcp_get_drvdata(mcp);
-	struct list_head *l, *n;
 
-	down(&ucb1x00_sem);
-	list_del(&ucb->node);
-	list_for_each_safe(l, n, &ucb->devs) {
-		struct ucb1x00_dev *dev = list_entry(l, struct ucb1x00_dev, dev_node);
-		ucb1x00_remove_dev(dev);
+	class_device_unregister(&ucb->cdev);
+	if (ucb->id == UCB_ID_1400 || ucb->id == UCB_ID_1400_BUGGY) {
+		send_sig(SIGKILL, ucb->irq_task, 1);
+		wait_for_completion(&ucb->complete);
 	}
-	up(&ucb1x00_sem);
-
 	free_irq(ucb->irq, ucb);
-	class_device_unregister(&ucb->cdev);
 }
 
 static void ucb1x00_release(struct class_device *dev)
@@ -557,59 +576,15 @@
 	.release	= ucb1x00_release,
 };
 
-int ucb1x00_register_driver(struct ucb1x00_driver *drv)
-{
-	struct ucb1x00 *ucb;
-
-	INIT_LIST_HEAD(&drv->devs);
-	down(&ucb1x00_sem);
-	list_add(&drv->node, &ucb1x00_drivers);
-	list_for_each_entry(ucb, &ucb1x00_devices, node) {
-		ucb1x00_add_dev(ucb, drv);
-	}
-	up(&ucb1x00_sem);
-	return 0;
-}
-
-void ucb1x00_unregister_driver(struct ucb1x00_driver *drv)
+int ucb1x00_register_interface(struct class_interface *intf)
 {
-	struct list_head *n, *l;
-
-	down(&ucb1x00_sem);
-	list_del(&drv->node);
-	list_for_each_safe(l, n, &drv->devs) {
-		struct ucb1x00_dev *dev = list_entry(l, struct ucb1x00_dev, drv_node);
-		ucb1x00_remove_dev(dev);
-	}
-	up(&ucb1x00_sem);
+	intf->class = &ucb1x00_class;
+	return class_interface_register(intf);
 }
 
-static int ucb1x00_suspend(struct mcp *mcp, pm_message_t state)
+void ucb1x00_unregister_interface(struct class_interface *intf)
 {
-	struct ucb1x00 *ucb = mcp_get_drvdata(mcp);
-	struct ucb1x00_dev *dev;
-
-	down(&ucb1x00_sem);
-	list_for_each_entry(dev, &ucb->devs, dev_node) {
-		if (dev->drv->suspend)
-			dev->drv->suspend(dev, state);
-	}
-	up(&ucb1x00_sem);
-	return 0;
-}
-
-static int ucb1x00_resume(struct mcp *mcp)
-{
-	struct ucb1x00 *ucb = mcp_get_drvdata(mcp);
-	struct ucb1x00_dev *dev;
-
-	down(&ucb1x00_sem);
-	list_for_each_entry(dev, &ucb->devs, dev_node) {
-		if (dev->drv->resume)
-			dev->drv->resume(dev);
-	}
-	up(&ucb1x00_sem);
-	return 0;
+	class_interface_unregister(intf);
 }
 
 static struct mcp_driver ucb1x00_driver = {
@@ -618,8 +593,6 @@
 	},
 	.probe		= ucb1x00_probe,
 	.remove		= ucb1x00_remove,
-	.suspend	= ucb1x00_suspend,
-	.resume		= ucb1x00_resume,
 };
 
 static int __init ucb1x00_init(void)
@@ -657,8 +630,8 @@
 EXPORT_SYMBOL(ucb1x00_enable_irq);
 EXPORT_SYMBOL(ucb1x00_disable_irq);
 
-EXPORT_SYMBOL(ucb1x00_register_driver);
-EXPORT_SYMBOL(ucb1x00_unregister_driver);
+EXPORT_SYMBOL(ucb1x00_register_interface);
+EXPORT_SYMBOL(ucb1x00_unregister_interface);
 
 MODULE_AUTHOR("Russell King <[email protected]>");
 MODULE_DESCRIPTION("UCB1x00 core driver");
Only in linux-rmk/drivers/misc: ucb1x00-ts.c
Only in linux-rmk/drivers/misc: ucb1x00.h
--- /dev/null	2005-07-11 13:10:49.000000000 +0200
+++ linux-z/drivers/input/touchscreen/collie_ts.c	2005-07-23 14:13:28.000000000 +0200
@@ -0,0 +1,367 @@
+/*
+ *  linux/drivers/input/touchscreen/collie_ts.c
+ *
+ *  Copyright (C) 2001 Russell King, All Rights Reserved.
+ *
+ * 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.
+ *
+ * 21-Jan-2002 <[email protected]> :
+ *
+ * Added support for synchronous A/D mode. This mode is useful to
+ * avoid noise induced in the touchpanel by the LCD, provided that
+ * the UCB1x00 has a valid LCD sync signal routed to its ADCSYNC pin.
+ * It is important to note that the signal connected to the ADCSYNC
+ * pin should provide pulses even when the LCD is blanked, otherwise
+ * a pen touch needed to unblank the LCD will never be read.
+ */
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/smp.h>
+#include <linux/smp_lock.h>
+#include <linux/sched.h>
+#include <linux/completion.h>
+#include <linux/delay.h>
+#include <linux/string.h>
+#include <linux/input.h>
+#include <linux/device.h>
+#include <linux/slab.h>
+#include <linux/kthread.h>
+
+#include <asm/dma.h>
+#include <asm/semaphore.h>
+
+#include <asm/arch-sa1100/ucb1x00.h>
+
+
+struct ucb1x00_ts {
+	struct input_dev	idev;
+	struct ucb1x00		*ucb;
+
+	struct semaphore	irq_wait;
+	struct task_struct	*rtask;
+	u16			x_res;
+	u16			y_res;
+
+	int			restart:1;
+	int			adcsync:1;
+};
+
+/*
+ * Switch to interrupt mode.
+ */
+static inline void ucb1x00_ts_mode_int(struct ucb1x00_ts *ts)
+{
+	int val = UCB_TS_CR_TSMX_POW | UCB_TS_CR_TSPX_POW |
+		  UCB_TS_CR_TSMY_GND | UCB_TS_CR_TSPY_GND |
+		  UCB_TS_CR_MODE_INT;
+	if (ts->ucb->id == UCB_ID_1400_BUGGY)
+		val &= ~(UCB_TS_CR_TSMX_POW | UCB_TS_CR_TSPX_POW);
+	ucb1x00_reg_write(ts->ucb, UCB_TS_CR, val);
+}
+
+/*
+ * Switch to pressure mode, and read pressure.  We don't need to wait
+ * here, since both plates are being driven.
+ */
+static inline unsigned int ucb1x00_ts_read_pressure(struct ucb1x00_ts *ts)
+{
+	ucb1x00_reg_write(ts->ucb, UCB_TS_CR,
+			UCB_TS_CR_TSMX_POW | UCB_TS_CR_TSPX_POW |
+			UCB_TS_CR_TSMY_GND | UCB_TS_CR_TSPY_GND |
+			UCB_TS_CR_MODE_PRES | UCB_TS_CR_BIAS_ENA);
+
+	return ucb1x00_adc_read(ts->ucb, UCB_ADC_INP_TSPY, ts->adcsync);
+}
+
+/*
+ * Switch to X position mode and measure Y plate.  We switch the plate
+ * configuration in pressure mode, then switch to position mode.  This
+ * gives a faster response time.  Even so, we need to wait about 55us
+ * for things to stabilise.
+ */
+static inline unsigned int ucb1x00_ts_read_xpos(struct ucb1x00_ts *ts)
+{
+	ucb1x00_reg_write(ts->ucb, UCB_TS_CR,
+			UCB_TS_CR_TSMX_GND | UCB_TS_CR_TSPX_POW |
+			UCB_TS_CR_MODE_PRES | UCB_TS_CR_BIAS_ENA);
+	ucb1x00_reg_write(ts->ucb, UCB_TS_CR,
+			UCB_TS_CR_TSMX_GND | UCB_TS_CR_TSPX_POW |
+			UCB_TS_CR_MODE_PRES | UCB_TS_CR_BIAS_ENA);
+	ucb1x00_reg_write(ts->ucb, UCB_TS_CR,
+			UCB_TS_CR_TSMX_GND | UCB_TS_CR_TSPX_POW |
+			UCB_TS_CR_MODE_POS | UCB_TS_CR_BIAS_ENA);
+
+	udelay(55);
+
+	return ucb1x00_adc_read(ts->ucb, UCB_ADC_INP_TSPY, ts->adcsync);
+}
+
+/*
+ * Switch to Y position mode and measure X plate.  We switch the plate
+ * configuration in pressure mode, then switch to position mode.  This
+ * gives a faster response time.  Even so, we need to wait about 55us
+ * for things to stabilise.
+ */
+static inline unsigned int ucb1x00_ts_read_ypos(struct ucb1x00_ts *ts)
+{
+	ucb1x00_reg_write(ts->ucb, UCB_TS_CR,
+			UCB_TS_CR_TSMY_GND | UCB_TS_CR_TSPY_POW |
+			UCB_TS_CR_MODE_PRES | UCB_TS_CR_BIAS_ENA);
+	ucb1x00_reg_write(ts->ucb, UCB_TS_CR,
+			UCB_TS_CR_TSMY_GND | UCB_TS_CR_TSPY_POW |
+			UCB_TS_CR_MODE_PRES | UCB_TS_CR_BIAS_ENA);
+	ucb1x00_reg_write(ts->ucb, UCB_TS_CR,
+			UCB_TS_CR_TSMY_GND | UCB_TS_CR_TSPY_POW |
+			UCB_TS_CR_MODE_POS | UCB_TS_CR_BIAS_ENA);
+
+	udelay(55);
+
+	return ucb1x00_adc_read(ts->ucb, UCB_ADC_INP_TSPX, ts->adcsync);
+}
+
+/*
+ * Switch to X plate resistance mode.  Set MX to ground, PX to
+ * supply.  Measure current.
+ */
+static inline unsigned int ucb1x00_ts_read_xres(struct ucb1x00_ts *ts)
+{
+	ucb1x00_reg_write(ts->ucb, UCB_TS_CR,
+			UCB_TS_CR_TSMX_GND | UCB_TS_CR_TSPX_POW |
+			UCB_TS_CR_MODE_PRES | UCB_TS_CR_BIAS_ENA);
+	return ucb1x00_adc_read(ts->ucb, 0, ts->adcsync);
+}
+
+/*
+ * Switch to Y plate resistance mode.  Set MY to ground, PY to
+ * supply.  Measure current.
+ */
+static inline unsigned int ucb1x00_ts_read_yres(struct ucb1x00_ts *ts)
+{
+	ucb1x00_reg_write(ts->ucb, UCB_TS_CR,
+			UCB_TS_CR_TSMY_GND | UCB_TS_CR_TSPY_POW |
+			UCB_TS_CR_MODE_PRES | UCB_TS_CR_BIAS_ENA);
+	return ucb1x00_adc_read(ts->ucb, 0, ts->adcsync);
+}
+
+/*
+ * This is a RT kernel thread that handles the ADC accesses
+ * (mainly so we can use semaphores in the UCB1200 core code
+ * to serialise accesses to the ADC).  The UCB1400 access
+ * functions are expected to be able to sleep as well.
+ */
+static int ucb1x00_thread(void *_ts)
+{
+	struct ucb1x00_ts *ts = _ts;
+	struct task_struct *tsk = current;
+	int valid;
+
+	ts->rtask = tsk;
+
+	/*
+	 * We run as a real-time thread.  However, thus far
+	 * this doesn't seem to be necessary.
+	 */
+	tsk->policy = SCHED_FIFO;
+	tsk->rt_priority = 1;
+
+	valid = 0;
+	for (;;) {
+		unsigned int x, y, p, val;
+
+		ts->restart = 0;
+
+		ucb1x00_adc_enable(ts->ucb);
+
+		x = ucb1x00_ts_read_xpos(ts);
+		y = ucb1x00_ts_read_ypos(ts);
+		p = ucb1x00_ts_read_pressure(ts);
+
+		/*
+		 * Switch back to interrupt mode.
+		 */
+		ucb1x00_ts_mode_int(ts);
+		ucb1x00_adc_disable(ts->ucb);
+
+		msleep(10);
+
+		ucb1x00_enable(ts->ucb);
+		val = ucb1x00_reg_read(ts->ucb, UCB_TS_CR);
+
+		if (val & (UCB_TS_CR_TSPX_LOW | UCB_TS_CR_TSMX_LOW)) {
+			ucb1x00_enable_irq(ts->ucb, UCB_IRQ_TSPX, UCB_FALLING);
+			ucb1x00_disable(ts->ucb);
+
+			/*
+			 * If we spat out a valid sample set last time,
+			 * spit out a "pen off" sample here.
+			 */
+			if (valid) {
+				input_report_abs(&ts->idev, ABS_PRESSURE, 0);
+				input_sync(&ts->idev);
+				valid = 0;
+			}
+
+			/*
+			 * Since ucb1x00_enable_irq() might sleep due
+			 * to the way the UCB1400 regs are accessed, we
+			 * can't use set_task_state() before that call,
+			 * and not changing state before enabling the
+			 * interrupt is racy.  A semaphore solves all
+			 * those issues quite nicely.
+			 */
+			down_interruptible(&ts->irq_wait);
+		} else {
+			ucb1x00_disable(ts->ucb);
+
+			/*
+			 * Filtering is policy.  Policy belongs in user
+			 * space.  We therefore leave it to user space
+			 * to do any filtering they please.
+			 */
+			if (!ts->restart) {
+				input_report_abs(&ts->idev, ABS_X, x);
+				input_report_abs(&ts->idev, ABS_Y, y);
+				input_report_abs(&ts->idev, ABS_PRESSURE, p);
+				input_sync(&ts->idev);
+				valid = 1;
+			}
+
+			msleep_interruptible(10);
+		}
+
+		if (kthread_should_stop())
+			break;
+	}
+
+	ts->rtask = NULL;
+	return 0;
+}
+
+/*
+ * We only detect touch screen _touches_ with this interrupt
+ * handler, and even then we just schedule our task.
+ */
+static void ucb1x00_ts_irq(int idx, void *id)
+{
+	struct ucb1x00_ts *ts = id;
+	ucb1x00_disable_irq(ts->ucb, UCB_IRQ_TSPX, UCB_FALLING);
+	up(&ts->irq_wait);
+}
+
+static int ucb1x00_ts_open(struct input_dev *idev)
+{
+	struct ucb1x00_ts *ts = (struct ucb1x00_ts *)idev;
+	int ret = 0;
+	struct task_struct *task;
+
+	BUG_ON(ts->rtask);
+
+	sema_init(&ts->irq_wait, 0);
+	ret = ucb1x00_hook_irq(ts->ucb, UCB_IRQ_TSPX, ucb1x00_ts_irq, ts);
+	if (ret < 0)
+		goto out;
+
+	/*
+	 * If we do this at all, we should allow the user to
+	 * measure and read the X and Y resistance at any time.
+	 */
+	ucb1x00_adc_enable(ts->ucb);
+	ts->x_res = ucb1x00_ts_read_xres(ts);
+	ts->y_res = ucb1x00_ts_read_yres(ts);
+	ucb1x00_adc_disable(ts->ucb);
+
+	task = kthread_run(ucb1x00_thread, ts, "ktsd");
+	if (!IS_ERR(task)) {
+		ret = 0;
+	} else {
+		ucb1x00_free_irq(ts->ucb, UCB_IRQ_TSPX, ts);
+		ret = -EFAULT;
+	}
+
+ out:
+	return ret;
+}
+
+/*
+ * Release touchscreen resources.  Disable IRQs.
+ */
+static void ucb1x00_ts_close(struct input_dev *idev)
+{
+	struct ucb1x00_ts *ts = (struct ucb1x00_ts *)idev;
+
+	if (ts->rtask)
+		kthread_stop(ts->rtask);
+
+	ucb1x00_enable(ts->ucb);
+	ucb1x00_free_irq(ts->ucb, UCB_IRQ_TSPX, ts);
+	ucb1x00_reg_write(ts->ucb, UCB_TS_CR, 0);
+	ucb1x00_disable(ts->ucb);
+}
+
+/*
+ * Initialisation.
+ */
+static int ucb1x00_ts_add(struct class_device *dev)
+{
+	struct ucb1x00 *ucb = classdev_to_ucb1x00(dev);
+	struct ucb1x00_ts *ts;
+
+	ts = kmalloc(sizeof(struct ucb1x00_ts), GFP_KERNEL);
+	if (!ts)
+		return -ENOMEM;
+
+	memset(ts, 0, sizeof(struct ucb1x00_ts));
+
+	ts->ucb = ucb;
+	ts->adcsync = UCB_NOSYNC;
+
+	ts->idev.name       = "Touchscreen panel";
+	ts->idev.id.product = ts->ucb->id;
+	ts->idev.open       = ucb1x00_ts_open;
+	ts->idev.close      = ucb1x00_ts_close;
+
+	set_bit(EV_ABS, ts->idev.evbit);
+	set_bit(ABS_X, ts->idev.absbit);
+	set_bit(ABS_Y, ts->idev.absbit);
+	set_bit(ABS_PRESSURE, ts->idev.absbit);
+
+	input_register_device(&ts->idev);
+
+	ucb->ts_data = ts;
+
+	return 0;
+}
+
+static void ucb1x00_ts_remove(struct class_device *dev)
+{
+	struct ucb1x00 *ucb = classdev_to_ucb1x00(dev);
+	struct ucb1x00_ts *ts = ucb->ts_data;
+
+	input_unregister_device(&ts->idev);
+	kfree(ts);
+}
+
+static struct class_interface ucb1x00_ts_interface = {
+	.add		= ucb1x00_ts_add,
+	.remove		= ucb1x00_ts_remove,
+};
+
+static int __init ucb1x00_ts_init(void)
+{
+	return ucb1x00_register_interface(&ucb1x00_ts_interface);
+}
+
+static void __exit ucb1x00_ts_exit(void)
+{
+	ucb1x00_unregister_interface(&ucb1x00_ts_interface);
+}
+
+module_init(ucb1x00_ts_init);
+module_exit(ucb1x00_ts_exit);
+
+MODULE_AUTHOR("Russell King <[email protected]>");
+MODULE_DESCRIPTION("UCB1x00 touchscreen driver");
+MODULE_LICENSE("GPL");


-- 
teflon -- maybe it is a trademark, but it should not be.
-
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to [email protected]
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Please read the FAQ at  http://www.tux.org/lkml/

[Index of Archives]     [Kernel Newbies]     [Netfilter]     [Bugtraq]     [Photo]     [Gimp]     [Yosemite News]     [MIPS Linux]     [ARM Linux]     [Linux Security]     [Linux RAID]     [Video 4 Linux]     [Linux for the blind]
  Powered by Linux