summaryrefslogtreecommitdiff
path: root/packages/linux/linux-2.6.27/boc01/012-090115-cy3218-btns.patch
diff options
context:
space:
mode:
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.patch406
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
++