Revert "tri_state_key: import driver from msm8996"
cheeseburger is a biatch!
This reverts commit 061081b187
.
Change-Id: Icecd4afbf9d298e7513edba02ac20ac27e85d014
This commit is contained in:
parent
6861312cbe
commit
5e0993b0c8
1 changed files with 315 additions and 274 deletions
|
@ -1,344 +1,385 @@
|
|||
/*
|
||||
* drivers/input/misc/tri_state_key.c
|
||||
*
|
||||
* Copyright (C) 2018, Sultanxda <sultanxda@gmail.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 and
|
||||
* only version 2 as published by the Free Software Foundation.
|
||||
*
|
||||
* 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.
|
||||
* 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/gpio.h>
|
||||
#include <linux/input.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/of_gpio.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/proc_fs.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/uaccess.h>
|
||||
#include <linux/gpio.h>
|
||||
#include <linux/gpio_keys.h>
|
||||
#include <linux/of_platform.h>
|
||||
#include <linux/of_gpio.h>
|
||||
|
||||
#define DRIVER_NAME "tri-state-key"
|
||||
#define NR_STATES (3)
|
||||
#include <linux/switch.h>
|
||||
#include <linux/workqueue.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <asm/uaccess.h>
|
||||
|
||||
enum key_vals {
|
||||
KEYCODE_TOTAL_SILENCE = 600,
|
||||
KEYCODE_MODE_ALARMS_ONLY,
|
||||
KEYCODE_MODE_PRIORITY_ONLY,
|
||||
KEYCODE_MODE_NONE,
|
||||
KEYCODE_MODE_MAX
|
||||
};
|
||||
#include <linux/regulator/consumer.h>
|
||||
|
||||
static const char *const proc_names[] = {
|
||||
"keyCode_top", "keyCode_middle", "keyCode_bottom"
|
||||
};
|
||||
#include <linux/timer.h>
|
||||
|
||||
#define DRV_NAME "tri-state-key"
|
||||
|
||||
/*
|
||||
* Original tri-state modes designed by OnePlus
|
||||
*/
|
||||
|
||||
typedef enum {
|
||||
MODE_UNKNOWN,
|
||||
MODE_MUTE,
|
||||
MODE_DO_NOT_DISTURB,
|
||||
MODE_NORMAL,
|
||||
} tri_mode_t;
|
||||
|
||||
/*
|
||||
* Target key codes sending to OPPO's Keyhandler
|
||||
* see KeyHandler.java in device/oppo/common/keyhandler
|
||||
*/
|
||||
|
||||
#define KEY_MODE_TOTAL_SILENCE 600
|
||||
#define KEY_MODE_ALARMS_ONLY 601
|
||||
#define KEY_MODE_PRIORITY_ONLY 602
|
||||
#define KEY_MODE_NONE 603
|
||||
|
||||
static int current_mode = MODE_UNKNOWN;
|
||||
|
||||
/*
|
||||
* Default mapping between OP's sti-state switch and OPPO's key codes
|
||||
* see Constants.java in device/oppo/common/configpanel
|
||||
*/
|
||||
|
||||
static int keyCode_slider_top = KEY_MODE_ALARMS_ONLY;
|
||||
static int keyCode_slider_middle = KEY_MODE_PRIORITY_ONLY;
|
||||
static int keyCode_slider_bottom = KEY_MODE_NONE;
|
||||
|
||||
struct switch_dev_data {
|
||||
int irq_key1;
|
||||
int irq_key2;
|
||||
int irq_key3;
|
||||
|
||||
int key1_gpio; // Mute
|
||||
int key2_gpio; // Do Not Disturb
|
||||
int key3_gpio; // Normal
|
||||
|
||||
struct work_struct work;
|
||||
|
||||
struct tristate_data {
|
||||
struct device *dev;
|
||||
struct input_dev *input;
|
||||
struct mutex irq_lock;
|
||||
enum key_vals key_codes[NR_STATES];
|
||||
int key_gpios[NR_STATES];
|
||||
uint8_t curr_state;
|
||||
struct switch_dev sdev;
|
||||
|
||||
struct timer_list s_timer;
|
||||
|
||||
struct pinctrl *key_pinctrl;
|
||||
struct pinctrl_state *set_state;
|
||||
};
|
||||
|
||||
static struct tristate_data *tdata_g;
|
||||
static struct switch_dev_data *switch_data;
|
||||
|
||||
static void send_input(struct tristate_data *t, int keycode)
|
||||
static DEFINE_MUTEX(sem);
|
||||
|
||||
static void send_input(int keyCode)
|
||||
{
|
||||
input_report_key(t->input, keycode, 1);
|
||||
input_sync(t->input);
|
||||
input_report_key(t->input, keycode, 0);
|
||||
input_sync(t->input);
|
||||
input_report_key(switch_data->input, keyCode, 1);
|
||||
input_sync(switch_data->input);
|
||||
input_report_key(switch_data->input, keyCode, 0);
|
||||
input_sync(switch_data->input);
|
||||
}
|
||||
|
||||
static void tristate_process_state(struct tristate_data *t)
|
||||
static void switch_dev_work(struct work_struct *work)
|
||||
{
|
||||
unsigned long key_state = 0;
|
||||
int i;
|
||||
int key1, key2, key3;
|
||||
int mode = MODE_UNKNOWN;
|
||||
int keyCode;
|
||||
|
||||
/* Store the GPIO values of the keys as a bitmap */
|
||||
for (i = 0; i < NR_STATES; i++)
|
||||
key_state |= !!gpio_get_value(t->key_gpios[i]) << i;
|
||||
mutex_lock(&sem);
|
||||
|
||||
/* Spurious interrupt; at least one of the pins should be zero */
|
||||
if (key_state == 0x7)
|
||||
return;
|
||||
key1 = gpio_get_value(switch_data->key1_gpio);
|
||||
key2 = gpio_get_value(switch_data->key2_gpio);
|
||||
key3 = gpio_get_value(switch_data->key3_gpio);
|
||||
|
||||
/* Redundant interrupt */
|
||||
if (key_state == t->curr_state)
|
||||
return;
|
||||
if (key1 == 0) {
|
||||
mode = MODE_MUTE;
|
||||
keyCode = keyCode_slider_top;
|
||||
} else if (key2 == 0) {
|
||||
mode = MODE_DO_NOT_DISTURB;
|
||||
keyCode = keyCode_slider_middle;
|
||||
} else if (key3 == 0) {
|
||||
mode = MODE_NORMAL;
|
||||
keyCode = keyCode_slider_bottom;
|
||||
}
|
||||
|
||||
t->curr_state = key_state;
|
||||
if (current_mode != mode && mode != MODE_UNKNOWN) {
|
||||
current_mode = mode;
|
||||
switch_set_state(&switch_data->sdev, current_mode);
|
||||
send_input(keyCode);
|
||||
printk(DRV_NAME " changed to mode: %d\n", switch_data->sdev.state);
|
||||
}
|
||||
|
||||
/*
|
||||
* Find the first bit set to zero; this is the current position. Bit 0
|
||||
* corresponds to the top position, bit 1 corresponds to the middle
|
||||
* position, and bit 2 corresponds to the bottom position.
|
||||
*/
|
||||
i = ffz(key_state);
|
||||
send_input(t, t->key_codes[i]);
|
||||
mutex_unlock(&sem);
|
||||
}
|
||||
|
||||
static irqreturn_t tristate_irq_handler(int irq, void *dev_id)
|
||||
irqreturn_t switch_dev_interrupt(int irq, void *_dev)
|
||||
{
|
||||
struct tristate_data *t = dev_id;
|
||||
|
||||
/* This handler is shared between three interrupt lines */
|
||||
mutex_lock(&t->irq_lock);
|
||||
tristate_process_state(t);
|
||||
mutex_unlock(&t->irq_lock);
|
||||
|
||||
schedule_work(&switch_data->work);
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static int keycode_get_idx(const char *filename)
|
||||
static void timer_handle(unsigned long arg)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < NR_STATES; i++) {
|
||||
if (!strcmp(proc_names[i], filename))
|
||||
break;
|
||||
}
|
||||
|
||||
return i;
|
||||
schedule_work(&switch_data->work);
|
||||
}
|
||||
|
||||
static int keycode_show(struct seq_file *seq, void *offset)
|
||||
#ifdef CONFIG_OF
|
||||
static int switch_dev_get_devtree_pdata(struct device *dev)
|
||||
{
|
||||
struct tristate_data *t = tdata_g;
|
||||
int idx = (int)(long)seq->private;
|
||||
struct device_node *node;
|
||||
|
||||
seq_printf(seq, "%d\n", t->key_codes[idx]);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static ssize_t tristate_keycode_write(struct file *file,
|
||||
const char __user *page, size_t count, loff_t *offset)
|
||||
{
|
||||
struct tristate_data *t = tdata_g;
|
||||
char buf[sizeof("600")];
|
||||
int data, idx;
|
||||
|
||||
memset(buf, 0, sizeof(buf));
|
||||
|
||||
if (copy_from_user(buf, page, min_t(int, count, 3))) {
|
||||
dev_err(t->dev, "Failed to read procfs input\n");
|
||||
return count;
|
||||
}
|
||||
|
||||
if (kstrtoint(buf, 10, &data) < 0)
|
||||
return count;
|
||||
|
||||
if (data < KEYCODE_TOTAL_SILENCE || data >= KEYCODE_MODE_MAX)
|
||||
return count;
|
||||
|
||||
idx = keycode_get_idx(file->f_path.dentry->d_iname);
|
||||
|
||||
mutex_lock(&t->irq_lock);
|
||||
t->key_codes[idx] = data;
|
||||
if (!(t->curr_state & BIT(idx)))
|
||||
send_input(t, data);
|
||||
mutex_unlock(&t->irq_lock);
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
static int tristate_keycode_open(struct inode *inode, struct file *file)
|
||||
{
|
||||
int idx = keycode_get_idx(file->f_path.dentry->d_iname);
|
||||
|
||||
/* Pass the index to keycode_show */
|
||||
return single_open(file, keycode_show, (void *)(long)idx);
|
||||
}
|
||||
|
||||
static const struct file_operations tristate_keycode_proc = {
|
||||
.owner = THIS_MODULE,
|
||||
.open = tristate_keycode_open,
|
||||
.read = seq_read,
|
||||
.write = tristate_keycode_write,
|
||||
.llseek = seq_lseek,
|
||||
.release = single_release,
|
||||
};
|
||||
|
||||
static int tristate_parse_dt(struct tristate_data *t)
|
||||
{
|
||||
struct device_node *np;
|
||||
char prop_name[sizeof("tristate,gpio_key#")];
|
||||
int i;
|
||||
|
||||
np = t->dev->of_node;
|
||||
if (!np)
|
||||
node = dev->of_node;
|
||||
if (!node)
|
||||
return -EINVAL;
|
||||
|
||||
for (i = 0; i < NR_STATES; i++) {
|
||||
sprintf(prop_name, "tristate,gpio_key%d", i + 1);
|
||||
switch_data->key1_gpio = of_get_named_gpio(node, "tristate,gpio_key1", 0);
|
||||
if (!gpio_is_valid(switch_data->key1_gpio))
|
||||
return -EINVAL;
|
||||
|
||||
t->key_gpios[i] = of_get_named_gpio(np, prop_name, 0);
|
||||
if (!gpio_is_valid(t->key_gpios[i])) {
|
||||
dev_err(t->dev, "Invalid %s property\n", prop_name);
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
switch_data->key2_gpio = of_get_named_gpio(node, "tristate,gpio_key2", 0);
|
||||
if (!gpio_is_valid(switch_data->key2_gpio))
|
||||
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;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int tristate_register_irqs(struct tristate_data *t)
|
||||
#else
|
||||
static inline int
|
||||
switch_dev_get_devtree_pdata(struct device *dev)
|
||||
{
|
||||
char label[sizeof("tristate_key#-int")];
|
||||
int i, j, key_irqs[NR_STATES], ret;
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
/* Get the IRQ numbers corresponding to the GPIOs */
|
||||
for (i = 0; i < NR_STATES; i++) {
|
||||
key_irqs[i] = gpio_to_irq(t->key_gpios[i]);
|
||||
if (key_irqs[i] < 0) {
|
||||
dev_err(t->dev, "Invalid IRQ (%d) for GPIO%d\n",
|
||||
key_irqs[i], i + 1);
|
||||
return -EINVAL;
|
||||
}
|
||||
#define KEYCODE_FOPS(WHICH)\
|
||||
static int keyCode_##WHICH##_show(struct seq_file *seq, void *offset)\
|
||||
{\
|
||||
seq_printf(seq, "%d\n", keyCode_slider_##WHICH);\
|
||||
return 0;\
|
||||
}\
|
||||
static ssize_t keyCode_##WHICH##_write(struct file *file,\
|
||||
const char __user *page, size_t t, loff_t *lo)\
|
||||
{\
|
||||
int data;\
|
||||
char buf[10];\
|
||||
if (copy_from_user(buf, page, t)) {\
|
||||
dev_err(switch_data->dev, "read proc input error.\n");\
|
||||
return t;\
|
||||
}\
|
||||
if (sscanf(buf, "%d", &data) != 1)\
|
||||
return t;\
|
||||
if (data < 600 || data > 603)\
|
||||
return t;\
|
||||
keyCode_slider_##WHICH = data;\
|
||||
if (current_mode == 1)\
|
||||
send_input(keyCode_slider_##WHICH);\
|
||||
return t;\
|
||||
}\
|
||||
static int keyCode_##WHICH##_open(struct inode *inode, struct file *file)\
|
||||
{\
|
||||
return single_open(file, keyCode_##WHICH##_show, inode->i_private);\
|
||||
}\
|
||||
const struct file_operations proc_keyCode_##WHICH = {\
|
||||
.owner = THIS_MODULE,\
|
||||
.open = keyCode_##WHICH##_open,\
|
||||
.read = seq_read,\
|
||||
.write = keyCode_##WHICH##_write,\
|
||||
.llseek = seq_lseek,\
|
||||
.release = single_release,\
|
||||
};
|
||||
|
||||
KEYCODE_FOPS(top);
|
||||
KEYCODE_FOPS(middle);
|
||||
KEYCODE_FOPS(bottom);
|
||||
|
||||
#define REGISTER_IRQ_FOR(KEY)\
|
||||
switch_data->irq_##KEY = gpio_to_irq(switch_data->KEY##_gpio);\
|
||||
if (switch_data->irq_##KEY <= 0) {\
|
||||
dev_err(dev, "%s: irq number is not specified, irq #= %d, int pin=%d\n",\
|
||||
__func__, switch_data->irq_##KEY, switch_data->KEY##_gpio);\
|
||||
goto err_detect_irq_num_failed;\
|
||||
} else {\
|
||||
error = gpio_request(switch_data->KEY##_gpio, "tristate_" #KEY "-int");\
|
||||
if (error < 0) {\
|
||||
dev_err(dev, "%s: gpio_request, err=%d\n", __func__, error);\
|
||||
goto err_request_gpio;\
|
||||
}\
|
||||
error = gpio_direction_input(switch_data->KEY##_gpio);\
|
||||
if (error < 0) {\
|
||||
dev_err(dev, "%s: gpio_direction_input, err=%d\n", __func__, error);\
|
||||
goto err_set_gpio_input;\
|
||||
}\
|
||||
error = request_irq(switch_data->irq_##KEY, switch_dev_interrupt,\
|
||||
IRQF_TRIGGER_FALLING|IRQF_TRIGGER_RISING, "tristate_" #KEY, switch_data);\
|
||||
if (error) {\
|
||||
dev_err(dev, "%s: request_irq, err=%d\n", __func__, error);\
|
||||
switch_data->irq_##KEY = -EINVAL;\
|
||||
goto err_request_irq;\
|
||||
}\
|
||||
}
|
||||
|
||||
for (i = 0; i < NR_STATES; i++) {
|
||||
sprintf(label, "tristate_key%d-int", i + 1);
|
||||
static int tristate_dev_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct device *dev = &pdev->dev;
|
||||
struct proc_dir_entry *procdir;
|
||||
int error = 0;
|
||||
|
||||
ret = gpio_request(t->key_gpios[i], label);
|
||||
if (ret < 0) {
|
||||
dev_err(t->dev, "Failed to request GPIO%d, ret: %d\n",
|
||||
i + 1, ret);
|
||||
goto free_gpios;
|
||||
}
|
||||
switch_data = kzalloc(sizeof(struct switch_dev_data), GFP_KERNEL);
|
||||
switch_data->dev = dev;
|
||||
|
||||
ret = gpio_direction_input(t->key_gpios[i]);
|
||||
if (ret < 0) {
|
||||
dev_err(t->dev, "Failed to set GPIO%d dir, ret: %d\n",
|
||||
i + 1, ret);
|
||||
goto free_gpios;
|
||||
}
|
||||
switch_data->input = input_allocate_device();
|
||||
|
||||
sprintf(label, "tristate_key%d", i + 1);
|
||||
// init input device
|
||||
|
||||
ret = request_threaded_irq(key_irqs[i], NULL,
|
||||
tristate_irq_handler,
|
||||
IRQF_TRIGGER_FALLING | IRQF_ONESHOT, label, t);
|
||||
if (ret < 0) {
|
||||
dev_err(t->dev, "Failed to register %s IRQ, ret: %d\n",
|
||||
label, ret);
|
||||
goto free_irqs;
|
||||
}
|
||||
switch_data->input->name = DRV_NAME;
|
||||
switch_data->input->dev.parent = &pdev->dev;
|
||||
|
||||
set_bit(EV_KEY, switch_data->input->evbit);
|
||||
|
||||
set_bit(KEY_MODE_TOTAL_SILENCE, switch_data->input->keybit);
|
||||
set_bit(KEY_MODE_ALARMS_ONLY, switch_data->input->keybit);
|
||||
set_bit(KEY_MODE_PRIORITY_ONLY, switch_data->input->keybit);
|
||||
set_bit(KEY_MODE_NONE, switch_data->input->keybit);
|
||||
|
||||
input_set_drvdata(switch_data->input, switch_data);
|
||||
|
||||
error = input_register_device(switch_data->input);
|
||||
if (error) {
|
||||
dev_err(dev, "Failed to register input device\n");
|
||||
goto err_input_device_register;
|
||||
}
|
||||
|
||||
for (i = 0; i < NR_STATES; i++)
|
||||
enable_irq_wake(key_irqs[i]);
|
||||
// init pinctrl
|
||||
|
||||
switch_data->key_pinctrl = devm_pinctrl_get(switch_data->dev);
|
||||
if (IS_ERR_OR_NULL(switch_data->key_pinctrl)) {
|
||||
dev_err(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(dev, "Failed to lookup_state\n");
|
||||
goto err_switch_dev_register;
|
||||
}
|
||||
|
||||
pinctrl_select_state(switch_data->key_pinctrl, switch_data->set_state);
|
||||
|
||||
// parse gpios from dt
|
||||
|
||||
error = switch_dev_get_devtree_pdata(dev);
|
||||
if (error) {
|
||||
dev_err(dev, "parse device tree fail!!!\n");
|
||||
goto err_switch_dev_register;
|
||||
}
|
||||
|
||||
// irqs and work, timer stuffs
|
||||
|
||||
REGISTER_IRQ_FOR(key1);
|
||||
REGISTER_IRQ_FOR(key2);
|
||||
REGISTER_IRQ_FOR(key3);
|
||||
|
||||
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);
|
||||
|
||||
// init switch device
|
||||
|
||||
switch_data->sdev.name = DRV_NAME;
|
||||
error = switch_dev_register(&switch_data->sdev);
|
||||
if (error < 0) {
|
||||
dev_err(dev, "Failed to register switch dev\n");
|
||||
goto err_request_gpio;
|
||||
}
|
||||
|
||||
// init proc fs
|
||||
|
||||
procdir = proc_mkdir("tri-state-key", NULL);
|
||||
|
||||
proc_create_data("keyCode_top", 0666, procdir,
|
||||
&proc_keyCode_top, NULL);
|
||||
|
||||
proc_create_data("keyCode_middle", 0666, procdir,
|
||||
&proc_keyCode_middle, NULL);
|
||||
|
||||
proc_create_data("keyCode_bottom", 0666, procdir,
|
||||
&proc_keyCode_bottom, NULL);
|
||||
|
||||
return 0;
|
||||
|
||||
free_irqs:
|
||||
for (j = i; j--;)
|
||||
free_irq(key_irqs[j], t);
|
||||
i = NR_STATES;
|
||||
free_gpios:
|
||||
for (j = i; j--;)
|
||||
gpio_free(t->key_gpios[j]);
|
||||
return -EINVAL;
|
||||
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->key1_gpio);
|
||||
gpio_free(switch_data->key2_gpio);
|
||||
gpio_free(switch_data->key3_gpio);
|
||||
err_switch_dev_register:
|
||||
kfree(switch_data);
|
||||
err_input_device_register:
|
||||
input_unregister_device(switch_data->input);
|
||||
input_free_device(switch_data->input);
|
||||
dev_err(dev, "%s error: %d\n", __func__, error);
|
||||
return error;
|
||||
}
|
||||
|
||||
static void tristate_create_procfs(void)
|
||||
static int tristate_dev_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct proc_dir_entry *proc_dir;
|
||||
int i;
|
||||
|
||||
proc_dir = proc_mkdir("tri-state-key", NULL);
|
||||
if (!proc_dir)
|
||||
return;
|
||||
|
||||
for (i = 0; i < NR_STATES; i++)
|
||||
proc_create_data(proc_names[i], 0644, proc_dir,
|
||||
&tristate_keycode_proc, NULL);
|
||||
}
|
||||
|
||||
static int tristate_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct tristate_data *t;
|
||||
int i, ret;
|
||||
|
||||
t = kzalloc(sizeof(*t), GFP_KERNEL);
|
||||
if (!t)
|
||||
return -ENOMEM;
|
||||
|
||||
t->dev = &pdev->dev;
|
||||
tdata_g = t;
|
||||
|
||||
ret = tristate_parse_dt(t);
|
||||
if (ret)
|
||||
goto free_t;
|
||||
|
||||
t->input = input_allocate_device();
|
||||
if (!t->input) {
|
||||
dev_err(t->dev, "Failed to allocate input device\n");
|
||||
ret = -ENOMEM;
|
||||
goto free_t;
|
||||
}
|
||||
|
||||
t->input->name = DRIVER_NAME;
|
||||
t->input->dev.parent = t->dev;
|
||||
input_set_drvdata(t->input, t);
|
||||
|
||||
set_bit(EV_KEY, t->input->evbit);
|
||||
set_bit(KEYCODE_TOTAL_SILENCE, t->input->keybit);
|
||||
set_bit(KEYCODE_MODE_ALARMS_ONLY, t->input->keybit);
|
||||
set_bit(KEYCODE_MODE_PRIORITY_ONLY, t->input->keybit);
|
||||
set_bit(KEYCODE_MODE_NONE, t->input->keybit);
|
||||
|
||||
ret = input_register_device(t->input);
|
||||
if (ret)
|
||||
goto free_input;
|
||||
|
||||
/* Initialize with default keycodes */
|
||||
for (i = 0; i < NR_STATES; i++)
|
||||
t->key_codes[i] = KEYCODE_TOTAL_SILENCE + i;
|
||||
|
||||
/* Process initial state */
|
||||
tristate_process_state(t);
|
||||
|
||||
mutex_init(&t->irq_lock);
|
||||
ret = tristate_register_irqs(t);
|
||||
if (ret)
|
||||
goto input_unregister;
|
||||
|
||||
tristate_create_procfs();
|
||||
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;
|
||||
|
||||
input_unregister:
|
||||
input_unregister_device(t->input);
|
||||
free_input:
|
||||
input_free_device(t->input);
|
||||
free_t:
|
||||
kfree(t);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static const struct of_device_id tristate_of_match[] = {
|
||||
#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_driver = {
|
||||
.probe = tristate_probe,
|
||||
static struct platform_driver tristate_dev_driver = {
|
||||
.probe = tristate_dev_probe,
|
||||
.remove = tristate_dev_remove,
|
||||
.driver = {
|
||||
.name = DRIVER_NAME,
|
||||
.name = DRV_NAME,
|
||||
.owner = THIS_MODULE,
|
||||
.of_match_table = tristate_of_match,
|
||||
.of_match_table = of_match_ptr(tristate_dev_of_match),
|
||||
},
|
||||
};
|
||||
module_platform_driver(tristate_dev_driver);
|
||||
|
||||
static int __init tristate_init(void)
|
||||
{
|
||||
return platform_driver_register(&tristate_driver);
|
||||
}
|
||||
device_initcall(tristate_init);
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_DESCRIPTION("switch Profiles by this triple key driver");
|
||||
|
|
Loading…
Add table
Reference in a new issue