/* SPDX-License-Identifier: GPL-2.0 */ /* * Copyright (c) 2019 MediaTek Inc. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifndef CONFIG_MACH_MT6771 #include #include #endif #define VIB_DEVICE "mtk_vibrator" #define VIB_TAG "[vibrator]" #undef pr_fmt #define pr_fmt(fmt) KBUILD_MODNAME " %s(%d) :" fmt, __func__, __LINE__ struct mt_vibr { struct workqueue_struct *vibr_queue; struct work_struct vibr_onwork; struct work_struct vibr_offwork; struct hrtimer vibr_timer; int ldo_state; int shutdown_flag; #ifndef CONFIG_MACH_MT6771 struct regulator *reg; #endif atomic_t vibr_dur; spinlock_t vibr_lock; atomic_t vibr_state; }; static struct mt_vibr *g_mt_vib; static int vibr_Enable(void) { if (!g_mt_vib->ldo_state) { pr_info("vibr enable"); #ifndef CONFIG_MACH_MT6771 vibr_Enable_HW(g_mt_vib->reg); #else vibr_Enable_HW(); #endif g_mt_vib->ldo_state = 1; } return 0; } static int vibr_Disable(void) { if (g_mt_vib->ldo_state) { pr_info("vibr disable"); #ifndef CONFIG_MACH_MT6771 vibr_Disable_HW(g_mt_vib->reg); #else vibr_Disable_HW(); #endif g_mt_vib->ldo_state = 0; } return 0; } static void on_vibrator(struct work_struct *work) { pr_info("update vibrator enable, ldo=%d", g_mt_vib->ldo_state); vibr_Enable(); } static void off_vibrator(struct work_struct *work) { pr_info("update vibrator disable, ldo=%d", g_mt_vib->ldo_state); vibr_Disable(); } static void vibrator_enable(unsigned int dur, unsigned int activate) { unsigned long flags; struct vibrator_hw *hw = mt_get_cust_vibrator_hw(); hrtimer_cancel(&g_mt_vib->vibr_timer); pr_info(VIB_TAG "cancel hrtimer, cust:%dms, value:%u, activate:%d, shutdown:%d\n", hw->vib_timer, dur, activate, g_mt_vib->shutdown_flag); cancel_work_sync(&g_mt_vib->vibr_onwork); spin_lock_irqsave(&g_mt_vib->vibr_lock, flags); if (activate == 0 || g_mt_vib->shutdown_flag == 1) { atomic_set(&g_mt_vib->vibr_state, 0); queue_work(g_mt_vib->vibr_queue, &g_mt_vib->vibr_offwork); } else { #ifdef CUST_VIBR_LIMIT if (dur > hw->vib_limit && dur < hw->vib_timer) #else if (dur >= 10 && dur < hw->vib_timer) #endif dur = hw->vib_timer; dur = (dur > 15000 ? 15000 : dur); atomic_set(&g_mt_vib->vibr_state, 1); queue_work(g_mt_vib->vibr_queue, &g_mt_vib->vibr_onwork); hrtimer_start(&g_mt_vib->vibr_timer, ktime_set(dur / 1000, (dur % 1000) * 1000000), HRTIMER_MODE_REL); } spin_unlock_irqrestore(&g_mt_vib->vibr_lock, flags); } static void vibrator_oc_handler(void) { pr_info(VIB_TAG "%s: disable vibr for oc intr happened\n", __func__); vibrator_enable(0, 0); } static enum hrtimer_restart vibrator_timer_func(struct hrtimer *timer) { struct mt_vibr *vibr = container_of(timer, struct mt_vibr, vibr_timer); atomic_set(&vibr->vibr_state, 0); pr_info(VIB_TAG "set vibr_state 0"); queue_work(vibr->vibr_queue, &vibr->vibr_offwork); return HRTIMER_NORESTART; } static const struct of_device_id vibr_of_ids[] = { { .compatible = "mediatek,vibrator", }, {} }; static atomic_t vib_state; static ssize_t vibr_activate_show(struct device *dev, struct device_attribute *attr, char *buf) { return sprintf(buf, "%d\n", atomic_read(&g_mt_vib->vibr_state)); } static ssize_t vibr_activate_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t size) { unsigned int activate = 0, dur = 0; ssize_t ret; ret = kstrtouint(buf, 10, &activate); if (ret) { pr_err(VIB_TAG "set activate fail\n"); return ret; } dur = atomic_read(&g_mt_vib->vibr_dur); vibrator_enable(dur, activate); ret = size; return ret; } static ssize_t vibr_state_show(struct device *dev, struct device_attribute *attr, char *buf) { return sprintf(buf, "%d\n", atomic_read(&vib_state)); } static ssize_t vibr_state_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t size) { unsigned int state = 0; ssize_t ret; ret = kstrtouint(buf, 10, &state); if (ret) { pr_err(VIB_TAG "set state fail\n"); return ret; } atomic_set(&vib_state, state); ret = size; return ret; } static ssize_t vibr_duration_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t size) { unsigned int duration = 0; ssize_t ret; ret = kstrtouint(buf, 10, &duration); if (ret) { pr_err(VIB_TAG "set duration fail\n"); return ret; } atomic_set(&g_mt_vib->vibr_dur, duration); ret = size; return ret; } static DEVICE_ATTR(activate, 0644, vibr_activate_show, vibr_activate_store); static DEVICE_ATTR(state, 0644, vibr_state_show, vibr_state_store); static DEVICE_ATTR(duration, 0644, NULL, vibr_duration_store); static struct attribute *activate_attrs[] = { &dev_attr_activate.attr, NULL, }; static struct attribute *state_attrs[] = { &dev_attr_state.attr, NULL, }; static struct attribute *duration_attrs[] = { &dev_attr_duration.attr, NULL, }; static struct attribute_group activate_group = { .attrs = activate_attrs, }; static struct attribute_group state_group = { .attrs = state_attrs, }; static struct attribute_group duration_group = { .attrs = duration_attrs, }; static const struct attribute_group *vibr_group[] = { &activate_group, &state_group, &duration_group, NULL }; static struct led_classdev led_vibr = { .name = "vibrator", .groups = vibr_group, }; static int vib_probe(struct platform_device *pdev) { int ret = 0; struct mt_vibr *vibr; init_vibr_oc_handler(vibrator_oc_handler); vibr = devm_kzalloc(&pdev->dev, sizeof(*vibr), GFP_KERNEL); if (!vibr) return -ENOMEM; ret = devm_led_classdev_register(&pdev->dev, &led_vibr); if (ret < 0) { pr_err(VIB_TAG "led class register fail\n"); return ret; } vibr->vibr_queue = create_singlethread_workqueue(VIB_DEVICE); if (!vibr->vibr_queue) { pr_err(VIB_TAG "unable to create workqueue\n"); return -ENODATA; } #ifndef CONFIG_MACH_MT6771 vibr->reg = devm_regulator_get(&pdev->dev, "vibr"); if (IS_ERR(vibr->reg)) { ret = PTR_ERR(vibr->reg); pr_info("Error load dts: get regulator return %d\n", ret); return ret; } #endif INIT_WORK(&vibr->vibr_onwork, on_vibrator); INIT_WORK(&vibr->vibr_offwork, off_vibrator); spin_lock_init(&vibr->vibr_lock); vibr->shutdown_flag = 0; atomic_set(&vibr->vibr_state, 0); hrtimer_init(&vibr->vibr_timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL); vibr->vibr_timer.function = vibrator_timer_func; dev_set_drvdata(&pdev->dev, vibr); g_mt_vib = vibr; init_cust_vibrator_dtsi(pdev); vibr_power_set(); pr_debug(VIB_TAG "probe done\n"); return 0; } static int vib_remove(struct platform_device *pdev) { struct mt_vibr *vibr = dev_get_drvdata(&pdev->dev); cancel_work_sync(&vibr->vibr_onwork); cancel_work_sync(&vibr->vibr_offwork); hrtimer_cancel(&vibr->vibr_timer); devm_led_classdev_unregister(&pdev->dev, &led_vibr); return 0; } static void vib_shutdown(struct platform_device *pdev) { unsigned long flags; struct mt_vibr *vibr = dev_get_drvdata(&pdev->dev); pr_debug(VIB_TAG "shutdown: enter!\n"); spin_lock_irqsave(&vibr->vibr_lock, flags); vibr->shutdown_flag = 1; if (atomic_read(&vibr->vibr_state)) { atomic_set(&vibr->vibr_state, 0); spin_unlock_irqrestore(&vibr->vibr_lock, flags); pr_debug(VIB_TAG "%s: vibrator will disable\n", __func__); vibr_Disable(); } else { spin_unlock_irqrestore(&vibr->vibr_lock, flags); } } static int vib_suspend(struct device *dev) { int ret = 0; struct platform_device *pdev = to_platform_device(dev); struct mt_vibr *vibr = platform_get_drvdata(pdev); if (atomic_read(&vibr->vibr_state)) { atomic_set(&vibr->vibr_state, 0); ret = vibr_Disable(); pr_info("vibr disbale vibr ret=%d, enter suspend.", ret); } return ret; } static SIMPLE_DEV_PM_OPS(vib_pm_ops, vib_suspend, NULL); #define VIB_PM_OPS (&vib_pm_ops) static struct platform_driver vibrator_driver = { .probe = vib_probe, .remove = vib_remove, .shutdown = vib_shutdown, .driver = { .name = VIB_DEVICE, .owner = THIS_MODULE, .pm = VIB_PM_OPS, #ifdef CONFIG_OF .of_match_table = vibr_of_ids, #endif }, }; static int vib_mod_init(void) { s32 ret; ret = platform_driver_register(&vibrator_driver); if (ret) { pr_err(VIB_TAG "Unable to register driver (%d)\n", ret); return ret; } pr_debug(VIB_TAG "init Done\n"); return 0; } static void vib_mod_exit(void) { pr_debug(VIB_TAG "%s: Done\n", __func__); } module_init(vib_mod_init); module_exit(vib_mod_exit); MODULE_AUTHOR("MediaTek Inc."); MODULE_DESCRIPTION("MTK Vibrator Driver (VIB)"); MODULE_LICENSE("GPL");