diff options
Diffstat (limited to 'packages/linux/linux-2.6.27/boc01/012-090115-cy3218-btns.patch')
-rw-r--r-- | packages/linux/linux-2.6.27/boc01/012-090115-cy3218-btns.patch | 406 |
1 files changed, 406 insertions, 0 deletions
diff --git a/packages/linux/linux-2.6.27/boc01/012-090115-cy3218-btns.patch b/packages/linux/linux-2.6.27/boc01/012-090115-cy3218-btns.patch new file mode 100644 index 0000000000..2529be5274 --- /dev/null +++ b/packages/linux/linux-2.6.27/boc01/012-090115-cy3218-btns.patch @@ -0,0 +1,406 @@ +Index: linux-2.6.27/drivers/input/misc/cy3218-btns.c +=================================================================== +--- /dev/null ++++ linux-2.6.27/drivers/input/misc/cy3218-btns.c +@@ -0,0 +1,373 @@ ++/* ++ * CAPSENSE Interface driver ++ * ++ * ++ * Copyright (C) 2008, CenoSYS (www.cenosys.com). ++ * ++ * Guillaume Ligneul <guillaume.ligneul@gmail.com> ++ * Jeremy Lainé <jeremy.laine@bolloretelecom.eu> ++ * Sylvain Giroudon <sylvain.giroudon@goobie.fr> ++ * ++ * This software program is licensed subject to the GNU General Public License ++ * (GPL).Version 2,June 1991, available at http://www.fsf.org/copyleft/gpl.html ++ */ ++ ++#include <linux/init.h> ++#include <linux/input-polldev.h> ++#include <linux/ioport.h> ++#include <linux/module.h> ++#include <linux/i2c.h> ++#include <linux/leds.h> ++ ++static int capsense_attach_adapter(struct i2c_adapter *adapter); ++static int capsense_detach_client(struct i2c_client *client); ++#ifdef CONFIG_PM ++static int capsense_suspend(struct i2c_client *client, pm_message_t mesg); ++static int capsense_resume(struct i2c_client *client); ++#endif ++ ++#define CAPSENSE_NAME "Capsense" ++ ++/* i2c configuration */ ++#define CAPSENSE_I2C_ADDR 0x25 ++// To debug (may be add in include/linux/i2c-id.h) ++#define I2C_DRIVERID_CAPSENSE 98 ++ ++#define BUTTONS_POLL_INTERVAL 30 /* msec */ ++ ++#define CAP_OUTPUT_PORT(port) (0x04+(port)) ++#define CAP_OP_SEL(port,bit) (0x1C+(25*(port))+(5*(bit))) ++#define CAP_READ_STATUS(port) (0x88+(port)) ++ ++#define MASK0 0x10 ++#define MASK1 0x4 ++#define MASK2 0x8 ++#define MASK3 0x1 ++ ++#define CAP_NLEDS 5 ++ ++static int poll_interval = BUTTONS_POLL_INTERVAL; ++module_param_named(poll, poll_interval, uint, 0); ++MODULE_PARM_DESC(poll, "poll interval in msec (30=default)"); ++ ++static const unsigned short normal_i2c[] = { ++ CAPSENSE_I2C_ADDR , I2C_CLIENT_END ++}; ++I2C_CLIENT_INSMOD; ++ ++static struct i2c_driver capsense_driver = { ++ .driver = { ++ .name = CAPSENSE_NAME, ++ }, ++ .id = I2C_DRIVERID_CAPSENSE, ++ .attach_adapter = &capsense_attach_adapter, ++ .detach_client = &capsense_detach_client, ++#ifdef CONFIG_PM ++ .suspend = &capsense_suspend, ++ .resume = &capsense_resume, ++#endif ++}; ++ ++struct cy3218_led { ++ struct led_classdev cdev; ++ struct cy3218 *capsense; ++ int port; ++ int bit; ++}; ++ ++struct cy3218 { ++ struct input_polled_dev *ipdev; ++ struct i2c_client client; ++ unsigned char key_state; ++ struct cy3218_led leds[CAP_NLEDS]; ++ unsigned char led_state[2]; ++ struct mutex mutex; ++}; ++ ++static unsigned short keymap[] = { ++ // GP0 ++ KEY_F1, ++ KEY_ENTER, ++ KEY_DOWN, ++ KEY_BACKSPACE, ++ // GP1 ++ KEY_UP, ++}; ++ ++struct cy3218_ledmap { ++ char *name; ++ int port, bit; ++}; ++ ++static struct cy3218_ledmap ledmap[CAP_NLEDS] = { ++ { "capsense:blue:back", 0, 1 }, ++ { "capsense:blue:info", 1, 0 }, ++ { "capsense:blue:down", 1, 1 }, ++ { "capsense:blue:ok", 1, 2 }, ++ { "capsense:blue:up", 1, 3 }, ++}; ++ ++static void handle_buttons(struct input_polled_dev *dev) ++{ ++ struct cy3218 *capsense = dev->private; ++ u8 port_value; ++ u8 new_state = 0; ++ u8 changed; ++ int i; ++ ++ mutex_lock(&capsense->mutex); ++ ++ // read status ++ port_value = i2c_smbus_read_byte_data(&capsense->client, CAP_READ_STATUS(0)); ++ if (port_value & MASK0) new_state |= 0x01; ++ if (port_value & MASK1) new_state |= 0x02; ++ if (port_value & MASK2) new_state |= 0x04; ++ if (port_value & MASK3) new_state |= 0x08; ++ ++ port_value = i2c_smbus_read_byte_data(&capsense->client, CAP_READ_STATUS(1)); ++ if (port_value & MASK0) new_state |= 0x10; ++ ++ mutex_unlock(&capsense->mutex); ++ ++ // update keyboard state ++ changed = capsense->key_state ^ new_state; ++ for (i = 0; i < ARRAY_SIZE(keymap); i++) ++ if (changed & (1 << i)) ++ input_report_key(dev->input, keymap[i], (new_state & (1 << i))); ++ capsense->key_state = new_state; ++ input_sync(dev->input); ++} ++ ++ ++static void ++capsense_led_set(struct led_classdev *led_cdev, ++ enum led_brightness value) ++{ ++ struct cy3218_led *led = (struct cy3218_led *) led_cdev; ++ struct cy3218 *capsense = led->capsense; ++ int port = led->port; ++ unsigned char mask = (1 << led->bit); ++ ++ if ( value ) ++ capsense->led_state[port] |= mask; ++ else ++ capsense->led_state[port] &= ~mask; ++ ++ mutex_lock(&capsense->mutex); ++ i2c_smbus_write_byte_data(&capsense->client, CAP_OUTPUT_PORT(port), capsense->led_state[port]); ++ mutex_unlock(&capsense->mutex); ++} ++ ++static void ++capsense_led_enable(struct cy3218_led *led, int state) ++{ ++ struct cy3218 *capsense = led->capsense; ++ ++ mutex_lock(&capsense->mutex); ++ i2c_smbus_write_byte_data(&capsense->client, CAP_OP_SEL(led->port, led->bit), state ? 0x00 : 0x80); ++ mutex_unlock(&capsense->mutex); ++} ++ ++static int ++capsense_led_init(struct cy3218 *capsense) ++{ ++ int i; ++ int ret; ++ ++ for (i = 0; i < CAP_NLEDS; i++) { ++ struct cy3218_led *led = &(capsense->leds[i]); ++ ++ led->cdev.name = ledmap[i].name; ++ led->cdev.brightness_set = capsense_led_set; ++ led->capsense = capsense; ++ led->port = ledmap[i].port; ++ led->bit = ledmap[i].bit; ++ ++ ret = led_classdev_register(&capsense->ipdev->input->dev, &led->cdev); ++ if ( ret < 0 ) ++ return -1; ++ ++ capsense_led_enable(led, 1); ++ } ++ ++ /* Switch all leds off */ ++ mutex_lock(&capsense->mutex); ++ ++ capsense->led_state[0] = 0x00; ++ i2c_smbus_write_byte_data(&capsense->client, CAP_OUTPUT_PORT(0), 0x00); ++ ++ capsense->led_state[1] = 0x00; ++ i2c_smbus_write_byte_data(&capsense->client, CAP_OUTPUT_PORT(1), 0x00); ++ ++ mutex_unlock(&capsense->mutex); ++ ++ return 0; ++} ++ ++ ++static void ++capsense_led_exit(struct cy3218 *capsense) ++{ ++ int i; ++ ++ for (i = 0; i < CAP_NLEDS; i++) { ++ led_classdev_unregister(&capsense->leds[i].cdev); ++ } ++} ++ ++ ++static inline void ++capsense_led_suspend(struct cy3218 *capsense) ++{ ++ int i; ++ ++ for (i = 0; i < CAP_NLEDS; i++) { ++ struct cy3218_led *led = &(capsense->leds[i]); ++ ++ led_classdev_suspend(&led->cdev); ++ capsense_led_enable(led, 0); ++ } ++} ++ ++ ++static inline void ++capsense_led_resume(struct cy3218 *capsense) ++{ ++ int i; ++ ++ for (i = 0; i < CAP_NLEDS; i++) { ++ struct cy3218_led *led = &(capsense->leds[i]); ++ ++ capsense_led_enable(led, 1); ++ led_classdev_resume(&led->cdev); ++ } ++} ++ ++ ++static int ++capsense_probe(struct i2c_adapter *adapter, int addr, int kind) ++{ ++ struct cy3218 *capsense; ++ struct input_polled_dev *ipdev; ++ struct input_dev *input; ++ int rc = 0, err = -ENOMEM, i=0; ++ ++ capsense = kzalloc(sizeof(*capsense), GFP_KERNEL); ++ if (!capsense) ++ goto failout; ++ ++ mutex_init(&capsense->mutex); ++ ++ if (!i2c_check_functionality(adapter, I2C_FUNC_I2C)) { ++ goto failout; ++ } ++ ++ ipdev = input_allocate_polled_device(); ++ if (!ipdev) ++ goto failout; ++ ++ capsense->key_state = 0; ++ capsense->ipdev = ipdev; ++ capsense->client.adapter = adapter; ++ capsense->client.addr = addr; ++ capsense->client.driver = &capsense_driver; ++ strlcpy(capsense->client.name, CAPSENSE_NAME, I2C_NAME_SIZE); ++ i2c_set_clientdata(&capsense->client, capsense); ++ ++ rc = i2c_attach_client(&capsense->client); ++ if (rc) ++ goto out_attach; ++ ++ ipdev->poll = handle_buttons; ++ ipdev->private = capsense; ++ ipdev->poll_interval = poll_interval; ++ ++ input = ipdev->input; ++ input->name = "Capsense buttons"; ++ input->phys = "capsense/input0"; ++ input->id.bustype = BUS_I2C; ++ input->dev.parent = &capsense->client.dev; ++ ++ input->keycode = keymap; ++ input->keycodemax = ARRAY_SIZE(keymap); ++ input->keycodesize = sizeof(unsigned short); ++ ++ input_set_capability(input, EV_MSC, MSC_SCAN); ++ set_bit(EV_KEY, ipdev->input->evbit); ++ ++ for (i = 0; i < ARRAY_SIZE(keymap); i++) ++ set_bit(keymap[i], ipdev->input->keybit); ++ ++ rc = input_register_polled_device(ipdev); ++ if(rc) ++ goto out_polled; ++ ++ if ( capsense_led_init(capsense) ) ++ goto out_registered; ++ ++ return 0; ++ ++out_registered: ++ input_unregister_polled_device(ipdev); ++out_polled: ++ i2c_detach_client(&capsense->client); ++out_attach: ++ input_free_polled_device(ipdev); ++failout: ++ return err; ++} ++ ++static int ++capsense_attach_adapter (struct i2c_adapter *adapter) ++{ ++ return i2c_probe(adapter, &addr_data, capsense_probe); ++} ++ ++static int ++capsense_detach_client(struct i2c_client *client) ++{ ++ struct cy3218 *capsense = i2c_get_clientdata(client); ++ ++ capsense_led_exit(capsense); ++ input_unregister_polled_device(capsense->ipdev); ++ i2c_detach_client(&capsense->client); ++ input_free_polled_device(capsense->ipdev); ++ return 0; ++} ++ ++#ifdef CONFIG_PM ++static int capsense_suspend(struct i2c_client *client, pm_message_t mesg) ++{ ++ struct cy3218 *capsense = i2c_get_clientdata(client); ++ ++ capsense_led_suspend(capsense); ++ ++ return 0; ++} ++ ++static int capsense_resume(struct i2c_client *client) ++{ ++ struct cy3218 *capsense = i2c_get_clientdata(client); ++ ++ capsense_led_resume(capsense); ++ ++ return 0; ++} ++#endif ++ ++ ++static int __init capsense_buttons_init(void) ++{ ++ return i2c_add_driver(&capsense_driver); ++} ++ ++static void __exit capsense_buttons_exit(void) ++{ ++ i2c_del_driver(&capsense_driver); ++} ++ ++MODULE_AUTHOR("Guillaume Ligneul <guillaume.ligneul@cenosys.com>"); ++MODULE_DESCRIPTION("Capsense CY3218 Input driver"); ++MODULE_LICENSE("GPL"); ++module_init(capsense_buttons_init); ++module_exit(capsense_buttons_exit); +Index: linux-2.6.27/drivers/input/misc/Kconfig +=================================================================== +--- linux-2.6.27.orig/drivers/input/misc/Kconfig ++++ linux-2.6.27/drivers/input/misc/Kconfig +@@ -207,4 +207,13 @@ config HP_SDC_RTC + Say Y here if you want to support the built-in real time clock + of the HP SDC controller. + ++config INPUT_CAPSENSE_BTNS ++ tristate "CAPSENSE CY3218 button interface" ++ select INPUT_POLLDEV ++ select LEDS_CLASS ++ help ++ To compile this driver as a module, choose M here: the ++ module will be called cy3218-btns. ++ To change poll interval, invoque poll parameter in msecs. ++ + endif +Index: linux-2.6.27/drivers/input/misc/Makefile +=================================================================== +--- linux-2.6.27.orig/drivers/input/misc/Makefile ++++ linux-2.6.27/drivers/input/misc/Makefile +@@ -20,3 +20,5 @@ obj-$(CONFIG_HP_SDC_RTC) += hp_sdc_rtc. + obj-$(CONFIG_INPUT_UINPUT) += uinput.o + obj-$(CONFIG_INPUT_APANEL) += apanel.o + obj-$(CONFIG_INPUT_SGI_BTNS) += sgi_btns.o ++obj-$(CONFIG_INPUT_CAPSENSE_BTNS) += cy3218-btns.o ++ |