input: add pca9557 keypad driver for new mic board D607
authorjinrong.liao <jinrong.liao@amlogic.com>
Wed, 29 Aug 2018 04:35:24 +0000 (12:35 +0800)
committerjinrong.liao <jinrong.liao@amlogic.com>
Fri, 31 Aug 2018 01:44:06 +0000 (09:44 +0800)
PD#172286: this commit changes mainly for GVA

1) keypad: add pca9557 keypad driver for new mic board D607.

Change-Id: I0d9ec9626362b3d87d6c55e5c967bfa4486b1472
Signed-off-by: jinrong.liao <jinrong.liao@amlogic.com>
Documentation/devicetree/bindings/amlogic/input/pca9557_keypad.txt [new file with mode: 0644]
MAINTAINERS
arch/arm64/boot/dts/amlogic/axg_s400_v03gva.dts
arch/arm64/boot/dts/amlogic/axg_s420_v03gva.dts
arch/arm64/configs/meson64_smarthome_defconfig
drivers/amlogic/input/keyboard/Kconfig
drivers/amlogic/input/keyboard/Makefile
drivers/amlogic/input/keyboard/pca9557_keypad.c [new file with mode: 0644]

diff --git a/Documentation/devicetree/bindings/amlogic/input/pca9557_keypad.txt b/Documentation/devicetree/bindings/amlogic/input/pca9557_keypad.txt
new file mode 100644 (file)
index 0000000..a4ca67c
--- /dev/null
@@ -0,0 +1,10 @@
+pca9557: pca9557@0x1f {
+               compatible = "amlogic,pca9557_keypad";
+               reg = <0x1f>;
+               key_num = <4>;
+               key_name = "fdr", "hotword", "pause", "mute";
+               key_value = <106 105 139 116>;
+               key_index_mask = <0x4 0x8 0x10 0x20>;
+               key_input_mask = <0x3C>;
+               status = "okay";
+       };
index e5bf092..c7ede1a 100644 (file)
@@ -14620,3 +14620,5 @@ F:      arch/arm64/boot/dts/amlogic/axg_s400_v03gva.dts
 F:      arch/arm64/boot/dts/amlogic/axg_s420_v03gva.dts
 F:     Documentation/devicetree/bindings/input/cy8c4014_i2c.txt
 F:     drivers/amlogic/input/sensor/cy8c4014.c
+F:     Documentation/devicetree/bindings/amlogic/input/pca9557_keypad.txt
+F:      drivers/amlogic/input/keyboard/pca9557_keypad.c
index c9311bf..38936c2 100644 (file)
        pinctrl-0=<&ao_i2c_master_pin2>;
 
        aml_pca9557: aml_pca9557@0x1f {
-               compatible = "aml, ledring";
+               compatible = "amlogic,pca9557_keypad";
                reg = <0x1f>;
-               mode = <0>; /*0: 6-led 1: 4key+2led */
                key_num = <4>;
-               led_dev_name = "aml_ledring";
-               key_dev_name = "aml_pca_key";
-               key_name = "mute", "pause", "vol+", "vol-";
-               key_value = <200 201 202 203>;
+               key_name = "fdr", "hotword", "pause", "mute";
+               key_value = <106 105 139 116>;
+               key_index_mask = <0x4 0x8 0x10 0x20>;
+               key_input_mask = <0x3C>;
                status = "okay";
        };
 
index f55e35e..bb76720 100644 (file)
        pinctrl-0=<&ao_i2c_master_pin2>;
 
        aml_pca9557: aml_pca9557@0x1f {
-               compatible = "aml, ledring";
+               compatible = "amlogic,pca9557_keypad";
                reg = <0x1f>;
-               mode = <0>; /*0: 6-led 1: 4key+2led */
                key_num = <4>;
-               led_dev_name = "aml_ledring";
-               key_dev_name = "aml_pca_key";
-               key_name = "mute", "pause", "vol+", "vol-";
-               key_value = <200 201 202 203>;
+               key_name = "fdr", "hotword", "pause", "mute";
+               key_value = <106 105 139 116>;
+               key_index_mask = <0x4 0x8 0x10 0x20>;
+               key_input_mask = <0x3C>;
                status = "okay";
        };
 
index 46fe2d4..6898bb4 100644 (file)
@@ -311,6 +311,7 @@ CONFIG_AMLOGIC_IIO=y
 CONFIG_AMLOGIC_SARADC=y
 CONFIG_AMLOGIC_DDR_WINDOW_TOOL=m
 CONFIG_AMLOGIC_LEDRING=y
+CONFIG_AMLOGIC_PCA9557_KEYPAD=y
 CONFIG_AMLOGIC_SENSOR=y
 CONFIG_AMLOGIC_SENSOR_CY8C4014=y
 CONFIG_AMLOGIC_GPIO_IRQ=y
index 5281866..c8706ec 100644 (file)
@@ -26,4 +26,10 @@ config AMLOGIC_GPIO_KEY
         help
           Say Y here if you want to use the amlogic hold key.
 
+config AMLOGIC_PCA9557_KEYPAD
+        tristate "Amlogic pca9557 keypad device surport"
+        default n
+        help
+          Say Y here if you want to use the amlogic hold key.
+
 endif # AMLOGIC_INPUT_KEYBOARD
index 62b2278..d443190 100644 (file)
@@ -6,3 +6,4 @@
 
 obj-$(CONFIG_AMLOGIC_ADC_KEYPADS)              += adc_keypad.o
 obj-$(CONFIG_AMLOGIC_GPIO_KEY)          += gpio_keypad.o
+obj-$(CONFIG_AMLOGIC_PCA9557_KEYPAD)          += pca9557_keypad.o
diff --git a/drivers/amlogic/input/keyboard/pca9557_keypad.c b/drivers/amlogic/input/keyboard/pca9557_keypad.c
new file mode 100644 (file)
index 0000000..87b804b
--- /dev/null
@@ -0,0 +1,309 @@
+/*
+ * drivers/amlogic/input/keyboard/pca9557_keypad.c
+ *
+ * Copyright (C) 2017 Amlogic, Inc. 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 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.
+ *
+ */
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/i2c.h>
+#include <linux/uaccess.h>
+#include <linux/kobject.h>
+#include <linux/device.h>
+#include <linux/slab.h>
+#include <linux/pm.h>
+#include <linux/uaccess.h>
+#include <linux/pm_runtime.h>
+#include <linux/input.h>
+#include <linux/of_platform.h>
+
+#define PCA9557_INPUT_REG      0x00
+#define PCA9557_OUTPUT_REG     0x01
+#define PCA9557_POL_INV_REG    0x02
+#define PCA9557_CONFIG_REG     0x03
+
+#define DEFAULT_DELAY  200
+
+#define DEVICE_NAME    "pca9557_keypad"
+
+#define MAX_NAME_LEN   50
+
+static struct pca9557_keypad {
+       struct input_dev *input_dev;
+       struct delayed_work work;
+       int delay;
+       int key_num;
+       int key_tmp_val;
+       int key_last_val;
+       int major;
+       int key_input_mask;
+       struct i2c_client *i2c_client;
+} *keypad_desc;
+
+struct _key_des {
+       char name[MAX_NAME_LEN];
+       unsigned int key_val;
+       int key_index_mask;
+} *exp_key;
+
+static void delay_work_func(
+               struct work_struct *work)
+{
+       int i;
+       int key_val = 0;
+       unsigned long delay = msecs_to_jiffies(keypad_desc->delay);
+
+       key_val = i2c_smbus_read_byte_data(keypad_desc->i2c_client,
+               PCA9557_INPUT_REG) & keypad_desc->key_input_mask;
+       if (keypad_desc->key_tmp_val != key_val) {
+               keypad_desc->key_tmp_val = key_val;
+               if (key_val != 0)
+                       keypad_desc->key_last_val = key_val;
+               for (i = 0; i < keypad_desc->key_num; i++) {
+                       if (keypad_desc->key_last_val ==
+                               exp_key[i].key_index_mask) {
+                               if (key_val != 0) {
+                                       pr_info("key \"%s\" down\n",
+                                               exp_key[i].name);
+                                       input_event(keypad_desc->input_dev,
+                                               EV_KEY, exp_key[i].key_val, 1);
+                               } else {
+                                       pr_info("key \"%s\" up\n",
+                                               exp_key[i].name);
+                                       input_event(keypad_desc->input_dev,
+                                               EV_KEY, exp_key[i].key_val, 0);
+                               }
+                               input_event(keypad_desc->input_dev,
+                                       EV_SYN, 0, 0);
+                               break;
+                       }
+
+               }
+       }
+       schedule_delayed_work(&keypad_desc->work, delay);
+}
+
+static int pca9557_init_reg(struct i2c_client *client)
+{
+
+       int ret;
+
+       ret = i2c_smbus_write_byte_data(client,
+                       PCA9557_POL_INV_REG, keypad_desc->key_input_mask);
+       if (ret < 0) {
+               pr_err("pca9557 init POL_INV reg fail!\n");
+               return -1;
+       }
+       return 0;
+}
+
+static long pca9557_ioctl(struct file *file,
+                       unsigned int cmd,
+                       unsigned long args)
+{
+       return 0;
+}
+
+static ssize_t pca9557_read(struct file *filp, char __user *buf,
+                               size_t count, loff_t *ppos)
+{
+       return count;
+}
+
+static const struct file_operations pca9557_fops = {
+       .owner = THIS_MODULE,
+       .read = pca9557_read,
+       .compat_ioctl = pca9557_ioctl,
+       .unlocked_ioctl = pca9557_ioctl,
+};
+
+static int pca9557_parse_child_dt(const struct device *dev)
+{
+       int ret, cnt;
+       const char *uname;
+
+       if (dev->of_node) {
+               ret = of_property_read_u32(dev->of_node, "key_num",
+                       &keypad_desc->key_num);
+               if (ret) {
+                       pr_err("failed to get key_num!\n");
+                       return -EINVAL;
+               }
+               exp_key = kcalloc(keypad_desc->key_num,
+                       sizeof(struct _key_des), GFP_KERNEL);
+
+               for (cnt = 0; cnt < keypad_desc->key_num; cnt++) {
+                       ret = of_property_read_string_index(dev->of_node,
+                                       "key_name", cnt, &uname);
+                       if (ret < 0) {
+                               pr_err("invalid key name index[%d]\n", cnt);
+                               return  -EINVAL;
+                       }
+                       strncpy(exp_key[cnt].name, uname, MAX_NAME_LEN);
+                       ret = of_property_read_u32_index(dev->of_node,
+                               "key_value", cnt, &exp_key[cnt].key_val);
+                       if (ret < 0) {
+                               pr_err("invalid key value index[%d]\n", cnt);
+                               return  -EINVAL;
+                       }
+                       ret = of_property_read_u32_index(dev->of_node,
+                               "key_index_mask", cnt,
+                               &exp_key[cnt].key_index_mask);
+                       if (ret < 0) {
+                               pr_err("invalid key index mask index[%d]\n",
+                                       cnt);
+                               return  -EINVAL;
+                       }
+               }
+               ret = of_property_read_u32(dev->of_node, "key_input_mask",
+                       &keypad_desc->key_input_mask);
+               if (ret) {
+                       pr_err("failed to get key_input_mask!\n");
+                       return -EINVAL;
+               }
+               return 0;
+       }
+       return -EINVAL;
+}
+static ssize_t delay_show(struct device *dev,
+       struct device_attribute *attr, char *buf)
+{
+       int ret = 0;
+
+       ret = sprintf(buf, "pca9557 schedule delay: %d\n", keypad_desc->delay);
+       return ret;
+}
+static ssize_t delay_store(struct device *dev,
+                                       struct device_attribute *attr,
+                                       const char *buf, size_t count)
+{
+       int ret;
+
+       ret = kstrtouint(buf, 10, &keypad_desc->delay);
+       if (ret != 0) {
+               pr_info("set delay level fail, use default delay!!\n");
+               keypad_desc->delay = DEFAULT_DELAY;
+       }
+       schedule_delayed_work(&keypad_desc->work,
+               msecs_to_jiffies(keypad_desc->delay));
+       return count;
+}
+DEVICE_ATTR_RW(delay);
+
+static struct attribute *pca9557_attrs[] = {
+       &dev_attr_delay.attr,
+       NULL,
+};
+
+ATTRIBUTE_GROUPS(pca9557);
+
+static struct class pca9557_class = {
+       .name           = "pca9557_keypad",
+       .owner          = THIS_MODULE,
+       .dev_groups = pca9557_groups,
+};
+
+static int pca9557_probe(struct i2c_client *client,
+               const struct i2c_device_id *i2c_id)
+{
+       int ret, i;
+       struct device *dev = &client->dev;
+
+       pr_info("%s\n", __func__);
+       keypad_desc = devm_kzalloc(dev, sizeof(struct pca9557_keypad),
+               GFP_KERNEL);
+       if (!keypad_desc)
+               return -ENOMEM;
+       keypad_desc->i2c_client = client;
+       ret = pca9557_parse_child_dt(dev);
+       if (ret < 0) {
+               pr_err("%s,pca9557_parse_child_dt fail!\n", __func__);
+               return -EINVAL;
+       }
+
+       ret = pca9557_init_reg(client);
+       keypad_desc->input_dev = input_allocate_device();
+       if (keypad_desc->input_dev == NULL) {
+               pr_err("input_allocate_device err!\n");
+               return -1;
+       }
+       set_bit(EV_SYN, keypad_desc->input_dev->evbit);
+       set_bit(EV_KEY, keypad_desc->input_dev->evbit);
+       for (i = 0; i < keypad_desc->key_num; i++)
+               set_bit(exp_key[i].key_val, keypad_desc->input_dev->keybit);
+       keypad_desc->input_dev->name = DEVICE_NAME;
+       ret = input_register_device(keypad_desc->input_dev);
+       if (ret != 0) {
+               pr_err("input_register_device err!\n");
+               return -1;
+       }
+       keypad_desc->delay = DEFAULT_DELAY;
+       keypad_desc->key_tmp_val = 0;
+       keypad_desc->major = register_chrdev(0, DEVICE_NAME, &pca9557_fops);
+       class_register(&pca9557_class);
+       device_create(&pca9557_class, NULL, MKDEV(keypad_desc->major, 0),
+               NULL, DEVICE_NAME);
+
+       INIT_DELAYED_WORK(&keypad_desc->work, delay_work_func);
+       schedule_delayed_work(&keypad_desc->work, DEFAULT_DELAY);
+       return 0;
+}
+
+static int pca9557_remove(struct i2c_client *client)
+{
+       device_destroy(&pca9557_class, MKDEV(keypad_desc->major, 0));
+       cancel_delayed_work(&keypad_desc->work);
+
+       input_unregister_device(keypad_desc->input_dev);
+       input_free_device(keypad_desc->input_dev);
+       return 0;
+}
+static const struct i2c_device_id pca9557_id[] = {
+       {"pca9557_keypad", 0 },
+       {}
+};
+
+static const struct of_device_id pca9557_dt_id[] = {
+       { .compatible = "amlogic,pca9557_keypad",},
+       {},
+};
+
+static struct i2c_driver pca9557_drv = {
+       .driver = {
+               .name = "pca9557_keypad",
+               .owner = THIS_MODULE,
+               .of_match_table = pca9557_dt_id,
+       },
+       .probe = pca9557_probe,
+       .remove = pca9557_remove,
+       .id_table = pca9557_id,
+};
+
+static int __init pca9557_init(void)
+{
+       return i2c_add_driver(&pca9557_drv);
+}
+
+static void __exit pca9557_exit(void)
+{
+       i2c_del_driver(&pca9557_drv);
+}
+
+module_init(pca9557_init);
+module_exit(pca9557_exit);
+MODULE_AUTHOR("www.amlogic.com");
+MODULE_DESCRIPTION("pca9557 driver for keypad");
+MODULE_LICENSE("GPL");
+
+