/* arch/arm/mach-omap2/board-tuna-vibrator.c * * Copyright (C) 2011 Samsung Electronics Co. Ltd. All Rights Reserved. * Author: Rom Lemarchand * * This software is licensed under the terms of the GNU General Public * License version 2, as published by the Free Software Foundation, and * may be copied, distributed, and modified under those terms. * * 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 #include #include #include #include #include #include <../../../drivers/staging/android/timed_output.h> #include "mux.h" #include "board-tuna.h" /* Vibrator enable pin is changed on Rev 05 to block not intended vibration. */ #define GPIO_MOTOR_EN 162 #define GPIO_MOTOR_EN_REV05 54 #define VIB_GPTIMER_NUM 10 #define PWM_DUTY_MAX 1450 #define MAX_TIMEOUT 10000 /* 10s */ static struct vibrator { struct wake_lock wklock; struct hrtimer timer; struct mutex lock; struct omap_dm_timer *gptimer; bool enabled; unsigned gpio_en; } vibdata; static void vibrator_off(void) { if (!vibdata.enabled) return; omap_dm_timer_stop(vibdata.gptimer); gpio_set_value(vibdata.gpio_en, 0); vibdata.enabled = false; wake_unlock(&vibdata.wklock); } static int vibrator_get_time(struct timed_output_dev *dev) { if (hrtimer_active(&vibdata.timer)) { ktime_t r = hrtimer_get_remaining(&vibdata.timer); return ktime_to_ms(r); } return 0; } static int vibrator_timer_init(void) { int ret; ret = omap_dm_timer_set_source(vibdata.gptimer, OMAP_TIMER_SRC_SYS_CLK); if (ret < 0) return ret; omap_dm_timer_set_load(vibdata.gptimer, 1, -PWM_DUTY_MAX); omap_dm_timer_set_match(vibdata.gptimer, 1, -PWM_DUTY_MAX+10); omap_dm_timer_set_pwm(vibdata.gptimer, 0, 1, OMAP_TIMER_TRIGGER_OVERFLOW_AND_COMPARE); omap_dm_timer_enable(vibdata.gptimer); omap_dm_timer_write_counter(vibdata.gptimer, -2); omap_dm_timer_disable(vibdata.gptimer); return 0; } static void vibrator_enable(struct timed_output_dev *dev, int value) { mutex_lock(&vibdata.lock); /* cancel previous timer and set GPIO according to value */ hrtimer_cancel(&vibdata.timer); if (value) { wake_lock(&vibdata.wklock); vibrator_timer_init(); gpio_set_value(vibdata.gpio_en, 1); omap_dm_timer_start(vibdata.gptimer); vibdata.enabled = true; if (value > 0) { if (value > MAX_TIMEOUT) value = MAX_TIMEOUT; hrtimer_start(&vibdata.timer, ns_to_ktime((u64)value * NSEC_PER_MSEC), HRTIMER_MODE_REL); } } else { vibrator_off(); } mutex_unlock(&vibdata.lock); } static struct timed_output_dev to_dev = { .name = "vibrator", .get_time = vibrator_get_time, .enable = vibrator_enable, }; static enum hrtimer_restart vibrator_timer_func(struct hrtimer *timer) { vibrator_off(); return HRTIMER_NORESTART; } static int __init vibrator_init(void) { int ret; vibdata.enabled = false; hrtimer_init(&vibdata.timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL); vibdata.timer.function = vibrator_timer_func; vibdata.gptimer = omap_dm_timer_request_specific(VIB_GPTIMER_NUM); if (vibdata.gptimer == NULL) return -1; omap_dm_timer_dump_regs(vibdata.gptimer); ret = vibrator_timer_init(); if (ret < 0) goto err_dm_timer_init; omap_dm_timer_dump_regs(vibdata.gptimer); wake_lock_init(&vibdata.wklock, WAKE_LOCK_SUSPEND, "vibrator"); mutex_init(&vibdata.lock); ret = timed_output_dev_register(&to_dev); if (ret < 0) goto err_to_dev_reg; return 0; err_to_dev_reg: mutex_destroy(&vibdata.lock); wake_lock_destroy(&vibdata.wklock); err_dm_timer_init: omap_dm_timer_free(vibdata.gptimer); vibdata.gptimer = NULL; return -1; } static int __init omap4_tuna_vibrator_init(void) { int ret; if (!machine_is_tuna()) return 0; vibdata.gpio_en = (omap4_tuna_get_revision() >= 5) ? GPIO_MOTOR_EN_REV05 : GPIO_MOTOR_EN; omap_mux_init_gpio(vibdata.gpio_en, OMAP_PIN_OUTPUT | OMAP_PIN_OFF_OUTPUT_LOW); omap_mux_init_signal("dpm_emu18.dmtimer10_pwm_evt", OMAP_PIN_OUTPUT); ret = gpio_request(vibdata.gpio_en, "vibrator-en"); if (ret) return ret; gpio_direction_output(vibdata.gpio_en, 0); ret = vibrator_init(); if (ret < 0) gpio_free(vibdata.gpio_en); return ret; } /* * This is needed because the vibrator is dependent on omap_dm_timers which get * initialized at device_init time */ late_initcall(omap4_tuna_vibrator_init);