/* include/asm/mach-msm/htc_pwrsink.h * * Copyright (C) 2008 HTC Corporation. * Copyright (C) 2007 Google, Inc. * Copyright (c) 2011 The Linux Foundation. All rights reserved. * * 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 "pmic.h" #include "timed_output.h" #include #define PM_LIBPROG 0x30000061 #if (CONFIG_MSM_AMSS_VERSION == 6220) || (CONFIG_MSM_AMSS_VERSION == 6225) #define PM_LIBVERS 0xfb837d0b #else #define PM_LIBVERS 0x10001 #endif #define HTC_PROCEDURE_SET_VIB_ON_OFF 21 #define PMIC_VIBRATOR_LEVEL (3000) static struct work_struct work_vibrator_on; static struct work_struct work_vibrator_off; static struct hrtimer vibe_timer; #ifdef CONFIG_PM8XXX_RPC_VIBRATOR static void set_pmic_vibrator(int on) { int rc; rc = pmic_vib_mot_set_mode(PM_VIB_MOT_MODE__MANUAL); if (rc) { pr_err("%s: Vibrator set mode failed", __func__); return; } if (on) rc = pmic_vib_mot_set_volt(PMIC_VIBRATOR_LEVEL); else rc = pmic_vib_mot_set_volt(0); if (rc) pr_err("%s: Vibrator set voltage level failed", __func__); } #else static void set_pmic_vibrator(int on) { static struct msm_rpc_endpoint *vib_endpoint; struct set_vib_on_off_req { struct rpc_request_hdr hdr; uint32_t data; } req; if (!vib_endpoint) { vib_endpoint = msm_rpc_connect(PM_LIBPROG, PM_LIBVERS, 0); if (IS_ERR(vib_endpoint)) { printk(KERN_ERR "init vib rpc failed!\n"); vib_endpoint = 0; return; } } if (on) req.data = cpu_to_be32(PMIC_VIBRATOR_LEVEL); else req.data = cpu_to_be32(0); msm_rpc_call(vib_endpoint, HTC_PROCEDURE_SET_VIB_ON_OFF, &req, sizeof(req), 5 * HZ); } #endif static void pmic_vibrator_on(struct work_struct *work) { set_pmic_vibrator(1); } static void pmic_vibrator_off(struct work_struct *work) { set_pmic_vibrator(0); } static void timed_vibrator_on(struct timed_output_dev *sdev) { schedule_work(&work_vibrator_on); } static void timed_vibrator_off(struct timed_output_dev *sdev) { schedule_work(&work_vibrator_off); } static void vibrator_enable(struct timed_output_dev *dev, int value) { hrtimer_cancel(&vibe_timer); if (value == 0) timed_vibrator_off(dev); else { value = (value > 15000 ? 15000 : value); timed_vibrator_on(dev); hrtimer_start(&vibe_timer, ktime_set(value / 1000, (value % 1000) * 1000000), HRTIMER_MODE_REL); } } static int vibrator_get_time(struct timed_output_dev *dev) { if (hrtimer_active(&vibe_timer)) { ktime_t r = hrtimer_get_remaining(&vibe_timer); struct timeval t = ktime_to_timeval(r); return t.tv_sec * 1000 + t.tv_usec / 1000; } return 0; } static enum hrtimer_restart vibrator_timer_func(struct hrtimer *timer) { timed_vibrator_off(NULL); return HRTIMER_NORESTART; } static struct timed_output_dev pmic_vibrator = { .name = "vibrator", .get_time = vibrator_get_time, .enable = vibrator_enable, }; void __init msm_init_pmic_vibrator(void) { INIT_WORK(&work_vibrator_on, pmic_vibrator_on); INIT_WORK(&work_vibrator_off, pmic_vibrator_off); hrtimer_init(&vibe_timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL); vibe_timer.function = vibrator_timer_func; timed_output_dev_register(&pmic_vibrator); } MODULE_DESCRIPTION("timed output pmic vibrator device"); MODULE_LICENSE("GPL");