msm: rc: Add support for MSM GPIO TSOP IR Reciever driver.

Add GPIO based TSOP IR Reciever driver. It decodes signals based on
NEC protocol.

Change-Id: Ic19fab61152a99828806cf451ae797797602ec70
Signed-off-by: Ankush Khandelwal <akhand@codeaurora.org>
diff --git a/drivers/media/rc/Kconfig b/drivers/media/rc/Kconfig
index 7d4bbc2..57c418e 100644
--- a/drivers/media/rc/Kconfig
+++ b/drivers/media/rc/Kconfig
@@ -226,4 +226,13 @@
 	   To compile this driver as a module, choose M here: the module will
 	   be called rc_loopback.
 
+config IR_TSOP_CIR
+	tristate "TSOP IR remote control"
+	depends on RC_CORE
+	---help---
+	   Say Y if you want to use GPIO based TSOP IR Receiver.
+
+	   To compile this driver as a module, choose M here: the module will
+	   be called tsop-ir.
+
 endif #RC_CORE
diff --git a/drivers/media/rc/Makefile b/drivers/media/rc/Makefile
index 52830e5..d5c1a33 100644
--- a/drivers/media/rc/Makefile
+++ b/drivers/media/rc/Makefile
@@ -23,3 +23,4 @@
 obj-$(CONFIG_IR_STREAMZAP) += streamzap.o
 obj-$(CONFIG_IR_WINBOND_CIR) += winbond-cir.o
 obj-$(CONFIG_RC_LOOPBACK) += rc-loopback.o
+obj-$(CONFIG_IR_TSOP_CIR) += tsop-ir.o
diff --git a/drivers/media/rc/keymaps/Makefile b/drivers/media/rc/keymaps/Makefile
index b57fc83..45b0134 100644
--- a/drivers/media/rc/keymaps/Makefile
+++ b/drivers/media/rc/keymaps/Makefile
@@ -70,6 +70,7 @@
 			rc-hauppauge.o \
 			rc-rc6-mce.o \
 			rc-real-audio-220-32-keys.o \
+			rc-samsung-necx.o\
 			rc-streamzap.o \
 			rc-tbs-nec.o \
 			rc-technisat-usb2.o \
diff --git a/drivers/media/rc/keymaps/rc-samsung-necx.c b/drivers/media/rc/keymaps/rc-samsung-necx.c
new file mode 100644
index 0000000..e99c057
--- /dev/null
+++ b/drivers/media/rc/keymaps/rc-samsung-necx.c
@@ -0,0 +1,80 @@
+/* Copyright (c) 2012, 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.
+ */
+
+#include <media/rc-map.h>
+
+static struct rc_map_table samsung_necx[] = {
+	{ 0x70702, KEY_POWER},		/* power */
+	{ 0x7070f, KEY_MUTE},		/* mute */
+	{ 0x70704, KEY_1},
+	{ 0x70705, KEY_2},
+	{ 0x70706, KEY_3},
+	{ 0x70708, KEY_4},
+	{ 0x70709, KEY_5},
+	{ 0x7070a, KEY_6},
+	{ 0x7070c, KEY_7},
+	{ 0x7070d, KEY_8},
+	{ 0x7070e, KEY_9},
+	{ 0x70711, KEY_0},
+	{ 0x70712, KEY_CHANNELUP},
+	{ 0x70710, KEY_CHANNELDOWN},
+	{ 0x70707, KEY_VOLUMEUP},
+	{ 0x7070b, KEY_VOLUMEDOWN},
+	{ 0x70760, KEY_UP},
+	{ 0x70768, KEY_ENTER},		/* ok */
+	{ 0x70761, KEY_DOWN},
+	{ 0x70765, KEY_LEFT},
+	{ 0x70762, KEY_RIGHT},
+	{ 0x7072d, KEY_EXIT},
+	{ 0x70749, KEY_RECORD},
+	{ 0x70747, KEY_PLAY},
+	{ 0x70746, KEY_STOP},
+	{ 0x70745, KEY_REWIND},
+	{ 0x70748, KEY_FORWARD},
+	{ 0x7074a, KEY_PAUSE},
+	{ 0x70703, KEY_SLEEP},
+	{ 0x7076c, KEY_A},		/* search */
+	{ 0x70714, KEY_B},		/* camera */
+	{ 0x70715, KEY_C},
+	{ 0x70716, KEY_D},
+	{ 0x70758, KEY_BACK},
+	{ 0x7071a, KEY_MENU},
+	{ 0x7076b, KEY_LIST},
+	{ 0x70701, KEY_SCREENLOCK},
+	{ 0x7071f, KEY_HOME},
+
+};
+
+static struct rc_map_list samsung_necx_map = {
+	.map = {
+		.scan    = samsung_necx,
+		.size    = ARRAY_SIZE(samsung_necx),
+		.rc_type = RC_TYPE_NEC,
+		.name    = RC_MAP_SAMSUNG_NECX,
+	}
+};
+
+static int __init init_rc_map_samsung_necx(void)
+{
+	return rc_map_register(&samsung_necx_map);
+}
+
+static void __exit exit_rc_map_samsung_necx(void)
+{
+	rc_map_unregister(&samsung_necx_map);
+}
+
+module_init(init_rc_map_samsung_necx)
+module_exit(exit_rc_map_samsung_necx)
+
+MODULE_DESCRIPTION("SAMSUNG IR Remote Keymap");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/media/rc/tsop-ir.c b/drivers/media/rc/tsop-ir.c
new file mode 100644
index 0000000..ffffa9f
--- /dev/null
+++ b/drivers/media/rc/tsop-ir.c
@@ -0,0 +1,198 @@
+/* Copyright (c) 2012, 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.
+ */
+
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/interrupt.h>
+#include <linux/gpio.h>
+#include <linux/slab.h>
+#include <linux/platform_device.h>
+#include <linux/irq.h>
+#include <media/rc-core.h>
+#include <media/tsop-ir.h>
+
+#define TSOP_DRIVER_NAME	"tsop-rc"
+#define TSOP_DEVICE_NAME	"tsop_ir"
+
+struct tsop_remote {
+	struct rc_dev *rcdev;
+	struct mutex lock;
+	unsigned int gpio_nr;
+	bool active_low;
+	bool can_wakeup;
+	struct work_struct work;
+};
+
+static void ir_decoder_work(struct work_struct *work)
+{
+	struct tsop_remote *tsop_dev = container_of(work,
+			struct tsop_remote, work);
+	unsigned int gval;
+	int rc = 0;
+	enum raw_event_type type = IR_SPACE;
+
+	mutex_lock(&tsop_dev->lock);
+	gval = gpio_get_value_cansleep(tsop_dev->gpio_nr);
+
+	if (gval < 0)
+		goto err_get_value;
+
+	if (tsop_dev->active_low)
+		gval = !gval;
+
+	if (gval == 1)
+		type = IR_PULSE;
+
+	rc = ir_raw_event_store_edge(tsop_dev->rcdev, type);
+	if (rc < 0)
+		goto err_get_value;
+
+	ir_raw_event_handle(tsop_dev->rcdev);
+
+err_get_value:
+	mutex_unlock(&tsop_dev->lock);
+}
+
+static irqreturn_t tsop_irq_handler(int irq, void *data)
+{
+	struct tsop_remote *tsop_dev = data;
+
+	schedule_work(&tsop_dev->work);
+
+	return IRQ_HANDLED;
+}
+
+static int __devinit tsop_driver_probe(struct platform_device *pdev)
+{
+	struct tsop_remote *tsop_dev;
+	struct rc_dev *rcdev;
+	const struct tsop_platform_data *pdata = pdev->dev.platform_data;
+	int rc = 0;
+
+	if (!pdata)
+		return -EINVAL;
+
+	if (pdata->gpio_nr < 0)
+		return -EINVAL;
+
+	tsop_dev = kzalloc(sizeof(struct tsop_remote), GFP_KERNEL);
+	if (!tsop_dev)
+		return -ENOMEM;
+
+	mutex_init(&tsop_dev->lock);
+
+	rcdev = rc_allocate_device();
+	if (!rcdev) {
+		rc = -ENOMEM;
+		goto err_allocate_device;
+	}
+
+	rcdev->driver_type = RC_DRIVER_IR_RAW;
+	rcdev->allowed_protos = RC_TYPE_NEC;
+	rcdev->input_name = TSOP_DEVICE_NAME;
+	rcdev->input_id.bustype = BUS_HOST;
+	rcdev->driver_name = TSOP_DRIVER_NAME;
+	rcdev->map_name = RC_MAP_SAMSUNG_NECX;
+
+	tsop_dev->rcdev = rcdev;
+	tsop_dev->gpio_nr = pdata->gpio_nr;
+	tsop_dev->active_low = pdata->active_low;
+	tsop_dev->can_wakeup = pdata->can_wakeup;
+
+	INIT_WORK(&tsop_dev->work, ir_decoder_work);
+
+	rc = gpio_request(pdata->gpio_nr, "tsop-ir");
+	if (rc < 0)
+		goto err_gpio_request;
+	rc  = gpio_direction_input(pdata->gpio_nr);
+	if (rc < 0)
+		goto err_gpio_direction_input;
+
+	rc = rc_register_device(rcdev);
+	if (rc < 0) {
+		dev_err(&pdev->dev, "failed to register rc device\n");
+		goto err_register_rc_device;
+	}
+
+	platform_set_drvdata(pdev, tsop_dev);
+
+	rc = request_irq(gpio_to_irq(pdata->gpio_nr), tsop_irq_handler,
+			IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING,
+			"tsop-irq", tsop_dev);
+	if (rc < 0)
+		goto err_request_irq;
+
+	if (pdata->can_wakeup == true) {
+		rc = enable_irq_wake(gpio_to_irq(pdata->gpio_nr));
+		if (rc < 0)
+			goto err_enable_irq_wake;
+	}
+
+	return 0;
+
+err_enable_irq_wake:
+	free_irq(gpio_to_irq(tsop_dev->gpio_nr), tsop_dev);
+err_request_irq:
+	platform_set_drvdata(pdev, NULL);
+	rc_unregister_device(rcdev);
+err_register_rc_device:
+err_gpio_direction_input:
+	gpio_free(pdata->gpio_nr);
+err_gpio_request:
+	rc_free_device(rcdev);
+	rcdev = NULL;
+err_allocate_device:
+	mutex_destroy(&tsop_dev->lock);
+	kfree(tsop_dev);
+	return rc;
+}
+
+static int __devexit tsop_driver_remove(struct platform_device *pdev)
+{
+	struct tsop_remote *tsop_dev = platform_get_drvdata(pdev);
+
+	flush_work_sync(&tsop_dev->work);
+	disable_irq_wake(gpio_to_irq(tsop_dev->gpio_nr));
+	free_irq(gpio_to_irq(tsop_dev->gpio_nr), tsop_dev);
+	platform_set_drvdata(pdev, NULL);
+	rc_unregister_device(tsop_dev->rcdev);
+	gpio_free(tsop_dev->gpio_nr);
+	rc_free_device(tsop_dev->rcdev);
+	mutex_destroy(&tsop_dev->lock);
+	kfree(tsop_dev);
+	return 0;
+}
+
+static struct platform_driver tsop_driver = {
+	.probe  = tsop_driver_probe,
+	.remove = __devexit_p(tsop_driver_remove),
+	.driver = {
+		.name   = TSOP_DRIVER_NAME,
+		.owner  = THIS_MODULE,
+	},
+};
+
+static int __init tsop_init(void)
+{
+	return platform_driver_register(&tsop_driver);
+}
+module_init(tsop_init);
+
+static void __exit tsop_exit(void)
+{
+	platform_driver_unregister(&tsop_driver);
+}
+module_exit(tsop_exit);
+
+MODULE_DESCRIPTION("TSOP IR driver");
+MODULE_LICENSE("GPL v2");