linux-2.6.27: update keypad support for boc01
authorJeremy Lainé <jeremy.laine@m4x.org>
Mon, 23 Feb 2009 18:06:40 +0000 (19:06 +0100)
committerJeremy Lainé <jeremy.laine@m4x.org>
Mon, 23 Feb 2009 18:06:40 +0000 (19:06 +0100)
packages/linux/linux-2.6.27/boc01/012-090115-cy3218-btns.patch [deleted file]
packages/linux/linux-2.6.27/boc01/012-090219-capsense.patch [new file with mode: 0644]
packages/linux/linux_2.6.27.bb

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
deleted file mode 100644 (file)
index 2529be5..0000000
+++ /dev/null
@@ -1,406 +0,0 @@
-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
-+
diff --git a/packages/linux/linux-2.6.27/boc01/012-090219-capsense.patch b/packages/linux/linux-2.6.27/boc01/012-090219-capsense.patch
new file mode 100644 (file)
index 0000000..fed8218
--- /dev/null
@@ -0,0 +1,865 @@
+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 CY8C201xx buttons interface"
++      select INPUT_POLLDEV
++      select LEDS_CLASS
++      help
++        To compile this driver as a module, choose M here:
++        the module will be called capsense-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,4 @@ 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)     += capsense-btns.o
+Index: linux-2.6.27/drivers/input/misc/capsense-btns.c
+===================================================================
+--- /dev/null
++++ linux-2.6.27/drivers/input/misc/capsense-btns.c
+@@ -0,0 +1,438 @@
++/*
++ * 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 DRIVER_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 CAP_CS(command,port,bit)     ((command)+(5*(port))+(bit))
++#define CAP_CS_FINGER_TH(port,bit)   CAP_CS(0x61,port,bit)
++#define CAP_CS_IDAC(port,bit)        CAP_CS(0x6B,port,bit)
++
++#define CAP_DEVICE_ID           0x7A
++#define CAP_DEVICE_STATUS       0x7B
++#define CAP_I2C_ADDR_DM         0x7C
++
++#define CAP_COMMAND_REG         0xA0
++#define CAP_COMMAND_STORE_NVM        0x01
++#define CAP_COMMAND_RESTORE_FACTORY  0x02
++#define CAP_COMMAND_RECONFIGURE      0x06
++#define CAP_COMMAND_NORMAL_MODE      0x07
++#define CAP_COMMAND_SETUP_MODE       0x08
++
++#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   = DRIVER_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 capsense_led {
++      struct led_classdev cdev;
++      struct capsense_ctx *capsense;
++      int port;
++      int bit;
++};
++
++struct capsense_ctx {
++      struct input_polled_dev *ipdev;
++      struct i2c_client client;
++      unsigned char key_state;
++      struct capsense_led leds[CAP_NLEDS];
++      unsigned char led_state[2];
++      struct mutex mutex;
++};
++
++static unsigned short input_keymap[] = {
++      // GP0
++      KEY_F1,
++      KEY_ENTER,
++      KEY_DOWN,
++      KEY_BACKSPACE,
++      // GP1
++      KEY_UP,
++};
++
++struct capsense_keymap {
++      char *name;
++      int port, bit;
++};
++
++static struct capsense_keymap phys_keymap[] = {
++      { "info", 0, 4 },
++      { "ok",   0, 2 },
++      { "down", 0, 3 },
++      { "back", 0, 0 },
++      { "up",   1, 4 },
++};
++
++
++struct capsense_ledmap {
++      char *name;
++      int port, bit;
++};
++
++static struct capsense_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 },
++};
++
++
++/*
++ * Buttons events handling
++ */
++
++static void handle_buttons(struct input_polled_dev *dev)
++{
++      struct capsense_ctx *capsense = dev->private;
++      u8 port_value;
++      u8 new_state = 0;
++      int port;
++      u8 changed;
++      int i;
++
++      mutex_lock(&capsense->mutex);
++
++      // read status
++      port = -1;
++      port_value = 0;
++      for (i = 0; i < ARRAY_SIZE(phys_keymap); i++) {
++              if ( phys_keymap[i].port != port ) {
++                      port = phys_keymap[i].port;
++                      port_value = i2c_smbus_read_byte_data(&capsense->client, CAP_READ_STATUS(port));
++              }
++
++              if ( port_value & (1 << phys_keymap[i].bit) )
++                      new_state |= (1 << i);
++      }
++
++      mutex_unlock(&capsense->mutex);
++
++      // update keyboard state
++      changed = capsense->key_state ^ new_state;
++      for (i = 0; i < ARRAY_SIZE(input_keymap); i++)
++              if (changed & (1 << i))
++                      input_report_key(dev->input, input_keymap[i], (new_state & (1 << i)));
++      capsense->key_state = new_state;
++      input_sync(dev->input);
++}
++
++
++/*
++ * LEDs management
++ */
++
++static void
++capsense_led_set(struct led_classdev *led_cdev,
++               enum led_brightness value)
++{
++      struct capsense_led *led = (struct capsense_led *) led_cdev;
++      struct capsense_ctx *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 capsense_led *led, int state)
++{
++      struct capsense_ctx *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 capsense_ctx *capsense)
++{
++      int i;
++      int ret;
++
++      for (i = 0; i < CAP_NLEDS; i++) {
++              struct capsense_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 capsense_ctx *capsense)
++{
++      int i;
++
++      for (i = 0; i < CAP_NLEDS; i++) {
++              led_classdev_unregister(&capsense->leds[i].cdev);
++      }
++}
++
++
++static inline void
++capsense_led_suspend(struct capsense_ctx *capsense)
++{
++      int i;
++
++      for (i = 0; i < CAP_NLEDS; i++) {
++              struct capsense_led *led = &(capsense->leds[i]);
++
++              led_classdev_suspend(&led->cdev);
++              capsense_led_enable(led, 0);
++      }
++}
++
++
++static inline void
++capsense_led_resume(struct capsense_ctx *capsense)
++{
++      int i;
++
++      for (i = 0; i < CAP_NLEDS; i++) {
++              struct capsense_led *led = &(capsense->leds[i]);
++
++              capsense_led_enable(led, 1);
++              led_classdev_resume(&led->cdev);
++      }
++}
++
++
++/*
++ * Device configuration through procfs entries
++ */
++
++#ifdef CONFIG_PROC_FS
++#include "capsense-procfs.c"
++#endif
++
++
++/*
++ * Device initialisation
++ */
++
++static int
++capsense_probe(struct i2c_adapter *adapter, int addr, int kind)
++{
++      struct capsense_ctx *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, DRIVER_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 = input_keymap;
++      input->keycodemax = ARRAY_SIZE(input_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(input_keymap); i++)
++              set_bit(input_keymap[i], ipdev->input->keybit);
++
++      rc = input_register_polled_device(ipdev);
++      if(rc)
++              goto out_polled;
++
++      if ( capsense_led_init(capsense) )
++              goto out_registered;
++
++#ifdef CONFIG_PROC_FS
++      /* Create /proc entries for hardware device configuration */
++      capsense_proc_init(capsense);
++#endif
++
++      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 capsense_ctx *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;
++}
++
++
++/*
++ * Power management
++ */
++
++#ifdef CONFIG_PM
++static int capsense_suspend(struct i2c_client *client, pm_message_t mesg)
++{
++      struct capsense_ctx *capsense = i2c_get_clientdata(client);
++
++      capsense_led_suspend(capsense);
++
++      return 0;
++}
++
++static int capsense_resume(struct i2c_client *client)
++{
++      struct capsense_ctx *capsense = i2c_get_clientdata(client);
++
++      capsense_led_resume(capsense);
++
++      return 0;
++}
++#endif
++
++
++/*
++ * Driver initialisation
++ */
++
++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 CY8C201xx Input driver");
++MODULE_LICENSE("GPL");
++module_init(capsense_buttons_init);
++module_exit(capsense_buttons_exit);
+Index: linux-2.6.27/drivers/input/misc/capsense-procfs.c
+===================================================================
+--- /dev/null
++++ linux-2.6.27/drivers/input/misc/capsense-procfs.c
+@@ -0,0 +1,390 @@
++/*
++ * CAPSENSE Interface driver
++ * Device setup using procfs
++ *
++ * Copyright (C) 2008, Goobie (www.goobie.fr).
++ *
++ * 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/proc_fs.h>
++#include <linux/uaccess.h>
++
++
++struct capsense_proc_data {
++      struct capsense_ctx *capsense;
++      struct capsense_keymap *phys;
++      char *name;
++      unsigned char command;
++      unsigned char hex;
++};
++
++struct capsense_proc_entry {
++      char *name;
++      unsigned char command;
++      struct capsense_proc_data data[ARRAY_SIZE(phys_keymap)];
++};
++
++static struct capsense_proc_entry capsense_proc_key_entries[] = {
++      { "CS_FINGER_TH", CAP_CS_FINGER_TH(0,0) },
++      { "CS_IDAC",      CAP_CS_IDAC(0,0)      },
++};
++
++static struct capsense_proc_entry capsense_proc_device_entries[] = {
++      { "DEVICE_ID",     CAP_DEVICE_ID        },
++      { "DEVICE_STATUS", CAP_DEVICE_STATUS    },
++};
++
++struct capsense_proc_command {
++      char *name;
++      unsigned char command;
++      struct capsense_ctx *capsense;
++};
++
++static struct capsense_proc_command capsense_proc_commands[] = {
++      { "store",       CAP_COMMAND_STORE_NVM       },
++      //{ "factory",     CAP_COMMAND_RESTORE_FACTORY },
++      //{ "reconfigure", CAP_COMMAND_RECONFIGURE     },
++};
++
++
++static int capsense_proc_read(char *page, char **start, off_t off, int count,
++                            int *eof, void *_data)
++{
++      struct capsense_proc_data *data = _data;
++      struct capsense_ctx *capsense = data->capsense;
++      u8 value;
++      int len;
++
++      mutex_lock(&capsense->mutex);
++      value = i2c_smbus_read_byte_data(&capsense->client, data->command);
++      mutex_unlock(&capsense->mutex);
++
++      if ( data->hex )
++              len = sprintf(page, "%02X\n", value);
++      else
++              len = sprintf(page, "%u\n", value);
++
++      len -= off;
++      if ( len < count ) {
++              *eof = 1;
++              if ( len <= 0 )
++                      return 0;
++      } else {
++              len = count;
++      }
++
++      *start = page + off;
++
++      return len;
++}
++
++
++static int capsense_proc_write(struct file *file, const char *buf,
++                             unsigned long count, void *_data)
++{
++      struct capsense_proc_data *data = _data;
++      struct capsense_ctx *capsense = data->capsense;
++      char lbuf[count+1];
++      u8 value;
++
++      /* Only root can do this */
++      if ( !capable(CAP_SYS_ADMIN) )
++              return -EACCES;
++
++      memset(lbuf, 0, sizeof(lbuf));
++
++      if (copy_from_user(lbuf, buf, count))
++              return -EFAULT;
++
++      if ( sscanf(lbuf, "%hhi", &value) == 1 ) {
++              mutex_lock(&capsense->mutex);
++              i2c_smbus_write_byte_data(&capsense->client, CAP_COMMAND_REG, CAP_COMMAND_SETUP_MODE);
++              i2c_smbus_write_byte_data(&capsense->client, data->command, value);
++              i2c_smbus_write_byte_data(&capsense->client, CAP_COMMAND_REG, CAP_COMMAND_NORMAL_MODE);
++              mutex_unlock(&capsense->mutex);
++      }
++      else {
++              printk(KERN_INFO DRIVER_NAME ": [%s/%s] Syntax error in expression\n", data->phys->name, data->name);
++              return -EINVAL;
++      }
++
++      return count;
++}
++
++
++static inline char *str_skip_blanks(char *s)
++{
++      while ( (*s != '\0') && (*s <= ' ') )
++              s++;
++      return s;
++}
++
++
++static inline char *str_skip_chars(char *s)
++{
++      while ( *s > ' ' )
++              s++;
++      return s;
++}
++
++
++static int capsense_proc_write_iic(struct file *file, const char *buf,
++                                 unsigned long count, void *data)
++{
++      struct capsense_ctx *capsense = data;
++      char lbuf[count+1];
++      int lnum;
++      char *s;
++
++      /* Only root can do this */
++      if ( !capable(CAP_SYS_ADMIN) )
++              return -EACCES;
++
++      printk(KERN_INFO DRIVER_NAME ": Reading configuration script from /proc/" DRIVER_NAME "/iic (%lu bytes)\n", count);
++
++      memset(lbuf, 0, sizeof(lbuf));
++
++      if (copy_from_user(lbuf, buf, count))
++              return -EFAULT;
++
++      s = lbuf;
++      lnum = 0;
++
++      while ( *s != '\0' ) {
++              char *line;
++              char operation;
++              unsigned char data[255];
++              int size;
++              struct i2c_msg msg;
++              int ret;
++
++              lnum++;
++
++              /* Spot the end of the line */
++              line = s;
++              while ( (*s != '\0') && (*s != '\n') )
++                      s++;
++              if ( *s != '\0' )
++                      *(s++) = '\0';
++
++              //printk(KERN_INFO DRIVER_NAME ": iic:%d: '%s'\n", lnum, line);
++
++              /* Strip leading blank characters */
++              line = str_skip_blanks(line);
++
++              /* Empty or commented line: skip! */
++              if ( (*line == '\0') || (*line == '#') )
++                      continue;
++
++              /* Only accept write operations 'w' */
++              operation = *(line++);
++              if ( operation != 'w' ) {
++                      printk(KERN_ERR DRIVER_NAME ": iic:%d: Unknown operation '%c ...' -- skipped\n", lnum, operation);
++                      continue;
++              }
++
++              /* Extract data values */
++              size = 0;
++              while ( (*line != '\0') && (size < sizeof(data)) ) {
++                      line = str_skip_blanks(line);
++                      if ( *line != '\0' )
++                              sscanf(line, "%hhx", &data[size++]);
++                      line = str_skip_chars(line);
++              }
++
++              {
++                      int i;
++
++                      printk(KERN_DEBUG DRIVER_NAME ": iic:%d: %c", lnum, operation);
++                      for (i = 0; i < size; i++)
++                              printk(" %02X", data[i]);
++                      printk("\n");
++              }
++
++              /* Do nothing if there are less than 2 data bytes (address, command) */
++              if ( size < 3 ) {
++                      printk(KERN_ERR DRIVER_NAME ": iic:%d: Too few data for operation '%c ...' -- skipped\n", lnum, operation);
++                      continue;
++              }
++
++              /* First data byte is the i2c device address:
++                 Warn if it does not match the standard i2c address */
++              if ( data[0] != CAPSENSE_I2C_ADDR ) {
++                      printk(KERN_WARNING DRIVER_NAME ": iic:%d: WARNING - Specified i2c address (%02X) differs from standard i2c address (%02X)\n", lnum, data[0], CAPSENSE_I2C_ADDR);
++              }
++
++              /* Second data byte is the capsense register:
++                 Warn if changing the device I2C address */
++              if ( data[1] == CAP_I2C_ADDR_DM ) {
++                      printk(KERN_WARNING DRIVER_NAME ": iic:%d: WARNING - Changing i2c address to %02X (I2C_ADDR_DM=%02X)\n", lnum, data[2] & 0x7F, data[2]);
++              }
++
++              /* Send command to i2c device */
++              mutex_lock(&capsense->mutex);
++
++              msg.addr = data[0];
++              msg.flags = capsense->client.flags;
++              msg.len = size - 1;
++              msg.buf = data + 1;
++              //printk(KERN_INFO DRIVER_NAME ": iic:%d: i2c transfer: addr=0x%02X flags=0x%04X len=%d\n", lnum, msg.addr, msg.flags, msg.len);
++
++              ret = i2c_transfer(capsense->client.adapter, &msg, 1);
++              if ( ret < 0 )
++                      printk(KERN_ERR DRIVER_NAME ": iic:%d: i2c transfer failed (%d), command rejected\n", lnum, ret);
++
++              mutex_unlock(&capsense->mutex);
++      }
++
++      return count;
++}
++
++
++static int capsense_proc_write_command(struct file *file, const char *buf,
++                                     unsigned long count, void *data)
++{
++      struct capsense_proc_command *command = data;
++      struct capsense_ctx *capsense = command->capsense;
++
++      /* Only root can do this */
++      if ( !capable(CAP_SYS_ADMIN) )
++              return -EACCES;
++
++      printk(KERN_INFO DRIVER_NAME ": %s (%02X)\n", command->name, command->command);
++
++      mutex_lock(&capsense->mutex);
++      i2c_smbus_write_byte_data(&capsense->client, CAP_COMMAND_REG, CAP_COMMAND_SETUP_MODE);
++      i2c_smbus_write_byte_data(&capsense->client, CAP_COMMAND_REG, command->command);
++      i2c_smbus_write_byte_data(&capsense->client, CAP_COMMAND_REG, CAP_COMMAND_NORMAL_MODE);
++      mutex_unlock(&capsense->mutex);
++
++      return count;
++}
++
++
++static int capsense_proc_init(struct capsense_ctx *capsense)
++{
++      struct proc_dir_entry *root;
++      struct proc_dir_entry *dir;
++      struct proc_dir_entry *ent;
++      int i;
++
++      /* Create capsense proc directory */
++      printk(KERN_INFO DRIVER_NAME ": Creating setup entries in /proc/" DRIVER_NAME "/\n");
++
++      root = proc_mkdir(DRIVER_NAME, NULL);
++      if ( root == NULL ) {
++              printk(KERN_WARNING DRIVER_NAME ": Cannot create directory /proc/" DRIVER_NAME "\n");
++              return -1;
++      }
++
++      root->owner = THIS_MODULE;
++
++      /* Create iic config file dump entry */
++      ent = create_proc_entry("iic", S_IFREG|S_IWUSR, root);
++      if ( ent == NULL ) {
++              printk(KERN_WARNING DRIVER_NAME ": Cannot create entry /proc/" DRIVER_NAME "/iic\n");
++              return -1;
++      }
++
++      ent->owner = THIS_MODULE;
++      ent->data = capsense;
++      ent->write_proc = capsense_proc_write_iic;
++
++      /* Create commands directory */
++      dir = proc_mkdir("commands", root);
++      if ( dir == NULL ) {
++              printk(KERN_WARNING DRIVER_NAME ": Cannot create directory /proc/" DRIVER_NAME "/commands\n");
++              return -1;
++      }
++
++      dir->owner = THIS_MODULE;
++
++      /* Create command entries */
++      for (i = 0; i < ARRAY_SIZE(capsense_proc_commands); i++) {
++              struct capsense_proc_command *command = &capsense_proc_commands[i];
++
++              command->capsense = capsense;
++
++              ent = create_proc_entry(command->name, S_IFREG|S_IWUSR, dir);
++              if ( ent == NULL ) {
++                      printk(KERN_WARNING DRIVER_NAME ": Cannot create entry /proc/" DRIVER_NAME "/commands/%s\n", command->name);
++                      return -1;
++              }
++
++              ent->owner = THIS_MODULE;
++              ent->data = command;
++              ent->write_proc = capsense_proc_write_command;
++      }
++
++      /* Create device status read entries */
++      for (i = 0; i < ARRAY_SIZE(capsense_proc_device_entries); i++) {
++              struct capsense_proc_entry *entry = &(capsense_proc_device_entries[i]);
++              struct capsense_proc_data *data = &(entry->data[i]);
++
++              data->capsense = capsense;
++              data->phys = NULL;
++              data->name = entry->name;
++              data->command = entry->command;
++              data->hex = 1;
++
++              ent = create_proc_entry(entry->name, S_IFREG|S_IRUSR, root);
++              if ( ent == NULL ) {
++                      printk(KERN_WARNING DRIVER_NAME ": Cannot create entry /proc/" DRIVER_NAME "/%s\n", entry->name);
++                      continue;
++              }
++
++              ent->owner = THIS_MODULE;
++              ent->data = data;
++              ent->read_proc = capsense_proc_read;
++      }
++
++      /* Create keys management directory */
++      dir = proc_mkdir("keys", root);
++      if ( dir == NULL ) {
++              printk(KERN_WARNING DRIVER_NAME ": Cannot create directory /proc/" DRIVER_NAME "/keys\n");
++              return -1;
++      }
++
++      dir->owner = THIS_MODULE;
++
++      /* Create keys sensitivity adjustment entries */
++      for (i = 0; i < ARRAY_SIZE(phys_keymap); i++) {
++              struct capsense_keymap *phys = &phys_keymap[i];
++              struct proc_dir_entry *subdir;
++              int ientry;
++
++              subdir = proc_mkdir(phys->name, dir);
++              if ( subdir == NULL ) {
++                      printk(KERN_WARNING DRIVER_NAME ": Cannot create directory /proc/" DRIVER_NAME "/keys/%s\n", phys->name);
++                      continue;
++              }
++
++              for (ientry = 0; ientry < ARRAY_SIZE(capsense_proc_key_entries); ientry++) {
++                      struct capsense_proc_entry *entry = &(capsense_proc_key_entries[ientry]);
++                      struct capsense_proc_data *data = &(entry->data[i]);
++
++                      data->capsense = capsense;
++                      data->phys = phys;
++                      data->name = entry->name;
++                      data->command = CAP_CS(entry->command, phys->port, phys->bit);
++                      data->hex = 0;
++
++                      ent = create_proc_entry(entry->name, S_IFREG|S_IRUSR|S_IWUSR, subdir);
++                      if ( ent == NULL ) {
++                              printk(KERN_WARNING DRIVER_NAME ": Cannot create entry /proc/" DRIVER_NAME "/keys/%s/%s\n", phys->name, entry->name);
++                              continue;
++                      }
++
++                      ent->owner = THIS_MODULE;
++                      ent->data = data;
++                      ent->read_proc = capsense_proc_read;
++                      ent->write_proc = capsense_proc_write;
++              }
++      }
++
++      return 0;
++}
index 4b094bf..0a2055e 100644 (file)
@@ -1,6 +1,6 @@
 require linux.inc
 
-PR = "r4"
+PR = "r5"
 
 # Mark archs/machines that this kernel supports
 DEFAULT_PREFERENCE = "-1"
@@ -20,7 +20,7 @@ SRC_URI_append_boc01 = "\
        file://008-081208-spi.patch;patch=1 \
        file://010-090112-mii.patch;patch=1 \
        file://011-090115-gpio.patch;patch=1 \
-       file://012-090115-cy3218-btns.patch;patch=1 \
+       file://012-090219-capsense.patch;patch=1 \
        file://013-090209-lcd.patch;patch=1 \
        file://014-090209-pm-wakeup.patch;patch=1 \
        "