/* * Copyright (C) 2009 Samsung Electronics * Kyungmin Park * * Copyright (c) 2011, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and * only version 2 as published by the Free Software Foundation. * * 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. * */ #define pr_fmt(fmt) "%s: " fmt, __func__ #include #include #include #include #include #include #include #include #include #include #include #define ISA1200_HCTRL0 0x30 #define HCTRL0_MODE_CTRL_BIT (3) #define HCTRL0_OVERDRIVE_HIGH_BIT (5) #define HCTRL0_OVERDRIVE_EN_BIT (6) #define HCTRL0_HAP_EN (7) #define HCTRL0_RESET 0x01 #define HCTRL1_RESET 0x4B #define ISA1200_HCTRL1 0x31 #define HCTRL1_SMART_ENABLE_BIT (3) #define HCTRL1_ERM_BIT (5) #define HCTRL1_EXT_CLK_ENABLE_BIT (7) #define ISA1200_HCTRL5 0x35 #define HCTRL5_VIB_STRT 0xD5 #define HCTRL5_VIB_STOP 0x6B #define DIVIDER_128 (128) #define DIVIDER_1024 (1024) #define DIVIDE_SHIFTER_128 (7) #define FREQ_22400 (22400) #define FREQ_172600 (172600) #define POR_DELAY_USEC 250 struct isa1200_chip { const struct isa1200_platform_data *pdata; struct i2c_client *client; struct input_dev *input_device; struct pwm_device *pwm; unsigned int period_ns; unsigned int state; struct work_struct work; }; static void isa1200_vib_set(struct isa1200_chip *haptic, int enable) { int rc; if (enable) { if (haptic->pdata->mode_ctrl == PWM_INPUT_MODE) { int period_us = haptic->period_ns / NSEC_PER_USEC; rc = pwm_config(haptic->pwm, (period_us * haptic->pdata->duty) / 100, period_us); if (rc < 0) pr_err("pwm_config fail\n"); rc = pwm_enable(haptic->pwm); if (rc < 0) pr_err("pwm_enable fail\n"); } else if (haptic->pdata->mode_ctrl == PWM_GEN_MODE) { rc = i2c_smbus_write_byte_data(haptic->client, ISA1200_HCTRL5, HCTRL5_VIB_STRT); if (rc < 0) pr_err("start vibration fail\n"); } } else { if (haptic->pdata->mode_ctrl == PWM_INPUT_MODE) pwm_disable(haptic->pwm); else if (haptic->pdata->mode_ctrl == PWM_GEN_MODE) { rc = i2c_smbus_write_byte_data(haptic->client, ISA1200_HCTRL5, HCTRL5_VIB_STOP); if (rc < 0) pr_err("stop vibration fail\n"); } } } static int isa1200_setup(struct i2c_client *client) { struct isa1200_chip *haptic = i2c_get_clientdata(client); int value, temp, rc; gpio_set_value_cansleep(haptic->pdata->hap_en_gpio, 0); udelay(POR_DELAY_USEC); gpio_set_value_cansleep(haptic->pdata->hap_en_gpio, 1); value = (haptic->pdata->smart_en << HCTRL1_SMART_ENABLE_BIT) | (haptic->pdata->is_erm << HCTRL1_ERM_BIT) | (haptic->pdata->ext_clk_en << HCTRL1_EXT_CLK_ENABLE_BIT); rc = i2c_smbus_write_byte_data(client, ISA1200_HCTRL1, value); if (rc < 0) { pr_err("i2c write failure\n"); return rc; } if (haptic->pdata->mode_ctrl == PWM_GEN_MODE) { temp = haptic->pdata->pwm_fd.pwm_div; if (temp < DIVIDER_128 || temp > DIVIDER_1024 || temp % DIVIDER_128) { pr_err("Invalid divider\n"); rc = -EINVAL; goto reset_hctrl1; } value = ((temp >> DIVIDE_SHIFTER_128) - 1); } else if (haptic->pdata->mode_ctrl == PWM_INPUT_MODE) { temp = haptic->pdata->pwm_fd.pwm_freq; if (temp < FREQ_22400 || temp > FREQ_172600 || temp % FREQ_22400) { pr_err("Invalid frequency\n"); rc = -EINVAL; goto reset_hctrl1; } value = ((temp / FREQ_22400) - 1); haptic->period_ns = NSEC_PER_SEC / temp; } value |= (haptic->pdata->mode_ctrl << HCTRL0_MODE_CTRL_BIT) | (haptic->pdata->overdrive_high << HCTRL0_OVERDRIVE_HIGH_BIT) | (haptic->pdata->overdrive_en << HCTRL0_OVERDRIVE_EN_BIT) | (haptic->pdata->chip_en << HCTRL0_HAP_EN); rc = i2c_smbus_write_byte_data(client, ISA1200_HCTRL0, value); if (rc < 0) { pr_err("i2c write failure\n"); goto reset_hctrl1; } return 0; reset_hctrl1: i2c_smbus_write_byte_data(client, ISA1200_HCTRL1, HCTRL1_RESET); return rc; } static void isa1200_worker(struct work_struct *work) { struct isa1200_chip *haptic; haptic = container_of(work, struct isa1200_chip, work); isa1200_vib_set(haptic, !!haptic->state); } static int isa1200_play_effect(struct input_dev *dev, void *data, struct ff_effect *effect) { struct isa1200_chip *haptic = input_get_drvdata(dev); /* support basic vibration */ haptic->state = effect->u.rumble.strong_magnitude >> 8; if (!haptic->state) haptic->state = effect->u.rumble.weak_magnitude >> 9; schedule_work(&haptic->work); return 0; } #ifdef CONFIG_PM static int isa1200_suspend(struct device *dev) { struct isa1200_chip *haptic = dev_get_drvdata(dev); int rc; cancel_work_sync(&haptic->work); /* turn-off current vibration */ isa1200_vib_set(haptic, 0); if (haptic->pdata->power_on) { rc = haptic->pdata->power_on(0); if (rc) { pr_err("power-down failed\n"); return rc; } } return 0; } static int isa1200_resume(struct device *dev) { struct isa1200_chip *haptic = dev_get_drvdata(dev); int rc; if (haptic->pdata->power_on) { rc = haptic->pdata->power_on(1); if (rc) { pr_err("power-up failed\n"); return rc; } } isa1200_setup(haptic->client); return 0; } #else #define isa1200_suspend NULL #define isa1200_resume NULL #endif static int isa1200_open(struct input_dev *dev) { struct isa1200_chip *haptic = input_get_drvdata(dev); int rc; /* device setup */ if (haptic->pdata->dev_setup) { rc = haptic->pdata->dev_setup(true); if (rc < 0) { pr_err("setup failed!\n"); return rc; } } /* power on */ if (haptic->pdata->power_on) { rc = haptic->pdata->power_on(true); if (rc < 0) { pr_err("power failed\n"); goto err_setup; } } /* request gpio */ rc = gpio_is_valid(haptic->pdata->hap_en_gpio); if (rc) { rc = gpio_request(haptic->pdata->hap_en_gpio, "haptic_gpio"); if (rc) { pr_err("gpio %d request failed\n", haptic->pdata->hap_en_gpio); goto err_power_on; } } else { pr_err("Invalid gpio %d\n", haptic->pdata->hap_en_gpio); goto err_power_on; } rc = gpio_direction_output(haptic->pdata->hap_en_gpio, 0); if (rc) { pr_err("gpio %d set direction failed\n", haptic->pdata->hap_en_gpio); goto err_gpio_free; } /* setup registers */ rc = isa1200_setup(haptic->client); if (rc < 0) { pr_err("setup fail %d\n", rc); goto err_gpio_free; } if (haptic->pdata->mode_ctrl == PWM_INPUT_MODE) { haptic->pwm = pwm_request(haptic->pdata->pwm_ch_id, haptic->client->driver->id_table->name); if (IS_ERR(haptic->pwm)) { pr_err("pwm request failed\n"); rc = PTR_ERR(haptic->pwm); goto err_reset_hctrl0; } } /* init workqeueue */ INIT_WORK(&haptic->work, isa1200_worker); return 0; err_reset_hctrl0: i2c_smbus_write_byte_data(haptic->client, ISA1200_HCTRL0, HCTRL0_RESET); err_gpio_free: gpio_free(haptic->pdata->hap_en_gpio); err_power_on: if (haptic->pdata->power_on) haptic->pdata->power_on(0); err_setup: if (haptic->pdata->dev_setup) haptic->pdata->dev_setup(false); return rc; } static void isa1200_close(struct input_dev *dev) { struct isa1200_chip *haptic = input_get_drvdata(dev); /* turn-off current vibration */ isa1200_vib_set(haptic, 0); if (haptic->pdata->mode_ctrl == PWM_INPUT_MODE) pwm_free(haptic->pwm); gpio_free(haptic->pdata->hap_en_gpio); /* reset hardware registers */ i2c_smbus_write_byte_data(haptic->client, ISA1200_HCTRL0, HCTRL0_RESET); i2c_smbus_write_byte_data(haptic->client, ISA1200_HCTRL1, HCTRL1_RESET); if (haptic->pdata->dev_setup) haptic->pdata->dev_setup(false); /* power-off the chip */ if (haptic->pdata->power_on) haptic->pdata->power_on(0); } static int __devinit isa1200_probe(struct i2c_client *client, const struct i2c_device_id *id) { struct isa1200_chip *haptic; int rc; if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA)) { pr_err("i2c is not supported\n"); return -EIO; } if (!client->dev.platform_data) { pr_err("pdata is not avaiable\n"); return -EINVAL; } haptic = kzalloc(sizeof(struct isa1200_chip), GFP_KERNEL); if (!haptic) { pr_err("no memory\n"); return -ENOMEM; } haptic->pdata = client->dev.platform_data; haptic->client = client; i2c_set_clientdata(client, haptic); haptic->input_device = input_allocate_device(); if (!haptic->input_device) { pr_err("input device alloc failed\n"); rc = -ENOMEM; goto err_mem_alloc; } input_set_drvdata(haptic->input_device, haptic); haptic->input_device->name = haptic->pdata->name ? : "isa1200-ff-memless"; haptic->input_device->dev.parent = &client->dev; input_set_capability(haptic->input_device, EV_FF, FF_RUMBLE); haptic->input_device->open = isa1200_open; haptic->input_device->close = isa1200_close; rc = input_ff_create_memless(haptic->input_device, NULL, isa1200_play_effect); if (rc < 0) { pr_err("unable to register with ff\n"); goto err_free_dev; } rc = input_register_device(haptic->input_device); if (rc < 0) { pr_err("unable to register input device\n"); goto err_ff_destroy; } return 0; err_ff_destroy: input_ff_destroy(haptic->input_device); err_free_dev: input_free_device(haptic->input_device); err_mem_alloc: kfree(haptic); return rc; } static int __devexit isa1200_remove(struct i2c_client *client) { struct isa1200_chip *haptic = i2c_get_clientdata(client); input_unregister_device(haptic->input_device); kfree(haptic); return 0; } static const struct i2c_device_id isa1200_id_table[] = { {"isa1200_1", 0}, { }, }; MODULE_DEVICE_TABLE(i2c, isa1200_id_table); static const struct dev_pm_ops isa1200_pm_ops = { .suspend = isa1200_suspend, .resume = isa1200_resume, }; static struct i2c_driver isa1200_driver = { .driver = { .name = "isa1200-ff-memless", .owner = THIS_MODULE, .pm = &isa1200_pm_ops, }, .probe = isa1200_probe, .remove = __devexit_p(isa1200_remove), .id_table = isa1200_id_table, }; static int __init isa1200_init(void) { return i2c_add_driver(&isa1200_driver); } module_init(isa1200_init); static void __exit isa1200_exit(void) { i2c_del_driver(&isa1200_driver); } module_exit(isa1200_exit); MODULE_DESCRIPTION("isa1200 based vibrator chip driver"); MODULE_LICENSE("GPL v2"); MODULE_AUTHOR("Kyungmin Park ");