blob: 7182e595ab8976cbfdfb65de1769e5f79198bfa4 [file] [log] [blame]
/*
* drivers/amlogic/input/keyboard/adc_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/module.h>
#include <linux/init.h>
#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/delay.h>
#include <linux/platform_device.h>
#include <linux/errno.h>
#include <linux/io.h>
#include <linux/slab.h>
#include <linux/uaccess.h>
#include <linux/string.h>
#include <linux/of.h>
#include <linux/iio/consumer.h>
#include <linux/input-polldev.h>
#include <linux/amlogic/scpi_protocol.h>
#include "adc_keypad.h"
#define POLL_INTERVAL_DEFAULT 25
#define KEY_JITTER_COUNT 1
#define TMP_BUF_MAX 128
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 meson_adc_kp_search_key(struct meson_adc_kp *kp)
{
struct adc_key *key;
int value, i;
mutex_lock(&kp->kp_lock);
for (i = 0; i < kp->chan_num; i++) {
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;
}
}
}
}
mutex_unlock(&kp->kp_lock);
return KEY_RESERVED;
}
static void meson_adc_kp_poll(struct input_polled_dev *dev)
{
struct meson_adc_kp *kp = dev->private;
int code = meson_adc_kp_search_key(kp);
if (kp->report_code && kp->report_code != code) {
dev_info(&kp->poll_dev->input->dev,
"key %d up\n", kp->report_code);
input_report_key(kp->poll_dev->input, kp->report_code, 0);
input_sync(kp->poll_dev->input);
kp->report_code = 0;
}
if (code) {
if (kp->count > 0 && code != kp->prev_code)
kp->count = 0;
if (kp->count < KEY_JITTER_COUNT)
kp->count++;
else {
if (keypad_enable_flag && kp->report_code != code) {
dev_info(&kp->poll_dev->input->dev,
"key %d down\n", code);
input_report_key(kp->poll_dev->input, code, 1);
input_sync(kp->poll_dev->input);
kp->report_code = code;
}
kp->count = 0;
}
kp->prev_code = code;
}
}
static void send_data_to_bl301(void)
{
u32 val;
if (!strcmp(adc_key_mode_name, "POWER_WAKEUP_POWER")) {
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; /*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; /*no key can resume*/
scpi_send_usr_data(SCPI_CL_POWER, &val, sizeof(val));
}
}
static void kernel_keypad_enable_mode_enable(void)
{
if (!strcmp(kernelkey_en_name, "KEYPAD_UNLOCK"))
keypad_enable_flag = 1; /*unlock, normal mode*/
else if (!strcmp(kernelkey_en_name, "KEYPAD_LOCK"))
keypad_enable_flag = 0; /*lock, press key will be not useful*/
else
keypad_enable_flag = 1;
}
/*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)
{
unsigned char incr;
struct adc_key *key;
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 value;
int state = 0;
unsigned char cnt;
const char *uname;
unsigned int key_num;
struct adc_key *key;
struct of_phandle_args chanspec;
if (!pdev->dev.of_node) {
dev_err(&pdev->dev, "failed to get device node\n");
return -EINVAL;
}
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;
}
ret = of_property_read_u32(pdev->dev.of_node, "poll-interval", &value);
if (ret)
kp->poll_period = POLL_INTERVAL_DEFAULT;
else
kp->poll_period = value;
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]]);
}
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;
}
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", cnt, &uname);
if (ret < 0) {
dev_err(&pdev->dev, "invalid key name index[%d]\n",
cnt);
state = -EINVAL;
goto err;
}
snprintf(key->name, MAX_NAME_LEN, "%s", uname);
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;
}
list_add_tail(&key->list, &kp->adckey_head);
}
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);
}
mutex_unlock(&kp->kp_lock);
}
static ssize_t table_show(struct class *cls, struct class_attribute *attr,
char *buf)
{
struct meson_adc_kp *kp = container_of(cls,
struct meson_adc_kp, kp_class);
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++;
}
mutex_unlock(&kp->kp_lock);
return len;
}
static ssize_t table_store(struct class *cls, struct class_attribute *attr,
const char *buf, size_t count)
{
struct meson_adc_kp *kp = container_of(cls,
struct meson_adc_kp, kp_class);
struct device *dev = kp->poll_dev->input->dev.parent;
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]: %zu\n",
TMP_BUF_MAX, count);
return -EINVAL;
}
/*trim all invisible characters include '\0', tab, space etc*/
while (*buf) {
if (*buf > ' ')
nbuf[nsize++] = *buf;
if (*buf == ':')
colon_num++;
buf++;
}
nbuf[nsize] = '\0';
/*write "null" or "NULL" to clean up all key table*/
if (strcasecmp("null", nbuf) == 0) {
meson_adc_kp_list_free(kp);
return count;
}
/*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)
snprintf(dkey->name, MAX_NAME_LEN, "%s", pval);
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;
}
/*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->poll_dev->input->keybit);
list_del(&key->list);
kfree(key);
}
}
set_bit(dkey->code, kp->poll_dev->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);
meson_adc_kp_get_valid_chan(kp);
return count;
err:
kfree(dkey);
return state;
}
struct class_attribute meson_adckey_attrs[] = {
__ATTR_RW(table),
__ATTR_NULL
};
static void meson_adc_kp_init_keybit(struct meson_adc_kp *kp)
{
struct adc_key *key;
list_for_each_entry(key, &kp->adckey_head, list)
set_bit(key->code,
kp->poll_dev->input->keybit); /*set event code*/
}
static int meson_adc_kp_probe(struct platform_device *pdev)
{
struct meson_adc_kp *kp;
struct input_dev *input;
int ret = 0;
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->prev_code = 0;
kp->count = 0;
ret = meson_adc_kp_get_devtree_pdata(pdev, kp);
if (ret)
goto err;
/*alloc input poll device*/
kp->poll_dev = devm_input_allocate_polled_device(&pdev->dev);
if (!kp->poll_dev) {
dev_err(&pdev->dev, "alloc input poll device failed!\n");
ret = -ENOMEM;
goto err;
}
kp->poll_dev->poll = meson_adc_kp_poll;
kp->poll_dev->poll_interval = kp->poll_period;
kp->poll_dev->private = kp;
input = kp->poll_dev->input;
set_bit(EV_KEY, input->evbit);
set_bit(EV_REP, input->evbit);
meson_adc_kp_init_keybit(kp);
input->name = "adc_keypad";
input->phys = "adc_keypad/input0";
input->dev.parent = &pdev->dev;
input->id.bustype = BUS_ISA;
input->id.vendor = 0x0001;
input->id.product = 0x0001;
input->id.version = 0x0100;
input->rep[REP_DELAY] = 0xffffffff;
input->rep[REP_PERIOD] = 0xffffffff;
input->keycodesize = sizeof(unsigned short);
input->keycodemax = 0x1ff;
/*init class*/
kp->kp_class.name = DRIVE_NAME;
kp->kp_class.owner = THIS_MODULE;
kp->kp_class.class_attrs = meson_adckey_attrs;
ret = class_register(&kp->kp_class);
if (ret) {
dev_err(&pdev->dev, "fail to create adc keypad class.\n");
goto err;
}
/*register input poll device*/
ret = input_register_polled_device(kp->poll_dev);
if (ret) {
dev_err(&pdev->dev,
"unable to register keypad input poll device.\n");
goto err1;
}
return ret;
err1:
class_unregister(&kp->kp_class);
err:
meson_adc_kp_list_free(kp);
kfree(kp);
return ret;
}
static int meson_adc_kp_remove(struct platform_device *pdev)
{
struct meson_adc_kp *kp = platform_get_drvdata(pdev);
class_unregister(&kp->kp_class);
cancel_delayed_work(&kp->poll_dev->work);
meson_adc_kp_list_free(kp);
kfree(kp);
return 0;
}
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 adc_key *key;
struct meson_adc_kp *kp = platform_get_drvdata(pdev);
if (get_resume_method() == POWER_KEY_WAKEUP) {
list_for_each_entry(key, &kp->adckey_head, list) {
if (key->code == KEY_POWER) {
dev_info(&pdev->dev, "adc keypad wakeup\n");
input_report_key(kp->poll_dev->input,
KEY_POWER, 1);
input_sync(kp->poll_dev->input);
input_report_key(kp->poll_dev->input,
KEY_POWER, 0);
input_sync(kp->poll_dev->input);
break;
}
}
}
return 0;
}
static void meson_adc_kp_shutdown(struct platform_device *pdev)
{
struct meson_adc_kp *kp = platform_get_drvdata(pdev);
cancel_delayed_work(&kp->poll_dev->work);
}
static const struct of_device_id key_dt_match[] = {
{.compatible = "amlogic, adc_keypad",},
{},
};
static struct platform_driver kp_driver = {
.probe = meson_adc_kp_probe,
.remove = meson_adc_kp_remove,
.suspend = meson_adc_kp_suspend,
.resume = meson_adc_kp_resume,
.shutdown = meson_adc_kp_shutdown,
.driver = {
.name = DRIVE_NAME,
.of_match_table = key_dt_match,
},
};
static int __init meson_adc_kp_init(void)
{
return platform_driver_register(&kp_driver);
}
static void __exit meson_adc_kp_exit(void)
{
platform_driver_unregister(&kp_driver);
}
/*
* 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)
sprintf(adc_key_mode_name, "%s", s);
return 0;
}
__setup("adckeyswitch=", adc_key_mode_para_setup);
static int __init kernel_keypad_enable_setup(char *s)
{
if (s)
sprintf(kernelkey_en_name, "%s", s);
return 0;
}
__setup("kernelkey_enable=", kernel_keypad_enable_setup);
#endif
late_initcall(meson_adc_kp_init);
module_exit(meson_adc_kp_exit);
MODULE_AUTHOR("Amlogic");
MODULE_DESCRIPTION("ADC Keypad Driver");
MODULE_LICENSE("GPL");