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.26/arch/arm/mach-pxa/spitz.c =================================================================== --- linux-2.6.26.orig/arch/arm/mach-pxa/spitz.c 2008-07-13 21:51:29.000000000 +0000 +++ linux-2.6.26/arch/arm/mach-pxa/spitz.c 2008-07-15 10:11:15.000000000 +0000 @@ -261,6 +261,13 @@ .id = -1, }; +/* + * Spitz Remote Control Device + */ +static struct platform_device sharpsl_rc_device = { + .name = "sharpsl-remote-control", + .id = -1, +}; /* * Spitz LEDs @@ -522,6 +529,7 @@ &spitzscoop_device, &spitzssp_device, &spitzkbd_device, + &sharpsl_rc_device, &spitzts_device, &spitzbl_device, &spitzled_device, Index: linux-2.6.26/drivers/input/keyboard/Kconfig =================================================================== --- linux-2.6.26.orig/drivers/input/keyboard/Kconfig 2008-07-13 21:51:29.000000000 +0000 +++ linux-2.6.26/drivers/input/keyboard/Kconfig 2008-07-15 10:11:15.000000000 +0000 @@ -175,6 +175,17 @@ Say Y only if you know, what you are doing! +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.26/drivers/input/keyboard/Makefile =================================================================== --- linux-2.6.26.orig/drivers/input/keyboard/Makefile 2008-07-13 21:51:29.000000000 +0000 +++ linux-2.6.26/drivers/input/keyboard/Makefile 2008-07-15 10:11:15.000000000 +0000 @@ -26,4 +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 obj-$(CONFIG_KEYBOARD_SH_KEYSC) += sh_keysc.o Index: linux-2.6.26/drivers/input/keyboard/sharpsl_rc.c =================================================================== --- /dev/null 1970-01-01 00:00:00.000000000 +0000 +++ linux-2.6.26/drivers/input/keyboard/sharpsl_rc.c 2008-07-15 10:11:15.000000000 +0000 @@ -0,0 +1,319 @@ +/* + * 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 +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#define DPRINTK(fmt, args...) dev_dbg(data->dev, fmt "\n", ##args) + +struct remote_control_key { + unsigned char min; + unsigned char max; + unsigned char key; +}; + +static struct remote_control_key remote_keys_spitz[] = { + /* CE-RH2 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}, +}; +static struct remote_control_key remote_keys_corgi[] = { + /* CE-RH1 values */ + { 27, 35, KEY_STOPCD}, + { 7, 13, KEY_PLAYPAUSE}, + { 77, 93, KEY_NEXTSONG}, + { 115, 132, KEY_VOLUMEUP}, + { 46, 58, KEY_PREVIOUSSONG}, + { 170, 186, KEY_VOLUMEDOWN}, +}; + +#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; + struct remote_control_key *remote_keys; + + if (machine_is_borzoi() || machine_is_spitz() || machine_is_akita()) + remote_keys = remote_keys_spitz; + else + remote_keys = remote_keys_corgi; + + val = sharpsl_pm_pxa_read_max1111(MAX1111_REMCOM); + for (i = 0; i < (machine_is_borzoi() || machine_is_spitz() || machine_is_akita() ? + ARRAY_SIZE(remote_keys_spitz) : ARRAY_SIZE(remote_keys_corgi)); + ++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 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; + + if (machine_is_borzoi() || machine_is_spitz()) + reset_scoop_gpio(platform_scoop_config->devs[1].dev, SPITZ_SCP2_AKIN_PULLUP); + else if (machine_is_akita()) + akita_reset_ioexp(&akitaioexp_device.dev, AKITA_IOEXP_AKIN_PULLUP); + else + reset_scoop_gpio(platform_scoop_config->devs[0].dev, CORGI_SCP_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 { + if (machine_is_borzoi() || machine_is_spitz()) + set_scoop_gpio(platform_scoop_config->devs[1].dev, SPITZ_SCP2_AKIN_PULLUP); + else if (machine_is_akita()) + akita_set_ioexp(&akitaioexp_device.dev, AKITA_IOEXP_AKIN_PULLUP); + else + set_scoop_gpio(platform_scoop_config->devs[0].dev, CORGI_SCP_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; + struct remote_control_key *remote_keys; + + 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->dev.parent = &pdev->dev; + + input_dev->evbit[0] = BIT(EV_KEY); + + if (machine_is_borzoi() || machine_is_spitz() || machine_is_akita()) + remote_keys = remote_keys_spitz; + else + remote_keys = remote_keys_corgi; + for (i = 0; i < (machine_is_borzoi() || machine_is_spitz() || machine_is_akita() ? + ARRAY_SIZE(remote_keys_spitz) : ARRAY_SIZE(remote_keys_corgi)); + ++i) + set_bit(remote_keys[i].key, input_dev->keybit); + + ret = input_register_device(sharpsl_rc->input); + if (ret) { + dev_dbg(&pdev->dev, "Failed to register Sharp Remote input device\n"); + kfree(sharpsl_rc); + input_free_device(input_dev); + return ret; + } + + if (machine_is_borzoi() || machine_is_spitz() || machine_is_akita()) { + pxa_gpio_mode(SPITZ_GPIO_AK_INT | GPIO_IN); + ret = request_irq(SPITZ_IRQ_GPIO_AK_INT, + sharpsl_rc_interrupt, + IRQF_DISABLED | IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING | IRQF_SHARED, + "sharpsl_rc", + sharpsl_rc); + } else { + pxa_gpio_mode(CORGI_GPIO_AK_INT | GPIO_IN); + ret = request_irq(CORGI_IRQ_GPIO_AK_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"); + + if (machine_is_borzoi() || machine_is_spitz() || machine_is_akita()) + free_irq(SPITZ_IRQ_GPIO_AK_INT, sharpsl_rc); + else + free_irq(CORGI_IRQ_GPIO_AK_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 "); +MODULE_AUTHOR("Richard Purdie "); +MODULE_DESCRIPTION("SharpSL Remote Control Driver"); +MODULE_LICENSE("GPL"); Index: linux-2.6.26/drivers/input/keyboard/spitzkbd.c =================================================================== --- linux-2.6.26.orig/drivers/input/keyboard/spitzkbd.c 2008-07-13 21:51:29.000000000 +0000 +++ linux-2.6.26/drivers/input/keyboard/spitzkbd.c 2008-07-15 10:11:15.000000000 +0000 @@ -19,6 +19,7 @@ #include #include #include +#include #include #include @@ -280,13 +281,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; @@ -300,9 +309,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)); @@ -396,6 +414,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) @@ -433,9 +452,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; @@ -456,6 +478,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.26/arch/arm/mach-pxa/sharpsl.h =================================================================== --- linux-2.6.26.orig/arch/arm/mach-pxa/sharpsl.h 2008-07-13 21:51:29.000000000 +0000 +++ linux-2.6.26/arch/arm/mach-pxa/sharpsl.h 2008-07-15 10:11:15.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.26/arch/arm/mach-pxa/sharpsl_pm.c =================================================================== --- linux-2.6.26.orig/arch/arm/mach-pxa/sharpsl_pm.c 2008-07-13 21:51:29.000000000 +0000 +++ linux-2.6.26/arch/arm/mach-pxa/sharpsl_pm.c 2008-07-15 10:11:15.000000000 +0000 @@ -136,6 +136,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.26/include/asm-arm/hardware/sharpsl_pm.h =================================================================== --- linux-2.6.26.orig/include/asm-arm/hardware/sharpsl_pm.h 2008-07-13 21:51:29.000000000 +0000 +++ linux-2.6.26/include/asm-arm/hardware/sharpsl_pm.h 2008-07-15 10:11:15.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.26/include/linux/input.h =================================================================== --- linux-2.6.26.orig/include/linux/input.h 2008-07-13 21:51:29.000000000 +0000 +++ linux-2.6.26/include/linux/input.h 2008-07-15 10:13:04.000000000 +0000 @@ -640,6 +640,7 @@ #define SW_RFKILL_ALL 0x03 /* rfkill master switch, type "any" set = radio enabled */ #define SW_RADIO SW_RFKILL_ALL /* deprecated */ +#define SW_REMOTE_INSERT 0x04 /* set = remote */ #define SW_MAX 0x0f #define SW_CNT (SW_MAX+1) Index: linux-2.6.26/arch/arm/mach-pxa/spitz_pm.c =================================================================== --- linux-2.6.26.orig/arch/arm/mach-pxa/spitz_pm.c 2008-07-13 21:51:29.000000000 +0000 +++ linux-2.6.26/arch/arm/mach-pxa/spitz_pm.c 2008-07-15 10:11:15.000000000 +0000 @@ -160,6 +160,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; }