blob: 32f2d455549f965e4794c4ecb2c28ad8b872d702 [file] [log] [blame]
/* msm8974_pwm_vibrator.c
*
* Copyright (C) 2009-2013 LGE, Inc.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include <linux/init.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/platform_device.h>
#include <linux/gpio.h>
#include <linux/of_gpio.h>
#include <linux/delay.h>
#include <linux/timer.h>
#include <linux/clk.h>
#include <linux/err.h>
#include <linux/stat.h>
#include <linux/io.h>
#include <linux/slab.h>
#include <linux/of.h>
#include <linux/regulator/consumer.h>
#include <linux/mutex.h>
#include <mach/msm_iomap.h>
#include "../staging/android/timed_output.h"
/* gpio and clock control for vibrator */
static void __iomem *virt_base;
#define MMSS_CC_GP1_BASE(x) (void __iomem *)(virt_base + (x))
#define REG_CMD_RCGR 0x00
#define REG_CFG_RCGR 0x04
#define REG_M 0x08
#define REG_N 0x0C
#define REG_D 0x10
#define REG_CBCR 0x24
#define MMSS_CC_PWM_SET 0xFD8C3450
#define MMSS_CC_PWM_SIZE SZ_1K
#define DEVICE_NAME "msm8974_pwm_vibrator"
#define MMSS_CC_M_DEFAULT 1
#define MMSS_CC_N_DEFAULT 128
#define MMSS_CC_D_MAX MMSS_CC_N_DEFAULT
#define MMSS_CC_D_HALF (MMSS_CC_N_DEFAULT >> 1)
static DEFINE_MUTEX(vib_lock);
struct timed_vibrator_data {
struct timed_output_dev dev;
struct hrtimer timer;
struct mutex lock;
int max_timeout;
int ms_time; /* vibrator duration */
atomic_t vib_status; /* on/off */
int vibe_gain; /* default max gain */
int vibe_pwm;
atomic_t gp1_clk_flag;
int amp;
int vibe_n_value;
int haptic_en_gpio;
int motor_pwm_gpio;
int vibe_warmup_delay; /* in ms */
struct work_struct work_vibrator_off;
struct work_struct work_vibrator_on;
bool use_vdd_supply;
struct regulator *vdd_reg;
};
static struct clk *cam_gp1_clk;
static int vibrator_regulator_init(
struct platform_device *pdev,
struct timed_vibrator_data *vib)
{
if (!vib->use_vdd_supply)
return 0;
vib->vdd_reg = devm_regulator_get(&pdev->dev, "vdd");
if (IS_ERR(vib->vdd_reg)) {
pr_err("%s: could not get vdd-supply\n", __func__);
return -ENODEV;
}
return 0;
}
static int vibrator_set_power(int enable,
struct timed_vibrator_data *vib)
{
int ret = 0;
static int vibrator_enabled = 0;
if (!vib->use_vdd_supply)
return 0;
if (enable == vibrator_enabled)
return 0;
mutex_lock(&vib_lock);
vibrator_enabled = enable;
if (enable) {
ret = regulator_enable(vib->vdd_reg);
if (ret < 0)
pr_err("%s: vdd_reg->enable failed\n", __func__);
} else {
if (regulator_is_enabled(vib->vdd_reg) > 0) {
ret = regulator_disable(vib->vdd_reg);
if (ret < 0)
pr_err("%s: vdd_reg->disable failed\n",
__func__);
}
}
mutex_unlock(&vib_lock);
return ret;
}
static int vibrator_ic_enable_set(int enable,
struct timed_vibrator_data *vib)
{
pr_debug("%s: enable %d\n", __func__, enable);
if (enable)
gpio_direction_output(vib->haptic_en_gpio, 1);
else
gpio_direction_output(vib->haptic_en_gpio, 0);
return 0;
}
static int vibrator_adjust_amp(int amp)
{
int level = 0;
bool minus = false;
if (amp < 0) {
minus = true;
amp = -amp;
}
level = (2 * amp * (MMSS_CC_D_HALF - 2) + 100) / (2 * 100);
if (!level && amp)
level = 1;
if (minus && level)
level = -level;
return level;
}
static int vibrator_pwm_set(int enable, int amp, int n_value)
{
int d_val = 0;
int m_val = MMSS_CC_M_DEFAULT;
pr_debug("%s: amp %d, value %d\n", __func__, amp, n_value);
enable = !!enable;
if (enable) {
if (amp)
d_val = vibrator_adjust_amp(amp) + MMSS_CC_D_HALF;
writel((2 << 12) | /* dual edge mode */
(0 << 8) | /* cxo */
(7 << 0),
MMSS_CC_GP1_BASE(REG_CFG_RCGR));
writel(((m_val) & 0xff), MMSS_CC_GP1_BASE(REG_M));
writel(((~(n_value - m_val)) & 0xff), MMSS_CC_GP1_BASE(REG_N));
writel(((~(d_val << 1)) & 0xff), MMSS_CC_GP1_BASE(REG_D));
writel(enable, MMSS_CC_GP1_BASE(REG_CMD_RCGR)); /* UPDATE */
}
writel(enable, MMSS_CC_GP1_BASE(REG_CBCR));
return 0;
}
#ifdef ANDROID_VIBRATOR_USE_WORKQUEUE
static inline void vibrator_work_on(struct work_struct *work)
{
queue_work(vibrator_workqueue, work);
}
static inline void vibrator_work_off(struct work_struct *work)
{
if (!work_pending(work))
queue_work(vibrator_workqueue, work);
}
#else
static inline void vibrator_work_on(struct work_struct *work)
{
schedule_work(work);
}
static inline void vibrator_work_off(struct work_struct *work)
{
if (!work_pending(work))
schedule_work(work);
}
#endif
static int msm8974_pwm_vibrator_force_set(struct timed_vibrator_data *vib,
int nForce, int n_value)
{
/* Check the Force value with Max and Min force value */
int vib_duration_ms = 0;
if (vib->vibe_warmup_delay > 0) { /*warmup delay isn't used now */
if (atomic_read(&vib->vib_status))
usleep(vib->vibe_warmup_delay * 1000);
}
/* TODO: control the gain of vibrator */
if (nForce == 0) {
vibrator_ic_enable_set(0, vib);
vibrator_pwm_set(0, 0, n_value);
/* should be checked for vibrator response time */
vibrator_set_power(0, vib);
atomic_set(&vib->vib_status, false);
mutex_lock(&vib_lock);
if (atomic_read(&vib->gp1_clk_flag) == 1) {
clk_disable_unprepare(cam_gp1_clk);
atomic_set(&vib->gp1_clk_flag, 0);
}
mutex_unlock(&vib_lock);
} else {
mutex_lock(&vib_lock);
if (atomic_read(&vib->gp1_clk_flag) == 0) {
clk_prepare_enable(cam_gp1_clk);
atomic_set(&vib->gp1_clk_flag, 1);
}
mutex_unlock(&vib_lock);
if (work_pending(&vib->work_vibrator_off))
cancel_work_sync(&vib->work_vibrator_off);
hrtimer_cancel(&vib->timer);
vib_duration_ms = vib->ms_time;
/* should be checked for vibrator response time */
vibrator_set_power(1, vib);
vibrator_pwm_set(1, nForce, n_value);
vibrator_ic_enable_set(1, vib);
atomic_set(&vib->vib_status, true);
hrtimer_start(&vib->timer,
ns_to_ktime((u64)vib_duration_ms * NSEC_PER_MSEC),
HRTIMER_MODE_REL);
}
return 0;
}
static void msm8974_pwm_vibrator_on(struct work_struct *work)
{
struct timed_vibrator_data *vib =
container_of(work, struct timed_vibrator_data,
work_vibrator_on);
int gain = vib->vibe_gain;
int pwm = vib->vibe_pwm;
/* suspend /resume logging test */
pr_debug("%s: gain = %d pwm = %d\n", __func__, gain, pwm);
msm8974_pwm_vibrator_force_set(vib, gain, pwm);
}
static void msm8974_pwm_vibrator_off(struct work_struct *work)
{
struct timed_vibrator_data *vib =
container_of(work, struct timed_vibrator_data,
work_vibrator_off);
msm8974_pwm_vibrator_force_set(vib, 0, vib->vibe_n_value);
}
static enum hrtimer_restart vibrator_timer_func(struct hrtimer *timer)
{
struct timed_vibrator_data *vib =
container_of(timer, struct timed_vibrator_data, timer);
vibrator_work_off(&vib->work_vibrator_off);
return HRTIMER_NORESTART;
}
static int vibrator_get_time(struct timed_output_dev *dev)
{
struct timed_vibrator_data *vib =
container_of(dev, struct timed_vibrator_data, dev);
if (hrtimer_active(&vib->timer)) {
ktime_t r = hrtimer_get_remaining(&vib->timer);
return ktime_to_ms(r);
}
return 0;
}
static void vibrator_enable(struct timed_output_dev *dev, int value)
{
struct timed_vibrator_data *vib =
container_of(dev, struct timed_vibrator_data, dev);
mutex_lock(&vib->lock);
if (value > 0) {
if (value > vib->max_timeout)
value = vib->max_timeout;
vib->ms_time = value;
vibrator_work_on(&vib->work_vibrator_on);
} else {
vibrator_work_off(&vib->work_vibrator_off);
}
mutex_unlock(&vib->lock);
}
static int vibrator_gpio_init(struct timed_vibrator_data *vib)
{
int rc;
rc = gpio_request(vib->haptic_en_gpio, "motor_en");
if (rc < 0) {
pr_err("%s: gpio %d request failed\n", __func__,
vib->haptic_en_gpio);
return rc;
}
rc = gpio_request(vib->motor_pwm_gpio, "motor_pwm");
if (rc < 0) {
pr_err("%s: gpio %d request failed\n", __func__,
vib->motor_pwm_gpio);
gpio_free(vib->haptic_en_gpio);
return rc;
}
return 0;
}
static void vibrator_gpio_deinit(struct timed_vibrator_data *vib)
{
gpio_free(vib->motor_pwm_gpio);
gpio_free(vib->haptic_en_gpio);
}
static int vibrator_parse_dt(struct device *dev,
struct timed_vibrator_data *vib)
{
int ret;
struct device_node *np = dev->of_node;
ret = of_get_named_gpio_flags(np, "haptic-pwr-gpio", 0, NULL);
if (ret < 0) {
pr_err("%s: haptic-pwr-gpio failed\n", __func__);
return ret;
}
vib->haptic_en_gpio = ret;
ret = of_get_named_gpio_flags(np, "motor-pwm-gpio", 0, NULL);
if (ret < 0) {
pr_err("%s: motor-pwm-gpio failed\n", __func__);
return ret;
}
vib->motor_pwm_gpio = ret;
ret = of_property_read_u32(np, "motor-amp", &vib->amp);
if (ret < 0) {
pr_err("%s: motor-amp failed\n", __func__);
return ret;
}
ret = of_property_read_u32(np, "n-value", &vib->vibe_n_value);
if (ret < 0) {
pr_err("%s: n-value failed\n", __func__);
return ret;
}
vib->use_vdd_supply = of_property_read_bool(np, "use-vdd-supply");
pr_debug("%s: motor_en %d, motor_pwm %d, amp %d, n_value %d vdd %d\n",
__func__,
vib->haptic_en_gpio,
vib->motor_pwm_gpio,
vib->amp, vib->vibe_n_value,
vib->use_vdd_supply);
ret = of_property_read_u32(np, "vibe-warmup-delay",
&vib->vibe_warmup_delay);
if (!ret)
pr_debug("%s: vibe_warmup_delay %d ms\n", __func__,
vib->vibe_warmup_delay);
return 0;
}
static ssize_t vibrator_amp_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct timed_output_dev *_dev = dev_get_drvdata(dev);
struct timed_vibrator_data *vib =
container_of(_dev, struct timed_vibrator_data, dev);
int gain = vib->vibe_gain;
return sprintf(buf, "%d\n", gain);
}
static ssize_t vibrator_amp_store(struct device *dev,
struct device_attribute *attr, const char *buf, size_t size)
{
struct timed_output_dev *_dev = dev_get_drvdata(dev);
struct timed_vibrator_data *vib =
container_of(_dev, struct timed_vibrator_data, dev);
int gain;
sscanf(buf, "%d", &gain);
vib->vibe_gain = gain;
return size;
}
static ssize_t vibrator_pwm_show(struct device *dev, struct device_attribute *attr, char *buf)
{
struct timed_output_dev *_dev = dev_get_drvdata(dev);
struct timed_vibrator_data *vib =
container_of(_dev, struct timed_vibrator_data, dev);
return sprintf(buf, "%d\n", vib->vibe_pwm);
}
static ssize_t vibrator_pwm_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t size)
{
struct timed_output_dev *_dev = dev_get_drvdata(dev);
struct timed_vibrator_data *vib =
container_of(_dev, struct timed_vibrator_data, dev);
int pwm;
sscanf(buf, "%d", &pwm);
vib->vibe_pwm = pwm;
return size;
}
static struct device_attribute vibrator_device_attrs[] = {
__ATTR(amp, S_IRUGO | S_IWUSR, vibrator_amp_show, vibrator_amp_store),
__ATTR(n_val, S_IRUGO | S_IWUSR, vibrator_pwm_show, vibrator_pwm_store),
};
static struct timed_vibrator_data msm8974_pwm_vibrator_data = {
.dev.name = "vibrator",
.dev.enable = vibrator_enable,
.dev.get_time = vibrator_get_time,
.max_timeout = 30000, /* max time for vibrator enable 30 sec. */
};
static int msm8974_pwm_vibrator_probe(struct platform_device *pdev)
{
int i, ret = 0;
struct timed_vibrator_data *vib;
platform_set_drvdata(pdev, &msm8974_pwm_vibrator_data);
vib = (struct timed_vibrator_data *)platform_get_drvdata(pdev);
if (pdev->dev.of_node) {
pr_debug("%s: device has of_node\n", __func__);
ret = vibrator_parse_dt(&pdev->dev, vib);
if (ret < 0)
return ret;
}
if (vibrator_regulator_init(pdev, vib) < 0) {
return -ENODEV;
}
pdev->dev.init_name = vib->dev.name;
if (vibrator_gpio_init(vib) < 0) {
pr_err("%s: vibrator_gpio_init failed\n", __func__);
return -ENODEV;
}
virt_base = ioremap(MMSS_CC_PWM_SET, MMSS_CC_PWM_SIZE);
if (virt_base == NULL) {
pr_err("%s: ioremap failed\n", __func__);
ret = -ENODEV;
goto err_ioremap;
}
cam_gp1_clk = clk_get(&pdev->dev, "cam_gp1_clk");
if (cam_gp1_clk == NULL) {
ret = -ENODEV;
goto err_clk_get;
}
clk_set_rate(cam_gp1_clk, 29268);
vib->vibe_gain = vib->amp;
vib->vibe_pwm = vib->vibe_n_value;
atomic_set(&vib->vib_status, false);
atomic_set(&vib->gp1_clk_flag, 0);
INIT_WORK(&vib->work_vibrator_off, msm8974_pwm_vibrator_off);
INIT_WORK(&vib->work_vibrator_on, msm8974_pwm_vibrator_on);
hrtimer_init(&vib->timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
vib->timer.function = vibrator_timer_func;
mutex_init(&vib->lock);
ret = timed_output_dev_register(&vib->dev);
if (ret < 0) {
pr_err("%s: failed to register timed_output_dev\n", __func__);
goto err_timed_output_dev_register;
}
for (i = 0; i < ARRAY_SIZE(vibrator_device_attrs); i++) {
ret = device_create_file(vib->dev.dev,
&vibrator_device_attrs[i]);
if (ret < 0) {
pr_err("%s: failed to create sysfs\n", __func__);
goto err_sysfs;
}
}
pr_info("%s: probed\n", __func__);
return 0;
err_sysfs:
for (; i >= 0; i--) {
device_remove_file(vib->dev.dev,
&vibrator_device_attrs[i]);
}
timed_output_dev_unregister(&vib->dev);
err_timed_output_dev_register:
clk_put(cam_gp1_clk);
err_clk_get:
iounmap(virt_base);
err_ioremap:
vibrator_gpio_deinit(vib);
return ret;
}
static int msm8974_pwm_vibrator_remove(struct platform_device *pdev)
{
struct timed_vibrator_data *vib = platform_get_drvdata(pdev);
int i;
msm8974_pwm_vibrator_force_set(vib, 0, vib->vibe_n_value);
for (i = ARRAY_SIZE(vibrator_device_attrs); i >= 0; i--) {
device_remove_file(vib->dev.dev,
&vibrator_device_attrs[i]);
}
timed_output_dev_unregister(&vib->dev);
clk_put(cam_gp1_clk);
iounmap(virt_base);
vibrator_gpio_deinit(vib);
return 0;
}
static int msm8974_pwm_vibrator_suspend(struct platform_device *pdev,
pm_message_t state)
{
struct timed_vibrator_data *vib = platform_get_drvdata(pdev);
msm8974_pwm_vibrator_force_set(vib, 0, vib->vibe_n_value);
gpio_tlmm_config(GPIO_CFG(vib->motor_pwm_gpio, 0, GPIO_CFG_OUTPUT,
GPIO_CFG_PULL_DOWN, GPIO_CFG_2MA), GPIO_CFG_ENABLE);
gpio_tlmm_config(GPIO_CFG(vib->haptic_en_gpio, 0, GPIO_CFG_OUTPUT,
GPIO_CFG_PULL_DOWN, GPIO_CFG_2MA), GPIO_CFG_ENABLE);
return 0;
}
static int msm8974_pwm_vibrator_resume(struct platform_device *pdev)
{
struct timed_vibrator_data *vib = platform_get_drvdata(pdev);
msm8974_pwm_vibrator_force_set(vib, 0, vib->vibe_n_value);
gpio_tlmm_config(GPIO_CFG(vib->motor_pwm_gpio, 6, GPIO_CFG_INPUT,
GPIO_CFG_NO_PULL, GPIO_CFG_2MA), GPIO_CFG_ENABLE);
gpio_tlmm_config(GPIO_CFG(vib->haptic_en_gpio, 0, GPIO_CFG_OUTPUT,
GPIO_CFG_NO_PULL, GPIO_CFG_2MA), GPIO_CFG_ENABLE);
return 0;
}
static void msm8974_pwm_vibrator_shutdown(struct platform_device *pdev)
{
struct timed_vibrator_data *vib;
vib = (struct timed_vibrator_data *)platform_get_drvdata(pdev);
msm8974_pwm_vibrator_force_set(vib, 0, vib->vibe_n_value);
}
static struct of_device_id vibrator_match_table[] = {
{ .compatible = "msm,pwm_vibrator",},
{ },
};
static struct platform_driver msm8974_pwm_vibrator_driver = {
.probe = msm8974_pwm_vibrator_probe,
.remove = msm8974_pwm_vibrator_remove,
.shutdown = msm8974_pwm_vibrator_shutdown,
.suspend = msm8974_pwm_vibrator_suspend,
.resume = msm8974_pwm_vibrator_resume,
.driver = {
.name = DEVICE_NAME,
.of_match_table = vibrator_match_table,
},
};
static int __init msm8974_pwm_vibrator_init(void)
{
#ifdef ANDROID_VIBRATOR_USE_WORKQUEUE
vibrator_workqueue = create_workqueue("vibrator");
if (!vibrator_workqueue) {
pr_err("%s: out of memory\n", __func__);
return -ENOMEM;
}
#endif
return platform_driver_register(&msm8974_pwm_vibrator_driver);
}
static void __exit msm8974_pwm_vibrator_exit(void)
{
#ifdef ANDROID_VIBRATOR_USE_WORKQUEUE
if (vibrator_workqueue)
destroy_workqueue(vibrator_workqueue);
vibrator_workqueue = NULL;
#endif
platform_driver_unregister(&msm8974_pwm_vibrator_driver);
}
/* to let init lately */
late_initcall_sync(msm8974_pwm_vibrator_init);
module_exit(msm8974_pwm_vibrator_exit);
MODULE_AUTHOR("LG Electronics Inc.");
MODULE_DESCRIPTION("MSM8974 PWM Vibrator Driver");
MODULE_LICENSE("GPL");