Linux Input/HID development
 help / color / mirror / Atom feed
* ATENCIÓN;
From: Sistemas, Administrador, Equipo, [administrator@ancol.com] @ 2014-07-29 12:43 UTC (permalink / raw)
  To: Recipients

ATENCIÓN;

Su buzón ha superado el límite de almacenamiento, que es de 5 GB como se define por el administrador, que se está ejecutando actualmente en 10.9GB, es posible que no pueda enviar ni recibir correo nuevo hasta que vuelva a validar su correo del buzón. Para revalidar su buzón de correo, enviar la siguiente información a continuación:

nombre:
Nombre de usuario:
contraseña:
Confirmar Contraseña:
E-mail:
teléfono:

Si no puede revalidar su buzón, el buzón se deshabilitará!

Lo siento por los inconvenientes ocasionados.
Código de verificación: es: 006524
Soporte Técnico Correo © 2014

gracias
Administrador de Sistemas.
--
To unsubscribe from this list: send the line "unsubscribe linux-input" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply

* Re: [Patch v3] input: drv260x: Add TI drv260x haptics driver
From: Murphy, Dan @ 2014-07-29 20:28 UTC (permalink / raw)
  To: Dmitry Torokhov
  Cc: linux-input@vger.kernel.org, linux-kernel@vger.kernel.org,
	devicetree@vger.kernel.org, mark.rutland@arm.com,
	madcatxster@devoid-pointer.net, simon@mungewell.org,
	elias.vds@gmail.com
In-Reply-To: <20140729200126.GB34989@core.coreip.homeip.net>

Dmitry

Thanks for the review.

On 07/29/2014 03:01 PM, Dmitry Torokhov wrote:
> On Tue, Jul 29, 2014 at 02:32:27PM -0500, Dan Murphy wrote:
>> Add the TI drv260x haptics/vibrator driver.
>> This device uses the input force feedback
>> to produce a wave form to driver an
>> ERM or LRA actuator device.
>>
>> The initial driver supports the devices
>> real time playback mode.  But the device
>> has additional wave patterns in ROM.
>>
>> This functionality will be added in
>> future patchsets.
>>
>> Product data sheet is located here:
>> http://www.ti.com/product/drv2605
>>
>> Signed-off-by: Dan Murphy <dmurphy@ti.com>
>> ---
>>
>> v3 - Updated binding doc, changed to memless device, updated input alloc to
>> devm, removed mutex locking, add sanity checks for mode and library - https://patchwork.kernel.org/patch/4635421/
>> v2 - Fixed binding doc and patch headline - https://patchwork.kernel.org/patch/4619641/
>>
>>  .../devicetree/bindings/input/ti,drv260x.txt       |   50 ++
>>  drivers/input/misc/Kconfig                         |    9 +
>>  drivers/input/misc/Makefile                        |    1 +
>>  drivers/input/misc/drv260x.c                       |  515 ++++++++++++++++++++
>>  include/dt-bindings/input/ti-drv260x.h             |   30 ++
>>  include/linux/input/drv260x.h                      |  181 +++++++
>>  6 files changed, 786 insertions(+)
>>  create mode 100644 Documentation/devicetree/bindings/input/ti,drv260x.txt
>>  create mode 100644 drivers/input/misc/drv260x.c
>>  create mode 100644 include/dt-bindings/input/ti-drv260x.h
>>  create mode 100644 include/linux/input/drv260x.h
>>
>> diff --git a/Documentation/devicetree/bindings/input/ti,drv260x.txt b/Documentation/devicetree/bindings/input/ti,drv260x.txt
>> new file mode 100644
>> index 0000000..8e6970d
>> --- /dev/null
>> +++ b/Documentation/devicetree/bindings/input/ti,drv260x.txt
>> @@ -0,0 +1,50 @@
>> +Texas Instruments - drv260x Haptics driver family
>> +
>> +The drv260x family serial control bus communicates through I2C protocols
>> +
>> +Required properties:
>> +	- compatible - One of:
>> +		"ti,drv2604" - DRV2604
>> +		"ti,drv2605" - DRV2605
>> +		"ti,drv2605l" - DRV2605L
>> +	- reg -  I2C slave address
>> +	- supply- Required supply regulators are:
>> +		"vbat" - battery voltage
>> +	- mode - Power up mode of the chip (defined in include/dt-bindings/input/ti-drv260x.h)
>> +		DRV260X_LRA_MODE - Linear Resonance Actuator mode (Piezoelectric)
>> +		DRV260X_LRA_NO_CAL_MODE - This is a LRA Mode but there is no calibration
>> +				sequence during init.  And the device is configured for real
>> +				time playback mode (RTP mode).
>> +		DRV260X_ERM_MODE - Eccentric Rotating Mass mode (Rotary vibrator)
>> +	- library-sel - These are ROM based waveforms pre-programmed into the IC.
>> +				This should be set to set the library to use at power up.
>> +				(defined in include/dt-bindings/input/ti-drv260x.h)
>> +		DRV260X_LIB_A - Pre-programmed Library
>> +		DRV260X_LIB_B - Pre-programmed Library
>> +		DRV260X_LIB_C - Pre-programmed Library
>> +		DRV260X_LIB_D - Pre-programmed Library
>> +		DRV260X_LIB_E - Pre-programmed Library
>> +		DRV260X_LIB_F - Pre-programmed Library
>> +
>> +Optional properties:
>> +	- enable-gpio - gpio pin to enable/disable the device.
>> +	- vib_rated_voltage - The rated voltage of the actuator in millivolts.
>> +			  If this is not set then the value will be defaulted to
>> +			  3.2 v.
>> +	- vib_overdrive_voltage - The overdrive voltage of the actuator in millivolts.
>> +			  If this is not set then the value will be defaulted to
>> +			  3.2 v.
>> +Example:
>> +
>> +drv2605l: drv2605l@5a {
>> +		compatible = "ti,drv2605l";
>> +		reg = <0x5a>;
>> +		enable-gpio = <&gpio1 28 GPIO_ACTIVE_HIGH>;
>> +		mode = <DRV260X_LRA_MODE>;
>> +		library-sel = <DRV260X_LIB_SEL_DEFAULT>;
>> +		vib-rated-voltage = <3200>;
>> +		vib-overdriver-voltage = <3200>;
>> +};
>> +
>> +For more product information please see the link below:
>> +http://www.ti.com/product/drv2605
>> diff --git a/drivers/input/misc/Kconfig b/drivers/input/misc/Kconfig
>> index 2ff4425..99f6762 100644
>> --- a/drivers/input/misc/Kconfig
>> +++ b/drivers/input/misc/Kconfig
>> @@ -676,4 +676,13 @@ config INPUT_SOC_BUTTON_ARRAY
>>  	  To compile this driver as a module, choose M here: the
>>  	  module will be called soc_button_array.
>>  
>> +config INPUT_DRV260X_HAPTICS
>> +	tristate "TI DRV260X haptics support"
>> +	depends on INPUT && I2C
>> +	help
>> +	  Say Y to enable support for the TI DRV260X haptics driver.
>> +
>> +	  To compile this driver as a module, choose M here: the
>> +	  module will be called drv260x-haptics.
>> +
>>  endif
>> diff --git a/drivers/input/misc/Makefile b/drivers/input/misc/Makefile
>> index 4955ad3..d8ef3c7 100644
>> --- a/drivers/input/misc/Makefile
>> +++ b/drivers/input/misc/Makefile
>> @@ -64,3 +64,4 @@ obj-$(CONFIG_INPUT_WM831X_ON)		+= wm831x-on.o
>>  obj-$(CONFIG_INPUT_XEN_KBDDEV_FRONTEND)	+= xen-kbdfront.o
>>  obj-$(CONFIG_INPUT_YEALINK)		+= yealink.o
>>  obj-$(CONFIG_INPUT_IDEAPAD_SLIDEBAR)	+= ideapad_slidebar.o
>> +obj-$(CONFIG_INPUT_DRV260X_HAPTICS)	+= drv260x.o
>> diff --git a/drivers/input/misc/drv260x.c b/drivers/input/misc/drv260x.c
>> new file mode 100644
>> index 0000000..edf75a2
>> --- /dev/null
>> +++ b/drivers/input/misc/drv260x.c
>> @@ -0,0 +1,515 @@
>> +/*
>> + * drv260x.c - DRV260X haptics driver family
>> + *
>> + * Author: Dan Murphy <dmurphy@ti.com>
>> + *
>> + * Copyright:   (C) 2014 Texas Instruments, Inc.
>> + *
>> + * This program is free software; you can redistribute it and/or modify
>> + * it under the terms of the GNU General Public License 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.
>> + */
>> +
>> +#include <linux/i2c.h>
>> +#include <linux/input.h>
>> +#include <linux/module.h>
>> +#include <linux/of_gpio.h>
>> +#include <linux/platform_device.h>
>> +#include <linux/regmap.h>
>> +#include <linux/slab.h>
>> +#include <linux/delay.h>
>> +#include <linux/regulator/consumer.h>
>> +
>> +#include <linux/input/drv260x.h>
>> +#include <dt-bindings/input/ti-drv260x.h>
>> +
>> +/**
>> + * struct drv260x_data -
>> + * @input_dev - Pointer to the input device
>> + * @client - Pointer to the I2C client
>> + * @regmap - Register map of the device
>> + * @work - Work item used to off load the enable/disable of the vibration
>> + * @enable_gpio - Pointer to the gpio used for enable/disabling
>> + * @regulator - Pointer to the regulator for the IC
>> + * @magnitude - Magnitude of the vibration event
>> + * @mode - The operating mode of the IC (LRA_NO_CAL, ERM or LRA)
>> + * @library - The vibration library to be used
>> + * @rated_voltage - The rated_voltage of the actuator
>> + * @overdriver_voltage - The over drive voltage of the actuator
>> +**/
>> +struct drv260x_data {
>> +	struct input_dev *input_dev;
>> +	struct i2c_client *client;
>> +	struct regmap *regmap;
>> +	struct work_struct work;
>> +	struct gpio_desc *enable_gpio;
>> +	struct regulator *regulator;
>> +	u32 magnitude;
>> +	u32 mode;
>> +	u32 library;
>> +	int rated_voltage;
>> +	int overdrive_voltage;
>> +};
>> +
>> +static struct reg_default drv260x_reg_defs[] = {
>> +	{ DRV260X_STATUS, 0xe0 },
>> +	{ DRV260X_MODE, 0x40 },
>> +	{ DRV260X_RT_PB_IN, 0x00},
>> +	{ DRV260X_LIB_SEL, 0x00},
>> +	{ DRV260X_WV_SEQ_1, 0x01},
>> +	{ DRV260X_WV_SEQ_2, 0x00},
>> +	{ DRV260X_WV_SEQ_3, 0x00},
>> +	{ DRV260X_WV_SEQ_4, 0x00},
>> +	{ DRV260X_WV_SEQ_5, 0x00},
>> +	{ DRV260X_WV_SEQ_6, 0x00},
>> +	{ DRV260X_WV_SEQ_7, 0x00},
>> +	{ DRV260X_WV_SEQ_8, 0x00},
>> +	{ DRV260X_GO, 0x00},
>> +	{ DRV260X_OVERDRIVE_OFF, 0x00},
>> +	{ DRV260X_SUSTAIN_P_OFF, 0x00},
>> +	{ DRV260X_SUSTAIN_N_OFF, 0x00},
>> +	{ DRV260X_BRAKE_OFF	, 0x00},
>> +	{ DRV260X_A_TO_V_CTRL	, 0x05},
>> +	{ DRV260X_A_TO_V_MIN_INPUT, 0x19},
>> +	{ DRV260X_A_TO_V_MAX_INPUT, 0xff},
>> +	{ DRV260X_A_TO_V_MIN_OUT, 0x19},
>> +	{ DRV260X_A_TO_V_MAX_OUT, 0xff},
>> +	{ DRV260X_RATED_VOLT, 0x3e},
>> +	{ DRV260X_OD_CLAMP_VOLT, 0x8c},
>> +	{ DRV260X_CAL_COMP, 0x0c},
>> +	{ DRV260X_CAL_BACK_EMF, 0x6c},
>> +	{ DRV260X_FEEDBACK_CTRL, 0x36},
>> +	{ DRV260X_CTRL1, 0x93},
>> +	{ DRV260X_CTRL2, 0xfa},
>> +	{ DRV260X_CTRL3, 0xa0},
>> +	{ DRV260X_CTRL4, 0x20},
>> +	{ DRV260X_CTRL5, 0x80},
>> +	{ DRV260X_LRA_LOOP_PERIOD, 0x33},
>> +	{ DRV260X_VBAT_MON, 0x00},
>> +	{ DRV260X_LRA_RES_PERIOD, 0x00},
>> +};
>> +
>> +/**
>> + * Rated and Overdriver Voltages:
>> + * Calculated using the formula r = v * 255 / 5.6
>> + * where r is what will be written to the register
>> + * and v is the rated or overdriver voltage of the actuator
>> + **/
>> +#define DRV260X_DEF_RATED_VOLT		0x90
>> +#define DRV260X_DEF_OD_CLAMP_VOLT	0x90
>> +
>> +static int drv260x_calculate_voltage(int voltage)
>> +{
>> +	return (voltage * 255 / 5600);
>> +}
>> +
>> +static void drv260x_worker(struct work_struct *work)
>> +{
>> +	struct drv260x_data *haptics = container_of(work, struct drv260x_data, work);
>> +
>> +	regmap_write(haptics->regmap, DRV260X_RT_PB_IN,	haptics->magnitude);
> 
> Error handling.
> 
>> +
>> +}
>> +
>> +static int drv260x_haptics_play(struct input_dev *input, void *data,
>> +				struct ff_effect *effect)
>> +{
>> +	struct drv260x_data *haptics = input_get_drvdata(input);
>> +	int ret;
>> +
>> +	gpiod_set_value(haptics->enable_gpio, 1);
> 
> Error handling please. Also, is it possible that chip would need sleep
> to control gpio?
> 
>> +	/* Data sheet says to wait 250us before trying to communicate */
>> +	udelay(250);
>> +
>> +	ret = regmap_write(haptics->regmap, DRV260X_MODE, DRV260X_RT_PLAYBACK);
>> +	if (ret != 0) {
>> +		dev_err(&haptics->client->dev,
>> +			"Failed to write set mode: %d\n",
>> +			ret);
>> +		return ret;
>> +	}
> 
> Does it actually work? The playback handler is running with interrupts
> disabled so I2C transfers are forbidden. I think you need to move all
> this stuff in workqueue handler.
> 
>> +
>> +	haptics->mode = DRV260X_LRA_NO_CAL_MODE;
>> +	haptics->magnitude = 0;
>> +
>> +	if (effect->u.rumble.strong_magnitude ||
>> +		effect->u.rumble.weak_magnitude) {
>> +		if (effect->u.rumble.strong_magnitude > 0)
>> +			haptics->magnitude = effect->u.rumble.strong_magnitude;
>> +		else if	(effect->u.rumble.weak_magnitude > 0)
>> +			haptics->magnitude = effect->u.rumble.weak_magnitude;
>> +	}
>> +
>> +	schedule_work(&haptics->work);
> 
> Hm, can you please tell be why exactly you split off just one regmap
> access into a separate work item?

The original work had a lot more code in previous patches.  I will end up moving the regmap calls
and gpio calls to the work handler.

> 
>> +
>> +	return 0;
>> +}
>> +
>> +static void drv260x_close(struct input_dev *input)
>> +{
>> +	struct drv260x_data *haptics = input_get_drvdata(input);
>> +
>> +	cancel_work_sync(&haptics->work);
>> +	regmap_write(haptics->regmap, DRV260X_MODE, DRV260X_STANDBY);
> 
> Error handling.
> 
>> +	gpiod_set_value(haptics->enable_gpio, 0);
>> +}
>> +
>> +static const struct reg_default drv260x_lra_cal_regs[] = {
>> +	{ DRV260X_MODE, DRV260X_AUTO_CAL },
>> +	{ DRV260X_CTRL3, DRV260X_NG_THRESH_2},
>> +	{ DRV260X_FEEDBACK_CTRL, DRV260X_FB_REG_LRA_MODE | DRV260X_BRAKE_FACTOR_4X | DRV260X_LOOP_GAIN_HIGH },
>> +};
>> +
>> +static const struct reg_default drv260x_lra_init_regs[] = {
>> +	{ DRV260X_MODE, DRV260X_RT_PLAYBACK},
>> +	{ DRV260X_A_TO_V_CTRL, DRV260X_AUDIO_HAPTICS_PEAK_20MS | DRV260X_AUDIO_HAPTICS_FILTER_125HZ},
>> +	{ DRV260X_A_TO_V_MIN_INPUT, DRV260X_AUDIO_HAPTICS_MIN_IN_VOLT },
>> +	{ DRV260X_A_TO_V_MAX_INPUT, DRV260X_AUDIO_HAPTICS_MAX_IN_VOLT },
>> +	{ DRV260X_A_TO_V_MIN_OUT, DRV260X_AUDIO_HAPTICS_MIN_OUT_VOLT },
>> +	{ DRV260X_A_TO_V_MAX_OUT, DRV260X_AUDIO_HAPTICS_MAX_OUT_VOLT },
>> +	{ DRV260X_FEEDBACK_CTRL, DRV260X_FB_REG_LRA_MODE | DRV260X_BRAKE_FACTOR_2X | DRV260X_LOOP_GAIN_MED | DRV260X_BEMF_GAIN_3 },
>> +	{ DRV260X_CTRL1, DRV260X_STARTUP_BOOST },
>> +	{ DRV260X_CTRL2, DRV260X_SAMP_TIME_250 },
>> +	{ DRV260X_CTRL3, DRV260X_NG_THRESH_2 | DRV260X_ANANLOG_IN },
>> +	{ DRV260X_CTRL4, DRV260X_AUTOCAL_TIME_500MS },
>> +};
>> +
>> +static const struct reg_default drv260x_erm_cal_regs[] = {
>> +	{ DRV260X_MODE, DRV260X_AUTO_CAL },
>> +	{ DRV260X_A_TO_V_MIN_INPUT, DRV260X_AUDIO_HAPTICS_MIN_IN_VOLT },
>> +	{ DRV260X_A_TO_V_MAX_INPUT, DRV260X_AUDIO_HAPTICS_MAX_IN_VOLT },
>> +	{ DRV260X_A_TO_V_MIN_OUT, DRV260X_AUDIO_HAPTICS_MIN_OUT_VOLT },
>> +	{ DRV260X_A_TO_V_MAX_OUT, DRV260X_AUDIO_HAPTICS_MAX_OUT_VOLT },
>> +	{ DRV260X_FEEDBACK_CTRL, DRV260X_BRAKE_FACTOR_3X | DRV260X_LOOP_GAIN_MED | DRV260X_BEMF_GAIN_2 },
>> +	{ DRV260X_CTRL1, DRV260X_STARTUP_BOOST },
>> +	{ DRV260X_CTRL2, DRV260X_SAMP_TIME_250 | DRV260X_BLANK_TIME_75 | DRV260X_SAMP_TIME_250 | DRV260X_IDISS_TIME_75 },
>> +	{ DRV260X_CTRL3, DRV260X_NG_THRESH_2 | DRV260X_ERM_OPEN_LOOP },
>> +	{ DRV260X_CTRL4, DRV260X_AUTOCAL_TIME_500MS },
>> +};
>> +
>> +static int drv260x_init(struct drv260x_data *haptics)
>> +{
>> +	int ret;
>> +	unsigned int cal_buf;
>> +
>> +	ret = regmap_write(haptics->regmap,
>> +			   DRV260X_RATED_VOLT, haptics->rated_voltage);
>> +	if (ret != 0)
>> +		goto write_failure;
>> +
>> +	ret = regmap_write(haptics->regmap,
>> +			   DRV260X_OD_CLAMP_VOLT, haptics->overdrive_voltage);
>> +	if (ret != 0)
>> +		goto write_failure;
>> +
>> +	if (haptics->mode == DRV260X_LRA_MODE) {
>> +		ret = regmap_register_patch(haptics->regmap,
>> +					drv260x_lra_cal_regs,
>> +					ARRAY_SIZE(drv260x_lra_cal_regs));
>> +		if (ret != 0)
>> +			goto write_failure;
>> +
>> +	} else if (haptics->mode == DRV260X_ERM_MODE) {
>> +		ret = regmap_register_patch(haptics->regmap,
>> +					drv260x_erm_cal_regs,
>> +					ARRAY_SIZE(drv260x_erm_cal_regs));
>> +		if (ret != 0)
>> +			goto write_failure;
>> +
>> +		ret = regmap_update_bits(haptics->regmap, DRV260X_LIB_SEL,
>> +					DRV260X_LIB_SEL_MASK,
>> +					haptics->library);
>> +		if (ret != 0)
>> +			goto write_failure;
>> +
>> +	} else {
>> +		ret = regmap_register_patch(haptics->regmap,
>> +					drv260x_lra_init_regs,
>> +					ARRAY_SIZE(drv260x_lra_init_regs));
>> +		if (ret != 0)
>> +			goto write_failure;
>> +
>> +		ret = regmap_update_bits(haptics->regmap, DRV260X_LIB_SEL,
>> +					DRV260X_LIB_SEL_MASK,
>> +					haptics->library);
>> +		if (ret != 0)
>> +			goto write_failure;
>> +
>> +		goto skip_go_bit;
>> +	}
> 
> 	switch (haptics->mode) {
> 	case DRV260X_LRA_MODE:
> 		...
> 	}
> 
>> +
>> +	if (ret != 0) {
>> +		dev_err(&haptics->client->dev,
>> +			"Failed to write init registers: %d\n",
>> +			ret);
>> +		goto write_failure;
>> +	}
>> +
>> +	ret = regmap_write(haptics->regmap, DRV260X_GO, DRV260X_GO_BIT);
>> +	if (ret != 0)
>> +		goto write_failure;
>> +
>> +	do {
>> +		ret = regmap_read(haptics->regmap, DRV260X_GO, &cal_buf);
>> +		if (ret != 0)
>> +			goto write_failure;
>> +	} while (cal_buf == DRV260X_GO_BIT || ret != 0);
>> +
>> +	return ret;
>> +
>> +write_failure:
>> +	dev_err(&haptics->client->dev,
>> +		"Failed to write init registers: %d\n",
>> +		ret);
>> +skip_go_bit:
>> +	return ret;
>> +}
>> +
>> +static const struct regmap_config drv260x_regmap_config = {
>> +	.reg_bits = 8,
>> +	.val_bits = 8,
>> +
>> +	.max_register = DRV260X_MAX_REG,
>> +	.reg_defaults = drv260x_reg_defs,
>> +	.num_reg_defaults = ARRAY_SIZE(drv260x_reg_defs),
>> +	.cache_type = REGCACHE_NONE,
>> +};
>> +
>> +static int drv260x_probe(struct i2c_client *client,
>> +			   const struct i2c_device_id *id)
>> +{
>> +	struct drv260x_data *haptics;
>> +	struct device_node *np = client->dev.of_node;
>> +	struct drv260x_platform_data *pdata = client->dev.platform_data;
> 
> 	const struct drv260x_platform_data *pdata =
> 					dev_get_platdata(&client->dev);
> 
>> +	int ret;
>> +	int voltage;
>> +
>> +	haptics = devm_kzalloc(&client->dev, sizeof(*haptics), GFP_KERNEL);
>> +	if (!haptics)
>> +		return -ENOMEM;
>> +
>> +	haptics->rated_voltage = DRV260X_DEF_OD_CLAMP_VOLT;
>> +	haptics->rated_voltage = DRV260X_DEF_RATED_VOLT;
>> +
>> +	if (np) {
>> +		ret = of_property_read_u32(np, "mode", &haptics->mode);
>> +		if (ret < 0) {
>> +			dev_err(&client->dev,
>> +				"%s: No entry for mode\n", __func__);
>> +
>> +			return ret;
>> +		}
>> +		ret = of_property_read_u32(np, "library-sel",
>> +					&haptics->library);
>> +		if (ret < 0) {
>> +			dev_err(&client->dev,
>> +				"%s: No entry for library selection\n",
>> +				__func__);
>> +
>> +			return ret;
>> +		}
>> +		ret = of_property_read_u32(np, "vib-rated-voltage",
>> +					&voltage);
>> +		if (!ret)
>> +			haptics->rated_voltage = drv260x_calculate_voltage(voltage);
>> +
>> +
>> +		ret = of_property_read_u32(np, "vib-overdrive-voltage",
>> +					&voltage);
>> +		if (!ret)
>> +			haptics->overdrive_voltage = drv260x_calculate_voltage(voltage);
>> +
> 
> That should probably be split into a separate functionand guarded by
> CONFIG_OF.
> 
>> +	} else if (pdata) {
> 
> Platform data, if supplied, should take precedence.
> 
>> +		haptics->mode = pdata->mode;
>> +		haptics->library = pdata->library_selection;
>> +		if (pdata->vib_overdrive_voltage)
>> +			haptics->overdrive_voltage = drv260x_calculate_voltage(pdata->vib_overdrive_voltage);
>> +		if (pdata->vib_rated_voltage)
>> +			haptics->rated_voltage = drv260x_calculate_voltage(pdata->vib_rated_voltage);
>> +	} else {
>> +		dev_err(&client->dev, "Platform data not set\n");
>> +		return -ENODEV;
>> +	}
>> +
>> +
>> +	if (haptics->mode < DRV260X_LRA_MODE ||
>> +		haptics->mode > DRV260X_ERM_MODE) {
>> +			dev_err(&client->dev,
>> +				"Mode value is invalid: %i using default RTP mode\n",
>> +				haptics->mode);
>> +			return -EINVAL;
> 
> The message is misleading: it does not use the default mode, it aborts.

You are right the comment needs changing.

> 
>> +	}
>> +	
>> +	if (haptics->library < DRV260X_LIB_SEL_DEFAULT ||
>> +		haptics->library > DRV260X_LIB_F) {
>> +			dev_err(&client->dev,
>> +				"Library value is invalid: %i\n", haptics->library);
>> +			return -EINVAL;
>> +	}	
>> +
>> +	haptics->regulator = regulator_get(&client->dev, "vbat");
> 
> devm_regulator_get()
> 
>> +	if (IS_ERR(haptics->regulator)) {
>> +		ret = PTR_ERR(haptics->regulator);
>> +		dev_err(&client->dev,
>> +			"unable to get regulator, error: %d\n", ret);
>> +		goto err_regulator;
>> +	}
>> +
>> +	haptics->enable_gpio = devm_gpiod_get(&client->dev, "enable");
>> +	if (IS_ERR(haptics->enable_gpio)) {
>> +		ret = PTR_ERR(haptics->enable_gpio);
>> +		if (ret != -ENOENT && ret != -ENOSYS)
>> +			goto err_gpio;
>> +
>> +		haptics->enable_gpio = NULL;
>> +	} else {
>> +		gpiod_direction_output(haptics->enable_gpio, 1);
>> +	}
>> +
>> +	haptics->input_dev = devm_input_allocate_device(&client->dev);
>> +	if (haptics->input_dev == NULL) {
>> +		dev_err(&client->dev, "Failed to allocate input device\n");
>> +		ret = -ENOMEM;
>> +		goto err_input_alloc;
>> +	}
>> +
>> +	haptics->input_dev->name = "drv260x:haptics";
>> +	haptics->input_dev->dev.parent = client->dev.parent;
>> +	haptics->input_dev->close = drv260x_close;
>> +	input_set_drvdata(haptics->input_dev, haptics);
>> +	input_set_capability(haptics->input_dev, EV_FF, FF_RUMBLE);
>> +
>> +	ret = input_ff_create_memless(haptics->input_dev, NULL,
>> +				      drv260x_haptics_play);
>> +	if (ret < 0) {
>> +		dev_err(&client->dev, "input_ff_create() failed: %d\n",
>> +			ret);
>> +		goto err_ff_create;
>> +	}
>> +
>> +	INIT_WORK(&haptics->work, drv260x_worker);
>> +
>> +	haptics->client = client;
>> +	i2c_set_clientdata(client, haptics);
>> +
>> +	haptics->regmap = devm_regmap_init_i2c(client, &drv260x_regmap_config);
>> +	if (IS_ERR(haptics->regmap)) {
>> +		ret = PTR_ERR(haptics->regmap);
>> +		dev_err(&client->dev, "Failed to allocate register map: %d\n",
>> +			ret);
>> +		goto err_regmap;
>> +	}
>> +
>> +	drv260x_init(haptics);
>> +
>> +	ret = input_register_device(haptics->input_dev);
>> +	if (ret < 0) {
>> +		dev_err(&client->dev, "couldn't register input device: %d\n",
>> +			ret);
>> +		goto err_iff;
>> +	}
>> +	return 0;
>> +
>> +err_iff:
>> +err_regmap:
>> +	if (haptics->input_dev)
>> +		input_ff_destroy(haptics->input_dev);
> 
> Is not really needed with devm.
> 
>> +err_ff_create:
>> +err_input_alloc:
>> +err_gpio:
>> +	regulator_put(haptics->regulator);
>> +err_regulator:
>> +	return ret;
>> +}
>> +
>> +static int drv260x_remove(struct i2c_client *client)
>> +{
>> +	struct drv260x_data *haptics = i2c_get_clientdata(client);
>> +
>> +	cancel_work_sync(&haptics->work);
>> +
>> +	regmap_write(haptics->regmap, DRV260X_MODE, DRV260X_STANDBY);
>> +	gpiod_set_value(haptics->enable_gpio, 0);
> 
> This is already done in ->close() so not needed here.
> 
>> +
>> +	input_unregister_device(haptics->input_dev);
> 
> Not nedded with devm.
> 
>> +	regulator_put(haptics->regulator);
> 
> Should go away if you use devm_regulator_get().
> 
>> +
>> +	return 0;
>> +}
>> +
>> +#ifdef CONFIG_PM_SLEEP
>> +static int drv260x_suspend(struct device *dev)
>> +{
>> +	struct drv260x_data *haptics = dev_get_drvdata(dev);
>> +
>> +	regmap_update_bits(haptics->regmap,
>> +			   DRV260X_MODE,
>> +			   DRV260X_STANDBY_MASK,
>> +			   DRV260X_STANDBY);
>> +	gpiod_set_value(haptics->enable_gpio, 0);
>> +
>> +	regulator_disable(haptics->regulator);
>> +
>> +	return 0;
>> +}
>> +
>> +static int drv260x_resume(struct device *dev)
>> +{
>> +	struct drv260x_data *haptics = dev_get_drvdata(dev);
>> +	int ret;
>> +
>> +	ret = regulator_enable(haptics->regulator);
>> +	if (ret) {
>> +		dev_err(dev, "Failed to enable regulator\n");
>> +		return ret;
>> +	}
>> +	regmap_update_bits(haptics->regmap,
>> +			   DRV260X_MODE,
>> +			   DRV260X_STANDBY_MASK, 0);
>> +
>> +	gpiod_set_value(haptics->enable_gpio, 1);
> 
> Should take input->mutex and check input->users to not enable device if
> there are no users.
> 
>> +
>> +	return 0;
>> +}
>> +#endif
>> +
>> +static SIMPLE_DEV_PM_OPS(drv260x_pm_ops, drv260x_suspend, drv260x_resume);
>> +
>> +static const struct i2c_device_id drv260x_id[] = {
>> +	{ "drv2605l", 0 },
>> +	{ }
>> +};
>> +MODULE_DEVICE_TABLE(i2c, drv260x_id);
>> +
>> +#if IS_ENABLED(CONFIG_OF)
>> +static const struct of_device_id drv260x_of_match[] = {
>> +	{ .compatible = "ti,drv2604", },
>> +	{ .compatible = "ti,drv2605", },
>> +	{ .compatible = "ti,drv2605l", },
>> +	{}
>> +};
>> +MODULE_DEVICE_TABLE(of, drv260x_of_match);
>> +#endif
>> +
>> +static struct i2c_driver drv260x_driver = {
>> +	.probe		= drv260x_probe,
>> +	.remove		= drv260x_remove,
>> +	.driver		= {
>> +		.name	= "drv260x-haptics",
>> +		.owner	= THIS_MODULE,
>> +		.of_match_table = of_match_ptr(drv260x_of_match),
>> +		.pm	= &drv260x_pm_ops,
>> +	},
>> +	.id_table = drv260x_id,
>> +};
>> +module_i2c_driver(drv260x_driver);
>> +
>> +MODULE_ALIAS("platform:drv260x-haptics");
>> +MODULE_DESCRIPTION("TI DRV260x haptics driver");
>> +MODULE_LICENSE("GPL");
>> +MODULE_AUTHOR("Dan Murphy <dmurphy@ti.com>");
>> diff --git a/include/dt-bindings/input/ti-drv260x.h b/include/dt-bindings/input/ti-drv260x.h
>> new file mode 100644
>> index 0000000..3843b9a
>> --- /dev/null
>> +++ b/include/dt-bindings/input/ti-drv260x.h
>> @@ -0,0 +1,30 @@
>> +/*
>> + * ti-drv260x.h - DRV260X haptics driver family
> 
> Please no file name sin comments - if you rename you'll have to fix it
> here as well.

This is a TI standard that we put on all our TI files.  I can remove it
if you don't like it but if you look at other TI files this is how we
do it.

> 
>> + *
>> + * Author: Dan Murphy <dmurphy@ti.com>
>> + *
>> + * Copyright:   (C) 2014 Texas Instruments, Inc.
>> + *
>> + * This program is free software; you can redistribute it and/or modify
>> + * it under the terms of the GNU General Public License 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.
>> + */
>> +
>> +/* Calibration Types */
>> +#define DRV260X_LRA_MODE		0x00
>> +#define DRV260X_LRA_NO_CAL_MODE	0x01
>> +#define DRV260X_ERM_MODE		0x02
>> +
>> +/* Library Selection */
>> +#define DRV260X_LIB_SEL_DEFAULT		0x00
>> +#define DRV260X_LIB_A				0x01
>> +#define DRV260X_LIB_B				0x02
>> +#define DRV260X_LIB_C				0x03
>> +#define DRV260X_LIB_D				0x04
>> +#define DRV260X_LIB_E				0x05
>> +#define DRV260X_LIB_F				0x06
>> diff --git a/include/linux/input/drv260x.h b/include/linux/input/drv260x.h
>> new file mode 100644
>> index 0000000..709395e
>> --- /dev/null
>> +++ b/include/linux/input/drv260x.h
> 
> Wonder if it should go into platform data directory.
> 
>> @@ -0,0 +1,181 @@
>> +/*
>> + * drv260x.h - DRV260X haptics driver family
>> + *
>> + * Author: Dan Murphy <dmurphy@ti.com>
>> + *
>> + * Copyright:   (C) 2014 Texas Instruments, Inc.
>> + *
>> + * This program is free software; you can redistribute it and/or modify
>> + * it under the terms of the GNU General Public License 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.
>> + */
>> +
>> +#ifndef _LINUX_DRV260X_I2C_H
>> +#define _LINUX_DRV260X_I2C_H
>> +
>> +#define DRV260X_STATUS		0x0
>> +#define DRV260X_MODE		0x1
>> +#define DRV260X_RT_PB_IN	0x2
>> +#define DRV260X_LIB_SEL		0x3
>> +#define DRV260X_WV_SEQ_1	0x4
>> +#define DRV260X_WV_SEQ_2	0x5
>> +#define DRV260X_WV_SEQ_3	0x6
>> +#define DRV260X_WV_SEQ_4	0x7
>> +#define DRV260X_WV_SEQ_5	0x8
>> +#define DRV260X_WV_SEQ_6	0x9
>> +#define DRV260X_WV_SEQ_7	0xa
>> +#define DRV260X_WV_SEQ_8	0xb
>> +#define DRV260X_GO				0xc
>> +#define DRV260X_OVERDRIVE_OFF	0xd
>> +#define DRV260X_SUSTAIN_P_OFF	0xe
>> +#define DRV260X_SUSTAIN_N_OFF	0xf
>> +#define DRV260X_BRAKE_OFF		0x10
>> +#define DRV260X_A_TO_V_CTRL		0x11
>> +#define DRV260X_A_TO_V_MIN_INPUT	0x12
>> +#define DRV260X_A_TO_V_MAX_INPUT	0x13
>> +#define DRV260X_A_TO_V_MIN_OUT	0x14
>> +#define DRV260X_A_TO_V_MAX_OUT	0x15
>> +#define DRV260X_RATED_VOLT		0x16
>> +#define DRV260X_OD_CLAMP_VOLT	0x17
>> +#define DRV260X_CAL_COMP		0x18
>> +#define DRV260X_CAL_BACK_EMF	0x19
>> +#define DRV260X_FEEDBACK_CTRL	0x1a
>> +#define DRV260X_CTRL1			0x1b
>> +#define DRV260X_CTRL2			0x1c
>> +#define DRV260X_CTRL3			0x1d
>> +#define DRV260X_CTRL4			0x1e
>> +#define DRV260X_CTRL5			0x1f
>> +#define DRV260X_LRA_LOOP_PERIOD	0x20
>> +#define DRV260X_VBAT_MON		0x21
>> +#define DRV260X_LRA_RES_PERIOD	0x22
>> +#define DRV260X_MAX_REG			0x23
>> +
>> +#define DRV260X_ALLOWED_R_BYTES	25
>> +#define DRV260X_ALLOWED_W_BYTES	2
>> +#define DRV260X_MAX_RW_RETRIES	5
>> +#define DRV260X_I2C_RETRY_DELAY 10
>> +
>> +#define DRV260X_GO_BIT				0x01
>> +
>> +/* Library Selection */
>> +#define DRV260X_LIB_SEL_MASK		0x07
>> +#define DRV260X_LIB_SEL_RAM			0x0
>> +#define DRV260X_LIB_SEL_OD			0x1
>> +#define DRV260X_LIB_SEL_40_60		0x2
>> +#define DRV260X_LIB_SEL_60_80		0x3
>> +#define DRV260X_LIB_SEL_100_140		0x4
>> +#define DRV260X_LIB_SEL_140_PLUS	0x5
>> +
>> +#define DRV260X_LIB_SEL_HIZ_MASK	0x10
>> +#define DRV260X_LIB_SEL_HIZ_EN		0x01
>> +#define DRV260X_LIB_SEL_HIZ_DIS		0
>> +
>> +/* Mode register */
>> +#define DRV260X_STANDBY				(1 << 6)
>> +#define DRV260X_STANDBY_MASK		0x40
>> +#define DRV260X_INTERNAL_TRIGGER	0x00
>> +#define DRV260X_EXT_TRIGGER_EDGE	0x01
>> +#define DRV260X_EXT_TRIGGER_LEVEL	0x02
>> +#define DRV260X_PWM_ANALOG_IN		0x03
>> +#define DRV260X_AUDIOHAPTIC			0x04
>> +#define DRV260X_RT_PLAYBACK			0x05
>> +#define DRV260X_DIAGNOSTICS			0x06
>> +#define DRV260X_AUTO_CAL			0x07
>> +
>> +/* Audio to Haptics Control */
>> +#define DRV260X_AUDIO_HAPTICS_PEAK_10MS		(0 << 2)
>> +#define DRV260X_AUDIO_HAPTICS_PEAK_20MS		(1 << 2)
>> +#define DRV260X_AUDIO_HAPTICS_PEAK_30MS		(2 << 2)
>> +#define DRV260X_AUDIO_HAPTICS_PEAK_40MS		(3 << 2)
>> +
>> +#define DRV260X_AUDIO_HAPTICS_FILTER_100HZ	0x00
>> +#define DRV260X_AUDIO_HAPTICS_FILTER_125HZ	0x01
>> +#define DRV260X_AUDIO_HAPTICS_FILTER_150HZ	0x02
>> +#define DRV260X_AUDIO_HAPTICS_FILTER_200HZ	0x03
>> +
>> +/* Min/Max Input/Output Voltages */
>> +#define DRV260X_AUDIO_HAPTICS_MIN_IN_VOLT	0x19
>> +#define DRV260X_AUDIO_HAPTICS_MAX_IN_VOLT	0x64
>> +#define DRV260X_AUDIO_HAPTICS_MIN_OUT_VOLT	0x19
>> +#define DRV260X_AUDIO_HAPTICS_MAX_OUT_VOLT	0xFF
>> +
>> +/* Feedback register */
>> +#define DRV260X_FB_REG_ERM_MODE			0x7f
>> +#define DRV260X_FB_REG_LRA_MODE			(1 << 7)
>> +
>> +#define DRV260X_BRAKE_FACTOR_MASK	0x1f
>> +#define DRV260X_BRAKE_FACTOR_2X		(1 << 0)
>> +#define DRV260X_BRAKE_FACTOR_3X		(2 << 4)
>> +#define DRV260X_BRAKE_FACTOR_4X		(3 << 4)
>> +#define DRV260X_BRAKE_FACTOR_6X		(4 << 4)
>> +#define DRV260X_BRAKE_FACTOR_8X		(5 << 4)
>> +#define DRV260X_BRAKE_FACTOR_16		(6 << 4)
>> +#define DRV260X_BRAKE_FACTOR_DIS	(7 << 4)
>> +
>> +#define DRV260X_LOOP_GAIN_LOW		0xf3
>> +#define DRV260X_LOOP_GAIN_MED		(1 << 2)
>> +#define DRV260X_LOOP_GAIN_HIGH		(2 << 2)
>> +#define DRV260X_LOOP_GAIN_VERY_HIGH	(3 << 2)
>> +
>> +#define DRV260X_BEMF_GAIN_0			0xfc
>> +#define DRV260X_BEMF_GAIN_1		(1 << 0)
>> +#define DRV260X_BEMF_GAIN_2		(2 << 0)
>> +#define DRV260X_BEMF_GAIN_3		(3 << 0)
>> +
>> +/* Control 1 register */
>> +#define DRV260X_AC_CPLE_EN			(1 << 5)
>> +#define DRV260X_STARTUP_BOOST		(1 << 7)
>> +
>> +/* Control 2 register */
>> +
>> +#define DRV260X_IDISS_TIME_45		0
>> +#define DRV260X_IDISS_TIME_75		(1 << 0)
>> +#define DRV260X_IDISS_TIME_150		(1 << 1)
>> +#define DRV260X_IDISS_TIME_225		0x03
>> +
>> +#define DRV260X_BLANK_TIME_45	(0 << 2)
>> +#define DRV260X_BLANK_TIME_75	(1 << 2)
>> +#define DRV260X_BLANK_TIME_150	(2 << 2)
>> +#define DRV260X_BLANK_TIME_225	(3 << 2)
>> +
>> +#define DRV260X_SAMP_TIME_150	(0 << 4)
>> +#define DRV260X_SAMP_TIME_200	(1 << 4)
>> +#define DRV260X_SAMP_TIME_250	(2 << 4)
>> +#define DRV260X_SAMP_TIME_300	(3 << 4)
>> +
>> +#define DRV260X_BRAKE_STABILIZER	(1 << 6)
>> +#define DRV260X_UNIDIR_IN			(0 << 7)
>> +#define DRV260X_BIDIR_IN			(1 << 7)
>> +
>> +/* Control 3 Register */
>> +#define DRV260X_LRA_OPEN_LOOP		(1 << 0)
>> +#define DRV260X_ANANLOG_IN			(1 << 1)
>> +#define DRV260X_LRA_DRV_MODE		(1 << 2)
>> +#define DRV260X_RTP_UNSIGNED_DATA	(1 << 3)
>> +#define DRV260X_SUPPLY_COMP_DIS		(1 << 4)
>> +#define DRV260X_ERM_OPEN_LOOP		(1 << 5)
>> +#define DRV260X_NG_THRESH_0			(0 << 6)
>> +#define DRV260X_NG_THRESH_2			(1 << 6)
>> +#define DRV260X_NG_THRESH_4			(2 << 6)
>> +#define DRV260X_NG_THRESH_8			(3 << 6)
>> +
>> +/* Control 4 Register */
>> +#define DRV260X_AUTOCAL_TIME_150MS		(0 << 4)
>> +#define DRV260X_AUTOCAL_TIME_250MS		(1 << 4)
>> +#define DRV260X_AUTOCAL_TIME_500MS		(2 << 4)
>> +#define DRV260X_AUTOCAL_TIME_1000MS		(3 << 4)
>> +
>> +struct drv260x_platform_data {
>> +	int enable_gpio;
>> +	int library_selection;
>> +	int mode;
>> +	int vib_rated_voltage;
>> +	int vib_overdrive_voltage;
> 
> Should any of these be unsigned?

Yes they should be.

> 
>> +};
>> +
>> +#endif
>> -- 
>> 1.7.9.5
>>
> 
> Thanks.
> 

Dan

-- 
------------------
Dan Murphy

^ permalink raw reply

* Re: [PATCH v3 4/7] Input - wacom: Check for bluetooth protocol while setting OLEDs
From: Przemo Firszt @ 2014-07-29 22:53 UTC (permalink / raw)
  To: Benjamin Tissoires
  Cc: Dmitry Torokhov, Jiri Kosina, Ping Cheng, Jason Gerecke,
	linux-kernel, linux-input
In-Reply-To: <1406659385-3256-5-git-send-email-benjamin.tissoires@redhat.com>

Dnia 2014-07-29, wto o godzinie 14:43 -0400, Benjamin Tissoires pisze:
> Bluetooth Intuos 4 use 1-bit definition while the USB ones use a 4-bits
> definition. This changes the size of the raw image we receive, and thus
> the kernel will only accept 1-bit images for Bluetooth and 4-bits for
> USB.
> 
> Signed-off-by: Benjamin Tissoires <benjamin.tissoires@redhat.com>
> ---
>  drivers/hid/wacom_sys.c | 29 +++++++++++++++++++++--------
>  1 file changed, 21 insertions(+), 8 deletions(-)
> 
> diff --git a/drivers/hid/wacom_sys.c b/drivers/hid/wacom_sys.c
> index f5c9c56..a12cd9c 100644
> --- a/drivers/hid/wacom_sys.c
> +++ b/drivers/hid/wacom_sys.c
> @@ -20,6 +20,7 @@
>  #define WAC_CMD_LED_CONTROL	0x20
>  #define WAC_CMD_ICON_START	0x21
>  #define WAC_CMD_ICON_XFER	0x23
> +#define WAC_CMD_ICON_BT_XFER	0x26
>  #define WAC_CMD_RETRIES		10
>  
>  static int wacom_get_report(struct hid_device *hdev, u8 type, u8 id,
> @@ -526,12 +527,14 @@ static int wacom_led_control(struct wacom *wacom)
>  	return retval;
>  }
>  
> -static int wacom_led_putimage(struct wacom *wacom, int button_id, const void *img)
> +static int wacom_led_putimage(struct wacom *wacom, int button_id, u8 xfer_id,
> +		const unsigned len, const void *img)
>  {
>  	unsigned char *buf;
>  	int i, retval;
> +	const unsigned chunk_len = len / 4; /* 4 chunks are needed to be sent */
>  
> -	buf = kzalloc(259, GFP_KERNEL);
> +	buf = kzalloc(chunk_len + 3 , GFP_KERNEL);
>  	if (!buf)
>  		return -ENOMEM;
>  
> @@ -543,15 +546,15 @@ static int wacom_led_putimage(struct wacom *wacom, int button_id, const void *im
>  	if (retval < 0)
>  		goto out;
>  
> -	buf[0] = WAC_CMD_ICON_XFER;
> +	buf[0] = xfer_id;
>  	buf[1] = button_id & 0x07;
>  	for (i = 0; i < 4; i++) {
>  		buf[2] = i;
> -		memcpy(buf + 3, img + i * 256, 256);
> +		memcpy(buf + 3, img + i * chunk_len, chunk_len);
>  
>  		retval = wacom_set_report(wacom->hdev, HID_FEATURE_REPORT,
> -					  WAC_CMD_ICON_XFER,
> -					  buf, 259, WAC_CMD_RETRIES);
> +					  xfer_id, buf, chunk_len + 3,
> +					  WAC_CMD_RETRIES);
>  		if (retval < 0)
>  			break;
>  	}
> @@ -652,13 +655,23 @@ static ssize_t wacom_button_image_store(struct device *dev, int button_id,
>  	struct hid_device *hdev = container_of(dev, struct hid_device, dev);
>  	struct wacom *wacom = hid_get_drvdata(hdev);
>  	int err;
> +	unsigned len;
> +	u8 xfer_id;
>  
> -	if (count != 1024)
> +	if (hdev->bus == BUS_BLUETOOTH) {
> +		len = 256;
> +		xfer_id = WAC_CMD_ICON_BT_XFER;
> +	} else {
> +		len = 1024;
> +		xfer_id = WAC_CMD_ICON_XFER;
> +	}
> +
> +	if (count != len)
>  		return -EINVAL;
>  
>  	mutex_lock(&wacom->lock);
>  
> -	err = wacom_led_putimage(wacom, button_id, buf);
> +	err = wacom_led_putimage(wacom, button_id, xfer_id, len, buf);
>  
>  	mutex_unlock(&wacom->lock);

Signed-off-by: Przemo Firszt <przemo@firszt.eu>

I'll test the whole series tomorrow.
-- 
Kind regards,
Przemo Firszt

^ permalink raw reply

* [PATCH] hid: sony: Default initialize all elements of the LED max_brightness array to 1.
From: Frank Praznik @ 2014-07-30  2:55 UTC (permalink / raw)
  To: linux-input; +Cc: jkosina, Frank Praznik

Previously only the first element of the array was initialized to 1
leading to potential incorrect max brightness values for the LEDs
on the Dualshock 3 and buzzer controllers.

Use a designated initializer to initialize the whole array to the
correct value.

Signed-off-by: Frank Praznik <frank.praznik@oh.rr.com>
---

This patch is against jikos/hid.git/for-3.16/upstream-fixes
It applies cleanly against for-3.17/sony as well

This error never created any noticeable effects since the max value was
set to LED_FULL by the led initialization code if it was 0 and it was
clamped to 1 in the led setting code if the user tried to set a higher
value. This fix is needed in the interest of correctness though.

 drivers/hid/hid-sony.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/drivers/hid/hid-sony.c b/drivers/hid/hid-sony.c
index 2259eaa..e435223 100644
--- a/drivers/hid/hid-sony.c
+++ b/drivers/hid/hid-sony.c
@@ -1307,7 +1307,7 @@ static int sony_leds_init(struct sony_sc *sc)
 	static const char * const ds4_name_str[] = { "red", "green", "blue",
 						  "global" };
 	__u8 initial_values[MAX_LEDS] = { 0 };
-	__u8 max_brightness[MAX_LEDS] = { 1 };
+	__u8 max_brightness[MAX_LEDS] = { [0 ... (MAX_LEDS - 1)] = 1 };
 	__u8 use_hw_blink[MAX_LEDS] = { 0 };
 
 	BUG_ON(!(sc->quirks & SONY_LED_SUPPORT));
-- 
1.9.1


^ permalink raw reply related

* [PATCH] input: ads7846: Release resources on failure for clean exit
From: Pramod Gurav @ 2014-07-30  5:54 UTC (permalink / raw)
  To: linux-input, linux-kernel
  Cc: Pramod Gurav, Dmitry Torokhov, Lejun Zhu, Sachin Kamat

From: Pramod Gurav <pramod.gurav@smartplayin.com>

Input device must be released(input_free_device) when ads7846_probe_dt
fails. This fixes the same by releasing resources on failure.

CC: Dmitry Torokhov <dmitry.torokhov@gmail.com>
CC: Lejun Zhu <lejun.zhu@linux.intel.com>
CC: Sachin Kamat <sachin.kamat@linaro.org>

Signed-off-by: Pramod Gurav <pramod.gurav@smartplayin.com>
---
 drivers/input/touchscreen/ads7846.c |    6 ++++--
 1 file changed, 4 insertions(+), 2 deletions(-)

diff --git a/drivers/input/touchscreen/ads7846.c b/drivers/input/touchscreen/ads7846.c
index da201b8..e57ba52 100644
--- a/drivers/input/touchscreen/ads7846.c
+++ b/drivers/input/touchscreen/ads7846.c
@@ -1302,8 +1302,10 @@ static int ads7846_probe(struct spi_device *spi)
 	pdata = dev_get_platdata(&spi->dev);
 	if (!pdata) {
 		pdata = ads7846_probe_dt(&spi->dev);
-		if (IS_ERR(pdata))
-			return PTR_ERR(pdata);
+		if (IS_ERR(pdata)) {
+			err = PTR_ERR(pdata);
+			goto err_free_mem;
+		}
 	}
 
 	ts->model = pdata->model ? : 7846;
-- 
1.7.9.5


^ permalink raw reply related

* Re: [PATCH] soc_button_array: fix the issue that button device can't be enumerated since 3.16-rc1
From: Zhang Rui @ 2014-07-30  6:45 UTC (permalink / raw)
  To: Dmitry Torokhov; +Cc: Rafael J. Wysocki, yao.jin, linux-input
In-Reply-To: <20140709165732.GD10364@core.coreip.homeip.net>

Hi, Dmitry,

On Wed, 2014-07-09 at 09:57 -0700, Dmitry Torokhov wrote:
> On Tue, Jul 08, 2014 at 10:50:04PM +0200, Rafael J. Wysocki wrote:
> > On Wednesday, July 02, 2014 10:01:53 PM Zhang Rui wrote:
> > > From c2ee1886ba230d9d93d2ea2f350b1dc1a2d5ead5 Mon Sep 17 00:00:00 2001
> > > From: Jin Yao <yao.jin@linux.intel.com>
> > > Date: Thu, 26 Jun 2014 10:26:44 +0800
> > > Subject: [PATCH] soc_button_array: fix the issue that button device can't be
> > >  enumerated since 3.16-rc1
> > 
> > Hi Rui,
> > 
> > For 3.16 I'm afraid we need to add the missing device ID to the PNP list.
> > It is too late to do the conversion at this point IMO and we can do it later.
> 
> But for 3.17 this patch is the right way of doing things, right?

This is the patch we should use for 3.17.
Compared with the previous version, I just removed the soc_button_array id
from acpi pnp id list, which was added in 3.16-rc5, as an urgent fix.

Please review.

>From f09ff78ba75a9de0f6df333be6238a5b1bf36464 Mon Sep 17 00:00:00 2001
From: Jin Yao <yao.jin@linux.intel.com>
Date: Thu, 26 Jun 2014 10:26:44 +0800
Subject: [PATCH] convert soc_button_array driver to platform bus

ACPI device enumeration mechanism changed a lot since 3.16-rc1.
ACPI device objects with _HID will be enumerated to platform bus by default.
For the existing PNP drivers that probe the PNPACPI devices, the device ids
are listed explicitly in drivers/acpi/acpi_pnp.c.
But ACPI folks will continue their effort on shrinking this id list by
converting the PNP drivers to platform drivers, for the devices that don't
belong to PNP bus in nature.

convert soc_button_array driver from PNP bus to platform bus in this patch.

Signed-off-by: Jin Yao <yao.jin@intel.com>
Signed-off-by: Zhang Rui <rui.zhang@intel.com>
---
 drivers/acpi/acpi_pnp.c               |  2 --
 drivers/input/misc/soc_button_array.c | 60 ++++++++++++++++++-----------------
 2 files changed, 31 insertions(+), 31 deletions(-)

diff --git a/drivers/acpi/acpi_pnp.c b/drivers/acpi/acpi_pnp.c
index 4ddb0dc..6703c1f 100644
--- a/drivers/acpi/acpi_pnp.c
+++ b/drivers/acpi/acpi_pnp.c
@@ -14,8 +14,6 @@
 #include <linux/module.h>
 
 static const struct acpi_device_id acpi_pnp_device_ids[] = {
-	/* soc_button_array */
-	{"PNP0C40"},
 	/* pata_isapnp */
 	{"PNP0600"},		/* Generic ESDI/IDE/ATA compatible hard disk controller */
 	/* floppy */
diff --git a/drivers/input/misc/soc_button_array.c b/drivers/input/misc/soc_button_array.c
index 5a6334b..16ca162 100644
--- a/drivers/input/misc/soc_button_array.c
+++ b/drivers/input/misc/soc_button_array.c
@@ -18,7 +18,7 @@
 #include <linux/gpio/consumer.h>
 #include <linux/gpio_keys.h>
 #include <linux/platform_device.h>
-#include <linux/pnp.h>
+#include <linux/acpi.h>
 
 /*
  * Definition of buttons on the tablet. The ACPI index of each button
@@ -67,7 +67,7 @@ static int soc_button_lookup_gpio(struct device *dev, int acpi_index)
 }
 
 static struct platform_device *
-soc_button_device_create(struct pnp_dev *pdev,
+soc_button_device_create(struct platform_device *pdev,
 			 const struct soc_button_info *button_info,
 			 bool autorepeat)
 {
@@ -135,30 +135,40 @@ soc_button_device_create(struct pnp_dev *pdev,
 	return ERR_PTR(error);
 }
 
-static void soc_button_remove(struct pnp_dev *pdev)
+static int soc_button_remove(struct platform_device *pdev)
 {
-	struct soc_button_data *priv = pnp_get_drvdata(pdev);
+	struct soc_button_data *priv = platform_get_drvdata(pdev);
+
 	int i;
 
 	for (i = 0; i < BUTTON_TYPES; i++)
 		if (priv->children[i])
 			platform_device_unregister(priv->children[i]);
+
+	return 0;
 }
 
-static int soc_button_pnp_probe(struct pnp_dev *pdev,
-				const struct pnp_device_id *id)
+static int soc_button_probe(struct platform_device *pdev)
 {
-	const struct soc_button_info *button_info = (void *)id->driver_data;
+	struct device *dev = &pdev->dev;
+	const struct acpi_device_id *id;
+	struct soc_button_info *button_info;
 	struct soc_button_data *priv;
 	struct platform_device *pd;
 	int i;
 	int error;
 
+	id = acpi_match_device(dev->driver->acpi_match_table, dev);
+	if (!id)
+		return -ENODEV;
+
+	button_info = (struct soc_button_info *)id->driver_data;
+
 	priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
 	if (!priv)
 		return -ENOMEM;
 
-	pnp_set_drvdata(pdev, priv);
+	platform_set_drvdata(pdev, priv);
 
 	for (i = 0; i < BUTTON_TYPES; i++) {
 		pd = soc_button_device_create(pdev, button_info, i == 0);
@@ -189,30 +199,22 @@ static struct soc_button_info soc_button_PNP0C40[] = {
 	{ }
 };
 
-static const struct pnp_device_id soc_button_pnp_match[] = {
-	{ .id = "PNP0C40", .driver_data = (long)soc_button_PNP0C40 },
-	{ .id = "" }
+static const struct acpi_device_id soc_button_acpi_match[] = {
+	{ "PNP0C40", (unsigned long)soc_button_PNP0C40 },
+	{ }
 };
-MODULE_DEVICE_TABLE(pnp, soc_button_pnp_match);
 
-static struct pnp_driver soc_button_pnp_driver = {
-	.name		= KBUILD_MODNAME,
-	.id_table	= soc_button_pnp_match,
-	.probe          = soc_button_pnp_probe,
+MODULE_DEVICE_TABLE(acpi, soc_button_acpi_match);
+
+static struct platform_driver soc_button_driver = {
+	.probe          = soc_button_probe,
 	.remove		= soc_button_remove,
+	.driver		= {
+		.name = KBUILD_MODNAME,
+		.owner = THIS_MODULE,
+		.acpi_match_table = ACPI_PTR(soc_button_acpi_match),
+	},
 };
-
-static int __init soc_button_init(void)
-{
-	return pnp_register_driver(&soc_button_pnp_driver);
-}
-
-static void __exit soc_button_exit(void)
-{
-	pnp_unregister_driver(&soc_button_pnp_driver);
-}
-
-module_init(soc_button_init);
-module_exit(soc_button_exit);
+module_platform_driver(soc_button_driver);
 
 MODULE_LICENSE("GPL");
-- 
1.8.3.2




^ permalink raw reply related

* Re: [PATCH] hid: sony: Default initialize all elements of the LED max_brightness array to 1.
From: Jiri Kosina @ 2014-07-30  9:28 UTC (permalink / raw)
  To: Frank Praznik; +Cc: linux-input
In-Reply-To: <1406688948-33348-1-git-send-email-frank.praznik@oh.rr.com>

On Tue, 29 Jul 2014, Frank Praznik wrote:

> Previously only the first element of the array was initialized to 1
> leading to potential incorrect max brightness values for the LEDs
> on the Dualshock 3 and buzzer controllers.
> 
> Use a designated initializer to initialize the whole array to the
> correct value.
> 
> Signed-off-by: Frank Praznik <frank.praznik@oh.rr.com>
> ---
> 
> This patch is against jikos/hid.git/for-3.16/upstream-fixes
> It applies cleanly against for-3.17/sony as well

I have applied it to for-3.17/sony, as it doesn't seem to justify 3.16-rc 
pull request.

Thanks,

-- 
Jiri Kosina
SUSE Labs

^ permalink raw reply

* PATCH hid: Implement mode switching on Logitech gaming wheels accordingly to the documentation
From: Michal Malý @ 2014-07-30 10:10 UTC (permalink / raw)
  To: Jiri Kosina
  Cc: linux-input, linux-kernel@vger.kernel.org, edwin,
	elias.vds@gmail.com, simon@mungewell.org, Roland Bosa

Implement mode switching on Logitech gaming wheels accordingly to the documentation

Signed-off-by: Michal Malý <madcatxster@devoid-pointer.net>

---
 Logitech has recently released technical documentation which describes the
 protocol used by their force feedback gaming devices. The documentation
 describes the method by which the driver is supposed to recognize what
 model of the wheel is connected and switch it to so-called "native" mode.
 (https://opensource.logitech.com/opensource/index.php/Technical_Information)

 The patch implements this logic and provides an additional module parameter
 which can force the driver either not perform the switch at all or switch the
 wheel into an "extended compatibility" mode (not applicable for all wheels).
 If a wheel does not support the mode enforced by the parameter, it is left in
 its original mode. Default behavior is to switch all wheels into native mode.

 drivers/hid/hid-lg.c    |  17 +++-
 drivers/hid/hid-lg.h    |  11 ++-
 drivers/hid/hid-lg4ff.c | 224 +++++++++++++++++++++++++++++++++++-------------
 3 files changed, 188 insertions(+), 64 deletions(-)

diff --git a/drivers/hid/hid-lg.c b/drivers/hid/hid-lg.c
index a976f48..dc0f2f1 100644
--- a/drivers/hid/hid-lg.c
+++ b/drivers/hid/hid-lg.c
@@ -334,6 +334,16 @@ static __u8 momo2_rdesc_fixed[] = {
 };
 
 /*
+ * Certain Logitech wheels provide various compatibililty modes
+ * for games that cannot handle their advanced features properly.
+ * This switch forces the wheel into a specific compatibililty
+ * instead of its native mode
+ */
+#ifdef CONFIG_LOGIWHEELS_FF
+static int lg4ff_switch_force_mode;
+#endif
+
+/*
  * Certain Logitech keyboards send in report #3 keys which are far
  * above the logical maximum described in descriptor. This extends
  * the original value of 0x28c of logical maximum to 0x104d
@@ -717,7 +727,7 @@ static int lg_probe(struct hid_device *hdev, const struct hid_device_id *id)
 	if (drv_data->quirks & LG_FF3)
 		lg3ff_init(hdev);
 	if (drv_data->quirks & LG_FF4)
-		lg4ff_init(hdev);
+		lg4ff_init(hdev, lg4ff_switch_force_mode);
 
 	return 0;
 err_free:
@@ -818,4 +828,9 @@ static struct hid_driver lg_driver = {
 };
 module_hid_driver(lg_driver);
 
+#ifdef CONFIG_LOGIWHEELS_FF
+module_param_named(lg4ff_switch_force_mode, lg4ff_switch_force_mode, int, S_IRUGO);
+MODULE_PARM_DESC(lg4ff_switch_force_mode, "Force gaming wheel into specific compatibililty mode (only certain devices)");
+#endif
+
 MODULE_LICENSE("GPL");
diff --git a/drivers/hid/hid-lg.h b/drivers/hid/hid-lg.h
index 142ce3f..d070e479 100644
--- a/drivers/hid/hid-lg.h
+++ b/drivers/hid/hid-lg.h
@@ -25,14 +25,21 @@ static inline int lg3ff_init(struct hid_device *hdev) { return -1; }
 #endif
 
 #ifdef CONFIG_LOGIWHEELS_FF
+#define LG4FF_MSW_MIN 0
+#define LG4FF_MSW_NATIVE 0	/* Switch device to its native mode (if applicable) */
+#define LG4FF_MSW_DONTSWITCH 1	/* Leave device in its current mode */
+#define LG4FF_MSW_DFP 2		/* Switch device so that it emulates Driving Force Pro (only G25, G27, DFGT) */
+#define LG4FF_MSW_G25 3		/* Switch device so that it emulates G25 (only G27) */
+#define LG4FF_MSW_MAX 3
+
 int lg4ff_adjust_input_event(struct hid_device *hid, struct hid_field *field,
 			     struct hid_usage *usage, __s32 value, struct lg_drv_data *drv_data);
-int lg4ff_init(struct hid_device *hdev);
+int lg4ff_init(struct hid_device *hdev, const int switch_force_mode);
 int lg4ff_deinit(struct hid_device *hdev);
 #else
 static inline int lg4ff_adjust_input_event(struct hid_device *hid, struct hid_field *field,
 					   struct hid_usage *usage, __s32 value, struct lg_drv_data *drv_data) { return 0; }
-static inline int lg4ff_init(struct hid_device *hdev) { return -1; }
+static inline int lg4ff_init(struct hid_device *hdev, const int switch_force_mode) { return -1; }
 static inline int lg4ff_deinit(struct hid_device *hdev) { return -1; }
 #endif
 
diff --git a/drivers/hid/hid-lg4ff.c b/drivers/hid/hid-lg4ff.c
index cc2bd20..14692d9 100644
--- a/drivers/hid/hid-lg4ff.c
+++ b/drivers/hid/hid-lg4ff.c
@@ -32,21 +32,10 @@
 #include "hid-lg.h"
 #include "hid-ids.h"
 
-#define DFGT_REV_MAJ 0x13
-#define DFGT_REV_MIN 0x22
-#define DFGT2_REV_MIN 0x26
-#define DFP_REV_MAJ 0x11
-#define DFP_REV_MIN 0x06
-#define FFEX_REV_MAJ 0x21
-#define FFEX_REV_MIN 0x00
-#define G25_REV_MAJ 0x12
-#define G25_REV_MIN 0x22
-#define G27_REV_MAJ 0x12
-#define G27_REV_MIN 0x38
-#define G27_2_REV_MIN 0x39
-
 #define to_hid_device(pdev) container_of(pdev, struct hid_device, dev)
 
+#define LG4FF_FFEX_BCDDEVICE 0x2100
+
 static void hid_lg4ff_set_range_dfp(struct hid_device *hid, u16 range);
 static void hid_lg4ff_set_range_g25(struct hid_device *hid, u16 range);
 static ssize_t lg4ff_range_show(struct device *dev, struct device_attribute *attr, char *buf);
@@ -73,6 +62,26 @@ static const signed short lg4ff_wheel_effects[] = {
 	-1
 };
 
+struct lg4ff_mode_switch_cmd {
+	const __u8 cmd_count;	/* Number of commands to send */
+	const __u8 *cmd[];
+};
+
+struct lg4ff_emulated_wheel_mode {
+	const int tag;
+	const __u32 pid;
+	const struct lg4ff_mode_switch_cmd *cmd;
+};
+
+struct lg4ff_mode_switcher {
+	const u16 bcdDevice;
+	const u16 mask;
+	const __u32 native_pid;
+	const __u32 *nonnative_pids;
+	const struct lg4ff_mode_switch_cmd *native_cmds;
+	const struct lg4ff_emulated_wheel_mode **emulated_modes;
+};
+
 struct lg4ff_wheel {
 	const __u32 product_id;
 	const signed short *ff_effects;
@@ -92,46 +101,79 @@ static const struct lg4ff_wheel lg4ff_devices[] = {
 	{USB_DEVICE_ID_LOGITECH_WII_WHEEL,   lg4ff_wheel_effects, 40, 270, NULL}
 };
 
-struct lg4ff_native_cmd {
-	const __u8 cmd_num;	/* Number of commands to send */
-	const __u8 cmd[];
+
+static const u8 lg4ff_go_native_dfp_cmd[] = {0xf8, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00};
+static const u8 lg4ff_go_native_g25_cmd[] = {0xf8, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00};
+
+static const u8 lg4ff_no_compat_on_usb_reset_cmd[] = {0xf8, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00};
+
+static const u8 lg4ff_force_dfp_cmd[] = {0xf8, 0x09, 0x01, 0x01, 0x00, 0x00, 0x00};
+static const u8 lg4ff_force_g25_cmd[] = {0xf8, 0x09, 0x02, 0x01, 0x00, 0x00, 0x00};
+static const u8 lg4ff_force_dfgt_cmd[] = {0xf8, 0x09, 0x03, 0x01, 0x00, 0x00, 0x00};
+static const u8 lg4ff_force_g27_cmd[] = {0xf8, 0x09, 0x04, 0x01, 0x00, 0x00, 0x00};
+
+static const struct lg4ff_mode_switch_cmd lg4ff_switch_native_dfp_cmd = {
+	1,
+	{ lg4ff_go_native_dfp_cmd }
 };
 
-struct lg4ff_usb_revision {
-	const __u16 rev_maj;
-	const __u16 rev_min;
-	const struct lg4ff_native_cmd *command;
+static const struct lg4ff_mode_switch_cmd lg4ff_switch_native_dfgt_cmd = {
+	2,
+	{ lg4ff_no_compat_on_usb_reset_cmd,
+	  lg4ff_force_dfgt_cmd }
 };
 
-static const struct lg4ff_native_cmd native_dfp = {
+static const struct lg4ff_mode_switch_cmd lg4ff_switch_native_g25_cmd = {
 	1,
-	{0xf8, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00}
+	{ lg4ff_go_native_g25_cmd }
 };
 
-static const struct lg4ff_native_cmd native_dfgt = {
+static const struct lg4ff_mode_switch_cmd lg4ff_switch_native_g27_cmd = {
 	2,
-	{0xf8, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00,	/* 1st command */
-	 0xf8, 0x09, 0x03, 0x01, 0x00, 0x00, 0x00}	/* 2nd command */
+	{ lg4ff_no_compat_on_usb_reset_cmd,
+	  lg4ff_force_g27_cmd }
 };
 
-static const struct lg4ff_native_cmd native_g25 = {
+static const struct lg4ff_mode_switch_cmd lg4ff_switch_emulate_dfp_cmd = {
 	1,
-	{0xf8, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00}
+	{ lg4ff_force_dfp_cmd }
 };
 
-static const struct lg4ff_native_cmd native_g27 = {
-	2,
-	{0xf8, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00,	/* 1st command */
-	 0xf8, 0x09, 0x04, 0x01, 0x00, 0x00, 0x00}	/* 2nd command */
+static const struct lg4ff_mode_switch_cmd lg4ff_switch_emulate_g25_cmd = {
+	1,
+	{ lg4ff_force_g25_cmd }
+};
+
+static const struct lg4ff_emulated_wheel_mode lg4ff_emulated_dfp_mode = {
+	LG4FF_MSW_DFP,
+	USB_DEVICE_ID_LOGITECH_DFP_WHEEL,
+	&lg4ff_switch_emulate_dfp_cmd
 };
 
-static const struct lg4ff_usb_revision lg4ff_revs[] = {
-	{DFGT_REV_MAJ, DFGT_REV_MIN, &native_dfgt},	/* Driving Force GT */
-	{DFGT_REV_MAJ, DFGT2_REV_MIN, &native_dfgt},	/* Driving Force GT v2 */
-	{DFP_REV_MAJ,  DFP_REV_MIN,  &native_dfp},	/* Driving Force Pro */
-	{G25_REV_MAJ,  G25_REV_MIN,  &native_g25},	/* G25 */
-	{G27_REV_MAJ,  G27_REV_MIN,  &native_g27},	/* G27 */
-	{G27_REV_MAJ,  G27_2_REV_MIN,  &native_g27},	/* G27 v2 */
+static const struct lg4ff_emulated_wheel_mode lg4ff_emulated_g25_mode = {
+	LG4FF_MSW_G25,
+	USB_DEVICE_ID_LOGITECH_G25_WHEEL,
+	&lg4ff_switch_emulate_g25_cmd
+};
+
+static const __u32 lg4ff_nonnative_pids_ffex_dfp[] = { USB_DEVICE_ID_LOGITECH_WHEEL, USB_DEVICE_ID_LOGITECH_DFP_WHEEL, 0 };
+static const __u32 lg4ff_nonnative_pids_ffex_dfp_g25[] = { USB_DEVICE_ID_LOGITECH_WHEEL, USB_DEVICE_ID_LOGITECH_DFP_WHEEL,
+						    USB_DEVICE_ID_LOGITECH_G25_WHEEL, 0 };
+static const __u32 lg4ff_nonnative_pids_ffex[] = { USB_DEVICE_ID_LOGITECH_WHEEL, 0 };
+
+static const struct lg4ff_emulated_wheel_mode *lg4ff_emulated_modes_dfp[] = { &lg4ff_emulated_dfp_mode, NULL };
+static const struct lg4ff_emulated_wheel_mode *lg4ff_emulated_modes_dfp_g25[] = { &lg4ff_emulated_dfp_mode, &lg4ff_emulated_g25_mode, NULL };
+static const struct lg4ff_emulated_wheel_mode *lg4ff_emulated_modes_none[] = { NULL };
+
+static const struct lg4ff_mode_switcher lg4ff_mode_switchers[] = {
+	/* DFGT */
+	{0x1300, 0xff00, USB_DEVICE_ID_LOGITECH_DFGT_WHEEL, lg4ff_nonnative_pids_ffex_dfp, &lg4ff_switch_native_dfgt_cmd, lg4ff_emulated_modes_dfp},
+	/* G27 */
+	{0x1230, 0xfff0, USB_DEVICE_ID_LOGITECH_G27_WHEEL, lg4ff_nonnative_pids_ffex_dfp_g25, &lg4ff_switch_native_g27_cmd, lg4ff_emulated_modes_dfp_g25},
+	/* G25 */
+	{0x1200, 0xff00, USB_DEVICE_ID_LOGITECH_G25_WHEEL, lg4ff_nonnative_pids_ffex_dfp, &lg4ff_switch_native_g25_cmd, lg4ff_emulated_modes_dfp},
+	/* DFP */
+	{0x1000, 0xf000, USB_DEVICE_ID_LOGITECH_DFP_WHEEL, lg4ff_nonnative_pids_ffex, &lg4ff_switch_native_dfp_cmd, lg4ff_emulated_modes_none}
 };
 
 /* Recalculates X axis value accordingly to currently selected range */
@@ -400,19 +442,28 @@ static void hid_lg4ff_set_range_dfp(struct hid_device *hid, __u16 range)
 	hid_hw_request(hid, report, HID_REQ_SET_REPORT);
 }
 
-static void hid_lg4ff_switch_native(struct hid_device *hid, const struct lg4ff_native_cmd *cmd)
+static int lg4ff_switch_mode(struct hid_device *hid, const struct lg4ff_mode_switch_cmd *cmd)
 {
 	struct list_head *report_list = &hid->report_enum[HID_OUTPUT_REPORT].report_list;
 	struct hid_report *report = list_entry(report_list->next, struct hid_report, list);
-	__u8 i, j;
+	__s32 *value = report->field[0]->value;
+	int i;
+
+	for (i = 0; i < cmd->cmd_count; i++) {
+		const u8 *c = cmd->cmd[i];
 
-	j = 0;
-	while (j < 7*cmd->cmd_num) {
-		for (i = 0; i < 7; i++)
-			report->field[0]->value[i] = cmd->cmd[j++];
+		value[0] = c[0];
+		value[1] = c[1];
+		value[2] = c[2];
+		value[3] = c[3];
+		value[4] = c[4];
+		value[5] = c[5];
+		value[6] = c[6];
 
 		hid_hw_request(hid, report, HID_REQ_SET_REPORT);
 	}
+
+	return 0;
 }
 
 /* Read current range and display it in terminal */
@@ -556,7 +607,69 @@ static enum led_brightness lg4ff_led_get_brightness(struct led_classdev *led_cde
 }
 #endif
 
-int lg4ff_init(struct hid_device *hid)
+static int lg4ff_switch_ext_compatibility(struct hid_device *hid, const struct lg4ff_mode_switcher *s, const int switch_force_mode,
+					  const __u32 pid)
+{
+	int k = 0;
+	const struct lg4ff_emulated_wheel_mode *emul;
+
+	while ((emul = s->emulated_modes[k++]) != NULL) {
+		if (emul->tag == switch_force_mode) {
+			if (pid != emul->pid) {
+				dbg_hid("Switching device to extended compatibility mode\n");
+				return lg4ff_switch_mode(hid, emul->cmd);
+			}
+			dbg_hid("Device already is in requested extended compatibility mode\n");
+			return 0;
+		}
+	}
+	dbg_hid("This device does not support the enforced compatibility mode, leaving in FFEX mode\n");
+	return 0;
+}
+
+static int lg4ff_try_mode_switch(struct hid_device *hid, const u16 bcdDevice, int switch_force_mode)
+{
+	const __u32 pid = hid->product;
+	int i;
+
+	if (switch_force_mode < LG4FF_MSW_MIN || switch_force_mode > LG4FF_MSW_MAX)
+		switch_force_mode = LG4FF_MSW_NATIVE;
+	if (switch_force_mode == LG4FF_MSW_DONTSWITCH) {
+		dbg_hid("Leaving device as it is\n");
+		return 0;
+	}
+
+	for (i = 0; i < ARRAY_SIZE(lg4ff_mode_switchers); i++) {
+		const struct lg4ff_mode_switcher *s = &lg4ff_mode_switchers[i];
+		int j = 0;
+		__u32 nonnative_pid;
+
+		if (s->bcdDevice != (bcdDevice & s->mask))
+			continue;
+
+		if (pid == s->native_pid) {
+			if (switch_force_mode != LG4FF_MSW_NATIVE)
+				return lg4ff_switch_ext_compatibility(hid, s, switch_force_mode, pid);
+			dbg_hid("Device already is in its native mode\n");
+			return 0;
+		}
+
+		/* Check into which mode we want to switch the device to */
+		while ((nonnative_pid = s->nonnative_pids[j++]) != 0) {
+			if (pid == nonnative_pid) {
+				if (switch_force_mode == LG4FF_MSW_NATIVE) {
+					dbg_hid("Switching device to native mode\n");
+					return lg4ff_switch_mode(hid, s->native_cmds);
+				}
+				return lg4ff_switch_ext_compatibility(hid, s, switch_force_mode, pid);
+			}
+		}
+	}
+
+	return 0;
+}
+
+int lg4ff_init(struct hid_device *hid, const int switch_force_mode)
 {
 	struct hid_input *hidinput = list_entry(hid->inputs.next, struct hid_input, list);
 	struct input_dev *dev = hidinput->input;
@@ -564,7 +677,7 @@ int lg4ff_init(struct hid_device *hid)
 	struct lg_drv_data *drv_data;
 	struct usb_device_descriptor *udesc;
 	int error, i, j;
-	__u16 bcdDevice, rev_maj, rev_min;
+	u16 bcdDevice;
 
 	/* Check that the report looks ok */
 	if (!hid_validate_values(hid, HID_OUTPUT_REPORT, 0, 0, 7))
@@ -591,20 +704,9 @@ int lg4ff_init(struct hid_device *hid)
 		return -1;
 	}
 	bcdDevice = le16_to_cpu(udesc->bcdDevice);
-	rev_maj = bcdDevice >> 8;
-	rev_min = bcdDevice & 0xff;
-
-	if (lg4ff_devices[i].product_id == USB_DEVICE_ID_LOGITECH_WHEEL) {
-		dbg_hid("Generic wheel detected, can it do native?\n");
-		dbg_hid("USB revision: %2x.%02x\n", rev_maj, rev_min);
-
-		for (j = 0; j < ARRAY_SIZE(lg4ff_revs); j++) {
-			if (lg4ff_revs[j].rev_maj == rev_maj && lg4ff_revs[j].rev_min == rev_min) {
-				hid_lg4ff_switch_native(hid, lg4ff_revs[j].command);
-				hid_info(hid, "Switched to native mode\n");
-			}
-		}
-	}
+	error = lg4ff_try_mode_switch(hid, bcdDevice, switch_force_mode);
+	if (error)
+		return error;
 
 	/* Set supported force feedback capabilities */
 	for (j = 0; lg4ff_devices[i].ff_effects[j] >= 0; j++)
@@ -638,7 +740,7 @@ int lg4ff_init(struct hid_device *hid)
 	/* Check if autocentering is available and
 	 * set the centering force to zero by default */
 	if (test_bit(FF_AUTOCENTER, dev->ffbit)) {
-		if (rev_maj == FFEX_REV_MAJ && rev_min == FFEX_REV_MIN)	/* Formula Force EX expects different autocentering command */
+		if (bcdDevice == LG4FF_FFEX_BCDDEVICE)	/* Formula Force EX does not seem to support hi-res autocentering */
 			dev->ff->set_autocenter = hid_lg4ff_set_autocenter_ffex;
 		else
 			dev->ff->set_autocenter = hid_lg4ff_set_autocenter_default;
-- 
2.0.2


--
To unsubscribe from this list: send the line "unsubscribe linux-input" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply related

* [PATCH] Input: jornada720_ts: Switch to using Managed resources
From: Pramod Gurav @ 2014-07-30 12:09 UTC (permalink / raw)
  To: linux-input, linux-kernel; +Cc: Pramod Gurav, Dmitry Torokhov, Paul Gortmaker

This switches the driver to using managed resources to simplify
error handling and to do away with remove function.

Also fixes some indentations by replacing spaces with tabs.

CC: Dmitry Torokhov <dmitry.torokhov@gmail.com>
CC: Paul Gortmaker <paul.gortmaker@windriver.com>

Signed-off-by: Pramod Gurav <pramod.gurav@smartplayin.com>
---
 drivers/input/touchscreen/jornada720_ts.c |   62 ++++++++++-------------------
 1 file changed, 20 insertions(+), 42 deletions(-)

diff --git a/drivers/input/touchscreen/jornada720_ts.c b/drivers/input/touchscreen/jornada720_ts.c
index 7324c5c..0c23bba 100644
--- a/drivers/input/touchscreen/jornada720_ts.c
+++ b/drivers/input/touchscreen/jornada720_ts.c
@@ -37,21 +37,21 @@ struct jornada_ts {
 static void jornada720_ts_collect_data(struct jornada_ts *jornada_ts)
 {
 
-    /* 3 low word X samples */
-    jornada_ts->x_data[0] = jornada_ssp_byte(TXDUMMY);
-    jornada_ts->x_data[1] = jornada_ssp_byte(TXDUMMY);
-    jornada_ts->x_data[2] = jornada_ssp_byte(TXDUMMY);
+	/* 3 low word X samples */
+	jornada_ts->x_data[0] = jornada_ssp_byte(TXDUMMY);
+	jornada_ts->x_data[1] = jornada_ssp_byte(TXDUMMY);
+	jornada_ts->x_data[2] = jornada_ssp_byte(TXDUMMY);
 
-    /* 3 low word Y samples */
-    jornada_ts->y_data[0] = jornada_ssp_byte(TXDUMMY);
-    jornada_ts->y_data[1] = jornada_ssp_byte(TXDUMMY);
-    jornada_ts->y_data[2] = jornada_ssp_byte(TXDUMMY);
+	/* 3 low word Y samples */
+	jornada_ts->y_data[0] = jornada_ssp_byte(TXDUMMY);
+	jornada_ts->y_data[1] = jornada_ssp_byte(TXDUMMY);
+	jornada_ts->y_data[2] = jornada_ssp_byte(TXDUMMY);
 
-    /* combined x samples bits */
-    jornada_ts->x_data[3] = jornada_ssp_byte(TXDUMMY);
+	/* combined x samples bits */
+	jornada_ts->x_data[3] = jornada_ssp_byte(TXDUMMY);
 
-    /* combined y samples bits */
-    jornada_ts->y_data[3] = jornada_ssp_byte(TXDUMMY);
+	/* combined y samples bits */
+	jornada_ts->y_data[3] = jornada_ssp_byte(TXDUMMY);
 }
 
 static int jornada720_ts_average(int coords[4])
@@ -104,13 +104,10 @@ static int jornada720_ts_probe(struct platform_device *pdev)
 	struct input_dev *input_dev;
 	int error;
 
-	jornada_ts = kzalloc(sizeof(struct jornada_ts), GFP_KERNEL);
-	input_dev = input_allocate_device();
-
-	if (!jornada_ts || !input_dev) {
-		error = -ENOMEM;
-		goto fail1;
-	}
+	jornada_ts = devm_kzalloc(&pdev->dev, sizeof(*jornada_ts), GFP_KERNEL);
+	input_dev = devm_input_allocate_device(&pdev->dev);
+	if (!jornada_ts || !input_dev)
+		return -ENOMEM;
 
 	platform_set_drvdata(pdev, jornada_ts);
 
@@ -126,36 +123,18 @@ static int jornada720_ts_probe(struct platform_device *pdev)
 	input_set_abs_params(input_dev, ABS_X, 270, 3900, 0, 0);
 	input_set_abs_params(input_dev, ABS_Y, 180, 3700, 0, 0);
 
-	error = request_irq(IRQ_GPIO9,
+	error = devm_request_irq(&pdev->dev, IRQ_GPIO9,
 			jornada720_ts_interrupt,
 			IRQF_TRIGGER_RISING,
 			"HP7XX Touchscreen driver", pdev);
 	if (error) {
-		printk(KERN_INFO "HP7XX TS : Unable to acquire irq!\n");
-		goto fail1;
+		dev_err(&pdev->dev, "HP7XX TS : Unable to acquire irq!\n");
+		return error;
 	}
 
 	error = input_register_device(jornada_ts->dev);
 	if (error)
-		goto fail2;
-
-	return 0;
-
- fail2:
-	free_irq(IRQ_GPIO9, pdev);
- fail1:
-	input_free_device(input_dev);
-	kfree(jornada_ts);
-	return error;
-}
-
-static int jornada720_ts_remove(struct platform_device *pdev)
-{
-	struct jornada_ts *jornada_ts = platform_get_drvdata(pdev);
-
-	free_irq(IRQ_GPIO9, pdev);
-	input_unregister_device(jornada_ts->dev);
-	kfree(jornada_ts);
+		return error;
 
 	return 0;
 }
@@ -165,7 +144,6 @@ MODULE_ALIAS("platform:jornada_ts");
 
 static struct platform_driver jornada720_ts_driver = {
 	.probe		= jornada720_ts_probe,
-	.remove		= jornada720_ts_remove,
 	.driver		= {
 		.name	= "jornada_ts",
 		.owner	= THIS_MODULE,
-- 
1.7.9.5

^ permalink raw reply related

* Re: [PATCH] Input: jornada720_ts: Switch to using Managed resources
From: Paul Gortmaker @ 2014-07-30 13:18 UTC (permalink / raw)
  To: Pramod Gurav, linux-input, linux-kernel; +Cc: Dmitry Torokhov
In-Reply-To: <1406722183-22678-1-git-send-email-pramod.gurav@smartplayin.com>

On 14-07-30 08:09 AM, Pramod Gurav wrote:
> This switches the driver to using managed resources to simplify
> error handling and to do away with remove function.
> 
> Also fixes some indentations by replacing spaces with tabs.
> 
> CC: Dmitry Torokhov <dmitry.torokhov@gmail.com>
> CC: Paul Gortmaker <paul.gortmaker@windriver.com>
> 
> Signed-off-by: Pramod Gurav <pramod.gurav@smartplayin.com>
> ---
>  drivers/input/touchscreen/jornada720_ts.c |   62 ++++++++++-------------------
>  1 file changed, 20 insertions(+), 42 deletions(-)
> 
> diff --git a/drivers/input/touchscreen/jornada720_ts.c b/drivers/input/touchscreen/jornada720_ts.c
> index 7324c5c..0c23bba 100644
> --- a/drivers/input/touchscreen/jornada720_ts.c
> +++ b/drivers/input/touchscreen/jornada720_ts.c
> @@ -37,21 +37,21 @@ struct jornada_ts {
>  static void jornada720_ts_collect_data(struct jornada_ts *jornada_ts)
>  {
>  
> -    /* 3 low word X samples */
> -    jornada_ts->x_data[0] = jornada_ssp_byte(TXDUMMY);
> -    jornada_ts->x_data[1] = jornada_ssp_byte(TXDUMMY);
> -    jornada_ts->x_data[2] = jornada_ssp_byte(TXDUMMY);
> +	/* 3 low word X samples */
> +	jornada_ts->x_data[0] = jornada_ssp_byte(TXDUMMY);
> +	jornada_ts->x_data[1] = jornada_ssp_byte(TXDUMMY);
> +	jornada_ts->x_data[2] = jornada_ssp_byte(TXDUMMY);

Please don't mix pure whitespace changes with technical changes.
While I can't fault you for wanting to get rid of the 4 space
tabs, it makes review more complicated when they are mixed in
a single commit.

P.
--

>  
> -    /* 3 low word Y samples */
> -    jornada_ts->y_data[0] = jornada_ssp_byte(TXDUMMY);
> -    jornada_ts->y_data[1] = jornada_ssp_byte(TXDUMMY);
> -    jornada_ts->y_data[2] = jornada_ssp_byte(TXDUMMY);
> +	/* 3 low word Y samples */
> +	jornada_ts->y_data[0] = jornada_ssp_byte(TXDUMMY);
> +	jornada_ts->y_data[1] = jornada_ssp_byte(TXDUMMY);
> +	jornada_ts->y_data[2] = jornada_ssp_byte(TXDUMMY);
>  
> -    /* combined x samples bits */
> -    jornada_ts->x_data[3] = jornada_ssp_byte(TXDUMMY);
> +	/* combined x samples bits */
> +	jornada_ts->x_data[3] = jornada_ssp_byte(TXDUMMY);
>  
> -    /* combined y samples bits */
> -    jornada_ts->y_data[3] = jornada_ssp_byte(TXDUMMY);
> +	/* combined y samples bits */
> +	jornada_ts->y_data[3] = jornada_ssp_byte(TXDUMMY);
>  }
>  
>  static int jornada720_ts_average(int coords[4])
> @@ -104,13 +104,10 @@ static int jornada720_ts_probe(struct platform_device *pdev)
>  	struct input_dev *input_dev;
>  	int error;
>  
> -	jornada_ts = kzalloc(sizeof(struct jornada_ts), GFP_KERNEL);
> -	input_dev = input_allocate_device();
> -
> -	if (!jornada_ts || !input_dev) {
> -		error = -ENOMEM;
> -		goto fail1;
> -	}
> +	jornada_ts = devm_kzalloc(&pdev->dev, sizeof(*jornada_ts), GFP_KERNEL);
> +	input_dev = devm_input_allocate_device(&pdev->dev);
> +	if (!jornada_ts || !input_dev)
> +		return -ENOMEM;
>  
>  	platform_set_drvdata(pdev, jornada_ts);
>  
> @@ -126,36 +123,18 @@ static int jornada720_ts_probe(struct platform_device *pdev)
>  	input_set_abs_params(input_dev, ABS_X, 270, 3900, 0, 0);
>  	input_set_abs_params(input_dev, ABS_Y, 180, 3700, 0, 0);
>  
> -	error = request_irq(IRQ_GPIO9,
> +	error = devm_request_irq(&pdev->dev, IRQ_GPIO9,
>  			jornada720_ts_interrupt,
>  			IRQF_TRIGGER_RISING,
>  			"HP7XX Touchscreen driver", pdev);
>  	if (error) {
> -		printk(KERN_INFO "HP7XX TS : Unable to acquire irq!\n");
> -		goto fail1;
> +		dev_err(&pdev->dev, "HP7XX TS : Unable to acquire irq!\n");
> +		return error;
>  	}
>  
>  	error = input_register_device(jornada_ts->dev);
>  	if (error)
> -		goto fail2;
> -
> -	return 0;
> -
> - fail2:
> -	free_irq(IRQ_GPIO9, pdev);
> - fail1:
> -	input_free_device(input_dev);
> -	kfree(jornada_ts);
> -	return error;
> -}
> -
> -static int jornada720_ts_remove(struct platform_device *pdev)
> -{
> -	struct jornada_ts *jornada_ts = platform_get_drvdata(pdev);
> -
> -	free_irq(IRQ_GPIO9, pdev);
> -	input_unregister_device(jornada_ts->dev);
> -	kfree(jornada_ts);
> +		return error;
>  
>  	return 0;
>  }
> @@ -165,7 +144,6 @@ MODULE_ALIAS("platform:jornada_ts");
>  
>  static struct platform_driver jornada720_ts_driver = {
>  	.probe		= jornada720_ts_probe,
> -	.remove		= jornada720_ts_remove,
>  	.driver		= {
>  		.name	= "jornada_ts",
>  		.owner	= THIS_MODULE,
> 

^ permalink raw reply

* Re: [PATCH] Input: jornada720_ts: Switch to using Managed resources
From: Pramod Gurav @ 2014-07-30 13:28 UTC (permalink / raw)
  To: Paul Gortmaker
  Cc: Pramod Gurav, linux-input, linux-kernel@vger.kernel.org,
	Dmitry Torokhov
In-Reply-To: <53D8F0A1.3000800@windriver.com>

On Wed, Jul 30, 2014 at 6:48 PM, Paul Gortmaker
<paul.gortmaker@windriver.com> wrote:
> On 14-07-30 08:09 AM, Pramod Gurav wrote:
>> This switches the driver to using managed resources to simplify
>> error handling and to do away with remove function.
>>
>> Also fixes some indentations by replacing spaces with tabs.
>>
>> CC: Dmitry Torokhov <dmitry.torokhov@gmail.com>
>> CC: Paul Gortmaker <paul.gortmaker@windriver.com>
>>
>> Signed-off-by: Pramod Gurav <pramod.gurav@smartplayin.com>
>> ---
>>  drivers/input/touchscreen/jornada720_ts.c |   62 ++++++++++-------------------
>>  1 file changed, 20 insertions(+), 42 deletions(-)
>>
>> diff --git a/drivers/input/touchscreen/jornada720_ts.c b/drivers/input/touchscreen/jornada720_ts.c
>> index 7324c5c..0c23bba 100644
>> --- a/drivers/input/touchscreen/jornada720_ts.c
>> +++ b/drivers/input/touchscreen/jornada720_ts.c
>> @@ -37,21 +37,21 @@ struct jornada_ts {
>>  static void jornada720_ts_collect_data(struct jornada_ts *jornada_ts)
>>  {
>>
>> -    /* 3 low word X samples */
>> -    jornada_ts->x_data[0] = jornada_ssp_byte(TXDUMMY);
>> -    jornada_ts->x_data[1] = jornada_ssp_byte(TXDUMMY);
>> -    jornada_ts->x_data[2] = jornada_ssp_byte(TXDUMMY);
>> +     /* 3 low word X samples */
>> +     jornada_ts->x_data[0] = jornada_ssp_byte(TXDUMMY);
>> +     jornada_ts->x_data[1] = jornada_ssp_byte(TXDUMMY);
>> +     jornada_ts->x_data[2] = jornada_ssp_byte(TXDUMMY);
>
> Please don't mix pure whitespace changes with technical changes.
> While I can't fault you for wanting to get rid of the 4 space
> tabs, it makes review more complicated when they are mixed in
> a single commit.

Thanks for highlighting that. Will send two patches then.

>
> P.
> --
>
>>
>> -    /* 3 low word Y samples */
>> -    jornada_ts->y_data[0] = jornada_ssp_byte(TXDUMMY);
>> -    jornada_ts->y_data[1] = jornada_ssp_byte(TXDUMMY);
>> -    jornada_ts->y_data[2] = jornada_ssp_byte(TXDUMMY);
>> +     /* 3 low word Y samples */
>> +     jornada_ts->y_data[0] = jornada_ssp_byte(TXDUMMY);
>> +     jornada_ts->y_data[1] = jornada_ssp_byte(TXDUMMY);
>> +     jornada_ts->y_data[2] = jornada_ssp_byte(TXDUMMY);
>>
>> -    /* combined x samples bits */
>> -    jornada_ts->x_data[3] = jornada_ssp_byte(TXDUMMY);
>> +     /* combined x samples bits */
>> +     jornada_ts->x_data[3] = jornada_ssp_byte(TXDUMMY);
>>
>> -    /* combined y samples bits */
>> -    jornada_ts->y_data[3] = jornada_ssp_byte(TXDUMMY);
>> +     /* combined y samples bits */
>> +     jornada_ts->y_data[3] = jornada_ssp_byte(TXDUMMY);
>>  }
>>
>>  static int jornada720_ts_average(int coords[4])
>> @@ -104,13 +104,10 @@ static int jornada720_ts_probe(struct platform_device *pdev)
>>       struct input_dev *input_dev;
>>       int error;
>>
>> -     jornada_ts = kzalloc(sizeof(struct jornada_ts), GFP_KERNEL);
>> -     input_dev = input_allocate_device();
>> -
>> -     if (!jornada_ts || !input_dev) {
>> -             error = -ENOMEM;
>> -             goto fail1;
>> -     }
>> +     jornada_ts = devm_kzalloc(&pdev->dev, sizeof(*jornada_ts), GFP_KERNEL);
>> +     input_dev = devm_input_allocate_device(&pdev->dev);
>> +     if (!jornada_ts || !input_dev)
>> +             return -ENOMEM;
>>
>>       platform_set_drvdata(pdev, jornada_ts);
>>
>> @@ -126,36 +123,18 @@ static int jornada720_ts_probe(struct platform_device *pdev)
>>       input_set_abs_params(input_dev, ABS_X, 270, 3900, 0, 0);
>>       input_set_abs_params(input_dev, ABS_Y, 180, 3700, 0, 0);
>>
>> -     error = request_irq(IRQ_GPIO9,
>> +     error = devm_request_irq(&pdev->dev, IRQ_GPIO9,
>>                       jornada720_ts_interrupt,
>>                       IRQF_TRIGGER_RISING,
>>                       "HP7XX Touchscreen driver", pdev);
>>       if (error) {
>> -             printk(KERN_INFO "HP7XX TS : Unable to acquire irq!\n");
>> -             goto fail1;
>> +             dev_err(&pdev->dev, "HP7XX TS : Unable to acquire irq!\n");
>> +             return error;
>>       }
>>
>>       error = input_register_device(jornada_ts->dev);
>>       if (error)
>> -             goto fail2;
>> -
>> -     return 0;
>> -
>> - fail2:
>> -     free_irq(IRQ_GPIO9, pdev);
>> - fail1:
>> -     input_free_device(input_dev);
>> -     kfree(jornada_ts);
>> -     return error;
>> -}
>> -
>> -static int jornada720_ts_remove(struct platform_device *pdev)
>> -{
>> -     struct jornada_ts *jornada_ts = platform_get_drvdata(pdev);
>> -
>> -     free_irq(IRQ_GPIO9, pdev);
>> -     input_unregister_device(jornada_ts->dev);
>> -     kfree(jornada_ts);
>> +             return error;
>>
>>       return 0;
>>  }
>> @@ -165,7 +144,6 @@ MODULE_ALIAS("platform:jornada_ts");
>>
>>  static struct platform_driver jornada720_ts_driver = {
>>       .probe          = jornada720_ts_probe,
>> -     .remove         = jornada720_ts_remove,
>>       .driver         = {
>>               .name   = "jornada_ts",
>>               .owner  = THIS_MODULE,
>>
> --
> To unsubscribe from this list: send the line "unsubscribe linux-input" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html



-- 
Thanks and Regards
Pramod

^ permalink raw reply

* [PATCH 1/2] Input: jornada720_ts: Switch to using Managed resources
From: Pramod Gurav @ 2014-07-30 13:48 UTC (permalink / raw)
  To: linux-input, linux-kernel; +Cc: Pramod Gurav, Dmitry Torokhov, Paul Gortmaker

This switches the driver to using managed resources to simplify
error handling and to do away with remove function.

CC: Dmitry Torokhov <dmitry.torokhov@gmail.com>
CC: Paul Gortmaker <paul.gortmaker@windriver.com>

Signed-off-by: Pramod Gurav <pramod.gurav@smartplayin.com>
---
 drivers/input/touchscreen/jornada720_ts.c |   38 ++++++-----------------------
 1 file changed, 8 insertions(+), 30 deletions(-)

diff --git a/drivers/input/touchscreen/jornada720_ts.c b/drivers/input/touchscreen/jornada720_ts.c
index 7324c5c..7422480 100644
--- a/drivers/input/touchscreen/jornada720_ts.c
+++ b/drivers/input/touchscreen/jornada720_ts.c
@@ -104,13 +104,10 @@ static int jornada720_ts_probe(struct platform_device *pdev)
 	struct input_dev *input_dev;
 	int error;
 
-	jornada_ts = kzalloc(sizeof(struct jornada_ts), GFP_KERNEL);
-	input_dev = input_allocate_device();
-
-	if (!jornada_ts || !input_dev) {
-		error = -ENOMEM;
-		goto fail1;
-	}
+	jornada_ts = devm_kzalloc(&pdev->dev, sizeof(*jornada_ts), GFP_KERNEL);
+	input_dev = devm_input_allocate_device(&pdev->dev);
+	if (!jornada_ts || !input_dev)
+		return -ENOMEM;
 
 	platform_set_drvdata(pdev, jornada_ts);
 
@@ -126,36 +123,18 @@ static int jornada720_ts_probe(struct platform_device *pdev)
 	input_set_abs_params(input_dev, ABS_X, 270, 3900, 0, 0);
 	input_set_abs_params(input_dev, ABS_Y, 180, 3700, 0, 0);
 
-	error = request_irq(IRQ_GPIO9,
+	error = devm_request_irq(&pdev->dev, IRQ_GPIO9,
 			jornada720_ts_interrupt,
 			IRQF_TRIGGER_RISING,
 			"HP7XX Touchscreen driver", pdev);
 	if (error) {
-		printk(KERN_INFO "HP7XX TS : Unable to acquire irq!\n");
-		goto fail1;
+		dev_err(&pdev->dev, "HP7XX TS : Unable to acquire irq!\n");
+		return error;
 	}
 
 	error = input_register_device(jornada_ts->dev);
 	if (error)
-		goto fail2;
-
-	return 0;
-
- fail2:
-	free_irq(IRQ_GPIO9, pdev);
- fail1:
-	input_free_device(input_dev);
-	kfree(jornada_ts);
-	return error;
-}
-
-static int jornada720_ts_remove(struct platform_device *pdev)
-{
-	struct jornada_ts *jornada_ts = platform_get_drvdata(pdev);
-
-	free_irq(IRQ_GPIO9, pdev);
-	input_unregister_device(jornada_ts->dev);
-	kfree(jornada_ts);
+		return error;
 
 	return 0;
 }
@@ -165,7 +144,6 @@ MODULE_ALIAS("platform:jornada_ts");
 
 static struct platform_driver jornada720_ts_driver = {
 	.probe		= jornada720_ts_probe,
-	.remove		= jornada720_ts_remove,
 	.driver		= {
 		.name	= "jornada_ts",
 		.owner	= THIS_MODULE,
-- 
1.7.9.5


^ permalink raw reply related

* [PATCH 2/2] Input: jornada720_ts: Get rid of space indentation and use tab
From: Pramod Gurav @ 2014-07-30 13:48 UTC (permalink / raw)
  To: linux-input, linux-kernel; +Cc: Pramod Gurav, Dmitry Torokhov, Paul Gortmaker
In-Reply-To: <1406728107-26969-1-git-send-email-pramod.gurav@smartplayin.com>

CC: Dmitry Torokhov <dmitry.torokhov@gmail.com>
CC: Paul Gortmaker <paul.gortmaker@windriver.com>

Signed-off-by: Pramod Gurav <pramod.gurav@smartplayin.com>
---
 drivers/input/touchscreen/jornada720_ts.c |   25 ++++++++++++-------------
 1 file changed, 12 insertions(+), 13 deletions(-)

diff --git a/drivers/input/touchscreen/jornada720_ts.c b/drivers/input/touchscreen/jornada720_ts.c
index 7422480..5d34e3d 100644
--- a/drivers/input/touchscreen/jornada720_ts.c
+++ b/drivers/input/touchscreen/jornada720_ts.c
@@ -36,22 +36,21 @@ struct jornada_ts {
 
 static void jornada720_ts_collect_data(struct jornada_ts *jornada_ts)
 {
+	/* 3 low word X samples */
+	jornada_ts->x_data[0] = jornada_ssp_byte(TXDUMMY);
+	jornada_ts->x_data[1] = jornada_ssp_byte(TXDUMMY);
+	jornada_ts->x_data[2] = jornada_ssp_byte(TXDUMMY);
 
-    /* 3 low word X samples */
-    jornada_ts->x_data[0] = jornada_ssp_byte(TXDUMMY);
-    jornada_ts->x_data[1] = jornada_ssp_byte(TXDUMMY);
-    jornada_ts->x_data[2] = jornada_ssp_byte(TXDUMMY);
+	/* 3 low word Y samples */
+	jornada_ts->y_data[0] = jornada_ssp_byte(TXDUMMY);
+	jornada_ts->y_data[1] = jornada_ssp_byte(TXDUMMY);
+	jornada_ts->y_data[2] = jornada_ssp_byte(TXDUMMY);
 
-    /* 3 low word Y samples */
-    jornada_ts->y_data[0] = jornada_ssp_byte(TXDUMMY);
-    jornada_ts->y_data[1] = jornada_ssp_byte(TXDUMMY);
-    jornada_ts->y_data[2] = jornada_ssp_byte(TXDUMMY);
+	/* combined x samples bits */
+	jornada_ts->x_data[3] = jornada_ssp_byte(TXDUMMY);
 
-    /* combined x samples bits */
-    jornada_ts->x_data[3] = jornada_ssp_byte(TXDUMMY);
-
-    /* combined y samples bits */
-    jornada_ts->y_data[3] = jornada_ssp_byte(TXDUMMY);
+	/* combined y samples bits */
+	jornada_ts->y_data[3] = jornada_ssp_byte(TXDUMMY);
 }
 
 static int jornada720_ts_average(int coords[4])
-- 
1.7.9.5


^ permalink raw reply related

* [Patch v4] input: drv260x: Add TI drv260x haptics driver
From: Dan Murphy @ 2014-07-30 14:14 UTC (permalink / raw)
  To: linux-input
  Cc: linux-kernel, devicetree, dmitry.torokhov, mark.rutland,
	madcatxster, simon, elias.vds, Dan Murphy

Add the TI drv260x haptics/vibrator driver.
This device uses the input force feedback
to produce a wave form to driver an
ERM or LRA actuator device.

The initial driver supports the devices
real time playback mode.  But the device
has additional wave patterns in ROM.

This functionality will be added in
future patchsets.

Product data sheet is located here:
http://www.ti.com/product/drv2605

Signed-off-by: Dan Murphy <dmurphy@ti.com>
---

v4 - Convert regulator to devm, added error checking where required,
updated bindings doc, moved dt parsing to separate call and made platform
data the first data point, moved vibrator enable and mode programming from
play entry to worker thread, added user check and input mutex in suspend/resume
calls, moved platform data to individual file and into include platform-data directory,
removed file names from file headers - https://patchwork.kernel.org/patch/4642221/
v3 - Updated binding doc, changed to memless device, updated input alloc to
devm, removed mutex locking, add sanity checks for mode and library - https://patchwork.kernel.org/patch/4635421/
v2 - Fixed binding doc and patch headline - https://patchwork.kernel.org/patch/4619641/

 .../devicetree/bindings/input/ti,drv260x.txt       |   50 ++
 drivers/input/misc/Kconfig                         |    9 +
 drivers/input/misc/Makefile                        |    1 +
 drivers/input/misc/drv260x.c                       |  563 ++++++++++++++++++++
 include/dt-bindings/input/ti-drv260x.h             |   35 ++
 include/linux/input/drv260x.h                      |  173 ++++++
 include/linux/platform_data/drv260x-pdata.h        |   29 +
 7 files changed, 860 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/input/ti,drv260x.txt
 create mode 100644 drivers/input/misc/drv260x.c
 create mode 100644 include/dt-bindings/input/ti-drv260x.h
 create mode 100644 include/linux/input/drv260x.h
 create mode 100644 include/linux/platform_data/drv260x-pdata.h

diff --git a/Documentation/devicetree/bindings/input/ti,drv260x.txt b/Documentation/devicetree/bindings/input/ti,drv260x.txt
new file mode 100644
index 0000000..8e6970d
--- /dev/null
+++ b/Documentation/devicetree/bindings/input/ti,drv260x.txt
@@ -0,0 +1,50 @@
+Texas Instruments - drv260x Haptics driver family
+
+The drv260x family serial control bus communicates through I2C protocols
+
+Required properties:
+	- compatible - One of:
+		"ti,drv2604" - DRV2604
+		"ti,drv2605" - DRV2605
+		"ti,drv2605l" - DRV2605L
+	- reg -  I2C slave address
+	- supply- Required supply regulators are:
+		"vbat" - battery voltage
+	- mode - Power up mode of the chip (defined in include/dt-bindings/input/ti-drv260x.h)
+		DRV260X_LRA_MODE - Linear Resonance Actuator mode (Piezoelectric)
+		DRV260X_LRA_NO_CAL_MODE - This is a LRA Mode but there is no calibration
+				sequence during init.  And the device is configured for real
+				time playback mode (RTP mode).
+		DRV260X_ERM_MODE - Eccentric Rotating Mass mode (Rotary vibrator)
+	- library-sel - These are ROM based waveforms pre-programmed into the IC.
+				This should be set to set the library to use at power up.
+				(defined in include/dt-bindings/input/ti-drv260x.h)
+		DRV260X_LIB_A - Pre-programmed Library
+		DRV260X_LIB_B - Pre-programmed Library
+		DRV260X_LIB_C - Pre-programmed Library
+		DRV260X_LIB_D - Pre-programmed Library
+		DRV260X_LIB_E - Pre-programmed Library
+		DRV260X_LIB_F - Pre-programmed Library
+
+Optional properties:
+	- enable-gpio - gpio pin to enable/disable the device.
+	- vib_rated_voltage - The rated voltage of the actuator in millivolts.
+			  If this is not set then the value will be defaulted to
+			  3.2 v.
+	- vib_overdrive_voltage - The overdrive voltage of the actuator in millivolts.
+			  If this is not set then the value will be defaulted to
+			  3.2 v.
+Example:
+
+drv2605l: drv2605l@5a {
+		compatible = "ti,drv2605l";
+		reg = <0x5a>;
+		enable-gpio = <&gpio1 28 GPIO_ACTIVE_HIGH>;
+		mode = <DRV260X_LRA_MODE>;
+		library-sel = <DRV260X_LIB_SEL_DEFAULT>;
+		vib-rated-voltage = <3200>;
+		vib-overdriver-voltage = <3200>;
+};
+
+For more product information please see the link below:
+http://www.ti.com/product/drv2605
diff --git a/drivers/input/misc/Kconfig b/drivers/input/misc/Kconfig
index 2ff4425..99f6762 100644
--- a/drivers/input/misc/Kconfig
+++ b/drivers/input/misc/Kconfig
@@ -676,4 +676,13 @@ config INPUT_SOC_BUTTON_ARRAY
 	  To compile this driver as a module, choose M here: the
 	  module will be called soc_button_array.
 
+config INPUT_DRV260X_HAPTICS
+	tristate "TI DRV260X haptics support"
+	depends on INPUT && I2C
+	help
+	  Say Y to enable support for the TI DRV260X haptics driver.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called drv260x-haptics.
+
 endif
diff --git a/drivers/input/misc/Makefile b/drivers/input/misc/Makefile
index 4955ad3..d8ef3c7 100644
--- a/drivers/input/misc/Makefile
+++ b/drivers/input/misc/Makefile
@@ -64,3 +64,4 @@ obj-$(CONFIG_INPUT_WM831X_ON)		+= wm831x-on.o
 obj-$(CONFIG_INPUT_XEN_KBDDEV_FRONTEND)	+= xen-kbdfront.o
 obj-$(CONFIG_INPUT_YEALINK)		+= yealink.o
 obj-$(CONFIG_INPUT_IDEAPAD_SLIDEBAR)	+= ideapad_slidebar.o
+obj-$(CONFIG_INPUT_DRV260X_HAPTICS)	+= drv260x.o
diff --git a/drivers/input/misc/drv260x.c b/drivers/input/misc/drv260x.c
new file mode 100644
index 0000000..fdec7cf
--- /dev/null
+++ b/drivers/input/misc/drv260x.c
@@ -0,0 +1,563 @@
+/*
+ * DRV260X haptics driver family
+ *
+ * Author: Dan Murphy <dmurphy@ti.com>
+ *
+ * Copyright:   (C) 2014 Texas Instruments, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License 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.
+ */
+
+#include <linux/i2c.h>
+#include <linux/input.h>
+#include <linux/module.h>
+#include <linux/of_gpio.h>
+#include <linux/platform_device.h>
+#include <linux/regmap.h>
+#include <linux/slab.h>
+#include <linux/delay.h>
+#include <linux/regulator/consumer.h>
+
+#include <linux/input/drv260x.h>
+#include <dt-bindings/input/ti-drv260x.h>
+#include <linux/platform_data/drv260x-pdata.h>
+
+/**
+ * struct drv260x_data -
+ * @input_dev - Pointer to the input device
+ * @client - Pointer to the I2C client
+ * @regmap - Register map of the device
+ * @work - Work item used to off load the enable/disable of the vibration
+ * @enable_gpio - Pointer to the gpio used for enable/disabling
+ * @regulator - Pointer to the regulator for the IC
+ * @magnitude - Magnitude of the vibration event
+ * @mode - The operating mode of the IC (LRA_NO_CAL, ERM or LRA)
+ * @library - The vibration library to be used
+ * @rated_voltage - The rated_voltage of the actuator
+ * @overdriver_voltage - The over drive voltage of the actuator
+**/
+struct drv260x_data {
+	struct input_dev *input_dev;
+	struct i2c_client *client;
+	struct regmap *regmap;
+	struct work_struct work;
+	struct gpio_desc *enable_gpio;
+	struct regulator *regulator;
+	u32 magnitude;
+	u32 mode;
+	u32 library;
+	int rated_voltage;
+	int overdrive_voltage;
+};
+
+static struct reg_default drv260x_reg_defs[] = {
+	{ DRV260X_STATUS, 0xe0 },
+	{ DRV260X_MODE, 0x40 },
+	{ DRV260X_RT_PB_IN, 0x00},
+	{ DRV260X_LIB_SEL, 0x00},
+	{ DRV260X_WV_SEQ_1, 0x01},
+	{ DRV260X_WV_SEQ_2, 0x00},
+	{ DRV260X_WV_SEQ_3, 0x00},
+	{ DRV260X_WV_SEQ_4, 0x00},
+	{ DRV260X_WV_SEQ_5, 0x00},
+	{ DRV260X_WV_SEQ_6, 0x00},
+	{ DRV260X_WV_SEQ_7, 0x00},
+	{ DRV260X_WV_SEQ_8, 0x00},
+	{ DRV260X_GO, 0x00},
+	{ DRV260X_OVERDRIVE_OFF, 0x00},
+	{ DRV260X_SUSTAIN_P_OFF, 0x00},
+	{ DRV260X_SUSTAIN_N_OFF, 0x00},
+	{ DRV260X_BRAKE_OFF	, 0x00},
+	{ DRV260X_A_TO_V_CTRL	, 0x05},
+	{ DRV260X_A_TO_V_MIN_INPUT, 0x19},
+	{ DRV260X_A_TO_V_MAX_INPUT, 0xff},
+	{ DRV260X_A_TO_V_MIN_OUT, 0x19},
+	{ DRV260X_A_TO_V_MAX_OUT, 0xff},
+	{ DRV260X_RATED_VOLT, 0x3e},
+	{ DRV260X_OD_CLAMP_VOLT, 0x8c},
+	{ DRV260X_CAL_COMP, 0x0c},
+	{ DRV260X_CAL_BACK_EMF, 0x6c},
+	{ DRV260X_FEEDBACK_CTRL, 0x36},
+	{ DRV260X_CTRL1, 0x93},
+	{ DRV260X_CTRL2, 0xfa},
+	{ DRV260X_CTRL3, 0xa0},
+	{ DRV260X_CTRL4, 0x20},
+	{ DRV260X_CTRL5, 0x80},
+	{ DRV260X_LRA_LOOP_PERIOD, 0x33},
+	{ DRV260X_VBAT_MON, 0x00},
+	{ DRV260X_LRA_RES_PERIOD, 0x00},
+};
+
+/**
+ * Rated and Overdriver Voltages:
+ * Calculated using the formula r = v * 255 / 5.6
+ * where r is what will be written to the register
+ * and v is the rated or overdriver voltage of the actuator
+ **/
+#define DRV260X_DEF_RATED_VOLT		0x90
+#define DRV260X_DEF_OD_CLAMP_VOLT	0x90
+
+static int drv260x_calculate_voltage(int voltage)
+{
+	return (voltage * 255 / 5600);
+}
+
+static void drv260x_worker(struct work_struct *work)
+{
+	struct drv260x_data *haptics = container_of(work, struct drv260x_data, work);
+	int ret;
+
+	gpiod_set_value(haptics->enable_gpio, 1);
+	/* Data sheet says to wait 250us before trying to communicate */
+	udelay(250);
+
+	ret = regmap_write(haptics->regmap, DRV260X_MODE, DRV260X_RT_PLAYBACK);
+	if (ret != 0) {
+		dev_err(&haptics->client->dev,
+			"Failed to write set mode: %d\n", ret);
+		return;
+	}
+
+	ret = regmap_write(haptics->regmap, DRV260X_RT_PB_IN, haptics->magnitude);
+	if (ret != 0) {
+		dev_err(&haptics->client->dev,
+			"Failed to set magnitude: %d\n", ret);
+	}
+}
+
+static int drv260x_haptics_play(struct input_dev *input, void *data,
+				struct ff_effect *effect)
+{
+	struct drv260x_data *haptics = input_get_drvdata(input);
+
+	haptics->mode = DRV260X_LRA_NO_CAL_MODE;
+	haptics->magnitude = 0;
+
+	if (effect->u.rumble.strong_magnitude ||
+		effect->u.rumble.weak_magnitude) {
+		if (effect->u.rumble.strong_magnitude > 0)
+			haptics->magnitude = effect->u.rumble.strong_magnitude;
+		else if	(effect->u.rumble.weak_magnitude > 0)
+			haptics->magnitude = effect->u.rumble.weak_magnitude;
+	}
+
+	schedule_work(&haptics->work);
+
+	return 0;
+}
+
+static void drv260x_close(struct input_dev *input)
+{
+	struct drv260x_data *haptics = input_get_drvdata(input);
+	int ret;
+
+	cancel_work_sync(&haptics->work);
+
+	ret = regmap_write(haptics->regmap, DRV260X_MODE, DRV260X_STANDBY);
+	if (ret != 0) {
+		dev_err(&haptics->client->dev,
+			"Failed to write standby mode: %d\n", ret);
+	}
+
+	gpiod_set_value(haptics->enable_gpio, 0);
+}
+
+static const struct reg_default drv260x_lra_cal_regs[] = {
+	{ DRV260X_MODE, DRV260X_AUTO_CAL },
+	{ DRV260X_CTRL3, DRV260X_NG_THRESH_2},
+	{ DRV260X_FEEDBACK_CTRL, DRV260X_FB_REG_LRA_MODE | DRV260X_BRAKE_FACTOR_4X | DRV260X_LOOP_GAIN_HIGH },
+};
+
+static const struct reg_default drv260x_lra_init_regs[] = {
+	{ DRV260X_MODE, DRV260X_RT_PLAYBACK},
+	{ DRV260X_A_TO_V_CTRL, DRV260X_AUDIO_HAPTICS_PEAK_20MS | DRV260X_AUDIO_HAPTICS_FILTER_125HZ},
+	{ DRV260X_A_TO_V_MIN_INPUT, DRV260X_AUDIO_HAPTICS_MIN_IN_VOLT },
+	{ DRV260X_A_TO_V_MAX_INPUT, DRV260X_AUDIO_HAPTICS_MAX_IN_VOLT },
+	{ DRV260X_A_TO_V_MIN_OUT, DRV260X_AUDIO_HAPTICS_MIN_OUT_VOLT },
+	{ DRV260X_A_TO_V_MAX_OUT, DRV260X_AUDIO_HAPTICS_MAX_OUT_VOLT },
+	{ DRV260X_FEEDBACK_CTRL, DRV260X_FB_REG_LRA_MODE | DRV260X_BRAKE_FACTOR_2X | DRV260X_LOOP_GAIN_MED | DRV260X_BEMF_GAIN_3 },
+	{ DRV260X_CTRL1, DRV260X_STARTUP_BOOST },
+	{ DRV260X_CTRL2, DRV260X_SAMP_TIME_250 },
+	{ DRV260X_CTRL3, DRV260X_NG_THRESH_2 | DRV260X_ANANLOG_IN },
+	{ DRV260X_CTRL4, DRV260X_AUTOCAL_TIME_500MS },
+};
+
+static const struct reg_default drv260x_erm_cal_regs[] = {
+	{ DRV260X_MODE, DRV260X_AUTO_CAL },
+	{ DRV260X_A_TO_V_MIN_INPUT, DRV260X_AUDIO_HAPTICS_MIN_IN_VOLT },
+	{ DRV260X_A_TO_V_MAX_INPUT, DRV260X_AUDIO_HAPTICS_MAX_IN_VOLT },
+	{ DRV260X_A_TO_V_MIN_OUT, DRV260X_AUDIO_HAPTICS_MIN_OUT_VOLT },
+	{ DRV260X_A_TO_V_MAX_OUT, DRV260X_AUDIO_HAPTICS_MAX_OUT_VOLT },
+	{ DRV260X_FEEDBACK_CTRL, DRV260X_BRAKE_FACTOR_3X | DRV260X_LOOP_GAIN_MED | DRV260X_BEMF_GAIN_2 },
+	{ DRV260X_CTRL1, DRV260X_STARTUP_BOOST },
+	{ DRV260X_CTRL2, DRV260X_SAMP_TIME_250 | DRV260X_BLANK_TIME_75 | DRV260X_SAMP_TIME_250 | DRV260X_IDISS_TIME_75 },
+	{ DRV260X_CTRL3, DRV260X_NG_THRESH_2 | DRV260X_ERM_OPEN_LOOP },
+	{ DRV260X_CTRL4, DRV260X_AUTOCAL_TIME_500MS },
+};
+
+static int drv260x_init(struct drv260x_data *haptics)
+{
+	int ret;
+	unsigned int cal_buf;
+
+	ret = regmap_write(haptics->regmap,
+			   DRV260X_RATED_VOLT, haptics->rated_voltage);
+	if (ret != 0)
+		goto write_failure;
+
+	ret = regmap_write(haptics->regmap,
+			   DRV260X_OD_CLAMP_VOLT, haptics->overdrive_voltage);
+	if (ret != 0)
+		goto write_failure;
+
+	switch(haptics->mode) {
+	case DRV260X_LRA_MODE:
+		ret = regmap_register_patch(haptics->regmap,
+					drv260x_lra_cal_regs,
+					ARRAY_SIZE(drv260x_lra_cal_regs));
+		if (ret != 0)
+			goto write_failure;
+		break;
+	case DRV260X_ERM_MODE:
+		ret = regmap_register_patch(haptics->regmap,
+					drv260x_erm_cal_regs,
+					ARRAY_SIZE(drv260x_erm_cal_regs));
+		if (ret != 0)
+			goto write_failure;
+
+		ret = regmap_update_bits(haptics->regmap, DRV260X_LIB_SEL,
+					DRV260X_LIB_SEL_MASK,
+					haptics->library);
+		if (ret != 0)
+			goto write_failure;
+		break;
+	default:
+		ret = regmap_register_patch(haptics->regmap,
+					drv260x_lra_init_regs,
+					ARRAY_SIZE(drv260x_lra_init_regs));
+		if (ret != 0)
+			goto write_failure;
+
+		ret = regmap_update_bits(haptics->regmap, DRV260X_LIB_SEL,
+					DRV260X_LIB_SEL_MASK,
+					haptics->library);
+		if (ret != 0)
+			goto write_failure;
+
+		goto skip_go_bit;
+		break;
+	}
+
+	if (ret != 0) {
+		dev_err(&haptics->client->dev,
+			"Failed to write init registers: %d\n",
+			ret);
+		goto write_failure;
+	}
+
+	ret = regmap_write(haptics->regmap, DRV260X_GO, DRV260X_GO_BIT);
+	if (ret != 0)
+		goto write_failure;
+
+	do {
+		ret = regmap_read(haptics->regmap, DRV260X_GO, &cal_buf);
+		if (ret != 0)
+			goto write_failure;
+	} while (cal_buf == DRV260X_GO_BIT || ret != 0);
+
+	return ret;
+
+write_failure:
+	dev_err(&haptics->client->dev,
+		"Failed to write init registers: %d\n",
+		ret);
+skip_go_bit:
+	return ret;
+}
+
+static const struct regmap_config drv260x_regmap_config = {
+	.reg_bits = 8,
+	.val_bits = 8,
+
+	.max_register = DRV260X_MAX_REG,
+	.reg_defaults = drv260x_reg_defs,
+	.num_reg_defaults = ARRAY_SIZE(drv260x_reg_defs),
+	.cache_type = REGCACHE_NONE,
+};
+
+#ifdef CONFIG_OF
+static int drv260x_parse_dt(struct device_node *dev_node,
+				struct drv260x_data *haptics,
+				struct device *dev)
+{
+	int ret;
+	int voltage;
+
+	ret = of_property_read_u32(dev_node, "mode", &haptics->mode);
+	if (ret < 0) {
+		dev_err(dev, "%s: No entry for mode\n", __func__);
+
+		return ret;
+	}
+	ret = of_property_read_u32(dev_node, "library-sel",
+				&haptics->library);
+	if (ret < 0) {
+		dev_err(dev, "%s: No entry for library selection\n",
+			__func__);
+
+		return ret;
+	}
+	ret = of_property_read_u32(dev_node, "vib-rated-voltage",
+				&voltage);
+	if (!ret)
+		haptics->rated_voltage = drv260x_calculate_voltage(voltage);
+
+
+	ret = of_property_read_u32(dev_node, "vib-overdrive-voltage",
+				&voltage);
+	if (!ret)
+		haptics->overdrive_voltage = drv260x_calculate_voltage(voltage);
+
+	return ret;
+
+}
+#else
+static inline int drv260x_parse_dt(struct device *dev)
+{
+	dev_err(dev, "no platform data defined\n");
+
+	return -EINVAL;
+}
+#endif
+static int drv260x_probe(struct i2c_client *client,
+			   const struct i2c_device_id *id)
+{
+	struct drv260x_data *haptics;
+	struct device_node *np = client->dev.of_node;
+	struct drv260x_platform_data *pdata = dev_get_platdata(&client->dev);
+	int ret;
+
+	haptics = devm_kzalloc(&client->dev, sizeof(*haptics), GFP_KERNEL);
+	if (!haptics)
+		return -ENOMEM;
+
+	haptics->rated_voltage = DRV260X_DEF_OD_CLAMP_VOLT;
+	haptics->rated_voltage = DRV260X_DEF_RATED_VOLT;
+
+	 if (pdata) {
+		haptics->mode = pdata->mode;
+		haptics->library = pdata->library_selection;
+		if (pdata->vib_overdrive_voltage)
+			haptics->overdrive_voltage = drv260x_calculate_voltage(pdata->vib_overdrive_voltage);
+		if (pdata->vib_rated_voltage)
+			haptics->rated_voltage = drv260x_calculate_voltage(pdata->vib_rated_voltage);
+	} else if (np) {
+		ret = drv260x_parse_dt(np, haptics, &client->dev);
+		if (ret)
+			return ret;
+	} else {
+		dev_err(&client->dev, "Platform data not set\n");
+		return -ENODEV;
+	}
+
+
+	if (haptics->mode < DRV260X_LRA_MODE ||
+		haptics->mode > DRV260X_ERM_MODE) {
+			dev_err(&client->dev,
+				"Vibrator mode is invalid: %i\n",
+				haptics->mode);
+			return -EINVAL;
+	}
+	
+	if (haptics->library < DRV260X_LIB_SEL_DEFAULT ||
+		haptics->library > DRV260X_LIB_F) {
+			dev_err(&client->dev,
+				"Library value is invalid: %i\n", haptics->library);
+			return -EINVAL;
+	}	
+
+	haptics->regulator = devm_regulator_get(&client->dev, "vbat");
+	if (IS_ERR(haptics->regulator)) {
+		ret = PTR_ERR(haptics->regulator);
+		dev_err(&client->dev,
+			"unable to get regulator, error: %d\n", ret);
+		goto err_regulator;
+	}
+
+	haptics->enable_gpio = devm_gpiod_get(&client->dev, "enable");
+	if (IS_ERR(haptics->enable_gpio)) {
+		ret = PTR_ERR(haptics->enable_gpio);
+		if (ret != -ENOENT && ret != -ENOSYS)
+			goto err_gpio;
+
+		haptics->enable_gpio = NULL;
+	} else {
+		gpiod_direction_output(haptics->enable_gpio, 1);
+	}
+
+	haptics->input_dev = devm_input_allocate_device(&client->dev);
+	if (haptics->input_dev == NULL) {
+		dev_err(&client->dev, "Failed to allocate input device\n");
+		ret = -ENOMEM;
+		goto err_input_alloc;
+	}
+
+	haptics->input_dev->name = "drv260x:haptics";
+	haptics->input_dev->dev.parent = client->dev.parent;
+	haptics->input_dev->close = drv260x_close;
+	input_set_drvdata(haptics->input_dev, haptics);
+	input_set_capability(haptics->input_dev, EV_FF, FF_RUMBLE);
+
+	ret = input_ff_create_memless(haptics->input_dev, NULL,
+				      drv260x_haptics_play);
+	if (ret < 0) {
+		dev_err(&client->dev, "input_ff_create() failed: %d\n",
+			ret);
+		goto err_ff_create;
+	}
+
+	INIT_WORK(&haptics->work, drv260x_worker);
+
+	haptics->client = client;
+	i2c_set_clientdata(client, haptics);
+
+	haptics->regmap = devm_regmap_init_i2c(client, &drv260x_regmap_config);
+	if (IS_ERR(haptics->regmap)) {
+		ret = PTR_ERR(haptics->regmap);
+		dev_err(&client->dev, "Failed to allocate register map: %d\n",
+			ret);
+		goto err_regmap;
+	}
+
+	drv260x_init(haptics);
+
+	ret = input_register_device(haptics->input_dev);
+	if (ret < 0) {
+		dev_err(&client->dev, "couldn't register input device: %d\n",
+			ret);
+		goto err_iff;
+	}
+	return 0;
+
+err_iff:
+err_regmap:
+err_ff_create:
+err_input_alloc:
+err_gpio:
+err_regulator:
+	return ret;
+}
+
+static int drv260x_remove(struct i2c_client *client)
+{
+	struct drv260x_data *haptics = i2c_get_clientdata(client);
+
+	cancel_work_sync(&haptics->work);
+
+	regmap_write(haptics->regmap, DRV260X_MODE, DRV260X_STANDBY);
+	gpiod_set_value(haptics->enable_gpio, 0);
+
+	return 0;
+}
+
+#ifdef CONFIG_PM_SLEEP
+static int drv260x_suspend(struct device *dev)
+{
+	struct drv260x_data *haptics = dev_get_drvdata(dev);
+	int ret = 0;
+
+	mutex_lock(&haptics->input_dev->mutex);
+
+	if (haptics->input_dev->users) {
+		ret = regmap_update_bits(haptics->regmap,
+				   DRV260X_MODE,
+				   DRV260X_STANDBY_MASK,
+				   DRV260X_STANDBY);
+		if (ret) {
+			dev_err(dev, "Failed to set standby mode\n");
+			goto out;
+		}
+
+		gpiod_set_value(haptics->enable_gpio, 0);
+
+		ret = regulator_disable(haptics->regulator);
+		if (ret)
+			dev_err(dev, "Failed to disable regulator\n");
+	}
+out:
+	mutex_unlock(&haptics->input_dev->mutex);
+	return ret;
+}
+
+static int drv260x_resume(struct device *dev)
+{
+	struct drv260x_data *haptics = dev_get_drvdata(dev);
+	int ret = 0;
+
+	mutex_lock(&haptics->input_dev->mutex);
+
+	if (haptics->input_dev->users) {
+		ret = regulator_enable(haptics->regulator);
+		if (ret) {
+			dev_err(dev, "Failed to enable regulator\n");
+			goto out;
+		}
+		ret = regmap_update_bits(haptics->regmap,
+				   DRV260X_MODE,
+				   DRV260X_STANDBY_MASK, 0);
+		if (ret) {
+			dev_err(dev, "Failed to unset standby mode\n");
+			goto out;
+		}
+
+		gpiod_set_value(haptics->enable_gpio, 1);
+	}
+
+out:
+	mutex_unlock(&haptics->input_dev->mutex);
+	return ret;
+}
+#endif
+
+static SIMPLE_DEV_PM_OPS(drv260x_pm_ops, drv260x_suspend, drv260x_resume);
+
+static const struct i2c_device_id drv260x_id[] = {
+	{ "drv2605l", 0 },
+	{ }
+};
+MODULE_DEVICE_TABLE(i2c, drv260x_id);
+
+#if IS_ENABLED(CONFIG_OF)
+static const struct of_device_id drv260x_of_match[] = {
+	{ .compatible = "ti,drv2604", },
+	{ .compatible = "ti,drv2605", },
+	{ .compatible = "ti,drv2605l", },
+	{}
+};
+MODULE_DEVICE_TABLE(of, drv260x_of_match);
+#endif
+
+static struct i2c_driver drv260x_driver = {
+	.probe		= drv260x_probe,
+	.remove		= drv260x_remove,
+	.driver		= {
+		.name	= "drv260x-haptics",
+		.owner	= THIS_MODULE,
+		.of_match_table = of_match_ptr(drv260x_of_match),
+		.pm	= &drv260x_pm_ops,
+	},
+	.id_table = drv260x_id,
+};
+module_i2c_driver(drv260x_driver);
+
+MODULE_ALIAS("platform:drv260x-haptics");
+MODULE_DESCRIPTION("TI DRV260x haptics driver");
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Dan Murphy <dmurphy@ti.com>");
diff --git a/include/dt-bindings/input/ti-drv260x.h b/include/dt-bindings/input/ti-drv260x.h
new file mode 100644
index 0000000..be7f245
--- /dev/null
+++ b/include/dt-bindings/input/ti-drv260x.h
@@ -0,0 +1,35 @@
+/*
+ * DRV260X haptics driver family
+ *
+ * Author: Dan Murphy <dmurphy@ti.com>
+ *
+ * Copyright:   (C) 2014 Texas Instruments, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License 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.
+ */
+
+#ifndef _DT_BINDINGS_TI_DRV260X_H
+#define _DT_BINDINGS_TI_DRV260X_H
+
+/* Calibration Types */
+#define DRV260X_LRA_MODE		0x00
+#define DRV260X_LRA_NO_CAL_MODE	0x01
+#define DRV260X_ERM_MODE		0x02
+
+/* Library Selection */
+#define DRV260X_LIB_SEL_DEFAULT		0x00
+#define DRV260X_LIB_A				0x01
+#define DRV260X_LIB_B				0x02
+#define DRV260X_LIB_C				0x03
+#define DRV260X_LIB_D				0x04
+#define DRV260X_LIB_E				0x05
+#define DRV260X_LIB_F				0x06
+
+#endif
diff --git a/include/linux/input/drv260x.h b/include/linux/input/drv260x.h
new file mode 100644
index 0000000..85fdaa4
--- /dev/null
+++ b/include/linux/input/drv260x.h
@@ -0,0 +1,173 @@
+/*
+ * DRV260X haptics driver family
+ *
+ * Author: Dan Murphy <dmurphy@ti.com>
+ *
+ * Copyright:   (C) 2014 Texas Instruments, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License 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.
+ */
+
+#ifndef _LINUX_DRV260X_I2C_H
+#define _LINUX_DRV260X_I2C_H
+
+#define DRV260X_STATUS		0x0
+#define DRV260X_MODE		0x1
+#define DRV260X_RT_PB_IN	0x2
+#define DRV260X_LIB_SEL		0x3
+#define DRV260X_WV_SEQ_1	0x4
+#define DRV260X_WV_SEQ_2	0x5
+#define DRV260X_WV_SEQ_3	0x6
+#define DRV260X_WV_SEQ_4	0x7
+#define DRV260X_WV_SEQ_5	0x8
+#define DRV260X_WV_SEQ_6	0x9
+#define DRV260X_WV_SEQ_7	0xa
+#define DRV260X_WV_SEQ_8	0xb
+#define DRV260X_GO				0xc
+#define DRV260X_OVERDRIVE_OFF	0xd
+#define DRV260X_SUSTAIN_P_OFF	0xe
+#define DRV260X_SUSTAIN_N_OFF	0xf
+#define DRV260X_BRAKE_OFF		0x10
+#define DRV260X_A_TO_V_CTRL		0x11
+#define DRV260X_A_TO_V_MIN_INPUT	0x12
+#define DRV260X_A_TO_V_MAX_INPUT	0x13
+#define DRV260X_A_TO_V_MIN_OUT	0x14
+#define DRV260X_A_TO_V_MAX_OUT	0x15
+#define DRV260X_RATED_VOLT		0x16
+#define DRV260X_OD_CLAMP_VOLT	0x17
+#define DRV260X_CAL_COMP		0x18
+#define DRV260X_CAL_BACK_EMF	0x19
+#define DRV260X_FEEDBACK_CTRL	0x1a
+#define DRV260X_CTRL1			0x1b
+#define DRV260X_CTRL2			0x1c
+#define DRV260X_CTRL3			0x1d
+#define DRV260X_CTRL4			0x1e
+#define DRV260X_CTRL5			0x1f
+#define DRV260X_LRA_LOOP_PERIOD	0x20
+#define DRV260X_VBAT_MON		0x21
+#define DRV260X_LRA_RES_PERIOD	0x22
+#define DRV260X_MAX_REG			0x23
+
+#define DRV260X_ALLOWED_R_BYTES	25
+#define DRV260X_ALLOWED_W_BYTES	2
+#define DRV260X_MAX_RW_RETRIES	5
+#define DRV260X_I2C_RETRY_DELAY 10
+
+#define DRV260X_GO_BIT				0x01
+
+/* Library Selection */
+#define DRV260X_LIB_SEL_MASK		0x07
+#define DRV260X_LIB_SEL_RAM			0x0
+#define DRV260X_LIB_SEL_OD			0x1
+#define DRV260X_LIB_SEL_40_60		0x2
+#define DRV260X_LIB_SEL_60_80		0x3
+#define DRV260X_LIB_SEL_100_140		0x4
+#define DRV260X_LIB_SEL_140_PLUS	0x5
+
+#define DRV260X_LIB_SEL_HIZ_MASK	0x10
+#define DRV260X_LIB_SEL_HIZ_EN		0x01
+#define DRV260X_LIB_SEL_HIZ_DIS		0
+
+/* Mode register */
+#define DRV260X_STANDBY				(1 << 6)
+#define DRV260X_STANDBY_MASK		0x40
+#define DRV260X_INTERNAL_TRIGGER	0x00
+#define DRV260X_EXT_TRIGGER_EDGE	0x01
+#define DRV260X_EXT_TRIGGER_LEVEL	0x02
+#define DRV260X_PWM_ANALOG_IN		0x03
+#define DRV260X_AUDIOHAPTIC			0x04
+#define DRV260X_RT_PLAYBACK			0x05
+#define DRV260X_DIAGNOSTICS			0x06
+#define DRV260X_AUTO_CAL			0x07
+
+/* Audio to Haptics Control */
+#define DRV260X_AUDIO_HAPTICS_PEAK_10MS		(0 << 2)
+#define DRV260X_AUDIO_HAPTICS_PEAK_20MS		(1 << 2)
+#define DRV260X_AUDIO_HAPTICS_PEAK_30MS		(2 << 2)
+#define DRV260X_AUDIO_HAPTICS_PEAK_40MS		(3 << 2)
+
+#define DRV260X_AUDIO_HAPTICS_FILTER_100HZ	0x00
+#define DRV260X_AUDIO_HAPTICS_FILTER_125HZ	0x01
+#define DRV260X_AUDIO_HAPTICS_FILTER_150HZ	0x02
+#define DRV260X_AUDIO_HAPTICS_FILTER_200HZ	0x03
+
+/* Min/Max Input/Output Voltages */
+#define DRV260X_AUDIO_HAPTICS_MIN_IN_VOLT	0x19
+#define DRV260X_AUDIO_HAPTICS_MAX_IN_VOLT	0x64
+#define DRV260X_AUDIO_HAPTICS_MIN_OUT_VOLT	0x19
+#define DRV260X_AUDIO_HAPTICS_MAX_OUT_VOLT	0xFF
+
+/* Feedback register */
+#define DRV260X_FB_REG_ERM_MODE			0x7f
+#define DRV260X_FB_REG_LRA_MODE			(1 << 7)
+
+#define DRV260X_BRAKE_FACTOR_MASK	0x1f
+#define DRV260X_BRAKE_FACTOR_2X		(1 << 0)
+#define DRV260X_BRAKE_FACTOR_3X		(2 << 4)
+#define DRV260X_BRAKE_FACTOR_4X		(3 << 4)
+#define DRV260X_BRAKE_FACTOR_6X		(4 << 4)
+#define DRV260X_BRAKE_FACTOR_8X		(5 << 4)
+#define DRV260X_BRAKE_FACTOR_16		(6 << 4)
+#define DRV260X_BRAKE_FACTOR_DIS	(7 << 4)
+
+#define DRV260X_LOOP_GAIN_LOW		0xf3
+#define DRV260X_LOOP_GAIN_MED		(1 << 2)
+#define DRV260X_LOOP_GAIN_HIGH		(2 << 2)
+#define DRV260X_LOOP_GAIN_VERY_HIGH	(3 << 2)
+
+#define DRV260X_BEMF_GAIN_0			0xfc
+#define DRV260X_BEMF_GAIN_1		(1 << 0)
+#define DRV260X_BEMF_GAIN_2		(2 << 0)
+#define DRV260X_BEMF_GAIN_3		(3 << 0)
+
+/* Control 1 register */
+#define DRV260X_AC_CPLE_EN			(1 << 5)
+#define DRV260X_STARTUP_BOOST		(1 << 7)
+
+/* Control 2 register */
+
+#define DRV260X_IDISS_TIME_45		0
+#define DRV260X_IDISS_TIME_75		(1 << 0)
+#define DRV260X_IDISS_TIME_150		(1 << 1)
+#define DRV260X_IDISS_TIME_225		0x03
+
+#define DRV260X_BLANK_TIME_45	(0 << 2)
+#define DRV260X_BLANK_TIME_75	(1 << 2)
+#define DRV260X_BLANK_TIME_150	(2 << 2)
+#define DRV260X_BLANK_TIME_225	(3 << 2)
+
+#define DRV260X_SAMP_TIME_150	(0 << 4)
+#define DRV260X_SAMP_TIME_200	(1 << 4)
+#define DRV260X_SAMP_TIME_250	(2 << 4)
+#define DRV260X_SAMP_TIME_300	(3 << 4)
+
+#define DRV260X_BRAKE_STABILIZER	(1 << 6)
+#define DRV260X_UNIDIR_IN			(0 << 7)
+#define DRV260X_BIDIR_IN			(1 << 7)
+
+/* Control 3 Register */
+#define DRV260X_LRA_OPEN_LOOP		(1 << 0)
+#define DRV260X_ANANLOG_IN			(1 << 1)
+#define DRV260X_LRA_DRV_MODE		(1 << 2)
+#define DRV260X_RTP_UNSIGNED_DATA	(1 << 3)
+#define DRV260X_SUPPLY_COMP_DIS		(1 << 4)
+#define DRV260X_ERM_OPEN_LOOP		(1 << 5)
+#define DRV260X_NG_THRESH_0			(0 << 6)
+#define DRV260X_NG_THRESH_2			(1 << 6)
+#define DRV260X_NG_THRESH_4			(2 << 6)
+#define DRV260X_NG_THRESH_8			(3 << 6)
+
+/* Control 4 Register */
+#define DRV260X_AUTOCAL_TIME_150MS		(0 << 4)
+#define DRV260X_AUTOCAL_TIME_250MS		(1 << 4)
+#define DRV260X_AUTOCAL_TIME_500MS		(2 << 4)
+#define DRV260X_AUTOCAL_TIME_1000MS		(3 << 4)
+
+#endif
diff --git a/include/linux/platform_data/drv260x-pdata.h b/include/linux/platform_data/drv260x-pdata.h
new file mode 100644
index 0000000..4091a9b
--- /dev/null
+++ b/include/linux/platform_data/drv260x-pdata.h
@@ -0,0 +1,29 @@
+/*
+ * Platform data for DRV260X haptics driver family
+ *
+ * Author: Dan Murphy <dmurphy@ti.com>
+ *
+ * Copyright:   (C) 2014 Texas Instruments, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License 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.
+ */
+
+#ifndef _LINUX_DRV260X_PDATA_H
+#define _LINUX_DRV260X_PDATA_H
+
+struct drv260x_platform_data {
+	int enable_gpio;
+	u32 library_selection;
+	u32 mode;
+	u32 vib_rated_voltage;
+	u32 vib_overdrive_voltage;
+};
+
+#endif
-- 
1.7.9.5


^ permalink raw reply related

* [PATCH 1/2] Input: mcs5000_ts - Protect PM functions with CONFIG_PM_SLEEP
From: Fabio Estevam @ 2014-07-30 16:17 UTC (permalink / raw)
  To: dmitry.torokhov; +Cc: beomho.seo, linux-input, Fabio Estevam

From: Fabio Estevam <fabio.estevam@freescale.com>

When a kernel has CONFIG_PM=y and CONFIG_PM_SLEEP=n the following warnings are 
seen:
drivers/input/touchscreen/mcs5000_ts.c:252:12: warning: 'mcs5000_ts_suspend' defined but not used [-Wunused-function]
 static int mcs5000_ts_suspend(struct device *dev)
            ^
drivers/input/touchscreen/mcs5000_ts.c:262:12: warning: 'mcs5000_ts_resume' defined but not used [-Wunused-function]
 static int mcs5000_ts_resume(struct device *dev)

Protect the suspend/resume functions with CONFIG_PM_SLEEP in order to fix these
build warnings.

Signed-off-by: Fabio Estevam <fabio.estevam@freescale.com>
---
 drivers/input/touchscreen/mcs5000_ts.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/drivers/input/touchscreen/mcs5000_ts.c b/drivers/input/touchscreen/mcs5000_ts.c
index 00510a9..1fb760c 100644
--- a/drivers/input/touchscreen/mcs5000_ts.c
+++ b/drivers/input/touchscreen/mcs5000_ts.c
@@ -248,7 +248,7 @@ static int mcs5000_ts_probe(struct i2c_client *client,
 	return 0;
 }
 
-#ifdef CONFIG_PM
+#ifdef CONFIG_PM_SLEEP
 static int mcs5000_ts_suspend(struct device *dev)
 {
 	struct i2c_client *client = to_i2c_client(dev);
-- 
1.9.1


^ permalink raw reply related

* [PATCH 2/2] Input: mcs5000_ts - Remove ifdef
From: Fabio Estevam @ 2014-07-30 16:17 UTC (permalink / raw)
  To: dmitry.torokhov; +Cc: beomho.seo, linux-input, Fabio Estevam
In-Reply-To: <1406737039-1404-1-git-send-email-festevam@gmail.com>

From: Fabio Estevam <fabio.estevam@freescale.com>

We can annonate the suspend/resume functions with '__maybe_unused' and get rid
of the ifdef, which makes the code smaller and simpler.

Signed-off-by: Fabio Estevam <fabio.estevam@freescale.com>
---
 drivers/input/touchscreen/mcs5000_ts.c | 6 ++----
 1 file changed, 2 insertions(+), 4 deletions(-)

diff --git a/drivers/input/touchscreen/mcs5000_ts.c b/drivers/input/touchscreen/mcs5000_ts.c
index 1fb760c..8b47e1f 100644
--- a/drivers/input/touchscreen/mcs5000_ts.c
+++ b/drivers/input/touchscreen/mcs5000_ts.c
@@ -248,8 +248,7 @@ static int mcs5000_ts_probe(struct i2c_client *client,
 	return 0;
 }
 
-#ifdef CONFIG_PM_SLEEP
-static int mcs5000_ts_suspend(struct device *dev)
+static int __maybe_unused mcs5000_ts_suspend(struct device *dev)
 {
 	struct i2c_client *client = to_i2c_client(dev);
 
@@ -259,7 +258,7 @@ static int mcs5000_ts_suspend(struct device *dev)
 	return 0;
 }
 
-static int mcs5000_ts_resume(struct device *dev)
+static int __maybe_unused mcs5000_ts_resume(struct device *dev)
 {
 	struct i2c_client *client = to_i2c_client(dev);
 	struct mcs5000_ts_data *data = i2c_get_clientdata(client);
@@ -269,7 +268,6 @@ static int mcs5000_ts_resume(struct device *dev)
 
 	return 0;
 }
-#endif
 
 static SIMPLE_DEV_PM_OPS(mcs5000_ts_pm, mcs5000_ts_suspend, mcs5000_ts_resume);
 
-- 
1.9.1


^ permalink raw reply related

* Re: Trouble with an HP stylus
From: Éric Brunet @ 2014-07-30 21:50 UTC (permalink / raw)
  To: benjamin.tissoires; +Cc: jkosina, linux-input, eric.brunet

Hi!

Thanks for both answers.

To recall the context:

>PRODUCT=3/3eb/840b/111
>NAME="Atmel Atmel maXTouch Digitizer Pen"
>
>Playing with evtest, it appears that the kernel reports the X coordinate
>in ABS_X and in ABS_Y, while the Y coordinate goes into ABS_Z and ABS_RX;
 
> I have already seen this Atmel panel, and the problem was that they used
> twice X then twice Y to report coordinates:
> 
> diff --git a/drivers/hid/hid-input.c b/drivers/hid/hid-input.c
> index 2619f7f..b70d4c3 100644
> --- a/drivers/hid/hid-input.c
> +++ b/drivers/hid/hid-input.c
> @@ -603,7 +603,7 @@ static void hidinput_configure_usage(struct hid_input
> *hidinput, struct hid_fiel if (field->flags & HID_MAIN_ITEM_RELATIVE)
> map_rel(usage->hid & 0xf);
>                         else
> -                               map_abs(usage->hid & 0xf);
> +                               map_abs_clear(usage->hid & 0xf);
>                         break;
> 
>                 case HID_GD_HATSWITCH: 

Yep, this patch helps. It helps a lot. the ABS_X and ABS_Y coordinates are now 
correct, the ABS_Z and ABS_RX coordinates are gone, I can point and click with 
the pen.

However, things do not work totally as expected, and the pen remains unusable 
for many drawing applications. I am not sure if the problem lies in the 
input/hid system, or in Xorg's evdev driver or in the applications, but the 
problem I am about to describe occurs only with the pen: everything works as 
expected with the touchpad (recognized as a simple mouse) or with the finger on 
the touchscreen:
  * when I try to make annotations with okular with the "draw a freehand line" 
tool, the line I draw with the pen jumps (drawing a straight line) to some 
apparently random point outside okular's window and jumps back (drawing 
another straight line) to where the pen is, several times per second. I don't 
want to send a jpg to the whole mailing list, but I can send a snapshot on a 
private email on request.
  * when I try to use a drawing tool with gimp or mypaint, each time I raise
the pen and put it at some other place to make another drawing, a straight line 
is drawn between the last and the new position of the pen.

Here is what happens on evtest if I touch the screen with the pen, move a
bit and stops touching; I didn't see anything suspicious, except maybe that 
sometimes there is a single ABS_X or a single ABS_Y without the other 
coordinate between two SYN_REPORT.

Event: time 1406710046.690615, type 1 (EV_KEY), code 320 (BTN_TOOL_PEN), value 1
Event: time 1406710046.690615, type 3 (EV_ABS), code 0 (ABS_X), value 3279
Event: time 1406710046.690615, type 3 (EV_ABS), code 1 (ABS_Y), value 2555
Event: time 1406710046.690615, -------------- SYN_REPORT ------------
Event: time 1406710046.697616, type 4 (EV_MSC), code 4 (MSC_SCAN), value d0042
Event: time 1406710046.697616, type 1 (EV_KEY), code 330 (BTN_TOUCH), value 1
Event: time 1406710046.697616, -------------- SYN_REPORT ------------
Event: time 1406710046.713550, type 3 (EV_ABS), code 0 (ABS_X), value 3287
Event: time 1406710046.713550, type 3 (EV_ABS), code 1 (ABS_Y), value 2539
[ Many other EV_ABS events]
Event: time 1406710047.317556, type 3 (EV_ABS), code 0 (ABS_X), value 3755
Event: time 1406710047.317556, type 3 (EV_ABS), code 1 (ABS_Y), value 1699
Event: time 1406710047.317556, -------------- SYN_REPORT ------------
Event: time 1406710047.340556, type 4 (EV_MSC), code 4 (MSC_SCAN), value d0042
Event: time 1406710047.340556, type 1 (EV_KEY), code 330 (BTN_TOUCH), value 0
Event: time 1406710047.340556, -------------- SYN_REPORT ------------
Event: time 1406710047.342504, type 1 (EV_KEY), code 320 (BTN_TOOL_PEN), value 0
Event: time 1406710047.342504, -------------- SYN_REPORT ------------

Thanks again for the patch,

        Éric Brunet
--
To unsubscribe from this list: send the line "unsubscribe linux-input" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply

* -- Re: Very Urgent............
From: Fabian Morision @ 2014-07-30 23:27 UTC (permalink / raw)


Greetings from gulf region

Thanks for the e-mail. I am very interested on funding lucrative
business partnership with you acting as the manager and sole
controller of the investment while i remain a silent investor for a
period of ten yrs , though I am only looking at investment
opportunities within the range you specified for a start. You can
reply me here (fmorision@yahoo.com)

Let me know your thought asap

Regards

Financial Consultant

Mr.Fabian Morision

^ permalink raw reply

* Kredit-Angebot
From: ROYAL ASSURED LOANS @ 2014-07-30 23:40 UTC (permalink / raw)




-- 
Wir bieten privaten und gewerblichen Darlehen ohne Sicherheiten (nur 
Identifikation) bei 3% Zinssatz, ab € 10.000 bis € 90.000.000 in 1 Jahr 
bis 20 Jahren Laufzeit überall in der Welt.

Anna.
--
To unsubscribe from this list: send the line "unsubscribe linux-input" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply

* Re: [Patch v4] input: drv260x: Add TI drv260x haptics driver
From: Dmitry Torokhov @ 2014-07-31  5:44 UTC (permalink / raw)
  To: Dan Murphy
  Cc: linux-input, linux-kernel, devicetree, mark.rutland, madcatxster,
	simon, elias.vds
In-Reply-To: <1406729680-3591-1-git-send-email-dmurphy@ti.com>

Hi Dan,

On Wed, Jul 30, 2014 at 09:14:40AM -0500, Dan Murphy wrote:
> Add the TI drv260x haptics/vibrator driver.
> This device uses the input force feedback
> to produce a wave form to driver an
> ERM or LRA actuator device.
> 
> The initial driver supports the devices
> real time playback mode.  But the device
> has additional wave patterns in ROM.
> 
> This functionality will be added in
> future patchsets.
> 
> Product data sheet is located here:
> http://www.ti.com/product/drv2605
> 
> Signed-off-by: Dan Murphy <dmurphy@ti.com>
> ---
> 
> v4 - Convert regulator to devm, added error checking where required,
> updated bindings doc, moved dt parsing to separate call and made platform
> data the first data point, moved vibrator enable and mode programming from
> play entry to worker thread, added user check and input mutex in suspend/resume
> calls, moved platform data to individual file and into include platform-data directory,
> removed file names from file headers - https://patchwork.kernel.org/patch/4642221/
> v3 - Updated binding doc, changed to memless device, updated input alloc to
> devm, removed mutex locking, add sanity checks for mode and library - https://patchwork.kernel.org/patch/4635421/
> v2 - Fixed binding doc and patch headline - https://patchwork.kernel.org/patch/4619641/

Thank you for making changes, just a few more small nits from me and if
Mark is OK with the bindings then I will merge it.

...

> +
> +
> +	if (haptics->mode < DRV260X_LRA_MODE ||
> +		haptics->mode > DRV260X_ERM_MODE) {
> +			dev_err(&client->dev,
> +				"Vibrator mode is invalid: %i\n",
> +				haptics->mode);
> +			return -EINVAL;

Indentation for the body is wrong here.

> +	}
> +	
> +	if (haptics->library < DRV260X_LIB_SEL_DEFAULT ||
> +		haptics->library > DRV260X_LIB_F) {
> +			dev_err(&client->dev,
> +				"Library value is invalid: %i\n", haptics->library);
> +			return -EINVAL;

Here as well

> +	}	
> +
> +	haptics->regulator = devm_regulator_get(&client->dev, "vbat");
> +	if (IS_ERR(haptics->regulator)) {
> +		ret = PTR_ERR(haptics->regulator);
> +		dev_err(&client->dev,
> +			"unable to get regulator, error: %d\n", ret);
> +		goto err_regulator;

Here and below you can return directly from the probe function, there is
no need keeping "empty" labels.

> +	}
> +
> +	haptics->enable_gpio = devm_gpiod_get(&client->dev, "enable");
> +	if (IS_ERR(haptics->enable_gpio)) {
> +		ret = PTR_ERR(haptics->enable_gpio);
> +		if (ret != -ENOENT && ret != -ENOSYS)
> +			goto err_gpio;
> +
> +		haptics->enable_gpio = NULL;
> +	} else {
> +		gpiod_direction_output(haptics->enable_gpio, 1);
> +	}
> +
> +	haptics->input_dev = devm_input_allocate_device(&client->dev);
> +	if (haptics->input_dev == NULL) {
> +		dev_err(&client->dev, "Failed to allocate input device\n");
> +		ret = -ENOMEM;
> +		goto err_input_alloc;
> +	}
> +
> +	haptics->input_dev->name = "drv260x:haptics";
> +	haptics->input_dev->dev.parent = client->dev.parent;
> +	haptics->input_dev->close = drv260x_close;
> +	input_set_drvdata(haptics->input_dev, haptics);
> +	input_set_capability(haptics->input_dev, EV_FF, FF_RUMBLE);
> +
> +	ret = input_ff_create_memless(haptics->input_dev, NULL,
> +				      drv260x_haptics_play);
> +	if (ret < 0) {
> +		dev_err(&client->dev, "input_ff_create() failed: %d\n",
> +			ret);
> +		goto err_ff_create;
> +	}
> +
> +	INIT_WORK(&haptics->work, drv260x_worker);
> +
> +	haptics->client = client;
> +	i2c_set_clientdata(client, haptics);
> +
> +	haptics->regmap = devm_regmap_init_i2c(client, &drv260x_regmap_config);
> +	if (IS_ERR(haptics->regmap)) {
> +		ret = PTR_ERR(haptics->regmap);
> +		dev_err(&client->dev, "Failed to allocate register map: %d\n",
> +			ret);
> +		goto err_regmap;
> +	}
> +
> +	drv260x_init(haptics);

I believe we should abort if init fails.

> +
> +	ret = input_register_device(haptics->input_dev);
> +	if (ret < 0) {
> +		dev_err(&client->dev, "couldn't register input device: %d\n",
> +			ret);
> +		goto err_iff;
> +	}
> +	return 0;
> +
> +err_iff:
> +err_regmap:
> +err_ff_create:
> +err_input_alloc:
> +err_gpio:
> +err_regulator:
> +	return ret;
> +}
> +
> +static int drv260x_remove(struct i2c_client *client)
> +{
> +	struct drv260x_data *haptics = i2c_get_clientdata(client);
> +
> +	cancel_work_sync(&haptics->work);
> +
> +	regmap_write(haptics->regmap, DRV260X_MODE, DRV260X_STANDBY);
> +	gpiod_set_value(haptics->enable_gpio, 0);

Since you provide close() method and you do not activate device in
resme() if there are no users you do not need to do this here. In fact
entire remove() can be dropped now.

> +
> +	return 0;
> +}
> +
> +#ifdef CONFIG_PM_SLEEP
> +static int drv260x_suspend(struct device *dev)
> +{
> +	struct drv260x_data *haptics = dev_get_drvdata(dev);
> +	int ret = 0;
> +
> +	mutex_lock(&haptics->input_dev->mutex);
> +
> +	if (haptics->input_dev->users) {
> +		ret = regmap_update_bits(haptics->regmap,
> +				   DRV260X_MODE,
> +				   DRV260X_STANDBY_MASK,
> +				   DRV260X_STANDBY);
> +		if (ret) {
> +			dev_err(dev, "Failed to set standby mode\n");
> +			goto out;
> +		}
> +
> +		gpiod_set_value(haptics->enable_gpio, 0);
> +
> +		ret = regulator_disable(haptics->regulator);
> +		if (ret)
> +			dev_err(dev, "Failed to disable regulator\n");

Maybe factor this out into drv260x_stop()?

> +	}
> +out:
> +	mutex_unlock(&haptics->input_dev->mutex);
> +	return ret;
> +}
> +
> +static int drv260x_resume(struct device *dev)
> +{
> +	struct drv260x_data *haptics = dev_get_drvdata(dev);
> +	int ret = 0;
> +
> +	mutex_lock(&haptics->input_dev->mutex);
> +
> +	if (haptics->input_dev->users) {
> +		ret = regulator_enable(haptics->regulator);
> +		if (ret) {
> +			dev_err(dev, "Failed to enable regulator\n");
> +			goto out;
> +		}
> +		ret = regmap_update_bits(haptics->regmap,
> +				   DRV260X_MODE,
> +				   DRV260X_STANDBY_MASK, 0);
> +		if (ret) {
> +			dev_err(dev, "Failed to unset standby mode\n");
> +			goto out;
> +		}
> +
> +		gpiod_set_value(haptics->enable_gpio, 1);
> +	}
> +
> +out:
> +	mutex_unlock(&haptics->input_dev->mutex);
> +	return ret;
> +}
> +#endif
> +
> +static SIMPLE_DEV_PM_OPS(drv260x_pm_ops, drv260x_suspend, drv260x_resume);
> +
> +static const struct i2c_device_id drv260x_id[] = {
> +	{ "drv2605l", 0 },
> +	{ }
> +};
> +MODULE_DEVICE_TABLE(i2c, drv260x_id);
> +
> +#if IS_ENABLED(CONFIG_OF)
> +static const struct of_device_id drv260x_of_match[] = {
> +	{ .compatible = "ti,drv2604", },
> +	{ .compatible = "ti,drv2605", },
> +	{ .compatible = "ti,drv2605l", },
> +	{}
> +};
> +MODULE_DEVICE_TABLE(of, drv260x_of_match);
> +#endif
> +
> +static struct i2c_driver drv260x_driver = {
> +	.probe		= drv260x_probe,
> +	.remove		= drv260x_remove,
> +	.driver		= {
> +		.name	= "drv260x-haptics",
> +		.owner	= THIS_MODULE,
> +		.of_match_table = of_match_ptr(drv260x_of_match),
> +		.pm	= &drv260x_pm_ops,
> +	},
> +	.id_table = drv260x_id,
> +};
> +module_i2c_driver(drv260x_driver);
> +
> +MODULE_ALIAS("platform:drv260x-haptics");
> +MODULE_DESCRIPTION("TI DRV260x haptics driver");
> +MODULE_LICENSE("GPL");
> +MODULE_AUTHOR("Dan Murphy <dmurphy@ti.com>");
> diff --git a/include/dt-bindings/input/ti-drv260x.h b/include/dt-bindings/input/ti-drv260x.h
> new file mode 100644
> index 0000000..be7f245
> --- /dev/null
> +++ b/include/dt-bindings/input/ti-drv260x.h
> @@ -0,0 +1,35 @@
> +/*
> + * DRV260X haptics driver family
> + *
> + * Author: Dan Murphy <dmurphy@ti.com>
> + *
> + * Copyright:   (C) 2014 Texas Instruments, Inc.
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License 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.
> + */
> +
> +#ifndef _DT_BINDINGS_TI_DRV260X_H
> +#define _DT_BINDINGS_TI_DRV260X_H
> +
> +/* Calibration Types */
> +#define DRV260X_LRA_MODE		0x00
> +#define DRV260X_LRA_NO_CAL_MODE	0x01
> +#define DRV260X_ERM_MODE		0x02
> +
> +/* Library Selection */
> +#define DRV260X_LIB_SEL_DEFAULT		0x00
> +#define DRV260X_LIB_A				0x01
> +#define DRV260X_LIB_B				0x02
> +#define DRV260X_LIB_C				0x03
> +#define DRV260X_LIB_D				0x04
> +#define DRV260X_LIB_E				0x05
> +#define DRV260X_LIB_F				0x06
> +
> +#endif
> diff --git a/include/linux/input/drv260x.h b/include/linux/input/drv260x.h
> new file mode 100644
> index 0000000..85fdaa4
> --- /dev/null
> +++ b/include/linux/input/drv260x.h
> @@ -0,0 +1,173 @@
> +/*
> + * DRV260X haptics driver family
> + *
> + * Author: Dan Murphy <dmurphy@ti.com>
> + *
> + * Copyright:   (C) 2014 Texas Instruments, Inc.
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License 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.
> + */
> +
> +#ifndef _LINUX_DRV260X_I2C_H
> +#define _LINUX_DRV260X_I2C_H
> +
> +#define DRV260X_STATUS		0x0
> +#define DRV260X_MODE		0x1
> +#define DRV260X_RT_PB_IN	0x2
> +#define DRV260X_LIB_SEL		0x3
> +#define DRV260X_WV_SEQ_1	0x4
> +#define DRV260X_WV_SEQ_2	0x5
> +#define DRV260X_WV_SEQ_3	0x6
> +#define DRV260X_WV_SEQ_4	0x7
> +#define DRV260X_WV_SEQ_5	0x8
> +#define DRV260X_WV_SEQ_6	0x9
> +#define DRV260X_WV_SEQ_7	0xa
> +#define DRV260X_WV_SEQ_8	0xb
> +#define DRV260X_GO				0xc
> +#define DRV260X_OVERDRIVE_OFF	0xd
> +#define DRV260X_SUSTAIN_P_OFF	0xe
> +#define DRV260X_SUSTAIN_N_OFF	0xf
> +#define DRV260X_BRAKE_OFF		0x10
> +#define DRV260X_A_TO_V_CTRL		0x11
> +#define DRV260X_A_TO_V_MIN_INPUT	0x12
> +#define DRV260X_A_TO_V_MAX_INPUT	0x13
> +#define DRV260X_A_TO_V_MIN_OUT	0x14
> +#define DRV260X_A_TO_V_MAX_OUT	0x15
> +#define DRV260X_RATED_VOLT		0x16
> +#define DRV260X_OD_CLAMP_VOLT	0x17
> +#define DRV260X_CAL_COMP		0x18
> +#define DRV260X_CAL_BACK_EMF	0x19
> +#define DRV260X_FEEDBACK_CTRL	0x1a
> +#define DRV260X_CTRL1			0x1b
> +#define DRV260X_CTRL2			0x1c
> +#define DRV260X_CTRL3			0x1d
> +#define DRV260X_CTRL4			0x1e
> +#define DRV260X_CTRL5			0x1f
> +#define DRV260X_LRA_LOOP_PERIOD	0x20
> +#define DRV260X_VBAT_MON		0x21
> +#define DRV260X_LRA_RES_PERIOD	0x22
> +#define DRV260X_MAX_REG			0x23
> +
> +#define DRV260X_ALLOWED_R_BYTES	25
> +#define DRV260X_ALLOWED_W_BYTES	2
> +#define DRV260X_MAX_RW_RETRIES	5
> +#define DRV260X_I2C_RETRY_DELAY 10
> +
> +#define DRV260X_GO_BIT				0x01
> +
> +/* Library Selection */
> +#define DRV260X_LIB_SEL_MASK		0x07
> +#define DRV260X_LIB_SEL_RAM			0x0
> +#define DRV260X_LIB_SEL_OD			0x1
> +#define DRV260X_LIB_SEL_40_60		0x2
> +#define DRV260X_LIB_SEL_60_80		0x3
> +#define DRV260X_LIB_SEL_100_140		0x4
> +#define DRV260X_LIB_SEL_140_PLUS	0x5
> +
> +#define DRV260X_LIB_SEL_HIZ_MASK	0x10
> +#define DRV260X_LIB_SEL_HIZ_EN		0x01
> +#define DRV260X_LIB_SEL_HIZ_DIS		0
> +
> +/* Mode register */
> +#define DRV260X_STANDBY				(1 << 6)
> +#define DRV260X_STANDBY_MASK		0x40
> +#define DRV260X_INTERNAL_TRIGGER	0x00
> +#define DRV260X_EXT_TRIGGER_EDGE	0x01
> +#define DRV260X_EXT_TRIGGER_LEVEL	0x02
> +#define DRV260X_PWM_ANALOG_IN		0x03
> +#define DRV260X_AUDIOHAPTIC			0x04
> +#define DRV260X_RT_PLAYBACK			0x05
> +#define DRV260X_DIAGNOSTICS			0x06
> +#define DRV260X_AUTO_CAL			0x07
> +
> +/* Audio to Haptics Control */
> +#define DRV260X_AUDIO_HAPTICS_PEAK_10MS		(0 << 2)
> +#define DRV260X_AUDIO_HAPTICS_PEAK_20MS		(1 << 2)
> +#define DRV260X_AUDIO_HAPTICS_PEAK_30MS		(2 << 2)
> +#define DRV260X_AUDIO_HAPTICS_PEAK_40MS		(3 << 2)
> +
> +#define DRV260X_AUDIO_HAPTICS_FILTER_100HZ	0x00
> +#define DRV260X_AUDIO_HAPTICS_FILTER_125HZ	0x01
> +#define DRV260X_AUDIO_HAPTICS_FILTER_150HZ	0x02
> +#define DRV260X_AUDIO_HAPTICS_FILTER_200HZ	0x03
> +
> +/* Min/Max Input/Output Voltages */
> +#define DRV260X_AUDIO_HAPTICS_MIN_IN_VOLT	0x19
> +#define DRV260X_AUDIO_HAPTICS_MAX_IN_VOLT	0x64
> +#define DRV260X_AUDIO_HAPTICS_MIN_OUT_VOLT	0x19
> +#define DRV260X_AUDIO_HAPTICS_MAX_OUT_VOLT	0xFF
> +
> +/* Feedback register */
> +#define DRV260X_FB_REG_ERM_MODE			0x7f
> +#define DRV260X_FB_REG_LRA_MODE			(1 << 7)
> +
> +#define DRV260X_BRAKE_FACTOR_MASK	0x1f
> +#define DRV260X_BRAKE_FACTOR_2X		(1 << 0)
> +#define DRV260X_BRAKE_FACTOR_3X		(2 << 4)
> +#define DRV260X_BRAKE_FACTOR_4X		(3 << 4)
> +#define DRV260X_BRAKE_FACTOR_6X		(4 << 4)
> +#define DRV260X_BRAKE_FACTOR_8X		(5 << 4)
> +#define DRV260X_BRAKE_FACTOR_16		(6 << 4)
> +#define DRV260X_BRAKE_FACTOR_DIS	(7 << 4)
> +
> +#define DRV260X_LOOP_GAIN_LOW		0xf3
> +#define DRV260X_LOOP_GAIN_MED		(1 << 2)
> +#define DRV260X_LOOP_GAIN_HIGH		(2 << 2)
> +#define DRV260X_LOOP_GAIN_VERY_HIGH	(3 << 2)
> +
> +#define DRV260X_BEMF_GAIN_0			0xfc
> +#define DRV260X_BEMF_GAIN_1		(1 << 0)
> +#define DRV260X_BEMF_GAIN_2		(2 << 0)
> +#define DRV260X_BEMF_GAIN_3		(3 << 0)
> +
> +/* Control 1 register */
> +#define DRV260X_AC_CPLE_EN			(1 << 5)
> +#define DRV260X_STARTUP_BOOST		(1 << 7)
> +
> +/* Control 2 register */
> +
> +#define DRV260X_IDISS_TIME_45		0
> +#define DRV260X_IDISS_TIME_75		(1 << 0)
> +#define DRV260X_IDISS_TIME_150		(1 << 1)
> +#define DRV260X_IDISS_TIME_225		0x03
> +
> +#define DRV260X_BLANK_TIME_45	(0 << 2)
> +#define DRV260X_BLANK_TIME_75	(1 << 2)
> +#define DRV260X_BLANK_TIME_150	(2 << 2)
> +#define DRV260X_BLANK_TIME_225	(3 << 2)
> +
> +#define DRV260X_SAMP_TIME_150	(0 << 4)
> +#define DRV260X_SAMP_TIME_200	(1 << 4)
> +#define DRV260X_SAMP_TIME_250	(2 << 4)
> +#define DRV260X_SAMP_TIME_300	(3 << 4)
> +
> +#define DRV260X_BRAKE_STABILIZER	(1 << 6)
> +#define DRV260X_UNIDIR_IN			(0 << 7)
> +#define DRV260X_BIDIR_IN			(1 << 7)
> +
> +/* Control 3 Register */
> +#define DRV260X_LRA_OPEN_LOOP		(1 << 0)
> +#define DRV260X_ANANLOG_IN			(1 << 1)
> +#define DRV260X_LRA_DRV_MODE		(1 << 2)
> +#define DRV260X_RTP_UNSIGNED_DATA	(1 << 3)
> +#define DRV260X_SUPPLY_COMP_DIS		(1 << 4)
> +#define DRV260X_ERM_OPEN_LOOP		(1 << 5)
> +#define DRV260X_NG_THRESH_0			(0 << 6)
> +#define DRV260X_NG_THRESH_2			(1 << 6)
> +#define DRV260X_NG_THRESH_4			(2 << 6)
> +#define DRV260X_NG_THRESH_8			(3 << 6)
> +
> +/* Control 4 Register */
> +#define DRV260X_AUTOCAL_TIME_150MS		(0 << 4)
> +#define DRV260X_AUTOCAL_TIME_250MS		(1 << 4)
> +#define DRV260X_AUTOCAL_TIME_500MS		(2 << 4)
> +#define DRV260X_AUTOCAL_TIME_1000MS		(3 << 4)

Hmm, are all these defines interesting to anyone but driver itself?
Maybe put majority of them into the driver code (and what is needed to
properly define platform data into drv260x-pdata.h?

Thanks!

-- 
Dmitry

^ permalink raw reply

* Re: [PATCH 2/2] Input: mcs5000_ts - Remove ifdef
From: Dmitry Torokhov @ 2014-07-31  6:01 UTC (permalink / raw)
  To: Fabio Estevam; +Cc: beomho.seo, linux-input, Fabio Estevam
In-Reply-To: <1406737039-1404-2-git-send-email-festevam@gmail.com>

On Wed, Jul 30, 2014 at 01:17:19PM -0300, Fabio Estevam wrote:
> From: Fabio Estevam <fabio.estevam@freescale.com>
> 
> We can annonate the suspend/resume functions with '__maybe_unused' and get rid
> of the ifdef, which makes the code smaller and simpler.
> 
> Signed-off-by: Fabio Estevam <fabio.estevam@freescale.com>

Applied both, thank you.

> ---
>  drivers/input/touchscreen/mcs5000_ts.c | 6 ++----
>  1 file changed, 2 insertions(+), 4 deletions(-)
> 
> diff --git a/drivers/input/touchscreen/mcs5000_ts.c b/drivers/input/touchscreen/mcs5000_ts.c
> index 1fb760c..8b47e1f 100644
> --- a/drivers/input/touchscreen/mcs5000_ts.c
> +++ b/drivers/input/touchscreen/mcs5000_ts.c
> @@ -248,8 +248,7 @@ static int mcs5000_ts_probe(struct i2c_client *client,
>  	return 0;
>  }
>  
> -#ifdef CONFIG_PM_SLEEP
> -static int mcs5000_ts_suspend(struct device *dev)
> +static int __maybe_unused mcs5000_ts_suspend(struct device *dev)
>  {
>  	struct i2c_client *client = to_i2c_client(dev);
>  
> @@ -259,7 +258,7 @@ static int mcs5000_ts_suspend(struct device *dev)
>  	return 0;
>  }
>  
> -static int mcs5000_ts_resume(struct device *dev)
> +static int __maybe_unused mcs5000_ts_resume(struct device *dev)
>  {
>  	struct i2c_client *client = to_i2c_client(dev);
>  	struct mcs5000_ts_data *data = i2c_get_clientdata(client);
> @@ -269,7 +268,6 @@ static int mcs5000_ts_resume(struct device *dev)
>  
>  	return 0;
>  }
> -#endif
>  
>  static SIMPLE_DEV_PM_OPS(mcs5000_ts_pm, mcs5000_ts_suspend, mcs5000_ts_resume);
>  
> -- 
> 1.9.1
> 

-- 
Dmitry

^ permalink raw reply

* Re: [PATCH] input: ads7846: Release resources on failure for clean exit
From: Dmitry Torokhov @ 2014-07-31  6:01 UTC (permalink / raw)
  To: Pramod Gurav
  Cc: linux-input, linux-kernel, Pramod Gurav, Lejun Zhu, Sachin Kamat
In-Reply-To: <1406699677-1347-1-git-send-email-pramod.gurav.etc@gmail.com>

On Wed, Jul 30, 2014 at 11:24:37AM +0530, Pramod Gurav wrote:
> From: Pramod Gurav <pramod.gurav@smartplayin.com>
> 
> Input device must be released(input_free_device) when ads7846_probe_dt
> fails. This fixes the same by releasing resources on failure.
> 
> CC: Dmitry Torokhov <dmitry.torokhov@gmail.com>
> CC: Lejun Zhu <lejun.zhu@linux.intel.com>
> CC: Sachin Kamat <sachin.kamat@linaro.org>
> 
> Signed-off-by: Pramod Gurav <pramod.gurav@smartplayin.com>

Applied, thank you.

> ---
>  drivers/input/touchscreen/ads7846.c |    6 ++++--
>  1 file changed, 4 insertions(+), 2 deletions(-)
> 
> diff --git a/drivers/input/touchscreen/ads7846.c b/drivers/input/touchscreen/ads7846.c
> index da201b8..e57ba52 100644
> --- a/drivers/input/touchscreen/ads7846.c
> +++ b/drivers/input/touchscreen/ads7846.c
> @@ -1302,8 +1302,10 @@ static int ads7846_probe(struct spi_device *spi)
>  	pdata = dev_get_platdata(&spi->dev);
>  	if (!pdata) {
>  		pdata = ads7846_probe_dt(&spi->dev);
> -		if (IS_ERR(pdata))
> -			return PTR_ERR(pdata);
> +		if (IS_ERR(pdata)) {
> +			err = PTR_ERR(pdata);
> +			goto err_free_mem;
> +		}
>  	}
>  
>  	ts->model = pdata->model ? : 7846;
> -- 
> 1.7.9.5
> 

-- 
Dmitry

^ permalink raw reply

* Re: PATCH hid: Implement mode switching on Logitech gaming wheels accordingly to the documentation
From: Elias Vanderstuyft @ 2014-07-31  9:20 UTC (permalink / raw)
  To: Michal Malý
  Cc: Jiri Kosina, linux-input, linux-kernel@vger.kernel.org,
	Edwin Velds, simon@mungewell.org, Roland Bosa
In-Reply-To: <3335238.KGDcEM6DjH@sigyn>

On Wed, Jul 30, 2014 at 12:10 PM, Michal Malý
<madcatxster@devoid-pointer.net> wrote:
> Implement mode switching on Logitech gaming wheels accordingly to the documentation
>
> Signed-off-by: Michal Malý <madcatxster@devoid-pointer.net>

Tested all lg4ff_switch_force_mode
modes (0, 1, 2, 3, default), on both:
- MOMO (Black) : PID 0xCA03
- Formula Vibration : PID 0xCA04

Behaviour didn't deviate from normal, which is as expected because
these devices don't use the native versus compatibility mode,
according to the documentation.
So I consider this patch successful for these devices:

Tested-by: Elias Vanderstuyft <elias.vds@gmail.com>

Elias
--
To unsubscribe from this list: send the line "unsubscribe linux-input" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply

* re: HID: huion: Switch to generating report descriptor
From: Dan Carpenter @ 2014-07-31  9:40 UTC (permalink / raw)
  To: spbnick; +Cc: linux-input

Hello Nikolai Kondrashov,

The patch f8dd5cb2c6d0: "HID: huion: Switch to generating report
descriptor" from Jul 23, 2014, leads to the following static checker
warning:

	drivers/hid/hid-huion.c:126 huion_tablet_enable()
	error: doing dma on the stack (buf)

drivers/hid/hid-huion.c
   113  static int huion_tablet_enable(struct hid_device *hdev)
   114  {
   115          int rc;
   116          struct usb_device *usb_dev = hid_to_usb_dev(hdev);
   117          struct huion_drvdata *drvdata = hid_get_drvdata(hdev);
   118          __le16 buf[6];
   119  
   120          /*
   121           * Read string descriptor containing tablet parameters. The specific
   122           * string descriptor and data were discovered by sniffing the Windows
   123           * driver traffic.
   124           * NOTE: This enables fully-functional tablet mode.
   125           */
   126          rc = usb_control_msg(usb_dev, usb_rcvctrlpipe(usb_dev, 0),
   127                                  USB_REQ_GET_DESCRIPTOR, USB_DIR_IN,
   128                                  (USB_DT_STRING << 8) + 0x64,
   129                                  0x0409, buf, sizeof(buf),
                                                ^^^
   130                                  USB_CTRL_GET_TIMEOUT);

regards,
dan carpenter

^ permalink raw reply

* [PATCH] input: Add ROHM BU21023/24 Dual touch support resistive touchscreens
From: Yoichi Yuasa @ 2014-07-31 10:54 UTC (permalink / raw)
  To: Dmitry Torokhov; +Cc: yuasa, linux-input

Signed-off-by: Yoichi Yuasa <yuasa@linux-mips.org>
---
 drivers/input/touchscreen/Kconfig        |   11 +
 drivers/input/touchscreen/Makefile       |    1 +
 drivers/input/touchscreen/rohm_bu21023.c |  791 ++++++++++++++++++++++++++++++
 drivers/input/touchscreen/rohm_bu21023.h |  255 ++++++++++
 4 files changed, 1058 insertions(+)
 create mode 100644 drivers/input/touchscreen/rohm_bu21023.c
 create mode 100644 drivers/input/touchscreen/rohm_bu21023.h

diff --git a/drivers/input/touchscreen/Kconfig b/drivers/input/touchscreen/Kconfig
index 6bb9a7d..9ae5c88 100644
--- a/drivers/input/touchscreen/Kconfig
+++ b/drivers/input/touchscreen/Kconfig
@@ -950,4 +950,15 @@ config TOUCHSCREEN_ZFORCE
 	  To compile this driver as a module, choose M here: the
 	  module will be called zforce_ts.
 
+config TOUCHSCREEN_ROHM_BU21023
+	tristate "ROHM BU21023/24 Dual touch support resistive touchscreens"
+	depends on I2C
+	help
+	  Say Y here if you have a touchscreen using ROHM BU21023/24.
+
+	  If unsure, say N.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called bu21023_ts.
+
 endif
diff --git a/drivers/input/touchscreen/Makefile b/drivers/input/touchscreen/Makefile
index 4be94fc..af766d0 100644
--- a/drivers/input/touchscreen/Makefile
+++ b/drivers/input/touchscreen/Makefile
@@ -52,6 +52,7 @@ obj-$(CONFIG_TOUCHSCREEN_USB_COMPOSITE)	+= usbtouchscreen.o
 obj-$(CONFIG_TOUCHSCREEN_PCAP)		+= pcap_ts.o
 obj-$(CONFIG_TOUCHSCREEN_PENMOUNT)	+= penmount.o
 obj-$(CONFIG_TOUCHSCREEN_PIXCIR)	+= pixcir_i2c_ts.o
+obj-$(CONFIG_TOUCHSCREEN_ROHM_BU21023)	+= rohm_bu21023.o
 obj-$(CONFIG_TOUCHSCREEN_S3C2410)	+= s3c2410_ts.o
 obj-$(CONFIG_TOUCHSCREEN_ST1232)	+= st1232.o
 obj-$(CONFIG_TOUCHSCREEN_STMPE)		+= stmpe-ts.o
diff --git a/drivers/input/touchscreen/rohm_bu21023.c b/drivers/input/touchscreen/rohm_bu21023.c
new file mode 100644
index 0000000..317d59b
--- /dev/null
+++ b/drivers/input/touchscreen/rohm_bu21023.c
@@ -0,0 +1,791 @@
+/*
+ * ROHM BU21023/24 Dual touch support resistive touch screen driver
+ * Copyright (C) 2012 ROHM CO.,LTD.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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.
+ */
+#include <linux/delay.h>
+#include <linux/firmware.h>
+#include <linux/hrtimer.h>
+#include <linux/i2c.h>
+#include <linux/input.h>
+#include <linux/interrupt.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+
+#include "rohm_bu21023.h"
+
+struct rohm_ts_point {
+	unsigned int x;
+	unsigned int y;
+};
+
+struct rohm_ts_data {
+	struct i2c_client *client;
+	struct input_dev *input_dev;
+
+	int use_irq;
+	struct hrtimer hrtimer;
+	struct workqueue_struct *workqueue;
+	struct work_struct work;
+
+	unsigned int untouch_count;
+	unsigned int single_touch_count;
+	unsigned int dual_touch_count;
+	unsigned int prev_touch_report;
+};
+
+/*
+ * rohm_i2c_burst_read - execute combined I2C message for ROHM BU21023/24
+ * @adap: Handle to I2C bus
+ * @msgs: combined messages to execute
+ * @num: Number of messages to be executed.
+ *
+ * Returns negative errno, else the number of messages executed.
+ *
+ * Note
+ * In BU21023/24 burst read, stop condition is needed after "address write".
+ * Therefore, transmission is performed in 2 steps.
+ */
+static inline int rohm_i2c_burst_read(struct i2c_adapter *adap,
+				      struct i2c_msg *msgs, int num)
+{
+	int ret, i;
+
+	if (!adap->algo->master_xfer) {
+		dev_dbg(&adap->dev, "I2C level transfers not supported\n");
+		return -EOPNOTSUPP;
+	}
+
+	i2c_lock_adapter(adap);
+
+	for (i = 0; i < num; i++) {
+		ret = __i2c_transfer(adap, &msgs[i], 1);
+		if (ret < 0)
+			break;
+
+		ret = i;
+	}
+
+	i2c_unlock_adapter(adap);
+
+	return ret;
+}
+
+static int rohm_ts_manual_calibration(struct rohm_ts_data *ts)
+{
+	struct i2c_client *client = ts->client;
+	struct device *dev = &client->dev;
+	struct i2c_msg msg[2];
+	u8 buf[33];
+	u8 addr_buf;		/* burst read start address */
+
+	int retry;
+	bool success = false;
+	bool first_time = true;
+	bool calibration_done;
+
+	u8 reg1, reg2, reg3;
+	u8 reg1_orig, reg2_orig, reg3_orig;
+
+	s32 val;
+
+	int calib_x = 0, calib_y = 0;
+	int reg_x, reg_y;
+	int err_x, err_y;
+
+	int ret;
+	int i;
+
+	addr_buf = PRM1_X_H;
+	msg[0].addr = client->addr;
+	msg[0].flags = 0;
+	msg[0].len = 1;
+	msg[0].buf = &addr_buf;
+
+	msg[1].addr = client->addr;
+	msg[1].flags = I2C_M_RD;
+	msg[1].len = sizeof(buf);
+	msg[1].buf = buf;
+
+#define READ_CALIB_BUF(reg)	((u16)buf[((reg) - PRM1_X_H)])
+
+	reg1_orig = i2c_smbus_read_byte_data(client, CALIBRATION_REG1);
+	reg2_orig = i2c_smbus_read_byte_data(client, CALIBRATION_REG2);
+	reg3_orig = i2c_smbus_read_byte_data(client, CALIBRATION_REG3);
+
+	if (ts->use_irq)
+		disable_irq(client->irq);
+
+	i2c_smbus_write_byte_data(client, INT_MASK,
+				  COORD_UPDATE | SLEEP_IN | SLEEP_OUT |
+				  PROGRAM_LOAD_DONE);
+
+	i2c_smbus_write_byte_data(client, TEST1, DUALTOUCH_STABILIZE_ON);
+
+	for (retry = 0; retry < CALIBRATION_RETRY_MAX; retry++) {
+		/* wait 2 sampling for update */
+		mdelay(2 * SAMPLING_DELAY);
+
+		ret = rohm_i2c_burst_read(client->adapter, msg, 2);
+		if (!ret) {
+			dev_err(dev, "I2C transfer error\n");
+			return ret;
+		}
+
+		if (READ_CALIB_BUF(TOUCH) & TOUCH_DETECT)
+			continue;
+
+		if (first_time) {
+			/* generate calibration parameter */
+			calib_x =
+			    (READ_CALIB_BUF(PRM1_X_H) << 2 |
+			     READ_CALIB_BUF(PRM1_X_L)) - AXIS_OFFSET;
+			calib_y =
+			    (READ_CALIB_BUF(PRM1_Y_H) << 2 |
+			     READ_CALIB_BUF(PRM1_Y_L)) - AXIS_OFFSET;
+
+			i2c_smbus_write_byte_data(client, TEST1,
+						  DUALTOUCH_STABILIZE_ON |
+						  DUALTOUCH_REG_ON);
+
+			first_time = false;
+		} else {
+			/* generate adjustment parameter */
+			err_x = READ_CALIB_BUF(PRM1_X_H) << 2 |
+			    READ_CALIB_BUF(PRM1_X_L);
+			err_y = READ_CALIB_BUF(PRM1_Y_H) << 2 |
+			    READ_CALIB_BUF(PRM1_Y_L);
+
+			/* X axis ajust */
+			if (err_x <= 4)
+				calib_x -= AXIS_ADJUST;
+			else if (err_x >= 60)
+				calib_x += AXIS_ADJUST;
+
+			/* Y axis ajust */
+			if (err_y <= 4)
+				calib_y -= AXIS_ADJUST;
+			else if (err_y >= 60)
+				calib_y += AXIS_ADJUST;
+		}
+
+		/* generate calibration setting value */
+		reg_x = calib_x + ((calib_x & 0x200) << 1);
+		reg_y = calib_y + ((calib_y & 0x200) << 1);
+
+		/* convert for register format */
+		reg1 = reg_x >> 3;
+		reg2 = (reg_y & 0x7) << 4 | (reg_x & 0x7);
+		reg3 = reg_y >> 3;
+
+		i2c_smbus_write_byte_data(client, CALIBRATION_REG1, reg1);
+		i2c_smbus_write_byte_data(client, CALIBRATION_REG2, reg2);
+		i2c_smbus_write_byte_data(client, CALIBRATION_REG3, reg3);
+
+		/*
+		 * force calibration sequcence
+		 */
+		i2c_smbus_write_byte_data(client, FORCE_CALIBRATION,
+					  FORCE_CALIBRATION_OFF);
+
+		i2c_smbus_write_byte_data(client, FORCE_CALIBRATION,
+					  FORCE_CALIBRATION_ON);
+
+		/* clear all interrupts */
+		i2c_smbus_write_byte_data(client, INT_CLEAR, 0xff);
+
+		/*
+		 * Wait for the status change of calibration, max 10 sampling
+		 */
+		calibration_done = false;
+
+		for (i = 0; i < 10; i++) {
+			mdelay(SAMPLING_DELAY);
+
+			val = i2c_smbus_read_byte_data(client, TOUCH_GESTURE);
+			if (!(val & CALIBRATION_MASK)) {
+				calibration_done = true;
+				break;
+			}
+		}
+
+		if (calibration_done) {
+			val = i2c_smbus_read_byte_data(client, INT_STATUS);
+			if (val == CALIBRATION_DONE) {
+				success = true;
+				break;
+			}
+		} else
+			dev_warn(dev, "Calibration timeout\n");
+	}
+
+	if (!success) {
+		i2c_smbus_write_byte_data(client, CALIBRATION_REG1, reg1_orig);
+		i2c_smbus_write_byte_data(client, CALIBRATION_REG2, reg2_orig);
+		i2c_smbus_write_byte_data(client, CALIBRATION_REG3, reg3_orig);
+
+		/* calibration data enable */
+		i2c_smbus_write_byte_data(client, TEST1,
+					  DUALTOUCH_STABILIZE_ON |
+					  DUALTOUCH_REG_ON);
+
+		/* wait 10 sampling */
+		mdelay(10 * SAMPLING_DELAY);
+
+		dev_warn(dev, "Failed to manual calibration\n");
+		ret = -EBUSY;
+	} else {
+		dev_dbg(dev, "Manual calibration done\n");
+		ret = 0;
+	}
+
+	i2c_smbus_write_byte_data(client, INT_MASK,
+				  COORD_UPDATE | CALIBRATION_DONE |
+				  SLEEP_IN | SLEEP_OUT |
+				  PROGRAM_LOAD_DONE | ERROR);
+
+	/* Clear all interrupts */
+	i2c_smbus_write_byte_data(client, INT_CLEAR, 0xff);
+
+	if (ts->use_irq)
+		enable_irq(client->irq);
+
+	return ret;
+}
+
+static unsigned long inactive_polling_interval[2] = { 1, 0 };
+static unsigned long active_polling_interval[2] = { 0, 10000000 };
+
+module_param_array(inactive_polling_interval, ulong, NULL, S_IRUGO | S_IWUSR);
+module_param_array(active_polling_interval, ulong, NULL, S_IRUGO | S_IWUSR);
+
+MODULE_PARM_DESC(inactive_polling_interval,
+		 "Polling interval for un-touch detection");
+MODULE_PARM_DESC(active_polling_interval,
+		 "Polling interval for touch detection");
+
+#define INACTIVE_POLLING_INTERVAL_KTIME	\
+	ktime_set(inactive_polling_interval[0], inactive_polling_interval[1])
+#define ACTIVE_POLLING_INTERVAL_KTIME	\
+	ktime_set(active_polling_interval[0], active_polling_interval[1])
+
+static unsigned int untouch_threshold[3] = { 0, 1, 5 };
+static unsigned int single_touch_threshold[3] = { 0, 0, 4 };
+static unsigned int dual_touch_threshold[3] = { 10, 8, 0 };
+
+module_param_array(untouch_threshold, uint, NULL, S_IRUGO | S_IWUSR);
+module_param_array(single_touch_threshold, uint, NULL, S_IRUGO | S_IWUSR);
+module_param_array(dual_touch_threshold, uint, NULL, S_IRUGO | S_IWUSR);
+
+MODULE_PARM_DESC(untouch_threshold, "Thresholds for un-touch detection");
+MODULE_PARM_DESC(single_touch_threshold,
+		 "Thresholds for single touch detection");
+MODULE_PARM_DESC(dual_touch_threshold, "Thresholds for dual touch detection");
+
+static void rohm_ts_work_func(struct work_struct *work)
+{
+	struct rohm_ts_data *ts = container_of(work, struct rohm_ts_data, work);
+	struct i2c_client *client = ts->client;
+	struct input_dev *input_dev = ts->input_dev;
+	struct device *dev = &client->dev;
+	struct i2c_msg msg[2];
+	u16 addr = client->addr;
+	u8 addr_buf;
+	u8 buf[10];		/* for 0x20-0x29 */
+
+	struct rohm_ts_point tp1, tp2;
+	u8 touch_flags;
+	unsigned int threshold;
+	int touch_report = -1;
+	unsigned int prev_touch_report = ts->prev_touch_report;
+	bool next_sensing_immediately = false;
+
+	int ret;
+
+	i2c_smbus_write_byte_data(client, INT_MASK,
+				  COORD_UPDATE | CALIBRATION_DONE |
+				  SLEEP_OUT | SLEEP_IN | PROGRAM_LOAD_DONE |
+				  ERROR);
+
+	/* Clear all interrupts */
+	i2c_smbus_write_byte_data(client, INT_CLEAR, 0xff);
+
+	addr_buf = POS_X1_H;
+	msg[0].addr = addr;
+	msg[0].flags = 0;
+	msg[0].len = 1;
+	msg[0].buf = &addr_buf;
+
+	msg[1].addr = addr;
+	msg[1].flags = I2C_M_RD;
+	msg[1].len = sizeof(buf);
+	msg[1].buf = buf;
+
+#define READ_POS_BUF(reg)	((u16)buf[((reg) - POS_X1_H)])
+
+	ret = rohm_i2c_burst_read(client->adapter, msg, 2);
+	if (ret < 0) {
+		dev_err(dev, "I2C transfer error\n");
+		return;
+	}
+
+	touch_flags = READ_POS_BUF(TOUCH_GESTURE) & TOUCH_MASK;
+	if (touch_flags) {
+		next_sensing_immediately = true;
+
+		/* generate coordinates */
+		tp1.x = (READ_POS_BUF(POS_X1_H) << 2) | READ_POS_BUF(POS_X1_L);
+		tp1.y = (READ_POS_BUF(POS_Y1_H) << 2) | READ_POS_BUF(POS_Y1_L);
+		tp2.x = (READ_POS_BUF(POS_X2_H) << 2) | READ_POS_BUF(POS_X2_L);
+		tp2.y = (READ_POS_BUF(POS_Y2_H) << 2) | READ_POS_BUF(POS_Y2_L);
+
+		switch (touch_flags) {
+		case SINGLE_TOUCH:
+			ts->untouch_count = 0;
+			ts->single_touch_count++;
+			ts->dual_touch_count = 0;
+
+			threshold = single_touch_threshold[prev_touch_report];
+			if (ts->single_touch_count > threshold)
+				touch_report = 1;
+
+			if (touch_report == 1) {
+				if (tp2.x != 0 && tp2.y != 0) {
+					tp1.x = tp2.x;
+					tp1.y = tp2.y;
+					tp2.x = 0;
+					tp2.y = 0;
+				}
+			}
+			break;
+		case DUAL_TOUCH:
+			ts->untouch_count = 0;
+			ts->single_touch_count = 0;
+			ts->dual_touch_count++;
+
+			threshold = dual_touch_threshold[prev_touch_report];
+			if (ts->dual_touch_count > threshold)
+				touch_report = 2;
+			break;
+		default:
+			dev_err(dev,
+				"Three or more touches are not supported\n");
+			break;
+		}
+	} else {
+		ts->untouch_count++;
+
+		threshold = untouch_threshold[prev_touch_report];
+		if (ts->untouch_count > threshold) {
+			ts->untouch_count = 0;
+			ts->single_touch_count = 0;
+			ts->dual_touch_count = 0;
+
+			touch_report = 0;
+		} else if (prev_touch_report > 0) {
+			next_sensing_immediately = true;
+		}
+	}
+
+	switch (touch_report) {
+	case 0:
+		input_mt_sync(input_dev);
+
+		input_sync(input_dev);
+		ts->prev_touch_report = touch_report;
+		break;
+	case 1:
+		input_report_abs(input_dev, ABS_MT_POSITION_X, tp1.x);
+		input_report_abs(input_dev, ABS_MT_POSITION_Y, tp1.y);
+		input_mt_sync(input_dev);
+
+		input_sync(input_dev);
+		ts->prev_touch_report = touch_report;
+		break;
+	case 2:
+		input_report_abs(input_dev, ABS_MT_POSITION_X, tp1.x);
+		input_report_abs(input_dev, ABS_MT_POSITION_Y, tp1.y);
+		input_mt_sync(input_dev);
+		input_report_abs(input_dev, ABS_MT_POSITION_X, tp2.x);
+		input_report_abs(input_dev, ABS_MT_POSITION_Y, tp2.y);
+		input_mt_sync(input_dev);
+
+		input_sync(input_dev);
+		ts->prev_touch_report = touch_report;
+		break;
+	default:
+		break;
+	}
+
+	/* set next sensing time */
+	if (next_sensing_immediately) {
+		hrtimer_start(&ts->hrtimer,
+			      ACTIVE_POLLING_INTERVAL_KTIME, HRTIMER_MODE_REL);
+	} else {
+		if (READ_POS_BUF(TOUCH_GESTURE) & CALIBRATION_REQUEST)
+			rohm_ts_manual_calibration(ts);
+
+		if (ts->use_irq) {
+			i2c_smbus_write_byte_data(client, INT_MASK,
+						  CALIBRATION_DONE |
+						  SLEEP_OUT | SLEEP_IN |
+						  PROGRAM_LOAD_DONE);
+		} else {
+			hrtimer_start(&ts->hrtimer,
+				      INACTIVE_POLLING_INTERVAL_KTIME,
+				      HRTIMER_MODE_REL);
+		}
+	}
+}
+
+static enum hrtimer_restart rohm_ts_timer_func(struct hrtimer *hrtimer)
+{
+	struct rohm_ts_data *ts =
+	    container_of(hrtimer, struct rohm_ts_data, hrtimer);
+
+	queue_work(ts->workqueue, &ts->work);
+
+	return HRTIMER_NORESTART;
+}
+
+static irqreturn_t rohm_ts_irq_handler(int irq, void *dev_id)
+{
+	struct rohm_ts_data *ts = dev_id;
+
+	queue_work(ts->workqueue, &ts->work);
+
+	return IRQ_HANDLED;
+}
+
+static int rohm_ts_load_firmware(struct i2c_client *client,
+				 const char *firmware_name)
+{
+	struct device *dev = &client->dev;
+	const struct firmware *firmware = NULL;
+	s32 status;
+	int blocks, remainder, retry, offset;
+	int ret;
+	int i;
+
+	ret = request_firmware(&firmware, firmware_name, dev);
+	if (ret) {
+		dev_err(dev, "Unable to open firmware %s\n", firmware_name);
+		return ret;
+	}
+
+	blocks = firmware->size / FIRMWARE_BLOCK_SIZE;
+	remainder = firmware->size % FIRMWARE_BLOCK_SIZE;
+
+	i2c_smbus_write_byte_data(client, INT_MASK,
+				  COORD_UPDATE | CALIBRATION_DONE |
+				  SLEEP_IN | SLEEP_OUT);
+
+	for (retry = 0; retry < FIRMWARE_RETRY_MAX; retry++) {
+		i2c_smbus_write_byte_data(client, COMMON_SETUP1,
+					  COMMON_SETUP1_DEFAULT);
+		i2c_smbus_write_byte_data(client, EX_ADDR_H, 0);
+		i2c_smbus_write_byte_data(client, EX_ADDR_L, 0);
+
+		offset = 0;
+
+		/* firmware load to the device */
+		for (i = 0; i < blocks; i++) {
+			i2c_smbus_write_i2c_block_data(client, EX_WDAT,
+						       FIRMWARE_BLOCK_SIZE,
+						       &firmware->data[offset]);
+			offset += FIRMWARE_BLOCK_SIZE;
+		}
+
+		if (remainder)
+			i2c_smbus_write_i2c_block_data(client, EX_WDAT,
+						       remainder,
+						       &firmware->data[offset]);
+
+		/* check formware load result */
+		status = i2c_smbus_read_byte_data(client, INT_STATUS);
+
+		/* clear all interrupts */
+		i2c_smbus_write_byte_data(client, INT_CLEAR, 0xff);
+
+		if (status == PROGRAM_LOAD_DONE)
+			break;
+
+		dev_warn(dev, "Retry firmware load\n");
+
+		/* settings for retry */
+		i2c_smbus_write_byte_data(client, EX_WDAT, 0);
+	}
+
+	i2c_smbus_write_byte_data(client, INT_MASK,
+				  COORD_UPDATE | CALIBRATION_DONE |
+				  SLEEP_IN | SLEEP_OUT |
+				  PROGRAM_LOAD_DONE | ERROR);
+
+	release_firmware(firmware);
+
+	if (retry >= FIRMWARE_RETRY_MAX)
+		return -EBUSY;
+
+	return 0;
+}
+
+static bool swap_xy;
+module_param(swap_xy, bool, S_IRUGO);
+MODULE_PARM_DESC(swap_xy, "Swap X-axis and Y-axis");
+
+static bool inv_x;
+module_param(inv_x, bool, S_IRUGO);
+MODULE_PARM_DESC(inv_x, "Invert X-axis");
+
+static bool inv_y;
+module_param(inv_y, bool, S_IRUGO);
+MODULE_PARM_DESC(inv_y, "Invert Y-axis");
+
+static int rohm_ts_device_init(struct i2c_client *client)
+{
+	u8 val;
+	int ret;
+
+	/*
+	 * Wait 200usec for reset
+	 */
+	udelay(200);
+
+	/* Release analog reset */
+	ret = i2c_smbus_write_byte_data(client, SYSTEM, ANALOG_POWER_ON);
+	if (ret < 0)
+		return ret;
+
+	/* Waiting for the analog warm-up, max. 200usec */
+	udelay(200);
+
+	/* clear all interrupts */
+	i2c_smbus_write_byte_data(client, INT_CLEAR, 0xff);
+
+	val = MAF_1SAMPLE;
+	if (swap_xy)
+		val |= SWAP_XY;
+	if (inv_x)
+		val |= INV_X;
+	if (inv_y)
+		val |= INV_Y;
+
+	i2c_smbus_write_byte_data(client, EX_WDAT, 0);
+	i2c_smbus_write_byte_data(client, COMMON_SETUP1, 0);
+	i2c_smbus_write_byte_data(client, COMMON_SETUP2, val);
+	i2c_smbus_write_byte_data(client, COMMON_SETUP3,
+				  SEL_TBL_DEFAULT | EN_GESTURE | EN_MULTI);
+	i2c_smbus_write_byte_data(client, THRESHOLD_GESTURE,
+				  THRESHOLD_GESTURE_DEFAULT);
+
+	i2c_smbus_write_byte_data(client, INTERVAL_TIME, INTERVAL_TIME_DEFAULT);
+	i2c_smbus_write_byte_data(client, CPU_FREQ, CPU_FREQ_10MHZ);
+	i2c_smbus_write_byte_data(client, PRM_SWOFF_TIME,
+				  PRM_SWOFF_TIME_DEFAULT);
+	i2c_smbus_write_byte_data(client, ADC_CTRL, ADC_DIV_DEFAULT);
+	i2c_smbus_write_byte_data(client, ADC_WAIT, ADC_WAIT_DEFAULT);
+
+	/*
+	 * Panel setup, these values change with the panel.
+	 */
+	i2c_smbus_write_byte_data(client, STEP_X, STEP_X_DEFAULT);
+	i2c_smbus_write_byte_data(client, STEP_Y, STEP_Y_DEFAULT);
+	i2c_smbus_write_byte_data(client, OFFSET_X, OFFSET_X_DEFAULT);
+	i2c_smbus_write_byte_data(client, OFFSET_Y, OFFSET_Y_DEFAULT);
+	i2c_smbus_write_byte_data(client, THRESHOLD_TOUCH,
+				  THRESHOLD_TOUCH_DEFAULT);
+	i2c_smbus_write_byte_data(client, EVR_XY, EVR_XY_DEFAULT);
+	i2c_smbus_write_byte_data(client, EVR_X, EVR_X_DEFAULT);
+	i2c_smbus_write_byte_data(client, EVR_Y, EVR_Y_DEFAULT);
+
+	/* Fixed value settings */
+	i2c_smbus_write_byte_data(client, CALIBRATION_ADJUST,
+				  CALIBRATION_ADJUST_DEFAULT);
+	i2c_smbus_write_byte_data(client, SWCONT, SWCONT_DEFAULT);
+	i2c_smbus_write_byte_data(client, TEST1,
+				  DUALTOUCH_STABILIZE_ON | DUALTOUCH_REG_ON);
+
+	ret = rohm_ts_load_firmware(client, BU21023_FIRMWARE_NAME);
+	if (ret) {
+		dev_err(&client->dev, "Failed to firmware load\n");
+		return ret;
+	}
+
+	/*
+	 * Manual calibration results are not changed in same environment.
+	 * If the force calibration is performed,
+	 * the controller will not require calibration request interrupt
+	 * when the typical values are set to the calibration registers.
+	 */
+	i2c_smbus_write_byte_data(client, CALIBRATION_REG1,
+				  CALIBRATION_REG1_DEFAULT);
+	i2c_smbus_write_byte_data(client, CALIBRATION_REG2,
+				  CALIBRATION_REG2_DEFAULT);
+	i2c_smbus_write_byte_data(client, CALIBRATION_REG3,
+				  CALIBRATION_REG3_DEFAULT);
+
+	i2c_smbus_write_byte_data(client, FORCE_CALIBRATION,
+				  FORCE_CALIBRATION_OFF);
+	i2c_smbus_write_byte_data(client, FORCE_CALIBRATION,
+				  FORCE_CALIBRATION_ON);
+
+	/* Clear all interrupts */
+	i2c_smbus_write_byte_data(client, INT_CLEAR, 0xff);
+
+	/* Enable coordinates update interrupt */
+	i2c_smbus_write_byte_data(client, INT_MASK,
+				  CALIBRATION_DONE | SLEEP_OUT | SLEEP_IN |
+				  PROGRAM_LOAD_DONE);
+
+	i2c_smbus_write_byte_data(client, ERR_MASK,
+				  PROGRAM_LOAD_ERR | CPU_TIMEOUT | ADC_TIMEOUT);
+
+	/* controller CPU power on */
+	i2c_smbus_write_byte_data(client, SYSTEM,
+				  CPU_POWER_ON | ANALOG_POWER_ON);
+
+	return 0;
+}
+
+static int rohm_bu21023_i2c_probe(struct i2c_client *client,
+				  const struct i2c_device_id *id)
+{
+	struct rohm_ts_data *ts;
+	struct device *dev = &client->dev;
+	struct input_dev *input_dev;
+	int ret;
+
+	ts = kzalloc(sizeof(struct rohm_ts_data), GFP_KERNEL);
+	if (!ts)
+		return -ENOMEM;
+
+	INIT_WORK(&ts->work, rohm_ts_work_func);
+
+	ts->client = client;
+	i2c_set_clientdata(client, ts);
+
+	ret = rohm_ts_device_init(client);
+	if (ret) {
+		dev_err(dev, "Failed to device initialization\n");
+		goto err_ts_free_exit;
+	}
+
+	input_dev = input_allocate_device();
+	if (!input_dev) {
+		ret = -ENOMEM;
+		dev_err(dev, "Failed to allocate input device\n");
+		goto err_ts_free_exit;
+	}
+
+	input_dev->name = BU21023_NAME;
+	input_dev->id.bustype = BUS_I2C;
+	input_dev->dev.parent = &client->dev;
+
+	ts->input_dev = input_dev;
+
+	set_bit(EV_SYN, input_dev->evbit);
+	set_bit(EV_KEY, input_dev->evbit);
+	set_bit(EV_ABS, input_dev->evbit);
+
+	input_set_abs_params(input_dev, ABS_MT_POSITION_X,
+			     ROHM_TS_ABS_X_MIN, ROHM_TS_ABS_X_MAX, 0, 0);
+	input_set_abs_params(input_dev, ABS_MT_POSITION_Y,
+			     ROHM_TS_ABS_Y_MIN, ROHM_TS_ABS_Y_MAX, 0, 0);
+
+	ret = input_register_device(input_dev);
+	if (ret) {
+		dev_err(dev, "Unable to register input device\n");
+		goto err_input_free_device_exit;
+	}
+
+	ts->workqueue = create_singlethread_workqueue("rohm_ts_wq");
+	if (!ts->workqueue) {
+		dev_err(dev, "Cannot create workqueue\n");
+		ret = -ENOMEM;
+		goto err_input_unregister_device_exit;
+	}
+
+	if (client->irq) {
+		ret = request_irq(client->irq, rohm_ts_irq_handler,
+				  IRQF_TRIGGER_FALLING, client->name, ts);
+
+		/* If the request IRQ failed, use polling mode */
+		if (!ret)
+			ts->use_irq = 1;
+	}
+
+	hrtimer_init(&ts->hrtimer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
+	ts->hrtimer.function = rohm_ts_timer_func;
+	if (!ts->use_irq)
+		hrtimer_start(&ts->hrtimer, INACTIVE_POLLING_INTERVAL_KTIME,
+			      HRTIMER_MODE_REL);
+
+	dev_info(dev, "%s mode\n", ts->use_irq ? "interrupt" : "polling");
+
+	return 0;
+
+err_input_unregister_device_exit:
+	input_unregister_device(input_dev);
+
+err_input_free_device_exit:
+	input_free_device(input_dev);
+
+err_ts_free_exit:
+	kfree(ts);
+
+	return ret;
+}
+
+static int rohm_bu21023_i2c_remove(struct i2c_client *client)
+{
+	struct rohm_ts_data *ts = i2c_get_clientdata(client);
+
+	if (ts->use_irq)
+		free_irq(client->irq, ts);
+	else
+		hrtimer_cancel(&ts->hrtimer);
+
+	if (ts->workqueue)
+		destroy_workqueue(ts->workqueue);
+
+	input_unregister_device(ts->input_dev);
+
+	i2c_smbus_write_byte_data(client, SYSTEM,
+				  ANALOG_POWER_ON | CPU_POWER_OFF);
+
+	i2c_smbus_write_byte_data(client, SYSTEM,
+				  ANALOG_POWER_OFF | CPU_POWER_OFF);
+
+	kfree(ts);
+
+	return 0;
+}
+
+static const struct i2c_device_id rohm_bu21023_i2c_id[] = {
+	{BU21023_NAME, 0},
+	{},
+};
+
+MODULE_DEVICE_TABLE(i2c, rohm_bu21023_i2c_id);
+
+static struct i2c_driver rohm_bu21023_i2c_driver = {
+	.driver = {
+		   .name = BU21023_NAME,
+		   },
+	.probe = rohm_bu21023_i2c_probe,
+	.remove = rohm_bu21023_i2c_remove,
+	.id_table = rohm_bu21023_i2c_id,
+};
+
+module_i2c_driver(rohm_bu21023_i2c_driver);
+
+MODULE_DESCRIPTION("ROHM BU21023/24 Touchscreen driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/input/touchscreen/rohm_bu21023.h b/drivers/input/touchscreen/rohm_bu21023.h
new file mode 100644
index 0000000..2db32848
--- /dev/null
+++ b/drivers/input/touchscreen/rohm_bu21023.h
@@ -0,0 +1,255 @@
+/*
+ * ROHM BU21023/24 Dual touch support resistive touch screen driver
+ * Copyright (C) 2012 ROHM CO.,LTD.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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.
+ */
+#ifndef _BU21023_TS_H
+#define _BU21023_TS_H
+
+#define BU21023_NAME		"bu21023_ts"
+#define BU21023_FIRMWARE_NAME	"bu21023.bin"
+
+#define AXIS_ADJUST		4
+#define AXIS_OFFSET		8
+
+#define FIRMWARE_BLOCK_SIZE	32
+#define FIRMWARE_RETRY_MAX	4
+
+#define SAMPLING_DELAY		12	/* msec */
+
+#define CALIBRATION_RETRY_MAX	6
+
+#define ROHM_TS_ABS_X_MIN	40
+#define ROHM_TS_ABS_X_MAX	990
+#define ROHM_TS_ABS_Y_MIN	160
+#define ROHM_TS_ABS_Y_MAX	920
+
+/*
+ * BU21023GUL/BU21023MUV/BU21024FV-M registers map
+ */
+#define VADOUT_YP_H		0x00
+#define VADOUT_YP_L		0x01
+#define VADOUT_XP_H		0x02
+#define VADOUT_XP_L		0x03
+#define VADOUT_YN_H		0x04
+#define VADOUT_YN_L		0x05
+#define VADOUT_XN_H		0x06
+#define VADOUT_XN_L		0x07
+
+#define PRM1_X_H		0x08
+#define PRM1_X_L		0x09
+#define PRM1_Y_H		0x0a
+#define PRM1_Y_L		0x0b
+#define PRM2_X_H		0x0c
+#define PRM2_X_L		0x0d
+#define PRM2_Y_H		0x0e
+#define PRM2_Y_L		0x0f
+
+#define MLT_PRM_MONI_X		0x10
+#define MLT_PRM_MONI_Y		0x11
+
+#define DEBUG_MONI_1		0x12
+#define DEBUG_MONI_2		0x13
+
+#define VADOUT_ZX_H		0x14
+#define VADOUT_ZX_L		0x15
+#define VADOUT_ZY_H		0x16
+#define VADOUT_ZY_L		0x17
+
+#define Z_PARAM_H		0x18
+#define Z_PARAM_L		0x19
+
+/*
+ * Value for VADOUT_*_L
+ */
+#define VADOUT_L_MASK		0x01
+
+/*
+ * Value for PRM*_*_L
+ */
+#define PRM_L_MASK		0x01
+
+#define POS_X1_H		0x20
+#define POS_X1_L		0x21
+#define POS_Y1_H		0x22
+#define POS_Y1_L		0x23
+#define POS_X2_H		0x24
+#define POS_X2_L		0x25
+#define POS_Y2_H		0x26
+#define POS_Y2_L		0x27
+
+/*
+ * Value for POS_*_L
+ */
+#define POS_L_MASK		0x01
+
+#define TOUCH			0x28
+#define TOUCH_DETECT		0x01
+
+#define TOUCH_GESTURE		0x29
+#define SINGLE_TOUCH		0x01
+#define DUAL_TOUCH		0x03
+#define TOUCH_MASK		0x03
+#define CALIBRATION_REQUEST	0x04
+#define CALIBRATION_STATUS	0x08
+#define CALIBRATION_MASK	0x0c
+#define GESTURE_SPREAD		0x10
+#define GESTURE_PINCH		0x20
+#define GESTURE_ROTATE_R	0x40
+#define GESTURE_ROTATE_L	0x80
+
+#define INT_STATUS		0x2a
+#define INT_MASK		0x3d
+#define INT_CLEAR		0x3e
+
+/*
+ * Values for INT_*
+ */
+#define COORD_UPDATE		0x01
+#define CALIBRATION_DONE	0x02
+#define SLEEP_IN		0x04
+#define SLEEP_OUT		0x08
+#define PROGRAM_LOAD_DONE	0x10
+#define ERROR			0x80
+
+#define ERR_STATUS		0x2b
+#define ERR_MASK		0x3f
+
+/*
+ * Values for ERR_*
+ */
+#define ADC_TIMEOUT		0x01
+#define CPU_TIMEOUT		0x02
+#define CALIBRATION_ERR		0x04
+#define PROGRAM_LOAD_ERR	0x10
+
+#define COMMON_SETUP1			0x30
+#define PROGRAM_LOAD_HOST		0x02
+#define PROGRAM_LOAD_EEPROM		0x03
+#define CENSOR_4PORT			0x04
+#define CENSOR_8PORT			0x00	/* Not supported by BU21023 */
+#define CALIBRATION_TYPE_DEFAULT	0x08
+#define CALIBRATION_TYPE_SPECIAL	0x00
+#define INT_ACTIVE_HIGH			0x10
+#define INT_ACTIVE_LOW			0x00
+#define AUTO_CALIBRATION		0x40
+#define MANUAL_CALIBRATION		0x00
+#define COMMON_SETUP1_DEFAULT		0x4e
+
+#define COMMON_SETUP2		0x31
+#define MAF_NONE		0x00
+#define MAF_1SAMPLE		0x01
+#define MAF_3SAMPLES		0x02
+#define MAF_5SAMPLES		0x03
+#define INV_Y			0x04
+#define INV_X			0x08
+#define SWAP_XY			0x10
+
+#define COMMON_SETUP3		0x32
+#define EN_SLEEP		0x01
+#define EN_MULTI		0x02
+#define EN_GESTURE		0x04
+#define EN_INTVL		0x08
+#define SEL_STEP		0x10
+#define SEL_MULTI		0x20
+#define SEL_TBL_DEFAULT		0x40
+
+#define INTERVAL_TIME		0x33
+#define INTERVAL_TIME_DEFAULT	0x10
+
+#define STEP_X			0x34
+#define STEP_X_DEFAULT		0x41
+
+#define STEP_Y			0x35
+#define STEP_Y_DEFAULT		0x8d
+
+#define OFFSET_X		0x38
+#define OFFSET_X_DEFAULT	0x0c
+
+#define OFFSET_Y		0x39
+#define OFFSET_Y_DEFAULT	0x0c
+
+#define THRESHOLD_TOUCH		0x3a
+#define THRESHOLD_TOUCH_DEFAULT	0xa0
+
+#define THRESHOLD_GESTURE		0x3b
+#define THRESHOLD_GESTURE_DEFAULT	0x17
+
+#define SYSTEM			0x40
+#define ANALOG_POWER_ON		0x01
+#define ANALOG_POWER_OFF	0x00
+#define CPU_POWER_ON		0x02
+#define CPU_POWER_OFF		0x00
+
+#define FORCE_CALIBRATION	0x42
+#define FORCE_CALIBRATION_ON	0x01
+#define FORCE_CALIBRATION_OFF	0x00
+
+#define CPU_FREQ		0x50	/* 10 / (reg + 1) MHz */
+#define CPU_FREQ_10MHZ		0x00
+#define CPU_FREQ_5MHZ		0x01
+#define CPU_FREQ_1MHZ		0x09
+
+#define EEPROM_ADDR		0x51
+
+#define CALIBRATION_ADJUST		0x52
+#define CALIBRATION_ADJUST_DEFAULT	0x00
+
+#define THRESHOLD_SLEEP_IN	0x53
+
+#define EVR_XY			0x56
+#define EVR_XY_DEFAULT		0x10
+
+#define PRM_SWOFF_TIME		0x57
+#define PRM_SWOFF_TIME_DEFAULT	0x04
+
+#define PROGRAM_VERSION		0x5f
+
+#define ADC_CTRL		0x60
+#define ADC_DIV_MASK		0x1f	/* The minimum value is 4 */
+#define ADC_DIV_DEFAULT		0x08
+
+#define ADC_WAIT		0x61
+#define ADC_WAIT_DEFAULT	0x0a
+
+#define SWCONT			0x62
+#define SWCONT_DEFAULT		0x0f
+
+#define EVR_X			0x63
+#define EVR_X_DEFAULT		0x86
+
+#define EVR_Y			0x64
+#define EVR_Y_DEFAULT		0x64
+
+#define TEST1			0x65
+#define DUALTOUCH_STABILIZE_ON	0x01
+#define DUALTOUCH_STABILIZE_OFF	0x00
+#define DUALTOUCH_REG_ON	0x20
+#define DUALTOUCH_REG_OFF	0x00
+
+#define CALIBRATION_REG1		0x68
+#define CALIBRATION_REG1_DEFAULT	0xd9
+
+#define CALIBRATION_REG2		0x69
+#define CALIBRATION_REG2_DEFAULT	0x36
+
+#define CALIBRATION_REG3		0x6a
+#define CALIBRATION_REG3_DEFAULT	0x32
+
+#define EX_ADDR_H		0x70
+#define EX_ADDR_L		0x71
+#define EX_WDAT			0x72
+#define EX_RDAT			0x73
+#define EX_CHK_SUM1		0x74
+#define EX_CHK_SUM2		0x75
+#define EX_CHK_SUM3		0x76
+
+#endif /* _BU21023_TS_H */
-- 
1.7.9.5


^ permalink raw reply related


This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox