diff --git a/arch/arm/configs/mako-perf_defconfig b/arch/arm/configs/mako-perf_defconfig index 6bb139218d8..61d4f69384a 100644 --- a/arch/arm/configs/mako-perf_defconfig +++ b/arch/arm/configs/mako-perf_defconfig @@ -239,8 +239,7 @@ CONFIG_CONNECTOR=y CONFIG_BLK_DEV_LOOP=y CONFIG_BLK_DEV_RAM=y CONFIG_UID_STAT=y -CONFIG_HAPTIC_ISA1200=y -CONFIG_PMIC8XXX_VIBRATOR=y +CONFIG_ANDROID_VIBRATOR=y CONFIG_QSEECOM=y CONFIG_USB_HSIC_SMSC_HUB=y CONFIG_SCSI=y diff --git a/arch/arm/mach-msm/lge/mako/board-mako-misc.c b/arch/arm/mach-msm/lge/mako/board-mako-misc.c index 27f58112388..3b422c2547f 100644 --- a/arch/arm/mach-msm/lge/mako/board-mako-misc.c +++ b/arch/arm/mach-msm/lge/mako/board-mako-misc.c @@ -30,10 +30,6 @@ #include -#ifdef CONFIG_LGE_ISA1200 -#include -#endif - #ifdef CONFIG_BU52031NVX #include #endif @@ -52,7 +48,7 @@ 230Hz motor : 29.4KHZ - M=1, N=163 , */ -#if !defined(CONFIG_LGE_ISA1200) && !defined(CONFIG_ANDROID_VIBRATOR) +#if !defined(CONFIG_ANDROID_VIBRATOR) /* temporal code due to build error.. */ #define GPIO_LIN_MOTOR_EN 0 #define GPIO_LIN_MOTOR_PWM 0 @@ -73,7 +69,7 @@ #define MOTOR_AMP 120 #endif -#if defined(CONFIG_LGE_ISA1200) || defined(CONFIG_ANDROID_VIBRATOR) +#if defined(CONFIG_ANDROID_VIBRATOR) static struct gpiomux_setting vibrator_suspend_cfg = { .func = GPIOMUX_FUNC_GPIO, .drv = GPIOMUX_DRV_2MA, @@ -260,8 +256,6 @@ static int vibrator_init(void) return 0; } - - static struct android_vibrator_platform_data vibrator_data = { .enable_status = 0, .amp = MOTOR_AMP, @@ -281,88 +275,11 @@ static struct platform_device android_vibrator_device = { }; #endif /* CONFIG_ANDROID_VIBRATOR */ -#ifdef CONFIG_LGE_ISA1200 -static int lge_isa1200_clock(int enable) -{ - if (enable) { - REG_WRITEL( - (((0 & 0xffU) << 16U) + /* N_VAL[23:16] */ - (1U << 11U) + /* CLK_ROOT_ENA[11] : Enable(1) */ - (0U << 10U) + /* CLK_INV[10] : Disable(0) */ - (1U << 9U) + /* CLK_BRANCH_ENA[9] : Enable(1) */ - (0U << 8U) + /* NMCNTR_EN[8] : Enable(1) */ - (0U << 7U) + /* MNCNTR_RST[7] : Not Active(0) */ - (0U << 5U) + /* MNCNTR_MODE[6:5] : Dual-edge mode(2) */ - (0U << 3U) + /* PRE_DIV_SEL[4:3] : Div-4 (3) */ - (0U << 0U)), /* SRC_SEL[2:0] : pxo (0) */ - GPn_NS_REG(1)); - /* printk("GPIO_LIN_MOTOR_PWM is enabled. pxo clock."); */ - } else { - REG_WRITEL( - (((0 & 0xffU) << 16U) + /* N_VAL[23:16] */ - (0U << 11U) + /* CLK_ROOT_ENA[11] : Disable(0) */ - (0U << 10U) + /* CLK_INV[10] : Disable(0) */ - (0U << 9U) + /* CLK_BRANCH_ENA[9] : Disable(0) */ - (0U << 8U) + /* NMCNTR_EN[8] : Disable(0) */ - (1U << 7U) + /* MNCNTR_RST[7] : Not Active(0) */ - (0U << 5U) + /* MNCNTR_MODE[6:5] : Dual-edge mode(2) */ - (0U << 3U) + /* PRE_DIV_SEL[4:3] : Div-4 (3) */ - (0U << 0U)), /* SRC_SEL[2:0] : pxo (0) */ - GPn_NS_REG(1)); - /* printk("GPIO_LIN_MOTOR_PWM is disabled."); */ - } - - return 0; -} - -static struct isa1200_reg_cmd isa1200_init_seq[] = { - - {LGE_ISA1200_HCTRL2, 0x00}, /* bk : release sw reset */ - - {LGE_ISA1200_SCTRL , 0x0F}, /* LDO:3V */ - - {LGE_ISA1200_HCTRL0, 0x10}, /* [4:3]10 : PWM Generation Mode [1:0]00 : Divider 1/128 */ - {LGE_ISA1200_HCTRL1, 0xC0}, /* [7] 1 : Ext. Clock Selection, [5] 0:LRA, 1:ERM */ - {LGE_ISA1200_HCTRL3, 0x33}, /* [6:4] 1:PWM/SE Generation PLL Clock Divider */ - - {LGE_ISA1200_HCTRL4, 0x81}, /* bk */ - {LGE_ISA1200_HCTRL5, 0x99}, /* [7:0] PWM High Duty(PWM Gen) 0-6B-D6 */ /* TODO make it platform data? */ - {LGE_ISA1200_HCTRL6, 0x9C}, /* [7:0] PWM Period(PWM Gen) */ /* TODO make it platform data? */ - -}; - -static struct isa1200_reg_seq isa1200_init = { - .reg_cmd_list = isa1200_init_seq, - .number_of_reg_cmd_list = ARRAY_SIZE(isa1200_init_seq), -}; - -static struct lge_isa1200_platform_data lge_isa1200_platform_data = { - .vibrator_name = "vibrator", - - .gpio_hen = GPIO_MOTOR_EN, - .gpio_len = GPIO_MOTOR_EN, - - .clock = lge_isa1200_clock, - .max_timeout = 30000, - .default_vib_strength = 255, /* max strength value is 255 */ - .init_seq = &isa1200_init, -}; - -static struct i2c_board_info lge_i2c_isa1200_board_info[] = { - { - I2C_BOARD_INFO("lge_isa1200", ISA1200_SLAVE_ADDR>>1), - .platform_data = &lge_isa1200_platform_data - }, -}; -#endif - -#if defined(CONFIG_LGE_ISA1200) || defined(CONFIG_ANDROID_VIBRATOR) static struct platform_device *misc_devices[] __initdata = { #ifdef CONFIG_ANDROID_VIBRATOR &android_vibrator_device, #endif }; -#endif #ifdef CONFIG_BU52031NVX #define GPIO_POUCH_INT 22 @@ -432,20 +349,13 @@ static struct platform_device cradle_device = { void __init apq8064_init_misc(void) { -#if defined(CONFIG_ANDROID_VIBRATOR) - int vib_flag = 0; -#endif - INFO_MSG("%s\n", __func__); -#if defined(CONFIG_LGE_ISA1200) || defined(CONFIG_ANDROID_VIBRATOR) +#if defined(CONFIG_ANDROID_VIBRATOR) vibrator_gpio_init(); #endif -#if defined(CONFIG_ANDROID_VIBRATOR) - if (vib_flag == 0) - platform_add_devices(misc_devices, ARRAY_SIZE(misc_devices)); -#endif + platform_add_devices(misc_devices, ARRAY_SIZE(misc_devices)); #ifdef CONFIG_BU52031NVX hall_ic_init(); diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig index 81250044eb5..f1be946322f 100644 --- a/drivers/misc/Kconfig +++ b/drivers/misc/Kconfig @@ -588,6 +588,14 @@ config PMIC8XXX_VIBRATOR on the PM8XXX chips. The vibrator is controlled using the timed output class. +config ANDROID_VIBRATOR + bool "Android vibrator support" + depends on ANDROID_TIMED_OUTPUT + default n + help + This option enalbes device driver support for Android Vibrator driver. + Some functions should be given by platform data for enable PWM, Motor IC, etc. + config PMIC8XXX_NFC tristate "Qualcomm PM8XXX support for Near Field Communication" depends on MFD_PM8XXX diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile index aa4c5724e22..6a370a9b3cb 100644 --- a/drivers/misc/Makefile +++ b/drivers/misc/Makefile @@ -63,6 +63,7 @@ obj-$(CONFIG_PMIC8058_PWM) += pmic8058-pwm.o obj-$(CONFIG_PMIC8XXX_VIBRATOR) += pm8xxx-vibrator.o obj-$(CONFIG_PMIC8XXX_NFC) += pm8xxx-nfc.o obj-$(CONFIG_PMIC8XXX_UPL) += pm8xxx-upl.o +obj-$(CONFIG_ANDROID_VIBRATOR) += android_vibrator.o obj-$(CONFIG_MSM_MEMORY_LOW_POWER_MODE_SUSPEND_DEEP_POWER_DOWN) \ += msm_migrate_pages.o obj-$(CONFIG_PMIC8058_XOADC) += pmic8058-xoadc.o diff --git a/drivers/misc/android_vibrator.c b/drivers/misc/android_vibrator.c new file mode 100644 index 00000000000..ab233f83d77 --- /dev/null +++ b/drivers/misc/android_vibrator.c @@ -0,0 +1,375 @@ +/* + * android vibrator driver + * + * Copyright (C) 2009-2012 LGE, Inc. + * + * Author: Jinkyu Choi + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include "../staging/android/timed_output.h" + +#define ANDROID_VIBRATOR_USE_WORKQUEUE + +#ifdef ANDROID_VIBRATOR_USE_WORKQUEUE +static struct workqueue_struct *vibrator_workqueue; +#endif + +struct timed_vibrator_data { + struct timed_output_dev dev; + struct hrtimer timer; + spinlock_t lock; + atomic_t vib_status; /* on/off */ + + int max_timeout; + atomic_t vibe_gain; /* default max gain */ + atomic_t vibe_pwm; + atomic_t vib_timems; /* vibrator duration */ + struct android_vibrator_platform_data *vibe_data; + struct work_struct work_vibrator_off; + struct work_struct work_vibrator_on; +}; + +static int android_vibrator_force_set(struct timed_vibrator_data *vib, + int nVibIntensity, int n_value) +{ + /* Check the Force value with Max and Min force value */ + int vib_dutation_ms = 0; + INFO_MSG("nVibIntensity : %d\n", nVibIntensity); + + if (nVibIntensity > 127) + nVibIntensity = 127; + if (nVibIntensity < -127) + nVibIntensity = -127; + + /* TODO: control the gain of vibrator */ + if (nVibIntensity == 0) { + vib->vibe_data->ic_enable_set(0); + vib->vibe_data->pwm_set(0, 0, n_value); + /* should be checked for vibrator response time */ + vib->vibe_data->power_set(0); + + atomic_set(&vib->vib_status, false); + } else { + cancel_work_sync(&vib->work_vibrator_off); + hrtimer_cancel(&vib->timer); + vib_dutation_ms = atomic_read(&vib->vib_timems); + /* should be checked for vibrator response time */ + vib->vibe_data->power_set(1); + vib->vibe_data->pwm_set(1, nVibIntensity, n_value); + vib->vibe_data->ic_enable_set(1); + + atomic_set(&vib->vib_status, true); + hrtimer_start(&vib->timer, + ktime_set(vib_dutation_ms / 1000, + (vib_dutation_ms % 1000) * 1000000), + HRTIMER_MODE_REL); + } + return 0; +} + +static void android_vibrator_on(struct work_struct *work) +{ + struct timed_vibrator_data *vib = + container_of(work, struct timed_vibrator_data, + work_vibrator_on); + int gain = atomic_read(&vib->vibe_gain); + int n_value = atomic_read(&vib->vibe_pwm); + INFO_MSG("%s\n", __func__); + /* suspend /resume logging test */ + INFO_MSG("%s is stating ...gain = %d n_value = %d\n", __func__, + gain, n_value); + + android_vibrator_force_set(vib, gain, n_value); + + INFO_MSG("%s is exting ... \n", __func__); +} + +static void android_vibrator_off(struct work_struct *work) +{ + struct timed_vibrator_data *vib = + container_of(work, struct timed_vibrator_data, + work_vibrator_off); + + INFO_MSG("%s is stating ... \n", __func__); + android_vibrator_force_set(vib, 0, vib->vibe_data->vibe_n_value); + INFO_MSG("%s is exting ... \n", __func__); +} + +static enum hrtimer_restart vibrator_timer_func(struct hrtimer *timer) +{ + struct timed_vibrator_data *vib = + container_of(timer, struct timed_vibrator_data, timer); +#ifdef ANDROID_VIBRATOR_USE_WORKQUEUE + queue_work(vibrator_workqueue,&vib->work_vibrator_off); +#endif + 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 nDurationMs) +{ + struct timed_vibrator_data *vib = + container_of(dev, struct timed_vibrator_data, dev); + unsigned long flags; + + INFO_MSG("%s is stating ... \n", __func__); + INFO_MSG("Android_Vibrator[%d] DurationMs = %d \n", + __LINE__, nDurationMs); + spin_lock_irqsave(&vib->lock, flags); + + if (nDurationMs > 0) { + if (nDurationMs > vib->max_timeout) + nDurationMs = vib->max_timeout; + + atomic_set(&vib->vib_timems, nDurationMs); + +#ifdef ANDROID_VIBRATOR_USE_WORKQUEUE + queue_work(vibrator_workqueue,&vib->work_vibrator_on); +#endif + } else { +#ifdef ANDROID_VIBRATOR_USE_WORKQUEUE + queue_work(vibrator_workqueue,&vib->work_vibrator_off); +#endif + } + spin_unlock_irqrestore(&vib->lock, flags); + + INFO_MSG("%s is exting ... \n", __func__); +} + +static ssize_t vibrator_amp_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct timed_output_dev *dev_ = + (struct timed_output_dev *)dev_get_drvdata(dev); + struct timed_vibrator_data *vib = + container_of(dev_, struct timed_vibrator_data, dev); + int gain = atomic_read(&(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_ = + (struct timed_output_dev *)dev_get_drvdata(dev); + struct timed_vibrator_data *vib = + container_of(dev_, struct timed_vibrator_data, dev); + + int gain; + sscanf(buf, "%d", &gain); + atomic_set(&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_ = + (struct timed_output_dev *)dev_get_drvdata(dev); + struct timed_vibrator_data *vib = + container_of(dev_, struct timed_vibrator_data, dev); + int gain = atomic_read(&(vib->vibe_pwm)); + + gain = 4800/gain; + + return sprintf(buf, "%d\n", gain); +} + +static ssize_t vibrator_pwm_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t size) +{ + struct timed_output_dev *dev_ = + (struct timed_output_dev *)dev_get_drvdata(dev); + struct timed_vibrator_data *vib = + container_of(dev_, struct timed_vibrator_data, dev); + + int gain; + sscanf(buf, "%d", &gain); + + gain = 4800/gain; + + atomic_set(&vib->vibe_pwm, gain); + + return size; +} + +static struct device_attribute sm100_device_attrs[] = { + __ATTR(amp, S_IRUGO | S_IWUSR, vibrator_amp_show, vibrator_amp_store), + __ATTR(pwm, S_IRUGO | S_IWUSR, vibrator_pwm_show, vibrator_pwm_store), +}; + +struct timed_vibrator_data android_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. */ + .vibe_data = NULL, +}; + +static int android_vibrator_probe(struct platform_device *pdev) +{ + + int i, ret = 0; + struct timed_vibrator_data *vib; + + platform_set_drvdata(pdev, &android_vibrator_data); + vib = (struct timed_vibrator_data *)platform_get_drvdata(pdev); + vib->vibe_data = + (struct android_vibrator_platform_data *)pdev->dev.platform_data; + + if (vib->vibe_data->vibrator_init() < 0) { + ERR_MSG("Android Vreg, GPIO set failed\n"); + return -ENODEV; + } + + atomic_set(&vib->vibe_gain, vib->vibe_data->amp); /* max value is 128 */ + atomic_set(&vib->vibe_pwm, vib->vibe_data->vibe_n_value); + atomic_set(&vib->vib_status, false); + INFO_MSG("Android_Vibrator[%d] %s nVibIntensity = %d \n", + __LINE__, __func__,vib->vibe_data->amp); + + INIT_WORK(&vib->work_vibrator_off, android_vibrator_off); + INIT_WORK(&vib->work_vibrator_on, android_vibrator_on); + hrtimer_init(&vib->timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL); + vib->timer.function = vibrator_timer_func; + spin_lock_init(&vib->lock); + + ret = timed_output_dev_register(&vib->dev); + if (ret < 0) { + timed_output_dev_unregister(&vib->dev); + return -ENODEV; + } + for (i = 0; i < ARRAY_SIZE(sm100_device_attrs); i++) { + ret = device_create_file(vib->dev.dev, &sm100_device_attrs[i]); + if (ret < 0) { + timed_output_dev_unregister(&vib->dev); + device_remove_file(vib->dev.dev, &sm100_device_attrs[i]); + return -ENODEV; + } + } + + INFO_MSG("Android Vibrator Initialization was done\n"); + + return 0; +} + +static int android_vibrator_remove(struct platform_device *pdev) +{ + struct timed_vibrator_data *vib = + (struct timed_vibrator_data *)platform_get_drvdata(pdev); + +#ifdef ANDROID_VIBRATOR_USE_WORKQUEUE + queue_work(vibrator_workqueue, &vib->work_vibrator_off); +#endif + + timed_output_dev_unregister(&vib->dev); + + return 0; +} + +#ifdef CONFIG_PM +static int android_vibrator_suspend(struct platform_device *pdev, + pm_message_t state) +{ + return 0; +} + +static int android_vibrator_resume(struct platform_device *pdev) +{ + return 0; +} +#endif + +static void android_vibrator_shutdown(struct platform_device *pdev) +{ +} + +static struct platform_driver android_vibrator_driver = { + .probe = android_vibrator_probe, + .remove = android_vibrator_remove, + .shutdown = android_vibrator_shutdown, +#ifdef CONFIG_PM + .suspend = android_vibrator_suspend, + .resume = android_vibrator_resume, +#else + .suspend = NULL, + .resume = NULL, +#endif + .driver = { + .name = "android-vibrator", + }, +}; + +static int __init android_vibrator_init(void) +{ +#if defined(CONFIG_ANDROID_VIBRATOR) + INFO_MSG("Android Vibrator Driver Init\n"); +#ifdef ANDROID_VIBRATOR_USE_WORKQUEUE + vibrator_workqueue = create_workqueue("vibrator"); + + if(!vibrator_workqueue) + return -ENOMEM; +#endif + return platform_driver_register(&android_vibrator_driver); +#endif + return 0; +} + +static void __exit android_vibrator_exit(void) +{ +#if defined(CONFIG_ANDROID_VIBRATOR) + INFO_MSG("Android Vibrator Driver Exit\n"); +#ifdef ANDROID_VIBRATOR_USE_WORKQUEUE + if (vibrator_workqueue) + destroy_workqueue(vibrator_workqueue); + + vibrator_workqueue = NULL; +#endif + platform_driver_unregister(&android_vibrator_driver); +#endif +} + +late_initcall_sync(android_vibrator_init); /* to let init lately */ +module_exit(android_vibrator_exit); + +MODULE_AUTHOR("LG Electronics Inc."); +MODULE_DESCRIPTION("Android Vibrator Driver"); +MODULE_LICENSE("GPL");