android_kernel_oneplus_msm8998/drivers/input/misc/tri_state_key.c
liochen 8148b9d900 Synchronize codes for OnePlus5 & 5T OxygenOS 9.0.0
kernel device tree source code for OnePlus 5 & 5T P device

Change-Id: I84f40e66833ea1ce30eb1d9a710d6e1529e9e637
2018-12-26 11:02:39 +08:00

386 lines
8.8 KiB
C

/*
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/slab.h>
#include <linux/input.h>
#include <linux/ioport.h>
#include <linux/platform_device.h>
#include <linux/gpio.h>
#include <linux/gpio_keys.h>
#include <linux/of_platform.h>
#include <linux/of_gpio.h>
#include <linux/switch.h>
#include <linux/workqueue.h>
#include <linux/interrupt.h>
#include <asm/uaccess.h>
#include <linux/regulator/consumer.h>
#include <linux/timer.h>
#include <linux/delay.h>
#define DRV_NAME "tri-state-key"
/*
KEY1(GPIO1) KEY2(GPIO92)
pin1 connect to pin4 0 1 | MUTE
pin2 connect to pin5 1 1 | Do Not Disturb
pin4 connect to pin3 1 0 | Normal
*/
typedef enum
{
MODE_UNKNOWN,
MODE_MUTE,
MODE_DO_NOT_DISTURB,
MODE_NORMAL,
MODE_MAX_NUM
} tri_mode_t;
struct switch_dev_data
{
int irq_key3;
int irq_key2;
int irq_key1;
int key1_gpio;
int key2_gpio;
int key3_gpio;
struct regulator *vdd_io;
struct work_struct work;
struct switch_dev sdev;
struct device *dev;
struct timer_list s_timer;
struct pinctrl * key_pinctrl;
struct pinctrl_state * set_state;
};
static struct switch_dev_data *switch_data;
static DEFINE_MUTEX(sem);
static int set_gpio_by_pinctrl(void)
{
return pinctrl_select_state(switch_data->key_pinctrl, switch_data->set_state);
}
/*set 3 gpio default state high */
static int key_pre[3] = {1, 1, 1};
static int delay_time;
static void switch_dev_work(struct work_struct *work)
{
int key[3];
int i, j;
bool have_wrong_key = false;
int state_same = 0;
msleep(delay_time);
key[0] = gpio_get_value(switch_data->key1_gpio);
key[1] = gpio_get_value(switch_data->key2_gpio);
key[2] = gpio_get_value(switch_data->key3_gpio);
for (i = 0; i < 3; i++) {
/*if 3 gpio status is the same as before ,ignore them*/
if (key_pre[i] == key[i])
state_same++;
if (state_same == 3)
return;
}
for (i = 0; i < 3; i++) {
/*
* 1,if the gpio key is low ,and previous status is low ,
* we suspect that the gpio is in wrong states
*/
if (key[i] + key_pre[i] == 0) {
pr_err("[sk]key[%d] is in wrong state\n", i);
have_wrong_key = true;
delay_time = 300;
break;
}
}
mutex_lock(&sem);
if (have_wrong_key == true) {
if (key[0]+key[1]+key[2] == 2) {
if (i == 0)
switch_set_state(
&switch_data->sdev,
MODE_MUTE);
if (i == 1)
switch_set_state(
&switch_data->sdev,
MODE_DO_NOT_DISTURB);
if (i == 2)
switch_set_state(
&switch_data->sdev,
MODE_NORMAL);
}
else {
for (j = 0; j < 3; j++) {
/* we got the gpio is wrong state,
* then check which gpio
*/
if ((key[j] == 0) && (i != j)) {
if (j == 0)
switch_set_state(
&switch_data->sdev,
MODE_MUTE);
if (j == 1)
switch_set_state(
&switch_data->sdev,
MODE_DO_NOT_DISTURB);
if (j == 2)
switch_set_state(
&switch_data->sdev,
MODE_NORMAL);
}
}
}
} else {
if (!key[0])
switch_set_state(
&switch_data->sdev,
MODE_MUTE);
if (!key[1])
switch_set_state(
&switch_data->sdev,
MODE_DO_NOT_DISTURB);
if (!key[2])
switch_set_state(
&switch_data->sdev,
MODE_NORMAL);
}
for (i = 0; i < 3; i++)
key_pre[i] = key[i];
mutex_unlock(&sem);
}
static irqreturn_t switch_dev_interrupt(int irq, void *_dev)
{
schedule_work(&switch_data->work);
return IRQ_HANDLED;
}
static void timer_handle(unsigned long arg)
{
schedule_work(&switch_data->work);
}
#ifdef CONFIG_OF
static int switch_dev_get_devtree_pdata(struct device *dev)
{
struct device_node *node;
node = dev->of_node;
if (!node)
return -EINVAL;
switch_data->key3_gpio= of_get_named_gpio(node, "tristate,gpio_key3", 0);
if ((!gpio_is_valid(switch_data->key3_gpio)))
return -EINVAL;
pr_err("switch_data->key3_gpio=%d \n", switch_data->key3_gpio);
switch_data->key2_gpio= of_get_named_gpio(node, "tristate,gpio_key2", 0);
if ((!gpio_is_valid(switch_data->key2_gpio)))
return -EINVAL;
pr_err("switch_data->key2_gpio=%d \n", switch_data->key2_gpio);
switch_data->key1_gpio= of_get_named_gpio(node, "tristate,gpio_key1", 0);
if ((!gpio_is_valid(switch_data->key1_gpio)))
return -EINVAL;
pr_err("switch_data->key1_gpio=%d \n", switch_data->key1_gpio);
return 0;
}
#else
static inline int
switch_dev_get_devtree_pdata(struct device *dev)
{
pr_info("%s inline function", __func__);
return 0;
}
#endif
static int tristate_dev_probe(struct platform_device *pdev)
{
struct device *dev;
int ret = 0;
dev= &pdev->dev;
switch_data = kzalloc(sizeof(struct switch_dev_data), GFP_KERNEL);
if (!switch_data)
return -ENOMEM;
switch_data->dev = dev;
switch_data->sdev.name = DRV_NAME;
switch_data->key_pinctrl = devm_pinctrl_get(switch_data->dev);
if (IS_ERR_OR_NULL(switch_data->key_pinctrl)) {
dev_err(switch_data->dev, "Failed to get pinctrl \n");
goto err_switch_dev_register;
}
switch_data->set_state = pinctrl_lookup_state(switch_data->key_pinctrl,
"pmx_tri_state_key_active");
if (IS_ERR_OR_NULL(switch_data->set_state)) {
dev_err(switch_data->dev, "Failed to lookup_state \n");
goto err_switch_dev_register;
}
set_gpio_by_pinctrl();
ret = switch_dev_get_devtree_pdata(dev);
if (ret) {
dev_err(dev, "parse device tree fail!!!\n");
goto err_switch_dev_register;
}
ret = switch_dev_register(&switch_data->sdev);
if (ret < 0)
goto err_switch_dev_register;
//config irq gpio and request irq
ret = gpio_request(switch_data->key1_gpio, "tristate_key1");
if (ret < 0)
goto err_request_gpio;
ret = gpio_direction_input(switch_data->key1_gpio);
if (ret < 0)
goto err_set_gpio_input;
switch_data->irq_key1 = gpio_to_irq(switch_data->key1_gpio);
if (switch_data->irq_key1 < 0) {
ret = switch_data->irq_key1;
goto err_detect_irq_num_failed;
}
ret = request_irq(switch_data->irq_key1, switch_dev_interrupt,
IRQF_TRIGGER_FALLING|IRQF_TRIGGER_RISING,
"tristate_key1", switch_data);
if (ret < 0)
goto err_request_irq;
ret = gpio_request(switch_data->key2_gpio,
"tristate_key2");
if (ret < 0)
goto err_request_gpio;
ret = gpio_direction_input(switch_data->key2_gpio);
if (ret < 0)
goto err_set_gpio_input;
switch_data->irq_key2 = gpio_to_irq(switch_data->key2_gpio);
if (switch_data->irq_key2 < 0) {
ret = switch_data->irq_key2;
goto err_detect_irq_num_failed;
}
ret = request_irq(switch_data->irq_key2, switch_dev_interrupt,
IRQF_TRIGGER_FALLING|IRQF_TRIGGER_RISING,
"tristate_key2", switch_data);
if (ret < 0)
goto err_request_irq;
ret = gpio_request(switch_data->key3_gpio,
"tristate_key3");
if (ret < 0)
goto err_request_gpio;
ret = gpio_direction_input(switch_data->key3_gpio);
if (ret < 0)
goto err_set_gpio_input;
switch_data->irq_key3 = gpio_to_irq(switch_data->key3_gpio);
if (switch_data->irq_key3 < 0) {
ret = switch_data->irq_key3;
goto err_detect_irq_num_failed;
}
ret = request_irq(switch_data->irq_key3, switch_dev_interrupt,
IRQF_TRIGGER_FALLING|IRQF_TRIGGER_RISING,
"tristate_key3", switch_data);
if (ret < 0)
goto err_request_irq;
INIT_WORK(&switch_data->work, switch_dev_work);
init_timer(&switch_data->s_timer);
switch_data->s_timer.function = &timer_handle;
switch_data->s_timer.expires = jiffies + 5*HZ;
add_timer(&switch_data->s_timer);
enable_irq_wake(switch_data->irq_key1);
enable_irq_wake(switch_data->irq_key2);
enable_irq_wake(switch_data->irq_key3);
return 0;
err_request_gpio:
switch_dev_unregister(&switch_data->sdev);
err_request_irq:
err_detect_irq_num_failed:
err_set_gpio_input:
gpio_free(switch_data->key2_gpio);
gpio_free(switch_data->key1_gpio);
gpio_free(switch_data->key3_gpio);
err_switch_dev_register:
kfree(switch_data);
return ret;
}
static int tristate_dev_remove(struct platform_device *pdev)
{
cancel_work_sync(&switch_data->work);
gpio_free(switch_data->key1_gpio);
gpio_free(switch_data->key2_gpio);
gpio_free(switch_data->key3_gpio);
switch_dev_unregister(&switch_data->sdev);
kfree(switch_data);
return 0;
}
#ifdef CONFIG_OF
static struct of_device_id tristate_dev_of_match[] =
{
{ .compatible = "oneplus,tri-state-key", },
{ },
};
MODULE_DEVICE_TABLE(of, tristate_dev_of_match);
#endif
static struct platform_driver tristate_dev_driver = {
.probe = tristate_dev_probe,
.remove = tristate_dev_remove,
.driver = {
.name = DRV_NAME,
.owner = THIS_MODULE,
.of_match_table = tristate_dev_of_match,
},
};
static int __init oem_tristate_init(void)
{
return platform_driver_register(&tristate_dev_driver);
}
module_init(oem_tristate_init);
static void __exit oem_tristate_exit(void)
{
platform_driver_unregister(&tristate_dev_driver);
}
module_exit(oem_tristate_exit);
MODULE_DESCRIPTION("oem tri_state_key driver");
MODULE_LICENSE("GPL v2");