aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/input/misc/flip.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/input/misc/flip.c')
-rw-r--r--drivers/input/misc/flip.c408
1 files changed, 408 insertions, 0 deletions
diff --git a/drivers/input/misc/flip.c b/drivers/input/misc/flip.c
new file mode 100644
index 0000000..769268d
--- /dev/null
+++ b/drivers/input/misc/flip.c
@@ -0,0 +1,408 @@
+
+/* Copyright (c) 2010-2011, Code Aurora Forum. 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 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/input.h>
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/platform_device.h>
+#include <linux/log2.h>
+#include <linux/spinlock.h>
+#include <linux/hrtimer.h>
+#include <linux/sched.h>
+#include <linux/pm.h>
+#include <linux/slab.h>
+#include <linux/pm_runtime.h>
+#include <linux/gpio.h>
+#include <linux/interrupt.h>
+#include <linux/switch.h>
+#include <linux/workqueue.h>
+#include <linux/irqdesc.h>
+#include <linux/fs.h>
+#include <linux/sysctl.h>
+#include <linux/proc_fs.h>
+#include <linux/delay.h>
+#include <linux/input.h>
+#include <linux/workqueue.h>
+#include <linux/irqdesc.h>
+#include <linux/wakelock.h>
+
+#define REPORT_KEY 0
+#define FLIP_NOTINIT -1
+#define FLIP_OPEN 1
+#define FLIP_CLOSE 0
+
+#define FLIP_SCAN_INTERVAL (50) /* ms */
+#define FLIP_STABLE_COUNT (1)
+#define GPIO_FLIP GPIO_HALL_SW
+
+extern struct class *sec_class;
+
+#if 0 /* DEBUG */
+#define dbg_printk(fmt, ...) \
+ printk(KERN_DEBUG pr_fmt(fmt), ##__VA_ARGS__)
+#else
+#define dbg_printk(fmt, ...) \
+ ({ if (0) printk(KERN_DEBUG pr_fmt(fmt), ##__VA_ARGS__); 0; })
+#endif
+
+/////////////////////////////////////////////////////////////////////
+struct sec_flip_pdata {
+ int wakeup;
+ int debounce_interval;
+ unsigned int rep:1; /* enable input subsystem auto repeat */
+};
+
+struct sec_flip {
+ struct input_dev *input;
+ struct wake_lock wlock;
+ struct workqueue_struct *wq; /* The actuak work queue */
+ struct work_struct flip_id_det; /* The work being queued */
+ struct timer_list flip_timer;
+ struct device *sec_flip;
+ int timer_debounce;
+ int gpio;
+ int irq;
+};
+
+
+/////////////////////////////////////////////////////////////////////
+#ifdef CONFIG_S5P_DSIM_SWITCHABLE_DUAL_LCD
+extern void s3cfb_switch_dual_lcd(int lcd_sel);
+#endif
+extern void samsung_switching_tsp(int flip);
+extern void samsung_switching_tkey(int flip);
+/////////////////////////////////////////////////////////////////////
+
+static int flip_status;
+static int flip_status_before;
+
+/* 0 : open, 1: close */
+int Is_folder_state(void)
+{
+ printk("%s: flip_status = %d\n", __func__,flip_status);
+ return !flip_status;
+}
+EXPORT_SYMBOL(Is_folder_state);
+
+
+static void sec_report_flip_key(struct sec_flip *flip)
+{
+#if REPORT_KEY
+ if (flip_status) {
+ input_report_key(flip->input, KEY_FOLDER_OPEN, 1);
+ input_report_key(flip->input, KEY_FOLDER_OPEN, 0);
+ input_sync(flip->input);
+ dbg_printk("[FLIP] %s: input flip key : open\n", __FUNCTION__);
+ } else {
+ input_report_key(flip->input, KEY_FOLDER_CLOSE, 1);
+ input_report_key(flip->input, KEY_FOLDER_CLOSE, 0);
+ input_sync(flip->input);
+ dbg_printk ("[FLIP] %s: input flip key : close\n", __FUNCTION__);
+ }
+#else
+ if (flip_status) {
+ input_report_switch(flip->input, SW_LID, 0);
+ input_sync(flip->input);
+ dbg_printk("[FLIP] %s: input flip key : open\n", __FUNCTION__);
+ } else {
+ input_report_switch(flip->input, SW_LID, 1);
+ input_sync(flip->input);
+ dbg_printk ("[FLIP] %s: input flip key : close\n", __FUNCTION__);
+ }
+#endif
+}
+
+static void set_flip_status(struct sec_flip *flip)
+{
+ int val = 0;
+
+ val = gpio_get_value_cansleep(flip->gpio);
+ dbg_printk("%s, val:%d, flip_status:%d\n", __FUNCTION__, val, flip_status);
+ if (flip_status != val) {
+ flip_status_before = flip_status;
+ flip_status = val ? FLIP_OPEN : FLIP_CLOSE;
+ }
+
+}
+
+static void sec_flip_work_func(struct work_struct *work)
+{
+ struct sec_flip* flip = container_of( work, struct sec_flip, flip_id_det);
+
+ //enable_irq(flip->irq);
+
+ set_flip_status(flip);
+ printk("%s: %s, before:%d \n", __func__, (flip_status) ? "OPEN 1" : "CLOSE 0", flip_status_before);
+
+ sec_report_flip_key(flip);
+
+ if (flip_status != flip_status_before) {
+#ifdef CONFIG_S5P_DSIM_SWITCHABLE_DUAL_LCD
+ s3cfb_switch_dual_lcd(!flip_status);
+#endif
+ samsung_switching_tsp(flip_status);
+ samsung_switching_tkey(flip_status);
+ }
+}
+
+static irqreturn_t sec_flip_irq_handler(int irq, void *_flip)
+{
+ struct sec_flip *flip = _flip;
+
+ dbg_printk("%s\n", __FUNCTION__);
+
+/*
+ val = gpio_get_value_cansleep(flip->gpio);
+
+ if(val){ // OPEN
+ flip_status = 1;
+ } else{ // CLOSE
+ flip_status = 0;
+ samsung_switching_tsp_pre(0, flip_status);
+ }
+ printk("[FLIP] %s: val=%d (1:open, 0:close)\n", __func__, val);
+
+ wake_lock_timeout(&flip->wlock, 1 * HZ);
+*/
+ if (flip->timer_debounce)
+ mod_timer(&flip->flip_timer,
+ jiffies + msecs_to_jiffies(flip->timer_debounce));
+ else
+ queue_work(flip->wq, &flip->flip_id_det );
+
+ return IRQ_HANDLED;
+}
+
+
+void sec_flip_timer(unsigned long data)
+{
+ struct sec_flip* flip = (struct sec_flip*)data;
+
+ dbg_printk("%s\n", __FUNCTION__);
+ queue_work(flip->wq, &flip->flip_id_det);
+}
+
+#if 1 /*CONFIG_PM*/
+static int sec_flip_suspend(struct device *dev)
+{
+ struct sec_flip *flip = dev_get_drvdata(dev);
+
+ dbg_printk(KERN_INFO "[FLIP]%s:\n", __func__);
+
+ if (device_may_wakeup(dev)) {
+ enable_irq_wake(flip->irq);
+ printk(KERN_INFO "[FLIP]%s: enable_irq_wake\n", __func__);
+ }
+
+ return 0;
+}
+
+static int sec_flip_resume(struct device *dev)
+{
+ struct sec_flip *flip = dev_get_drvdata(dev);
+
+ dbg_printk("[FLIP]%s:\n", __func__);
+
+ if (device_may_wakeup(dev)) {
+ disable_irq_wake(flip->irq);
+ printk(KERN_INFO "[FLIP]%s: disable_irq_wake\n", __func__);
+ }
+
+ return 0;
+}
+
+static struct dev_pm_ops pm8058_flip_pm_ops = {
+ .suspend = sec_flip_suspend,
+ .resume = sec_flip_resume,
+};
+#endif
+
+static ssize_t status_check(struct device *dev, struct device_attribute *attr, char *buf)
+{
+ printk("flip status check : %d\n", flip_status);
+ return sprintf(buf, "%d\n", flip_status);
+}
+
+static DEVICE_ATTR(flipStatus, S_IRUGO | S_IWUSR | S_IWGRP , status_check, NULL);
+
+static int __devinit sec_flip_probe(struct platform_device *pdev)
+{
+ struct input_dev *input;
+ int err;
+ struct sec_flip *flip;
+ struct sec_flip_pdata *pdata = pdev->dev.platform_data;
+
+ dev_info(&pdev->dev, "probe\n");
+
+ if (!pdata) {
+ dev_err(&pdev->dev, "power key platform data not supplied\n");
+ return -EINVAL;
+ }
+
+ flip = kzalloc(sizeof(*flip), GFP_KERNEL);
+ if (!flip)
+ return -ENOMEM;
+
+/* INPUT DEVICE */
+ input = input_allocate_device();
+ if (!input) {
+ dev_err(&pdev->dev, "Can't allocate power button\n");
+ err = -ENOMEM;
+ goto free_flip;
+ }
+
+ if (pdata->rep)
+ __set_bit(EV_REP, input->evbit);
+
+#if REPORT_KEY
+ input_set_capability(input, EV_KEY, KEY_FOLDER_OPEN);
+ input_set_capability(input, EV_KEY, KEY_FOLDER_CLOSE);
+#else
+ input_set_capability(input, EV_SW, SW_LID);
+#endif
+
+ input->name = "sec_flip";
+ input->phys = "sec_flip/input0";
+ input->dev.parent = &pdev->dev;
+
+ err = input_register_device(input);
+ if (err) {
+ dev_err(&pdev->dev, "Can't register power key: %d\n", err);
+ goto free_input_dev;
+ }
+ flip->input = input;
+
+ platform_set_drvdata(pdev, flip);
+ wake_lock_init(&flip->wlock, WAKE_LOCK_SUSPEND, "sec_flip");
+ setup_timer(&flip->flip_timer, sec_flip_timer, (unsigned long)flip);
+
+/* Initialize the static variable*/
+ flip_status = FLIP_NOTINIT;
+ flip_status_before = FLIP_NOTINIT;
+
+/* INTERRUPT */
+ flip->gpio = GPIO_FLIP;
+ flip->irq = gpio_to_irq(GPIO_FLIP);
+
+ if (pdata->debounce_interval) {
+ err = gpio_set_debounce(flip->gpio,
+ pdata->debounce_interval * 1000);
+ /* use timer if gpiolib doesn't provide debounce */
+ if (err < 0)
+ flip->timer_debounce = pdata->debounce_interval;
+
+ printk("%s: error:%d, timer_debounce=%d\n", __func__, err, flip->timer_debounce);
+ }
+
+
+ flip->wq = create_singlethread_workqueue("sec_flip_wq");
+ if (!flip->wq) {
+ dev_err(&pdev->dev, "create_workqueue failed.\n");
+ }
+ INIT_WORK(&flip->flip_id_det, sec_flip_work_func);
+
+ device_init_wakeup(&pdev->dev, pdata->wakeup);
+
+
+ err = request_threaded_irq(flip->irq, NULL, sec_flip_irq_handler, (IRQF_TRIGGER_RISING|IRQF_TRIGGER_FALLING), "flip_det_irq", flip);
+ if (err < 0) {
+ dev_err(&pdev->dev, "Can't get %d IRQ for flip: %d\n",
+ flip->irq, err);
+ goto unreg_input_dev;
+ }
+ /*enable_irq_wake(flip->irq);*/
+ /*disable_irq_nosync(flip->irq);*/
+
+ printk("%s: gpio=%d irq=%d\n", __func__, flip->gpio, flip->irq);
+
+/* SYSFS for FACTORY TEST */
+ flip->sec_flip = device_create(sec_class, NULL, 0, NULL, "sec_flip");
+ err = device_create_file(flip->sec_flip , &dev_attr_flipStatus);
+ if(err<0){
+ printk("flip status check cannot create file : %d\n", flip_status);
+ goto free_flip;
+ }
+
+ mod_timer(&flip->flip_timer, jiffies + msecs_to_jiffies(5000));
+ return 0;
+
+
+unreg_input_dev:
+ input_unregister_device(input);
+ input = NULL;
+free_input_dev:
+ input_free_device(input);
+free_flip:
+
+ kfree(flip);
+
+ return err;
+}
+
+static int __devexit sec_flip_remove(struct platform_device *pdev)
+{
+ struct sec_flip *flip = platform_get_drvdata(pdev);
+
+ printk("%s:\n", __func__);
+
+ if (flip!=NULL)
+ del_timer_sync(&flip->flip_timer);
+
+ device_init_wakeup(&pdev->dev, 0);
+
+ if (flip!=NULL) {
+ free_irq(flip->irq, NULL);
+ destroy_workqueue(flip->wq);
+ input_unregister_device(flip->input);
+ kfree(flip);
+ }
+
+ return 0;
+}
+
+static struct platform_driver sec_flip_driver = {
+ .probe = sec_flip_probe,
+ .remove = __devexit_p(sec_flip_remove),
+ .driver = {
+ .name = "sec_flip",
+ .owner = THIS_MODULE,
+#if 1 /* CONFIG_PM */
+ .pm = &pm8058_flip_pm_ops,
+#endif
+ },
+};
+
+static int __init sec_flip_init(void)
+{
+ return platform_driver_register(&sec_flip_driver);
+}
+module_init(sec_flip_init);
+
+static void __exit sec_flip_exit(void)
+{
+ platform_driver_unregister(&sec_flip_driver);
+}
+module_exit(sec_flip_exit);
+
+MODULE_ALIAS("platform:sec_flip");
+MODULE_DESCRIPTION("Flip Key with GPIO");
+MODULE_LICENSE("GPL v2");