Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

at v2.6.39-rc7 299 lines 7.4 kB view raw
1/* 2 * twl4030-vibra.c - TWL4030 Vibrator driver 3 * 4 * Copyright (C) 2008-2010 Nokia Corporation 5 * 6 * Written by Henrik Saari <henrik.saari@nokia.com> 7 * Updates by Felipe Balbi <felipe.balbi@nokia.com> 8 * Input by Jari Vanhala <ext-jari.vanhala@nokia.com> 9 * 10 * This program is free software; you can redistribute it and/or modify 11 * it under the terms of the GNU General Public License version 2 as 12 * published by the Free Software Foundation. 13 * 14 * This program is distributed in the hope that it will be useful, but 15 * WITHOUT ANY WARRANTY; without even the implied warranty of 16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 17 * General Public License for more details. 18 * 19 * You should have received a copy of the GNU General Public License 20 * along with this program; if not, write to the Free Software 21 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 22 * 02110-1301 USA 23 * 24 */ 25 26#include <linux/module.h> 27#include <linux/jiffies.h> 28#include <linux/platform_device.h> 29#include <linux/workqueue.h> 30#include <linux/i2c/twl.h> 31#include <linux/mfd/twl4030-codec.h> 32#include <linux/mfd/core.h> 33#include <linux/input.h> 34#include <linux/slab.h> 35 36/* MODULE ID2 */ 37#define LEDEN 0x00 38 39/* ForceFeedback */ 40#define EFFECT_DIR_180_DEG 0x8000 /* range is 0 - 0xFFFF */ 41 42struct vibra_info { 43 struct device *dev; 44 struct input_dev *input_dev; 45 46 struct workqueue_struct *workqueue; 47 struct work_struct play_work; 48 49 bool enabled; 50 int speed; 51 int direction; 52 53 bool coexist; 54}; 55 56static void vibra_disable_leds(void) 57{ 58 u8 reg; 59 60 /* Disable LEDA & LEDB, cannot be used with vibra (PWM) */ 61 twl_i2c_read_u8(TWL4030_MODULE_LED, &reg, LEDEN); 62 reg &= ~0x03; 63 twl_i2c_write_u8(TWL4030_MODULE_LED, LEDEN, reg); 64} 65 66/* Powers H-Bridge and enables audio clk */ 67static void vibra_enable(struct vibra_info *info) 68{ 69 u8 reg; 70 71 twl4030_codec_enable_resource(TWL4030_CODEC_RES_POWER); 72 73 /* turn H-Bridge on */ 74 twl_i2c_read_u8(TWL4030_MODULE_AUDIO_VOICE, 75 &reg, TWL4030_REG_VIBRA_CTL); 76 twl_i2c_write_u8(TWL4030_MODULE_AUDIO_VOICE, 77 (reg | TWL4030_VIBRA_EN), TWL4030_REG_VIBRA_CTL); 78 79 twl4030_codec_enable_resource(TWL4030_CODEC_RES_APLL); 80 81 info->enabled = true; 82} 83 84static void vibra_disable(struct vibra_info *info) 85{ 86 u8 reg; 87 88 /* Power down H-Bridge */ 89 twl_i2c_read_u8(TWL4030_MODULE_AUDIO_VOICE, 90 &reg, TWL4030_REG_VIBRA_CTL); 91 twl_i2c_write_u8(TWL4030_MODULE_AUDIO_VOICE, 92 (reg & ~TWL4030_VIBRA_EN), TWL4030_REG_VIBRA_CTL); 93 94 twl4030_codec_disable_resource(TWL4030_CODEC_RES_APLL); 95 twl4030_codec_disable_resource(TWL4030_CODEC_RES_POWER); 96 97 info->enabled = false; 98} 99 100static void vibra_play_work(struct work_struct *work) 101{ 102 struct vibra_info *info = container_of(work, 103 struct vibra_info, play_work); 104 int dir; 105 int pwm; 106 u8 reg; 107 108 dir = info->direction; 109 pwm = info->speed; 110 111 twl_i2c_read_u8(TWL4030_MODULE_AUDIO_VOICE, 112 &reg, TWL4030_REG_VIBRA_CTL); 113 if (pwm && (!info->coexist || !(reg & TWL4030_VIBRA_SEL))) { 114 115 if (!info->enabled) 116 vibra_enable(info); 117 118 /* set vibra rotation direction */ 119 twl_i2c_read_u8(TWL4030_MODULE_AUDIO_VOICE, 120 &reg, TWL4030_REG_VIBRA_CTL); 121 reg = (dir) ? (reg | TWL4030_VIBRA_DIR) : 122 (reg & ~TWL4030_VIBRA_DIR); 123 twl_i2c_write_u8(TWL4030_MODULE_AUDIO_VOICE, 124 reg, TWL4030_REG_VIBRA_CTL); 125 126 /* set PWM, 1 = max, 255 = min */ 127 twl_i2c_write_u8(TWL4030_MODULE_AUDIO_VOICE, 128 256 - pwm, TWL4030_REG_VIBRA_SET); 129 } else { 130 if (info->enabled) 131 vibra_disable(info); 132 } 133} 134 135/*** Input/ForceFeedback ***/ 136 137static int vibra_play(struct input_dev *input, void *data, 138 struct ff_effect *effect) 139{ 140 struct vibra_info *info = input_get_drvdata(input); 141 142 info->speed = effect->u.rumble.strong_magnitude >> 8; 143 if (!info->speed) 144 info->speed = effect->u.rumble.weak_magnitude >> 9; 145 info->direction = effect->direction < EFFECT_DIR_180_DEG ? 0 : 1; 146 queue_work(info->workqueue, &info->play_work); 147 return 0; 148} 149 150static int twl4030_vibra_open(struct input_dev *input) 151{ 152 struct vibra_info *info = input_get_drvdata(input); 153 154 info->workqueue = create_singlethread_workqueue("vibra"); 155 if (info->workqueue == NULL) { 156 dev_err(&input->dev, "couldn't create workqueue\n"); 157 return -ENOMEM; 158 } 159 return 0; 160} 161 162static void twl4030_vibra_close(struct input_dev *input) 163{ 164 struct vibra_info *info = input_get_drvdata(input); 165 166 cancel_work_sync(&info->play_work); 167 INIT_WORK(&info->play_work, vibra_play_work); /* cleanup */ 168 destroy_workqueue(info->workqueue); 169 info->workqueue = NULL; 170 171 if (info->enabled) 172 vibra_disable(info); 173} 174 175/*** Module ***/ 176#if CONFIG_PM 177static int twl4030_vibra_suspend(struct device *dev) 178{ 179 struct platform_device *pdev = to_platform_device(dev); 180 struct vibra_info *info = platform_get_drvdata(pdev); 181 182 if (info->enabled) 183 vibra_disable(info); 184 185 return 0; 186} 187 188static int twl4030_vibra_resume(struct device *dev) 189{ 190 vibra_disable_leds(); 191 return 0; 192} 193 194static SIMPLE_DEV_PM_OPS(twl4030_vibra_pm_ops, 195 twl4030_vibra_suspend, twl4030_vibra_resume); 196#endif 197 198static int __devinit twl4030_vibra_probe(struct platform_device *pdev) 199{ 200 struct twl4030_codec_vibra_data *pdata = mfd_get_data(pdev); 201 struct vibra_info *info; 202 int ret; 203 204 if (!pdata) { 205 dev_dbg(&pdev->dev, "platform_data not available\n"); 206 return -EINVAL; 207 } 208 209 info = kzalloc(sizeof(*info), GFP_KERNEL); 210 if (!info) 211 return -ENOMEM; 212 213 info->dev = &pdev->dev; 214 info->coexist = pdata->coexist; 215 INIT_WORK(&info->play_work, vibra_play_work); 216 217 info->input_dev = input_allocate_device(); 218 if (info->input_dev == NULL) { 219 dev_err(&pdev->dev, "couldn't allocate input device\n"); 220 ret = -ENOMEM; 221 goto err_kzalloc; 222 } 223 224 input_set_drvdata(info->input_dev, info); 225 226 info->input_dev->name = "twl4030:vibrator"; 227 info->input_dev->id.version = 1; 228 info->input_dev->dev.parent = pdev->dev.parent; 229 info->input_dev->open = twl4030_vibra_open; 230 info->input_dev->close = twl4030_vibra_close; 231 __set_bit(FF_RUMBLE, info->input_dev->ffbit); 232 233 ret = input_ff_create_memless(info->input_dev, NULL, vibra_play); 234 if (ret < 0) { 235 dev_dbg(&pdev->dev, "couldn't register vibrator to FF\n"); 236 goto err_ialloc; 237 } 238 239 ret = input_register_device(info->input_dev); 240 if (ret < 0) { 241 dev_dbg(&pdev->dev, "couldn't register input device\n"); 242 goto err_iff; 243 } 244 245 vibra_disable_leds(); 246 247 platform_set_drvdata(pdev, info); 248 return 0; 249 250err_iff: 251 input_ff_destroy(info->input_dev); 252err_ialloc: 253 input_free_device(info->input_dev); 254err_kzalloc: 255 kfree(info); 256 return ret; 257} 258 259static int __devexit twl4030_vibra_remove(struct platform_device *pdev) 260{ 261 struct vibra_info *info = platform_get_drvdata(pdev); 262 263 /* this also free ff-memless and calls close if needed */ 264 input_unregister_device(info->input_dev); 265 kfree(info); 266 platform_set_drvdata(pdev, NULL); 267 268 return 0; 269} 270 271static struct platform_driver twl4030_vibra_driver = { 272 .probe = twl4030_vibra_probe, 273 .remove = __devexit_p(twl4030_vibra_remove), 274 .driver = { 275 .name = "twl4030-vibra", 276 .owner = THIS_MODULE, 277#ifdef CONFIG_PM 278 .pm = &twl4030_vibra_pm_ops, 279#endif 280 }, 281}; 282 283static int __init twl4030_vibra_init(void) 284{ 285 return platform_driver_register(&twl4030_vibra_driver); 286} 287module_init(twl4030_vibra_init); 288 289static void __exit twl4030_vibra_exit(void) 290{ 291 platform_driver_unregister(&twl4030_vibra_driver); 292} 293module_exit(twl4030_vibra_exit); 294 295MODULE_ALIAS("platform:twl4030-vibra"); 296 297MODULE_DESCRIPTION("TWL4030 Vibra driver"); 298MODULE_LICENSE("GPL"); 299MODULE_AUTHOR("Nokia Corporation");