cregit-Linux how code gets into the kernel

Release 4.11 drivers/input/misc/max8997_haptic.c

/*
 * MAX8997-haptic controller driver
 *
 * Copyright (C) 2012 Samsung Electronics
 * Donggeun Kim <dg77.kim@samsung.com>
 *
 * This program is not provided / owned by Maxim Integrated Products.
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * 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.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 *
 */

#include <linux/module.h>
#include <linux/slab.h>
#include <linux/platform_device.h>
#include <linux/err.h>
#include <linux/pwm.h>
#include <linux/input.h>
#include <linux/mfd/max8997-private.h>
#include <linux/mfd/max8997.h>
#include <linux/regulator/consumer.h>

/* Haptic configuration 2 register */

#define MAX8997_MOTOR_TYPE_SHIFT	7

#define MAX8997_ENABLE_SHIFT		6

#define MAX8997_MODE_SHIFT		5

/* Haptic driver configuration register */

#define MAX8997_CYCLE_SHIFT		6

#define MAX8997_SIG_PERIOD_SHIFT	4

#define MAX8997_SIG_DUTY_SHIFT		2

#define MAX8997_PWM_DUTY_SHIFT		0


struct max8997_haptic {
	
struct device *dev;
	
struct i2c_client *client;
	
struct input_dev *input_dev;
	
struct regulator *regulator;

	
struct work_struct work;
	
struct mutex mutex;

	
bool enabled;
	
unsigned int level;

	
struct pwm_device *pwm;
	
unsigned int pwm_period;
	
enum max8997_haptic_pwm_divisor pwm_divisor;

	
enum max8997_haptic_motor_type type;
	
enum max8997_haptic_pulse_mode mode;

	
unsigned int internal_mode_pattern;
	
unsigned int pattern_cycle;
	
unsigned int pattern_signal_period;
};


static int max8997_haptic_set_duty_cycle(struct max8997_haptic *chip) { int ret = 0; if (chip->mode == MAX8997_EXTERNAL_MODE) { unsigned int duty = chip->pwm_period * chip->level / 100; ret = pwm_config(chip->pwm, duty, chip->pwm_period); } else { int i; u8 duty_index = 0; for (i = 0; i <= 64; i++) { if (chip->level <= i * 100 / 64) { duty_index = i; break; } } switch (chip->internal_mode_pattern) { case 0: max8997_write_reg(chip->client, MAX8997_HAPTIC_REG_SIGPWMDC1, duty_index); break; case 1: max8997_write_reg(chip->client, MAX8997_HAPTIC_REG_SIGPWMDC2, duty_index); break; case 2: max8997_write_reg(chip->client, MAX8997_HAPTIC_REG_SIGPWMDC3, duty_index); break; case 3: max8997_write_reg(chip->client, MAX8997_HAPTIC_REG_SIGPWMDC4, duty_index); break; default: break; } } return ret; }

Contributors

PersonTokensPropCommitsCommitProp
Donggeun Kim173100.00%1100.00%
Total173100.00%1100.00%


static void max8997_haptic_configure(struct max8997_haptic *chip) { u8 value; value = chip->type << MAX8997_MOTOR_TYPE_SHIFT | chip->enabled << MAX8997_ENABLE_SHIFT | chip->mode << MAX8997_MODE_SHIFT | chip->pwm_divisor; max8997_write_reg(chip->client, MAX8997_HAPTIC_REG_CONF2, value); if (chip->mode == MAX8997_INTERNAL_MODE && chip->enabled) { value = chip->internal_mode_pattern << MAX8997_CYCLE_SHIFT | chip->internal_mode_pattern << MAX8997_SIG_PERIOD_SHIFT | chip->internal_mode_pattern << MAX8997_SIG_DUTY_SHIFT | chip->internal_mode_pattern << MAX8997_PWM_DUTY_SHIFT; max8997_write_reg(chip->client, MAX8997_HAPTIC_REG_DRVCONF, value); switch (chip->internal_mode_pattern) { case 0: value = chip->pattern_cycle << 4; max8997_write_reg(chip->client, MAX8997_HAPTIC_REG_CYCLECONF1, value); value = chip->pattern_signal_period; max8997_write_reg(chip->client, MAX8997_HAPTIC_REG_SIGCONF1, value); break; case 1: value = chip->pattern_cycle; max8997_write_reg(chip->client, MAX8997_HAPTIC_REG_CYCLECONF1, value); value = chip->pattern_signal_period; max8997_write_reg(chip->client, MAX8997_HAPTIC_REG_SIGCONF2, value); break; case 2: value = chip->pattern_cycle << 4; max8997_write_reg(chip->client, MAX8997_HAPTIC_REG_CYCLECONF2, value); value = chip->pattern_signal_period; max8997_write_reg(chip->client, MAX8997_HAPTIC_REG_SIGCONF3, value); break; case 3: value = chip->pattern_cycle; max8997_write_reg(chip->client, MAX8997_HAPTIC_REG_CYCLECONF2, value); value = chip->pattern_signal_period; max8997_write_reg(chip->client, MAX8997_HAPTIC_REG_SIGCONF4, value); break; default: break; } } }

Contributors

PersonTokensPropCommitsCommitProp
Donggeun Kim266100.00%1100.00%
Total266100.00%1100.00%


static void max8997_haptic_enable(struct max8997_haptic *chip) { int error; mutex_lock(&chip->mutex); error = max8997_haptic_set_duty_cycle(chip); if (error) { dev_err(chip->dev, "set_pwm_cycle failed, error: %d\n", error); goto out; } if (!chip->enabled) { error = regulator_enable(chip->regulator); if (error) { dev_err(chip->dev, "Failed to enable regulator\n"); goto out; } max8997_haptic_configure(chip); if (chip->mode == MAX8997_EXTERNAL_MODE) { error = pwm_enable(chip->pwm); if (error) { dev_err(chip->dev, "Failed to enable PWM\n"); regulator_disable(chip->regulator); goto out; } } chip->enabled = true; } out: mutex_unlock(&chip->mutex); }

Contributors

PersonTokensPropCommitsCommitProp
Donggeun Kim9664.00%150.00%
Sachin Kamat5436.00%150.00%
Total150100.00%2100.00%


static void max8997_haptic_disable(struct max8997_haptic *chip) { mutex_lock(&chip->mutex); if (chip->enabled) { chip->enabled = false; max8997_haptic_configure(chip); if (chip->mode == MAX8997_EXTERNAL_MODE) pwm_disable(chip->pwm); regulator_disable(chip->regulator); } mutex_unlock(&chip->mutex); }

Contributors

PersonTokensPropCommitsCommitProp
Donggeun Kim68100.00%1100.00%
Total68100.00%1100.00%


static void max8997_haptic_play_effect_work(struct work_struct *work) { struct max8997_haptic *chip = container_of(work, struct max8997_haptic, work); if (chip->level) max8997_haptic_enable(chip); else max8997_haptic_disable(chip); }

Contributors

PersonTokensPropCommitsCommitProp
Donggeun Kim43100.00%1100.00%
Total43100.00%1100.00%


static int max8997_haptic_play_effect(struct input_dev *dev, void *data, struct ff_effect *effect) { struct max8997_haptic *chip = input_get_drvdata(dev); chip->level = effect->u.rumble.strong_magnitude; if (!chip->level) chip->level = effect->u.rumble.weak_magnitude; schedule_work(&chip->work); return 0; }

Contributors

PersonTokensPropCommitsCommitProp
Donggeun Kim72100.00%1100.00%
Total72100.00%1100.00%


static void max8997_haptic_close(struct input_dev *dev) { struct max8997_haptic *chip = input_get_drvdata(dev); cancel_work_sync(&chip->work); max8997_haptic_disable(chip); }

Contributors

PersonTokensPropCommitsCommitProp
Donggeun Kim34100.00%1100.00%
Total34100.00%1100.00%


static int max8997_haptic_probe(struct platform_device *pdev) { struct max8997_dev *iodev = dev_get_drvdata(pdev->dev.parent); const struct max8997_platform_data *pdata = dev_get_platdata(iodev->dev); const struct max8997_haptic_platform_data *haptic_pdata = NULL; struct max8997_haptic *chip; struct input_dev *input_dev; int error; if (pdata) haptic_pdata = pdata->haptic_pdata; if (!haptic_pdata) { dev_err(&pdev->dev, "no haptic platform data\n"); return -EINVAL; } chip = kzalloc(sizeof(struct max8997_haptic), GFP_KERNEL); input_dev = input_allocate_device(); if (!chip || !input_dev) { dev_err(&pdev->dev, "unable to allocate memory\n"); error = -ENOMEM; goto err_free_mem; } INIT_WORK(&chip->work, max8997_haptic_play_effect_work); mutex_init(&chip->mutex); chip->client = iodev->haptic; chip->dev = &pdev->dev; chip->input_dev = input_dev; chip->pwm_period = haptic_pdata->pwm_period; chip->type = haptic_pdata->type; chip->mode = haptic_pdata->mode; chip->pwm_divisor = haptic_pdata->pwm_divisor; switch (chip->mode) { case MAX8997_INTERNAL_MODE: chip->internal_mode_pattern = haptic_pdata->internal_mode_pattern; chip->pattern_cycle = haptic_pdata->pattern_cycle; chip->pattern_signal_period = haptic_pdata->pattern_signal_period; break; case MAX8997_EXTERNAL_MODE: chip->pwm = pwm_request(haptic_pdata->pwm_channel_id, "max8997-haptic"); if (IS_ERR(chip->pwm)) { error = PTR_ERR(chip->pwm); dev_err(&pdev->dev, "unable to request PWM for haptic, error: %d\n", error); goto err_free_mem; } /* * FIXME: pwm_apply_args() should be removed when switching to * the atomic PWM API. */ pwm_apply_args(chip->pwm); break; default: dev_err(&pdev->dev, "Invalid chip mode specified (%d)\n", chip->mode); error = -EINVAL; goto err_free_mem; } chip->regulator = regulator_get(&pdev->dev, "inmotor"); if (IS_ERR(chip->regulator)) { error = PTR_ERR(chip->regulator); dev_err(&pdev->dev, "unable to get regulator, error: %d\n", error); goto err_free_pwm; } input_dev->name = "max8997-haptic"; input_dev->id.version = 1; input_dev->dev.parent = &pdev->dev; input_dev->close = max8997_haptic_close; input_set_drvdata(input_dev, chip); input_set_capability(input_dev, EV_FF, FF_RUMBLE); error = input_ff_create_memless(input_dev, NULL, max8997_haptic_play_effect); if (error) { dev_err(&pdev->dev, "unable to create FF device, error: %d\n", error); goto err_put_regulator; } error = input_register_device(input_dev); if (error) { dev_err(&pdev->dev, "unable to register input device, error: %d\n", error); goto err_destroy_ff; } platform_set_drvdata(pdev, chip); return 0; err_destroy_ff: input_ff_destroy(input_dev); err_put_regulator: regulator_put(chip->regulator); err_free_pwm: if (chip->mode == MAX8997_EXTERNAL_MODE) pwm_free(chip->pwm); err_free_mem: input_free_device(input_dev); kfree(chip); return error; }

Contributors

PersonTokensPropCommitsCommitProp
Donggeun Kim52396.49%133.33%
Marek Szyprowski112.03%133.33%
Boris Brezillon81.48%133.33%
Total542100.00%3100.00%


static int max8997_haptic_remove(struct platform_device *pdev) { struct max8997_haptic *chip = platform_get_drvdata(pdev); input_unregister_device(chip->input_dev); regulator_put(chip->regulator); if (chip->mode == MAX8997_EXTERNAL_MODE) pwm_free(chip->pwm); kfree(chip); return 0; }

Contributors

PersonTokensPropCommitsCommitProp
Donggeun Kim58100.00%1100.00%
Total58100.00%1100.00%


static int __maybe_unused max8997_haptic_suspend(struct device *dev) { struct platform_device *pdev = to_platform_device(dev); struct max8997_haptic *chip = platform_get_drvdata(pdev); max8997_haptic_disable(chip); return 0; }

Contributors

PersonTokensPropCommitsCommitProp
Donggeun Kim3997.50%150.00%
Jingoo Han12.50%150.00%
Total40100.00%2100.00%

static SIMPLE_DEV_PM_OPS(max8997_haptic_pm_ops, max8997_haptic_suspend, NULL); static const struct platform_device_id max8997_haptic_id[] = { { "max8997-haptic", 0 }, { }, }; MODULE_DEVICE_TABLE(platform, max8997_haptic_id); static struct platform_driver max8997_haptic_driver = { .driver = { .name = "max8997-haptic", .pm = &max8997_haptic_pm_ops, }, .probe = max8997_haptic_probe, .remove = max8997_haptic_remove, .id_table = max8997_haptic_id, }; module_platform_driver(max8997_haptic_driver); MODULE_AUTHOR("Donggeun Kim <dg77.kim@samsung.com>"); MODULE_DESCRIPTION("max8997_haptic driver"); MODULE_LICENSE("GPL");

Overall Contributors

PersonTokensPropCommitsCommitProp
Donggeun Kim159495.51%116.67%
Sachin Kamat543.24%116.67%
Marek Szyprowski110.66%116.67%
Boris Brezillon80.48%116.67%
Javier Martinez Canillas10.06%116.67%
Jingoo Han10.06%116.67%
Total1669100.00%6100.00%
Information contained on this website is for historical information purposes only and does not indicate or represent copyright ownership.
Created with cregit.