adc_keypad: update interface to access adc and add sysfs interface
authorxingyu.chen <xingyu.chen@amlogic.com>
Wed, 28 Jun 2017 06:17:12 +0000 (14:17 +0800)
committerJianxin Pan <jianxin.pan@amlogic.com>
Thu, 29 Jun 2017 04:06:14 +0000 (21:06 -0700)
PD#146614: adc_keypad: update interface to access adc and add sysfs interface

1.use sysfs interface to set dynamic key
inquire key map:
cat /sys/devices/platform/adc_keypad/table

set key:
echo [name]:[code]:[channel]:[value]:[tolerance] >
/sys/devices/platform/adc_keypad/table

2. use the consumer interface of the IIO to access sar adc

Change-Id: I0357e84720340b0fee4baa882f41f0aa7f36a9a1
Signed-off-by: xingyu.chen <xingyu.chen@amlogic.com>
arch/arm64/boot/dts/amlogic/axg_s400.dts
arch/arm64/boot/dts/amlogic/axg_s420.dts
arch/arm64/configs/meson64_defconfig
drivers/amlogic/input/keyboard/adc_keypad.c
include/linux/amlogic/adc_keypad.h

index 4da3073..2e22859 100644 (file)
                        "clk_ge2d_gate";
                reg = <0x0 0xff940000 0x0 0x10000>;
        };
+
+       adc_keypad {
+               compatible = "amlogic, adc_keypad";
+               status = "okay";
+               key_name = "voice", "vol-", "vol+", "wifi", "<<", ">>";
+               key_num = <6>;
+               io-channels = <&saradc SARADC_CH0>;
+               io-channel-names = "key-chan-0";
+               key_chan = <SARADC_CH0 SARADC_CH0 SARADC_CH0
+                                       SARADC_CH0 SARADC_CH0 SARADC_CH0>;
+               key_code = <164 114 115 139 105 106>;
+               key_val = <0 143 266 389 512 635>; //val=voltage/1800mV*1023
+               key_tolerance = <40 40 40 40 40 40>;
+       };
 }; /* end of / */
 
 /* Audio Related start */
index 1202dd2..4185967 100644 (file)
                };
        };
 
+       adc_keypad {
+               compatible = "amlogic, adc_keypad";
+               status = "okay";
+               key_name = "voice", "vol-", "vol+", "wifi", "<<", ">>";
+               key_num = <6>;
+               io-channels = <&saradc SARADC_CH0>;
+               io-channel-names = "key-chan-0";
+               key_chan = <SARADC_CH0 SARADC_CH0 SARADC_CH0
+                                       SARADC_CH0 SARADC_CH0 SARADC_CH0>;
+               key_code = <164 114 115 139 105 106>;
+               key_val = <0 143 266 389 512 635>; //val=voltage/1800mV*1023
+               key_tolerance = <40 40 40 40 40 40>;
+       };
+
        partitions: partitions{
                parts = <11>;
                part-0 = <&logo>;
index ea86d33..6809b9d 100644 (file)
@@ -222,6 +222,7 @@ CONFIG_AMLOGIC_CRYPTO=y
 CONFIG_AMLOGIC_CRYPTO_DMA=y
 CONFIG_AMLOGIC_INPUT=y
 CONFIG_AMLOGIC_INPUT_KEYBOARD=y
+CONFIG_AMLOGIC_ADC_KEYPADS=y
 CONFIG_AMLOGIC_REMOTE=y
 CONFIG_AMLOGIC_MESON_REMOTE=y
 CONFIG_AMLOGIC_IRBLASTER=y
index da73e02..e63173c 100644 (file)
 
 #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/delay.h>
 #include <linux/platform_device.h>
-#include <linux/mutex.h>
 #include <linux/errno.h>
-#include <asm/irq.h>
 #include <linux/io.h>
-#include <linux/amlogic/scpi_protocol.h>
-
-#include <linux/major.h>
 #include <linux/slab.h>
 #include <linux/uaccess.h>
-#include <linux/amlogic/saradc.h>
-#include <linux/amlogic/adc_keypad.h>
+#include <linux/string.h>
 #include <linux/of.h>
-#ifdef CONFIG_AMLOGIC_LEGACY_EARLY_SUSPEND
-#include <linux/amlogic/pm.h>
-#endif
+#include <linux/iio/consumer.h>
+#include <linux/amlogic/adc_keypad.h>
+#include <linux/amlogic/scpi_protocol.h>
 
-#define POLL_PERIOD_WHEN_KEY_DOWN 10 /* unit msec */
+#define POLL_PERIOD_WHEN_KEY_DOWN 20 /* unit msec */
 #define POLL_PERIOD_WHEN_KEY_UP   50
-#define KEY_JITTER_COUNT  2  /*  2 * POLL_PERIOD_WHEN_KEY_DOWN msec */
-
-struct kp {
-       struct input_dev *input;
-       struct timer_list timer;
-       unsigned int report_code;
-       unsigned int code;
-       unsigned int poll_period;
-       int count;
-       int config_major;
-       char config_name[20];
-       struct class *config_class;
-       struct device *config_dev;
-       int chan[SARADC_CHAN_NUM];
-       int chan_num;
-       struct adc_key *key;
-       int key_num;
-       struct work_struct work_update;
-#ifdef CONFIG_AMLOGIC_LEGACY_EARLY_SUSPEND
-       struct early_suspend early_suspend;
-#endif
-};
-
-#ifndef CONFIG_OF
-#define CONFIG_OF
-#endif
+#define KEY_JITTER_COUNT  1  /*  1 * POLL_PERIOD_WHEN_KEY_DOWN msec  */
+#define TMP_BUF_MAX 128
 
-static struct kp *gp_kp;
-static int keypad_enable_flag = 1;
-/* static int timer_count = 0; */
+static char adc_key_mode_name[MAX_NAME_LEN] = "abcdef";
+static char kernelkey_en_name[MAX_NAME_LEN] = "abcdef";
+static bool keypad_enable_flag = true;
 
-static int kp_search_key(struct kp *kp)
+static int meson_adc_kp_search_key(struct meson_adc_kp *kp)
 {
        struct adc_key *key;
-       int value, i, j;
+       int value, i;
 
+       mutex_lock(&kp->kp_lock);
        for (i = 0; i < kp->chan_num; i++) {
-               value = get_adc_sample(0, kp->chan[i]);
-               if (value < 0)
-                       continue;
-               key = kp->key;
-               for (j = 0; j < kp->key_num; j++) {
-                       if ((key->chan == kp->chan[i])
-                       && (value >= key->value - key->tolerance)
-                       && (value <= key->value + key->tolerance)) {
-                               return key->code;
+               if (iio_read_channel_processed(kp->pchan[kp->chan[i]],
+                               &value) >= 0) {
+                       if (value < 0)
+                               continue;
+                       list_for_each_entry(key, &kp->adckey_head, list) {
+                               if ((key->chan == kp->chan[i])
+                               && (value >= key->value - key->tolerance)
+                               && (value <= key->value + key->tolerance)) {
+                                       mutex_unlock(&kp->kp_lock);
+                                       return key->code;
+                               }
                        }
-                       key++;
                }
        }
-
-       return 0;
+       mutex_unlock(&kp->kp_lock);
+       return KEY_RESERVED;
 }
 
-static void kp_work(struct kp *kp)
+static void meson_adc_kp_work(struct meson_adc_kp *kp)
 {
-       int code = kp_search_key(kp);
+       int code = meson_adc_kp_search_key(kp);
 
        if (code)
                kp->poll_period = POLL_PERIOD_WHEN_KEY_DOWN;
@@ -110,9 +80,9 @@ static void kp_work(struct kp *kp)
                kp->count = 0;
        } else if (kp->count < KEY_JITTER_COUNT) {
                kp->count++;
-               } else {
-                       if ((kp->report_code != code) && keypad_enable_flag) {
-                               if (!code) { /* key up */
+       } else {
+               if ((kp->report_code != code) && keypad_enable_flag) {
+                       if (!code) { /* key up */
                                dev_info(&kp->input->dev,
                                        "key %d up\n", kp->report_code);
                                input_report_key(kp->input, kp->report_code, 0);
@@ -134,371 +104,551 @@ static void kp_work(struct kp *kp)
 
 static void update_work_func(struct work_struct *work)
 {
-       struct kp *kp = container_of(work, struct kp, work_update);
-
-       kp_work(kp);
+       struct meson_adc_kp *kp = container_of(work,
+                               struct meson_adc_kp, work_update);
+       meson_adc_kp_work(kp);
 }
 
-static void kp_timer_sr(unsigned long data)
+static void meson_adc_kp_timer_sr(unsigned long data)
 {
-       struct kp *kp = (struct kp *)data;
-
+       struct meson_adc_kp *kp = (struct meson_adc_kp *)data;
        schedule_work(&(kp->work_update));
        mod_timer(&kp->timer, jiffies+msecs_to_jiffies(kp->poll_period));
 }
 
-static int
-adckpd_config_open(struct inode *inode, struct file *file)
-{
-       file->private_data = gp_kp;
-       return 0;
-}
-
-static int
-adckpd_config_release(struct inode *inode, struct file *file)
-{
-       file->private_data = NULL;
-       return 0;
-}
-
-static const struct file_operations keypad_fops = {
-       .owner      = THIS_MODULE,
-       .open       = adckpd_config_open,
-       .release    = adckpd_config_release,
-};
-
-static int register_keypad_dev(struct kp  *kp)
-{
-       int ret = 0;
-
-       strcpy(kp->config_name, "am_adc_kpd");
-       ret = register_chrdev(0, kp->config_name, &keypad_fops);
-       if (ret <= 0) {
-               dev_info(&kp->input->dev, "register char device error\n");
-               return  ret;
-       }
-       kp->config_major = ret;
-       kp->config_class = class_create(THIS_MODULE, kp->config_name);
-       kp->config_dev = device_create(kp->config_class,        NULL,
-       MKDEV(kp->config_major, 0), NULL, kp->config_name);
-       return ret;
-}
-
 #ifdef CONFIG_AMLOGIC_LEGACY_EARLY_SUSPEND
-static void kp_early_suspend(struct early_suspend *h)
+static void meson_adc_kp_early_suspend(struct early_suspend *h)
 {
-       struct kp *kp = container_of(h, struct kp, early_suspend);
-
+       struct meson_adc_kp *kp = container_of(h,
+                       struct meson_adc_kp, early_suspend);
        del_timer_sync(&kp->timer);
        cancel_work_sync(&kp->work_update);
 }
 
-static void kp_late_resume(struct early_suspend *h)
+static void meson_adc_kp_late_resume(struct early_suspend *h)
 {
-       struct kp *kp = container_of(h, struct kp, early_suspend);
-
-        mod_timer(&kp->timer, jiffies+msecs_to_jiffies(kp->poll_period));
+       struct meson_adc_kp *kp = container_of(h,
+                       struct meson_adc_kp, early_suspend);
+       mod_timer(&kp->timer, jiffies+msecs_to_jiffies(kp->poll_period));
 }
 #endif
 
-/*
- *typedef enum
- *{
- *     POWER_WAKEUP_POWER,      /// only power key resume
- *     POWER_WAKEUP_ANY,        /// any key resume
- *     POWER_WAKEUP_NONE        /// no key can resume
- *}EN_FPP_POWER_KEYPAD_WAKEUP_METHOD;
- *
- *typedef enum
- *{
- *     KEYPAD_UNLOCK,///unlock, normal mode
- *     KEYPAD_LOCK /// lock, press key will be not useful
- *}EN_FPP_SYSTEM_KEYPAD_MODE;
- */
-
-static char adc_key_mode_name[20] = "abcdef";
-static char kernelkey_en_name[20] = "abcdef";
-
 static void send_data_to_bl301(void)
 {
        u32 val;
-
        if (!strcmp(adc_key_mode_name, "POWER_WAKEUP_POWER")) {
-               val = 0;
+               val = 0;  /*only power key resume*/
                scpi_send_usr_data(SCPI_CL_POWER, &val, sizeof(val));
        } else if (!strcmp(adc_key_mode_name, "POWER_WAKEUP_ANY")) {
-               val = 1;
+               val = 1; /*any key resume*/
                scpi_send_usr_data(SCPI_CL_POWER, &val, sizeof(val));
        } else if (!strcmp(adc_key_mode_name, "POWER_WAKEUP_NONE")) {
-               val = 2;
+               val = 2; /*no key can resume*/
                scpi_send_usr_data(SCPI_CL_POWER, &val, sizeof(val));
        }
 
 }
 
-void kernel_keypad_enable_mode_enable(void)
+static void kernel_keypad_enable_mode_enable(void)
 {
-
        if (!strcmp(kernelkey_en_name, "KEYPAD_UNLOCK"))
-               keypad_enable_flag = 1;
+               keypad_enable_flag = 1;  /*unlock, normal mode*/
        else if (!strcmp(kernelkey_en_name, "KEYPAD_LOCK"))
-               keypad_enable_flag = 0;
+               keypad_enable_flag = 0;  /*lock, press key will be not useful*/
        else
                keypad_enable_flag = 1;
 }
 
-static int kp_probe(struct platform_device *pdev)
+/*meson_adc_kp_get_valid_chan() - used to get valid adc channel
+ *
+ *@kp: to save number of channel in
+ *
+ */
+static void meson_adc_kp_get_valid_chan(struct meson_adc_kp *kp)
 {
-       struct kp *kp;
-
-       struct input_dev *input_dev;
-       int i, j, ret, key_size, name_len;
-       int new_chan_flag;
+       unsigned char incr;
        struct adc_key *key;
-       struct adc_kp_platform_data *pdata = NULL;
-       int *key_param = NULL;
-       int state = 0;
 
-       send_data_to_bl301();
-       kernel_keypad_enable_mode_enable();
+       mutex_lock(&kp->kp_lock);
+       kp->chan_num = 0; /*recalculate*/
+       list_for_each_entry(key, &kp->adckey_head, list) {
+               if (kp->chan_num == 0) {
+                       kp->chan[kp->chan_num++] = key->chan;
+               } else {
+                       for (incr = 0; incr < kp->chan_num; incr++) {
+                               if (key->chan == kp->chan[incr])
+                                       break;
+                               if (incr == (kp->chan_num - 1))
+                                       kp->chan[kp->chan_num++] = key->chan;
+                       }
+               }
+       }
+       mutex_unlock(&kp->kp_lock);
+}
+
+static int meson_adc_kp_get_devtree_pdata(struct platform_device *pdev,
+                       struct meson_adc_kp *kp)
+{
+       int ret;
+       int count;
+       int state = 0;
+       unsigned char cnt;
+       const char *uname;
+       unsigned int key_num;
+       struct adc_key *key;
+       struct of_phandle_args chanspec;
 
-#ifdef CONFIG_OF
        if (!pdev->dev.of_node) {
-               dev_err(&pdev->dev, "adc_key: pdev->dev.of_node == NULL!\n");
-               state =  -EINVAL;
-               goto get_key_node_fail;
-       }
-       ret = of_property_read_u32(pdev->dev.of_node, "key_num", &key_size);
-       if (ret) {
-               dev_err(&pdev->dev, "adc_key: failed to get key_num!\n");
-               state =  -EINVAL;
-               goto get_key_node_fail;
+               dev_err(&pdev->dev, "failed to get device node\n");
+               return -EINVAL;
        }
-       ret = of_property_read_u32(pdev->dev.of_node, "name_len", &name_len);
-       if (ret) {
-               dev_err(&pdev->dev, "adc_key: failed to get name_len!\n");
-               name_len = 20;
+
+       count = of_property_count_strings(pdev->dev.of_node,
+               "io-channel-names");
+       if (count < 0) {
+               dev_err(&pdev->dev, "failed to get io-channel-names");
+               return -ENODATA;
        }
-       pdata = kzalloc(sizeof(*pdata), GFP_KERNEL);
-       if (!pdata) {
-               state = -EINVAL;
-               goto get_key_node_fail;
+
+       for (cnt = 0; cnt < count; cnt++) {
+               ret = of_parse_phandle_with_args(pdev->dev.of_node,
+                       "io-channels", "#io-channel-cells", cnt, &chanspec);
+               if (ret)
+                       return ret;
+
+               if (!chanspec.args_count)
+                       return -EINVAL;
+
+               if (chanspec.args[0] >= SARADC_CH_NUM) {
+                       dev_err(&pdev->dev, "invalid channel index[%u]\n",
+                                       chanspec.args[0]);
+                       return -EINVAL;
+               }
+
+               ret = of_property_read_string_index(pdev->dev.of_node,
+                               "io-channel-names", cnt, &uname);
+               if (ret < 0) {
+                       dev_err(&pdev->dev, "invalid channel name index[%d]\n",
+                                       cnt);
+                       return -EINVAL;
+               }
+
+               kp->pchan[chanspec.args[0]] = devm_iio_channel_get(&pdev->dev,
+                               uname);
+               if (IS_ERR(kp->pchan[chanspec.args[0]]))
+                       return PTR_ERR(kp->pchan[chanspec.args[0]]);
        }
-       pdata->key = kcalloc(key_size, sizeof(*(pdata->key)), GFP_KERNEL);
-       if (!(pdata->key)) {
-               dev_err(&pdev->dev, "platform key is required!\n");
-               goto get_key_mem_fail;
+
+       ret = of_property_read_u32(pdev->dev.of_node, "key_num", &key_num);
+       if (ret) {
+               dev_err(&pdev->dev, "failed to get key_num!\n");
+               return -EINVAL;
        }
-       pdata->key_num = key_size;
-       for (i = 0; i < key_size; i++) {
+
+       for (cnt = 0; cnt < key_num; cnt++) {
+               key = kzalloc(sizeof(struct adc_key), GFP_KERNEL);
+               if (!key) {
+                       dev_err(&pdev->dev, "alloc mem failed!\n");
+                       return -ENOMEM;
+               }
+
                ret = of_property_read_string_index(pdev->dev.of_node,
-                        "key_name", i, &(pdata->key[i].name));
+                        "key_name", cnt, &uname);
                if (ret < 0) {
-                       dev_err(&pdev->dev,
-                                "adc_key: find key_name=%d finished\n", i);
-                       break;
+                       dev_err(&pdev->dev, "invalid key name index[%d]\n",
+                               cnt);
+                       state = -EINVAL;
+                       goto err;
+               }
+               strncpy(key->name, uname, MAX_NAME_LEN);
+
+               ret = of_property_read_u32_index(pdev->dev.of_node,
+                       "key_code", cnt, &key->code);
+               if (ret < 0) {
+                       dev_err(&pdev->dev, "invalid key code index[%d]\n",
+                               cnt);
+                       state = -EINVAL;
+                       goto err;
+               }
+
+               ret = of_property_read_u32_index(pdev->dev.of_node,
+                       "key_chan", cnt, &key->chan);
+               if (ret < 0) {
+                       dev_err(&pdev->dev, "invalid key chan index[%d]\n",
+                               cnt);
+                       state = -EINVAL;
+                       goto err;
+               }
+
+               if (!kp->pchan[key->chan]) {
+                       dev_err(&pdev->dev, "invalid channel[%u], please enable it first by DTS\n",
+                                       key->chan);
+                       state = -EINVAL;
+                       goto err;
                }
+
+               ret = of_property_read_u32_index(pdev->dev.of_node,
+                       "key_val", cnt, &key->value);
+               if (ret < 0) {
+                       dev_err(&pdev->dev, "invalid key value index[%d]\n",
+                               cnt);
+                       state = -EINVAL;
+                       goto err;
+               }
+
+               ret = of_property_read_u32_index(pdev->dev.of_node,
+                       "key_tolerance", cnt, &key->tolerance);
+               if (ret < 0) {
+                       dev_err(&pdev->dev, "invalid key tolerance index[%d]\n",
+                               cnt);
+                       state = -EINVAL;
+                       goto err;
+               }
+               mutex_lock(&kp->kp_lock);
+               set_bit(key->code, kp->input->keybit); /*set event code*/
+               list_add_tail(&key->list, &kp->adckey_head);
+               mutex_unlock(&kp->kp_lock);
        }
-       key_param =
-                kzalloc(4*(sizeof(*key_param))*(pdata->key_num), GFP_KERNEL);
-       if (!key_param) {
-               dev_err(&pdev->dev, "adc_key: key_param can not get mem\n");
-               goto get_param_mem_fail;
+       meson_adc_kp_get_valid_chan(kp);
+       return 0;
+err:
+       kfree(key);
+       return state;
+
+}
+
+static void meson_adc_kp_list_free(struct meson_adc_kp *kp)
+{
+       struct adc_key *key;
+       struct adc_key *key_tmp;
+
+       mutex_lock(&kp->kp_lock);
+       list_for_each_entry_safe(key, key_tmp, &kp->adckey_head, list) {
+               list_del(&key->list);
+               kfree(key);
        }
-       ret = of_property_read_u32_array(pdev->dev.of_node,
-                "key_code", key_param, pdata->key_num);
-       if (ret) {
-               dev_err(&pdev->dev, "adc_key: failed to get key_code!\n");
-               goto get_key_param_failed;
+       mutex_unlock(&kp->kp_lock);
+}
+
+static int kp_input_dev_register(struct platform_device *pdev,
+                       struct meson_adc_kp *kp)
+{
+       /*alloc input device*/
+       kp->input = input_allocate_device();
+       if (!kp->input) {
+               dev_err(&pdev->dev, "alloc input device failed!\n");
+               return -ENOMEM;
        }
-       ret = of_property_read_u32_array(pdev->dev.of_node,
-                "key_chan", key_param+pdata->key_num, pdata->key_num);
-       if (ret) {
-               dev_err(&pdev->dev, "adc_key: failed to get key_chan!\n");
-               goto get_key_param_failed;
+
+       /* init input device */
+       set_bit(EV_KEY, kp->input->evbit);
+       set_bit(EV_REP, kp->input->evbit);
+       kp->input->name = "adc_keypad";
+       kp->input->phys = "adc_keypad/input0";
+       kp->input->dev.parent = &pdev->dev;
+
+       kp->input->id.bustype = BUS_ISA;
+       kp->input->id.vendor = 0x0001;
+       kp->input->id.product = 0x0001;
+       kp->input->id.version = 0x0100;
+
+       kp->input->rep[REP_DELAY] = 0xffffffff;
+       kp->input->rep[REP_PERIOD] = 0xffffffff;
+
+       kp->input->keycodesize = sizeof(unsigned short);
+       kp->input->keycodemax = 0x1ff;
+
+       /*register input device*/
+       if (input_register_device(kp->input) < 0) {
+               dev_err(&pdev->dev,
+                        "unable to register keypad input device.\n");
+               return -EINVAL;
        }
-       ret = of_property_read_u32_array(pdev->dev.of_node,
-                "key_val", key_param+pdata->key_num*2, pdata->key_num);
-       if (ret) {
-               dev_err(&pdev->dev, "adc_key: failed to get key_val!\n");
-               goto get_key_param_failed;
+
+       return 0;
+}
+
+static ssize_t table_show(struct device *dev, struct device_attribute *attr,
+                       char *buf)
+{
+       struct platform_device *pdev = to_platform_device(dev);
+       struct meson_adc_kp *kp = platform_get_drvdata(pdev);
+       struct adc_key *key;
+       unsigned char key_num = 1;
+       int len = 0;
+
+       mutex_lock(&kp->kp_lock);
+       list_for_each_entry(key, &kp->adckey_head, list) {
+               len += sprintf(buf+len,
+                       "[%d]: name=%-21s code=%-5d channel=%-3d value=%-5d tolerance=%-5d\n",
+                       key_num,
+                       key->name,
+                       key->code,
+                       key->chan,
+                       key->value,
+                       key->tolerance);
+               key_num++;
        }
-       ret = of_property_read_u32_array(pdev->dev.of_node,
-                "key_tolerance", key_param+pdata->key_num*3, pdata->key_num);
-       if (ret) {
-               dev_err(&pdev->dev, "adc_key: failed to get tolerance!\n");
-               goto get_key_param_failed;
+       mutex_unlock(&kp->kp_lock);
+
+       return len;
+}
+
+static ssize_t table_store(struct device *dev, struct device_attribute *attr,
+                        const char *buf, size_t count)
+{
+       struct platform_device *pdev = to_platform_device(dev);
+       struct meson_adc_kp *kp = platform_get_drvdata(pdev);
+       struct adc_key *dkey;
+       struct adc_key *key;
+       struct adc_key *key_tmp;
+       char nbuf[TMP_BUF_MAX];
+       char *pbuf = nbuf;
+       unsigned char colon_num = 0;
+       int nsize = 0;
+       int state = 0;
+       char *pval;
+
+       /*count inclued '\0'*/
+       if (count > TMP_BUF_MAX) {
+               dev_err(dev, "write data is too long[max:%d]: %ld\n",
+                       TMP_BUF_MAX, count);
+               return -EINVAL;
        }
-       for (i = 0; i < pdata->key_num; i++) {
-               pdata->key[i].code = *(key_param+i);
-               pdata->key[i].chan = *(key_param+pdata->key_num+i);
-               pdata->key[i].value = *(key_param+pdata->key_num*2+i);
-               pdata->key[i].tolerance = *(key_param+pdata->key_num*3+i);
+
+       /*trim all invisible characters include '\0', tab, space etc*/
+       while (*buf) {
+               if (*buf > ' ')
+                       nbuf[nsize++] = *buf;
+               if (*buf == ':')
+                       colon_num++;
+               buf++;
        }
+       nbuf[nsize] = '\0';
 
-#else
-       pdata = pdev->dev.platform_data;
-#endif
-       kp = kzalloc(sizeof(struct kp), GFP_KERNEL);
-       input_dev = input_allocate_device();
-       if (!kp || !input_dev) {
-               kfree(kp);
-               input_free_device(input_dev);
-               state = -ENOMEM;
-               goto get_key_param_failed;
+       /*write "null" or "NULL" to clean up all key table*/
+       if (strcasecmp("null", nbuf) == 0) {
+               meson_adc_kp_list_free(kp);
+               return count;
        }
-       gp_kp = kp;
-       platform_set_drvdata(pdev, pdata);
-       kp->input = input_dev;
-       kp->report_code = 0;
-       kp->code = 0;
-       kp->poll_period = POLL_PERIOD_WHEN_KEY_UP;
-       kp->count = 0;
-       INIT_WORK(&(kp->work_update), update_work_func);
-       setup_timer(&kp->timer, kp_timer_sr, (unsigned long)kp);
-       mod_timer(&kp->timer, jiffies+msecs_to_jiffies(100));
-       /* setup input device */
-       set_bit(EV_KEY, input_dev->evbit);
-       set_bit(EV_REP, input_dev->evbit);
-
-       kp->key = pdata->key;
-       kp->key_num = pdata->key_num;
-
-       key = pdata->key;
-       kp->chan_num = 0;
-       for (i = 0; i < kp->key_num; i++) {
-               set_bit(key->code, input_dev->keybit);
-               /* search the key chan */
-               new_chan_flag = 1;
-               for (j = 0; j < kp->chan_num; j++) {
-                       if (key->chan == kp->chan[j]) {
-                               new_chan_flag = 0;
-                               break;
-                       }
+
+       /*to judge write data format whether valid or not*/
+       if (colon_num != 4) {
+               dev_err(dev, "write data invalid: %s\n", nbuf);
+               dev_err(dev, "=> [name]:[code]:[channel]:[value]:[tolerance]\n");
+               return -EINVAL;
+       }
+
+       dkey = kzalloc(sizeof(struct adc_key), GFP_KERNEL);
+       if (!dkey)
+               return -ENOMEM;
+
+       /*save the key data in order*/
+       pval = strsep(&pbuf, ":"); /*name*/
+       if (pval)
+               strncpy(dkey->name, pval, MAX_NAME_LEN);
+
+       pval = strsep(&pbuf, ":"); /*code*/
+       if (pval)
+               if (kstrtoint(pval, 0, &dkey->code) < 0) {
+                       state = -EINVAL;
+                       goto err;
+               }
+       pval = strsep(&pbuf, ":"); /*channel*/
+       if (pval)
+               if (kstrtoint(pval, 0, &dkey->chan) < 0) {
+                       state = -EINVAL;
+                       goto err;
+               }
+       if (!kp->pchan[dkey->chan]) {
+               dev_err(dev, "invalid channel[%u], please enable it first by DTS\n",
+                               dkey->chan);
+               state = -EINVAL;
+               goto err;
+       }
+       pval = strsep(&pbuf, ":"); /*value*/
+       if (pval)
+               if (kstrtoint(pval, 0, &dkey->value) < 0) {
+                       state = -EINVAL;
+                       goto err;
+               }
+       pval = strsep(&pbuf, ":"); /*tolerance*/
+       if (pval)
+               if (kstrtoint(pval, 0, &dkey->tolerance) < 0) {
+                       state = -EINVAL;
+                       goto err;
                }
-               if (new_chan_flag) {
-                       kp->chan[kp->chan_num] = key->chan;
-                       kp->chan_num++;
+
+       /*check channel data whether valid or not*/
+       if (dkey->chan >= SARADC_CH_NUM) {
+               dev_err(dev, "invalid channel[%d-%d]: %d\n", 0,
+                       SARADC_CH_NUM-1, dkey->chan);
+               state = -EINVAL;
+               goto err;
+       }
+
+       /*check sample data whether valid or not*/
+       if (dkey->value > SAM_MAX) {
+               dev_err(dev, "invalid sample value[%d-%d]: %d\n",
+                       SAM_MIN, SAM_MAX, dkey->value);
+               state = -EINVAL;
+               goto err;
+       }
+
+       /*check tolerance data whether valid or not*/
+       if (dkey->tolerance > TOL_MAX) {
+               dev_err(dev, "invalid tolerance[%d-%d]: %d\n",
+                       TOL_MIN, TOL_MAX, dkey->tolerance);
+               state = -EINVAL;
+               goto err;
+       }
+
+       mutex_lock(&kp->kp_lock);
+       list_for_each_entry_safe(key, key_tmp, &kp->adckey_head, list) {
+               if ((key->code == dkey->code) ||
+                       ((key->chan == dkey->chan) &&
+                       (key->value == dkey->value))) {
+                       dev_info(dev, "del older key => %s:%d:%d:%d:%d\n",
+                               key->name, key->code, key->chan,
+                               key->value, key->tolerance);
+                       clear_bit(key->code,  kp->input->keybit);
+                       list_del(&key->list);
+                       kfree(key);
                }
-               dev_info(&pdev->dev,
-                        "%s key(%d) registed.\n", key->name, key->code);
-               key++;
        }
+       set_bit(dkey->code,  kp->input->keybit);
+       list_add_tail(&dkey->list, &kp->adckey_head);
+       dev_info(dev, "add newer key => %s:%d:%d:%d:%d\n", dkey->name,
+               dkey->code, dkey->chan, dkey->value, dkey->tolerance);
+       mutex_unlock(&kp->kp_lock);
 
-       input_dev->name = "adc_keypad";
-       input_dev->phys = "adc_keypad/input0";
-       input_dev->dev.parent = &pdev->dev;
+       meson_adc_kp_get_valid_chan(kp);
 
-       input_dev->id.bustype = BUS_ISA;
-       input_dev->id.vendor = 0x0001;
-       input_dev->id.product = 0x0001;
-       input_dev->id.version = 0x0100;
+       return count;
+err:
+       kfree(dkey);
+       return state;
+}
 
-       input_dev->rep[REP_DELAY] = 0xffffffff;
-       input_dev->rep[REP_PERIOD] = 0xffffffff;
+static DEVICE_ATTR_RW(table);
 
-       input_dev->keycodesize = sizeof(unsigned short);
-       input_dev->keycodemax = 0x1ff;
+static int meson_adc_kp_probe(struct platform_device *pdev)
+{
+       struct meson_adc_kp *kp;
+       int state = 0;
 
-       ret = input_register_device(kp->input);
-       if (ret < 0) {
-               dev_err(&pdev->dev,
-                        "Unable to register keypad input device.\n");
-               kfree(kp);
-               input_free_device(input_dev);
+       send_data_to_bl301();
+       kernel_keypad_enable_mode_enable();
+
+       kp = kzalloc(sizeof(struct meson_adc_kp), GFP_KERNEL);
+       if (!kp)
+               return -ENOMEM;
+
+       platform_set_drvdata(pdev, kp);
+       mutex_init(&kp->kp_lock);
+       INIT_LIST_HEAD(&kp->adckey_head);
+       kp->report_code = 0;
+       kp->code = 0;
+       kp->poll_period = POLL_PERIOD_WHEN_KEY_UP;
+       kp->count = 0;
+       INIT_WORK(&(kp->work_update), update_work_func);
+       setup_timer(&kp->timer, meson_adc_kp_timer_sr, (unsigned long)kp);
+       if (kp_input_dev_register(pdev, kp) < 0) {
+               state = -EINVAL;
+               goto err;
+       }
+       if (meson_adc_kp_get_devtree_pdata(pdev, kp) < 0) {
+               meson_adc_kp_list_free(kp);
                state = -EINVAL;
-               goto get_key_param_failed;
+               goto err;
        }
-       register_keypad_dev(gp_kp);
-       kfree(key_param);
-       #ifdef CONFIG_AMLOGIC_LEGACY_EARLY_SUSPEND
+#ifdef CONFIG_AMLOGIC_LEGACY_EARLY_SUSPEND
        kp->early_suspend.level = EARLY_SUSPEND_LEVEL_BLANK_SCREEN;
-       kp->early_suspend.suspend = kp_early_suspend;
-       kp->early_suspend.resume = kp_late_resume;
+       kp->early_suspend.suspend = meson_adc_kp_early_suspend;
+       kp->early_suspend.resume = meson_adc_kp_late_resume;
        register_early_suspend(&kp->early_suspend);
-       #endif
+#endif
+       if (sysfs_create_file(&pdev->dev.kobj, &dev_attr_table.attr) < 0) {
+               dev_err(&pdev->dev, "create sysfs file failed!\n");
+               state = -EINVAL;
+               goto err;
+       }
+       /*enable timer*/
+       mod_timer(&kp->timer, jiffies+msecs_to_jiffies(100));
        return 0;
-
-get_key_param_failed:
-               kfree(key_param);
-get_param_mem_fail:
-               kfree(pdata->key);
-get_key_mem_fail:
-               kfree(pdata);
-get_key_node_fail:
+err:
+       if (kp->input)
+               input_free_device(kp->input);
+       kfree(kp);
        return state;
 }
 
-static int kp_remove(struct platform_device *pdev)
-       {
-       struct adc_kp_platform_data *pdata = platform_get_drvdata(pdev);
-       struct kp *kp = gp_kp;
+static int meson_adc_kp_remove(struct platform_device *pdev)
+{
+       struct meson_adc_kp *kp = platform_get_drvdata(pdev);
 
-       #ifdef CONFIG_AMLOGIC_LEGACY_EARLY_SUSPEND
+       sysfs_remove_file(&pdev->dev.kobj, &dev_attr_table.attr);
+#ifdef CONFIG_AMLOGIC_LEGACY_EARLY_SUSPEND
        unregister_early_suspend(&kp->early_suspend);
-       #endif
+#endif
        del_timer_sync(&kp->timer);
        cancel_work_sync(&kp->work_update);
        input_unregister_device(kp->input);
        input_free_device(kp->input);
-       unregister_chrdev(kp->config_major, kp->config_name);
-       if (kp->config_class) {
-               if (kp->config_dev)
-                       device_destroy(kp->config_class,
-                                MKDEV(kp->config_major, 0));
-               class_destroy(kp->config_class);
-       }
+       meson_adc_kp_list_free(kp);
        kfree(kp);
-       #ifdef CONFIG_OF
-       kfree(pdata->key);
-       kfree(pdata);
-       #endif
-       gp_kp = NULL;
        return 0;
 }
 
-#ifdef CONFIG_OF
+static int meson_adc_kp_suspend(struct platform_device *pdev,
+                               pm_message_t state)
+{
+       return 0;
+}
+
+static int meson_adc_kp_resume(struct platform_device *pdev)
+{
+       struct meson_adc_kp *kp = platform_get_drvdata(pdev);
+
+       if (get_resume_method() == POWER_KEY_WAKEUP) {
+               dev_info(&pdev->dev, "adc keypad wakeup\n");
+               input_report_key(kp->input,  KEY_POWER,  1);
+               input_sync(kp->input);
+               input_report_key(kp->input,  KEY_POWER,  0);
+               input_sync(kp->input);
+       }
+       return 0;
+}
+
 static const struct of_device_id key_dt_match[] = {
-       {       .compatible = "amlogic, adc_keypad",},
+       {.compatible = "amlogic, adc_keypad",},
        {},
 };
-#else
-#define key_dt_match NULL
-#endif
 
 static struct platform_driver kp_driver = {
-       .probe      = kp_probe,
-       .remove     = kp_remove,
-       .suspend    = NULL,
-       .resume     = NULL,
+       .probe      = meson_adc_kp_probe,
+       .remove     = meson_adc_kp_remove,
+       .suspend    = meson_adc_kp_suspend,
+       .resume     = meson_adc_kp_resume,
        .driver     = {
-               .name   = "m1-adckp",
+               .name   = DRIVE_NAME,
                .of_match_table = key_dt_match,
        },
 };
 
-static int __init kp_init(void)
+static int __init meson_adc_kp_init(void)
 {
        return platform_driver_register(&kp_driver);
 }
 
-static void __exit kp_exit(void)
+static void __exit meson_adc_kp_exit(void)
 {
        platform_driver_unregister(&kp_driver);
 }
 
-module_init(kp_init);
-module_exit(kp_exit);
-
-/*
- * Note: If the driver is compiled as a module, the __setup() do nothing
- *       __setup() is defined in include/linux/init.h
- */
-#ifndef MODULE
-
 static int __init adc_key_mode_para_setup(char *s)
 {
-       if (s != NULL)
+       if (s)
                sprintf(adc_key_mode_name, "%s", s);
 
        return 0;
@@ -507,15 +657,15 @@ __setup("adckeyswitch=", adc_key_mode_para_setup);
 
 static int __init kernel_keypad_enable_setup(char *s)
 {
-       if (s != NULL)
+       if (s)
                sprintf(kernelkey_en_name, "%s", s);
 
        return 0;
 }
 __setup("kernelkey_enable=", kernel_keypad_enable_setup);
 
-#endif
-
-MODULE_AUTHOR("Robin Zhu");
+late_initcall(meson_adc_kp_init);
+module_exit(meson_adc_kp_exit);
+MODULE_AUTHOR("Amlogic");
 MODULE_DESCRIPTION("ADC Keypad Driver");
 MODULE_LICENSE("GPL");
index 4fd90e3..c6c8dc5 100644 (file)
 
 #ifndef __LINUX_ADC_KEYPAD_H
 #define __LINUX_ADC_KEYPAD_H
+#include <linux/list.h>
+#include <linux/input.h>
+#include <linux/kobject.h>
+#include <linux/timer.h>
+#include <linux/workqueue.h>
+#include <linux/mutex.h>
+#include <linux/iio/consumer.h>
+#include <dt-bindings/iio/adc/amlogic-saradc.h>
+#ifdef CONFIG_AMLOGIC_LEGACY_EARLY_SUSPEND
+#include <linux/amlogic/pm.h>
+#endif
+
+#define DRIVE_NAME "adc_keypad"
+#define MAX_NAME_LEN 20
+
+enum TOLERANCE_RANGE {
+       TOL_MIN = 0,
+       TOL_MAX = 255
+};
+
+enum SAMPLE_VALUE_RANGE {
+       SAM_MIN = 0,
+       SAM_MAX = 4095 /*12bit adc*/
+};
 
 struct adc_key {
-       int code;       /* input key code */
-       const char *name;
-       int chan;
-       int value;      /* voltage/3.3v * 1023 */
+       char name[MAX_NAME_LEN];
+       unsigned int chan;
+       unsigned int code;  /* input key code */
+       int value; /* voltage/3.3v * 1023 */
        int tolerance;
+       struct list_head list;
 };
 
-struct adc_kp_platform_data {
-       struct adc_key *key;
-       int key_num;
-       int repeat_delay;
-       int repeat_period;
+struct meson_adc_kp {
+       unsigned char chan[SARADC_CH_NUM];
+       unsigned char chan_num;   /*number of channel exclude duplicate*/
+       unsigned char count;
+       unsigned int report_code;
+       unsigned int code;
+       unsigned int poll_period; /*key scan period*/
+       struct mutex kp_lock;
+       struct list_head adckey_head;
+       struct input_dev *input;
+       struct timer_list timer;
+       struct work_struct work_update;
+       struct iio_channel *pchan[SARADC_CH_NUM];
+#ifdef CONFIG_AMLOGIC_LEGACY_EARLY_SUSPEND
+       struct early_suspend early_suspend;
+#endif
 };
 
 #endif