All of lore.kernel.org
 help / color / mirror / Atom feed
From: Kyungmin Park <kyungmin.park@samsung.com>
To: linux-kernel@vger.kernel.org, linux-input@vger.kernel.org
Cc: soni.trilok@gmail.com
Subject: [PATCH 5/6] haptic: Samsung SoCs PWM controlled haptic support
Date: Wed, 07 Oct 2009 15:18:22 +0900	[thread overview]
Message-ID: <20091007061822.GA7602@july> (raw)

Samsung SoCs PWM and gpio connected haptic device support
It's tested on s3c64xx, s5pc1xx

Signed-off-by: Kyungmin Park <kyungmin.park@samsung.com>
---
 drivers/haptic/Kconfig              |    9 +
 drivers/haptic/Makefile             |    3 +
 drivers/haptic/haptic-samsung-pwm.c |  377 +++++++++++++++++++++++++++++++++++
 3 files changed, 389 insertions(+), 0 deletions(-)
 create mode 100644 drivers/haptic/haptic-samsung-pwm.c

diff --git a/drivers/haptic/Kconfig b/drivers/haptic/Kconfig
index 656b022..1e9849c 100644
--- a/drivers/haptic/Kconfig
+++ b/drivers/haptic/Kconfig
@@ -11,4 +11,13 @@ config HAPTIC_CLASS
 	help
 	  This option enables the haptic sysfs class in /sys/class/haptic.
 
+comment "Haptic drivers"
+
+config HAPTIC_SAMSUNG_PWM
+	tristate "Haptic Support for SAMSUNG PWM-controlled haptic (ISA1000)"
+	depends on HAPTIC_CLASS && (ARCH_S3C64XX || ARCH_S5PC1XX)
+	help
+	  This options enables support for haptic connected to GPIO lines
+	  controlled by a PWM timer on SAMSUNG CPUs.
+
 endif	# HAPTIC
diff --git a/drivers/haptic/Makefile b/drivers/haptic/Makefile
index d30f8cd..c691f7d 100644
--- a/drivers/haptic/Makefile
+++ b/drivers/haptic/Makefile
@@ -1,2 +1,5 @@
 # Haptic Core
 obj-$(CONFIG_HAPTIC_CLASS)		+= haptic-class.o
+
+# Drivers
+obj-$(CONFIG_HAPTIC_SAMSUNG_PWM)	+= haptic-samsung-pwm.o
diff --git a/drivers/haptic/haptic-samsung-pwm.c b/drivers/haptic/haptic-samsung-pwm.c
new file mode 100644
index 0000000..df6f30d
--- /dev/null
+++ b/drivers/haptic/haptic-samsung-pwm.c
@@ -0,0 +1,377 @@
+/*
+ *  drivers/haptic/haptic-samsung-pwm.c
+ *
+ *  Copyright (C) 2008 Samsung Electronics
+ *  Kyungmin Park <kyungmin.park@samsung.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 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/ctype.h>
+#include <linux/haptic.h>
+#include <linux/workqueue.h>
+#include <linux/gpio.h>
+#include <linux/err.h>
+#include <linux/pwm.h>
+#include <linux/timer.h>
+
+#include "haptic.h"
+
+#define PWM_HAPTIC_PERIOD		44640
+#define PWM_HAPTIC_DEFAULT_LEVEL		2
+
+static int haptic_levels[] = { 18360, 14880, 10860, 5280, 540, };
+
+struct samsung_pwm_haptic {
+	struct haptic_classdev cdev;
+	struct work_struct work;
+	struct haptic_platform_data *pdata;
+	struct pwm_device *pwm;
+	struct timer_list timer;
+
+	int enable;
+	int powered;
+
+	int level;
+	int level_max;
+};
+
+static inline struct samsung_pwm_haptic *cdev_to_samsung_pwm_haptic(
+		struct haptic_classdev *haptic_cdev)
+{
+	return container_of(haptic_cdev, struct samsung_pwm_haptic, cdev);
+}
+
+static void samsung_pwm_haptic_power_on(struct samsung_pwm_haptic *haptic)
+{
+	if (haptic->powered)
+		return;
+	haptic->powered = 1;
+
+	if (gpio_is_valid(haptic->pdata->gpio))
+		gpio_set_value(haptic->pdata->gpio, 1);
+
+	pwm_enable(haptic->pwm);
+}
+
+static void samsung_pwm_haptic_power_off(struct samsung_pwm_haptic *haptic)
+{
+	if (!haptic->powered)
+		return;
+	haptic->powered = 0;
+
+	if (gpio_is_valid(haptic->pdata->gpio))
+		gpio_set_value(haptic->pdata->gpio, 0);
+
+	pwm_disable(haptic->pwm);
+}
+
+static int samsung_pwm_haptic_set_pwm_cycle(struct samsung_pwm_haptic *haptic)
+{
+	int duty = haptic_levels[haptic->level];
+	return pwm_config(haptic->pwm, duty, PWM_HAPTIC_PERIOD);
+}
+
+static void samsung_pwm_haptic_work(struct work_struct *work)
+{
+	struct samsung_pwm_haptic *haptic;
+	int r;
+
+	haptic = container_of(work, struct samsung_pwm_haptic, work);
+
+	if (haptic->enable) {
+		r = samsung_pwm_haptic_set_pwm_cycle(haptic);
+		if (r) {
+			dev_dbg(haptic->cdev.dev, "set_pwm_cycle failed\n");
+			return;
+		}
+		samsung_pwm_haptic_power_on(haptic);
+	} else {
+		samsung_pwm_haptic_power_off(haptic);
+	}
+}
+
+static void samsung_pwm_haptic_timer(unsigned long data)
+{
+	struct samsung_pwm_haptic *haptic = (struct samsung_pwm_haptic *)data;
+
+	haptic->enable = 0;
+	samsung_pwm_haptic_power_off(haptic);
+}
+
+static void samsung_pwm_haptic_set(struct haptic_classdev *haptic_cdev,
+				enum haptic_value value)
+{
+	struct samsung_pwm_haptic *haptic =
+		cdev_to_samsung_pwm_haptic(haptic_cdev);
+
+	switch (value) {
+	case HAPTIC_OFF:
+		haptic->enable = 0;
+		break;
+	case HAPTIC_HALF:
+	case HAPTIC_FULL:
+	default:
+		haptic->enable = 1;
+		break;
+	}
+
+	schedule_work(&haptic->work);
+}
+
+static enum haptic_value samsung_pwm_haptic_get(
+		struct haptic_classdev *haptic_cdev)
+{
+	struct samsung_pwm_haptic *haptic =
+		cdev_to_samsung_pwm_haptic(haptic_cdev);
+
+	if (haptic->enable)
+		return HAPTIC_FULL;
+
+	return HAPTIC_OFF;
+}
+
+#define ATTR_DEF_SHOW(name) \
+static ssize_t samsung_pwm_haptic_show_##name(struct device *dev, \
+		struct device_attribute *attr, char *buf) \
+{ \
+	struct haptic_classdev *haptic_cdev = dev_get_drvdata(dev); \
+	struct samsung_pwm_haptic *haptic =\
+		cdev_to_samsung_pwm_haptic(haptic_cdev); \
+ \
+	return sprintf(buf, "%u\n", haptic->name) + 1; \
+}
+
+#define ATTR_DEF_STORE(name) \
+static ssize_t samsung_pwm_haptic_store_##name(struct device *dev, \
+		struct device_attribute *attr, \
+		const char *buf, size_t size) \
+{ \
+	struct haptic_classdev *haptic_cdev = dev_get_drvdata(dev); \
+	struct samsung_pwm_haptic *haptic =\
+		 cdev_to_samsung_pwm_haptic(haptic_cdev); \
+	ssize_t ret = -EINVAL; \
+	unsigned long val; \
+ \
+	ret = strict_strtoul(buf, 10, &val); \
+	if (ret == 0) { \
+		ret = size; \
+		haptic->name = val; \
+		schedule_work(&haptic->work); \
+	} \
+ \
+	return ret; \
+}
+
+ATTR_DEF_SHOW(enable);
+ATTR_DEF_STORE(enable);
+static DEVICE_ATTR(enable, 0644, samsung_pwm_haptic_show_enable,
+		samsung_pwm_haptic_store_enable);
+
+static ssize_t samsung_pwm_haptic_store_level(struct device *dev,
+		struct device_attribute *attr,
+		const char *buf, size_t size)
+{
+	struct haptic_classdev *haptic_cdev = dev_get_drvdata(dev);
+	struct samsung_pwm_haptic *haptic =
+		cdev_to_samsung_pwm_haptic(haptic_cdev);
+	ssize_t ret = -EINVAL;
+	unsigned long val;
+
+	ret = strict_strtoul(buf, 10, &val);
+	if (ret == 0) {
+		ret = size;
+		if (haptic->level_max < val)
+			val = haptic->level_max;
+		haptic->level = val;
+		schedule_work(&haptic->work);
+	}
+
+	return ret;
+}
+ATTR_DEF_SHOW(level);
+static DEVICE_ATTR(level, 0644, samsung_pwm_haptic_show_level,
+		samsung_pwm_haptic_store_level);
+
+ATTR_DEF_SHOW(level_max);
+static DEVICE_ATTR(level_max, 0444, samsung_pwm_haptic_show_level_max, NULL);
+
+static ssize_t samsung_pwm_haptic_store_oneshot(struct device *dev,
+		struct device_attribute *attr,
+		const char *buf, size_t size)
+{
+	struct haptic_classdev *haptic_cdev = dev_get_drvdata(dev);
+	struct samsung_pwm_haptic *haptic =
+		cdev_to_samsung_pwm_haptic(haptic_cdev);
+	ssize_t ret = -EINVAL;
+	unsigned long val;
+
+	ret = strict_strtoul(buf, 10, &val);
+	if (ret == 0) {
+		ret = size;
+		haptic->enable = 1;
+		mod_timer(&haptic->timer, jiffies + val * HZ / 1000);
+		schedule_work(&haptic->work);
+	}
+
+	return ret;
+}
+static DEVICE_ATTR(oneshot, 0200, NULL, samsung_pwm_haptic_store_oneshot);
+
+static struct attribute *haptic_attributes[] = {
+	&dev_attr_enable.attr,
+	&dev_attr_level.attr,
+	&dev_attr_level_max.attr,
+	&dev_attr_oneshot.attr,
+	NULL,
+};
+
+static const struct attribute_group haptic_group = {
+	.attrs = haptic_attributes,
+};
+
+static int __devinit samsung_pwm_haptic_probe(struct platform_device *pdev)
+{
+	struct haptic_platform_data *pdata = pdev->dev.platform_data;
+	struct samsung_pwm_haptic *haptic;
+	int ret;
+
+	haptic = kzalloc(sizeof(struct samsung_pwm_haptic), GFP_KERNEL);
+	if (!haptic) {
+		dev_err(&pdev->dev, "No memory for device\n");
+		return -ENOMEM;
+	}
+
+	platform_set_drvdata(pdev, haptic);
+	haptic->cdev.set = samsung_pwm_haptic_set;
+	haptic->cdev.get = samsung_pwm_haptic_get;
+	haptic->cdev.show_enable = samsung_pwm_haptic_show_enable;
+	haptic->cdev.store_enable = samsung_pwm_haptic_store_enable;
+	haptic->cdev.store_oneshot = samsung_pwm_haptic_store_oneshot;
+	haptic->cdev.show_level = samsung_pwm_haptic_show_level;
+	haptic->cdev.store_level = samsung_pwm_haptic_store_level;
+	haptic->cdev.show_level_max = samsung_pwm_haptic_show_level_max;
+	haptic->cdev.name = pdata->name;
+	haptic->pdata = pdata;
+	haptic->enable = 0;
+	haptic->level = PWM_HAPTIC_DEFAULT_LEVEL;
+	haptic->level_max = ARRAY_SIZE(haptic_levels);
+
+	if (pdata->setup_pin)
+		pdata->setup_pin();
+
+	INIT_WORK(&haptic->work, samsung_pwm_haptic_work);
+
+	/* register our new haptic device */
+	ret = haptic_classdev_register(&pdev->dev, &haptic->cdev);
+	if (ret < 0) {
+		dev_err(&pdev->dev, "haptic_classdev_register failed\n");
+		goto error_classdev;
+	}
+
+	haptic->pwm = pwm_request(pdata->pwm_timer, "haptic");
+	if (IS_ERR(haptic->pwm)) {
+		dev_err(&pdev->dev, "unable to request PWM for haptic\n");
+		ret = PTR_ERR(haptic->pwm);
+		goto err_pwm;
+	} else
+		dev_dbg(&pdev->dev, "got pwm for haptic\n");
+
+	ret = sysfs_create_group(&haptic->cdev.dev->kobj, &haptic_group);
+	if (ret)
+		goto error_enable;
+
+	if (gpio_is_valid(pdata->gpio)) {
+		printk(KERN_INFO "Motor enable gpio %d\n", pdata->gpio);
+		ret = gpio_request(pdata->gpio, "haptic enable");
+		if (ret)
+			goto error_gpio;
+		gpio_direction_output(pdata->gpio, 0);
+	}
+
+	init_timer(&haptic->timer);
+	haptic->timer.data = (unsigned long)haptic;
+	haptic->timer.function = &samsung_pwm_haptic_timer;
+
+	printk(KERN_INFO "samsung %s registed\n", pdata->name);
+	return 0;
+
+error_gpio:
+	gpio_free(pdata->gpio);
+error_enable:
+	sysfs_remove_group(&haptic->cdev.dev->kobj, &haptic_group);
+err_pwm:
+	pwm_free(haptic->pwm);
+error_classdev:
+	haptic_classdev_unregister(&haptic->cdev);
+	kfree(haptic);
+	return ret;
+}
+
+static int __devexit samsung_pwm_haptic_remove(struct platform_device *pdev)
+{
+	struct samsung_pwm_haptic *haptic = platform_get_drvdata(pdev);
+
+	samsung_pwm_haptic_set(&haptic->cdev, HAPTIC_OFF);
+	del_timer_sync(&haptic->timer);
+
+	if (haptic->pdata->gpio)
+		gpio_free(haptic->pdata->gpio);
+	device_remove_file(haptic->cdev.dev, &dev_attr_enable);
+	haptic_classdev_unregister(&haptic->cdev);
+	kfree(haptic);
+	return 0;
+}
+
+#ifdef CONFIG_PM
+static int samsung_pwm_haptic_suspend(
+	struct platform_device *pdev, pm_message_t state)
+{
+	struct samsung_pwm_haptic *haptic = platform_get_drvdata(pdev);
+
+	haptic_classdev_suspend(&haptic->cdev);
+	return 0;
+}
+
+static int samsung_pwm_haptic_resume(struct platform_device *pdev)
+{
+	struct samsung_pwm_haptic *haptic = platform_get_drvdata(pdev);
+
+	haptic_classdev_resume(&haptic->cdev);
+	return 0;
+}
+#else
+#define samsung_pwm_haptic_suspend	NULL
+#define samsung_pwm_haptic_resume	NULL
+#endif
+
+static struct platform_driver samsung_pwm_haptic_driver = {
+	.probe		= samsung_pwm_haptic_probe,
+	.remove		= samsung_pwm_haptic_remove,
+	.suspend	= samsung_pwm_haptic_suspend,
+	.resume		= samsung_pwm_haptic_resume,
+	.driver		= {
+		.name	= "samsung_pwm_haptic",
+		.owner	= THIS_MODULE,
+	},
+};
+
+static int __init samsung_pwm_haptic_init(void)
+{
+	return platform_driver_register(&samsung_pwm_haptic_driver);
+}
+module_init(samsung_pwm_haptic_init);
+
+static void __exit samsung_pwm_haptic_exit(void)
+{
+	platform_driver_unregister(&samsung_pwm_haptic_driver);
+}
+module_exit(samsung_pwm_haptic_exit);
+
+MODULE_AUTHOR("Kyungmin Park <kyungmin.park@samsung.com>");
+MODULE_DESCRIPTION("samsung PWM haptic driver");
+MODULE_LICENSE("GPL");
-- 
1.5.3.3


             reply	other threads:[~2009-10-07  6:19 UTC|newest]

Thread overview: 8+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2009-10-07  6:18 Kyungmin Park [this message]
2009-10-08 18:28 ` [PATCH 5/6] haptic: Samsung SoCs PWM controlled haptic support Trilok Soni
2009-10-08 18:36   ` Bill Gatliff
2009-10-08 23:51     ` Kyungmin Park
2009-10-09  8:30       ` Trilok Soni
2009-10-09  8:32     ` Trilok Soni
2009-10-09 20:41       ` Bill Gatliff
2009-10-12 19:22       ` RFC: Proposed PWM device API Bill Gatliff

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20091007061822.GA7602@july \
    --to=kyungmin.park@samsung.com \
    --cc=linux-input@vger.kernel.org \
    --cc=linux-kernel@vger.kernel.org \
    --cc=soni.trilok@gmail.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.