This patch adds support for Sharp CE-RH2 on Spitz.

It is not clean enough to be upstreamed:
- It is a bit syslog-noisy.
- Does not support other Zaurus models.
- Maybe split to more parts:
  * MAX1111 driver
  * linear input device
  * virtual keyboard on top of linear input device

Index: linux-2.6.24/arch/arm/mach-pxa/spitz.c
===================================================================
--- linux-2.6.24.orig/arch/arm/mach-pxa/spitz.c	2008-03-10 17:05:37.000000000 +0000
+++ linux-2.6.24/arch/arm/mach-pxa/spitz.c	2008-03-10 17:05:55.000000000 +0000
@@ -259,6 +259,13 @@
 	.id		= -1,
 };
 
+/*
+ * Spitz Remote Control Device
+ */
+static struct platform_device sharpsl_rc_device = {
+	.name		= "sharpsl-remote-control",
+	.id		= -1,
+};
 
 /*
  * Spitz LEDs
@@ -548,6 +555,7 @@
 	&spitzscoop_device,
 	&spitzssp_device,
 	&spitzkbd_device,
+	&sharpsl_rc_device,
 	&spitzts_device,
 	&spitzbl_device,
 	&spitzled_device,
Index: linux-2.6.24/drivers/input/keyboard/Kconfig
===================================================================
--- linux-2.6.24.orig/drivers/input/keyboard/Kconfig	2008-03-10 17:05:40.000000000 +0000
+++ linux-2.6.24/drivers/input/keyboard/Kconfig	2008-03-10 17:05:55.000000000 +0000
@@ -154,6 +154,17 @@
 	  To compile this driver as a module, choose M here: the
 	  module will be called spitzkbd.
 
+config SHARPSL_RC
+	tristate "Sharp SL-Cxx00 Remote Control"
+	depends on PXA_SHARPSL
+	default y
+	help
+	  Say Y here to enable the remote on the Sharp Zaurus SL-Cxx00,
+	  SL-C1000, SL-C3000 and Sl-C3100 series of PDAs.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called sharpsl_rc.
+
 config KEYBOARD_AMIGA
 	tristate "Amiga keyboard"
 	depends on AMIGA
Index: linux-2.6.24/drivers/input/keyboard/Makefile
===================================================================
--- linux-2.6.24.orig/drivers/input/keyboard/Makefile	2008-03-10 17:05:40.000000000 +0000
+++ linux-2.6.24/drivers/input/keyboard/Makefile	2008-03-10 17:06:17.000000000 +0000
@@ -26,3 +26,5 @@
 obj-$(CONFIG_KEYBOARD_HP7XX)		+= jornada720_kbd.o
 obj-$(CONFIG_KEYBOARD_MAPLE)		+= maple_keyb.o
 obj-$(CONFIG_KEYBOARD_BFIN)		+= bf54x-keys.o
+obj-$(CONFIG_SHARPSL_RC)		+= sharpsl_rc.o
+
Index: linux-2.6.24/drivers/input/keyboard/sharpsl_rc.c
===================================================================
--- /dev/null	1970-01-01 00:00:00.000000000 +0000
+++ linux-2.6.24/drivers/input/keyboard/sharpsl_rc.c	2008-03-10 17:05:55.000000000 +0000
@@ -0,0 +1,291 @@
+/*
+ *  Keyboard driver for Sharp Clamshell Models (SL-Cxx00)
+ *
+ *  Copyright (c) 2004-2005 Richard Purdie
+ *
+ *  Based on corgikbd.c and Sharp's RC driver
+ *
+ *  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.
+ *
+ */
+
+#define DEBUG 1
+#include <linux/delay.h>
+#include <linux/platform_device.h>
+#include <linux/init.h>
+#include <linux/input.h>
+#include <linux/interrupt.h>
+#include <linux/jiffies.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+
+#ifdef CONFIG_MACH_SPITZ
+#include <asm/arch/spitz.h>
+#endif
+#ifdef CONFIG_MACH_CORGI
+#include <asm/arch/corgi.h>
+#endif
+
+#include <asm/arch/hardware.h>
+#include <asm/arch/pxa-regs.h>
+#include <asm/hardware/scoop.h>
+#include <asm/arch/sharpsl.h>
+#include <asm/hardware/sharpsl_pm.h>
+
+#define DPRINTK(fmt, args...) dev_dbg(data->dev, fmt "\n", ##args)
+
+struct remote_control_key {
+	unsigned char min;
+	unsigned char max;
+	unsigned char key;
+};
+
+#ifdef CONFIG_MACH_SPITZ
+#define REMOTE_AKIN_PULLUP SPITZ_SCP2_AKIN_PULLUP
+#define REMOTE_SCOOP_DEVICE spitzscoop2_device
+#define REMOTE_GPIO_INT SPITZ_GPIO_AK_INT
+#define REMOTE_IRQ_INT SPITZ_IRQ_GPIO_AK_INT
+static struct remote_control_key remote_keys[] = {
+	{ 25, 35, KEY_STOPCD},
+	{ 55, 65, KEY_PLAYPAUSE},
+	{ 85, 95, KEY_NEXTSONG},
+	{ 115, 125, KEY_VOLUMEUP},
+	{ 145, 155, KEY_PREVIOUSSONG},
+	{ 180, 190, KEY_MUTE},
+	{ 215, 225, KEY_VOLUMEDOWN},
+};
+#endif
+#ifdef CONFIG_MACH_CORGI
+#define REMOTE_AKIN_PULLUP CORGI_SCP_AKIN_PULLUP
+#define REMOTE_SCOOP_DEVICE corgiscoop_device
+#define REMOTE_GPIO_INT CORGI_GPIO_AK_INT
+#define REMOTE_IRQ_INT CORGI_IRQ_GPIO_AK_INT
+static struct remote_control_key remote_keys[] = {
+    //These need to be fixed for the CE-RH1's values
+	{ 25, 35, KEY_STOPCD},
+	{ 55, 65, KEY_PLAYPAUSE},
+	{ 85, 95, KEY_NEXTSONG},
+	{ 115, 125, KEY_VOLUMEUP},
+	{ 145, 155, KEY_PREVIOUSSONG},
+	{ 180, 190, KEY_MUTE},
+	{ 215, 225, KEY_VOLUMEDOWN},
+};
+#endif
+
+#define RELEASE_HI      230
+#define MAX_EARPHONE    6
+#define RC_POLL_MS      10
+#define RC_FINISH_MS    500
+#define WAIT_STATE      3
+#define NOISE_THRESHOLD 100
+
+struct sharpsl_rc {
+	struct input_dev *input;
+	struct device *dev;
+
+	spinlock_t lock;
+	struct timer_list rctimer;
+	struct timer_list rctimer_finish;
+
+	unsigned int handling_press;
+	unsigned int noise;
+	unsigned int state;
+	unsigned int last_key;
+};
+
+static int get_remocon_raw(void)
+{
+	int i, val;
+
+	val = sharpsl_pm_pxa_read_max1111(MAX1111_REMCOM);
+	for (i = 0; i < ARRAY_SIZE(remote_keys); ++i) {
+		if (val >= remote_keys[i].min
+			&& val <= remote_keys[i].max) {
+			printk("get_remocon_raw: VAL=%i, KEY=%i\n", val, remote_keys[i].key);
+			return remote_keys[i].key;
+		}
+	}
+	return 0;
+}
+
+static irqreturn_t sharpsl_rc_interrupt(int irq, void *dev_id, struct pt_regs *regs)
+{
+	struct sharpsl_rc *data = dev_id;
+	DPRINTK("sharpsl_rc_interrupt %d\n", irq);
+	if (!data->handling_press) {
+		DPRINTK("handling interrupt");
+		data->handling_press = 1;
+		data->noise = 0;
+		data->state = 0;
+		data->last_key = 0;
+
+		reset_scoop_gpio(&REMOTE_SCOOP_DEVICE.dev, REMOTE_AKIN_PULLUP);
+
+		mod_timer(&data->rctimer, jiffies + msecs_to_jiffies(RC_POLL_MS));
+	}
+	return IRQ_HANDLED;
+}
+
+static void sharpsl_rc_timer_callback(unsigned long dataPtr)
+{
+	struct sharpsl_rc *data = (struct sharpsl_rc *) dataPtr;
+	int timer = 1;
+	int key = get_remocon_raw();
+	DPRINTK("timer callback, key: %d", key);
+
+	//wait for value to stabilize
+	if (data->state < WAIT_STATE) {
+		if (data->last_key != key) {
+			++data->noise;
+			if (data->noise > NOISE_THRESHOLD) {
+				DPRINTK("too much noise, bailing");
+				timer = 0;
+			}
+			data->state = 0;
+		} else {
+			++data->state;
+		}
+		data->last_key = key;
+
+	//stable value, send event
+	} else if (data->state == WAIT_STATE) {
+		data->noise = 0;
+		//non-key returned, skip the rest of the states and bail now
+		if (data->last_key == 0) {
+			DPRINTK("non-key detected %d, noise: %d", data->last_key, data->noise);
+			timer = 0;
+		//send button press
+		} else {
+			DPRINTK("key press detected %d, noise %d", data->last_key, data->noise);
+			input_report_key(data->input, data->last_key, 1);
+		}
+		++data->state;
+
+	//wait until key is released
+	} else if (data->state < WAIT_STATE * 2) {
+		if (key == data->last_key
+			&& data->noise < NOISE_THRESHOLD) {
+			data->state = WAIT_STATE + 1;
+			++data->noise;
+		} else {
+			++data->state;
+		}
+	//key is released, send event
+	} else {
+		//send button release
+		DPRINTK("release key %d", data->last_key);
+		input_report_key(data->input, data->last_key, 0);
+		timer = 0;
+	}
+	if (timer) {
+		mod_timer(&data->rctimer, jiffies + msecs_to_jiffies(RC_POLL_MS));
+	} else {
+		set_scoop_gpio(&REMOTE_SCOOP_DEVICE.dev, REMOTE_AKIN_PULLUP);
+		data->handling_press = 0;
+	}
+}
+
+static int __init sharpsl_rc_probe(struct platform_device *pdev)
+{
+	struct sharpsl_rc *sharpsl_rc;
+	struct input_dev *input_dev;
+	int i, ret;
+
+	dev_dbg(&pdev->dev, "sharpsl_rc_probe\n");
+
+	sharpsl_rc = kzalloc(sizeof(struct sharpsl_rc), GFP_KERNEL);
+	input_dev = input_allocate_device();
+	if (!sharpsl_rc || !input_dev) {
+		kfree(sharpsl_rc);
+		input_free_device(input_dev);
+		return -ENOMEM;
+	}
+
+	platform_set_drvdata(pdev, sharpsl_rc);
+
+	sharpsl_rc->dev = &pdev->dev;
+	sharpsl_rc->input = input_dev;
+	spin_lock_init(&sharpsl_rc->lock);
+
+	/* Init Remote Control Timer */
+	init_timer(&sharpsl_rc->rctimer);
+	sharpsl_rc->rctimer.function = sharpsl_rc_timer_callback;
+	sharpsl_rc->rctimer.data = (unsigned long) sharpsl_rc;
+
+	input_dev->name = "Sharp Remote Control CE-RHX";
+	input_dev->phys = "sharpsl_rc/input0";
+	input_dev->id.bustype = BUS_HOST;
+	input_dev->id.vendor = 0x0001;
+	input_dev->id.product = 0x0001;
+	input_dev->id.version = 0x0100;
+	input_dev->cdev.dev = &pdev->dev;
+	input_dev->private = sharpsl_rc;
+
+	input_dev->evbit[0] = BIT(EV_KEY);
+
+	for (i = 0; i <= ARRAY_SIZE(remote_keys); i++)
+	  set_bit(remote_keys[i].key, input_dev->keybit);
+
+	input_register_device(sharpsl_rc->input);
+
+	pxa_gpio_mode(REMOTE_GPIO_INT | GPIO_IN);
+	ret = request_irq(REMOTE_IRQ_INT,
+					  sharpsl_rc_interrupt,
+					  IRQF_DISABLED | IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING | IRQF_SHARED,
+					  "sharpsl_rc",
+					  sharpsl_rc);
+	if (ret < 0) {
+		dev_dbg(&pdev->dev, "Can't get IRQ: %d!\n", i);
+		kfree(sharpsl_rc);
+		input_free_device(input_dev);
+		return ret;
+	}
+
+	return 0;
+}
+
+static int sharpsl_rc_remove(struct platform_device *pdev)
+{
+	struct sharpsl_rc *sharpsl_rc = platform_get_drvdata(pdev);
+
+	dev_dbg(&pdev->dev, "sharpsl_rc_remove\n");
+
+	free_irq(REMOTE_IRQ_INT, sharpsl_rc);
+	del_timer_sync(&sharpsl_rc->rctimer);
+	input_unregister_device(sharpsl_rc->input);
+	kfree(sharpsl_rc);
+
+	return 0;
+}
+
+static struct platform_driver sharpsl_rc_driver = {
+	.probe		= sharpsl_rc_probe,
+	.remove		= sharpsl_rc_remove,
+	.suspend	= NULL,
+	.resume		= NULL,
+	.driver		= {
+		.name	= "sharpsl-remote-control",
+	},
+};
+
+static int __devinit sharpsl_rc_init(void)
+{
+	printk("sharpsl_rc_init\n");
+	return platform_driver_register(&sharpsl_rc_driver);
+}
+
+static void __exit sharpsl_rc_exit(void)
+{
+	printk("sharpsl_rc_exit\n");
+	platform_driver_unregister(&sharpsl_rc_driver);
+}
+
+module_init(sharpsl_rc_init);
+module_exit(sharpsl_rc_exit);
+
+MODULE_AUTHOR("Justin Patrin <papercrane@reversefold.com>");
+MODULE_AUTHOR("Richard Purdie <rpurdie@rpsys.net>");
+MODULE_DESCRIPTION("SharpSL Remote Control Driver");
+MODULE_LICENSE("GPL");
Index: linux-2.6.24/drivers/input/keyboard/spitzkbd.c
===================================================================
--- linux-2.6.24.orig/drivers/input/keyboard/spitzkbd.c	2008-01-24 22:58:37.000000000 +0000
+++ linux-2.6.24/drivers/input/keyboard/spitzkbd.c	2008-03-10 17:05:55.000000000 +0000
@@ -19,6 +19,7 @@
 #include <linux/jiffies.h>
 #include <linux/module.h>
 #include <linux/slab.h>
+#include <linux/kmod.h>
 
 #include <asm/arch/spitz.h>
 #include <asm/arch/hardware.h>
@@ -279,13 +280,21 @@
 static int sharpsl_hinge_state;
 static int hinge_count;
 
+void spitzkbd_handle_sharpsl_rc(void *arg) {
+	request_module("sharpsl_rc");
+}
+
+DECLARE_WORK(spitzkbd_work, spitzkbd_handle_sharpsl_rc);
+
 static void spitzkbd_hinge_timer(unsigned long data)
 {
 	struct spitzkbd *spitzkbd_data = (struct spitzkbd *) data;
 	unsigned long state;
 	unsigned long flags;
+	unsigned int headphone, remote;
 
 	state = GPLR(SPITZ_GPIO_SWA) & (GPIO_bit(SPITZ_GPIO_SWA)|GPIO_bit(SPITZ_GPIO_SWB));
+	state |= (GPLR(SPITZ_GPIO_HP_IN) & GPIO_bit(SPITZ_GPIO_HP_IN));
 	state |= (GPLR(SPITZ_GPIO_AK_INT) & GPIO_bit(SPITZ_GPIO_AK_INT));
 	if (state != sharpsl_hinge_state) {
 		hinge_count = 0;
@@ -299,9 +308,18 @@
 
 		input_report_switch(spitzkbd_data->input, SW_LID, ((GPLR(SPITZ_GPIO_SWA) & GPIO_bit(SPITZ_GPIO_SWA)) != 0));
 		input_report_switch(spitzkbd_data->input, SW_TABLET_MODE, ((GPLR(SPITZ_GPIO_SWB) & GPIO_bit(SPITZ_GPIO_SWB)) != 0));
-		input_report_switch(spitzkbd_data->input, SW_HEADPHONE_INSERT, ((GPLR(SPITZ_GPIO_AK_INT) & GPIO_bit(SPITZ_GPIO_AK_INT)) != 0));
+
+		headphone = ((GPLR(SPITZ_GPIO_HP_IN) & GPIO_bit(SPITZ_GPIO_HP_IN)) != 0);
+		input_report_switch(spitzkbd_data->input, SW_HEADPHONE_INSERT, headphone);
+
+		remote = headphone && ((GPLR(SPITZ_GPIO_AK_INT) & GPIO_bit(SPITZ_GPIO_AK_INT)) == 0);
+		input_report_switch(spitzkbd_data->input, SW_REMOTE_INSERT, remote);
 		input_sync(spitzkbd_data->input);
 
+		if (remote) {
+			schedule_work(&spitzkbd_work);
+		}
+
 		spin_unlock_irqrestore(&spitzkbd_data->lock, flags);
 	} else {
 		mod_timer(&spitzkbd_data->htimer, jiffies + msecs_to_jiffies(HINGE_SCAN_INTERVAL));
@@ -395,6 +413,7 @@
 	set_bit(SW_LID, input_dev->swbit);
 	set_bit(SW_TABLET_MODE, input_dev->swbit);
 	set_bit(SW_HEADPHONE_INSERT, input_dev->swbit);
+	set_bit(SW_REMOTE_INSERT, input_dev->swbit);
 
 	err = input_register_device(input_dev);
 	if (err)
@@ -432,9 +451,12 @@
 	request_irq(SPITZ_IRQ_GPIO_SWB, spitzkbd_hinge_isr,
 		    IRQF_DISABLED | IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING,
 		    "Spitzkbd SWB", spitzkbd);
-	request_irq(SPITZ_IRQ_GPIO_AK_INT, spitzkbd_hinge_isr,
+	request_irq(SPITZ_IRQ_GPIO_HP_IN, spitzkbd_hinge_isr,
 		    IRQF_DISABLED | IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING,
 		    "Spitzkbd HP", spitzkbd);
+  	request_irq(SPITZ_IRQ_GPIO_AK_INT, spitzkbd_hinge_isr,
+ 		    IRQF_DISABLED | IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING | IRQF_SHARED,
+            "Spitzkbd HP Type", spitzkbd);
 
 	return 0;
 
@@ -455,6 +477,7 @@
 	free_irq(SPITZ_IRQ_GPIO_ON_KEY, spitzkbd);
 	free_irq(SPITZ_IRQ_GPIO_SWA, spitzkbd);
 	free_irq(SPITZ_IRQ_GPIO_SWB, spitzkbd);
+	free_irq(SPITZ_IRQ_GPIO_HP_IN, spitzkbd);
 	free_irq(SPITZ_IRQ_GPIO_AK_INT, spitzkbd);
 
 	del_timer_sync(&spitzkbd->htimer);
Index: linux-2.6.24/arch/arm/mach-pxa/sharpsl.h
===================================================================
--- linux-2.6.24.orig/arch/arm/mach-pxa/sharpsl.h	2008-03-10 17:05:35.000000000 +0000
+++ linux-2.6.24/arch/arm/mach-pxa/sharpsl.h	2008-03-10 17:05:55.000000000 +0000
@@ -37,15 +37,10 @@
  */
 #define READ_GPIO_BIT(x)    (GPLR(x) & GPIO_bit(x))
 
-/* MAX1111 Channel Definitions */
-#define MAX1111_BATT_VOLT   4u
-#define MAX1111_BATT_TEMP   2u
-#define MAX1111_ACIN_VOLT   6u
-
 extern struct battery_thresh spitz_battery_levels_acin[];
 extern struct battery_thresh spitz_battery_levels_noac[];
 void sharpsl_pm_pxa_init(void);
 void sharpsl_pm_pxa_remove(void);
-int sharpsl_pm_pxa_read_max1111(int channel);
+
 
 
Index: linux-2.6.24/arch/arm/mach-pxa/sharpsl_pm.c
===================================================================
--- linux-2.6.24.orig/arch/arm/mach-pxa/sharpsl_pm.c	2008-01-24 22:58:37.000000000 +0000
+++ linux-2.6.24/arch/arm/mach-pxa/sharpsl_pm.c	2008-03-10 17:05:55.000000000 +0000
@@ -135,6 +135,8 @@
 			| MAXCTRL_SGL | MAXCTRL_UNI | MAXCTRL_STR);
 }
 
+EXPORT_SYMBOL(sharpsl_pm_pxa_read_max1111);
+
 void sharpsl_pm_pxa_init(void)
 {
 	pxa_gpio_mode(sharpsl_pm.machinfo->gpio_acin | GPIO_IN);
Index: linux-2.6.24/include/asm-arm/hardware/sharpsl_pm.h
===================================================================
--- linux-2.6.24.orig/include/asm-arm/hardware/sharpsl_pm.h	2008-01-24 22:58:37.000000000 +0000
+++ linux-2.6.24/include/asm-arm/hardware/sharpsl_pm.h	2008-03-10 17:05:55.000000000 +0000
@@ -104,3 +104,10 @@
 irqreturn_t sharpsl_chrg_full_isr(int irq, void *dev_id);
 irqreturn_t sharpsl_fatal_isr(int irq, void *dev_id);
 
+/* MAX1111 Channel Definitions */
+#define MAX1111_REMCOM      0u
+#define MAX1111_BATT_VOLT   4u
+#define MAX1111_BATT_TEMP   2u
+#define MAX1111_ACIN_VOLT   6u
+
+int sharpsl_pm_pxa_read_max1111(int channel);
Index: linux-2.6.24/include/linux/input.h
===================================================================
--- linux-2.6.24.orig/include/linux/input.h	2008-01-24 22:58:37.000000000 +0000
+++ linux-2.6.24/include/linux/input.h	2008-03-10 17:05:55.000000000 +0000
@@ -636,6 +636,7 @@
 #define SW_TABLET_MODE		0x01  /* set = tablet mode */
 #define SW_HEADPHONE_INSERT	0x02  /* set = inserted */
 #define SW_RADIO		0x03  /* set = radio enabled */
+#define SW_REMOTE_INSERT	0x04  /* set = remote */
 #define SW_MAX			0x0f
 #define SW_CNT			(SW_MAX+1)
 
Index: linux-2.6.24/arch/arm/mach-pxa/spitz_pm.c
===================================================================
--- linux-2.6.24.orig/arch/arm/mach-pxa/spitz_pm.c	2008-03-10 17:05:40.000000000 +0000
+++ linux-2.6.24/arch/arm/mach-pxa/spitz_pm.c	2008-03-10 17:05:55.000000000 +0000
@@ -162,6 +162,13 @@
 	if (resume_on_alarm && (PEDR & PWER_RTC))
 		is_resume |= PWER_RTC;
 
+	printk("wakeup: PEDR: %x, PKSR: %x, HP_IN: %x, AK_INT: %x\n", PEDR, PKSR, GPIO_bit(SPITZ_GPIO_HP_IN), GPIO_bit(SPITZ_GPIO_AK_INT));
+
+	//remote/headphone interrupt, wakeup
+	if (PEDR == 0 && (PKSR & 0xc0d01) != 0) {
+		is_resume |= PWER_RTC;
+	}
+
 	dev_dbg(sharpsl_pm.dev, "is_resume: %x\n",is_resume);
 	return is_resume;
 }