From c9ce8a78a095d59c77bc05a7e31d1e450e6175e3 Mon Sep 17 00:00:00 2001 From: "xingyu.chen" Date: Wed, 28 Jun 2017 14:17:12 +0800 Subject: [PATCH] adc_keypad: update interface to access adc and add sysfs interface 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 --- arch/arm64/boot/dts/amlogic/axg_s400.dts | 14 + arch/arm64/boot/dts/amlogic/axg_s420.dts | 14 + arch/arm64/configs/meson64_defconfig | 1 + drivers/amlogic/input/keyboard/adc_keypad.c | 802 +++++++++++++++++----------- include/linux/amlogic/adc_keypad.h | 54 +- 5 files changed, 550 insertions(+), 335 deletions(-) diff --git a/arch/arm64/boot/dts/amlogic/axg_s400.dts b/arch/arm64/boot/dts/amlogic/axg_s400.dts index 4da3073..2e22859 100644 --- a/arch/arm64/boot/dts/amlogic/axg_s400.dts +++ b/arch/arm64/boot/dts/amlogic/axg_s400.dts @@ -760,6 +760,20 @@ "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 = ; + 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 */ diff --git a/arch/arm64/boot/dts/amlogic/axg_s420.dts b/arch/arm64/boot/dts/amlogic/axg_s420.dts index 1202dd2..4185967 100644 --- a/arch/arm64/boot/dts/amlogic/axg_s420.dts +++ b/arch/arm64/boot/dts/amlogic/axg_s420.dts @@ -531,6 +531,20 @@ }; }; + 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 = ; + 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>; diff --git a/arch/arm64/configs/meson64_defconfig b/arch/arm64/configs/meson64_defconfig index ea86d33..6809b9d 100644 --- a/arch/arm64/configs/meson64_defconfig +++ b/arch/arm64/configs/meson64_defconfig @@ -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 diff --git a/drivers/amlogic/input/keyboard/adc_keypad.c b/drivers/amlogic/input/keyboard/adc_keypad.c index da73e02..e63173c 100644 --- a/drivers/amlogic/input/keyboard/adc_keypad.c +++ b/drivers/amlogic/input/keyboard/adc_keypad.c @@ -17,87 +17,57 @@ #include #include -#include #include -#include #include #include #include -#include #include -#include #include -#include - -#include #include #include -#include -#include +#include #include -#ifdef CONFIG_AMLOGIC_LEGACY_EARLY_SUSPEND -#include -#endif +#include +#include +#include -#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"); diff --git a/include/linux/amlogic/adc_keypad.h b/include/linux/amlogic/adc_keypad.h index 4fd90e3..c6c8dc5 100644 --- a/include/linux/amlogic/adc_keypad.h +++ b/include/linux/amlogic/adc_keypad.h @@ -17,20 +17,56 @@ #ifndef __LINUX_ADC_KEYPAD_H #define __LINUX_ADC_KEYPAD_H +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef CONFIG_AMLOGIC_LEGACY_EARLY_SUSPEND +#include +#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 -- 2.7.4