Pull input subsystem updates from Dmitry Torokhov: "Two new drivers for Elan hardware (for I2C touchpad and touchscreen found in several Chromebooks and other devices), a driver for Goodix touch panel, and small fixes to Cypress I2C trackpad and other input drivers. Also we switched to use __maybe_unused instead of gating suspend/ resume code with #ifdef guards to get better compile coverage" * 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/dtor/input: (27 commits) Input: gpio_keys - fix warning regarding uninitialized 'button' variable Input: add support for Elan eKTH I2C touchscreens Input: gpio_keys - fix warning regarding uninitialized 'irq' variable Input: cyapa - use 'error' for error codes Input: cyapa - fix resuming the device Input: gpio_keys - add device tree support for interrupt only keys Input: amikbd - allocate temporary keymap buffer on the stack Input: amikbd - fix build if !CONFIG_HW_CONSOLE Input: lm8323 - missing error check in lm8323_set_disable() Input: initialize device counter variables with -1 Input: initialize input_no to -1 to avoid subtraction Input: i8042 - do not try to load on Intel NUC D54250WYK Input: atkbd - correct MSC_SCAN events for force_release keys Input: cyapa - switch to using managed resources Input: lifebook - use "static inline" instead of "inline" in lifebook.h Input: touchscreen - use __maybe_unused instead of ifdef around suspend/resume Input: mouse - use __maybe_unused instead of ifdef around suspend/resume Input: misc - use __maybe_unused instead of ifdef around suspend/resume Input: cap11xx - support for irq-active-high option Input: cap11xx - add support for various cap11xx devices ...
		
			
				
	
	
		
			413 lines
		
	
	
	
		
			10 KiB
			
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			413 lines
		
	
	
	
		
			10 KiB
			
		
	
	
	
		
			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;
 | 
						|
}
 | 
						|
 | 
						|
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;
 | 
						|
		}
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
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);
 | 
						|
}
 | 
						|
 | 
						|
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);
 | 
						|
}
 | 
						|
 | 
						|
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);
 | 
						|
}
 | 
						|
 | 
						|
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;
 | 
						|
}
 | 
						|
 | 
						|
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);
 | 
						|
}
 | 
						|
 | 
						|
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 =
 | 
						|
					pdata->haptic_pdata;
 | 
						|
	struct max8997_haptic *chip;
 | 
						|
	struct input_dev *input_dev;
 | 
						|
	int error;
 | 
						|
 | 
						|
	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;
 | 
						|
		}
 | 
						|
		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;
 | 
						|
}
 | 
						|
 | 
						|
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;
 | 
						|
}
 | 
						|
 | 
						|
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;
 | 
						|
}
 | 
						|
 | 
						|
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(i2c, 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_ALIAS("platform:max8997-haptic");
 | 
						|
MODULE_AUTHOR("Donggeun Kim <dg77.kim@samsung.com>");
 | 
						|
MODULE_DESCRIPTION("max8997_haptic driver");
 | 
						|
MODULE_LICENSE("GPL");
 |