--- /dev/null
+aufs2 base patch for linux-2.6.27
+
+diff --git a/fs/namei.c b/fs/namei.c
+index 4ea63ed..2759ad4 100644
+--- a/fs/namei.c
++++ b/fs/namei.c
+@@ -1241,7 +1241,7 @@ out:
+ * needs parent already locked. Doesn't follow mounts.
+ * SMP-safe.
+ */
+-static struct dentry *lookup_hash(struct nameidata *nd)
++struct dentry *lookup_hash(struct nameidata *nd)
+ {
+ int err;
+
+@@ -1251,7 +1251,7 @@ static struct dentry *lookup_hash(struct nameidata *nd)
+ return __lookup_hash(&nd->last, nd->path.dentry, nd);
+ }
+
+-static int __lookup_one_len(const char *name, struct qstr *this,
++int __lookup_one_len(const char *name, struct qstr *this,
+ struct dentry *base, int len)
+ {
+ unsigned long hash;
+diff --git a/fs/splice.c b/fs/splice.c
+index a1e701c..409245a 100644
+--- a/fs/splice.c
++++ b/fs/splice.c
+@@ -887,8 +887,8 @@ EXPORT_SYMBOL(generic_splice_sendpage);
+ /*
+ * Attempt to initiate a splice from pipe to file.
+ */
+-static long do_splice_from(struct pipe_inode_info *pipe, struct file *out,
+- loff_t *ppos, size_t len, unsigned int flags)
++long do_splice_from(struct pipe_inode_info *pipe, struct file *out,
++ loff_t *ppos, size_t len, unsigned int flags)
+ {
+ int ret;
+
+@@ -911,9 +911,9 @@ static long do_splice_from(struct pipe_inode_info *pipe, struct file *out,
+ /*
+ * Attempt to initiate a splice from a file to a pipe.
+ */
+-static long do_splice_to(struct file *in, loff_t *ppos,
+- struct pipe_inode_info *pipe, size_t len,
+- unsigned int flags)
++long do_splice_to(struct file *in, loff_t *ppos,
++ struct pipe_inode_info *pipe, size_t len,
++ unsigned int flags)
+ {
+ int ret;
+
+diff --git a/include/linux/namei.h b/include/linux/namei.h
+index 68f8c32..5522432 100644
+--- a/include/linux/namei.h
++++ b/include/linux/namei.h
+@@ -71,6 +71,9 @@ extern struct file *lookup_instantiate_filp(struct nameidata *nd, struct dentry
+ extern struct file *nameidata_to_filp(struct nameidata *nd, int flags);
+ extern void release_open_intent(struct nameidata *);
+
++extern struct dentry *lookup_hash(struct nameidata *nd);
++extern int __lookup_one_len(const char *name, struct qstr *this,
++ struct dentry *base, int len);
+ extern struct dentry *lookup_one_len(const char *, struct dentry *, int);
+ extern struct dentry *lookup_one_noperm(const char *, struct dentry *);
+
+diff --git a/include/linux/splice.h b/include/linux/splice.h
+index 528dcb9..5123bc6 100644
+--- a/include/linux/splice.h
++++ b/include/linux/splice.h
+@@ -71,4 +71,10 @@ extern ssize_t splice_to_pipe(struct pipe_inode_info *,
+ extern ssize_t splice_direct_to_actor(struct file *, struct splice_desc *,
+ splice_direct_actor *);
+
++extern long do_splice_from(struct pipe_inode_info *pipe, struct file *out,
++ loff_t *ppos, size_t len, unsigned int flags);
++extern long do_splice_to(struct file *in, loff_t *ppos,
++ struct pipe_inode_info *pipe, size_t len,
++ unsigned int flags);
++
+ #endif
--- /dev/null
+aufs2 standalone patch for linux-2.6.27
+
+diff --git a/fs/namei.c b/fs/namei.c
+index 2759ad4..b207821 100644
+--- a/fs/namei.c
++++ b/fs/namei.c
+@@ -354,6 +354,7 @@ int deny_write_access(struct file * file)
+
+ return 0;
+ }
++EXPORT_SYMBOL(deny_write_access);
+
+ /**
+ * path_get - get a reference to a path
+@@ -1250,6 +1251,7 @@ struct dentry *lookup_hash(struct nameidata *nd)
+ return ERR_PTR(err);
+ return __lookup_hash(&nd->last, nd->path.dentry, nd);
+ }
++EXPORT_SYMBOL(lookup_hash);
+
+ int __lookup_one_len(const char *name, struct qstr *this,
+ struct dentry *base, int len)
+@@ -1272,6 +1274,7 @@ int __lookup_one_len(const char *name, struct qstr *this,
+ this->hash = end_name_hash(hash);
+ return 0;
+ }
++EXPORT_SYMBOL(__lookup_one_len);
+
+ /**
+ * lookup_one_len - filesystem helper to lookup single pathname component
+diff --git a/fs/namespace.c b/fs/namespace.c
+index 6e283c9..36030e8 100644
+--- a/fs/namespace.c
++++ b/fs/namespace.c
+@@ -37,6 +37,7 @@
+
+ /* spinlock for vfsmount related operations, inplace of dcache_lock */
+ __cacheline_aligned_in_smp DEFINE_SPINLOCK(vfsmount_lock);
++EXPORT_SYMBOL(vfsmount_lock);
+
+ static int event;
+ static DEFINE_IDA(mnt_id_ida);
+diff --git a/fs/open.c b/fs/open.c
+index 07da935..b8e9726 100644
+--- a/fs/open.c
++++ b/fs/open.c
+@@ -222,6 +222,7 @@ int do_truncate(struct dentry *dentry, loff_t length, unsigned int time_attrs,
+ mutex_unlock(&dentry->d_inode->i_mutex);
+ return err;
+ }
++EXPORT_SYMBOL(do_truncate);
+
+ static long do_sys_truncate(const char __user *pathname, loff_t length)
+ {
+diff --git a/fs/splice.c b/fs/splice.c
+index 409245a..48d7e72 100644
+--- a/fs/splice.c
++++ b/fs/splice.c
+@@ -907,6 +907,7 @@ long do_splice_from(struct pipe_inode_info *pipe, struct file *out,
+
+ return out->f_op->splice_write(pipe, out, ppos, len, flags);
+ }
++EXPORT_SYMBOL(do_splice_from);
+
+ /*
+ * Attempt to initiate a splice from a file to a pipe.
+@@ -929,6 +930,7 @@ long do_splice_to(struct file *in, loff_t *ppos,
+
+ return in->f_op->splice_read(in, ppos, pipe, len, flags);
+ }
++EXPORT_SYMBOL(do_splice_to);
+
+ /**
+ * splice_direct_to_actor - splices data directly between two non-pipes
+diff --git a/fs/super.c b/fs/super.c
+index e931ae9..141853c 100644
+--- a/fs/super.c
++++ b/fs/super.c
+@@ -270,6 +270,7 @@ int fsync_super(struct super_block *sb)
+ __fsync_super(sb);
+ return sync_blockdev(sb->s_bdev);
+ }
++EXPORT_SYMBOL(fsync_super);
+
+ /**
+ * generic_shutdown_super - common helper for ->kill_sb()
+diff --git a/fs/sync.c b/fs/sync.c
+index 2967562..34040d6 100644
+--- a/fs/sync.c
++++ b/fs/sync.c
+@@ -104,6 +104,7 @@ long do_fsync(struct file *file, int datasync)
+ out:
+ return ret;
+ }
++EXPORT_SYMBOL(do_fsync);
+
+ static long __do_fsync(unsigned int fd, int datasync)
+ {
+diff --git a/security/device_cgroup.c b/security/device_cgroup.c
+index 46f2397..00ec611 100644
+--- a/security/device_cgroup.c
++++ b/security/device_cgroup.c
+@@ -537,6 +537,7 @@ acc_check:
+
+ return -EPERM;
+ }
++EXPORT_SYMBOL(devcgroup_inode_permission);
+
+ int devcgroup_inode_mknod(int mode, dev_t dev)
+ {
+diff --git a/security/security.c b/security/security.c
+index 3a4b4f5..79be80b 100644
+--- a/security/security.c
++++ b/security/security.c
+@@ -425,6 +425,7 @@ int security_inode_readlink(struct dentry *dentry)
+ return 0;
+ return security_ops->inode_readlink(dentry);
+ }
++EXPORT_SYMBOL(security_inode_readlink);
+
+ int security_inode_follow_link(struct dentry *dentry, struct nameidata *nd)
+ {
+@@ -439,6 +440,7 @@ int security_inode_permission(struct inode *inode, int mask)
+ return 0;
+ return security_ops->inode_permission(inode, mask);
+ }
++EXPORT_SYMBOL(security_inode_permission);
+
+ int security_inode_setattr(struct dentry *dentry, struct iattr *attr)
+ {
+@@ -539,6 +541,7 @@ int security_file_permission(struct file *file, int mask)
+ {
+ return security_ops->file_permission(file, mask);
+ }
++EXPORT_SYMBOL(security_file_permission);
+
+ int security_file_alloc(struct file *file)
+ {
#
# Automatically generated make config: don't edit
# Linux kernel version: 2.6.27-omap1
-# Sun Aug 30 18:56:27 2009
+# Fri Nov 13 10:18:13 2009
#
CONFIG_ARM=y
CONFIG_SYS_SUPPORTS_APM_EMULATION=y
# CONFIG_PREEMPT is not set
CONFIG_HZ=128
CONFIG_AEABI=y
-# CONFIG_OABI_COMPAT is not set
+CONFIG_OABI_COMPAT=y
CONFIG_ARCH_FLATMEM_HAS_HOLES=y
# CONFIG_ARCH_DISCONTIGMEM_ENABLE is not set
CONFIG_SELECT_MEMORY_MODEL=y
#
# CPU Frequency scaling
#
-CONFIG_CPU_FREQ=y
-CONFIG_CPU_FREQ_TABLE=y
-# CONFIG_CPU_FREQ_DEBUG is not set
-CONFIG_CPU_FREQ_STAT=y
-# CONFIG_CPU_FREQ_STAT_DETAILS is not set
-CONFIG_CPU_FREQ_DEFAULT_GOV_PERFORMANCE=y
-# CONFIG_CPU_FREQ_DEFAULT_GOV_POWERSAVE is not set
-# CONFIG_CPU_FREQ_DEFAULT_GOV_USERSPACE is not set
-# CONFIG_CPU_FREQ_DEFAULT_GOV_ONDEMAND is not set
-# CONFIG_CPU_FREQ_DEFAULT_GOV_CONSERVATIVE is not set
-CONFIG_CPU_FREQ_GOV_PERFORMANCE=y
-CONFIG_CPU_FREQ_GOV_POWERSAVE=m
-CONFIG_CPU_FREQ_GOV_USERSPACE=m
-CONFIG_CPU_FREQ_GOV_ONDEMAND=m
-CONFIG_CPU_FREQ_GOV_CONSERVATIVE=m
+# CONFIG_CPU_FREQ is not set
#
# Floating point emulation
#
# At least one emulation must be selected
#
+# CONFIG_FPE_NWFPE is not set
+# CONFIG_FPE_FASTFPE is not set
CONFIG_VFP=y
CONFIG_VFPv3=y
CONFIG_NEON=y
# CONFIG_INET_ESP is not set
# CONFIG_INET_IPCOMP is not set
# CONFIG_INET_XFRM_TUNNEL is not set
-# CONFIG_INET_TUNNEL is not set
+CONFIG_INET_TUNNEL=m
CONFIG_INET_XFRM_MODE_TRANSPORT=y
CONFIG_INET_XFRM_MODE_TUNNEL=y
CONFIG_INET_XFRM_MODE_BEET=y
CONFIG_TCP_CONG_CUBIC=y
CONFIG_DEFAULT_TCP_CONG="cubic"
# CONFIG_TCP_MD5SIG is not set
-# CONFIG_IPV6 is not set
+CONFIG_IPV6=m
+# CONFIG_IPV6_PRIVACY is not set
+# CONFIG_IPV6_ROUTER_PREF is not set
+# CONFIG_IPV6_OPTIMISTIC_DAD is not set
+# CONFIG_INET6_AH is not set
+# CONFIG_INET6_ESP is not set
+# CONFIG_INET6_IPCOMP is not set
+# CONFIG_IPV6_MIP6 is not set
+# CONFIG_INET6_XFRM_TUNNEL is not set
+# CONFIG_INET6_TUNNEL is not set
+CONFIG_INET6_XFRM_MODE_TRANSPORT=m
+CONFIG_INET6_XFRM_MODE_TUNNEL=m
+CONFIG_INET6_XFRM_MODE_BEET=m
+# CONFIG_INET6_XFRM_MODE_ROUTEOPTIMIZATION is not set
+CONFIG_IPV6_SIT=m
+CONFIG_IPV6_NDISC_NODETYPE=y
+# CONFIG_IPV6_TUNNEL is not set
+# CONFIG_IPV6_MULTIPLE_TABLES is not set
+# CONFIG_IPV6_MROUTE is not set
# CONFIG_NETWORK_SECMARK is not set
# CONFIG_NETFILTER is not set
# CONFIG_IP_DCCP is not set
# CONFIG_IRDA is not set
CONFIG_BT=y
CONFIG_BT_L2CAP=y
-# CONFIG_BT_SCO is not set
+CONFIG_BT_SCO=y
CONFIG_BT_RFCOMM=y
CONFIG_BT_RFCOMM_TTY=y
CONFIG_BT_BNEP=y
#
# Bluetooth device drivers
#
-# CONFIG_BT_HCIUSB is not set
-# CONFIG_BT_HCIBTUSB is not set
+CONFIG_BT_HCIBTUSB=m
# CONFIG_BT_HCIBTSDIO is not set
-# CONFIG_BT_HCIUART is not set
+CONFIG_BT_HCIUART=y
+CONFIG_BT_HCIUART_H4=y
+# CONFIG_BT_HCIUART_BCSP is not set
+CONFIG_BT_HCIUART_LL=y
# CONFIG_BT_HCIBCM203X is not set
# CONFIG_BT_HCIBPA10X is not set
# CONFIG_BT_HCIBFUSB is not set
# CONFIG_MAC80211_LOWTX_FRAME_DUMP is not set
# CONFIG_MAC80211_VERBOSE_SPECT_MGMT_DEBUG is not set
# CONFIG_IEEE80211 is not set
-# CONFIG_RFKILL is not set
+CONFIG_RFKILL=m
+# CONFIG_RFKILL_INPUT is not set
+CONFIG_RFKILL_LEDS=y
# CONFIG_NET_9P is not set
#
CONFIG_USB_EPSON2888=y
CONFIG_USB_KC2190=y
CONFIG_USB_NET_ZAURUS=m
+# CONFIG_USB_HSO is not set
# CONFIG_WAN is not set
CONFIG_PPP=m
CONFIG_PPP_MULTILINK=y
# CONFIG_INPUT_ATI_REMOTE is not set
# CONFIG_INPUT_ATI_REMOTE2 is not set
# CONFIG_INPUT_KEYSPAN_REMOTE is not set
-# CONFIG_INPUT_POWERMATE is not set
+CONFIG_INPUT_POWERMATE=m
# CONFIG_INPUT_YEALINK is not set
CONFIG_INPUT_UINPUT=y
CONFIG_INPUT_VSENSE=y
CONFIG_TWL4030_POWEROFF=y
# CONFIG_SENSORS_MAX6875 is not set
# CONFIG_SENSORS_TSL2550 is not set
+# CONFIG_SENSORS_TSL2563 is not set
# CONFIG_LP5521 is not set
# CONFIG_I2C_DEBUG_CORE is not set
# CONFIG_I2C_DEBUG_ALGO is not set
# CONFIG_BATTERY_DS2760 is not set
# CONFIG_BATTERY_BQ27x00 is not set
CONFIG_TWL4030_BCI_BATTERY=y
-# CONFIG_HWMON is not set
+CONFIG_HWMON=m
+# CONFIG_HWMON_VID is not set
+# CONFIG_SENSORS_AD7414 is not set
+# CONFIG_SENSORS_AD7418 is not set
+# CONFIG_SENSORS_ADCXX is not set
+# CONFIG_SENSORS_ADM1021 is not set
+# CONFIG_SENSORS_ADM1025 is not set
+# CONFIG_SENSORS_ADM1026 is not set
+# CONFIG_SENSORS_ADM1029 is not set
+# CONFIG_SENSORS_ADM1031 is not set
+# CONFIG_SENSORS_ADM9240 is not set
+# CONFIG_SENSORS_ADT7470 is not set
+# CONFIG_SENSORS_ADT7473 is not set
+# CONFIG_SENSORS_ATXP1 is not set
+# CONFIG_SENSORS_DS1621 is not set
+# CONFIG_SENSORS_F71805F is not set
+# CONFIG_SENSORS_F71882FG is not set
+# CONFIG_SENSORS_F75375S is not set
+# CONFIG_SENSORS_GL518SM is not set
+# CONFIG_SENSORS_GL520SM is not set
+# CONFIG_SENSORS_IT87 is not set
+# CONFIG_SENSORS_LM63 is not set
+# CONFIG_SENSORS_LM70 is not set
+# CONFIG_SENSORS_LM75 is not set
+# CONFIG_SENSORS_LM77 is not set
+# CONFIG_SENSORS_LM78 is not set
+# CONFIG_SENSORS_LM80 is not set
+# CONFIG_SENSORS_LM83 is not set
+# CONFIG_SENSORS_LM85 is not set
+# CONFIG_SENSORS_LM87 is not set
+# CONFIG_SENSORS_LM90 is not set
+# CONFIG_SENSORS_LM92 is not set
+# CONFIG_SENSORS_LM93 is not set
+# CONFIG_SENSORS_MAX1619 is not set
+# CONFIG_SENSORS_MAX6650 is not set
+# CONFIG_SENSORS_PC87360 is not set
+# CONFIG_SENSORS_PC87427 is not set
+# CONFIG_SENSORS_DME1737 is not set
+# CONFIG_SENSORS_SMSC47M1 is not set
+# CONFIG_SENSORS_SMSC47M192 is not set
+# CONFIG_SENSORS_SMSC47B397 is not set
+# CONFIG_SENSORS_ADS7828 is not set
+# CONFIG_SENSORS_THMC50 is not set
+# CONFIG_SENSORS_VT1211 is not set
+# CONFIG_SENSORS_W83781D is not set
+# CONFIG_SENSORS_W83791D is not set
+# CONFIG_SENSORS_W83792D is not set
+# CONFIG_SENSORS_W83793 is not set
+# CONFIG_SENSORS_W83L785TS is not set
+# CONFIG_SENSORS_W83L786NG is not set
+# CONFIG_SENSORS_W83627HF is not set
+# CONFIG_SENSORS_W83627EHF is not set
+# CONFIG_SENSORS_TSC210X is not set
+# CONFIG_SENSORS_OMAP34XX is not set
+# CONFIG_HWMON_DEBUG_CHIP is not set
# CONFIG_WATCHDOG is not set
#
# CONFIG_VIDEO_SH_MOBILE_CEU is not set
CONFIG_RADIO_ADAPTERS=y
# CONFIG_RADIO_TEA5761 is not set
-# CONFIG_USB_DSBR is not set
+CONFIG_USB_DSBR=m
# CONFIG_USB_SI470X is not set
CONFIG_DVB_CAPTURE_DRIVERS=y
# CONFIG_TTPCI_EEPROM is not set
# CONFIG_DVB_ISL6405 is not set
CONFIG_DVB_ISL6421=m
CONFIG_DAB=y
-# CONFIG_USB_DABUSB is not set
+CONFIG_USB_DABUSB=m
#
# Graphics support
#
CONFIG_MMC_BLOCK=y
CONFIG_MMC_BLOCK_BOUNCE=y
-# CONFIG_SDIO_UART is not set
+CONFIG_SDIO_UART=m
# CONFIG_MMC_TEST is not set
#
CONFIG_FS_MBCACHE=y
# CONFIG_REISERFS_FS is not set
# CONFIG_JFS_FS is not set
-# CONFIG_FS_POSIX_ACL is not set
+CONFIG_FS_POSIX_ACL=y
# CONFIG_XFS_FS is not set
# CONFIG_GFS2_FS is not set
# CONFIG_OCFS2_FS is not set
# CONFIG_NFS_V3_ACL is not set
CONFIG_NFS_V4=y
CONFIG_ROOT_NFS=y
-# CONFIG_NFSD is not set
+CONFIG_NFSD=m
+CONFIG_NFSD_V3=y
+# CONFIG_NFSD_V3_ACL is not set
+CONFIG_NFSD_V4=y
CONFIG_LOCKD=y
CONFIG_LOCKD_V4=y
+CONFIG_EXPORTFS=m
CONFIG_NFS_COMMON=y
CONFIG_SUNRPC=y
CONFIG_SUNRPC_GSS=y
--- /dev/null
+From 917491ccf4546bbd34d999cd4878c3b10b621c1c Mon Sep 17 00:00:00 2001
+From: Grazvydas Ignotas <notasas@gmail.com>
+Date: Tue, 3 Nov 2009 15:15:00 +0200
+Subject: [PATCH 1/7] input: remove old twl4030keypad to replace it with mainline version
+
+---
+ drivers/input/keyboard/Kconfig | 10 -
+ drivers/input/keyboard/Makefile | 1 -
+ drivers/input/keyboard/omap-twl4030keypad.c | 422 ---------------------------
+ drivers/input/keyboard/twl4030-keypad.h | 82 ------
+ 4 files changed, 0 insertions(+), 515 deletions(-)
+ delete mode 100644 drivers/input/keyboard/omap-twl4030keypad.c
+ delete mode 100644 drivers/input/keyboard/twl4030-keypad.h
+
+diff --git a/drivers/input/keyboard/Kconfig b/drivers/input/keyboard/Kconfig
+index 616ce41..59ad05a 100644
+--- a/drivers/input/keyboard/Kconfig
++++ b/drivers/input/keyboard/Kconfig
+@@ -259,16 +259,6 @@ config KEYBOARD_OMAP
+ To compile this driver as a module, choose M here: the
+ module will be called omap-keypad.
+
+-config KEYBOARD_TWL4030
+- tristate "TI TWL4030 keypad support"
+- depends on TWL4030_CORE && (MACH_OMAP_2430SDP || MACH_OMAP2EVM || MACH_OMAP_3430SDP || MACH_OMAP3EVM || MACH_OMAP3_PANDORA)
+- help
+- Say Y here if you want to use the OMAP TWL4030 keypad.
+-
+- To compile this driver as a module, choose M here: the
+- module will be called omap-twl4030keypad. This driver depends on
+- TWL4030 Core and TWL4030 GPIO I2C client driver
+-
+ config OMAP_PS2
+ tristate "TI OMAP Innovator 1510 PS/2 keyboard & mouse support"
+ depends on ARCH_OMAP15XX && MACH_OMAP_INNOVATOR
+diff --git a/drivers/input/keyboard/Makefile b/drivers/input/keyboard/Makefile
+index ae47fff..81e59f7 100644
+--- a/drivers/input/keyboard/Makefile
++++ b/drivers/input/keyboard/Makefile
+@@ -22,7 +22,6 @@ obj-$(CONFIG_KEYBOARD_OMAP) += omap-keypad.o
+ obj-$(CONFIG_OMAP_PS2) += innovator_ps2.o
+ obj-$(CONFIG_KEYBOARD_TSC2301) += tsc2301_kp.o
+ obj-$(CONFIG_KEYBOARD_LM8323) += lm8323.o
+-obj-$(CONFIG_KEYBOARD_TWL4030) += omap-twl4030keypad.o
+ obj-$(CONFIG_KEYBOARD_PXA27x) += pxa27x_keypad.o
+ obj-$(CONFIG_KEYBOARD_AAED2000) += aaed2000_kbd.o
+ obj-$(CONFIG_KEYBOARD_GPIO) += gpio_keys.o
+diff --git a/drivers/input/keyboard/omap-twl4030keypad.c b/drivers/input/keyboard/omap-twl4030keypad.c
+deleted file mode 100644
+index f6f1ad8..0000000
+--- a/drivers/input/keyboard/omap-twl4030keypad.c
++++ /dev/null
+@@ -1,422 +0,0 @@
+-/*
+- * drivers/input/keyboard/omap-twl4030keypad.c
+- *
+- * Copyright (C) 2007 Texas Instruments, Inc.
+- * Copyright (C) 2008 Nokia Corporation
+- *
+- * Code re-written for 2430SDP by:
+- * Syed Mohammed Khasim <x0khasim@ti.com>
+- *
+- * Initial Code:
+- * Manjunatha G K <manjugk@ti.com>
+- *
+- * This program is free software; you can redistribute it and/or modify
+- * it under the terms of the GNU General Public License as published by
+- * the Free Software Foundation; either version 2 of the License, or
+- * (at your option) any later version.
+- *
+- * This program is distributed in the hope that it will be useful,
+- * but WITHOUT ANY WARRANTY; without even the implied warranty of
+- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+- * GNU General Public License for more details.
+- *
+- * You should have received a copy of the GNU General Public License
+- * along with this program; if not, write to the Free Software
+- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+- */
+-
+-#include <linux/module.h>
+-#include <linux/init.h>
+-#include <linux/interrupt.h>
+-#include <linux/types.h>
+-#include <linux/input.h>
+-#include <linux/kernel.h>
+-#include <linux/mutex.h>
+-#include <linux/delay.h>
+-#include <linux/bitops.h>
+-#include <linux/platform_device.h>
+-#include <linux/i2c.h>
+-#include <linux/i2c/twl4030.h>
+-#include <linux/irq.h>
+-#include "twl4030-keypad.h"
+-
+-#define PTV_PRESCALER 4
+-
+-#define MAX_ROWS 8 /* TWL4030 hardlimit */
+-#define ROWCOL_MASK 0xFF000000
+-#define KEYNUM_MASK 0x00FFFFFF
+-#define KEY(col, row, val) (((col) << 28) | ((row) << 24) | (val))
+-
+-/* Global variables */
+-
+-struct omap_keypad {
+- int *keymap;
+- unsigned int keymapsize;
+- u16 kp_state[MAX_ROWS];
+- int n_rows;
+- int n_cols;
+- int irq;
+-
+- struct device *dbg_dev;
+- struct input_dev *omap_twl4030kp;
+-
+- /* sync read/write */
+- struct mutex mutex;
+-};
+-
+-static int twl4030_kpread(struct omap_keypad *kp,
+- u32 module, u8 *data, u32 reg, u8 num_bytes)
+-{
+- int ret;
+-
+- ret = twl4030_i2c_read(module, data, reg, num_bytes);
+- if (ret < 0) {
+- dev_warn(kp->dbg_dev,
+- "Couldn't read TWL4030: %X - ret %d[%x]\n",
+- reg, ret, ret);
+- return ret;
+- }
+- return ret;
+-}
+-
+-static int twl4030_kpwrite_u8(struct omap_keypad *kp,
+- u32 module, u8 data, u32 reg)
+-{
+- int ret;
+-
+- ret = twl4030_i2c_write_u8(module, data, reg);
+- if (ret < 0) {
+- dev_warn(kp->dbg_dev,
+- "Could not write TWL4030: %X - ret %d[%x]\n",
+- reg, ret, ret);
+- return ret;
+- }
+- return ret;
+-}
+-
+-static int omap_kp_find_key(struct omap_keypad *kp, int col, int row)
+-{
+- int i, rc;
+-
+- rc = KEY(col, row, 0);
+- for (i = 0; i < kp->keymapsize; i++)
+- if ((kp->keymap[i] & ROWCOL_MASK) == rc)
+- return kp->keymap[i] & KEYNUM_MASK;
+-
+- return -EINVAL;
+-}
+-
+-static inline u16 omap_kp_col_xlate(struct omap_keypad *kp, u8 col)
+-{
+- /* If all bits in a row are active for all coloumns then
+- * we have that row line connected to gnd. Mark this
+- * key on as if it was on matrix position n_cols (ie
+- * one higher than the size of the matrix).
+- */
+- if (col == 0xFF)
+- return 1 << kp->n_cols;
+- else
+- return col & ((1 << kp->n_cols) - 1);
+-}
+-
+-static int omap_kp_read_kp_matrix_state(struct omap_keypad *kp, u16 *state)
+-{
+- u8 new_state[MAX_ROWS];
+- int row;
+- int ret = twl4030_kpread(kp, TWL4030_MODULE_KEYPAD,
+- new_state, KEYP_FULL_CODE_7_0, kp->n_rows);
+- if (ret >= 0) {
+- for (row = 0; row < kp->n_rows; row++)
+- state[row] = omap_kp_col_xlate(kp, new_state[row]);
+- }
+- return ret;
+-}
+-
+-static int omap_kp_is_in_ghost_state(struct omap_keypad *kp, u16 *key_state)
+-{
+- int i;
+- u16 check = 0;
+-
+- for (i = 0; i < kp->n_rows; i++) {
+- u16 col = key_state[i];
+-
+- if ((col & check) && hweight16(col) > 1)
+- return 1;
+- check |= col;
+- }
+-
+- return 0;
+-}
+-
+-static void twl4030_kp_scan(struct omap_keypad *kp, int release_all)
+-{
+- u16 new_state[MAX_ROWS];
+- int col, row;
+-
+- if (release_all)
+- memset(new_state, 0, sizeof(new_state));
+- else {
+- /* check for any changes */
+- int ret = omap_kp_read_kp_matrix_state(kp, new_state);
+- if (ret < 0) /* panic ... */
+- return;
+-
+- if (omap_kp_is_in_ghost_state(kp, new_state))
+- return;
+- }
+-
+- mutex_lock(&kp->mutex);
+-
+- /* check for changes and print those */
+- for (row = 0; row < kp->n_rows; row++) {
+- int changed = new_state[row] ^ kp->kp_state[row];
+-
+- if (!changed)
+- continue;
+-
+- for (col = 0; col < kp->n_cols; col++) {
+- int key;
+-
+- if (!(changed & (1 << col)))
+- continue;
+-
+- dev_dbg(kp->dbg_dev, "key [%d:%d] %s\n", row, col,
+- (new_state[row] & (1 << col)) ?
+- "press" : "release");
+-
+- key = omap_kp_find_key(kp, col, row);
+- if (key < 0)
+- dev_warn(kp->dbg_dev,
+- "Spurious key event %d-%d\n",
+- col, row);
+- else
+- input_report_key(kp->omap_twl4030kp, key,
+- new_state[row] & (1 << col));
+- }
+- kp->kp_state[row] = new_state[row];
+- }
+-
+- mutex_unlock(&kp->mutex);
+-}
+-
+-/*
+- * Keypad interrupt handler
+- */
+-static irqreturn_t do_kp_irq(int irq, void *_kp)
+-{
+- struct omap_keypad *kp = _kp;
+- u8 reg;
+- int ret;
+-
+-#ifdef CONFIG_LOCKDEP
+- /* WORKAROUND for lockdep forcing IRQF_DISABLED on us, which
+- * we don't want and can't tolerate. Although it might be
+- * friendlier not to borrow this thread context...
+- */
+- local_irq_enable();
+-#endif
+-
+- /* Read & Clear TWL4030 pending interrupt */
+- ret = twl4030_kpread(kp, TWL4030_MODULE_KEYPAD, ®, KEYP_ISR1, 1);
+-
+- /* Release all keys if I2C has gone bad or
+- * the KEYP has gone to idle state */
+- if ((ret >= 0) && (reg & KEYP_IMR1_KP))
+- twl4030_kp_scan(kp, 0);
+- else
+- twl4030_kp_scan(kp, 1);
+-
+- return IRQ_HANDLED;
+-}
+-
+-/*
+- * Registers keypad device with input sub system
+- * and configures TWL4030 keypad registers
+- */
+-static int __init omap_kp_probe(struct platform_device *pdev)
+-{
+- u8 reg;
+- int i;
+- int ret = 0;
+- struct omap_keypad *kp;
+- struct twl4030_keypad_data *pdata = pdev->dev.platform_data;
+-
+- kp = kzalloc(sizeof(*kp), GFP_KERNEL);
+- if (!kp)
+- return -ENOMEM;
+-
+- if (!pdata->rows || !pdata->cols || !pdata->keymap) {
+- dev_err(&pdev->dev, "No rows, cols or keymap from pdata\n");
+- kfree(kp);
+- return -EINVAL;
+- }
+-
+- dev_set_drvdata(&pdev->dev, kp);
+-
+- /* Get the debug Device */
+- kp->dbg_dev = &pdev->dev;
+-
+- kp->omap_twl4030kp = input_allocate_device();
+- if (!kp->omap_twl4030kp) {
+- kfree(kp);
+- return -ENOMEM;
+- }
+-
+- mutex_init(&kp->mutex);
+-
+- kp->keymap = pdata->keymap;
+- kp->keymapsize = pdata->keymapsize;
+- kp->n_rows = pdata->rows;
+- kp->n_cols = pdata->cols;
+- kp->irq = pdata->irq;
+-
+- /* setup input device */
+- set_bit(EV_KEY, kp->omap_twl4030kp->evbit);
+-
+- /* Enable auto repeat feature of Linux input subsystem */
+- if (pdata->rep)
+- set_bit(EV_REP, kp->omap_twl4030kp->evbit);
+-
+- for (i = 0; i < kp->keymapsize; i++)
+- set_bit(kp->keymap[i] & KEYNUM_MASK,
+- kp->omap_twl4030kp->keybit);
+-
+- kp->omap_twl4030kp->name = "omap_twl4030keypad";
+- kp->omap_twl4030kp->phys = "omap_twl4030keypad/input0";
+- kp->omap_twl4030kp->dev.parent = &pdev->dev;
+-
+- kp->omap_twl4030kp->id.bustype = BUS_HOST;
+- kp->omap_twl4030kp->id.vendor = 0x0001;
+- kp->omap_twl4030kp->id.product = 0x0001;
+- kp->omap_twl4030kp->id.version = 0x0003;
+-
+- kp->omap_twl4030kp->keycode = kp->keymap;
+- kp->omap_twl4030kp->keycodesize = sizeof(unsigned int);
+- kp->omap_twl4030kp->keycodemax = kp->keymapsize;
+-
+- ret = input_register_device(kp->omap_twl4030kp);
+- if (ret < 0) {
+- dev_err(kp->dbg_dev,
+- "Unable to register twl4030 keypad device\n");
+- goto err2;
+- }
+-
+- /* Disable auto-repeat */
+- reg = KEYP_CTRL_NOAUTORPT;
+- ret = twl4030_kpwrite_u8(kp, TWL4030_MODULE_KEYPAD, reg, KEYP_CTRL);
+- if (ret < 0)
+- goto err3;
+-
+- /* Enable TO rising and KP rising and falling edge detection */
+- reg = KEYP_EDR_KP_BOTH | KEYP_EDR_TO_RISING;
+- ret = twl4030_kpwrite_u8(kp, TWL4030_MODULE_KEYPAD, reg, KEYP_EDR);
+- if (ret < 0)
+- goto err3;
+-
+- /* Set PTV prescaler Field */
+- reg = (PTV_PRESCALER << KEYP_LK_PTV_PTV_SHIFT);
+- ret = twl4030_kpwrite_u8(kp, TWL4030_MODULE_KEYPAD, reg, KEYP_LK_PTV);
+- if (ret < 0)
+- goto err3;
+-
+- /* Set key debounce time to 20 ms */
+- i = KEYP_PERIOD_US(20000, PTV_PRESCALER);
+- ret = twl4030_kpwrite_u8(kp, TWL4030_MODULE_KEYPAD, i, KEYP_DEB);
+- if (ret < 0)
+- goto err3;
+-
+- /* Set timeout period to 100 ms */
+- i = KEYP_PERIOD_US(200000, PTV_PRESCALER);
+- ret = twl4030_kpwrite_u8(kp, TWL4030_MODULE_KEYPAD,
+- (i & 0xFF), KEYP_TIMEOUT_L);
+- if (ret < 0)
+- goto err3;
+-
+- ret = twl4030_kpwrite_u8(kp, TWL4030_MODULE_KEYPAD,
+- (i >> 8), KEYP_TIMEOUT_H);
+- if (ret < 0)
+- goto err3;
+-
+- /* Enable Clear-on-Read */
+- reg = KEYP_SIH_CTRL_COR | KEYP_SIH_CTRL_PEND_DIS;
+- ret = twl4030_kpwrite_u8(kp, TWL4030_MODULE_KEYPAD,
+- reg, KEYP_SIH_CTRL);
+- if (ret < 0)
+- goto err3;
+-
+- /*
+- * This ISR will always execute in kernel thread context because of
+- * the need to access the TWL4030 over the I2C bus.
+- */
+- ret = request_irq(kp->irq, do_kp_irq, 0, pdev->name, kp);
+- if (ret < 0) {
+- dev_info(kp->dbg_dev, "request_irq failed for irq no=%d\n",
+- kp->irq);
+- goto err3;
+- } else {
+- /* Enable KP and TO interrupts now. */
+- reg = ~(KEYP_IMR1_KP | KEYP_IMR1_TO);
+- ret = twl4030_kpwrite_u8(kp, TWL4030_MODULE_KEYPAD,
+- reg, KEYP_IMR1);
+- if (ret < 0)
+- goto err5;
+- }
+-
+- ret = omap_kp_read_kp_matrix_state(kp, kp->kp_state);
+- if (ret < 0)
+- goto err4;
+-
+- return ret;
+-err5:
+- /* mask all events - we don't care about the result */
+- (void) twl4030_kpwrite_u8(kp, TWL4030_MODULE_KEYPAD, 0xff, KEYP_IMR1);
+-err4:
+- free_irq(kp->irq, NULL);
+-err3:
+- input_unregister_device(kp->omap_twl4030kp);
+-err2:
+- input_free_device(kp->omap_twl4030kp);
+-
+- return -ENODEV;
+-}
+-
+-static int omap_kp_remove(struct platform_device *pdev)
+-{
+- struct omap_keypad *kp = dev_get_drvdata(&pdev->dev);
+-
+- free_irq(kp->irq, kp);
+- input_unregister_device(kp->omap_twl4030kp);
+- kfree(kp);
+-
+- return 0;
+-}
+-
+-
+-static struct platform_driver omap_kp_driver = {
+- .probe = omap_kp_probe,
+- .remove = __devexit_p(omap_kp_remove),
+- .driver = {
+- .name = "twl4030_keypad",
+- .owner = THIS_MODULE,
+- },
+-};
+-
+-/*
+- * OMAP TWL4030 Keypad init
+- */
+-static int __devinit omap_kp_init(void)
+-{
+- return platform_driver_register(&omap_kp_driver);
+-}
+-
+-static void __exit omap_kp_exit(void)
+-{
+- platform_driver_unregister(&omap_kp_driver);
+-}
+-
+-module_init(omap_kp_init);
+-module_exit(omap_kp_exit);
+-MODULE_ALIAS("platform:twl4030_keypad");
+-MODULE_AUTHOR("Texas Instruments");
+-MODULE_DESCRIPTION("OMAP TWL4030 Keypad Driver");
+-MODULE_LICENSE("GPL");
+diff --git a/drivers/input/keyboard/twl4030-keypad.h b/drivers/input/keyboard/twl4030-keypad.h
+deleted file mode 100644
+index b903a77..0000000
+--- a/drivers/input/keyboard/twl4030-keypad.h
++++ /dev/null
+@@ -1,82 +0,0 @@
+-/*
+- * drivers/input/keyboard/twl4030-keypad.h
+- *
+- * Copyright (C) 2006-2007 Texas Instruments, Inc.
+- *
+- * Intial Code:
+- * Syed Mohammed Khasim <x0khasim@ti.com>
+- *
+- * This program is free software; you can redistribute it and/or modify
+- * it under the terms of the GNU General Public License as published by
+- * the Free Software Foundation; either version 2 of the License, or
+- * (at your option) any later version.
+- *
+- * This program is distributed in the hope that it will be useful,
+- * but WITHOUT ANY WARRANTY; without even the implied warranty of
+- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+- * GNU General Public License for more details.
+- *
+- * You should have received a copy of the GNU General Public License
+- * along with this program; if not, write to the Free Software
+- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+- */
+-#ifndef __TWL4030_KEYPAD_H__
+-#define __TWL4030_KEYPAD_H__
+-
+-/* Register Definitions */
+-#define KEYP_CTRL 0x00
+-#define KEYP_DEB 0x01
+-#define KEYP_LONG_KEY 0x02
+-#define KEYP_LK_PTV 0x03
+-#define KEYP_TIMEOUT_L 0x04
+-#define KEYP_TIMEOUT_H 0x05
+-#define KEYP_FULL_CODE_7_0 0x09
+-#define KEYP_ISR1 0x11
+-#define KEYP_IMR1 0x12
+-#define KEYP_EDR 0x16
+-#define KEYP_SIH_CTRL 0x17
+-
+-/* KEYP_CTRL_REG Fields */
+-#define KEYP_CTRL_SOFT_NRST 0x01
+-#define KEYP_CTRL_SOFTMODEN 0x02
+-#define KEYP_CTRL_LK_EN 0x04
+-#define KEYP_CTRL_TOE_EN 0x08
+-#define KEYP_CTRL_TOLE_EN 0x10
+-#define KEYP_CTRL_RP_EN 0x20
+-#define KEYP_CTRL_KBD_ON 0x40
+-
+-
+-#define KEYP_CTRL_NOAUTORPT (KEYP_CTRL_SOFT_NRST | \
+- KEYP_CTRL_SOFTMODEN | \
+- KEYP_CTRL_TOE_EN | \
+- KEYP_CTRL_KBD_ON)
+-
+-/* KEYP_DEB, KEYP_LONG_KEY, KEYP_TIMEOUT_x*/
+-#define KEYP_PERIOD_US(T, prescale) (T / (31 << (prescale + 1)) - 1)
+-
+-/* KEYP_LK_PTV_REG Fields */
+-#define KEYP_LK_PTV_PTV_SHIFT 5
+-
+-/* KEYP_IMR1 Fields */
+-#define KEYP_IMR1_MIS 0x08
+-#define KEYP_IMR1_TO 0x04
+-#define KEYP_IMR1_LK 0x02
+-#define KEYP_IMR1_KP 0x01
+-
+-/* KEYP_EDR Fields */
+-#define KEYP_EDR_KP_FALLING 0x01
+-#define KEYP_EDR_KP_RISING 0x02
+-#define KEYP_EDR_KP_BOTH 0x03
+-#define KEYP_EDR_LK_FALLING 0x04
+-#define KEYP_EDR_LK_RISING 0x08
+-#define KEYP_EDR_TO_FALLING 0x10
+-#define KEYP_EDR_TO_RISING 0x20
+-#define KEYP_EDR_MIS_FALLING 0x40
+-#define KEYP_EDR_MIS_RISING 0x80
+-
+-/* KEYP_SIH_CTRL Fields */
+-#define KEYP_SIH_CTRL_COR 0x04
+-#define KEYP_SIH_CTRL_PEND_DIS 0x02
+-#define KEYP_SIH_CTRL_EXCL_EN 0x01
+-
+-#endif /* End of __TWL4030-KEYPAD_H__ */
+--
+1.6.3.1
+
--- /dev/null
+From a38e5cd354ef968dc6dc0decf579243c8d473698 Mon Sep 17 00:00:00 2001
+From: Eric Miao <eric.y.miao@gmail.com>
+Date: Tue, 3 Nov 2009 15:43:49 +0200
+Subject: [PATCH 2/7] Input: add support for generic GPIO-based matrix keypad
+
+Original patch by Marek Vasut, modified by Eric in:
+
+1. use delayed work to simplify the debouncing
+2. combine col_polarity/row_polarity into a single active_low field
+3. use a generic bit array based XOR algorithm to detect key
+ press/release, which should make the column assertion time
+ shorter and code a bit cleaner
+4. remove the ALT_FN handling, which is no way generic, the ALT_FN
+ key should be treated as no different from other keys, and
+ translation will be done by user space by commands like 'loadkeys'.
+5. explicitly disable row IRQs and flush potential pending work,
+ and schedule an immediate scan after resuming as suggested
+ by Uli Luckas
+6. incorporate review comments from many others
+
+Patch tested on Littleton/PXA310 (though PXA310 has a dedicate keypad
+controller, I have to configure those pins as generic GPIO to use this
+driver, works quite well, though), and Sharp Zaurus model SL-C7x0
+and SL-C1000.
+
+[dtor@mail.ru: fix error unwinding path, support changing keymap
+ from userspace]
+Signed-off-by: Marek Vasut <marek.vasut@gmail.com>
+Reviewed-by: Trilok Soni <soni.trilok@gmail.com>
+Reviewed-by: Uli Luckas <u.luckas@road.de>
+Reviewed-by: Russell King <linux@arm.linux.org.uk>
+Reviewed-by: Robert Jarzmik <robert.jarzmik@free.fr>
+Signed-off-by: Eric Miao <eric.miao@marvell.com>
+Signed-off-by: Dmitry Torokhov <dtor@mail.ru>
+
+Conflicts:
+
+ drivers/input/keyboard/Kconfig
+ drivers/input/keyboard/Makefile
+---
+ drivers/input/keyboard/Kconfig | 9 +
+ drivers/input/keyboard/Makefile | 1 +
+ drivers/input/keyboard/matrix_keypad.c | 453 ++++++++++++++++++++++++++++++++
+ include/linux/input/matrix_keypad.h | 65 +++++
+ 4 files changed, 528 insertions(+), 0 deletions(-)
+ create mode 100644 drivers/input/keyboard/matrix_keypad.c
+ create mode 100644 include/linux/input/matrix_keypad.h
+
+diff --git a/drivers/input/keyboard/Kconfig b/drivers/input/keyboard/Kconfig
+index 59ad05a..b0a3c78 100644
+--- a/drivers/input/keyboard/Kconfig
++++ b/drivers/input/keyboard/Kconfig
+@@ -199,6 +199,15 @@ config KEYBOARD_ATARI
+ To compile this driver as a module, choose M here: the
+ module will be called atakbd.
+
++config KEYBOARD_MATRIX
++ tristate "GPIO driven matrix keypad support"
++ depends on GENERIC_GPIO
++ help
++ Enable support for GPIO driven matrix keypad.
++
++ To compile this driver as a module, choose M here: the
++ module will be called matrix_keypad.
++
+ config KEYBOARD_HIL_OLD
+ tristate "HP HIL keyboard support (simple driver)"
+ depends on GSC || HP300
+diff --git a/drivers/input/keyboard/Makefile b/drivers/input/keyboard/Makefile
+index 81e59f7..7b8bc44 100644
+--- a/drivers/input/keyboard/Makefile
++++ b/drivers/input/keyboard/Makefile
+@@ -28,5 +28,6 @@ obj-$(CONFIG_KEYBOARD_GPIO) += gpio_keys.o
+ obj-$(CONFIG_KEYBOARD_HP6XX) += jornada680_kbd.o
+ obj-$(CONFIG_KEYBOARD_HP7XX) += jornada720_kbd.o
+ obj-$(CONFIG_KEYBOARD_MAPLE) += maple_keyb.o
++obj-$(CONFIG_KEYBOARD_MATRIX) += matrix_keypad.o
+ obj-$(CONFIG_KEYBOARD_BFIN) += bf54x-keys.o
+ obj-$(CONFIG_KEYBOARD_SH_KEYSC) += sh_keysc.o
+diff --git a/drivers/input/keyboard/matrix_keypad.c b/drivers/input/keyboard/matrix_keypad.c
+new file mode 100644
+index 0000000..e9b2e7c
+--- /dev/null
++++ b/drivers/input/keyboard/matrix_keypad.c
+@@ -0,0 +1,453 @@
++/*
++ * GPIO driven matrix keyboard driver
++ *
++ * Copyright (c) 2008 Marek Vasut <marek.vasut@gmail.com>
++ *
++ * Based on corgikbd.c
++ *
++ * 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.
++ *
++ */
++
++#include <linux/types.h>
++#include <linux/delay.h>
++#include <linux/platform_device.h>
++#include <linux/init.h>
++#include <linux/input.h>
++#include <linux/irq.h>
++#include <linux/interrupt.h>
++#include <linux/jiffies.h>
++#include <linux/module.h>
++#include <linux/gpio.h>
++#include <linux/input/matrix_keypad.h>
++
++struct matrix_keypad {
++ const struct matrix_keypad_platform_data *pdata;
++ struct input_dev *input_dev;
++ unsigned short *keycodes;
++
++ uint32_t last_key_state[MATRIX_MAX_COLS];
++ struct delayed_work work;
++ bool scan_pending;
++ bool stopped;
++ spinlock_t lock;
++};
++
++/*
++ * NOTE: normally the GPIO has to be put into HiZ when de-activated to cause
++ * minmal side effect when scanning other columns, here it is configured to
++ * be input, and it should work on most platforms.
++ */
++static void __activate_col(const struct matrix_keypad_platform_data *pdata,
++ int col, bool on)
++{
++ bool level_on = !pdata->active_low;
++
++ if (on) {
++ gpio_direction_output(pdata->col_gpios[col], level_on);
++ } else {
++ gpio_set_value_cansleep(pdata->col_gpios[col], !level_on);
++ gpio_direction_input(pdata->col_gpios[col]);
++ }
++}
++
++static void activate_col(const struct matrix_keypad_platform_data *pdata,
++ int col, bool on)
++{
++ __activate_col(pdata, col, on);
++
++ if (on && pdata->col_scan_delay_us)
++ udelay(pdata->col_scan_delay_us);
++}
++
++static void activate_all_cols(const struct matrix_keypad_platform_data *pdata,
++ bool on)
++{
++ int col;
++
++ for (col = 0; col < pdata->num_col_gpios; col++)
++ __activate_col(pdata, col, on);
++}
++
++static bool row_asserted(const struct matrix_keypad_platform_data *pdata,
++ int row)
++{
++ return gpio_get_value_cansleep(pdata->row_gpios[row]) ?
++ !pdata->active_low : pdata->active_low;
++}
++
++static void enable_row_irqs(struct matrix_keypad *keypad)
++{
++ const struct matrix_keypad_platform_data *pdata = keypad->pdata;
++ int i;
++
++ for (i = 0; i < pdata->num_row_gpios; i++)
++ enable_irq(gpio_to_irq(pdata->row_gpios[i]));
++}
++
++static void disable_row_irqs(struct matrix_keypad *keypad)
++{
++ const struct matrix_keypad_platform_data *pdata = keypad->pdata;
++ int i;
++
++ for (i = 0; i < pdata->num_row_gpios; i++)
++ disable_irq_nosync(gpio_to_irq(pdata->row_gpios[i]));
++}
++
++/*
++ * This gets the keys from keyboard and reports it to input subsystem
++ */
++static void matrix_keypad_scan(struct work_struct *work)
++{
++ struct matrix_keypad *keypad =
++ container_of(work, struct matrix_keypad, work.work);
++ struct input_dev *input_dev = keypad->input_dev;
++ const struct matrix_keypad_platform_data *pdata = keypad->pdata;
++ uint32_t new_state[MATRIX_MAX_COLS];
++ int row, col, code;
++
++ /* de-activate all columns for scanning */
++ activate_all_cols(pdata, false);
++
++ memset(new_state, 0, sizeof(new_state));
++
++ /* assert each column and read the row status out */
++ for (col = 0; col < pdata->num_col_gpios; col++) {
++
++ activate_col(pdata, col, true);
++
++ for (row = 0; row < pdata->num_row_gpios; row++)
++ new_state[col] |=
++ row_asserted(pdata, row) ? (1 << row) : 0;
++
++ activate_col(pdata, col, false);
++ }
++
++ for (col = 0; col < pdata->num_col_gpios; col++) {
++ uint32_t bits_changed;
++
++ bits_changed = keypad->last_key_state[col] ^ new_state[col];
++ if (bits_changed == 0)
++ continue;
++
++ for (row = 0; row < pdata->num_row_gpios; row++) {
++ if ((bits_changed & (1 << row)) == 0)
++ continue;
++
++ code = (row << 4) + col;
++ input_event(input_dev, EV_MSC, MSC_SCAN, code);
++ input_report_key(input_dev,
++ keypad->keycodes[code],
++ new_state[col] & (1 << row));
++ }
++ }
++ input_sync(input_dev);
++
++ memcpy(keypad->last_key_state, new_state, sizeof(new_state));
++
++ activate_all_cols(pdata, true);
++
++ /* Enable IRQs again */
++ spin_lock_irq(&keypad->lock);
++ keypad->scan_pending = false;
++ enable_row_irqs(keypad);
++ spin_unlock_irq(&keypad->lock);
++}
++
++static irqreturn_t matrix_keypad_interrupt(int irq, void *id)
++{
++ struct matrix_keypad *keypad = id;
++ unsigned long flags;
++
++ spin_lock_irqsave(&keypad->lock, flags);
++
++ /*
++ * See if another IRQ beaten us to it and scheduled the
++ * scan already. In that case we should not try to
++ * disable IRQs again.
++ */
++ if (unlikely(keypad->scan_pending || keypad->stopped))
++ goto out;
++
++ disable_row_irqs(keypad);
++ keypad->scan_pending = true;
++ schedule_delayed_work(&keypad->work,
++ msecs_to_jiffies(keypad->pdata->debounce_ms));
++
++out:
++ spin_unlock_irqrestore(&keypad->lock, flags);
++ return IRQ_HANDLED;
++}
++
++static int matrix_keypad_start(struct input_dev *dev)
++{
++ struct matrix_keypad *keypad = input_get_drvdata(dev);
++
++ keypad->stopped = false;
++ mb();
++
++ /*
++ * Schedule an immediate key scan to capture current key state;
++ * columns will be activated and IRQs be enabled after the scan.
++ */
++ schedule_delayed_work(&keypad->work, 0);
++
++ return 0;
++}
++
++static void matrix_keypad_stop(struct input_dev *dev)
++{
++ struct matrix_keypad *keypad = input_get_drvdata(dev);
++
++ keypad->stopped = true;
++ mb();
++ flush_work(&keypad->work.work);
++ /*
++ * matrix_keypad_scan() will leave IRQs enabled;
++ * we should disable them now.
++ */
++ disable_row_irqs(keypad);
++}
++
++#ifdef CONFIG_PM
++static int matrix_keypad_suspend(struct platform_device *pdev, pm_message_t state)
++{
++ struct matrix_keypad *keypad = platform_get_drvdata(pdev);
++ const struct matrix_keypad_platform_data *pdata = keypad->pdata;
++ int i;
++
++ matrix_keypad_stop(keypad->input_dev);
++
++ if (device_may_wakeup(&pdev->dev))
++ for (i = 0; i < pdata->num_row_gpios; i++)
++ enable_irq_wake(gpio_to_irq(pdata->row_gpios[i]));
++
++ return 0;
++}
++
++static int matrix_keypad_resume(struct platform_device *pdev)
++{
++ struct matrix_keypad *keypad = platform_get_drvdata(pdev);
++ const struct matrix_keypad_platform_data *pdata = keypad->pdata;
++ int i;
++
++ if (device_may_wakeup(&pdev->dev))
++ for (i = 0; i < pdata->num_row_gpios; i++)
++ disable_irq_wake(gpio_to_irq(pdata->row_gpios[i]));
++
++ matrix_keypad_start(keypad->input_dev);
++
++ return 0;
++}
++#else
++#define matrix_keypad_suspend NULL
++#define matrix_keypad_resume NULL
++#endif
++
++static int __devinit init_matrix_gpio(struct platform_device *pdev,
++ struct matrix_keypad *keypad)
++{
++ const struct matrix_keypad_platform_data *pdata = keypad->pdata;
++ int i, err = -EINVAL;
++
++ /* initialized strobe lines as outputs, activated */
++ for (i = 0; i < pdata->num_col_gpios; i++) {
++ err = gpio_request(pdata->col_gpios[i], "matrix_kbd_col");
++ if (err) {
++ dev_err(&pdev->dev,
++ "failed to request GPIO%d for COL%d\n",
++ pdata->col_gpios[i], i);
++ goto err_free_cols;
++ }
++
++ gpio_direction_output(pdata->col_gpios[i], !pdata->active_low);
++ }
++
++ for (i = 0; i < pdata->num_row_gpios; i++) {
++ err = gpio_request(pdata->row_gpios[i], "matrix_kbd_row");
++ if (err) {
++ dev_err(&pdev->dev,
++ "failed to request GPIO%d for ROW%d\n",
++ pdata->row_gpios[i], i);
++ goto err_free_rows;
++ }
++
++ gpio_direction_input(pdata->row_gpios[i]);
++ }
++
++ for (i = 0; i < pdata->num_row_gpios; i++) {
++ err = request_irq(gpio_to_irq(pdata->row_gpios[i]),
++ matrix_keypad_interrupt,
++ IRQF_DISABLED |
++ IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING,
++ "matrix-keypad", keypad);
++ if (err) {
++ dev_err(&pdev->dev,
++ "Unable to acquire interrupt for GPIO line %i\n",
++ pdata->row_gpios[i]);
++ goto err_free_irqs;
++ }
++ }
++
++ /* initialized as disabled - enabled by input->open */
++ disable_row_irqs(keypad);
++ return 0;
++
++err_free_irqs:
++ while (--i >= 0)
++ free_irq(gpio_to_irq(pdata->row_gpios[i]), keypad);
++ i = pdata->num_row_gpios;
++err_free_rows:
++ while (--i >= 0)
++ gpio_free(pdata->row_gpios[i]);
++ i = pdata->num_col_gpios;
++err_free_cols:
++ while (--i >= 0)
++ gpio_free(pdata->col_gpios[i]);
++
++ return err;
++}
++
++static int __devinit matrix_keypad_probe(struct platform_device *pdev)
++{
++ const struct matrix_keypad_platform_data *pdata;
++ const struct matrix_keymap_data *keymap_data;
++ struct matrix_keypad *keypad;
++ struct input_dev *input_dev;
++ unsigned short *keycodes;
++ int i;
++ int err;
++
++ pdata = pdev->dev.platform_data;
++ if (!pdata) {
++ dev_err(&pdev->dev, "no platform data defined\n");
++ return -EINVAL;
++ }
++
++ keymap_data = pdata->keymap_data;
++ if (!keymap_data) {
++ dev_err(&pdev->dev, "no keymap data defined\n");
++ return -EINVAL;
++ }
++
++ if (!keymap_data->max_keymap_size) {
++ dev_err(&pdev->dev, "invalid keymap data supplied\n");
++ return -EINVAL;
++ }
++
++ keypad = kzalloc(sizeof(struct matrix_keypad), GFP_KERNEL);
++ keycodes = kzalloc(keymap_data->max_keymap_size *
++ sizeof(keypad->keycodes),
++ GFP_KERNEL);
++ input_dev = input_allocate_device();
++ if (!keypad || !keycodes || !input_dev) {
++ err = -ENOMEM;
++ goto err_free_mem;
++ }
++
++ keypad->input_dev = input_dev;
++ keypad->pdata = pdata;
++ keypad->keycodes = keycodes;
++ keypad->stopped = true;
++ INIT_DELAYED_WORK(&keypad->work, matrix_keypad_scan);
++ spin_lock_init(&keypad->lock);
++
++ input_dev->name = pdev->name;
++ input_dev->id.bustype = BUS_HOST;
++ input_dev->dev.parent = &pdev->dev;
++ input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_REP);
++ input_dev->open = matrix_keypad_start;
++ input_dev->close = matrix_keypad_stop;
++
++ input_dev->keycode = keycodes;
++ input_dev->keycodesize = sizeof(*keycodes);
++ input_dev->keycodemax = keymap_data->max_keymap_size;
++
++ for (i = 0; i < keymap_data->keymap_size; i++) {
++ unsigned int key = keymap_data->keymap[i];
++ unsigned int row = KEY_ROW(key);
++ unsigned int col = KEY_COL(key);
++ unsigned short code = KEY_VAL(key);
++
++ keycodes[(row << 4) + col] = code;
++ __set_bit(code, input_dev->keybit);
++ }
++ __clear_bit(KEY_RESERVED, input_dev->keybit);
++
++ input_set_capability(input_dev, EV_MSC, MSC_SCAN);
++ input_set_drvdata(input_dev, keypad);
++
++ err = init_matrix_gpio(pdev, keypad);
++ if (err)
++ goto err_free_mem;
++
++ err = input_register_device(keypad->input_dev);
++ if (err)
++ goto err_free_mem;
++
++ device_init_wakeup(&pdev->dev, pdata->wakeup);
++ platform_set_drvdata(pdev, keypad);
++
++ return 0;
++
++err_free_mem:
++ input_free_device(input_dev);
++ kfree(keycodes);
++ kfree(keypad);
++ return err;
++}
++
++static int __devexit matrix_keypad_remove(struct platform_device *pdev)
++{
++ struct matrix_keypad *keypad = platform_get_drvdata(pdev);
++ const struct matrix_keypad_platform_data *pdata = keypad->pdata;
++ int i;
++
++ device_init_wakeup(&pdev->dev, 0);
++
++ for (i = 0; i < pdata->num_row_gpios; i++) {
++ free_irq(gpio_to_irq(pdata->row_gpios[i]), keypad);
++ gpio_free(pdata->row_gpios[i]);
++ }
++
++ for (i = 0; i < pdata->num_col_gpios; i++)
++ gpio_free(pdata->col_gpios[i]);
++
++ input_unregister_device(keypad->input_dev);
++ platform_set_drvdata(pdev, NULL);
++ kfree(keypad->keycodes);
++ kfree(keypad);
++
++ return 0;
++}
++
++static struct platform_driver matrix_keypad_driver = {
++ .probe = matrix_keypad_probe,
++ .remove = __devexit_p(matrix_keypad_remove),
++ .suspend = matrix_keypad_suspend,
++ .resume = matrix_keypad_resume,
++ .driver = {
++ .name = "matrix-keypad",
++ .owner = THIS_MODULE,
++ },
++};
++
++static int __init matrix_keypad_init(void)
++{
++ return platform_driver_register(&matrix_keypad_driver);
++}
++
++static void __exit matrix_keypad_exit(void)
++{
++ platform_driver_unregister(&matrix_keypad_driver);
++}
++
++module_init(matrix_keypad_init);
++module_exit(matrix_keypad_exit);
++
++MODULE_AUTHOR("Marek Vasut <marek.vasut@gmail.com>");
++MODULE_DESCRIPTION("GPIO Driven Matrix Keypad Driver");
++MODULE_LICENSE("GPL v2");
++MODULE_ALIAS("platform:matrix-keypad");
+diff --git a/include/linux/input/matrix_keypad.h b/include/linux/input/matrix_keypad.h
+new file mode 100644
+index 0000000..7964516
+--- /dev/null
++++ b/include/linux/input/matrix_keypad.h
+@@ -0,0 +1,65 @@
++#ifndef _MATRIX_KEYPAD_H
++#define _MATRIX_KEYPAD_H
++
++#include <linux/types.h>
++#include <linux/input.h>
++
++#define MATRIX_MAX_ROWS 16
++#define MATRIX_MAX_COLS 16
++
++#define KEY(row, col, val) ((((row) & (MATRIX_MAX_ROWS - 1)) << 24) |\
++ (((col) & (MATRIX_MAX_COLS - 1)) << 16) |\
++ (val & 0xffff))
++
++#define KEY_ROW(k) (((k) >> 24) & 0xff)
++#define KEY_COL(k) (((k) >> 16) & 0xff)
++#define KEY_VAL(k) ((k) & 0xffff)
++
++/**
++ * struct matrix_keymap_data - keymap for matrix keyboards
++ * @keymap: pointer to array of uint32 values encoded with KEY() macro
++ * representing keymap
++ * @keymap_size: number of entries (initialized) in this keymap
++ * @max_keymap_size: maximum size of keymap supported by the device
++ *
++ * This structure is supposed to be used by platform code to supply
++ * keymaps to drivers that implement matrix-like keypads/keyboards.
++ */
++struct matrix_keymap_data {
++ const uint32_t *keymap;
++ unsigned int keymap_size;
++ unsigned int max_keymap_size;
++};
++
++/**
++ * struct matrix_keypad_platform_data - platform-dependent keypad data
++ * @keymap_data: pointer to &matrix_keymap_data
++ * @row_gpios: array of gpio numbers reporesenting rows
++ * @col_gpios: array of gpio numbers reporesenting colums
++ * @num_row_gpios: actual number of row gpios used by device
++ * @num_col_gpios: actual number of col gpios used by device
++ * @col_scan_delay_us: delay, measured in microseconds, that is
++ * needed before we can keypad after activating column gpio
++ * @debounce_ms: debounce interval in milliseconds
++ *
++ * This structure represents platform-specific data that use used by
++ * matrix_keypad driver to perform proper initialization.
++ */
++struct matrix_keypad_platform_data {
++ const struct matrix_keymap_data *keymap_data;
++
++ unsigned int row_gpios[MATRIX_MAX_ROWS];
++ unsigned int col_gpios[MATRIX_MAX_COLS];
++ unsigned int num_row_gpios;
++ unsigned int num_col_gpios;
++
++ unsigned int col_scan_delay_us;
++
++ /* key debounce interval in milli-second */
++ unsigned int debounce_ms;
++
++ bool active_low;
++ bool wakeup;
++};
++
++#endif /* _MATRIX_KEYPAD_H */
+--
+1.6.3.1
+
--- /dev/null
+From 99250cbbae885e9535553e095c63c762d32b4325 Mon Sep 17 00:00:00 2001
+From: Eric Miao <eric.y.miao@gmail.com>
+Date: Wed, 5 Aug 2009 01:24:41 -0700
+Subject: [PATCH 3/7] Input: matrix_keypad - make matrix keymap size dynamic
+
+Remove assumption on the shift and size of rows/columns form
+matrix_keypad driver.
+
+Signed-off-by: Eric Miao <eric.y.miao@gmail.com>
+Signed-off-by: Dmitry Torokhov <dtor@mail.ru>
+(cherry picked from commit d82f1c35348cebe2fb2d4a4d31ce0ab0769e3d93)
+---
+ drivers/input/keyboard/matrix_keypad.c | 18 +++++++++---------
+ include/linux/input/matrix_keypad.h | 13 +++++++------
+ 2 files changed, 16 insertions(+), 15 deletions(-)
+
+diff --git a/drivers/input/keyboard/matrix_keypad.c b/drivers/input/keyboard/matrix_keypad.c
+index e9b2e7c..541b981 100644
+--- a/drivers/input/keyboard/matrix_keypad.c
++++ b/drivers/input/keyboard/matrix_keypad.c
+@@ -27,6 +27,7 @@ struct matrix_keypad {
+ const struct matrix_keypad_platform_data *pdata;
+ struct input_dev *input_dev;
+ unsigned short *keycodes;
++ unsigned int row_shift;
+
+ uint32_t last_key_state[MATRIX_MAX_COLS];
+ struct delayed_work work;
+@@ -136,7 +137,7 @@ static void matrix_keypad_scan(struct work_struct *work)
+ if ((bits_changed & (1 << row)) == 0)
+ continue;
+
+- code = (row << 4) + col;
++ code = MATRIX_SCAN_CODE(row, col, keypad->row_shift);
+ input_event(input_dev, EV_MSC, MSC_SCAN, code);
+ input_report_key(input_dev,
+ keypad->keycodes[code],
+@@ -317,6 +318,7 @@ static int __devinit matrix_keypad_probe(struct platform_device *pdev)
+ struct matrix_keypad *keypad;
+ struct input_dev *input_dev;
+ unsigned short *keycodes;
++ unsigned int row_shift;
+ int i;
+ int err;
+
+@@ -332,14 +334,11 @@ static int __devinit matrix_keypad_probe(struct platform_device *pdev)
+ return -EINVAL;
+ }
+
+- if (!keymap_data->max_keymap_size) {
+- dev_err(&pdev->dev, "invalid keymap data supplied\n");
+- return -EINVAL;
+- }
++ row_shift = get_count_order(pdata->num_col_gpios);
+
+ keypad = kzalloc(sizeof(struct matrix_keypad), GFP_KERNEL);
+- keycodes = kzalloc(keymap_data->max_keymap_size *
+- sizeof(keypad->keycodes),
++ keycodes = kzalloc((pdata->num_row_gpios << row_shift) *
++ sizeof(*keycodes),
+ GFP_KERNEL);
+ input_dev = input_allocate_device();
+ if (!keypad || !keycodes || !input_dev) {
+@@ -350,6 +349,7 @@ static int __devinit matrix_keypad_probe(struct platform_device *pdev)
+ keypad->input_dev = input_dev;
+ keypad->pdata = pdata;
+ keypad->keycodes = keycodes;
++ keypad->row_shift = row_shift;
+ keypad->stopped = true;
+ INIT_DELAYED_WORK(&keypad->work, matrix_keypad_scan);
+ spin_lock_init(&keypad->lock);
+@@ -363,7 +363,7 @@ static int __devinit matrix_keypad_probe(struct platform_device *pdev)
+
+ input_dev->keycode = keycodes;
+ input_dev->keycodesize = sizeof(*keycodes);
+- input_dev->keycodemax = keymap_data->max_keymap_size;
++ input_dev->keycodemax = pdata->num_row_gpios << keypad->row_shift;
+
+ for (i = 0; i < keymap_data->keymap_size; i++) {
+ unsigned int key = keymap_data->keymap[i];
+@@ -371,7 +371,7 @@ static int __devinit matrix_keypad_probe(struct platform_device *pdev)
+ unsigned int col = KEY_COL(key);
+ unsigned short code = KEY_VAL(key);
+
+- keycodes[(row << 4) + col] = code;
++ keycodes[MATRIX_SCAN_CODE(row, col, row_shift)] = code;
+ __set_bit(code, input_dev->keybit);
+ }
+ __clear_bit(KEY_RESERVED, input_dev->keybit);
+diff --git a/include/linux/input/matrix_keypad.h b/include/linux/input/matrix_keypad.h
+index 7964516..15d5903 100644
+--- a/include/linux/input/matrix_keypad.h
++++ b/include/linux/input/matrix_keypad.h
+@@ -15,12 +15,13 @@
+ #define KEY_COL(k) (((k) >> 16) & 0xff)
+ #define KEY_VAL(k) ((k) & 0xffff)
+
++#define MATRIX_SCAN_CODE(row, col, row_shift) (((row) << (row_shift)) + (col))
++
+ /**
+ * struct matrix_keymap_data - keymap for matrix keyboards
+ * @keymap: pointer to array of uint32 values encoded with KEY() macro
+ * representing keymap
+ * @keymap_size: number of entries (initialized) in this keymap
+- * @max_keymap_size: maximum size of keymap supported by the device
+ *
+ * This structure is supposed to be used by platform code to supply
+ * keymaps to drivers that implement matrix-like keypads/keyboards.
+@@ -28,14 +29,13 @@
+ struct matrix_keymap_data {
+ const uint32_t *keymap;
+ unsigned int keymap_size;
+- unsigned int max_keymap_size;
+ };
+
+ /**
+ * struct matrix_keypad_platform_data - platform-dependent keypad data
+ * @keymap_data: pointer to &matrix_keymap_data
+- * @row_gpios: array of gpio numbers reporesenting rows
+- * @col_gpios: array of gpio numbers reporesenting colums
++ * @row_gpios: pointer to array of gpio numbers representing rows
++ * @col_gpios: pointer to array of gpio numbers reporesenting colums
+ * @num_row_gpios: actual number of row gpios used by device
+ * @num_col_gpios: actual number of col gpios used by device
+ * @col_scan_delay_us: delay, measured in microseconds, that is
+@@ -48,8 +48,9 @@ struct matrix_keymap_data {
+ struct matrix_keypad_platform_data {
+ const struct matrix_keymap_data *keymap_data;
+
+- unsigned int row_gpios[MATRIX_MAX_ROWS];
+- unsigned int col_gpios[MATRIX_MAX_COLS];
++ const unsigned int *row_gpios;
++ const unsigned int *col_gpios;
++
+ unsigned int num_row_gpios;
+ unsigned int num_col_gpios;
+
+--
+1.6.3.1
+
--- /dev/null
+From 81d264643825f8318c2e0ca39a8b5ce6a4ce3a5a Mon Sep 17 00:00:00 2001
+From: Dmitry Torokhov <dmitry.torokhov@gmail.com>
+Date: Tue, 3 Nov 2009 15:49:37 +0200
+Subject: [PATCH 4/7] Input: matrix-keypad - add function to build device keymap
+
+Signed-off-by: Dmitry Torokhov <dtor@mail.ru>
+(cherry picked from commit 77a53fd21870c726b670c0d8179294ac1ea33468)
+
+Conflicts:
+
+ drivers/input/keyboard/w90p910_keypad.c
+---
+ drivers/input/keyboard/matrix_keypad.c | 15 +++------------
+ include/linux/input/matrix_keypad.h | 32 ++++++++++++++++++++++++++++++++
+ 2 files changed, 35 insertions(+), 12 deletions(-)
+
+diff --git a/drivers/input/keyboard/matrix_keypad.c b/drivers/input/keyboard/matrix_keypad.c
+index 541b981..91cfe51 100644
+--- a/drivers/input/keyboard/matrix_keypad.c
++++ b/drivers/input/keyboard/matrix_keypad.c
+@@ -319,7 +319,6 @@ static int __devinit matrix_keypad_probe(struct platform_device *pdev)
+ struct input_dev *input_dev;
+ unsigned short *keycodes;
+ unsigned int row_shift;
+- int i;
+ int err;
+
+ pdata = pdev->dev.platform_data;
+@@ -363,18 +362,10 @@ static int __devinit matrix_keypad_probe(struct platform_device *pdev)
+
+ input_dev->keycode = keycodes;
+ input_dev->keycodesize = sizeof(*keycodes);
+- input_dev->keycodemax = pdata->num_row_gpios << keypad->row_shift;
+-
+- for (i = 0; i < keymap_data->keymap_size; i++) {
+- unsigned int key = keymap_data->keymap[i];
+- unsigned int row = KEY_ROW(key);
+- unsigned int col = KEY_COL(key);
+- unsigned short code = KEY_VAL(key);
++ input_dev->keycodemax = pdata->num_row_gpios << row_shift;
+
+- keycodes[MATRIX_SCAN_CODE(row, col, row_shift)] = code;
+- __set_bit(code, input_dev->keybit);
+- }
+- __clear_bit(KEY_RESERVED, input_dev->keybit);
++ matrix_keypad_build_keymap(keymap_data, row_shift,
++ input_dev->keycode, input_dev->keybit);
+
+ input_set_capability(input_dev, EV_MSC, MSC_SCAN);
+ input_set_drvdata(input_dev, keypad);
+diff --git a/include/linux/input/matrix_keypad.h b/include/linux/input/matrix_keypad.h
+index 15d5903..b3cd42d 100644
+--- a/include/linux/input/matrix_keypad.h
++++ b/include/linux/input/matrix_keypad.h
+@@ -63,4 +63,36 @@ struct matrix_keypad_platform_data {
+ bool wakeup;
+ };
+
++/**
++ * matrix_keypad_build_keymap - convert platform keymap into matrix keymap
++ * @keymap_data: keymap supplied by the platform code
++ * @row_shift: number of bits to shift row value by to advance to the next
++ * line in the keymap
++ * @keymap: expanded version of keymap that is suitable for use by
++ * matrix keyboad driver
++ * @keybit: pointer to bitmap of keys supported by input device
++ *
++ * This function converts platform keymap (encoded with KEY() macro) into
++ * an array of keycodes that is suitable for using in a standard matrix
++ * keyboard driver that uses row and col as indices.
++ */
++static inline void
++matrix_keypad_build_keymap(const struct matrix_keymap_data *keymap_data,
++ unsigned int row_shift,
++ unsigned short *keymap, unsigned long *keybit)
++{
++ int i;
++
++ for (i = 0; i < keymap_data->keymap_size; i++) {
++ unsigned int key = keymap_data->keymap[i];
++ unsigned int row = KEY_ROW(key);
++ unsigned int col = KEY_COL(key);
++ unsigned short code = KEY_VAL(key);
++
++ keymap[MATRIX_SCAN_CODE(row, col, row_shift)] = code;
++ __set_bit(code, keybit);
++ }
++ __clear_bit(KEY_RESERVED, keybit);
++}
++
+ #endif /* _MATRIX_KEYPAD_H */
+--
+1.6.3.1
+
--- /dev/null
+From 0958f20b0237ef414371c3ff967a5e26649c30fe Mon Sep 17 00:00:00 2001
+From: David Brownell <dbrownell@users.sourceforge.net>
+Date: Tue, 3 Nov 2009 15:53:51 +0200
+Subject: [PATCH 5/7] Input: add twl4030_keypad driver
+
+Add a driver for the keypad controller on TWL4030 family chips.
+These support up to an 8x8 key matrix. The TWL4030 multifunction
+chips are mostly used on OMAP3 (or OMAP 2430) based boards.
+
+[dtor@mail.ru: switch to matrix-keypad framework, fix changing
+keymap from userspace]
+Reviewed-by: Trilok Soni <soni.trilok@gmail.com>
+Signed-off-by: David Brownell <dbrownell@users.sourceforge.net>
+Signed-off-by: Dmitry Torokhov <dtor@mail.ru>
+
+Conflicts:
+
+ drivers/input/keyboard/Kconfig
+ drivers/input/keyboard/Makefile
+---
+ drivers/input/keyboard/Kconfig | 11 +
+ drivers/input/keyboard/Makefile | 1 +
+ drivers/input/keyboard/twl4030_keypad.c | 480 +++++++++++++++++++++++++++++++
+ include/linux/i2c/twl4030.h | 19 +-
+ 4 files changed, 505 insertions(+), 6 deletions(-)
+ create mode 100644 drivers/input/keyboard/twl4030_keypad.c
+
+diff --git a/drivers/input/keyboard/Kconfig b/drivers/input/keyboard/Kconfig
+index b0a3c78..e6a0584 100644
+--- a/drivers/input/keyboard/Kconfig
++++ b/drivers/input/keyboard/Kconfig
+@@ -355,4 +355,15 @@ config KEYBOARD_SH_KEYSC
+
+ To compile this driver as a module, choose M here: the
+ module will be called sh_keysc.
++
++config KEYBOARD_TWL4030
++ tristate "TI TWL4030/TWL5030/TPS659x0 keypad support"
++ depends on TWL4030_CORE
++ help
++ Say Y here if your board use the keypad controller on
++ TWL4030 family chips. It's safe to say enable this
++ even on boards that don't use the keypad controller.
++
++ To compile this driver as a module, choose M here: the
++ module will be called twl4030_keypad.
+ endif
+diff --git a/drivers/input/keyboard/Makefile b/drivers/input/keyboard/Makefile
+index 7b8bc44..2085127 100644
+--- a/drivers/input/keyboard/Makefile
++++ b/drivers/input/keyboard/Makefile
+@@ -31,3 +31,4 @@ obj-$(CONFIG_KEYBOARD_MAPLE) += maple_keyb.o
+ obj-$(CONFIG_KEYBOARD_MATRIX) += matrix_keypad.o
+ obj-$(CONFIG_KEYBOARD_BFIN) += bf54x-keys.o
+ obj-$(CONFIG_KEYBOARD_SH_KEYSC) += sh_keysc.o
++obj-$(CONFIG_KEYBOARD_TWL4030) += twl4030_keypad.o
+diff --git a/drivers/input/keyboard/twl4030_keypad.c b/drivers/input/keyboard/twl4030_keypad.c
+new file mode 100644
+index 0000000..9a2977c
+--- /dev/null
++++ b/drivers/input/keyboard/twl4030_keypad.c
+@@ -0,0 +1,480 @@
++/*
++ * twl4030_keypad.c - driver for 8x8 keypad controller in twl4030 chips
++ *
++ * Copyright (C) 2007 Texas Instruments, Inc.
++ * Copyright (C) 2008 Nokia Corporation
++ *
++ * Code re-written for 2430SDP by:
++ * Syed Mohammed Khasim <x0khasim@ti.com>
++ *
++ * Initial Code:
++ * Manjunatha G K <manjugk@ti.com>
++ *
++ * This program is free software; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License as published by
++ * the Free Software Foundation; either version 2 of the License, or
++ * (at your option) any later version.
++ *
++ * This program is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
++ * GNU General Public License for more details.
++ *
++ * You should have received a copy of the GNU General Public License
++ * along with this program; if not, write to the Free Software
++ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
++ */
++
++#include <linux/kernel.h>
++#include <linux/module.h>
++#include <linux/init.h>
++#include <linux/interrupt.h>
++#include <linux/input.h>
++#include <linux/platform_device.h>
++#include <linux/i2c/twl4030.h>
++
++
++/*
++ * The TWL4030 family chips include a keypad controller that supports
++ * up to an 8x8 switch matrix. The controller can issue system wakeup
++ * events, since it uses only the always-on 32KiHz oscillator, and has
++ * an internal state machine that decodes pressed keys, including
++ * multi-key combinations.
++ *
++ * This driver lets boards define what keycodes they wish to report for
++ * which scancodes, as part of the "struct twl4030_keypad_data" used in
++ * the probe() routine.
++ *
++ * See the TPS65950 documentation; that's the general availability
++ * version of the TWL5030 second generation part.
++ */
++#define TWL4030_MAX_ROWS 8 /* TWL4030 hard limit */
++#define TWL4030_MAX_COLS 8
++#define TWL4030_ROW_SHIFT 3
++#define TWL4030_KEYMAP_SIZE (TWL4030_MAX_ROWS * TWL4030_MAX_COLS)
++
++struct twl4030_keypad {
++ unsigned short keymap[TWL4030_KEYMAP_SIZE];
++ u16 kp_state[TWL4030_MAX_ROWS];
++ unsigned n_rows;
++ unsigned n_cols;
++ unsigned irq;
++
++ struct device *dbg_dev;
++ struct input_dev *input;
++};
++
++/*----------------------------------------------------------------------*/
++
++/* arbitrary prescaler value 0..7 */
++#define PTV_PRESCALER 4
++
++/* Register Offsets */
++#define KEYP_CTRL 0x00
++#define KEYP_DEB 0x01
++#define KEYP_LONG_KEY 0x02
++#define KEYP_LK_PTV 0x03
++#define KEYP_TIMEOUT_L 0x04
++#define KEYP_TIMEOUT_H 0x05
++#define KEYP_KBC 0x06
++#define KEYP_KBR 0x07
++#define KEYP_SMS 0x08
++#define KEYP_FULL_CODE_7_0 0x09 /* row 0 column status */
++#define KEYP_FULL_CODE_15_8 0x0a /* ... row 1 ... */
++#define KEYP_FULL_CODE_23_16 0x0b
++#define KEYP_FULL_CODE_31_24 0x0c
++#define KEYP_FULL_CODE_39_32 0x0d
++#define KEYP_FULL_CODE_47_40 0x0e
++#define KEYP_FULL_CODE_55_48 0x0f
++#define KEYP_FULL_CODE_63_56 0x10
++#define KEYP_ISR1 0x11
++#define KEYP_IMR1 0x12
++#define KEYP_ISR2 0x13
++#define KEYP_IMR2 0x14
++#define KEYP_SIR 0x15
++#define KEYP_EDR 0x16 /* edge triggers */
++#define KEYP_SIH_CTRL 0x17
++
++/* KEYP_CTRL_REG Fields */
++#define KEYP_CTRL_SOFT_NRST BIT(0)
++#define KEYP_CTRL_SOFTMODEN BIT(1)
++#define KEYP_CTRL_LK_EN BIT(2)
++#define KEYP_CTRL_TOE_EN BIT(3)
++#define KEYP_CTRL_TOLE_EN BIT(4)
++#define KEYP_CTRL_RP_EN BIT(5)
++#define KEYP_CTRL_KBD_ON BIT(6)
++
++/* KEYP_DEB, KEYP_LONG_KEY, KEYP_TIMEOUT_x*/
++#define KEYP_PERIOD_US(t, prescale) ((t) / (31 << (prescale + 1)) - 1)
++
++/* KEYP_LK_PTV_REG Fields */
++#define KEYP_LK_PTV_PTV_SHIFT 5
++
++/* KEYP_{IMR,ISR,SIR} Fields */
++#define KEYP_IMR1_MIS BIT(3)
++#define KEYP_IMR1_TO BIT(2)
++#define KEYP_IMR1_LK BIT(1)
++#define KEYP_IMR1_KP BIT(0)
++
++/* KEYP_EDR Fields */
++#define KEYP_EDR_KP_FALLING 0x01
++#define KEYP_EDR_KP_RISING 0x02
++#define KEYP_EDR_KP_BOTH 0x03
++#define KEYP_EDR_LK_FALLING 0x04
++#define KEYP_EDR_LK_RISING 0x08
++#define KEYP_EDR_TO_FALLING 0x10
++#define KEYP_EDR_TO_RISING 0x20
++#define KEYP_EDR_MIS_FALLING 0x40
++#define KEYP_EDR_MIS_RISING 0x80
++
++
++/*----------------------------------------------------------------------*/
++
++static int twl4030_kpread(struct twl4030_keypad *kp,
++ u8 *data, u32 reg, u8 num_bytes)
++{
++ int ret = twl4030_i2c_read(TWL4030_MODULE_KEYPAD, data, reg, num_bytes);
++
++ if (ret < 0)
++ dev_warn(kp->dbg_dev,
++ "Couldn't read TWL4030: %X - ret %d[%x]\n",
++ reg, ret, ret);
++
++ return ret;
++}
++
++static int twl4030_kpwrite_u8(struct twl4030_keypad *kp, u8 data, u32 reg)
++{
++ int ret = twl4030_i2c_write_u8(TWL4030_MODULE_KEYPAD, data, reg);
++
++ if (ret < 0)
++ dev_warn(kp->dbg_dev,
++ "Could not write TWL4030: %X - ret %d[%x]\n",
++ reg, ret, ret);
++
++ return ret;
++}
++
++static inline u16 twl4030_col_xlate(struct twl4030_keypad *kp, u8 col)
++{
++ /* If all bits in a row are active for all coloumns then
++ * we have that row line connected to gnd. Mark this
++ * key on as if it was on matrix position n_cols (ie
++ * one higher than the size of the matrix).
++ */
++ if (col == 0xFF)
++ return 1 << kp->n_cols;
++ else
++ return col & ((1 << kp->n_cols) - 1);
++}
++
++static int twl4030_read_kp_matrix_state(struct twl4030_keypad *kp, u16 *state)
++{
++ u8 new_state[TWL4030_MAX_ROWS];
++ int row;
++ int ret = twl4030_kpread(kp, new_state,
++ KEYP_FULL_CODE_7_0, kp->n_rows);
++ if (ret >= 0)
++ for (row = 0; row < kp->n_rows; row++)
++ state[row] = twl4030_col_xlate(kp, new_state[row]);
++
++ return ret;
++}
++
++static int twl4030_is_in_ghost_state(struct twl4030_keypad *kp, u16 *key_state)
++{
++ int i;
++ u16 check = 0;
++
++ for (i = 0; i < kp->n_rows; i++) {
++ u16 col = key_state[i];
++
++ if ((col & check) && hweight16(col) > 1)
++ return 1;
++
++ check |= col;
++ }
++
++ return 0;
++}
++
++static void twl4030_kp_scan(struct twl4030_keypad *kp, bool release_all)
++{
++ struct input_dev *input = kp->input;
++ u16 new_state[TWL4030_MAX_ROWS];
++ int col, row;
++
++ if (release_all)
++ memset(new_state, 0, sizeof(new_state));
++ else {
++ /* check for any changes */
++ int ret = twl4030_read_kp_matrix_state(kp, new_state);
++
++ if (ret < 0) /* panic ... */
++ return;
++
++ if (twl4030_is_in_ghost_state(kp, new_state))
++ return;
++ }
++
++ /* check for changes and print those */
++ for (row = 0; row < kp->n_rows; row++) {
++ int changed = new_state[row] ^ kp->kp_state[row];
++
++ if (!changed)
++ continue;
++
++ for (col = 0; col < kp->n_cols; col++) {
++ int code;
++
++ if (!(changed & (1 << col)))
++ continue;
++
++ dev_dbg(kp->dbg_dev, "key [%d:%d] %s\n", row, col,
++ (new_state[row] & (1 << col)) ?
++ "press" : "release");
++
++ code = MATRIX_SCAN_CODE(row, col, TWL4030_ROW_SHIFT);
++ input_event(input, EV_MSC, MSC_SCAN, code);
++ input_report_key(input, kp->keymap[code],
++ new_state[row] & (1 << col));
++ }
++ kp->kp_state[row] = new_state[row];
++ }
++ input_sync(input);
++}
++
++/*
++ * Keypad interrupt handler
++ */
++static irqreturn_t do_kp_irq(int irq, void *_kp)
++{
++ struct twl4030_keypad *kp = _kp;
++ u8 reg;
++ int ret;
++
++#ifdef CONFIG_LOCKDEP
++ /* WORKAROUND for lockdep forcing IRQF_DISABLED on us, which
++ * we don't want and can't tolerate. Although it might be
++ * friendlier not to borrow this thread context...
++ */
++ local_irq_enable();
++#endif
++
++ /* Read & Clear TWL4030 pending interrupt */
++ ret = twl4030_kpread(kp, ®, KEYP_ISR1, 1);
++
++ /* Release all keys if I2C has gone bad or
++ * the KEYP has gone to idle state */
++ if (ret >= 0 && (reg & KEYP_IMR1_KP))
++ twl4030_kp_scan(kp, false);
++ else
++ twl4030_kp_scan(kp, true);
++
++ return IRQ_HANDLED;
++}
++
++static int __devinit twl4030_kp_program(struct twl4030_keypad *kp)
++{
++ u8 reg;
++ int i;
++
++ /* Enable controller, with hardware decoding but not autorepeat */
++ reg = KEYP_CTRL_SOFT_NRST | KEYP_CTRL_SOFTMODEN
++ | KEYP_CTRL_TOE_EN | KEYP_CTRL_KBD_ON;
++ if (twl4030_kpwrite_u8(kp, reg, KEYP_CTRL) < 0)
++ return -EIO;
++
++ /* NOTE: we could use sih_setup() here to package keypad
++ * event sources as four different IRQs ... but we don't.
++ */
++
++ /* Enable TO rising and KP rising and falling edge detection */
++ reg = KEYP_EDR_KP_BOTH | KEYP_EDR_TO_RISING;
++ if (twl4030_kpwrite_u8(kp, reg, KEYP_EDR) < 0)
++ return -EIO;
++
++ /* Set PTV prescaler Field */
++ reg = (PTV_PRESCALER << KEYP_LK_PTV_PTV_SHIFT);
++ if (twl4030_kpwrite_u8(kp, reg, KEYP_LK_PTV) < 0)
++ return -EIO;
++
++ /* Set key debounce time to 20 ms */
++ i = KEYP_PERIOD_US(20000, PTV_PRESCALER);
++ if (twl4030_kpwrite_u8(kp, i, KEYP_DEB) < 0)
++ return -EIO;
++
++ /* Set timeout period to 100 ms */
++ i = KEYP_PERIOD_US(200000, PTV_PRESCALER);
++ if (twl4030_kpwrite_u8(kp, (i & 0xFF), KEYP_TIMEOUT_L) < 0)
++ return -EIO;
++
++ if (twl4030_kpwrite_u8(kp, (i >> 8), KEYP_TIMEOUT_H) < 0)
++ return -EIO;
++
++ /*
++ * Enable Clear-on-Read; disable remembering events that fire
++ * after the IRQ but before our handler acks (reads) them,
++ */
++ reg = TWL4030_SIH_CTRL_COR_MASK | TWL4030_SIH_CTRL_PENDDIS_MASK;
++ if (twl4030_kpwrite_u8(kp, reg, KEYP_SIH_CTRL) < 0)
++ return -EIO;
++
++ /* initialize key state; irqs update it from here on */
++ if (twl4030_read_kp_matrix_state(kp, kp->kp_state) < 0)
++ return -EIO;
++
++ return 0;
++}
++
++/*
++ * Registers keypad device with input subsystem
++ * and configures TWL4030 keypad registers
++ */
++static int __devinit twl4030_kp_probe(struct platform_device *pdev)
++{
++ struct twl4030_keypad_data *pdata = pdev->dev.platform_data;
++ const struct matrix_keymap_data *keymap_data = pdata->keymap_data;
++ struct twl4030_keypad *kp;
++ struct input_dev *input;
++ u8 reg;
++ int error;
++
++ if (!pdata || !pdata->rows || !pdata->cols ||
++ pdata->rows > TWL4030_MAX_ROWS || pdata->cols > TWL4030_MAX_COLS) {
++ dev_err(&pdev->dev, "Invalid platform_data\n");
++ return -EINVAL;
++ }
++
++ kp = kzalloc(sizeof(*kp), GFP_KERNEL);
++ input = input_allocate_device();
++ if (!kp || !input) {
++ error = -ENOMEM;
++ goto err1;
++ }
++
++ /* Get the debug Device */
++ kp->dbg_dev = &pdev->dev;
++ kp->input = input;
++
++ kp->n_rows = pdata->rows;
++ kp->n_cols = pdata->cols;
++ kp->irq = platform_get_irq(pdev, 0);
++
++ /* setup input device */
++ __set_bit(EV_KEY, input->evbit);
++
++ /* Enable auto repeat feature of Linux input subsystem */
++ if (pdata->rep)
++ __set_bit(EV_REP, input->evbit);
++
++ input_set_capability(input, EV_MSC, MSC_SCAN);
++
++ input->name = "TWL4030 Keypad";
++ input->phys = "twl4030_keypad/input0";
++ input->dev.parent = &pdev->dev;
++
++ input->id.bustype = BUS_HOST;
++ input->id.vendor = 0x0001;
++ input->id.product = 0x0001;
++ input->id.version = 0x0003;
++
++ input->keycode = kp->keymap;
++ input->keycodesize = sizeof(kp->keymap[0]);
++ input->keycodemax = ARRAY_SIZE(kp->keymap);
++
++ matrix_keypad_build_keymap(keymap_data, TWL4030_ROW_SHIFT,
++ input->keycode, input->keybit);
++
++ error = input_register_device(input);
++ if (error) {
++ dev_err(kp->dbg_dev,
++ "Unable to register twl4030 keypad device\n");
++ goto err1;
++ }
++
++ error = twl4030_kp_program(kp);
++ if (error)
++ goto err2;
++
++ /*
++ * This ISR will always execute in kernel thread context because of
++ * the need to access the TWL4030 over the I2C bus.
++ *
++ * NOTE: we assume this host is wired to TWL4040 INT1, not INT2 ...
++ */
++ error = request_irq(kp->irq, do_kp_irq, 0, pdev->name, kp);
++ if (error) {
++ dev_info(kp->dbg_dev, "request_irq failed for irq no=%d\n",
++ kp->irq);
++ goto err3;
++ }
++
++ /* Enable KP and TO interrupts now. */
++ reg = (u8) ~(KEYP_IMR1_KP | KEYP_IMR1_TO);
++ if (twl4030_kpwrite_u8(kp, reg, KEYP_IMR1)) {
++ error = -EIO;
++ goto err4;
++ }
++
++ platform_set_drvdata(pdev, kp);
++ return 0;
++
++err4:
++ /* mask all events - we don't care about the result */
++ (void) twl4030_kpwrite_u8(kp, 0xff, KEYP_IMR1);
++err3:
++ free_irq(kp->irq, NULL);
++err2:
++ input_unregister_device(input);
++ input = NULL;
++err1:
++ input_free_device(input);
++ kfree(kp);
++ return error;
++}
++
++static int __devexit twl4030_kp_remove(struct platform_device *pdev)
++{
++ struct twl4030_keypad *kp = platform_get_drvdata(pdev);
++
++ free_irq(kp->irq, kp);
++ input_unregister_device(kp->input);
++ platform_set_drvdata(pdev, NULL);
++ kfree(kp);
++
++ return 0;
++}
++
++/*
++ * NOTE: twl4030 are multi-function devices connected via I2C.
++ * So this device is a child of an I2C parent, thus it needs to
++ * support unplug/replug (which most platform devices don't).
++ */
++
++static struct platform_driver twl4030_kp_driver = {
++ .probe = twl4030_kp_probe,
++ .remove = __devexit_p(twl4030_kp_remove),
++ .driver = {
++ .name = "twl4030_keypad",
++ .owner = THIS_MODULE,
++ },
++};
++
++static int __init twl4030_kp_init(void)
++{
++ return platform_driver_register(&twl4030_kp_driver);
++}
++module_init(twl4030_kp_init);
++
++static void __exit twl4030_kp_exit(void)
++{
++ platform_driver_unregister(&twl4030_kp_driver);
++}
++module_exit(twl4030_kp_exit);
++
++MODULE_AUTHOR("Texas Instruments");
++MODULE_DESCRIPTION("TWL4030 Keypad Driver");
++MODULE_LICENSE("GPL");
++MODULE_ALIAS("platform:twl4030_keypad");
++
+diff --git a/include/linux/i2c/twl4030.h b/include/linux/i2c/twl4030.h
+index dc40f95..3d6321e 100644
+--- a/include/linux/i2c/twl4030.h
++++ b/include/linux/i2c/twl4030.h
+@@ -25,6 +25,9 @@
+ #ifndef __TWL4030_H_
+ #define __TWL4030_H_
+
++#include <linux/types.h>
++#include <linux/input/matrix_keypad.h>
++
+ /*
+ * Using the twl4030 core we address registers using a pair
+ * { module id, relative register offset }
+@@ -254,13 +257,17 @@ struct twl4030_madc_platform_data {
+ int irq_line;
+ };
+
++/* Boards have uniqe mappings of {col, row} --> keycode.
++ * Column and row are 4 bits, but range only from 0..7.
++ * a PERSISTENT_KEY is "always on" and never reported.
++ */
++#define PERSISTENT_KEY(c, r) KEY((c), (r), KEY_RESERVED)
++
+ struct twl4030_keypad_data {
+- int rows;
+- int cols;
+- int *keymap;
+- int irq;
+- unsigned int keymapsize;
+- unsigned int rep:1;
++ const struct matrix_keymap_data *keymap_data;
++ unsigned rows;
++ unsigned cols;
++ bool rep;
+ };
+
+ enum twl4030_usb_mode {
+--
+1.6.3.1
+
--- /dev/null
+From 9416a510c6939bc6da62cf12de665fd11b89a9f6 Mon Sep 17 00:00:00 2001
+From: Grazvydas Ignotas <notasas@gmail.com>
+Date: Thu, 5 Nov 2009 12:44:01 +0200
+Subject: [PATCH 6/7] input: hacks+updates for mainline twl4030 driver
+
+This includes some differing #includes and col/row swap
+in the KEY() macro.
+---
+ arch/arm/mach-omap2/board-omap3pandora-input.c | 89 ++++++++++++-----------
+ drivers/input/keyboard/twl4030_keypad.c | 1 +
+ drivers/mfd/twl4030-core.c | 8 ++
+ include/linux/i2c/twl4030.h | 3 -
+ 4 files changed, 55 insertions(+), 46 deletions(-)
+
+diff --git a/arch/arm/mach-omap2/board-omap3pandora-input.c b/arch/arm/mach-omap2/board-omap3pandora-input.c
+index 6ca69ce..ce9df0e 100644
+--- a/arch/arm/mach-omap2/board-omap3pandora-input.c
++++ b/arch/arm/mach-omap2/board-omap3pandora-input.c
+@@ -23,68 +23,71 @@
+ #include <linux/platform_device.h>
+ #include <linux/input.h>
+ #include <linux/gpio_keys.h>
++#include <linux/input/matrix_keypad.h>
+
+ #include <mach/hardware.h>
+-#include <mach/keypad.h>
+ #include <mach/gpio.h>
+
+ /* hardware debounce, (value + 1) * 31us */
+ #define GPIO_DEBOUNCE_TIME 0x7f
+
+ static int omap3pandora_keymap[] = {
+- /* col, row, code */
++ /* row, col, code */
+ KEY(0, 0, KEY_9),
+- KEY(0, 1, KEY_0),
+- KEY(0, 2, KEY_BACKSPACE),
+- KEY(0, 3, KEY_O),
+- KEY(0, 4, KEY_P),
+- KEY(0, 5, KEY_K),
+- KEY(0, 6, KEY_L),
+- KEY(0, 7, KEY_ENTER),
+- KEY(1, 0, KEY_8),
++ KEY(0, 1, KEY_8),
++ KEY(0, 2, KEY_I),
++ KEY(0, 3, KEY_J),
++ KEY(0, 4, KEY_N),
++ KEY(0, 5, KEY_M),
++ KEY(1, 0, KEY_0),
+ KEY(1, 1, KEY_7),
+- KEY(1, 2, KEY_6),
+- KEY(1, 3, KEY_5),
+- KEY(1, 4, KEY_4),
+- KEY(1, 5, KEY_3),
+- KEY(1, 6, KEY_2),
+- KEY(1, 7, KEY_1),
+- KEY(2, 0, KEY_I),
+- KEY(2, 1, KEY_U),
++ KEY(1, 2, KEY_U),
++ KEY(1, 3, KEY_H),
++ KEY(1, 4, KEY_B),
++ KEY(1, 5, KEY_SPACE),
++ KEY(2, 0, KEY_BACKSPACE),
++ KEY(2, 1, KEY_6),
+ KEY(2, 2, KEY_Y),
+- KEY(2, 3, KEY_T),
+- KEY(2, 4, KEY_R),
+- KEY(2, 5, KEY_E),
+- KEY(2, 6, KEY_W),
+- KEY(2, 7, KEY_Q),
+- KEY(3, 0, KEY_J),
+- KEY(3, 1, KEY_H),
+- KEY(3, 2, KEY_G),
++ KEY(2, 3, KEY_G),
++ KEY(2, 4, KEY_V),
++ KEY(2, 5, KEY_FN),
++ KEY(3, 0, KEY_O),
++ KEY(3, 1, KEY_5),
++ KEY(3, 2, KEY_T),
+ KEY(3, 3, KEY_F),
+- KEY(3, 4, KEY_D),
+- KEY(3, 5, KEY_S),
+- KEY(3, 6, KEY_A),
+- KEY(3, 7, KEY_LEFTSHIFT),
+- KEY(4, 0, KEY_N),
+- KEY(4, 1, KEY_B),
+- KEY(4, 2, KEY_V),
+- KEY(4, 3, KEY_C),
++ KEY(3, 4, KEY_C),
++ KEY(4, 0, KEY_P),
++ KEY(4, 1, KEY_4),
++ KEY(4, 2, KEY_R),
++ KEY(4, 3, KEY_D),
+ KEY(4, 4, KEY_X),
+- KEY(4, 5, KEY_Z),
+- KEY(4, 6, KEY_DOT),
+- KEY(4, 7, KEY_COMMA),
+- KEY(5, 0, KEY_M),
+- KEY(5, 1, KEY_SPACE),
+- KEY(5, 2, KEY_FN),
++ KEY(5, 0, KEY_K),
++ KEY(5, 1, KEY_3),
++ KEY(5, 2, KEY_E),
++ KEY(5, 3, KEY_S),
++ KEY(5, 4, KEY_Z),
++ KEY(6, 0, KEY_L),
++ KEY(6, 1, KEY_2),
++ KEY(6, 2, KEY_W),
++ KEY(6, 3, KEY_A),
++ KEY(6, 4, KEY_DOT),
++ KEY(7, 0, KEY_ENTER),
++ KEY(7, 1, KEY_1),
++ KEY(7, 2, KEY_Q),
++ KEY(7, 3, KEY_LEFTSHIFT),
++ KEY(7, 4, KEY_COMMA),
++};
++
++static struct matrix_keymap_data board_map_data = {
++ .keymap = omap3pandora_keymap,
++ .keymap_size = ARRAY_SIZE(omap3pandora_keymap),
+ };
+
+ struct twl4030_keypad_data omap3pandora_kp_data = {
++ .keymap_data = &board_map_data,
+ .rows = 8,
+ .cols = 6,
+- .keymap = omap3pandora_keymap,
+- .keymapsize = ARRAY_SIZE(omap3pandora_keymap),
+ .rep = 1,
+- .irq = TWL4030_MODIRQ_KEYPAD,
+ };
+
+ static struct gpio_keys_button gpio_buttons[] = {
+diff --git a/drivers/input/keyboard/twl4030_keypad.c b/drivers/input/keyboard/twl4030_keypad.c
+index 9a2977c..99bb58d 100644
+--- a/drivers/input/keyboard/twl4030_keypad.c
++++ b/drivers/input/keyboard/twl4030_keypad.c
+@@ -32,6 +32,7 @@
+ #include <linux/input.h>
+ #include <linux/platform_device.h>
+ #include <linux/i2c/twl4030.h>
++#include <linux/input/matrix_keypad.h>
+
+
+ /*
+diff --git a/drivers/mfd/twl4030-core.c b/drivers/mfd/twl4030-core.c
+index dd843c4..4c502d7 100644
+--- a/drivers/mfd/twl4030-core.c
++++ b/drivers/mfd/twl4030-core.c
+@@ -468,7 +468,15 @@ static int add_children(struct twl4030_platform_data *pdata)
+ status);
+ platform_device_put(pdev);
+ goto err;
++ } else {
++ struct resource r = {
++ .start = pdata->irq_base + 1,
++ .flags = IORESOURCE_IRQ,
++ };
++
++ status = platform_device_add_resources(pdev, &r, 1);
+ }
++
+ status = platform_device_add(pdev);
+ if (status < 0) {
+ platform_device_put(pdev);
+diff --git a/include/linux/i2c/twl4030.h b/include/linux/i2c/twl4030.h
+index 3d6321e..4a31016 100644
+--- a/include/linux/i2c/twl4030.h
++++ b/include/linux/i2c/twl4030.h
+@@ -25,9 +25,6 @@
+ #ifndef __TWL4030_H_
+ #define __TWL4030_H_
+
+-#include <linux/types.h>
+-#include <linux/input/matrix_keypad.h>
+-
+ /*
+ * Using the twl4030 core we address registers using a pair
+ * { module id, relative register offset }
+--
+1.6.3.1
+
--- /dev/null
+From fc359f9dd7efd346c070950320dda62ae671519f Mon Sep 17 00:00:00 2001
+From: Grazvydas Ignotas <notasas@gmail.com>
+Date: Thu, 5 Nov 2009 12:52:23 +0200
+Subject: [PATCH 7/7] some hackish Fn handling for testing
+
+---
+ arch/arm/mach-omap2/board-omap3pandora-input.c | 47 ++++++++++++++++++++++++
+ drivers/input/keyboard/twl4030_keypad.c | 39 +++++++++++++++++---
+ 2 files changed, 81 insertions(+), 5 deletions(-)
+
+diff --git a/arch/arm/mach-omap2/board-omap3pandora-input.c b/arch/arm/mach-omap2/board-omap3pandora-input.c
+index ce9df0e..3d530cf 100644
+--- a/arch/arm/mach-omap2/board-omap3pandora-input.c
++++ b/arch/arm/mach-omap2/board-omap3pandora-input.c
+@@ -31,6 +31,9 @@
+ /* hardware debounce, (value + 1) * 31us */
+ #define GPIO_DEBOUNCE_TIME 0x7f
+
++/* HACK: this requires patched twl4030_keypad driver */
++#define FNKEY(row, col, code) KEY((row + 8), col, code)
++
+ static int omap3pandora_keymap[] = {
+ /* row, col, code */
+ KEY(0, 0, KEY_9),
+@@ -76,6 +79,50 @@ static int omap3pandora_keymap[] = {
+ KEY(7, 2, KEY_Q),
+ KEY(7, 3, KEY_LEFTSHIFT),
+ KEY(7, 4, KEY_COMMA),
++ /* Fn keys */
++ FNKEY(0, 0, KEY_F9),
++ FNKEY(0, 1, KEY_F8),
++ FNKEY(0, 2, KEY_BRIGHTNESSUP),
++ FNKEY(0, 3, KEY_F13), /* apostrophe, differs from Fn-A? */
++ FNKEY(0, 4, KEY_DOLLAR),
++ FNKEY(0, 5, KEY_EURO),
++ FNKEY(1, 0, KEY_F10),
++ FNKEY(1, 1, KEY_F7),
++ FNKEY(1, 2, KEY_BRIGHTNESSDOWN),
++ FNKEY(1, 3, KEY_GRAVE),
++ FNKEY(1, 4, KEY_F14), /* pipe/bar */
++ FNKEY(1, 5, KEY_TAB),
++ FNKEY(2, 0, KEY_INSERT),
++ FNKEY(2, 1, KEY_F6),
++ FNKEY(2, 2, KEY_F15), /* dash */
++ FNKEY(2, 3, KEY_EQUAL),
++ FNKEY(2, 4, KEY_F16), /* # (pound/hash) */
++ FNKEY(2, 5, KEY_FN),
++ FNKEY(3, 0, KEY_F11),
++ FNKEY(3, 1, KEY_F5),
++ FNKEY(3, 2, KEY_F17), /* ! */
++ FNKEY(3, 3, KEY_KPPLUS),
++ FNKEY(3, 4, KEY_BACKSLASH),
++ FNKEY(4, 0, KEY_F12),
++ FNKEY(4, 1, KEY_F4),
++ FNKEY(4, 2, KEY_RIGHTBRACE),
++ FNKEY(4, 3, KEY_KPMINUS),
++ FNKEY(4, 4, KEY_QUESTION),
++ FNKEY(5, 0, KEY_F18), /* £ (pound) */
++ FNKEY(5, 1, KEY_F3),
++ FNKEY(5, 2, KEY_LEFTBRACE),
++ FNKEY(5, 3, KEY_F19), /* " */
++ FNKEY(5, 4, KEY_SLASH),
++ FNKEY(6, 0, KEY_YEN),
++ FNKEY(6, 1, KEY_F2),
++ FNKEY(6, 2, KEY_F20), /* @ */
++ FNKEY(6, 3, KEY_APOSTROPHE),
++ FNKEY(6, 4, KEY_F21), /* : */
++ FNKEY(7, 0, KEY_ENTER),
++ FNKEY(7, 1, KEY_F1),
++ FNKEY(7, 2, KEY_ESC),
++ FNKEY(7, 3, KEY_CAPSLOCK),
++ FNKEY(7, 4, KEY_SEMICOLON),
+ };
+
+ static struct matrix_keymap_data board_map_data = {
+diff --git a/drivers/input/keyboard/twl4030_keypad.c b/drivers/input/keyboard/twl4030_keypad.c
+index 99bb58d..74485d6 100644
+--- a/drivers/input/keyboard/twl4030_keypad.c
++++ b/drivers/input/keyboard/twl4030_keypad.c
+@@ -52,7 +52,7 @@
+ #define TWL4030_MAX_ROWS 8 /* TWL4030 hard limit */
+ #define TWL4030_MAX_COLS 8
+ #define TWL4030_ROW_SHIFT 3
+-#define TWL4030_KEYMAP_SIZE (TWL4030_MAX_ROWS * TWL4030_MAX_COLS)
++#define TWL4030_KEYMAP_SIZE (TWL4030_MAX_ROWS * 2 * TWL4030_MAX_COLS)
+
+ struct twl4030_keypad {
+ unsigned short keymap[TWL4030_KEYMAP_SIZE];
+@@ -61,6 +61,9 @@ struct twl4030_keypad {
+ unsigned n_cols;
+ unsigned irq;
+
++ unsigned fn_down:1;
++ unsigned fn_sticked:1;
++
+ struct device *dbg_dev;
+ struct input_dev *input;
+ };
+@@ -226,7 +229,8 @@ static void twl4030_kp_scan(struct twl4030_keypad *kp, bool release_all)
+ continue;
+
+ for (col = 0; col < kp->n_cols; col++) {
+- int code;
++ int code, kcode, is_down;
++ int code2;
+
+ if (!(changed & (1 << col)))
+ continue;
+@@ -236,9 +240,34 @@ static void twl4030_kp_scan(struct twl4030_keypad *kp, bool release_all)
+ "press" : "release");
+
+ code = MATRIX_SCAN_CODE(row, col, TWL4030_ROW_SHIFT);
++ kcode = kp->keymap[code];
++ is_down = (new_state[row] & (1 << col)) ? 1 : 0;
++
++ dev_dbg(kp->dbg_dev, "code: %d %d\n", code, kcode);
++ /* Fn handling */
++ if (kcode == KEY_FN) {
++ kp->fn_down = is_down;
++ kp->fn_sticked |= is_down;
++ } else if (kp->fn_down || kp->fn_sticked) {
++ /* make sure other function is up */
++ input_event(input, EV_MSC, MSC_SCAN, code);
++ input_report_key(input, kcode, 0);
++
++ code = MATRIX_SCAN_CODE(row + TWL4030_MAX_ROWS,
++ col, TWL4030_ROW_SHIFT);
++ kcode = kp->keymap[code];
++
++ kp->fn_sticked = 0;
++ } else {
++ code2 = MATRIX_SCAN_CODE(row + TWL4030_MAX_ROWS,
++ col, TWL4030_ROW_SHIFT);
++ input_event(input, EV_MSC, MSC_SCAN, code2);
++ input_report_key(input, kp->keymap[code2], 0);
++ }
++
++ dev_dbg(kp->dbg_dev, "code(fn): %d %d\n", code, kcode);
+ input_event(input, EV_MSC, MSC_SCAN, code);
+- input_report_key(input, kp->keymap[code],
+- new_state[row] & (1 << col));
++ input_report_key(input, kcode, is_down);
+ }
+ kp->kp_state[row] = new_state[row];
+ }
+@@ -371,7 +400,7 @@ static int __devinit twl4030_kp_probe(struct platform_device *pdev)
+
+ input_set_capability(input, EV_MSC, MSC_SCAN);
+
+- input->name = "TWL4030 Keypad";
++ input->name = "omap_twl4030keypad";
+ input->phys = "twl4030_keypad/input0";
+ input->dev.parent = &pdev->dev;
+
+--
+1.6.3.1
+
COMPATIBLE_MACHINE = "omap3-pandora"
-# ----- Revision 2 Boards -----
-
-#GIT HEAD for Rev2 - Same as Rev3 without Batt Fuel Guage and Nub/Button changes.
-
-#SRCREV = "45dce3afbd5c2d2af899ae7e7a107d01a5c15558"
-
-#SRC_URI = " \
-# git://git.openpandora.org/pandora-kernel.git;protocol=git;branch=rev2 \
-#"
-
-# -----------------------------
-
-
-# ----- Revision 3 > Boards -----
-
-#GIT HEAD for Rev3 > (i.e. Shipping units) - Will run on Rev2 boards without Nubs and an incorrect Start button.
-
SRCREV = "b7075725f109114e144c5534cd519ffa2b9780d9"
SRC_URI = " \
file://0002-Add-a-very-basic-platform-driver-module-to-bring-up-.patch;patch=1 \
file://0003-Remove-old-msm_wifi-hack-as-the-temp-platform-driver.patch;patch=1 \
"
+
+# AUFS2 Patches - Used by ausf2-27 recipe to build as a module.
+
+SRC_URI_append = " \
+ file://aufs2/aufs2-base.patch;patch=1 \
+ file://aufs2/aufs2-standalone.patch;patch=1 \
+"
+
+# Temp Keypad Patches for FN.
+
+SRC_URI_append = " \
+ file://keypad/0001-input-remove-old-twl4030keypad-to-replace-it-with-ma.patch;patch=1 \
+ file://keypad/0002-Input-add-support-for-generic-GPIO-based-matrix-keyp.patch;patch=1 \
+ file://keypad/0003-Input-matrix_keypad-make-matrix-keymap-size-dynamic.patch;patch=1 \
+ file://keypad/0004-Input-matrix-keypad-add-function-to-build-device-key.patch;patch=1 \
+ file://keypad/0005-Input-add-twl4030_keypad-driver.patch;patch=1 \
+ file://keypad/0006-input-hacks-updates-for-mainline-twl4030-driver.patch;patch=1 \
+ file://keypad/0007-some-hackish-Fn-handling-for-testing.patch;patch=1 \
+"
S = "${WORKDIR}/git"