#include <linux/input.h>
#include <linux/adc.h>
#include <linux/slab.h>
+#include <linux/wakelock.h>
#include <linux/iio/iio.h>
#include <linux/iio/machine.h>
#include <linux/of_irq.h>
#include <linux/of_gpio.h>
#include <linux/of_platform.h>
+#include <linux/rk_keys.h>
-#define EMPTY_ADVALUE 950
-#define DRIFT_ADVALUE 70
+#define EMPTY_DEFAULT_ADVALUE 1024
+#define DRIFT_DEFAULT_ADVALUE 70
#define INVALID_ADVALUE -1
-#define EV_ENCALL KEY_F4
-#define EV_MENU KEY_F1
+#define EV_ENCALL KEY_F4
+#define EV_MENU KEY_F1
#if 0
#define key_dbg(bdata, format, arg...) \
#define key_dbg(bdata, format, arg...)
#endif
-#define DEFAULT_DEBOUNCE_INTERVAL 10 /* 10ms */
-#define ADC_SAMPLE_TIME 100
+#define DEBOUNCE_JIFFIES (10 / (MSEC_PER_SEC / HZ)) /* 10ms */
+#define ADC_SAMPLE_JIFFIES (100 / (MSEC_PER_SEC / HZ)) /* 100ms */
+#define WAKE_LOCK_JIFFIES (1 * HZ) /* 1s */
enum rk_key_type {
TYPE_GPIO = 1,
};
struct rk_keys_button {
+ struct device *dev;
u32 type; /* TYPE_GPIO, TYPE_ADC */
u32 code; /* key code */
const char *desc; /* key label */
bool in_suspend;
int result;
int rep;
+ int drift_advalue;
+ struct wake_lock wake_lock;
struct input_dev *input;
struct delayed_work adc_poll_work;
struct iio_channel *chan;
};
static struct input_dev *sinput_dev;
-static struct rk_keys_drvdata *spdata;
-
-static void *rk_key_get_drvdata(void)
-{
- BUG_ON(!spdata);
- return spdata;
-}
void rk_send_power_key(int state)
{
static void keys_timer(unsigned long _data)
{
- struct rk_keys_drvdata *pdata = rk_key_get_drvdata();
struct rk_keys_button *button = (struct rk_keys_button *)_data;
+ struct rk_keys_drvdata *pdata = dev_get_drvdata(button->dev);
struct input_dev *input = pdata->input;
int state;
}
if (state)
- mod_timer(&button->timer, jiffies + msecs_to_jiffies(DEFAULT_DEBOUNCE_INTERVAL));
+ mod_timer(&button->timer, jiffies + DEBOUNCE_JIFFIES);
}
static irqreturn_t keys_isr(int irq, void *dev_id)
{
- struct rk_keys_drvdata *pdata = rk_key_get_drvdata();
struct rk_keys_button *button = (struct rk_keys_button *)dev_id;
+ struct rk_keys_drvdata *pdata = dev_get_drvdata(button->dev);
struct input_dev *input = pdata->input;
BUG_ON(irq != gpio_to_irq(button->gpio));
- if (button->wakeup == 1 && pdata->in_suspend == true) {
+ if (button->wakeup && pdata->in_suspend) {
button->state = 1;
key_dbg(pdata,
"wakeup: %skey[%s]: report event[%d] state[%d]\n",
input_event(input, EV_KEY, button->code, button->state);
input_sync(input);
}
- mod_timer(&button->timer, jiffies + msecs_to_jiffies(DEFAULT_DEBOUNCE_INTERVAL));
+ if (button->wakeup)
+ wake_lock_timeout(&pdata->wake_lock, WAKE_LOCK_JIFFIES);
+ mod_timer(&button->timer, jiffies + DEBOUNCE_JIFFIES);
return IRQ_HANDLED;
}
ddata = container_of(work, struct rk_keys_drvdata, adc_poll_work.work);
if (!ddata->in_suspend) {
result = rk_key_adc_iio_read(ddata);
- if (result > INVALID_ADVALUE && result < EMPTY_ADVALUE)
+ if (result > INVALID_ADVALUE &&
+ result < (EMPTY_DEFAULT_ADVALUE - ddata->drift_advalue))
ddata->result = result;
for (i = 0; i < ddata->nbuttons; i++) {
struct rk_keys_button *button = &ddata->button[i];
if (!button->adc_value)
continue;
- if (result < button->adc_value + DRIFT_ADVALUE &&
- result > button->adc_value - DRIFT_ADVALUE)
+ if (result < button->adc_value + ddata->drift_advalue &&
+ result > button->adc_value - ddata->drift_advalue)
button->adc_state = 1;
else
button->adc_state = 0;
if (button->state != button->adc_state)
mod_timer(&button->timer,
- jiffies + msecs_to_jiffies(DEFAULT_DEBOUNCE_INTERVAL));
+ jiffies + DEBOUNCE_JIFFIES);
}
}
- schedule_delayed_work(&ddata->adc_poll_work, msecs_to_jiffies(ADC_SAMPLE_TIME));
+ schedule_delayed_work(&ddata->adc_poll_work, ADC_SAMPLE_JIFFIES);
}
static int rk_key_type_get(struct device_node *node,
struct device_node *child_node;
struct iio_channel *chan;
int ret, gpio, i = 0;
- u32 code, adc_value, flags;
+ u32 code, adc_value, flags, drift;
+
+ if (of_property_read_u32(node, "adc-drift", &drift))
+ pdata->drift_advalue = DRIFT_DEFAULT_ADVALUE;
+ else
+ pdata->drift_advalue = (int)drift;
chan = iio_channel_get(&pdev->dev, NULL);
if (IS_ERR(chan)) {
return error;
}
platform_set_drvdata(pdev, ddata);
+ dev_set_drvdata(&pdev->dev, ddata);
input->name = "rk29-keypad"; /* pdev->name; */
input->phys = "gpio-keys/input0";
if (ddata->rep)
__set_bit(EV_REP, input->evbit);
+ error = input_register_device(input);
+ if (error) {
+ pr_err("gpio-keys: Unable to register input device, error: %d\n",
+ error);
+ goto fail0;
+ }
+ sinput_dev = input;
+
for (i = 0; i < ddata->nbuttons; i++) {
struct rk_keys_button *button = &ddata->button[i];
input_set_capability(input, EV_KEY, button->code);
}
+ wake_lock_init(&ddata->wake_lock, WAKE_LOCK_SUSPEND, input->name);
+ device_init_wakeup(dev, wakeup);
+
for (i = 0; i < ddata->nbuttons; i++) {
struct rk_keys_button *button = &ddata->button[i];
+ button->dev = &pdev->dev;
if (button->type == TYPE_GPIO) {
int irq;
}
input_set_capability(input, EV_KEY, KEY_WAKEUP);
- device_init_wakeup(dev, wakeup);
-
- error = input_register_device(input);
- if (error) {
- pr_err("gpio-keys: Unable to register input device, error: %d\n",
- error);
- goto fail2;
- }
-
/* adc polling work */
if (ddata->chan) {
INIT_DELAYED_WORK(&ddata->adc_poll_work, adc_key_poll);
schedule_delayed_work(&ddata->adc_poll_work,
- msecs_to_jiffies(ADC_SAMPLE_TIME));
+ ADC_SAMPLE_JIFFIES);
}
- spdata = ddata;
- sinput_dev = input;
return error;
-fail2:
- device_init_wakeup(dev, 0);
fail1:
while (--i >= 0)
del_timer_sync(&ddata->button[i].timer);
+ device_init_wakeup(dev, 0);
+ wake_lock_destroy(&ddata->wake_lock);
fail0:
platform_set_drvdata(pdev, NULL);
if (ddata->chan)
cancel_delayed_work_sync(&ddata->adc_poll_work);
input_unregister_device(input);
+ wake_lock_destroy(&ddata->wake_lock);
sinput_dev = NULL;
- spdata = NULL;
return 0;
}
}
};
-module_platform_driver(keys_device_driver);
+static int __init rk_keys_driver_init(void)
+{
+ return platform_driver_register(&keys_device_driver);
+}
+
+static void __exit rk_keys_driver_exit(void)
+{
+ platform_driver_unregister(&keys_device_driver);
+}
+
+late_initcall_sync(rk_keys_driver_init);
+module_exit(rk_keys_driver_exit);