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/

Reply via email to