aboutsummaryrefslogtreecommitdiff
path: root/drivers
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2011-05-26 12:14:20 -0700
committerLinus Torvalds <torvalds@linux-foundation.org>2011-05-26 12:14:20 -0700
commit9f1912c48ce829d24789e3e5d499de0d44d3306a (patch)
tree056ca04727d478f74b20d8af5729e0776a942cca /drivers
parent4c171acc20794af16a27da25e11ec4e9cad5d9fa (diff)
parent099691081df40d8863cb2fb01ee64039633892dd (diff)
Merge branch 'for-next' of git://git.kernel.org/pub/scm/linux/kernel/git/sameo/mfd-2.6
* 'for-next' of git://git.kernel.org/pub/scm/linux/kernel/git/sameo/mfd-2.6: (57 commits) regulator: Fix 88pm8607.c printk format warning input: Add support for Qualcomm PMIC8XXX power key input: Add Qualcomm pm8xxx keypad controller driver mfd: Add omap-usbhs runtime PM support mfd: Fix ASIC3 SD Host Controller Configuration size mfd: Fix omap_usbhs_alloc_children error handling mfd: Fix omap usbhs crash when rmmoding ehci or ohci mfd: Add ASIC3 LED support leds: Add ASIC3 LED support mfd: Update twl4030-code maintainer e-mail address mfd: Correct the name and bitmask for ab8500-gpadc BTempPullUp mfd: Add manual ab8500-gpadc batt temp activation for AB8500 3.0 mfd: Provide ab8500-core enumerators for chip cuts mfd: Check twl4030-power remove script error condition after i2cwrite mfd: Fix twl6030 irq definitions mfd: Add phoenix lite (twl6025) support to twl6030 mfd: Avoid to use constraint name in 88pm860x regulator driver mfd: Remove checking on max8925 regulator[0] mfd: Remove unused parameter from 88pm860x API mfd: Avoid to allocate 88pm860x static platform data ...
Diffstat (limited to 'drivers')
-rw-r--r--drivers/dma/timb_dma.c3
-rw-r--r--drivers/gpio/janz-ttl.c3
-rw-r--r--drivers/gpio/rdc321x-gpio.c3
-rw-r--r--drivers/gpio/timbgpio.c6
-rw-r--r--drivers/i2c/busses/i2c-ocores.c3
-rw-r--r--drivers/i2c/busses/i2c-xiic.c3
-rw-r--r--drivers/input/keyboard/Kconfig11
-rw-r--r--drivers/input/keyboard/Makefile1
-rw-r--r--drivers/input/keyboard/pmic8xxx-keypad.c799
-rw-r--r--drivers/input/misc/Kconfig11
-rw-r--r--drivers/input/misc/Makefile1
-rw-r--r--drivers/input/misc/pmic8xxx-pwrkey.c231
-rw-r--r--drivers/input/misc/twl4030-vibra.c3
-rw-r--r--drivers/leds/Kconfig10
-rw-r--r--drivers/leds/Makefile1
-rw-r--r--drivers/leds/leds-88pm860x.c7
-rw-r--r--drivers/leds/leds-asic3.c165
-rw-r--r--drivers/leds/leds-mc13783.c7
-rw-r--r--drivers/media/radio/radio-timb.c3
-rw-r--r--drivers/media/radio/radio-wl1273.c2
-rw-r--r--drivers/media/video/timblogiw.c3
-rw-r--r--drivers/mfd/88pm860x-core.c155
-rw-r--r--drivers/mfd/Kconfig84
-rw-r--r--drivers/mfd/Makefile2
-rw-r--r--drivers/mfd/ab3100-core.c6
-rw-r--r--drivers/mfd/ab3550-core.c6
-rw-r--r--drivers/mfd/ab8500-core.c32
-rw-r--r--drivers/mfd/ab8500-gpadc.c34
-rw-r--r--drivers/mfd/asic3.c83
-rw-r--r--drivers/mfd/davinci_voicecodec.c6
-rw-r--r--drivers/mfd/htc-pasic3.c5
-rw-r--r--drivers/mfd/janz-cmodio.c3
-rw-r--r--drivers/mfd/max8925-core.c2
-rw-r--r--drivers/mfd/mc13xxx-core.c12
-rw-r--r--drivers/mfd/mfd-core.c7
-rw-r--r--drivers/mfd/omap-usb-host.c152
-rw-r--r--drivers/mfd/pm8921-core.c212
-rw-r--r--drivers/mfd/pm8xxx-irq.c371
-rw-r--r--drivers/mfd/rdc321x-southbridge.c6
-rw-r--r--drivers/mfd/t7l66xb.c6
-rw-r--r--drivers/mfd/tc6387xb.c3
-rw-r--r--drivers/mfd/tc6393xb.c10
-rw-r--r--drivers/mfd/timberdale.c81
-rw-r--r--drivers/mfd/tps6105x.c3
-rw-r--r--drivers/mfd/tps6586x.c4
-rw-r--r--drivers/mfd/twl-core.c252
-rw-r--r--drivers/mfd/twl4030-codec.c10
-rw-r--r--drivers/mfd/twl4030-power.c6
-rw-r--r--drivers/mfd/twl6030-irq.c4
-rw-r--r--drivers/mfd/wl1273-core.c7
-rw-r--r--drivers/mfd/wm831x-core.c13
-rw-r--r--drivers/mfd/wm831x-irq.c27
-rw-r--r--drivers/mfd/wm8400-core.c3
-rw-r--r--drivers/mmc/host/tmio_mmc.c2
-rw-r--r--drivers/mtd/nand/tmio_nand.c2
-rw-r--r--drivers/net/can/janz-ican3.c3
-rw-r--r--drivers/net/ks8842.c3
-rw-r--r--drivers/power/max8925_power.c10
-rw-r--r--drivers/regulator/88pm8607.c30
-rw-r--r--drivers/regulator/ab3100.c3
-rw-r--r--drivers/regulator/db8500-prcmu.c4
-rw-r--r--drivers/regulator/max8925-regulator.c11
-rw-r--r--drivers/regulator/mc13783-regulator.c7
-rw-r--r--drivers/regulator/mc13892-regulator.c7
-rw-r--r--drivers/regulator/tps6105x-regulator.c4
-rw-r--r--drivers/rtc/Kconfig10
-rw-r--r--drivers/rtc/Makefile1
-rw-r--r--drivers/rtc/rtc-88pm860x.c427
-rw-r--r--drivers/spi/xilinx_spi.c3
-rw-r--r--drivers/video/backlight/88pm860x_bl.c7
-rw-r--r--drivers/video/tmiofb.c12
-rw-r--r--drivers/w1/masters/ds1wm.c12
-rw-r--r--drivers/watchdog/rdc321x_wdt.c3
73 files changed, 2973 insertions, 471 deletions
diff --git a/drivers/dma/timb_dma.c b/drivers/dma/timb_dma.c
index d2c75feff7d..f69f90a6187 100644
--- a/drivers/dma/timb_dma.c
+++ b/drivers/dma/timb_dma.c
@@ -27,7 +27,6 @@
#include <linux/io.h>
#include <linux/module.h>
#include <linux/platform_device.h>
-#include <linux/mfd/core.h>
#include <linux/slab.h>
#include <linux/timb_dma.h>
@@ -685,7 +684,7 @@ static irqreturn_t td_irq(int irq, void *devid)
static int __devinit td_probe(struct platform_device *pdev)
{
- struct timb_dma_platform_data *pdata = mfd_get_data(pdev);
+ struct timb_dma_platform_data *pdata = pdev->dev.platform_data;
struct timb_dma *td;
struct resource *iomem;
int irq;
diff --git a/drivers/gpio/janz-ttl.c b/drivers/gpio/janz-ttl.c
index 2514fb075f4..813ac077e5d 100644
--- a/drivers/gpio/janz-ttl.c
+++ b/drivers/gpio/janz-ttl.c
@@ -15,7 +15,6 @@
#include <linux/interrupt.h>
#include <linux/delay.h>
#include <linux/platform_device.h>
-#include <linux/mfd/core.h>
#include <linux/io.h>
#include <linux/gpio.h>
#include <linux/slab.h>
@@ -150,7 +149,7 @@ static int __devinit ttl_probe(struct platform_device *pdev)
struct resource *res;
int ret;
- pdata = mfd_get_data(pdev);
+ pdata = pdev->dev.platform_data;
if (!pdata) {
dev_err(dev, "no platform data\n");
ret = -ENXIO;
diff --git a/drivers/gpio/rdc321x-gpio.c b/drivers/gpio/rdc321x-gpio.c
index a9bda881935..2762698e020 100644
--- a/drivers/gpio/rdc321x-gpio.c
+++ b/drivers/gpio/rdc321x-gpio.c
@@ -27,7 +27,6 @@
#include <linux/pci.h>
#include <linux/gpio.h>
#include <linux/mfd/rdc321x.h>
-#include <linux/mfd/core.h>
#include <linux/slab.h>
struct rdc321x_gpio {
@@ -136,7 +135,7 @@ static int __devinit rdc321x_gpio_probe(struct platform_device *pdev)
struct rdc321x_gpio *rdc321x_gpio_dev;
struct rdc321x_gpio_pdata *pdata;
- pdata = mfd_get_data(pdev);
+ pdata = pdev->dev.platform_data;
if (!pdata) {
dev_err(&pdev->dev, "no platform data supplied\n");
return -ENODEV;
diff --git a/drivers/gpio/timbgpio.c b/drivers/gpio/timbgpio.c
index edbe1eae531..0265872e57d 100644
--- a/drivers/gpio/timbgpio.c
+++ b/drivers/gpio/timbgpio.c
@@ -23,7 +23,6 @@
#include <linux/module.h>
#include <linux/gpio.h>
#include <linux/platform_device.h>
-#include <linux/mfd/core.h>
#include <linux/irq.h>
#include <linux/io.h>
#include <linux/timb_gpio.h>
@@ -229,7 +228,7 @@ static int __devinit timbgpio_probe(struct platform_device *pdev)
struct gpio_chip *gc;
struct timbgpio *tgpio;
struct resource *iomem;
- struct timbgpio_platform_data *pdata = mfd_get_data(pdev);
+ struct timbgpio_platform_data *pdata = pdev->dev.platform_data;
int irq = platform_get_irq(pdev, 0);
if (!pdata || pdata->nr_pins > 32) {
@@ -320,13 +319,14 @@ err_mem:
static int __devexit timbgpio_remove(struct platform_device *pdev)
{
int err;
+ struct timbgpio_platform_data *pdata = pdev->dev.platform_data;
struct timbgpio *tgpio = platform_get_drvdata(pdev);
struct resource *iomem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
int irq = platform_get_irq(pdev, 0);
if (irq >= 0 && tgpio->irq_base > 0) {
int i;
- for (i = 0; i < tgpio->gpio.ngpio; i++) {
+ for (i = 0; i < pdata->nr_pins; i++) {
irq_set_chip(tgpio->irq_base + i, NULL);
irq_set_chip_data(tgpio->irq_base + i, NULL);
}
diff --git a/drivers/i2c/busses/i2c-ocores.c b/drivers/i2c/busses/i2c-ocores.c
index fee1a261386..1b46a9d9f90 100644
--- a/drivers/i2c/busses/i2c-ocores.c
+++ b/drivers/i2c/busses/i2c-ocores.c
@@ -49,7 +49,6 @@
#include <linux/init.h>
#include <linux/errno.h>
#include <linux/platform_device.h>
-#include <linux/mfd/core.h>
#include <linux/i2c.h>
#include <linux/interrupt.h>
#include <linux/wait.h>
@@ -306,7 +305,7 @@ static int __devinit ocores_i2c_probe(struct platform_device *pdev)
return -EIO;
}
- pdata = mfd_get_data(pdev);
+ pdata = pdev->dev.platform_data;
if (pdata) {
i2c->regstep = pdata->regstep;
i2c->clock_khz = pdata->clock_khz;
diff --git a/drivers/i2c/busses/i2c-xiic.c b/drivers/i2c/busses/i2c-xiic.c
index e9d5ff4d149..4bb68f35caf 100644
--- a/drivers/i2c/busses/i2c-xiic.c
+++ b/drivers/i2c/busses/i2c-xiic.c
@@ -34,7 +34,6 @@
#include <linux/errno.h>
#include <linux/delay.h>
#include <linux/platform_device.h>
-#include <linux/mfd/core.h>
#include <linux/i2c.h>
#include <linux/interrupt.h>
#include <linux/wait.h>
@@ -705,7 +704,7 @@ static int __devinit xiic_i2c_probe(struct platform_device *pdev)
if (irq < 0)
goto resource_missing;
- pdata = mfd_get_data(pdev);
+ pdata = (struct xiic_i2c_platform_data *) pdev->dev.platform_data;
if (!pdata)
return -EINVAL;
diff --git a/drivers/input/keyboard/Kconfig b/drivers/input/keyboard/Kconfig
index 69badb4e06a..b4dee9d5a05 100644
--- a/drivers/input/keyboard/Kconfig
+++ b/drivers/input/keyboard/Kconfig
@@ -412,6 +412,17 @@ config KEYBOARD_PXA930_ROTARY
To compile this driver as a module, choose M here: the
module will be called pxa930_rotary.
+config KEYBOARD_PMIC8XXX
+ tristate "Qualcomm PMIC8XXX keypad support"
+ depends on MFD_PM8XXX
+ help
+ Say Y here if you want to enable the driver for the PMIC8XXX
+ keypad provided as a reference design from Qualcomm. This is intended
+ to support upto 18x8 matrix based keypad design.
+
+ To compile this driver as a module, choose M here: the module will
+ be called pmic8xxx-keypad.
+
config KEYBOARD_SAMSUNG
tristate "Samsung keypad support"
depends on SAMSUNG_DEV_KEYPAD
diff --git a/drivers/input/keyboard/Makefile b/drivers/input/keyboard/Makefile
index c49cf8e04cd..ddde0fd476f 100644
--- a/drivers/input/keyboard/Makefile
+++ b/drivers/input/keyboard/Makefile
@@ -34,6 +34,7 @@ obj-$(CONFIG_KEYBOARD_NOMADIK) += nomadik-ske-keypad.o
obj-$(CONFIG_KEYBOARD_OMAP) += omap-keypad.o
obj-$(CONFIG_KEYBOARD_OMAP4) += omap4-keypad.o
obj-$(CONFIG_KEYBOARD_OPENCORES) += opencores-kbd.o
+obj-$(CONFIG_KEYBOARD_PMIC8XXX) += pmic8xxx-keypad.o
obj-$(CONFIG_KEYBOARD_PXA27x) += pxa27x_keypad.o
obj-$(CONFIG_KEYBOARD_PXA930_ROTARY) += pxa930_rotary.o
obj-$(CONFIG_KEYBOARD_QT1070) += qt1070.o
diff --git a/drivers/input/keyboard/pmic8xxx-keypad.c b/drivers/input/keyboard/pmic8xxx-keypad.c
new file mode 100644
index 00000000000..40b02ae96f8
--- /dev/null
+++ b/drivers/input/keyboard/pmic8xxx-keypad.c
@@ -0,0 +1,799 @@
+/* Copyright (c) 2009-2011, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * 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.
+ */
+
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/kernel.h>
+#include <linux/interrupt.h>
+#include <linux/slab.h>
+#include <linux/input.h>
+#include <linux/bitops.h>
+#include <linux/delay.h>
+#include <linux/mutex.h>
+
+#include <linux/mfd/pm8xxx/core.h>
+#include <linux/mfd/pm8xxx/gpio.h>
+#include <linux/input/pmic8xxx-keypad.h>
+
+#define PM8XXX_MAX_ROWS 18
+#define PM8XXX_MAX_COLS 8
+#define PM8XXX_ROW_SHIFT 3
+#define PM8XXX_MATRIX_MAX_SIZE (PM8XXX_MAX_ROWS * PM8XXX_MAX_COLS)
+
+#define PM8XXX_MIN_ROWS 5
+#define PM8XXX_MIN_COLS 5
+
+#define MAX_SCAN_DELAY 128
+#define MIN_SCAN_DELAY 1
+
+/* in nanoseconds */
+#define MAX_ROW_HOLD_DELAY 122000
+#define MIN_ROW_HOLD_DELAY 30500
+
+#define MAX_DEBOUNCE_TIME 20
+#define MIN_DEBOUNCE_TIME 5
+
+#define KEYP_CTRL 0x148
+
+#define KEYP_CTRL_EVNTS BIT(0)
+#define KEYP_CTRL_EVNTS_MASK 0x3
+
+#define KEYP_CTRL_SCAN_COLS_SHIFT 5
+#define KEYP_CTRL_SCAN_COLS_MIN 5
+#define KEYP_CTRL_SCAN_COLS_BITS 0x3
+
+#define KEYP_CTRL_SCAN_ROWS_SHIFT 2
+#define KEYP_CTRL_SCAN_ROWS_MIN 5
+#define KEYP_CTRL_SCAN_ROWS_BITS 0x7
+
+#define KEYP_CTRL_KEYP_EN BIT(7)
+
+#define KEYP_SCAN 0x149
+
+#define KEYP_SCAN_READ_STATE BIT(0)
+#define KEYP_SCAN_DBOUNCE_SHIFT 1
+#define KEYP_SCAN_PAUSE_SHIFT 3
+#define KEYP_SCAN_ROW_HOLD_SHIFT 6
+
+#define KEYP_TEST 0x14A
+
+#define KEYP_TEST_CLEAR_RECENT_SCAN BIT(6)
+#define KEYP_TEST_CLEAR_OLD_SCAN BIT(5)
+#define KEYP_TEST_READ_RESET BIT(4)
+#define KEYP_TEST_DTEST_EN BIT(3)
+#define KEYP_TEST_ABORT_READ BIT(0)
+
+#define KEYP_TEST_DBG_SELECT_SHIFT 1
+
+/* bits of these registers represent
+ * '0' for key press
+ * '1' for key release
+ */
+#define KEYP_RECENT_DATA 0x14B
+#define KEYP_OLD_DATA 0x14C
+
+#define KEYP_CLOCK_FREQ 32768
+
+/**
+ * struct pmic8xxx_kp - internal keypad data structure
+ * @pdata - keypad platform data pointer
+ * @input - input device pointer for keypad
+ * @key_sense_irq - key press/release irq number
+ * @key_stuck_irq - key stuck notification irq number
+ * @keycodes - array to hold the key codes
+ * @dev - parent device pointer
+ * @keystate - present key press/release state
+ * @stuckstate - present state when key stuck irq
+ * @ctrl_reg - control register value
+ */
+struct pmic8xxx_kp {
+ const struct pm8xxx_keypad_platform_data *pdata;
+ struct input_dev *input;
+ int key_sense_irq;
+ int key_stuck_irq;
+
+ unsigned short keycodes[PM8XXX_MATRIX_MAX_SIZE];
+
+ struct device *dev;
+ u16 keystate[PM8XXX_MAX_ROWS];
+ u16 stuckstate[PM8XXX_MAX_ROWS];
+
+ u8 ctrl_reg;
+};
+
+static int pmic8xxx_kp_write_u8(struct pmic8xxx_kp *kp,
+ u8 data, u16 reg)
+{
+ int rc;
+
+ rc = pm8xxx_writeb(kp->dev->parent, reg, data);
+ return rc;
+}
+
+static int pmic8xxx_kp_read(struct pmic8xxx_kp *kp,
+ u8 *data, u16 reg, unsigned num_bytes)
+{
+ int rc;
+
+ rc = pm8xxx_read_buf(kp->dev->parent, reg, data, num_bytes);
+ return rc;
+}
+
+static int pmic8xxx_kp_read_u8(struct pmic8xxx_kp *kp,
+ u8 *data, u16 reg)
+{
+ int rc;
+
+ rc = pmic8xxx_kp_read(kp, data, reg, 1);
+ return rc;
+}
+
+static u8 pmic8xxx_col_state(struct pmic8xxx_kp *kp, u8 col)
+{
+ /* all keys pressed on that particular row? */
+ if (col == 0x00)
+ return 1 << kp->pdata->num_cols;
+ else
+ return col & ((1 << kp->pdata->num_cols) - 1);
+}
+
+/*
+ * Synchronous read protocol for RevB0 onwards:
+ *
+ * 1. Write '1' to ReadState bit in KEYP_SCAN register
+ * 2. Wait 2*32KHz clocks, so that HW can successfully enter read mode
+ * synchronously
+ * 3. Read rows in old array first if events are more than one
+ * 4. Read rows in recent array
+ * 5. Wait 4*32KHz clocks
+ * 6. Write '0' to ReadState bit of KEYP_SCAN register so that hw can
+ * synchronously exit read mode.
+ */
+static int pmic8xxx_chk_sync_read(struct pmic8xxx_kp *kp)
+{
+ int rc;
+ u8 scan_val;
+
+ rc = pmic8xxx_kp_read_u8(kp, &scan_val, KEYP_SCAN);
+ if (rc < 0) {
+ dev_err(kp->dev, "Error reading KEYP_SCAN reg, rc=%d\n", rc);
+ return rc;
+ }
+
+ scan_val |= 0x1;
+
+ rc = pmic8xxx_kp_write_u8(kp, scan_val, KEYP_SCAN);
+ if (rc < 0) {
+ dev_err(kp->dev, "Error writing KEYP_SCAN reg, rc=%d\n", rc);
+ return rc;
+ }
+
+ /* 2 * 32KHz clocks */
+ udelay((2 * DIV_ROUND_UP(USEC_PER_SEC, KEYP_CLOCK_FREQ)) + 1);
+
+ return rc;
+}
+
+static int pmic8xxx_kp_read_data(struct pmic8xxx_kp *kp, u16 *state,
+ u16 data_reg, int read_rows)
+{
+ int rc, row;
+ u8 new_data[PM8XXX_MAX_ROWS];
+
+ rc = pmic8xxx_kp_read(kp, new_data, data_reg, read_rows);
+ if (rc)
+ return rc;
+
+ for (row = 0; row < kp->pdata->num_rows; row++) {
+ dev_dbg(kp->dev, "new_data[%d] = %d\n", row,
+ new_data[row]);
+ state[row] = pmic8xxx_col_state(kp, new_data[row]);
+ }
+
+ return rc;
+}
+
+static int pmic8xxx_kp_read_matrix(struct pmic8xxx_kp *kp, u16 *new_state,
+ u16 *old_state)
+{
+ int rc, read_rows;
+ u8 scan_val;
+
+ if (kp->pdata->num_rows < PM8XXX_MIN_ROWS)
+ read_rows = PM8XXX_MIN_ROWS;
+ else
+ read_rows = kp->pdata->num_rows;
+
+ pmic8xxx_chk_sync_read(kp);
+
+ if (old_state) {
+ rc = pmic8xxx_kp_read_data(kp, old_state, KEYP_OLD_DATA,
+ read_rows);
+ if (rc < 0) {
+ dev_err(kp->dev,
+ "Error reading KEYP_OLD_DATA, rc=%d\n", rc);
+ return rc;
+ }
+ }
+
+ rc = pmic8xxx_kp_read_data(kp, new_state, KEYP_RECENT_DATA,
+ read_rows);
+ if (rc < 0) {
+ dev_err(kp->dev,
+ "Error reading KEYP_RECENT_DATA, rc=%d\n", rc);
+ return rc;
+ }
+
+ /* 4 * 32KHz clocks */
+ udelay((4 * DIV_ROUND_UP(USEC_PER_SEC, KEYP_CLOCK_FREQ)) + 1);
+
+ rc = pmic8xxx_kp_read_u8(kp, &scan_val, KEYP_SCAN);
+ if (rc < 0) {
+ dev_err(kp->dev, "Error reading KEYP_SCAN reg, rc=%d\n", rc);
+ return rc;
+ }
+
+ scan_val &= 0xFE;
+ rc = pmic8xxx_kp_write_u8(kp, scan_val, KEYP_SCAN);
+ if (rc < 0)
+ dev_err(kp->dev, "Error writing KEYP_SCAN reg, rc=%d\n", rc);
+
+ return rc;
+}
+
+static void __pmic8xxx_kp_scan_matrix(struct pmic8xxx_kp *kp, u16 *new_state,
+ u16 *old_state)
+{
+ int row, col, code;
+
+ for (row = 0; row < kp->pdata->num_rows; row++) {
+ int bits_changed = new_state[row] ^ old_state[row];
+
+ if (!bits_changed)
+ continue;
+
+ for (col = 0; col < kp->pdata->num_cols; col++) {
+ if (!(bits_changed & (1 << col)))
+ continue;
+
+ dev_dbg(kp->dev, "key [%d:%d] %s\n", row, col,
+ !(new_state[row] & (1 << col)) ?
+ "pressed" : "released");
+
+ code = MATRIX_SCAN_CODE(row, col, PM8XXX_ROW_SHIFT);
+
+ input_event(kp->input, EV_MSC, MSC_SCAN, code);
+ input_report_key(kp->input,
+ kp->keycodes[code],
+ !(new_state[row] & (1 << col)));
+
+ input_sync(kp->input);
+ }
+ }
+}
+
+static bool pmic8xxx_detect_ghost_keys(struct pmic8xxx_kp *kp, u16 *new_state)
+{
+ int row, found_first = -1;
+ u16 check, row_state;
+
+ check = 0;
+ for (row = 0; row < kp->pdata->num_rows; row++) {
+ row_state = (~new_state[row]) &
+ ((1 << kp->pdata->num_cols) - 1);
+
+ if (hweight16(row_state) > 1) {
+ if (found_first == -1)
+ found_first = row;
+ if (check & row_state) {
+ dev_dbg(kp->dev, "detected ghost key on row[%d]"
+ " and row[%d]\n", found_first, row);
+ return true;
+ }
+ }
+ check |= row_state;
+ }
+ return false;
+}
+
+static int pmic8xxx_kp_scan_matrix(struct pmic8xxx_kp *kp, unsigned int e