Linux Input/HID development
 help / color / mirror / Atom feed
* Re: Re: [PATCH 0/7] mfd: AXP20x: Add support for AXP202 and AXP209
From: Hans de Goede @ 2014-03-06 11:05 UTC (permalink / raw)
  To: linux-sunxi-/JYPxA39Uh5TLH3MbocFFw
  Cc: linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r, Maxime Ripard,
	emilio-0Z03zUJReD5OxF6Tv1QG9Q, wens Tsai,
	sameo-VuQAYsv1563Yd54FQh9/CA, Lee Jones,
	devicetree-u79uwXL29TY76Z2rM5mHXA, Dmitry Torokhov,
	linux-input-u79uwXL29TY76Z2rM5mHXA,
	linux-doc-u79uwXL29TY76Z2rM5mHXA, Liam Girdwood,
	broonie-DgEjT+Ai2ygdnm+yROfE0A
In-Reply-To: <CAOQ7t2aVchEe0Mtbp8KV4ZTzE_0C0BeO15S7aF4nJx6PaBno=w@mail.gmail.com>

Hi,

On 03/06/2014 11:10 AM, Carlo Caione wrote:
> On Thu, Mar 6, 2014 at 11:07 AM, Hans de Goede <hdegoede-H+wXaHxf7aLQT0dZR+AlfA@public.gmane.org> wrote:
>> Hi,
>>
>> On 03/05/2014 09:46 PM, Carlo Caione wrote:
>>> On Wed, Mar 5, 2014 at 9:02 PM, Hans de Goede <hdegoede-H+wXaHxf7aLQT0dZR+AlfA@public.gmane.org> wrote:
>>>>
>>>> So I've added your patches to my sunxi-devel tree and they work as advertised :)
>>>
>>> Thank you for keeping the tree updated :)
>>>
>>>> I've also added dt support for the axp209 to the cubietruck dts file, to avoid
>>>> things becoming a copy and paste fest I've introduced a new axp209.dtsi file,
>>>> as well as did some fixes to the existing sun?i-a*.dtsi files, see my tree here:
>>>> https://github.com/jwrdegoede/linux-sunxi/commits/sunxi-devel
>>>
>>> That was also my idea (creating a new axp20x.dtsi) but it has been
>>> turned down by Maxime and Lee
>>
>> Hmm, do you have a pointer to the mailinglist discussion about this ?
> 
> Sure, http://www.spinics.net/lists/arm-kernel/msg306497.html

Ah, yes but that is before you added all the regulators, which are a fixed part
of the chip. Once you get long dts bits like that having a dtsi file makes a lot
of sense IMHO. Note that the part of the dts this was discussing is actually the part
I did not put in the dtis, since it can vary per board :)

Regards,

Hans

^ permalink raw reply

* Re: Re: [PATCH 0/7] mfd: AXP20x: Add support for AXP202 and AXP209
From: Carlo Caione @ 2014-03-06 11:32 UTC (permalink / raw)
  To: Hans De Goede
  Cc: linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r, Maxime Ripard,
	emilio-0Z03zUJReD5OxF6Tv1QG9Q, wens Tsai,
	sameo-VuQAYsv1563Yd54FQh9/CA, Lee Jones,
	devicetree-u79uwXL29TY76Z2rM5mHXA, Dmitry Torokhov,
	linux-input-u79uwXL29TY76Z2rM5mHXA,
	linux-doc-u79uwXL29TY76Z2rM5mHXA, Liam Girdwood,
	broonie-DgEjT+Ai2ygdnm+yROfE0A,
	linux-sunxi-/JYPxA39Uh5TLH3MbocFFw
In-Reply-To: <5318568E.4040405-H+wXaHxf7aLQT0dZR+AlfA@public.gmane.org>

On Thu, Mar 6, 2014 at 12:05 PM, Hans de Goede <hdegoede-H+wXaHxf7aLQT0dZR+AlfA@public.gmane.org> wrote:
>
> Ah, yes but that is before you added all the regulators, which are a fixed part
> of the chip. Once you get long dts bits like that having a dtsi file makes a lot
> of sense IMHO. Note that the part of the dts this was discussing is actually the part
> I did not put in the dtis, since it can vary per board :)

Oook, now it makes sense. The idea was to integrate axp20x.dtsi at a
later time, but probably I was misunderstood. Thank you for clarifying
it.
I'll put axp20x.dtsi back in v2.

Thanks,

-- 
Carlo Caione

^ permalink raw reply

* [PATCH v3] mfd: max8997: use regmap to access registers
From: Robert Baldyga @ 2014-03-06 11:57 UTC (permalink / raw)
  To: sameo, lee.jones
  Cc: myungjoo.ham, cw00.choi, dmitry.torokhov, cooloney, rpurdie,
	dbaryshkov, dwmw2, lgirdwood, broonie, a.zummo, paul.gortmaker,
	linux-kernel, linux-input, linux-leds, rtc-linux, m.szyprowski,
	k.kozlowski, Robert Baldyga

This patch modifies max8997 driver and each associated function driver,
to use regmap instead of operating directly on i2c bus. It will allow to
simplify IRQ handling using regmap-irq.

Signed-off-by: Robert Baldyga <r.baldyga@samsung.com>
---

Changelog:

v3:
- remove unused variables
- use regmap_* functions to access registers in register map
- remove unused function get_i2c()

v2: http://www.spinics.net/lists/kernel/msg1700520.html
- check devm_regmap_init_i2c() return value
- use proper register maps for muic and haptic registers in functions
  max8997_freeze() and max8997_restore()
- fix style problems

v1: http://www.spinics.net/lists/kernel/msg1700375.html

 drivers/extcon/extcon-max8997.c     |   31 ++++----
 drivers/input/misc/max8997_haptic.c |   34 +++++----
 drivers/leds/leds-max8997.c         |   13 ++--
 drivers/mfd/max8997-irq.c           |   64 ++++++----------
 drivers/mfd/max8997.c               |  143 ++++++++++++++++-------------------
 drivers/power/max8997_charger.c     |   33 ++++----
 drivers/regulator/max8997.c         |   87 ++++++++++-----------
 drivers/rtc/rtc-max8997.c           |   56 ++++++++------
 include/linux/mfd/max8997-private.h |   17 ++---
 9 files changed, 229 insertions(+), 249 deletions(-)

diff --git a/drivers/extcon/extcon-max8997.c b/drivers/extcon/extcon-max8997.c
index 6a00464..f258c08 100644
--- a/drivers/extcon/extcon-max8997.c
+++ b/drivers/extcon/extcon-max8997.c
@@ -27,6 +27,7 @@
 #include <linux/mfd/max8997-private.h>
 #include <linux/extcon.h>
 #include <linux/irqdomain.h>
+#include <linux/regmap.h>
 
 #define	DEV_NAME			"max8997-muic"
 #define	DELAY_MS_DEFAULT		20000		/* unit: millisecond */
@@ -116,7 +117,7 @@ enum max8997_muic_charger_type {
 
 struct max8997_muic_info {
 	struct device *dev;
-	struct i2c_client *muic;
+	struct max8997_dev *max8997;
 	struct extcon_dev *edev;
 	int prev_cable_type;
 	int prev_chg_type;
@@ -190,10 +191,10 @@ static int max8997_muic_set_debounce_time(struct max8997_muic_info *info,
 	case ADC_DEBOUNCE_TIME_10MS:
 	case ADC_DEBOUNCE_TIME_25MS:
 	case ADC_DEBOUNCE_TIME_38_62MS:
-		ret = max8997_update_reg(info->muic,
+		ret = regmap_update_bits(info->max8997->regmap_muic,
 					  MAX8997_MUIC_REG_CONTROL3,
-					  time << CONTROL3_ADCDBSET_SHIFT,
-					  CONTROL3_ADCDBSET_MASK);
+					  CONTROL3_ADCDBSET_MASK,
+					  time << CONTROL3_ADCDBSET_SHIFT);
 		if (ret) {
 			dev_err(info->dev, "failed to set ADC debounce time\n");
 			return ret;
@@ -228,8 +229,8 @@ static int max8997_muic_set_path(struct max8997_muic_info *info,
 	else
 		ctrl1 = CONTROL1_SW_OPEN;
 
-	ret = max8997_update_reg(info->muic,
-			MAX8997_MUIC_REG_CONTROL1, ctrl1, COMP_SW_MASK);
+	ret = regmap_update_bits(info->max8997->regmap_muic,
+			MAX8997_MUIC_REG_CONTROL1, COMP_SW_MASK, ctrl1);
 	if (ret < 0) {
 		dev_err(info->dev, "failed to update MUIC register\n");
 		return ret;
@@ -240,9 +241,9 @@ static int max8997_muic_set_path(struct max8997_muic_info *info,
 	else
 		ctrl2 |= CONTROL2_LOWPWR_MASK;	/* LowPwr=1, CPEn=0 */
 
-	ret = max8997_update_reg(info->muic,
-			MAX8997_MUIC_REG_CONTROL2, ctrl2,
-			CONTROL2_LOWPWR_MASK | CONTROL2_CPEN_MASK);
+	ret = regmap_update_bits(info->max8997->regmap_muic,
+			MAX8997_MUIC_REG_CONTROL2,
+			CONTROL2_LOWPWR_MASK | CONTROL2_CPEN_MASK, ctrl2);
 	if (ret < 0) {
 		dev_err(info->dev, "failed to update MUIC register\n");
 		return ret;
@@ -543,8 +544,8 @@ static void max8997_muic_irq_work(struct work_struct *work)
 		if (info->irq == muic_irqs[i].virq)
 			irq_type = muic_irqs[i].irq;
 
-	ret = max8997_bulk_read(info->muic, MAX8997_MUIC_REG_STATUS1,
-				2, info->status);
+	ret = regmap_bulk_read(info->max8997->regmap_muic,
+				MAX8997_MUIC_REG_STATUS1, info->status, 2);
 	if (ret) {
 		dev_err(info->dev, "failed to read muic register\n");
 		mutex_unlock(&info->mutex);
@@ -605,8 +606,8 @@ static int max8997_muic_detect_dev(struct max8997_muic_info *info)
 	mutex_lock(&info->mutex);
 
 	/* Read STATUSx register to detect accessory */
-	ret = max8997_bulk_read(info->muic,
-			MAX8997_MUIC_REG_STATUS1, 2, info->status);
+	ret = regmap_bulk_read(info->max8997->regmap_muic,
+			MAX8997_MUIC_REG_STATUS1, info->status, 2);
 	if (ret) {
 		dev_err(info->dev, "failed to read MUIC register\n");
 		mutex_unlock(&info->mutex);
@@ -667,7 +668,7 @@ static int max8997_muic_probe(struct platform_device *pdev)
 	}
 
 	info->dev = &pdev->dev;
-	info->muic = max8997->muic;
+	info->max8997 = max8997;
 
 	platform_set_drvdata(pdev, info);
 	mutex_init(&info->mutex);
@@ -721,7 +722,7 @@ static int max8997_muic_probe(struct platform_device *pdev)
 
 		/* Initialize registers according to platform data */
 		for (i = 0; i < muic_pdata->num_init_data; i++) {
-			max8997_write_reg(info->muic,
+			regmap_write(info->max8997->regmap_muic,
 					muic_pdata->init_data[i].addr,
 					muic_pdata->init_data[i].data);
 		}
diff --git a/drivers/input/misc/max8997_haptic.c b/drivers/input/misc/max8997_haptic.c
index 1fea548..d3f7079 100644
--- a/drivers/input/misc/max8997_haptic.c
+++ b/drivers/input/misc/max8997_haptic.c
@@ -31,6 +31,7 @@
 #include <linux/mfd/max8997-private.h>
 #include <linux/mfd/max8997.h>
 #include <linux/regulator/consumer.h>
+#include <linux/regmap.h>
 
 /* Haptic configuration 2 register */
 #define MAX8997_MOTOR_TYPE_SHIFT	7
@@ -45,7 +46,7 @@
 
 struct max8997_haptic {
 	struct device *dev;
-	struct i2c_client *client;
+	struct max8997_dev *max8997;
 	struct input_dev *input_dev;
 	struct regulator *regulator;
 
@@ -86,19 +87,19 @@ static int max8997_haptic_set_duty_cycle(struct max8997_haptic *chip)
 		}
 		switch (chip->internal_mode_pattern) {
 		case 0:
-			max8997_write_reg(chip->client,
+			regmap_write(chip->max8997->regmap_haptic,
 				MAX8997_HAPTIC_REG_SIGPWMDC1, duty_index);
 			break;
 		case 1:
-			max8997_write_reg(chip->client,
+			regmap_write(chip->max8997->regmap_haptic,
 				MAX8997_HAPTIC_REG_SIGPWMDC2, duty_index);
 			break;
 		case 2:
-			max8997_write_reg(chip->client,
+			regmap_write(chip->max8997->regmap_haptic,
 				MAX8997_HAPTIC_REG_SIGPWMDC3, duty_index);
 			break;
 		case 3:
-			max8997_write_reg(chip->client,
+			regmap_write(chip->max8997->regmap_haptic,
 				MAX8997_HAPTIC_REG_SIGPWMDC4, duty_index);
 			break;
 		default:
@@ -115,50 +116,51 @@ static void max8997_haptic_configure(struct max8997_haptic *chip)
 	value = chip->type << MAX8997_MOTOR_TYPE_SHIFT |
 		chip->enabled << MAX8997_ENABLE_SHIFT |
 		chip->mode << MAX8997_MODE_SHIFT | chip->pwm_divisor;
-	max8997_write_reg(chip->client, MAX8997_HAPTIC_REG_CONF2, value);
+	regmap_write(chip->max8997->regmap_haptic,
+		MAX8997_HAPTIC_REG_CONF2, value);
 
 	if (chip->mode == MAX8997_INTERNAL_MODE && chip->enabled) {
 		value = chip->internal_mode_pattern << MAX8997_CYCLE_SHIFT |
 			chip->internal_mode_pattern << MAX8997_SIG_PERIOD_SHIFT |
 			chip->internal_mode_pattern << MAX8997_SIG_DUTY_SHIFT |
 			chip->internal_mode_pattern << MAX8997_PWM_DUTY_SHIFT;
-		max8997_write_reg(chip->client,
+		regmap_write(chip->max8997->regmap_haptic,
 			MAX8997_HAPTIC_REG_DRVCONF, value);
 
 		switch (chip->internal_mode_pattern) {
 		case 0:
 			value = chip->pattern_cycle << 4;
-			max8997_write_reg(chip->client,
+			regmap_write(chip->max8997->regmap_haptic,
 				MAX8997_HAPTIC_REG_CYCLECONF1, value);
 			value = chip->pattern_signal_period;
-			max8997_write_reg(chip->client,
+			regmap_write(chip->max8997->regmap_haptic,
 				MAX8997_HAPTIC_REG_SIGCONF1, value);
 			break;
 
 		case 1:
 			value = chip->pattern_cycle;
-			max8997_write_reg(chip->client,
+			regmap_write(chip->max8997->regmap_haptic,
 				MAX8997_HAPTIC_REG_CYCLECONF1, value);
 			value = chip->pattern_signal_period;
-			max8997_write_reg(chip->client,
+			regmap_write(chip->max8997->regmap_haptic,
 				MAX8997_HAPTIC_REG_SIGCONF2, value);
 			break;
 
 		case 2:
 			value = chip->pattern_cycle << 4;
-			max8997_write_reg(chip->client,
+			regmap_write(chip->max8997->regmap_haptic,
 				MAX8997_HAPTIC_REG_CYCLECONF2, value);
 			value = chip->pattern_signal_period;
-			max8997_write_reg(chip->client,
+			regmap_write(chip->max8997->regmap_haptic,
 				MAX8997_HAPTIC_REG_SIGCONF3, value);
 			break;
 
 		case 3:
 			value = chip->pattern_cycle;
-			max8997_write_reg(chip->client,
+			regmap_write(chip->max8997->regmap_haptic,
 				MAX8997_HAPTIC_REG_CYCLECONF2, value);
 			value = chip->pattern_signal_period;
-			max8997_write_reg(chip->client,
+			regmap_write(chip->max8997->regmap_haptic,
 				MAX8997_HAPTIC_REG_SIGCONF4, value);
 			break;
 
@@ -267,7 +269,7 @@ static int max8997_haptic_probe(struct platform_device *pdev)
 	INIT_WORK(&chip->work, max8997_haptic_play_effect_work);
 	mutex_init(&chip->mutex);
 
-	chip->client = iodev->haptic;
+	chip->max8997 = iodev;
 	chip->dev = &pdev->dev;
 	chip->input_dev = input_dev;
 	chip->pwm_period = haptic_pdata->pwm_period;
diff --git a/drivers/leds/leds-max8997.c b/drivers/leds/leds-max8997.c
index f449a8b..e2da8f2 100644
--- a/drivers/leds/leds-max8997.c
+++ b/drivers/leds/leds-max8997.c
@@ -18,6 +18,7 @@
 #include <linux/mfd/max8997.h>
 #include <linux/mfd/max8997-private.h>
 #include <linux/platform_device.h>
+#include <linux/regmap.h>
 
 #define MAX8997_LED_FLASH_SHIFT			3
 #define MAX8997_LED_FLASH_CUR_MASK		0xf8
@@ -53,7 +54,6 @@ static void max8997_led_set_mode(struct max8997_led *led,
 			enum max8997_led_mode mode)
 {
 	int ret;
-	struct i2c_client *client = led->iodev->i2c;
 	u8 mask = 0, val;
 
 	switch (mode) {
@@ -89,8 +89,8 @@ static void max8997_led_set_mode(struct max8997_led *led,
 	}
 
 	if (mask) {
-		ret = max8997_update_reg(client, MAX8997_REG_LEN_CNTL, val,
-					 mask);
+		ret = regmap_update_bits(led->iodev->regmap,
+					MAX8997_REG_LEN_CNTL, mask, val);
 		if (ret)
 			dev_err(led->iodev->dev,
 				"failed to update register(%d)\n", ret);
@@ -102,7 +102,6 @@ static void max8997_led_set_mode(struct max8997_led *led,
 static void max8997_led_enable(struct max8997_led *led, bool enable)
 {
 	int ret;
-	struct i2c_client *client = led->iodev->i2c;
 	u8 val = 0, mask = MAX8997_LED_BOOST_ENABLE_MASK;
 
 	if (led->enabled == enable)
@@ -110,7 +109,8 @@ static void max8997_led_enable(struct max8997_led *led, bool enable)
 
 	val = enable ? MAX8997_LED_BOOST_ENABLE_MASK : 0;
 
-	ret = max8997_update_reg(client, MAX8997_REG_BOOST_CNTL, val, mask);
+	ret = regmap_update_bits(led->iodev->regmap,
+				MAX8997_REG_BOOST_CNTL, mask, val);
 	if (ret)
 		dev_err(led->iodev->dev,
 			"failed to update register(%d)\n", ret);
@@ -122,7 +122,6 @@ static void max8997_led_set_current(struct max8997_led *led,
 				enum led_brightness value)
 {
 	int ret;
-	struct i2c_client *client = led->iodev->i2c;
 	u8 val = 0, mask = 0, reg = 0;
 
 	switch (led->led_mode) {
@@ -143,7 +142,7 @@ static void max8997_led_set_current(struct max8997_led *led,
 	}
 
 	if (mask) {
-		ret = max8997_update_reg(client, reg, val, mask);
+		ret = regmap_update_bits(led->iodev->regmap, reg, mask, val);
 		if (ret)
 			dev_err(led->iodev->dev,
 				"failed to update register(%d)\n", ret);
diff --git a/drivers/mfd/max8997-irq.c b/drivers/mfd/max8997-irq.c
index 43fa614..48ea7d7 100644
--- a/drivers/mfd/max8997-irq.c
+++ b/drivers/mfd/max8997-irq.c
@@ -26,6 +26,7 @@
 #include <linux/interrupt.h>
 #include <linux/mfd/max8997.h>
 #include <linux/mfd/max8997-private.h>
+#include <linux/regmap.h>
 
 static const u8 max8997_mask_reg[] = {
 	[PMIC_INT1] = MAX8997_REG_INT1MSK,
@@ -41,25 +42,6 @@ static const u8 max8997_mask_reg[] = {
 	[FLASH_STATUS] = MAX8997_REG_INVALID,
 };
 
-static struct i2c_client *get_i2c(struct max8997_dev *max8997,
-				enum max8997_irq_source src)
-{
-	switch (src) {
-	case PMIC_INT1 ... PMIC_INT4:
-		return max8997->i2c;
-	case FUEL_GAUGE:
-		return NULL;
-	case MUIC_INT1 ... MUIC_INT3:
-		return max8997->muic;
-	case GPIO_LOW ... GPIO_HI:
-		return max8997->i2c;
-	case FLASH_STATUS:
-		return max8997->i2c;
-	default:
-		return ERR_PTR(-EINVAL);
-	}
-}
-
 struct max8997_irq_data {
 	int mask;
 	enum max8997_irq_source group;
@@ -124,15 +106,20 @@ static void max8997_irq_sync_unlock(struct irq_data *data)
 	int i;
 
 	for (i = 0; i < MAX8997_IRQ_GROUP_NR; i++) {
+		struct regmap *map;
 		u8 mask_reg = max8997_mask_reg[i];
-		struct i2c_client *i2c = get_i2c(max8997, i);
+
+		if (i >= MUIC_INT1 && i <= MUIC_INT3)
+			map = max8997->regmap_muic;
+		else
+			map = max8997->regmap;
 
 		if (mask_reg == MAX8997_REG_INVALID ||
-				IS_ERR_OR_NULL(i2c))
+				IS_ERR_OR_NULL(map))
 			continue;
 		max8997->irq_masks_cache[i] = max8997->irq_masks_cur[i];
 
-		max8997_write_reg(i2c, max8997_mask_reg[i],
+		regmap_write(map, max8997_mask_reg[i],
 				max8997->irq_masks_cur[i]);
 	}
 
@@ -180,12 +167,12 @@ static struct irq_chip max8997_irq_chip = {
 static irqreturn_t max8997_irq_thread(int irq, void *data)
 {
 	struct max8997_dev *max8997 = data;
-	u8 irq_reg[MAX8997_IRQ_GROUP_NR] = {};
-	u8 irq_src;
+	unsigned int irq_reg[MAX8997_IRQ_GROUP_NR] = {};
+	unsigned int irq_src;
 	int ret;
 	int i, cur_irq;
 
-	ret = max8997_read_reg(max8997->i2c, MAX8997_REG_INTSRC, &irq_src);
+	ret = regmap_read(max8997->regmap, MAX8997_REG_INTSRC, &irq_src);
 	if (ret < 0) {
 		dev_err(max8997->dev, "Failed to read interrupt source: %d\n",
 				ret);
@@ -194,8 +181,9 @@ static irqreturn_t max8997_irq_thread(int irq, void *data)
 
 	if (irq_src & MAX8997_IRQSRC_PMIC) {
 		/* PMIC INT1 ~ INT4 */
-		max8997_bulk_read(max8997->i2c, MAX8997_REG_INT1, 4,
-				&irq_reg[PMIC_INT1]);
+		for (i=0; i<4; ++i)
+			regmap_read(max8997->regmap,
+				MAX8997_REG_INT1+i, &irq_reg[PMIC_INT1+i]);
 	}
 	if (irq_src & MAX8997_IRQSRC_FUELGAUGE) {
 		/*
@@ -215,8 +203,9 @@ static irqreturn_t max8997_irq_thread(int irq, void *data)
 	}
 	if (irq_src & MAX8997_IRQSRC_MUIC) {
 		/* MUIC INT1 ~ INT3 */
-		max8997_bulk_read(max8997->muic, MAX8997_MUIC_REG_INT1, 3,
-				&irq_reg[MUIC_INT1]);
+		for (i=0; i<3; ++i)
+			regmap_read(max8997->regmap_muic,
+				MAX8997_MUIC_REG_INT1+i, &irq_reg[MUIC_INT1+i]);
 	}
 	if (irq_src & MAX8997_IRQSRC_GPIO) {
 		/* GPIO Interrupt */
@@ -225,8 +214,8 @@ static irqreturn_t max8997_irq_thread(int irq, void *data)
 		irq_reg[GPIO_LOW] = 0;
 		irq_reg[GPIO_HI] = 0;
 
-		max8997_bulk_read(max8997->i2c, MAX8997_REG_GPIOCNTL1,
-				MAX8997_NUM_GPIO, gpio_info);
+		regmap_bulk_read(max8997->regmap,
+				MAX8997_REG_GPIOCNTL1, gpio_info, MAX8997_NUM_GPIO);
 		for (i = 0; i < MAX8997_NUM_GPIO; i++) {
 			bool interrupt = false;
 
@@ -260,7 +249,7 @@ static irqreturn_t max8997_irq_thread(int irq, void *data)
 	}
 	if (irq_src & MAX8997_IRQSRC_FLASH) {
 		/* Flash Status Interrupt */
-		ret = max8997_read_reg(max8997->i2c, MAX8997_REG_FLASHSTATUS,
+		ret = regmap_read(max8997->regmap, MAX8997_REG_FLASHSTATUS,
 				&irq_reg[FLASH_STATUS]);
 	}
 
@@ -312,7 +301,7 @@ int max8997_irq_init(struct max8997_dev *max8997)
 	struct irq_domain *domain;
 	int i;
 	int ret;
-	u8 val;
+	unsigned int val;
 
 	if (!max8997->irq) {
 		dev_warn(max8997->dev, "No interrupt specified.\n");
@@ -323,22 +312,19 @@ int max8997_irq_init(struct max8997_dev *max8997)
 
 	/* Mask individual interrupt sources */
 	for (i = 0; i < MAX8997_IRQ_GROUP_NR; i++) {
-		struct i2c_client *i2c;
-
 		max8997->irq_masks_cur[i] = 0xff;
 		max8997->irq_masks_cache[i] = 0xff;
-		i2c = get_i2c(max8997, i);
 
-		if (IS_ERR_OR_NULL(i2c))
+		if (IS_ERR_OR_NULL(max8997->regmap))
 			continue;
 		if (max8997_mask_reg[i] == MAX8997_REG_INVALID)
 			continue;
 
-		max8997_write_reg(i2c, max8997_mask_reg[i], 0xff);
+		regmap_write(max8997->regmap, max8997_mask_reg[i], 0xff);
 	}
 
 	for (i = 0; i < MAX8997_NUM_GPIO; i++) {
-		max8997->gpio_status[i] = (max8997_read_reg(max8997->i2c,
+		max8997->gpio_status[i] = (regmap_read(max8997->regmap,
 						MAX8997_REG_GPIOCNTL1 + i,
 						&val)
 					& MAX8997_GPIO_DATA_MASK) ?
diff --git a/drivers/mfd/max8997.c b/drivers/mfd/max8997.c
index be88a3b..2420da8 100644
--- a/drivers/mfd/max8997.c
+++ b/drivers/mfd/max8997.c
@@ -33,6 +33,7 @@
 #include <linux/mfd/core.h>
 #include <linux/mfd/max8997.h>
 #include <linux/mfd/max8997-private.h>
+#include <linux/regmap.h>
 
 #define I2C_ADDR_PMIC	(0xCC >> 1)
 #define I2C_ADDR_MUIC	(0x4A >> 1)
@@ -57,81 +58,29 @@ static struct of_device_id max8997_pmic_dt_match[] = {
 };
 #endif
 
-int max8997_read_reg(struct i2c_client *i2c, u8 reg, u8 *dest)
-{
-	struct max8997_dev *max8997 = i2c_get_clientdata(i2c);
-	int ret;
-
-	mutex_lock(&max8997->iolock);
-	ret = i2c_smbus_read_byte_data(i2c, reg);
-	mutex_unlock(&max8997->iolock);
-	if (ret < 0)
-		return ret;
-
-	ret &= 0xff;
-	*dest = ret;
-	return 0;
-}
-EXPORT_SYMBOL_GPL(max8997_read_reg);
-
-int max8997_bulk_read(struct i2c_client *i2c, u8 reg, int count, u8 *buf)
-{
-	struct max8997_dev *max8997 = i2c_get_clientdata(i2c);
-	int ret;
-
-	mutex_lock(&max8997->iolock);
-	ret = i2c_smbus_read_i2c_block_data(i2c, reg, count, buf);
-	mutex_unlock(&max8997->iolock);
-	if (ret < 0)
-		return ret;
-
-	return 0;
-}
-EXPORT_SYMBOL_GPL(max8997_bulk_read);
-
-int max8997_write_reg(struct i2c_client *i2c, u8 reg, u8 value)
-{
-	struct max8997_dev *max8997 = i2c_get_clientdata(i2c);
-	int ret;
-
-	mutex_lock(&max8997->iolock);
-	ret = i2c_smbus_write_byte_data(i2c, reg, value);
-	mutex_unlock(&max8997->iolock);
-	return ret;
-}
-EXPORT_SYMBOL_GPL(max8997_write_reg);
-
-int max8997_bulk_write(struct i2c_client *i2c, u8 reg, int count, u8 *buf)
-{
-	struct max8997_dev *max8997 = i2c_get_clientdata(i2c);
-	int ret;
+static const struct regmap_config max8997_regmap_config = {
+	.reg_bits = 8,
+	.val_bits = 8,
+	.max_register = MAX8997_REG_PMIC_END,
+};
 
-	mutex_lock(&max8997->iolock);
-	ret = i2c_smbus_write_i2c_block_data(i2c, reg, count, buf);
-	mutex_unlock(&max8997->iolock);
-	if (ret < 0)
-		return ret;
+static const struct regmap_config max8997_regmap_rtc_config = {
+	.reg_bits = 8,
+	.val_bits = 8,
+	.max_register = MAX8997_RTC_REG_END,
+};
 
-	return 0;
-}
-EXPORT_SYMBOL_GPL(max8997_bulk_write);
+static const struct regmap_config max8997_regmap_haptic_config = {
+	.reg_bits = 8,
+	.val_bits = 8,
+	.max_register = MAX8997_HAPTIC_REG_END,
+};
 
-int max8997_update_reg(struct i2c_client *i2c, u8 reg, u8 val, u8 mask)
-{
-	struct max8997_dev *max8997 = i2c_get_clientdata(i2c);
-	int ret;
-
-	mutex_lock(&max8997->iolock);
-	ret = i2c_smbus_read_byte_data(i2c, reg);
-	if (ret >= 0) {
-		u8 old_val = ret & 0xff;
-		u8 new_val = (val & mask) | (old_val & (~mask));
-		ret = i2c_smbus_write_byte_data(i2c, reg, new_val);
-	}
-	mutex_unlock(&max8997->iolock);
-	return ret;
-}
-EXPORT_SYMBOL_GPL(max8997_update_reg);
+static const struct regmap_config max8997_regmap_muic_config = {
+	.reg_bits = 8,
+	.val_bits = 8,
+	.max_register = MAX8997_MUIC_REG_END,
+};
 
 /*
  * Only the common platform data elements for max8997 are parsed here from the
@@ -202,6 +151,14 @@ static int max8997_i2c_probe(struct i2c_client *i2c,
 	if (!pdata)
 		return ret;
 
+	max8997->regmap = devm_regmap_init_i2c(i2c, &max8997_regmap_config);
+	if (IS_ERR(max8997->regmap)) {
+		ret = PTR_ERR(max8997->regmap);
+		dev_err(max8997->dev, "failed to allocate register map: %d\n",
+				ret);
+		return ret;
+	}
+
 	max8997->pdata = pdata;
 	max8997->ono = pdata->ono;
 
@@ -209,11 +166,40 @@ static int max8997_i2c_probe(struct i2c_client *i2c,
 
 	max8997->rtc = i2c_new_dummy(i2c->adapter, I2C_ADDR_RTC);
 	i2c_set_clientdata(max8997->rtc, max8997);
+
 	max8997->haptic = i2c_new_dummy(i2c->adapter, I2C_ADDR_HAPTIC);
 	i2c_set_clientdata(max8997->haptic, max8997);
+
 	max8997->muic = i2c_new_dummy(i2c->adapter, I2C_ADDR_MUIC);
 	i2c_set_clientdata(max8997->muic, max8997);
 
+	max8997->regmap_rtc = devm_regmap_init_i2c(max8997->rtc,
+					&max8997_regmap_rtc_config);
+	if (IS_ERR(max8997->regmap_rtc)) {
+		ret = PTR_ERR(max8997->regmap_rtc);
+		dev_err(max8997->dev,
+				"failed to allocate register map: %d\n", ret);
+		goto err_regmap;
+	}
+
+	max8997->regmap_haptic = devm_regmap_init_i2c(max8997->haptic,
+					&max8997_regmap_haptic_config);
+	if (IS_ERR(max8997->regmap_haptic)) {
+		ret = PTR_ERR(max8997->regmap_haptic);
+		dev_err(max8997->dev,
+				"failed to allocate register map: %d\n", ret);
+		goto err_regmap;
+	}
+
+	max8997->regmap_muic = devm_regmap_init_i2c(max8997->muic,
+					&max8997_regmap_muic_config);
+	if (IS_ERR(max8997->regmap_muic)) {
+		ret = PTR_ERR(max8997->regmap_muic);
+		dev_err(max8997->dev,
+				"failed to allocate register map: %d\n", ret);
+		goto err_regmap;
+	}
+
 	pm_runtime_set_active(max8997->dev);
 
 	max8997_irq_init(max8997);
@@ -238,6 +224,7 @@ static int max8997_i2c_probe(struct i2c_client *i2c,
 
 err_mfd:
 	mfd_remove_devices(max8997->dev);
+err_regmap:
 	i2c_unregister_device(max8997->muic);
 	i2c_unregister_device(max8997->haptic);
 	i2c_unregister_device(max8997->rtc);
@@ -423,15 +410,15 @@ static int max8997_freeze(struct device *dev)
 	int i;
 
 	for (i = 0; i < ARRAY_SIZE(max8997_dumpaddr_pmic); i++)
-		max8997_read_reg(i2c, max8997_dumpaddr_pmic[i],
+		regmap_read(max8997->regmap, max8997_dumpaddr_pmic[i],
 				&max8997->reg_dump[i]);
 
 	for (i = 0; i < ARRAY_SIZE(max8997_dumpaddr_muic); i++)
-		max8997_read_reg(i2c, max8997_dumpaddr_muic[i],
+		regmap_read(max8997->regmap_muic, max8997_dumpaddr_muic[i],
 				&max8997->reg_dump[i + MAX8997_REG_PMIC_END]);
 
 	for (i = 0; i < ARRAY_SIZE(max8997_dumpaddr_haptic); i++)
-		max8997_read_reg(i2c, max8997_dumpaddr_haptic[i],
+		regmap_read(max8997->regmap_haptic, max8997_dumpaddr_haptic[i],
 				&max8997->reg_dump[i + MAX8997_REG_PMIC_END +
 				MAX8997_MUIC_REG_END]);
 
@@ -445,15 +432,15 @@ static int max8997_restore(struct device *dev)
 	int i;
 
 	for (i = 0; i < ARRAY_SIZE(max8997_dumpaddr_pmic); i++)
-		max8997_write_reg(i2c, max8997_dumpaddr_pmic[i],
+		regmap_write(max8997->regmap, max8997_dumpaddr_pmic[i],
 				max8997->reg_dump[i]);
 
 	for (i = 0; i < ARRAY_SIZE(max8997_dumpaddr_muic); i++)
-		max8997_write_reg(i2c, max8997_dumpaddr_muic[i],
+		regmap_write(max8997->regmap_muic, max8997_dumpaddr_muic[i],
 				max8997->reg_dump[i + MAX8997_REG_PMIC_END]);
 
 	for (i = 0; i < ARRAY_SIZE(max8997_dumpaddr_haptic); i++)
-		max8997_write_reg(i2c, max8997_dumpaddr_haptic[i],
+		regmap_write(max8997->regmap_haptic, max8997_dumpaddr_haptic[i],
 				max8997->reg_dump[i + MAX8997_REG_PMIC_END +
 				MAX8997_MUIC_REG_END]);
 
diff --git a/drivers/power/max8997_charger.c b/drivers/power/max8997_charger.c
index 4bdedfe..43d8481 100644
--- a/drivers/power/max8997_charger.c
+++ b/drivers/power/max8997_charger.c
@@ -26,6 +26,7 @@
 #include <linux/power_supply.h>
 #include <linux/mfd/max8997.h>
 #include <linux/mfd/max8997-private.h>
+#include <linux/regmap.h>
 
 struct charger_data {
 	struct device *dev;
@@ -46,14 +47,14 @@ static int max8997_battery_get_property(struct power_supply *psy,
 {
 	struct charger_data *charger = container_of(psy,
 			struct charger_data, battery);
-	struct i2c_client *i2c = charger->iodev->i2c;
 	int ret;
-	u8 reg;
+	unsigned int reg;
 
 	switch (psp) {
 	case POWER_SUPPLY_PROP_STATUS:
 		val->intval = 0;
-		ret = max8997_read_reg(i2c, MAX8997_REG_STATUS4, &reg);
+		ret = regmap_read(charger->iodev->regmap,
+				MAX8997_REG_STATUS4, &reg);
 		if (ret)
 			return ret;
 		if ((reg & (1 << 0)) == 0x1)
@@ -62,7 +63,8 @@ static int max8997_battery_get_property(struct power_supply *psy,
 		break;
 	case POWER_SUPPLY_PROP_PRESENT:
 		val->intval = 0;
-		ret = max8997_read_reg(i2c, MAX8997_REG_STATUS4, &reg);
+		ret = regmap_read(charger->iodev->regmap,
+				MAX8997_REG_STATUS4, &reg);
 		if (ret)
 			return ret;
 		if ((reg & (1 << 2)) == 0x0)
@@ -71,7 +73,8 @@ static int max8997_battery_get_property(struct power_supply *psy,
 		break;
 	case POWER_SUPPLY_PROP_ONLINE:
 		val->intval = 0;
-		ret = max8997_read_reg(i2c, MAX8997_REG_STATUS4, &reg);
+		ret = regmap_read(charger->iodev->regmap,
+				MAX8997_REG_STATUS4, &reg);
 		if (ret)
 			return ret;
 		/* DCINOK */
@@ -103,8 +106,8 @@ static int max8997_battery_probe(struct platform_device *pdev)
 		if (val > 0xf)
 			val = 0xf;
 
-		ret = max8997_update_reg(iodev->i2c,
-				MAX8997_REG_MBCCTRL5, val, 0xf);
+		ret = regmap_update_bits(iodev->regmap,
+				MAX8997_REG_MBCCTRL5, 0xf, val);
 		if (ret < 0) {
 			dev_err(&pdev->dev, "Cannot use i2c bus.\n");
 			return ret;
@@ -113,20 +116,20 @@ static int max8997_battery_probe(struct platform_device *pdev)
 
 	switch (pdata->timeout) {
 	case 5:
-		ret = max8997_update_reg(iodev->i2c, MAX8997_REG_MBCCTRL1,
-				0x2 << 4, 0x7 << 4);
+		ret = regmap_update_bits(iodev->regmap,
+				MAX8997_REG_MBCCTRL1, 0x7 << 4, 0x2 << 4);
 		break;
 	case 6:
-		ret = max8997_update_reg(iodev->i2c, MAX8997_REG_MBCCTRL1,
-				0x3 << 4, 0x7 << 4);
+		ret = regmap_update_bits(iodev->regmap,
+				MAX8997_REG_MBCCTRL1, 0x7 << 4, 0x3 << 4);
 		break;
 	case 7:
-		ret = max8997_update_reg(iodev->i2c, MAX8997_REG_MBCCTRL1,
-				0x4 << 4, 0x7 << 4);
+		ret = regmap_update_bits(iodev->regmap,
+				MAX8997_REG_MBCCTRL1, 0x7 << 4, 0x4 << 4);
 		break;
 	case 0:
-		ret = max8997_update_reg(iodev->i2c, MAX8997_REG_MBCCTRL1,
-				0x7 << 4, 0x7 << 4);
+		ret = regmap_update_bits(iodev->regmap,
+				MAX8997_REG_MBCCTRL1, 0x7 << 4, 0x7 << 4);
 		break;
 	default:
 		dev_err(&pdev->dev, "incorrect timeout value (%d)\n",
diff --git a/drivers/regulator/max8997.c b/drivers/regulator/max8997.c
index 2d618fc..4001ed7 100644
--- a/drivers/regulator/max8997.c
+++ b/drivers/regulator/max8997.c
@@ -33,6 +33,7 @@
 #include <linux/mfd/max8997.h>
 #include <linux/mfd/max8997-private.h>
 #include <linux/regulator/of_regulator.h>
+#include <linux/regmap.h>
 
 struct max8997_data {
 	struct device *dev;
@@ -51,7 +52,7 @@ struct max8997_data {
 	int buck125_gpioindex;
 	bool ignore_gpiodvs_side_effect;
 
-	u8 saved_states[MAX8997_REG_MAX];
+	unsigned int saved_states[MAX8997_REG_MAX];
 };
 
 static const unsigned int safeoutvolt[] = {
@@ -258,15 +259,14 @@ static int max8997_get_enable_register(struct regulator_dev *rdev,
 static int max8997_reg_is_enabled(struct regulator_dev *rdev)
 {
 	struct max8997_data *max8997 = rdev_get_drvdata(rdev);
-	struct i2c_client *i2c = max8997->iodev->i2c;
 	int ret, reg, mask, pattern;
-	u8 val;
+	unsigned int val;
 
 	ret = max8997_get_enable_register(rdev, &reg, &mask, &pattern);
 	if (ret)
 		return ret;
 
-	ret = max8997_read_reg(i2c, reg, &val);
+	ret = regmap_read(max8997->iodev->regmap, reg, &val);
 	if (ret)
 		return ret;
 
@@ -276,27 +276,25 @@ static int max8997_reg_is_enabled(struct regulator_dev *rdev)
 static int max8997_reg_enable(struct regulator_dev *rdev)
 {
 	struct max8997_data *max8997 = rdev_get_drvdata(rdev);
-	struct i2c_client *i2c = max8997->iodev->i2c;
 	int ret, reg, mask, pattern;
 
 	ret = max8997_get_enable_register(rdev, &reg, &mask, &pattern);
 	if (ret)
 		return ret;
 
-	return max8997_update_reg(i2c, reg, pattern, mask);
+	return regmap_update_bits(max8997->iodev->regmap, reg, mask, pattern);
 }
 
 static int max8997_reg_disable(struct regulator_dev *rdev)
 {
 	struct max8997_data *max8997 = rdev_get_drvdata(rdev);
-	struct i2c_client *i2c = max8997->iodev->i2c;
 	int ret, reg, mask, pattern;
 
 	ret = max8997_get_enable_register(rdev, &reg, &mask, &pattern);
 	if (ret)
 		return ret;
 
-	return max8997_update_reg(i2c, reg, ~pattern, mask);
+	return regmap_update_bits(max8997->iodev->regmap, reg, mask, ~pattern);
 }
 
 static int max8997_get_voltage_register(struct regulator_dev *rdev,
@@ -368,15 +366,14 @@ static int max8997_get_voltage_register(struct regulator_dev *rdev,
 static int max8997_get_voltage_sel(struct regulator_dev *rdev)
 {
 	struct max8997_data *max8997 = rdev_get_drvdata(rdev);
-	struct i2c_client *i2c = max8997->iodev->i2c;
 	int reg, shift, mask, ret;
-	u8 val;
+	unsigned int val;
 
 	ret = max8997_get_voltage_register(rdev, &reg, &shift, &mask);
 	if (ret)
 		return ret;
 
-	ret = max8997_read_reg(i2c, reg, &val);
+	ret = regmap_read(max8997->iodev->regmap, reg, &val);
 	if (ret)
 		return ret;
 
@@ -413,7 +410,6 @@ static int max8997_set_voltage_charger_cv(struct regulator_dev *rdev,
 		int min_uV, int max_uV, unsigned *selector)
 {
 	struct max8997_data *max8997 = rdev_get_drvdata(rdev);
-	struct i2c_client *i2c = max8997->iodev->i2c;
 	int rid = rdev_get_id(rdev);
 	int lb, ub;
 	int reg, shift = 0, mask, ret = 0;
@@ -455,7 +451,8 @@ static int max8997_set_voltage_charger_cv(struct regulator_dev *rdev,
 
 	*selector = val;
 
-	ret = max8997_update_reg(i2c, reg, val << shift, mask);
+	ret = regmap_update_bits(max8997->iodev->regmap,
+				reg, mask, val << shift);
 
 	return ret;
 }
@@ -468,7 +465,6 @@ static int max8997_set_voltage_ldobuck(struct regulator_dev *rdev,
 		int min_uV, int max_uV, unsigned *selector)
 {
 	struct max8997_data *max8997 = rdev_get_drvdata(rdev);
-	struct i2c_client *i2c = max8997->iodev->i2c;
 	const struct voltage_map_desc *desc;
 	int rid = rdev_get_id(rdev);
 	int i, reg, shift, mask, ret;
@@ -500,7 +496,8 @@ static int max8997_set_voltage_ldobuck(struct regulator_dev *rdev,
 	if (ret)
 		return ret;
 
-	ret = max8997_update_reg(i2c, reg, i << shift, mask << shift);
+	ret = regmap_update_bits(max8997->iodev->regmap,
+				reg, mask << shift, i << shift);
 	*selector = i;
 
 	return ret;
@@ -710,7 +707,6 @@ static int max8997_set_voltage_safeout_sel(struct regulator_dev *rdev,
 					   unsigned selector)
 {
 	struct max8997_data *max8997 = rdev_get_drvdata(rdev);
-	struct i2c_client *i2c = max8997->iodev->i2c;
 	int rid = rdev_get_id(rdev);
 	int reg, shift = 0, mask, ret;
 
@@ -721,13 +717,13 @@ static int max8997_set_voltage_safeout_sel(struct regulator_dev *rdev,
 	if (ret)
 		return ret;
 
-	return max8997_update_reg(i2c, reg, selector << shift, mask << shift);
+	return regmap_update_bits(max8997->iodev->regmap,
+				reg, mask << shift, selector << shift);
 }
 
 static int max8997_reg_disable_suspend(struct regulator_dev *rdev)
 {
 	struct max8997_data *max8997 = rdev_get_drvdata(rdev);
-	struct i2c_client *i2c = max8997->iodev->i2c;
 	int ret, reg, mask, pattern;
 	int rid = rdev_get_id(rdev);
 
@@ -735,20 +731,22 @@ static int max8997_reg_disable_suspend(struct regulator_dev *rdev)
 	if (ret)
 		return ret;
 
-	max8997_read_reg(i2c, reg, &max8997->saved_states[rid]);
+	regmap_read(max8997->iodev->regmap,
+			reg, &max8997->saved_states[rid]);
 
 	if (rid == MAX8997_LDO1 ||
 			rid == MAX8997_LDO10 ||
 			rid == MAX8997_LDO21) {
 		dev_dbg(&rdev->dev, "Conditional Power-Off for %s\n",
 				rdev->desc->name);
-		return max8997_update_reg(i2c, reg, 0x40, mask);
+		return regmap_update_bits(max8997->iodev->regmap,
+				reg, mask, 0x40);
 	}
 
 	dev_dbg(&rdev->dev, "Full Power-Off for %s (%xh -> %xh)\n",
 			rdev->desc->name, max8997->saved_states[rid] & mask,
 			(~pattern) & mask);
-	return max8997_update_reg(i2c, reg, ~pattern, mask);
+	return regmap_update_bits(max8997->iodev->regmap, reg, mask, ~pattern);
 }
 
 static struct regulator_ops max8997_ldo_ops = {
@@ -1032,7 +1030,6 @@ static int max8997_pmic_probe(struct platform_device *pdev)
 	struct regulator_config config = { };
 	struct regulator_dev **rdev;
 	struct max8997_data *max8997;
-	struct i2c_client *i2c;
 	int i, ret, size, nr_dvs;
 	u8 max_buck1 = 0, max_buck2 = 0, max_buck5 = 0;
 
@@ -1062,7 +1059,6 @@ static int max8997_pmic_probe(struct platform_device *pdev)
 	max8997->iodev = iodev;
 	max8997->num_regulators = pdata->num_regulators;
 	platform_set_drvdata(pdev, max8997);
-	i2c = max8997->iodev->i2c;
 
 	max8997->buck125_gpioindex = pdata->buck125_default_idx;
 	max8997->buck1_gpiodvs = pdata->buck1_gpiodvs;
@@ -1112,25 +1108,25 @@ static int max8997_pmic_probe(struct platform_device *pdev)
 
 	/* For the safety, set max voltage before setting up */
 	for (i = 0; i < 8; i++) {
-		max8997_update_reg(i2c, MAX8997_REG_BUCK1DVS1 + i,
-				max_buck1, 0x3f);
-		max8997_update_reg(i2c, MAX8997_REG_BUCK2DVS1 + i,
-				max_buck2, 0x3f);
-		max8997_update_reg(i2c, MAX8997_REG_BUCK5DVS1 + i,
-				max_buck5, 0x3f);
+		regmap_update_bits(max8997->iodev->regmap,
+				MAX8997_REG_BUCK1DVS1 + i, 0x3f, max_buck1);
+		regmap_update_bits(max8997->iodev->regmap,
+				MAX8997_REG_BUCK2DVS1 + i, 0x3f, max_buck2);
+		regmap_update_bits(max8997->iodev->regmap,
+				MAX8997_REG_BUCK5DVS1 + i, 0x3f, max_buck5);
 	}
 
 	/* Initialize all the DVS related BUCK registers */
 	for (i = 0; i < nr_dvs; i++) {
-		max8997_update_reg(i2c, MAX8997_REG_BUCK1DVS1 + i,
-				max8997->buck1_vol[i],
-				0x3f);
-		max8997_update_reg(i2c, MAX8997_REG_BUCK2DVS1 + i,
-				max8997->buck2_vol[i],
-				0x3f);
-		max8997_update_reg(i2c, MAX8997_REG_BUCK5DVS1 + i,
-				max8997->buck5_vol[i],
-				0x3f);
+		regmap_update_bits(max8997->iodev->regmap,
+				MAX8997_REG_BUCK1DVS1 + i,
+				0x3f, max8997->buck1_vol[i]);
+		regmap_update_bits(max8997->iodev->regmap,
+				MAX8997_REG_BUCK2DVS1 + i,
+				0x3f, max8997->buck2_vol[i]);
+		regmap_update_bits(max8997->iodev->regmap,
+				MAX8997_REG_BUCK5DVS1 + i,
+				0x3f, max8997->buck5_vol[i]);
 	}
 
 	/*
@@ -1174,16 +1170,17 @@ static int max8997_pmic_probe(struct platform_device *pdev)
 	}
 
 	/* DVS-GPIO disabled */
-	max8997_update_reg(i2c, MAX8997_REG_BUCK1CTRL, (pdata->buck1_gpiodvs) ?
-			(1 << 1) : (0 << 1), 1 << 1);
-	max8997_update_reg(i2c, MAX8997_REG_BUCK2CTRL, (pdata->buck2_gpiodvs) ?
-			(1 << 1) : (0 << 1), 1 << 1);
-	max8997_update_reg(i2c, MAX8997_REG_BUCK5CTRL, (pdata->buck5_gpiodvs) ?
-			(1 << 1) : (0 << 1), 1 << 1);
+	regmap_update_bits(max8997->iodev->regmap, MAX8997_REG_BUCK1CTRL,
+			1 << 1, (pdata->buck1_gpiodvs) ? (1 << 1) : (0 << 1));
+	regmap_update_bits(max8997->iodev->regmap, MAX8997_REG_BUCK2CTRL,
+			1 << 1, (pdata->buck2_gpiodvs) ? (1 << 1) : (0 << 1));
+	regmap_update_bits(max8997->iodev->regmap, MAX8997_REG_BUCK5CTRL,
+			1 << 1, (pdata->buck5_gpiodvs) ? (1 << 1) : (0 << 1));
 
 	/* Misc Settings */
 	max8997->ramp_delay = 10; /* set 10mV/us, which is the default */
-	max8997_write_reg(i2c, MAX8997_REG_BUCKRAMP, (0xf << 4) | 0x9);
+	regmap_write(max8997->iodev->regmap,
+			MAX8997_REG_BUCKRAMP, (0xf << 4) | 0x9);
 
 	for (i = 0; i < pdata->num_regulators; i++) {
 		const struct voltage_map_desc *desc;
diff --git a/drivers/rtc/rtc-max8997.c b/drivers/rtc/rtc-max8997.c
index 0777c01..b866f7d5 100644
--- a/drivers/rtc/rtc-max8997.c
+++ b/drivers/rtc/rtc-max8997.c
@@ -20,6 +20,7 @@
 #include <linux/platform_device.h>
 #include <linux/mfd/max8997-private.h>
 #include <linux/irqdomain.h>
+#include <linux/regmap.h>
 
 /* Module parameter for WTSR function control */
 static int wtsr_en = 1;
@@ -68,7 +69,6 @@ enum {
 struct max8997_rtc_info {
 	struct device		*dev;
 	struct max8997_dev	*max8997;
-	struct i2c_client	*rtc;
 	struct rtc_device	*rtc_dev;
 	struct mutex		lock;
 	int virq;
@@ -118,8 +118,8 @@ static inline int max8997_rtc_set_update_reg(struct max8997_rtc_info *info)
 {
 	int ret;
 
-	ret = max8997_write_reg(info->rtc, MAX8997_RTC_UPDATE1,
-						RTC_UDR_MASK);
+	ret = regmap_write(info->max8997->regmap_rtc,
+				MAX8997_RTC_UPDATE1, RTC_UDR_MASK);
 	if (ret < 0)
 		dev_err(info->dev, "%s: fail to write update reg(%d)\n",
 				__func__, ret);
@@ -140,7 +140,8 @@ static int max8997_rtc_read_time(struct device *dev, struct rtc_time *tm)
 	int ret;
 
 	mutex_lock(&info->lock);
-	ret = max8997_bulk_read(info->rtc, MAX8997_RTC_SEC, RTC_NR_TIME, data);
+	ret = regmap_bulk_read(info->max8997->regmap_rtc,
+				MAX8997_RTC_SEC, data, RTC_NR_TIME);
 	mutex_unlock(&info->lock);
 
 	if (ret < 0) {
@@ -166,7 +167,8 @@ static int max8997_rtc_set_time(struct device *dev, struct rtc_time *tm)
 
 	mutex_lock(&info->lock);
 
-	ret = max8997_bulk_write(info->rtc, MAX8997_RTC_SEC, RTC_NR_TIME, data);
+	ret = regmap_bulk_write(info->max8997->regmap_rtc,
+				MAX8997_RTC_SEC, data, RTC_NR_TIME);
 	if (ret < 0) {
 		dev_err(info->dev, "%s: fail to write time reg(%d)\n", __func__,
 				ret);
@@ -183,13 +185,13 @@ static int max8997_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alrm)
 {
 	struct max8997_rtc_info *info = dev_get_drvdata(dev);
 	u8 data[RTC_NR_TIME];
-	u8 val;
+	unsigned int val;
 	int i, ret;
 
 	mutex_lock(&info->lock);
 
-	ret = max8997_bulk_read(info->rtc, MAX8997_RTC_ALARM1_SEC, RTC_NR_TIME,
-			data);
+	ret = regmap_bulk_read(info->max8997->regmap_rtc,
+				MAX8997_RTC_ALARM1_SEC, data, RTC_NR_TIME);
 	if (ret < 0) {
 		dev_err(info->dev, "%s:%d fail to read alarm reg(%d)\n",
 				__func__, __LINE__, ret);
@@ -207,7 +209,8 @@ static int max8997_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alrm)
 	}
 
 	alrm->pending = 0;
-	ret = max8997_read_reg(info->max8997->i2c, MAX8997_REG_STATUS1, &val);
+	ret = regmap_read(info->max8997->regmap_rtc,
+			       MAX8997_REG_STATUS1, &val);
 	if (ret < 0) {
 		dev_err(info->dev, "%s:%d fail to read status1 reg(%d)\n",
 				__func__, __LINE__, ret);
@@ -230,8 +233,8 @@ static int max8997_rtc_stop_alarm(struct max8997_rtc_info *info)
 	if (!mutex_is_locked(&info->lock))
 		dev_warn(info->dev, "%s: should have mutex locked\n", __func__);
 
-	ret = max8997_bulk_read(info->rtc, MAX8997_RTC_ALARM1_SEC, RTC_NR_TIME,
-				data);
+	ret = regmap_bulk_read(info->max8997->regmap_rtc,
+				MAX8997_RTC_ALARM1_SEC, data, RTC_NR_TIME);
 	if (ret < 0) {
 		dev_err(info->dev, "%s: fail to read alarm reg(%d)\n",
 				__func__, ret);
@@ -241,8 +244,8 @@ static int max8997_rtc_stop_alarm(struct max8997_rtc_info *info)
 	for (i = 0; i < RTC_NR_TIME; i++)
 		data[i] &= ~ALARM_ENABLE_MASK;
 
-	ret = max8997_bulk_write(info->rtc, MAX8997_RTC_ALARM1_SEC, RTC_NR_TIME,
-				 data);
+	ret = regmap_bulk_write(info->max8997->regmap_rtc,
+				 MAX8997_RTC_ALARM1_SEC, data, RTC_NR_TIME);
 	if (ret < 0) {
 		dev_err(info->dev, "%s: fail to write alarm reg(%d)\n",
 				__func__, ret);
@@ -262,8 +265,8 @@ static int max8997_rtc_start_alarm(struct max8997_rtc_info *info)
 	if (!mutex_is_locked(&info->lock))
 		dev_warn(info->dev, "%s: should have mutex locked\n", __func__);
 
-	ret = max8997_bulk_read(info->rtc, MAX8997_RTC_ALARM1_SEC, RTC_NR_TIME,
-				data);
+	ret = regmap_bulk_read(info->max8997->regmap_rtc,
+				MAX8997_RTC_ALARM1_SEC, data, RTC_NR_TIME);
 	if (ret < 0) {
 		dev_err(info->dev, "%s: fail to read alarm reg(%d)\n",
 				__func__, ret);
@@ -281,8 +284,8 @@ static int max8997_rtc_start_alarm(struct max8997_rtc_info *info)
 	if (data[RTC_DATE] & 0x1f)
 		data[RTC_DATE] |= (1 << ALARM_ENABLE_SHIFT);
 
-	ret = max8997_bulk_write(info->rtc, MAX8997_RTC_ALARM1_SEC, RTC_NR_TIME,
-				 data);
+	ret = regmap_bulk_write(info->max8997->regmap_rtc,
+				 MAX8997_RTC_ALARM1_SEC, data, RTC_NR_TIME);
 	if (ret < 0) {
 		dev_err(info->dev, "%s: fail to write alarm reg(%d)\n",
 				__func__, ret);
@@ -313,8 +316,8 @@ static int max8997_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alrm)
 	if (ret < 0)
 		goto out;
 
-	ret = max8997_bulk_write(info->rtc, MAX8997_RTC_ALARM1_SEC, RTC_NR_TIME,
-				data);
+	ret = regmap_bulk_write(info->max8997->regmap_rtc,
+				 MAX8997_RTC_ALARM1_SEC, data, RTC_NR_TIME);
 	if (ret < 0) {
 		dev_err(info->dev, "%s: fail to write alarm reg(%d)\n",
 				__func__, ret);
@@ -385,7 +388,8 @@ static void max8997_rtc_enable_wtsr(struct max8997_rtc_info *info, bool enable)
 	dev_info(info->dev, "%s: %s WTSR\n", __func__,
 			enable ? "enable" : "disable");
 
-	ret = max8997_update_reg(info->rtc, MAX8997_RTC_WTSR_SMPL, val, mask);
+	ret = regmap_update_bits(info->max8997->regmap_rtc,
+				 MAX8997_RTC_WTSR_SMPL, mask, val);
 	if (ret < 0) {
 		dev_err(info->dev, "%s: fail to update WTSR reg(%d)\n",
 				__func__, ret);
@@ -398,7 +402,7 @@ static void max8997_rtc_enable_wtsr(struct max8997_rtc_info *info, bool enable)
 static void max8997_rtc_enable_smpl(struct max8997_rtc_info *info, bool enable)
 {
 	int ret;
-	u8 val, mask;
+	unsigned int val, mask;
 
 	if (!smpl_en)
 		return;
@@ -413,7 +417,8 @@ static void max8997_rtc_enable_smpl(struct max8997_rtc_info *info, bool enable)
 	dev_info(info->dev, "%s: %s SMPL\n", __func__,
 			enable ? "enable" : "disable");
 
-	ret = max8997_update_reg(info->rtc, MAX8997_RTC_WTSR_SMPL, val, mask);
+	ret = regmap_update_bits(info->max8997->regmap_rtc,
+				 MAX8997_RTC_WTSR_SMPL, mask, val);
 	if (ret < 0) {
 		dev_err(info->dev, "%s: fail to update SMPL reg(%d)\n",
 				__func__, ret);
@@ -423,7 +428,8 @@ static void max8997_rtc_enable_smpl(struct max8997_rtc_info *info, bool enable)
 	max8997_rtc_set_update_reg(info);
 
 	val = 0;
-	max8997_read_reg(info->rtc, MAX8997_RTC_WTSR_SMPL, &val);
+	regmap_read(info->max8997->regmap_rtc,
+			 MAX8997_RTC_WTSR_SMPL, &val);
 	pr_info("%s: WTSR_SMPL(0x%02x)\n", __func__, val);
 }
 
@@ -438,7 +444,8 @@ static int max8997_rtc_init_reg(struct max8997_rtc_info *info)
 
 	info->rtc_24hr_mode = 1;
 
-	ret = max8997_bulk_write(info->rtc, MAX8997_RTC_CTRLMASK, 2, data);
+	ret = regmap_bulk_write(info->max8997->regmap_rtc,
+				 MAX8997_RTC_CTRLMASK, data, 2);
 	if (ret < 0) {
 		dev_err(info->dev, "%s: fail to write controlm reg(%d)\n",
 				__func__, ret);
@@ -463,7 +470,6 @@ static int max8997_rtc_probe(struct platform_device *pdev)
 	mutex_init(&info->lock);
 	info->dev = &pdev->dev;
 	info->max8997 = max8997;
-	info->rtc = max8997->rtc;
 
 	platform_set_drvdata(pdev, info);
 
diff --git a/include/linux/mfd/max8997-private.h b/include/linux/mfd/max8997-private.h
index ad1ae7f..27254b1 100644
--- a/include/linux/mfd/max8997-private.h
+++ b/include/linux/mfd/max8997-private.h
@@ -309,6 +309,8 @@ enum max8997_rtc_reg {
 	MAX8997_RTC_ALARM2_MONTH	= 0x22,
 	MAX8997_RTC_ALARM2_YEAR		= 0x23,
 	MAX8997_RTC_ALARM2_DAY_OF_MONTH	= 0x24,
+
+	MAX8997_RTC_REG_END		= 0x25,
 };
 
 enum max8997_irq_source {
@@ -390,6 +392,11 @@ struct max8997_dev {
 	int type;
 	struct platform_device *battery; /* battery control (not fuel gauge) */
 
+	struct regmap *regmap;
+	struct regmap *regmap_rtc;
+	struct regmap *regmap_haptic;
+	struct regmap *regmap_muic;
+
 	int irq;
 	int ono;
 	struct irq_domain *irq_domain;
@@ -398,7 +405,7 @@ struct max8997_dev {
 	int irq_masks_cache[MAX8997_IRQ_GROUP_NR];
 
 	/* For hibernation */
-	u8 reg_dump[MAX8997_REG_PMIC_END + MAX8997_MUIC_REG_END +
+	unsigned int reg_dump[MAX8997_REG_PMIC_END + MAX8997_MUIC_REG_END +
 		MAX8997_HAPTIC_REG_END];
 
 	bool gpio_status[MAX8997_NUM_GPIO];
@@ -413,14 +420,6 @@ extern int max8997_irq_init(struct max8997_dev *max8997);
 extern void max8997_irq_exit(struct max8997_dev *max8997);
 extern int max8997_irq_resume(struct max8997_dev *max8997);
 
-extern int max8997_read_reg(struct i2c_client *i2c, u8 reg, u8 *dest);
-extern int max8997_bulk_read(struct i2c_client *i2c, u8 reg, int count,
-				u8 *buf);
-extern int max8997_write_reg(struct i2c_client *i2c, u8 reg, u8 value);
-extern int max8997_bulk_write(struct i2c_client *i2c, u8 reg, int count,
-				u8 *buf);
-extern int max8997_update_reg(struct i2c_client *i2c, u8 reg, u8 val, u8 mask);
-
 #define MAX8997_GPIO_INT_BOTH	(0x3 << 4)
 #define MAX8997_GPIO_INT_RISE	(0x2 << 4)
 #define MAX8997_GPIO_INT_FALL	(0x1 << 4)
-- 
1.7.9.5


^ permalink raw reply related

* Re: [PATCH v2] mfd: max8997: use regmap to access registers
From: Dmitry Torokhov @ 2014-03-06 16:29 UTC (permalink / raw)
  To: Robert Baldyga
  Cc: sameo, lee.jones, myungjoo.ham, cw00.choi, cooloney, rpurdie,
	dbaryshkov, dwmw2, lgirdwood, broonie, a.zummo, paul.gortmaker,
	linux-kernel, linux-input, linux-leds, rtc-linux, m.szyprowski,
	k.kozlowski
In-Reply-To: <531818D6.3080500@samsung.com>

On Thu, Mar 06, 2014 at 07:42:30AM +0100, Robert Baldyga wrote:
> Hi,
> 
> On 03/05/2014 07:54 PM, Dmitry Torokhov wrote:
> > Hi Robert,
> > 
> > On Wed, Mar 05, 2014 at 03:58:17PM +0100, Robert Baldyga wrote:
> >>  
> >> -int max8997_write_reg(struct i2c_client *i2c, u8 reg, u8 value)
> >> +int max8997_write_reg(struct regmap *map, u8 reg, u8 value)
> > 
> > Why don't you make read/write reg to take struct max8997_dev as argument
> > instead of regmap? regmap seems to be the current implementation du jur,
> > but that is core's detail, functions do not need to care.
> > 
> > Thanks.
> > 
> 
> It's because there are few regmaps in max8997_dev, and read/write reg
> functions has no way to check which regmap should be used. I think it
> would be clearer if I remove this functions and use regmap_read and
> regmap_write instead, because there is no particular purpose for them.

I see.. Then another question - why do the regmaps belong to the core
of the driver instead of individual functions? Why haptic regmap, for
example, can not be initialized in haptic code instead?

Thanks.

-- 
Dmitry

^ permalink raw reply

* Re: [PATCH] input: ff-memless: don't schedule already playing effect to play again
From: Felix Rueegg @ 2014-03-06 18:05 UTC (permalink / raw)
  To: Dmitry Torokhov; +Cc: linux-input, linux-kernel
In-Reply-To: <20140304170731.GA30486@core.coreip.homeip.net>

Hi Dmitry,

On 03/04/2014 06:07 PM, Dmitry Torokhov wrote:
> Hi Felix,
>
> On Sun, Mar 02, 2014 at 12:35:43PM +0100, Felix Rueegg wrote:
>> When an effect with zero replay length, zero replay delay
>> and zero envelope attack length is uploaded, it is played and then scheduled to play
>> again one timer tick later. This triggers a warning (URB submitted while
>> active) in combination with the xpad driver.
>>
>> Skipping the rescheduling of this effect fixes the issue.
> Won't the same issue happen if we happen to schedule a different effect
> that would start at "now" time? We should not be "dropping" the new
> effect, at least not in core.
I can confirm this. It also happens sometimes with an effect that has a 
length and a replay count greater than one at the time, when the effect 
ends and restarts again.
> It looks to me xpad.c needs [more] love.
I agree and will try to come up with a fix for the xpad driver.

>> Signed-off-by: Felix Rueegg <felix.rueegg@gmail.com>
>> ---
>>   drivers/input/ff-memless.c | 7 +++++--
>>   1 file changed, 5 insertions(+), 2 deletions(-)
>>
>> diff --git a/drivers/input/ff-memless.c b/drivers/input/ff-memless.c
>> index 74c0d8c..2e06948 100644
>> --- a/drivers/input/ff-memless.c
>> +++ b/drivers/input/ff-memless.c
>> @@ -139,10 +139,13 @@ static void ml_schedule_timer(struct ml_device *ml)
>>   		if (!test_bit(FF_EFFECT_STARTED, &state->flags))
>>   			continue;
>>   
>> -		if (test_bit(FF_EFFECT_PLAYING, &state->flags))
>> +		if (test_bit(FF_EFFECT_PLAYING, &state->flags)) {
>>   			next_at = calculate_next_time(state);
>> -		else
>> +			if (next_at == now)
>> +				continue;
>> +		} else {
>>   			next_at = state->play_at;
>> +		}
>>   
>>   		if (time_before_eq(now, next_at) &&
>>   		    (++events == 1 || time_before(next_at, earliest)))
>> -- 
>> 1.9.0
>>
> Thanks.
>

^ permalink raw reply

* [PATCH] iio:hid-sensor-hub: Add in quirk for sensor hub in Lenovo Ideapad Yogas
From: Peter F. Patel-Schneider @ 2014-03-06 20:57 UTC (permalink / raw)
  To: linux-input; +Cc: srinivas.pandruvada

The sensor hub in Lenovo Yogas needs the enumeration quirk.  I've been running 
the patch for about a week with no problems, whereas the unpatched drivers 
reliably mis-initialized the sensors.

Signed-off-by: Peter F. Patel-Schneider <pfpschneider@gmail.com>
---
  drivers/hid/hid-ids.h        | 3 +++
  drivers/hid/hid-sensor-hub.c | 3 +++
  2 files changed, 6 insertions(+)

diff --git a/drivers/hid/hid-ids.h b/drivers/hid/hid-ids.h
index 5a5248f..f944e37 100644
--- a/drivers/hid/hid-ids.h
+++ b/drivers/hid/hid-ids.h
@@ -823,6 +823,9 @@
  #define USB_DEVICE_ID_SYNAPTICS_HD    0x0ac3
  #define USB_DEVICE_ID_SYNAPTICS_QUAD_HD    0x1ac3

+#define USB_VENDOR_ID_TEXAS_INSTRUMENTS    0x2047
+#define USB_DEVICE_ID_TEXAS_INSTRUMENTS_LENOVO_YOGA    0x0855
+
  #define USB_VENDOR_ID_THINGM        0x27b8
  #define USB_DEVICE_ID_BLINK1        0x01ed

diff --git a/drivers/hid/hid-sensor-hub.c b/drivers/hid/hid-sensor-hub.c
index 46f4480..597732b 100644
--- a/drivers/hid/hid-sensor-hub.c
+++ b/drivers/hid/hid-sensor-hub.c
@@ -665,6 +665,9 @@ static const struct hid_device_id sensor_hub_devices[] = {
      { HID_DEVICE(HID_BUS_ANY, HID_GROUP_SENSOR_HUB, USB_VENDOR_ID_INTEL_1,
              USB_DEVICE_ID_INTEL_HID_SENSOR),
              .driver_data = HID_SENSOR_HUB_ENUM_QUIRK},
+    { HID_DEVICE(HID_BUS_ANY, HID_GROUP_SENSOR_HUB, 
USB_VENDOR_ID_TEXAS_INSTRUMENTS,
+             USB_DEVICE_ID_TEXAS_INSTRUMENTS_LENOVO_YOGA),
+             .driver_data = HID_SENSOR_HUB_ENUM_QUIRK},
      { HID_DEVICE(HID_BUS_ANY, HID_GROUP_SENSOR_HUB, HID_ANY_ID,
               HID_ANY_ID) },
      { }

^ permalink raw reply related

* [PATCH v2 0/8] HID: sony: More Sony controller fixes and improvements.
From: Frank Praznik @ 2014-03-06 22:32 UTC (permalink / raw)
  To: linux-input; +Cc: jkosina, dh.herrmann, Frank Praznik

v2 rebases the previous set of patches against Benjamin Tissoires' latest patch
set and adds some additional changes:

- Adds a patch setting the new HID_QUIRK_NO_OUTPUT_REPORTS_ON_INTR_EP for the
  Sixaxis and DualShock 4 when they are connected via Bluetooth.  This adds
  support for sending output reports via hidraw.
  
- A patch with a new union/struct is introduced for the Sixaxis output report.
  This improves readability, particularly in the later blink support patch.
  
- Adds an IDA id allocator and uses it to assign the number at the end of the
  battery string and set the default LED values relative to other Sony
  controllers.

- Drops the LED trigger patch as it probably doesn't belong in kernel space.
  
- Addresses various issues raised during code review of the previous patch set.

^ permalink raw reply

* [PATCH v2 2/8] HID: sony: Set the HID quriks flag for Bluetooth controllers
From: Frank Praznik @ 2014-03-06 22:32 UTC (permalink / raw)
  To: linux-input; +Cc: jkosina, dh.herrmann, Frank Praznik
In-Reply-To: <1394145176-24174-1-git-send-email-frank.praznik@oh.rr.com>

The Sixaxis and DualShock 4 want HID output reports sent on the control
endpoint when connected via Bluetooth.  Set the
HID_QUIRK_NO_OUTPUT_REPORTS_ON_INTR_EP flag for these devices so hidraw write()
works properly.

Signed-off-by: Frank Praznik <frank.praznik@oh.rr.com>
---
 drivers/hid/hid-sony.c | 10 ++++++++++
 1 file changed, 10 insertions(+)

diff --git a/drivers/hid/hid-sony.c b/drivers/hid/hid-sony.c
index 4c445a0..908de27 100644
--- a/drivers/hid/hid-sony.c
+++ b/drivers/hid/hid-sony.c
@@ -1632,11 +1632,21 @@ static int sony_probe(struct hid_device *hdev, const struct hid_device_id *id)
 		sc->worker_initialized = 1;
 		INIT_WORK(&sc->state_worker, sixaxis_state_worker);
 	} else if (sc->quirks & SIXAXIS_CONTROLLER_BT) {
+		/*
+		 * The Sixaxis wants output reports sent on the ctrl endpoint
+		 * when connected via Bluetooth.
+		 */
+		hdev->quirks |= HID_QUIRK_NO_OUTPUT_REPORTS_ON_INTR_EP;
 		ret = sixaxis_set_operational_bt(hdev);
 		sc->worker_initialized = 1;
 		INIT_WORK(&sc->state_worker, sixaxis_state_worker);
 	} else if (sc->quirks & DUALSHOCK4_CONTROLLER) {
 		if (sc->quirks & DUALSHOCK4_CONTROLLER_BT) {
+			/*
+			 * The DualShock 4 wants output reports sent on the ctrl
+			 * endpoint when connected via Bluetooth.
+			 */
+			hdev->quirks |= HID_QUIRK_NO_OUTPUT_REPORTS_ON_INTR_EP;
 			ret = dualshock4_set_operational_bt(hdev);
 			if (ret < 0) {
 				hid_err(hdev, "failed to set the Dualshock 4 operational mode\n");
-- 
1.8.5.3


^ permalink raw reply related

* [PATCH v2 1/8] HID: sony: Fix Sixaxis cable state detection
From: Frank Praznik @ 2014-03-06 22:32 UTC (permalink / raw)
  To: linux-input; +Cc: jkosina, dh.herrmann, Frank Praznik
In-Reply-To: <1394145176-24174-1-git-send-email-frank.praznik@oh.rr.com>

Byte 31 of the Sixaxis report can change depending on whether or not the
controller is rumbling.  Using bit 3 is the only reliable way to detect the
state of the cable regardless of rumble activity.

Signed-off-by: Frank Praznik <frank.praznik@oh.rr.com>
---
 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 4884bb5..4c445a0 100644
--- a/drivers/hid/hid-sony.c
+++ b/drivers/hid/hid-sony.c
@@ -863,7 +863,7 @@ static void sixaxis_parse_report(struct sony_sc *sc, __u8 *rd, int size)
 		battery_capacity = sixaxis_battery_capacity[index];
 		battery_charging = 0;
 	}
-	cable_state = !((rd[31] >> 4) & 0x01);
+	cable_state = !(rd[31] & 0x04);
 
 	spin_lock_irqsave(&sc->lock, flags);
 	sc->cable_state = cable_state;
-- 
1.8.5.3


^ permalink raw reply related

* [PATCH v2 4/8] HID: sony: Use a struct for the Sixaxis output report.
From: Frank Praznik @ 2014-03-06 22:32 UTC (permalink / raw)
  To: linux-input; +Cc: jkosina, dh.herrmann, Frank Praznik
In-Reply-To: <1394145176-24174-1-git-send-email-frank.praznik@oh.rr.com>

Use a struct for the Sixaxis output report that uses named members to set the
report fields.

Signed-off-by: Frank Praznik <frank.praznik@oh.rr.com>
---
 drivers/hid/hid-sony.c | 66 +++++++++++++++++++++++++++++++++++++-------------
 1 file changed, 49 insertions(+), 17 deletions(-)

diff --git a/drivers/hid/hid-sony.c b/drivers/hid/hid-sony.c
index 3df3306..5e9bb57 100644
--- a/drivers/hid/hid-sony.c
+++ b/drivers/hid/hid-sony.c
@@ -717,6 +717,36 @@ static enum power_supply_property sony_battery_props[] = {
 	POWER_SUPPLY_PROP_STATUS,
 };
 
+struct sixaxis_led {
+	__u8 time_enabled; /* the total time the led is active (0xff means forever) */
+	__u8 duty_length;  /* how long a cycle is in deciseconds (0 means "really fast") */
+	__u8 enabled;
+	__u8 duty_off; /* % of duty_length the led is off (0xff means 100%) */
+	__u8 duty_on;  /* % of duty_length the led is on (0xff mean 100%) */
+} __packed;
+
+struct sixaxis_rumble {
+	__u8 padding;
+	__u8 right_duration; /* Right motor duration (0xff means forever) */
+	__u8 right_motor_on; /* Right (small) motor on/off, only supports values of 0 or 1 (off/on) */
+	__u8 left_duration; /* Left motor duration (0xff means forever) */
+	__u8 left_motor_force; /* left (large) motor, supports force values from 0 to 255 */
+} __packed;
+
+struct sixaxis_output_report {
+	__u8 report_id;
+	struct sixaxis_rumble rumble;
+	__u8 padding[4];
+	__u8 leds_bitmap; /* bitmap of enabled LEDs: LED_1 = 0x02, LED_2 = 0x04, ... */
+	struct sixaxis_led led[4]; /* LEDx at (4 - x), add a macro? */
+	struct sixaxis_led _reserved; /* LED5, not actually soldered */
+} __packed;
+
+union sixaxis_output_report_01 {
+	struct sixaxis_output_report data;
+	__u8 buf[36];
+};
+
 static spinlock_t sony_dev_list_lock;
 static LIST_HEAD(sony_device_list);
 
@@ -1244,29 +1274,31 @@ error_leds:
 static void sixaxis_state_worker(struct work_struct *work)
 {
 	struct sony_sc *sc = container_of(work, struct sony_sc, state_worker);
-	unsigned char buf[] = {
-		0x01,
-		0x00, 0xff, 0x00, 0xff, 0x00,
-		0x00, 0x00, 0x00, 0x00, 0x00,
-		0xff, 0x27, 0x10, 0x00, 0x32,
-		0xff, 0x27, 0x10, 0x00, 0x32,
-		0xff, 0x27, 0x10, 0x00, 0x32,
-		0xff, 0x27, 0x10, 0x00, 0x32,
-		0x00, 0x00, 0x00, 0x00, 0x00
+	union sixaxis_output_report_01 report = {
+		.buf = {
+			0x01,
+			0x00, 0xff, 0x00, 0xff, 0x00,
+			0x00, 0x00, 0x00, 0x00, 0x00,
+			0xff, 0x27, 0x10, 0x00, 0x32,
+			0xff, 0x27, 0x10, 0x00, 0x32,
+			0xff, 0x27, 0x10, 0x00, 0x32,
+			0xff, 0x27, 0x10, 0x00, 0x32,
+			0x00, 0x00, 0x00, 0x00, 0x00
+		}
 	};
 
 #ifdef CONFIG_SONY_FF
-	buf[3] = sc->right ? 1 : 0;
-	buf[5] = sc->left;
+	report.data.rumble.right_motor_on = sc->right ? 1 : 0;
+	report.data.rumble.left_motor_force = sc->left;
 #endif
 
-	buf[10] |= sc->led_state[0] << 1;
-	buf[10] |= sc->led_state[1] << 2;
-	buf[10] |= sc->led_state[2] << 3;
-	buf[10] |= sc->led_state[3] << 4;
+	report.data.leds_bitmap |= sc->led_state[0] << 1;
+	report.data.leds_bitmap |= sc->led_state[1] << 2;
+	report.data.leds_bitmap |= sc->led_state[2] << 3;
+	report.data.leds_bitmap |= sc->led_state[3] << 4;
 
-	hid_hw_raw_request(sc->hdev, 0x01, buf, sizeof(buf), HID_OUTPUT_REPORT,
-			HID_REQ_SET_REPORT);
+	hid_hw_raw_request(sc->hdev, report.data.report_id, report.buf,
+			sizeof(report), HID_OUTPUT_REPORT, HID_REQ_SET_REPORT);
 }
 
 static void dualshock4_state_worker(struct work_struct *work)
-- 
1.8.5.3


^ permalink raw reply related

* [PATCH v2 6/8] HID: sony: Add an IDA allocator to assign unique device ids
From: Frank Praznik @ 2014-03-06 22:32 UTC (permalink / raw)
  To: linux-input; +Cc: jkosina, dh.herrmann, Frank Praznik
In-Reply-To: <1394145176-24174-1-git-send-email-frank.praznik@oh.rr.com>

Add an IDA id allocator to assign unique, sequential device ids to Sixaxis and
DualShock 4 controllers.

Use explicit module init and exit functions since the IDA allocator must be
manually destroyed when the module is unloaded.

Use the device id as the unique number for the battery identification string.

Signed-off-by: Frank Praznik <frank.praznik@oh.rr.com>
---
 drivers/hid/hid-sony.c | 70 +++++++++++++++++++++++++++++++++++++++++++++-----
 1 file changed, 63 insertions(+), 7 deletions(-)

diff --git a/drivers/hid/hid-sony.c b/drivers/hid/hid-sony.c
index a9bcfbe..13af58c 100644
--- a/drivers/hid/hid-sony.c
+++ b/drivers/hid/hid-sony.c
@@ -33,6 +33,7 @@
 #include <linux/power_supply.h>
 #include <linux/spinlock.h>
 #include <linux/list.h>
+#include <linux/idr.h>
 #include <linux/input/mt.h>
 
 #include "hid-ids.h"
@@ -749,6 +750,7 @@ union sixaxis_output_report_01 {
 
 static spinlock_t sony_dev_list_lock;
 static LIST_HEAD(sony_device_list);
+static DEFINE_IDA(sony_device_id_allocator);
 
 struct sony_sc {
 	spinlock_t lock;
@@ -758,6 +760,7 @@ struct sony_sc {
 	unsigned long quirks;
 	struct work_struct state_worker;
 	struct power_supply battery;
+	int device_id;
 
 #ifdef CONFIG_SONY_FF
 	__u8 left;
@@ -1413,8 +1416,6 @@ static int sony_battery_get_property(struct power_supply *psy,
 
 static int sony_battery_probe(struct sony_sc *sc)
 {
-	static atomic_t power_id_seq = ATOMIC_INIT(0);
-	unsigned long power_id;
 	struct hid_device *hdev = sc->hdev;
 	int ret;
 
@@ -1424,15 +1425,13 @@ static int sony_battery_probe(struct sony_sc *sc)
 	 */
 	sc->battery_capacity = 100;
 
-	power_id = (unsigned long)atomic_inc_return(&power_id_seq);
-
 	sc->battery.properties = sony_battery_props;
 	sc->battery.num_properties = ARRAY_SIZE(sony_battery_props);
 	sc->battery.get_property = sony_battery_get_property;
 	sc->battery.type = POWER_SUPPLY_TYPE_BATTERY;
 	sc->battery.use_for_apm = 0;
-	sc->battery.name = kasprintf(GFP_KERNEL, "sony_controller_battery_%lu",
-				     power_id);
+	sc->battery.name = kasprintf(GFP_KERNEL, "sony_controller_battery_%i",
+				     sc->device_id);
 	if (!sc->battery.name)
 		return -ENOMEM;
 
@@ -1607,6 +1606,38 @@ static int sony_check_add(struct sony_sc *sc)
 	return sony_check_add_dev_list(sc);
 }
 
+static int sony_set_device_id(struct sony_sc *sc)
+{
+	int ret;
+
+	/*
+	 * Only DualShock 4 or Sixaxis controller get an id.
+	 * All others are set to -1.
+	 */
+	if ((sc->quirks & SIXAXIS_CONTROLLER) ||
+	    (sc->quirks & DUALSHOCK4_CONTROLLER)) {
+		ret = ida_simple_get(&sony_device_id_allocator, 0, 0,
+					GFP_KERNEL);
+		if (ret < 0) {
+			sc->device_id = -1;
+			return ret;
+		}
+		sc->device_id = ret;
+	} else {
+		sc->device_id = -1;
+	}
+
+	return 0;
+}
+
+static void sony_release_device_id(struct sony_sc *sc)
+{
+	if (sc->device_id >= 0) {
+		ida_simple_remove(&sony_device_id_allocator, sc->device_id);
+		sc->device_id = -1;
+	}
+}
+
 static inline void sony_init_work(struct sony_sc *sc,
 					void(*worker)(struct work_struct *))
 {
@@ -1658,6 +1689,12 @@ static int sony_probe(struct hid_device *hdev, const struct hid_device_id *id)
 		return ret;
 	}
 
+	ret = sony_set_device_id(sc);
+	if (ret < 0) {
+		hid_err(hdev, "failed to allocate the device id\n");
+		goto err_stop;
+	}
+
 	if (sc->quirks & SIXAXIS_CONTROLLER_USB) {
 		/*
 		 * The Sony Sixaxis does not handle HID Output Reports on the
@@ -1749,6 +1786,7 @@ err_stop:
 		sony_battery_remove(sc);
 	sony_cancel_work_sync(sc);
 	sony_remove_dev_list(sc);
+	sony_release_device_id(sc);
 	hid_hw_stop(hdev);
 	return ret;
 }
@@ -1769,6 +1807,8 @@ static void sony_remove(struct hid_device *hdev)
 
 	sony_remove_dev_list(sc);
 
+	sony_release_device_id(sc);
+
 	hid_hw_stop(hdev);
 }
 
@@ -1813,6 +1853,22 @@ static struct hid_driver sony_driver = {
 	.report_fixup  = sony_report_fixup,
 	.raw_event     = sony_raw_event
 };
-module_hid_driver(sony_driver);
+
+static int __init sony_init(void)
+{
+	dbg_hid("Sony:%s\n", __func__);
+
+	return hid_register_driver(&sony_driver);
+}
+
+static void __exit sony_exit(void)
+{
+	dbg_hid("Sony:%s\n", __func__);
+
+	ida_destroy(&sony_device_id_allocator);
+	hid_unregister_driver(&sony_driver);
+}
+module_init(sony_init);
+module_exit(sony_exit);
 
 MODULE_LICENSE("GPL");
-- 
1.8.5.3


^ permalink raw reply related

* [PATCH v2 3/8] HID: sony: Use inliners for work queue initialization and cancellation
From: Frank Praznik @ 2014-03-06 22:32 UTC (permalink / raw)
  To: linux-input; +Cc: jkosina, dh.herrmann, Frank Praznik
In-Reply-To: <1394145176-24174-1-git-send-email-frank.praznik@oh.rr.com>

Use inliners to make sure that the work queue initialization flag is always
checked and set correctly when initializing or cancelling the work queue.

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

 v2 doesn't set worker_initialized to 0 when cancelling work sync since
 cancel_work_sync doesn't deinitialize the work queue.

 drivers/hid/hid-sony.c | 29 +++++++++++++++++++----------
 1 file changed, 19 insertions(+), 10 deletions(-)

diff --git a/drivers/hid/hid-sony.c b/drivers/hid/hid-sony.c
index 908de27..3df3306 100644
--- a/drivers/hid/hid-sony.c
+++ b/drivers/hid/hid-sony.c
@@ -1578,6 +1578,20 @@ static int sony_check_add(struct sony_sc *sc)
 	return sony_check_add_dev_list(sc);
 }
 
+static inline void sony_init_work(struct sony_sc *sc,
+					void(*worker)(struct work_struct *))
+{
+	if (!sc->worker_initialized)
+		INIT_WORK(&sc->state_worker, worker);
+
+	sc->worker_initialized = 1;
+}
+
+static inline void sony_cancel_work_sync(struct sony_sc *sc)
+{
+	if (sc->worker_initialized)
+		cancel_work_sync(&sc->state_worker);
+}
 
 static int sony_probe(struct hid_device *hdev, const struct hid_device_id *id)
 {
@@ -1629,8 +1643,7 @@ static int sony_probe(struct hid_device *hdev, const struct hid_device_id *id)
 		hdev->quirks |= HID_QUIRK_NO_OUTPUT_REPORTS_ON_INTR_EP;
 		hdev->quirks |= HID_QUIRK_SKIP_OUTPUT_REPORT_ID;
 		ret = sixaxis_set_operational_usb(hdev);
-		sc->worker_initialized = 1;
-		INIT_WORK(&sc->state_worker, sixaxis_state_worker);
+		sony_init_work(sc, sixaxis_state_worker);
 	} else if (sc->quirks & SIXAXIS_CONTROLLER_BT) {
 		/*
 		 * The Sixaxis wants output reports sent on the ctrl endpoint
@@ -1638,8 +1651,7 @@ static int sony_probe(struct hid_device *hdev, const struct hid_device_id *id)
 		 */
 		hdev->quirks |= HID_QUIRK_NO_OUTPUT_REPORTS_ON_INTR_EP;
 		ret = sixaxis_set_operational_bt(hdev);
-		sc->worker_initialized = 1;
-		INIT_WORK(&sc->state_worker, sixaxis_state_worker);
+		sony_init_work(sc, sixaxis_state_worker);
 	} else if (sc->quirks & DUALSHOCK4_CONTROLLER) {
 		if (sc->quirks & DUALSHOCK4_CONTROLLER_BT) {
 			/*
@@ -1661,8 +1673,7 @@ static int sony_probe(struct hid_device *hdev, const struct hid_device_id *id)
 		if (ret < 0)
 			goto err_stop;
 
-		sc->worker_initialized = 1;
-		INIT_WORK(&sc->state_worker, dualshock4_state_worker);
+		sony_init_work(sc, dualshock4_state_worker);
 	} else {
 		ret = 0;
 	}
@@ -1707,8 +1718,7 @@ err_stop:
 		sony_leds_remove(hdev);
 	if (sc->quirks & SONY_BATTERY_SUPPORT)
 		sony_battery_remove(sc);
-	if (sc->worker_initialized)
-		cancel_work_sync(&sc->state_worker);
+	sony_cancel_work_sync(sc);
 	sony_remove_dev_list(sc);
 	hid_hw_stop(hdev);
 	return ret;
@@ -1726,8 +1736,7 @@ static void sony_remove(struct hid_device *hdev)
 		sony_battery_remove(sc);
 	}
 
-	if (sc->worker_initialized)
-		cancel_work_sync(&sc->state_worker);
+	sony_cancel_work_sync(sc);
 
 	sony_remove_dev_list(sc);
 
-- 
1.8.5.3


^ permalink raw reply related

* [PATCH v2 5/8] HID: sony: Convert startup and shutdown functions to use a uniform parameter type
From: Frank Praznik @ 2014-03-06 22:32 UTC (permalink / raw)
  To: linux-input; +Cc: jkosina, dh.herrmann, Frank Praznik
In-Reply-To: <1394145176-24174-1-git-send-email-frank.praznik@oh.rr.com>

Convert all of the local initialization and shutdown functions to take a
parameter type of struct sony_sc* instead of using a mix of struct sony_sc* and
struct hid_device*.

Allows for the removal of some calls to hid_get_drvdata().

Signed-off-by: Frank Praznik <frank.praznik@oh.rr.com>
---
 drivers/hid/hid-sony.c | 67 ++++++++++++++++++++++++--------------------------
 1 file changed, 32 insertions(+), 35 deletions(-)

diff --git a/drivers/hid/hid-sony.c b/drivers/hid/hid-sony.c
index 5e9bb57..a9bcfbe 100644
--- a/drivers/hid/hid-sony.c
+++ b/drivers/hid/hid-sony.c
@@ -1096,19 +1096,18 @@ static void buzz_set_leds(struct hid_device *hdev, const __u8 *leds)
 	hid_hw_request(hdev, report, HID_REQ_SET_REPORT);
 }
 
-static void sony_set_leds(struct hid_device *hdev, const __u8 *leds, int count)
+static void sony_set_leds(struct sony_sc *sc, const __u8 *leds, int count)
 {
-	struct sony_sc *drv_data = hid_get_drvdata(hdev);
 	int n;
 
 	BUG_ON(count > MAX_LEDS);
 
-	if (drv_data->quirks & BUZZ_CONTROLLER && count == 4) {
-		buzz_set_leds(hdev, leds);
+	if (sc->quirks & BUZZ_CONTROLLER && count == 4) {
+		buzz_set_leds(sc->hdev, leds);
 	} else {
 		for (n = 0; n < count; n++)
-			drv_data->led_state[n] = leds[n];
-		schedule_work(&drv_data->state_worker);
+			sc->led_state[n] = leds[n];
+		schedule_work(&sc->state_worker);
 	}
 }
 
@@ -1131,7 +1130,8 @@ static void sony_led_set_brightness(struct led_classdev *led,
 		if (led == drv_data->leds[n]) {
 			if (value != drv_data->led_state[n]) {
 				drv_data->led_state[n] = value;
-				sony_set_leds(hdev, drv_data->led_state, drv_data->led_count);
+				sony_set_leds(drv_data, drv_data->led_state,
+						drv_data->led_count);
 			}
 			break;
 		}
@@ -1160,30 +1160,28 @@ static enum led_brightness sony_led_get_brightness(struct led_classdev *led)
 	return LED_OFF;
 }
 
-static void sony_leds_remove(struct hid_device *hdev)
+static void sony_leds_remove(struct sony_sc *sc)
 {
-	struct sony_sc *drv_data;
 	struct led_classdev *led;
 	int n;
 
-	drv_data = hid_get_drvdata(hdev);
-	BUG_ON(!(drv_data->quirks & SONY_LED_SUPPORT));
+	BUG_ON(!(sc->quirks & SONY_LED_SUPPORT));
 
-	for (n = 0; n < drv_data->led_count; n++) {
-		led = drv_data->leds[n];
-		drv_data->leds[n] = NULL;
+	for (n = 0; n < sc->led_count; n++) {
+		led = sc->leds[n];
+		sc->leds[n] = NULL;
 		if (!led)
 			continue;
 		led_classdev_unregister(led);
 		kfree(led);
 	}
 
-	drv_data->led_count = 0;
+	sc->led_count = 0;
 }
 
-static int sony_leds_init(struct hid_device *hdev)
+static int sony_leds_init(struct sony_sc *sc)
 {
-	struct sony_sc *drv_data;
+	struct hid_device *hdev = sc->hdev;
 	int n, ret = 0;
 	int max_brightness;
 	int use_colors;
@@ -1195,11 +1193,10 @@ static int sony_leds_init(struct hid_device *hdev)
 	static const char * const color_str[] = { "red", "green", "blue" };
 	static const __u8 initial_values[MAX_LEDS] = { 0x00, 0x00, 0x00, 0x00 };
 
-	drv_data = hid_get_drvdata(hdev);
-	BUG_ON(!(drv_data->quirks & SONY_LED_SUPPORT));
+	BUG_ON(!(sc->quirks & SONY_LED_SUPPORT));
 
-	if (drv_data->quirks & BUZZ_CONTROLLER) {
-		drv_data->led_count = 4;
+	if (sc->quirks & BUZZ_CONTROLLER) {
+		sc->led_count = 4;
 		max_brightness = 1;
 		use_colors = 0;
 		name_len = strlen("::buzz#");
@@ -1207,14 +1204,14 @@ static int sony_leds_init(struct hid_device *hdev)
 		/* Validate expected report characteristics. */
 		if (!hid_validate_values(hdev, HID_OUTPUT_REPORT, 0, 0, 7))
 			return -ENODEV;
-	} else if (drv_data->quirks & DUALSHOCK4_CONTROLLER) {
-		drv_data->led_count = 3;
+	} else if (sc->quirks & DUALSHOCK4_CONTROLLER) {
+		sc->led_count = 3;
 		max_brightness = 255;
 		use_colors = 1;
 		name_len = 0;
 		name_fmt = "%s:%s";
 	} else {
-		drv_data->led_count = 4;
+		sc->led_count = 4;
 		max_brightness = 1;
 		use_colors = 0;
 		name_len = strlen("::sony#");
@@ -1226,11 +1223,11 @@ static int sony_leds_init(struct hid_device *hdev)
 	 * only relevant if the driver is loaded after somebody actively set the
 	 * LEDs to on
 	 */
-	sony_set_leds(hdev, initial_values, drv_data->led_count);
+	sony_set_leds(sc, initial_values, sc->led_count);
 
 	name_sz = strlen(dev_name(&hdev->dev)) + name_len + 1;
 
-	for (n = 0; n < drv_data->led_count; n++) {
+	for (n = 0; n < sc->led_count; n++) {
 
 		if (use_colors)
 			name_sz = strlen(dev_name(&hdev->dev)) + strlen(color_str[n]) + 2;
@@ -1260,13 +1257,13 @@ static int sony_leds_init(struct hid_device *hdev)
 			goto error_leds;
 		}
 
-		drv_data->leds[n] = led;
+		sc->leds[n] = led;
 	}
 
 	return ret;
 
 error_leds:
-	sony_leds_remove(hdev);
+	sony_leds_remove(sc);
 
 	return ret;
 }
@@ -1355,9 +1352,9 @@ static int sony_play_effect(struct input_dev *dev, void *data,
 	return 0;
 }
 
-static int sony_init_ff(struct hid_device *hdev)
+static int sony_init_ff(struct sony_sc *sc)
 {
-	struct hid_input *hidinput = list_entry(hdev->inputs.next,
+	struct hid_input *hidinput = list_entry(sc->hdev->inputs.next,
 						struct hid_input, list);
 	struct input_dev *input_dev = hidinput->input;
 
@@ -1366,7 +1363,7 @@ static int sony_init_ff(struct hid_device *hdev)
 }
 
 #else
-static int sony_init_ff(struct hid_device *hdev)
+static int sony_init_ff(struct sony_sc *sc)
 {
 	return 0;
 }
@@ -1718,7 +1715,7 @@ static int sony_probe(struct hid_device *hdev, const struct hid_device_id *id)
 		goto err_stop;
 
 	if (sc->quirks & SONY_LED_SUPPORT) {
-		ret = sony_leds_init(hdev);
+		ret = sony_leds_init(sc);
 		if (ret < 0)
 			goto err_stop;
 	}
@@ -1737,7 +1734,7 @@ static int sony_probe(struct hid_device *hdev, const struct hid_device_id *id)
 	}
 
 	if (sc->quirks & SONY_FF_SUPPORT) {
-		ret = sony_init_ff(hdev);
+		ret = sony_init_ff(sc);
 		if (ret < 0)
 			goto err_close;
 	}
@@ -1747,7 +1744,7 @@ err_close:
 	hid_hw_close(hdev);
 err_stop:
 	if (sc->quirks & SONY_LED_SUPPORT)
-		sony_leds_remove(hdev);
+		sony_leds_remove(sc);
 	if (sc->quirks & SONY_BATTERY_SUPPORT)
 		sony_battery_remove(sc);
 	sony_cancel_work_sync(sc);
@@ -1761,7 +1758,7 @@ static void sony_remove(struct hid_device *hdev)
 	struct sony_sc *sc = hid_get_drvdata(hdev);
 
 	if (sc->quirks & SONY_LED_SUPPORT)
-		sony_leds_remove(hdev);
+		sony_leds_remove(sc);
 
 	if (sc->quirks & SONY_BATTERY_SUPPORT) {
 		hid_hw_close(hdev);
-- 
1.8.5.3


^ permalink raw reply related

* [PATCH v2 7/8] HID: sony: Initialize the controller LEDs with the device ID value
From: Frank Praznik @ 2014-03-06 22:32 UTC (permalink / raw)
  To: linux-input; +Cc: jkosina, dh.herrmann, Frank Praznik
In-Reply-To: <1394145176-24174-1-git-send-email-frank.praznik@oh.rr.com>

Use the device ID to initialize the Sixaxis and DualShock 4 controller LEDs to
default values.  The number or color of the controller is set relative to other
connected Sony controllers.

Set the LED class brightness values to the initial values and add the new led to
the array before calling led_classdev_register so that the correct brightness value
shows up in the LED sysfs entry.

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

 v2 uses the id assigned by the IDA allocator to set the default values.

 drivers/hid/hid-sony.c | 57 ++++++++++++++++++++++++++++++++++++++++++++++----
 1 file changed, 53 insertions(+), 4 deletions(-)

diff --git a/drivers/hid/hid-sony.c b/drivers/hid/hid-sony.c
index 13af58c..6de42b4 100644
--- a/drivers/hid/hid-sony.c
+++ b/drivers/hid/hid-sony.c
@@ -1081,6 +1081,52 @@ static int dualshock4_set_operational_bt(struct hid_device *hdev)
 				HID_FEATURE_REPORT, HID_REQ_GET_REPORT);
 }
 
+static void sixaxis_set_leds_from_id(int id, __u8 values[MAX_LEDS])
+{
+	static const __u8 sixaxis_leds[10][4] = {
+				{ 0x01, 0x00, 0x00, 0x00 },
+				{ 0x00, 0x01, 0x00, 0x00 },
+				{ 0x00, 0x00, 0x01, 0x00 },
+				{ 0x00, 0x00, 0x00, 0x01 },
+				{ 0x01, 0x00, 0x00, 0x01 },
+				{ 0x00, 0x01, 0x00, 0x01 },
+				{ 0x00, 0x00, 0x01, 0x01 },
+				{ 0x01, 0x00, 0x01, 0x01 },
+				{ 0x00, 0x01, 0x01, 0x01 },
+				{ 0x01, 0x01, 0x01, 0x01 }
+	};
+
+	BUG_ON(MAX_LEDS < ARRAY_SIZE(sixaxis_leds[0]));
+
+	if (id < 0)
+		return;
+
+	id %= 10;
+	memcpy(values, sixaxis_leds[id], sizeof(sixaxis_leds[id]));
+}
+
+static void dualshock4_set_leds_from_id(int id, __u8 values[MAX_LEDS])
+{
+	/* The first 4 color/index entries match what the PS4 assigns */
+	static const __u8 color_code[7][3] = {
+			/* Blue   */	{ 0x00, 0x00, 0x01 },
+			/* Red	  */	{ 0x01, 0x00, 0x00 },
+			/* Green  */	{ 0x00, 0x01, 0x00 },
+			/* Pink   */	{ 0x02, 0x00, 0x01 },
+			/* Orange */	{ 0x02, 0x01, 0x00 },
+			/* Teal   */	{ 0x00, 0x01, 0x01 },
+			/* White  */	{ 0x01, 0x01, 0x01 }
+	};
+
+	BUG_ON(MAX_LEDS < ARRAY_SIZE(color_code[0]));
+
+	if (id < 0)
+		return;
+
+	id %= 7;
+	memcpy(values, color_code[id], sizeof(color_code[id]));
+}
+
 static void buzz_set_leds(struct hid_device *hdev, const __u8 *leds)
 {
 	struct list_head *report_list =
@@ -1194,7 +1240,7 @@ static int sony_leds_init(struct sony_sc *sc)
 	size_t name_len;
 	const char *name_fmt;
 	static const char * const color_str[] = { "red", "green", "blue" };
-	static const __u8 initial_values[MAX_LEDS] = { 0x00, 0x00, 0x00, 0x00 };
+	__u8 initial_values[MAX_LEDS] = { 0 };
 
 	BUG_ON(!(sc->quirks & SONY_LED_SUPPORT));
 
@@ -1208,12 +1254,14 @@ static int sony_leds_init(struct sony_sc *sc)
 		if (!hid_validate_values(hdev, HID_OUTPUT_REPORT, 0, 0, 7))
 			return -ENODEV;
 	} else if (sc->quirks & DUALSHOCK4_CONTROLLER) {
+		dualshock4_set_leds_from_id(sc->device_id, initial_values);
 		sc->led_count = 3;
 		max_brightness = 255;
 		use_colors = 1;
 		name_len = 0;
 		name_fmt = "%s:%s";
 	} else {
+		sixaxis_set_leds_from_id(sc->device_id, initial_values);
 		sc->led_count = 4;
 		max_brightness = 1;
 		use_colors = 0;
@@ -1248,19 +1296,20 @@ static int sony_leds_init(struct sony_sc *sc)
 		else
 			snprintf(name, name_sz, name_fmt, dev_name(&hdev->dev), n + 1);
 		led->name = name;
-		led->brightness = 0;
+		led->brightness = initial_values[n];
 		led->max_brightness = max_brightness;
 		led->brightness_get = sony_led_get_brightness;
 		led->brightness_set = sony_led_set_brightness;
 
+		sc->leds[n] = led;
+
 		ret = led_classdev_register(&hdev->dev, led);
 		if (ret) {
 			hid_err(hdev, "Failed to register LED %d\n", n);
+			sc->leds[n] = NULL;
 			kfree(led);
 			goto error_leds;
 		}
-
-		sc->leds[n] = led;
 	}
 
 	return ret;
-- 
1.8.5.3


^ permalink raw reply related

* [PATCH v2 8/8] HID: sony: Add blink support to the Sixaxis and DualShock 4 LEDs
From: Frank Praznik @ 2014-03-06 22:32 UTC (permalink / raw)
  To: linux-input; +Cc: jkosina, dh.herrmann, Frank Praznik
In-Reply-To: <1394145176-24174-1-git-send-email-frank.praznik@oh.rr.com>

Add support for setting the blink rate of the LEDs.  The Sixaxis allows control
over each individual LED, but the Dualshock 4 only has one global control for
the light bar so changing any individual color changes the global blink rate.

Setting the brightness cancels the blinking as per the LED class specifications.

The Sixaxis and Dualshock 4 controllers accept delays in decisecond increments
from 0 to 255 (2550 milliseconds).

The value at index 1 of the DualShock 4 USB output report must be 0xFF or the
light bar won't blink.

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

 v2 addresses various code review issues and fixes a bug where the DualShock 4
 wouldn't blink when connected via USB unless byte 1 of the report is 0xFF.

 drivers/hid/hid-sony.c | 113 +++++++++++++++++++++++++++++++++++++++++++++----
 1 file changed, 104 insertions(+), 9 deletions(-)

diff --git a/drivers/hid/hid-sony.c b/drivers/hid/hid-sony.c
index 6de42b4..585d9c7 100644
--- a/drivers/hid/hid-sony.c
+++ b/drivers/hid/hid-sony.c
@@ -773,6 +773,8 @@ struct sony_sc {
 	__u8 battery_charging;
 	__u8 battery_capacity;
 	__u8 led_state[MAX_LEDS];
+	__u8 led_delay_on[MAX_LEDS];
+	__u8 led_delay_off[MAX_LEDS];
 	__u8 led_count;
 };
 
@@ -1167,7 +1169,7 @@ static void sony_led_set_brightness(struct led_classdev *led,
 	struct hid_device *hdev = container_of(dev, struct hid_device, dev);
 	struct sony_sc *drv_data;
 
-	int n;
+	int n, blink_index;
 
 	drv_data = hid_get_drvdata(hdev);
 	if (!drv_data) {
@@ -1176,14 +1178,31 @@ static void sony_led_set_brightness(struct led_classdev *led,
 	}
 
 	for (n = 0; n < drv_data->led_count; n++) {
-		if (led == drv_data->leds[n]) {
-			if (value != drv_data->led_state[n]) {
-				drv_data->led_state[n] = value;
-				sony_set_leds(drv_data, drv_data->led_state,
-						drv_data->led_count);
-			}
+		if (led == drv_data->leds[n])
 			break;
-		}
+	}
+
+	/* This LED is not registered on this device */
+	if (n >= drv_data->led_count)
+		return;
+
+	/* The DualShock 4 has a global blink setting and always uses index 0 */
+	if (drv_data->quirks & DUALSHOCK4_CONTROLLER)
+		blink_index = 0;
+	else
+		blink_index = n;
+
+	if ((value != drv_data->led_state[n]) ||
+		drv_data->led_delay_on[blink_index] ||
+		drv_data->led_delay_off[blink_index]) {
+		drv_data->led_state[n] = value;
+
+		/* Setting the brightness stops the blinking */
+		drv_data->led_delay_on[blink_index] = 0;
+		drv_data->led_delay_off[blink_index] = 0;
+
+		sony_set_leds(drv_data, drv_data->led_state,
+				drv_data->led_count);
 	}
 }
 
@@ -1209,6 +1228,58 @@ static enum led_brightness sony_led_get_brightness(struct led_classdev *led)
 	return LED_OFF;
 }
 
+static int sony_led_blink_set(struct led_classdev *led, unsigned long *delay_on,
+				unsigned long *delay_off)
+{
+	struct device *dev = led->dev->parent;
+	struct hid_device *hdev = container_of(dev, struct hid_device, dev);
+	struct sony_sc *drv_data = hid_get_drvdata(hdev);
+	int n;
+	__u8 new_on, new_off;
+
+	if (!drv_data) {
+		hid_err(hdev, "No device data\n");
+		return -EINVAL;
+	}
+
+	/* Max delay is 255 deciseconds or 2550 milliseconds */
+	if (*delay_on > 2550)
+		*delay_on = 2550;
+	if (*delay_off > 2550)
+		*delay_off = 2550;
+
+	/* Blink at 1 Hz if both values are zero */
+	if (!*delay_on && !*delay_off)
+		*delay_on = *delay_off = 1000;
+
+	new_on = *delay_on / 10;
+	new_off = *delay_off / 10;
+
+	/* The DualShock 4 has a global blink setting and always uses index 0 */
+	if (drv_data->quirks & DUALSHOCK4_CONTROLLER) {
+		n = 0;
+	} else {
+		for (n = 0; n < drv_data->led_count; n++) {
+			if (led == drv_data->leds[n])
+				break;
+		}
+	}
+
+	/* This LED is not registered on this device */
+	if (n >= drv_data->led_count)
+		return -EINVAL;
+
+	/* Don't schedule work if the values didn't change */
+	if (new_on != drv_data->led_delay_on[n] ||
+		new_off != drv_data->led_delay_off[n]) {
+		drv_data->led_delay_on[n] = new_on;
+		drv_data->led_delay_off[n] = new_off;
+		schedule_work(&drv_data->state_worker);
+	}
+
+	return 0;
+}
+
 static void sony_leds_remove(struct sony_sc *sc)
 {
 	struct led_classdev *led;
@@ -1301,6 +1372,9 @@ static int sony_leds_init(struct sony_sc *sc)
 		led->brightness_get = sony_led_get_brightness;
 		led->brightness_set = sony_led_set_brightness;
 
+		if (!(sc->quirks & BUZZ_CONTROLLER))
+			led->blink_set = sony_led_blink_set;
+
 		sc->leds[n] = led;
 
 		ret = led_classdev_register(&hdev->dev, led);
@@ -1323,6 +1397,7 @@ error_leds:
 static void sixaxis_state_worker(struct work_struct *work)
 {
 	struct sony_sc *sc = container_of(work, struct sony_sc, state_worker);
+	int n;
 	union sixaxis_output_report_01 report = {
 		.buf = {
 			0x01,
@@ -1346,6 +1421,22 @@ static void sixaxis_state_worker(struct work_struct *work)
 	report.data.leds_bitmap |= sc->led_state[2] << 3;
 	report.data.leds_bitmap |= sc->led_state[3] << 4;
 
+	/*
+	 * The LEDs in the report are indexed in reverse order to their
+	 * corresponding light on the controller.
+	 * Index 0 = LED 4, index 1 = LED 3, etc...
+	 *
+	 * In the case of both delay values being zero (blinking disabled) the
+	 * default report values should be used or the controller LED will be
+	 * always off.
+	 */
+	for (n = 0; n < 4; n++) {
+		if (sc->led_delay_on[n] || sc->led_delay_off[n]) {
+			report.data.led[3 - n].duty_off = sc->led_delay_off[n];
+			report.data.led[3 - n].duty_on = sc->led_delay_on[n];
+		}
+	}
+
 	hid_hw_raw_request(sc->hdev, report.data.report_id, report.buf,
 			sizeof(report), HID_OUTPUT_REPORT, HID_REQ_SET_REPORT);
 }
@@ -1360,7 +1451,7 @@ static void dualshock4_state_worker(struct work_struct *work)
 
 	if (sc->quirks & DUALSHOCK4_CONTROLLER_USB) {
 		buf[0] = 0x05;
-		buf[1] = 0x03;
+		buf[1] = 0xFF;
 		offset = 4;
 	} else {
 		buf[0] = 0x11;
@@ -1380,6 +1471,10 @@ static void dualshock4_state_worker(struct work_struct *work)
 	buf[offset++] = sc->led_state[1];
 	buf[offset++] = sc->led_state[2];
 
+	/* If both delay values are zero the DualShock 4 disables blinking. */
+	buf[offset++] = sc->led_delay_on[0];
+	buf[offset++] = sc->led_delay_off[0];
+
 	if (sc->quirks & DUALSHOCK4_CONTROLLER_USB)
 		hid_hw_output_report(hdev, buf, 32);
 	else
-- 
1.8.5.3


^ permalink raw reply related

* Re: [PATCH] iio:hid-sensor-hub: Add in quirk for sensor hub in Lenovo Ideapad Yogas
From: Srinivas Pandruvada @ 2014-03-06 22:43 UTC (permalink / raw)
  To: Peter F. Patel-Schneider; +Cc: linux-input
In-Reply-To: <5318E151.9040702@gmail.com>

On 03/06/2014 12:57 PM, Peter F. Patel-Schneider wrote:
> The sensor hub in Lenovo Yogas needs the enumeration quirk.  I've been 
> running the patch for about a week with no problems, whereas the 
> unpatched drivers reliably mis-initialized the sensors.
>
> Signed-off-by: Peter F. Patel-Schneider <pfpschneider@gmail.com>
> ---
>  drivers/hid/hid-ids.h        | 3 +++
>  drivers/hid/hid-sensor-hub.c | 3 +++
>  2 files changed, 6 insertions(+)
>
> diff --git a/drivers/hid/hid-ids.h b/drivers/hid/hid-ids.h
> index 5a5248f..f944e37 100644
> --- a/drivers/hid/hid-ids.h
> +++ b/drivers/hid/hid-ids.h
> @@ -823,6 +823,9 @@
>  #define USB_DEVICE_ID_SYNAPTICS_HD    0x0ac3
>  #define USB_DEVICE_ID_SYNAPTICS_QUAD_HD    0x1ac3
>
> +#define USB_VENDOR_ID_TEXAS_INSTRUMENTS    0x2047
> +#define USB_DEVICE_ID_TEXAS_INSTRUMENTS_LENOVO_YOGA    0x0855
> +
>  #define USB_VENDOR_ID_THINGM        0x27b8
>  #define USB_DEVICE_ID_BLINK1        0x01ed
>
> diff --git a/drivers/hid/hid-sensor-hub.c b/drivers/hid/hid-sensor-hub.c
> index 46f4480..597732b 100644
> --- a/drivers/hid/hid-sensor-hub.c
> +++ b/drivers/hid/hid-sensor-hub.c
> @@ -665,6 +665,9 @@ static const struct hid_device_id 
> sensor_hub_devices[] = {
>      { HID_DEVICE(HID_BUS_ANY, HID_GROUP_SENSOR_HUB, 
> USB_VENDOR_ID_INTEL_1,
>              USB_DEVICE_ID_INTEL_HID_SENSOR),
>              .driver_data = HID_SENSOR_HUB_ENUM_QUIRK},
> +    { HID_DEVICE(HID_BUS_ANY, HID_GROUP_SENSOR_HUB, 
> USB_VENDOR_ID_TEXAS_INSTRUMENTS,
> +             USB_DEVICE_ID_TEXAS_INSTRUMENTS_LENOVO_YOGA),
> +             .driver_data = HID_SENSOR_HUB_ENUM_QUIRK},
>      { HID_DEVICE(HID_BUS_ANY, HID_GROUP_SENSOR_HUB, HID_ANY_ID,
>               HID_ANY_ID) },
>      { }
>
Acked-by: Srinivas Pandruvada <srinivas.pandruvada@linux.intel.com>


^ permalink raw reply

* [PATCH 1/1] HID: microsoft: adding support for Sidewinder X4 and X6 keyboards
From: Tolga Cakir @ 2014-03-07  1:39 UTC (permalink / raw)
  To: jkosina, linux-input; +Cc: linux-kernel, Tolga Cakir

Microsoft Sidewinder X4 and X6 gaming keyboards offer special keys,
which now can be read via sysfs, so they can be handled by user-space
tools. Profile switching support, LED support and macro pad
support (X6 only) are implemented aswell.

Both keyboards have been tested and run perfectly well.

Signed-off-by: Tolga Cakir <tolga@cevel.net>
---
 .../ABI/testing/sysfs-driver-hid-microsoft         |  31 ++
 drivers/hid/hid-core.c                             |   2 +
 drivers/hid/hid-ids.h                              |   2 +
 drivers/hid/hid-microsoft.c                        | 390 ++++++++++++++++++++-
 4 files changed, 410 insertions(+), 15 deletions(-)
 create mode 100644 Documentation/ABI/testing/sysfs-driver-hid-microsoft

diff --git a/Documentation/ABI/testing/sysfs-driver-hid-microsoft b/Documentation/ABI/testing/sysfs-driver-hid-microsoft
new file mode 100644
index 0000000..35a90c7
--- /dev/null
+++ b/Documentation/ABI/testing/sysfs-driver-hid-microsoft
@@ -0,0 +1,31 @@
+What:		/sys/bus/usb/devices/<busnum>-<devnum>:<config num>.<interface num>/<hid-bus>:<vendor-id>:<product-id>.<num>/auto_led
+Date:		March 2014
+Contact:	Tolga Cakir <cevelnet@gmail.com>
+Description:	This file allows you to set and view the Auto LED status.
+		Valid values are 0 and 1.
+
+What:		/sys/bus/usb/devices/<busnum>-<devnum>:<config num>.<interface num>/<hid-bus>:<vendor-id>:<product-id>.<num>/profile
+Date:		March 2014
+Contact:	Tolga Cakir <cevelnet@gmail.com>
+Description:	Both, the Sidewinder X4 and the X6 can handle profiles.
+		They can be switched either by pressing the Bank switch button, or
+		writing to this file.
+		Valid values are 1 - 3.
+
+What:		/sys/bus/usb/devices/<busnum>-<devnum>:<config num>.<interface num>/<hid-bus>:<vendor-id>:<product-id>.<num>/key_mask
+Date:		March 2014
+Contact:	Tolga Cakir <cevelnet@gmail.com>
+Description:	This file is read-only and outputs an unsigned long integer.
+		Every bit individually represents one of the S1 - S6 extra keys on
+		the Sidewinder X4 and S1 - S30 on the Sidewinder X6. The least
+		significant bit represents S1 on both gaming keyboards, the most
+		significant bit represents S6 on the Sidewinder X4 and S30 on the
+		Sidewinder X6 keyboard. Multiple special keys can be pressed at the
+		same time.
+
+What:		/sys/bus/usb/devices/<busnum>-<devnum>:<config num>.<interface num>/<hid-bus>:<vendor-id>:<product-id>.<num>/record_led
+Date:		March 2014
+Contact:	Tolga Cakir <cevelnet@gmail.com>
+Description:	This file allows you to set and view the Record LED status.
+		Valid values are 0 - 2. 0 stands for off, 1 for solid mode and 2
+		for blinking mode.
diff --git a/drivers/hid/hid-core.c b/drivers/hid/hid-core.c
index cc32a6f..6b3435a 100644
--- a/drivers/hid/hid-core.c
+++ b/drivers/hid/hid-core.c
@@ -1774,6 +1774,8 @@ static const struct hid_device_id hid_have_special_driver[] = {
 	{ HID_USB_DEVICE(USB_VENDOR_ID_MICROCHIP, USB_DEVICE_ID_PICOLCD_BOOTLOADER) },
 	{ HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_COMFORT_MOUSE_4500) },
 	{ HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_SIDEWINDER_GV) },
+	{ HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_SIDEWINDER_X6) },
+	{ HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_SIDEWINDER_X4) },
 	{ HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_NE4K) },
 	{ HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_NE4K_JP) },
 	{ HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_LK6K) },
diff --git a/drivers/hid/hid-ids.h b/drivers/hid/hid-ids.h
index 22f28d6..4268d35 100644
--- a/drivers/hid/hid-ids.h
+++ b/drivers/hid/hid-ids.h
@@ -623,6 +623,8 @@
 #define USB_DEVICE_ID_MS_PRESENTER_8K_BT	0x0701
 #define USB_DEVICE_ID_MS_PRESENTER_8K_USB	0x0713
 #define USB_DEVICE_ID_MS_DIGITAL_MEDIA_3K	0x0730
+#define USB_DEVICE_ID_SIDEWINDER_X6	0x074b
+#define USB_DEVICE_ID_SIDEWINDER_X4	0x0768
 #define USB_DEVICE_ID_MS_COMFORT_MOUSE_4500	0x076c
 #define USB_DEVICE_ID_MS_TOUCH_COVER_2	0x07a7
 #define USB_DEVICE_ID_MS_TYPE_COVER_2	0x07a9
diff --git a/drivers/hid/hid-microsoft.c b/drivers/hid/hid-microsoft.c
index 404a3a8..154ac93 100644
--- a/drivers/hid/hid-microsoft.c
+++ b/drivers/hid/hid-microsoft.c
@@ -19,6 +19,8 @@
 #include <linux/input.h>
 #include <linux/hid.h>
 #include <linux/module.h>
+#include <linux/sysfs.h>
+#include <linux/usb.h>
 
 #include "hid-ids.h"
 
@@ -29,24 +31,47 @@
 #define MS_NOGET		0x10
 #define MS_DUPLICATE_USAGES	0x20
 #define MS_RDESC_3K		0x40
+#define MS_SIDEWINDER	0x80
+
+struct ms_data {
+	unsigned long quirks;
+	void *extra;
+};
+
+/*
+ * For Sidewinder X4 / X6 devices.
+ * @profile: currently, only 3 profiles are used, eventhough it would
+ * be possible to set up more (combining LEDs 1 -3 for profile
+ * indication).
+ * @status: holds information about LED states and numpad mode (X6
+ * only). The 1st bit is for numpad mode, bits 2 - 7 are reserved for
+ * LED configuration and the last bit is currently unused.
+ * @key_mask: holds information about pressed special keys. It's
+ * readable via sysfs, so user-space tools can handle keypresses.
+ */
+struct ms_sidewinder_extra {
+	unsigned profile;
+	__u8 status;
+	unsigned long key_mask;
+};
 
 static __u8 *ms_report_fixup(struct hid_device *hdev, __u8 *rdesc,
 		unsigned int *rsize)
 {
-	unsigned long quirks = (unsigned long)hid_get_drvdata(hdev);
+	struct ms_data *sc = hid_get_drvdata(hdev);
 
 	/*
 	 * Microsoft Wireless Desktop Receiver (Model 1028) has
 	 * 'Usage Min/Max' where it ought to have 'Physical Min/Max'
 	 */
-	if ((quirks & MS_RDESC) && *rsize == 571 && rdesc[557] == 0x19 &&
+	if ((sc->quirks & MS_RDESC) && *rsize == 571 && rdesc[557] == 0x19 &&
 			rdesc[559] == 0x29) {
 		hid_info(hdev, "fixing up Microsoft Wireless Receiver Model 1028 report descriptor\n");
 		rdesc[557] = 0x35;
 		rdesc[559] = 0x45;
 	}
 	/* the same as above (s/usage/physical/) */
-	if ((quirks & MS_RDESC_3K) && *rsize == 106 && rdesc[94] == 0x19 &&
+	if ((sc->quirks & MS_RDESC_3K) && *rsize == 106 && rdesc[94] == 0x19 &&
 			rdesc[95] == 0x00 && rdesc[96] == 0x29 &&
 			rdesc[97] == 0xff) {
 		rdesc[94] = 0x35;
@@ -96,25 +121,261 @@ static int ms_presenter_8k_quirk(struct hid_input *hi, struct hid_usage *usage,
 	return 1;
 }
 
+static int ms_sidewinder_kb_quirk(struct hid_input *hi, struct hid_usage *usage,
+		unsigned long **bit, int *max)
+{
+	set_bit(EV_REP, hi->input->evbit);
+	switch (usage->hid & HID_USAGE) {
+	/*
+	 * Registering Sidewinder X4 / X6 special keys. S1 - S6 macro keys
+	 * are shared between Sidewinder X4 & X6 and are programmable.
+	 */
+	case 0xfb01: ms_map_key_clear(KEY_UNKNOWN);	break;	/* S1 */
+	case 0xfb02: ms_map_key_clear(KEY_UNKNOWN);	break;	/* S2 */
+	case 0xfb03: ms_map_key_clear(KEY_UNKNOWN);	break;	/* S3 */
+	case 0xfb04: ms_map_key_clear(KEY_UNKNOWN);	break;	/* S4 */
+	case 0xfb05: ms_map_key_clear(KEY_UNKNOWN);	break;	/* S5 */
+	case 0xfb06: ms_map_key_clear(KEY_UNKNOWN);	break;	/* S6 */
+	/* S7 - S30 macro keys are only present on the Sidewinder X6 */
+	case 0xfb07: ms_map_key_clear(KEY_UNKNOWN);	break;	/* S7 */
+	case 0xfb08: ms_map_key_clear(KEY_UNKNOWN);	break;	/* S8 */
+	case 0xfb09: ms_map_key_clear(KEY_UNKNOWN);	break;	/* S9 */
+	case 0xfb0a: ms_map_key_clear(KEY_UNKNOWN);	break;	/* S10 */
+	case 0xfb0b: ms_map_key_clear(KEY_UNKNOWN);	break;	/* S11 */
+	case 0xfb0c: ms_map_key_clear(KEY_UNKNOWN);	break;	/* S12 */
+	case 0xfb0d: ms_map_key_clear(KEY_UNKNOWN);	break;	/* S13 */
+	case 0xfb0e: ms_map_key_clear(KEY_UNKNOWN);	break;	/* S14 */
+	case 0xfb0f: ms_map_key_clear(KEY_UNKNOWN);	break;	/* S15 */
+	case 0xfb10: ms_map_key_clear(KEY_UNKNOWN);	break;	/* S16 */
+	case 0xfb11: ms_map_key_clear(KEY_UNKNOWN);	break;	/* S17 */
+	case 0xfb12: ms_map_key_clear(KEY_UNKNOWN);	break;	/* S18 */
+	case 0xfb13: ms_map_key_clear(KEY_UNKNOWN);	break;	/* S19 */
+	case 0xfb14: ms_map_key_clear(KEY_UNKNOWN);	break;	/* S20 */
+	case 0xfb15: ms_map_key_clear(KEY_UNKNOWN);	break;	/* S21 */
+	case 0xfb16: ms_map_key_clear(KEY_UNKNOWN);	break;	/* S22 */
+	case 0xfb17: ms_map_key_clear(KEY_UNKNOWN);	break;	/* S23 */
+	case 0xfb18: ms_map_key_clear(KEY_UNKNOWN);	break;	/* S24 */
+	case 0xfb19: ms_map_key_clear(KEY_UNKNOWN);	break;	/* S25 */
+	case 0xfb1a: ms_map_key_clear(KEY_UNKNOWN);	break;	/* S26 */
+	case 0xfb1b: ms_map_key_clear(KEY_UNKNOWN);	break;	/* S27 */
+	case 0xfb1c: ms_map_key_clear(KEY_UNKNOWN);	break;	/* S28 */
+	case 0xfb1d: ms_map_key_clear(KEY_UNKNOWN);	break;	/* S29 */
+	case 0xfb1e: ms_map_key_clear(KEY_UNKNOWN);	break;	/* S30 */
+	/* Not programmable keys: Profile, Game Center (X6 only) and Macro Key */
+	case 0xfd11: ms_map_key_clear(KEY_UNKNOWN);	break;	/* X6 only: Macro Pad toggle key*/
+	case 0xfd12: ms_map_key_clear(KEY_MACRO);	break;	/* Macro Record key */
+	case 0xfd15: ms_map_key_clear(KEY_UNKNOWN);	break;	/* Profile switch key */
+	default:
+		return 0;
+	}
+	return 1;
+}
+#undef ms_map_key_clear
+
+static int ms_sidewinder_control(struct hid_device *hdev, __u8 setup)
+{
+	struct ms_data *sc = hid_get_drvdata(hdev);
+	struct ms_sidewinder_extra *sidewinder = sc->extra;
+	struct hid_report *report =
+			hdev->report_enum[HID_FEATURE_REPORT].report_id_hash[7];
+
+	/*
+	 * LEDs 1 - 3 should not be set simultaneously, however
+	 * they can be set in any combination with Auto or Record LEDs.
+	 */
+	report->field[0]->value[0] = (setup & 0x01) ? 0x01 : 0x00;	/* X6 only: Macro Pad toggle */
+	report->field[0]->value[1] = (setup & 0x02) ? 0x01 : 0x00;	/* LED Auto */
+	report->field[0]->value[2] = (setup & 0x04) ? 0x01 : 0x00;	/* LED 1 */
+	report->field[0]->value[3] = (setup & 0x08) ? 0x01 : 0x00;	/* LED 2 */
+	report->field[0]->value[4] = (setup & 0x10) ? 0x01 : 0x00;	/* LED 3 */
+	report->field[1]->value[0] = 0x00;	/* Clear Record LED */
+
+	switch (setup & 0x60) {
+	case 0x40: report->field[1]->value[0] = 0x02;	break;	/* Record LED Blink */
+	case 0x20: report->field[1]->value[0] = 0x03;	break;	/* Record LED Solid */
+	}
+
+	/*
+	 * Check if there are any changes, in order to avoid unnecessary
+	 * setup packets. Both, the Sidewinder X4 and X6, have identical
+	 * USB communication.
+	 */
+	if (sidewinder->status != setup) {
+		hid_hw_request(hdev, report, HID_REQ_SET_REPORT);
+		sidewinder->status = setup;
+	}
+
+	return 0;
+}
+
+/*
+ * Sidewinder sysfs
+ * @key_mask: show pressed special keys
+ * @profile: show and set profile count and LED status
+ * @auto_led: show and set LED Auto
+ * @record_led: show and set Record LED
+ */
+static ssize_t ms_sidewinder_key_mask_show(struct device *dev,
+		struct device_attribute *attr, char *buf)
+{
+	struct hid_device *hdev = container_of(dev, struct hid_device, dev);
+	struct ms_data *sc = hid_get_drvdata(hdev);
+	struct ms_sidewinder_extra *sidewinder = sc->extra;
+
+	return snprintf(buf, PAGE_SIZE, "%lu\n", sidewinder->key_mask);
+}
+
+static struct device_attribute dev_attr_ms_sidewinder_key_mask = {
+	.attr = { .name = __stringify(key_mask), .mode = S_IRUGO },
+	.show = ms_sidewinder_key_mask_show
+};
+
+static ssize_t ms_sidewinder_profile_show(struct device *dev,
+		struct device_attribute *attr, char *buf)
+{
+	struct hid_device *hdev = container_of(dev, struct hid_device, dev);
+	struct ms_data *sc = hid_get_drvdata(hdev);
+	struct ms_sidewinder_extra *sidewinder = sc->extra;
+
+	return snprintf(buf, PAGE_SIZE, "%1u\n", sidewinder->profile);
+}
+
+static ssize_t ms_sidewinder_profile_store(struct device *dev,
+		struct device_attribute *attr, char const *buf, size_t count)
+{
+	struct hid_device *hdev = container_of(dev, struct hid_device, dev);
+	struct ms_data *sc = hid_get_drvdata(hdev);
+	struct ms_sidewinder_extra *sidewinder = sc->extra;
+	__u8 leds = sidewinder->status & ~(0x1c);	/* Clear Profile LEDs */
+
+	if (sscanf(buf, "%1u", &sidewinder->profile) != 1)
+		return -EINVAL;
+
+	if (sidewinder->profile >= 1 && sidewinder->profile <= 3) {
+		leds |= 0x02 << sidewinder->profile;
+		ms_sidewinder_control(hdev, leds);
+		return strnlen(buf, PAGE_SIZE);
+	} else {
+		return -EINVAL;
+	}
+}
+
+static struct device_attribute dev_attr_ms_sidewinder_profile =
+	__ATTR(profile, S_IWUSR | S_IRUGO,
+		ms_sidewinder_profile_show,
+		ms_sidewinder_profile_store);
+
+static ssize_t ms_sidewinder_record_show(struct device *dev,
+		struct device_attribute *attr, char *buf)
+{
+	struct hid_device *hdev = container_of(dev, struct hid_device, dev);
+	struct ms_data *sc = hid_get_drvdata(hdev);
+	struct ms_sidewinder_extra *sidewinder = sc->extra;
+
+	return snprintf(buf, PAGE_SIZE, "%1d\n", (sidewinder->status & 0x60) >> 5);
+}
+
+static ssize_t ms_sidewinder_record_store(struct device *dev,
+		struct device_attribute *attr, char const *buf, size_t count)
+{
+	struct hid_device *hdev = container_of(dev, struct hid_device, dev);
+	struct ms_data *sc = hid_get_drvdata(hdev);
+	struct ms_sidewinder_extra *sidewinder = sc->extra;
+	unsigned int record_led;
+	__u8 leds;
+
+	if (sscanf(buf, "%1d", &record_led) != 1)
+		return -EINVAL;
+
+	if (record_led >= 0 && record_led <= 2) {
+		leds = sidewinder->status & ~(0xe0);	/* Clear Record LED */
+		if (record_led)
+			leds |= 0x10 << record_led;
+		ms_sidewinder_control(hdev, leds);
+		return strnlen(buf, PAGE_SIZE);
+	} else {
+		return -EINVAL;
+	}
+}
+
+static struct device_attribute dev_attr_ms_sidewinder_record =
+	__ATTR(record_led, S_IWUSR | S_IRUGO,
+		ms_sidewinder_record_show,
+		ms_sidewinder_record_store);
+
+static ssize_t ms_sidewinder_auto_show(struct device *dev,
+		struct device_attribute *attr, char *buf)
+{
+	struct hid_device *hdev = container_of(dev, struct hid_device, dev);
+	struct ms_data *sc = hid_get_drvdata(hdev);
+	struct ms_sidewinder_extra *sidewinder = sc->extra;
+
+	return snprintf(buf, PAGE_SIZE, "%1d\n", (sidewinder->status & 0x02) >> 1);	/* Check if Auto LED bit is set */
+}
+
+static ssize_t ms_sidewinder_auto_store(struct device *dev,
+		struct device_attribute *attr, char const *buf, size_t count)
+{
+	struct hid_device *hdev = container_of(dev, struct hid_device, dev);
+	struct ms_data *sc = hid_get_drvdata(hdev);
+	struct ms_sidewinder_extra *sidewinder = sc->extra;
+	unsigned int auto_led;
+	__u8 leds;
+
+	if (sscanf(buf, "%1d", &auto_led) != 1)
+		return -EINVAL;
+
+	if (auto_led == 0 || auto_led == 1) {
+		leds = sidewinder->status & ~(0x02);	/* Clear Auto LED */
+		if (auto_led)
+			leds |= 0x02;
+		ms_sidewinder_control(hdev, leds);
+		return strnlen(buf, PAGE_SIZE);
+	} else {
+		return -EINVAL;
+	}
+}
+
+static struct device_attribute dev_attr_ms_sidewinder_auto =
+	__ATTR(auto_led, S_IWUSR | S_IRUGO,
+		ms_sidewinder_auto_show,
+		ms_sidewinder_auto_store);
+
+static struct attribute *ms_attributes[] = {
+	&dev_attr_ms_sidewinder_key_mask.attr,
+	&dev_attr_ms_sidewinder_profile.attr,
+	&dev_attr_ms_sidewinder_record.attr,
+	&dev_attr_ms_sidewinder_auto.attr,
+	NULL
+};
+
+static const struct attribute_group ms_attr_group = {
+	.attrs = ms_attributes,
+};
+
 static int ms_input_mapping(struct hid_device *hdev, struct hid_input *hi,
 		struct hid_field *field, struct hid_usage *usage,
 		unsigned long **bit, int *max)
 {
-	unsigned long quirks = (unsigned long)hid_get_drvdata(hdev);
+	struct ms_data *sc = hid_get_drvdata(hdev);
 
 	if ((usage->hid & HID_USAGE_PAGE) != HID_UP_MSVENDOR)
 		return 0;
 
-	if (quirks & MS_ERGONOMY) {
+	if (sc->quirks & MS_ERGONOMY) {
 		int ret = ms_ergonomy_kb_quirk(hi, usage, bit, max);
 		if (ret)
 			return ret;
 	}
 
-	if ((quirks & MS_PRESENTER) &&
+	if ((sc->quirks & MS_PRESENTER) &&
 			ms_presenter_8k_quirk(hi, usage, bit, max))
 		return 1;
 
+	if ((sc->quirks & MS_SIDEWINDER) &&
+			ms_sidewinder_kb_quirk(hi, usage, bit, max))
+		return 1;
+
 	return 0;
 }
 
@@ -122,25 +383,39 @@ static int ms_input_mapped(struct hid_device *hdev, struct hid_input *hi,
 		struct hid_field *field, struct hid_usage *usage,
 		unsigned long **bit, int *max)
 {
-	unsigned long quirks = (unsigned long)hid_get_drvdata(hdev);
+	struct ms_data *sc = hid_get_drvdata(hdev);
 
-	if (quirks & MS_DUPLICATE_USAGES)
+	if (sc->quirks & MS_DUPLICATE_USAGES)
 		clear_bit(usage->code, *bit);
 
 	return 0;
 }
 
+/* Setting initial profile and LED of Sidewinder keyboards */
+static void ms_feature_mapping(struct hid_device *hdev,
+		struct hid_field *field, struct hid_usage *usage)
+{
+	struct ms_data *sc = hid_get_drvdata(hdev);
+
+	if (sc->quirks & MS_SIDEWINDER) {
+		struct ms_sidewinder_extra *sidewinder = sc->extra;
+
+		sidewinder->profile = 1;
+		ms_sidewinder_control(hdev, 0x02 << sidewinder->profile);
+	}
+}
+
 static int ms_event(struct hid_device *hdev, struct hid_field *field,
 		struct hid_usage *usage, __s32 value)
 {
-	unsigned long quirks = (unsigned long)hid_get_drvdata(hdev);
+	struct ms_data *sc = hid_get_drvdata(hdev);
 
 	if (!(hdev->claimed & HID_CLAIMED_INPUT) || !field->hidinput ||
 			!usage->type)
 		return 0;
 
 	/* Handling MS keyboards special buttons */
-	if (quirks & MS_ERGONOMY && usage->hid == (HID_UP_MSVENDOR | 0xff05)) {
+	if (sc->quirks & MS_ERGONOMY && usage->hid == (HID_UP_MSVENDOR | 0xff05)) {
 		struct input_dev *input = field->hidinput->input;
 		static unsigned int last_key = 0;
 		unsigned int key = 0;
@@ -154,8 +429,53 @@ static int ms_event(struct hid_device *hdev, struct hid_field *field,
 		if (key) {
 			input_event(input, usage->type, key, 1);
 			last_key = key;
-		} else
+		} else {
 			input_event(input, usage->type, last_key, 0);
+		}
+
+		return 1;
+	}
+
+	/*
+	 * Sidewinder special button handling & profile switching
+	 *
+	 * Pressing S1 - S30 macro keys will not send out any keycodes, but
+	 * set bits on key_mask (readable via sysfs). It's possible to press
+	 * multiple special keys at the same time.
+	 */
+	if (sc->quirks & MS_SIDEWINDER) {
+		struct input_dev *input = field->hidinput->input;
+		struct ms_sidewinder_extra *sidewinder = sc->extra;
+		int i;
+
+		for (i = 0; i <= 29; i++) {	/* Run through S1 - S30 keys */
+			if ((usage->hid & HID_USAGE) == (0xfb01 + i)) {
+				value ? set_bit(i, &sidewinder->key_mask) : clear_bit(i, &sidewinder->key_mask);
+				break;	/* Exit loop, when correct hid usage has been found */
+			}
+		}
+
+		switch (usage->hid & HID_USAGE) {
+		case 0xfd11:
+			if (value) {	/* Run this only once on a keypress */
+				__u8 numpad = sidewinder->status ^ (0x01);	/* Toggle Macro Pad */
+				ms_sidewinder_control(hdev, numpad);
+			}
+			break;
+		case 0xfd12: input_event(input, usage->type, KEY_MACRO, value);	break;
+		case 0xfd15:
+			if (value) {	/* Run this only once on a keypress */
+				__u8 leds = sidewinder->status & ~(0x1c);	/* Clear Profile LEDs */
+				if (sidewinder->profile < 1 || sidewinder->profile >= 3) {
+					sidewinder->profile = 1;
+				} else
+					sidewinder->profile++;
+
+				leds |= 0x02 << sidewinder->profile;	/* Set Profile LEDs */
+				ms_sidewinder_control(hdev, leds);
+			}
+			break;
+		}
 
 		return 1;
 	}
@@ -165,21 +485,47 @@ static int ms_event(struct hid_device *hdev, struct hid_field *field,
 
 static int ms_probe(struct hid_device *hdev, const struct hid_device_id *id)
 {
-	unsigned long quirks = id->driver_data;
+	struct ms_data *sc;
 	int ret;
 
-	hid_set_drvdata(hdev, (void *)quirks);
+	sc = devm_kzalloc(&hdev->dev, sizeof(struct ms_data), GFP_KERNEL);
+	if (!sc) {
+		hid_err(hdev, "can't alloc microsoft descriptor\n");
+		return -ENOMEM;
+	}
+
+	sc->quirks = id->driver_data;
+	hid_set_drvdata(hdev, sc);
 
-	if (quirks & MS_NOGET)
+	if (sc->quirks & MS_NOGET)
 		hdev->quirks |= HID_QUIRK_NOGET;
 
+	if (sc->quirks & MS_SIDEWINDER) {
+		struct ms_sidewinder_extra *sidewinder;
+
+		sidewinder = devm_kzalloc(&hdev->dev, sizeof(struct ms_sidewinder_extra),
+					GFP_KERNEL);
+		if (!sidewinder) {
+			hid_err(hdev, "can't alloc microsoft descriptor\n");
+			return -ENOMEM;
+		}
+		sc->extra = sidewinder;
+
+		/* Create sysfs files for the Consumer Control Device only */
+		if (hdev->type == 2) {
+			if (sysfs_create_group(&hdev->dev.kobj, &ms_attr_group)) {
+				hid_warn(hdev, "Could not create sysfs group\n");
+			}
+		}
+	}
+
 	ret = hid_parse(hdev);
 	if (ret) {
 		hid_err(hdev, "parse failed\n");
 		goto err_free;
 	}
 
-	ret = hid_hw_start(hdev, HID_CONNECT_DEFAULT | ((quirks & MS_HIDINPUT) ?
+	ret = hid_hw_start(hdev, HID_CONNECT_DEFAULT | ((sc->quirks & MS_HIDINPUT) ?
 				HID_CONNECT_HIDINPUT_FORCE : 0));
 	if (ret) {
 		hid_err(hdev, "hw start failed\n");
@@ -191,9 +537,21 @@ err_free:
 	return ret;
 }
 
+static void ms_remove(struct hid_device *hdev)
+{
+	sysfs_remove_group(&hdev->dev.kobj,
+		&ms_attr_group);
+
+	hid_hw_stop(hdev);
+}
+
 static const struct hid_device_id ms_devices[] = {
 	{ HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_SIDEWINDER_GV),
 		.driver_data = MS_HIDINPUT },
+	{ HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_SIDEWINDER_X6),
+		.driver_data = MS_SIDEWINDER },
+	{ HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_SIDEWINDER_X4),
+		.driver_data = MS_SIDEWINDER },
 	{ HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_NE4K),
 		.driver_data = MS_ERGONOMY },
 	{ HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_NE4K_JP),
@@ -225,8 +583,10 @@ static struct hid_driver ms_driver = {
 	.report_fixup = ms_report_fixup,
 	.input_mapping = ms_input_mapping,
 	.input_mapped = ms_input_mapped,
+	.feature_mapping = ms_feature_mapping,
 	.event = ms_event,
 	.probe = ms_probe,
+	.remove = ms_remove,
 };
 module_hid_driver(ms_driver);
 
-- 
1.9.0


^ permalink raw reply related

* Re: [PATCH v2 1/4] HID: cp2112: remove various hid_out_raw_report calls
From: David Herrmann @ 2014-03-07  9:47 UTC (permalink / raw)
  To: Benjamin Tissoires
  Cc: Benjamin Tissoires, Jiri Kosina, David Barksdale, Antonio Ospite,
	open list:HID CORE LAYER, linux-kernel
In-Reply-To: <1394054334-20438-2-git-send-email-benjamin.tissoires@redhat.com>

Hi

On Wed, Mar 5, 2014 at 10:18 PM, Benjamin Tissoires
<benjamin.tissoires@redhat.com> wrote:
> hid_out_raw_report is going to be obsoleted as it is not part of the
> unified HID low level transport documentation
> (Documentation/hid/hid-transport.txt)
>
>   hid_output_raw_report(hdev, buf, sizeof(buf), HID_FEATURE_REPORT);
> is strictly equivalent to:
>   hid_hw_raw_request(hdev, buf[0], buf, sizeof(buf),
>                 HID_FEATURE_REPORT, HID_REQ_SET_REPORT);

This time you might be right that feature-reports always put the
report-id into the first byte, but I'd still prefer if we avoid using
this. Besides, the code is much nicer imho if we pass the ID directly,
see below..

>
> So use the new api.
>
> Signed-off-by: Benjamin Tissoires <benjamin.tissoires@redhat.com>
> ---
>
> no changes since v1
>
>  drivers/hid/hid-cp2112.c | 12 ++++++------
>  1 file changed, 6 insertions(+), 6 deletions(-)
>
> diff --git a/drivers/hid/hid-cp2112.c b/drivers/hid/hid-cp2112.c
> index 1025982..860db694 100644
> --- a/drivers/hid/hid-cp2112.c
> +++ b/drivers/hid/hid-cp2112.c
> @@ -185,8 +185,8 @@ static int cp2112_gpio_direction_input(struct gpio_chip *chip, unsigned offset)
>         buf[1] &= ~(1 << offset);
>         buf[2] = gpio_push_pull;
>
> -       ret = hdev->hid_output_raw_report(hdev, buf, sizeof(buf),
> -                                         HID_FEATURE_REPORT);
> +       ret = hid_hw_raw_request(hdev, buf[0], buf, sizeof(buf),
> +                                HID_FEATURE_REPORT, HID_REQ_SET_REPORT);

buf[0] => CP2112_GPIO_CONFIG

>         if (ret < 0) {
>                 hid_err(hdev, "error setting GPIO config: %d\n", ret);
>                 return ret;
> @@ -207,8 +207,8 @@ static void cp2112_gpio_set(struct gpio_chip *chip, unsigned offset, int value)
>         buf[1] = value ? 0xff : 0;
>         buf[2] = 1 << offset;
>
> -       ret = hdev->hid_output_raw_report(hdev, buf, sizeof(buf),
> -                                         HID_FEATURE_REPORT);
> +       ret = hid_hw_raw_request(hdev, buf[0], buf, sizeof(buf),
> +                                HID_FEATURE_REPORT, HID_REQ_SET_REPORT);

Here buf[0] seems fine as it is set explicitly just 3 lines above to
CP2112_GPIO_SET.

>         if (ret < 0)
>                 hid_err(hdev, "error setting GPIO values: %d\n", ret);
>  }
> @@ -253,8 +253,8 @@ static int cp2112_gpio_direction_output(struct gpio_chip *chip,
>         buf[1] |= 1 << offset;
>         buf[2] = gpio_push_pull;
>
> -       ret = hdev->hid_output_raw_report(hdev, buf, sizeof(buf),
> -                                         HID_FEATURE_REPORT);
> +       ret = hid_hw_raw_request(hdev, buf[0], buf, sizeof(buf),
> +                                HID_FEATURE_REPORT, HID_REQ_SET_REPORT);

Here an explicit CP2112_GPIO_CONFIG seems nicer, imho.

Thanks
David

>         if (ret < 0) {
>                 hid_err(hdev, "error setting GPIO config: %d\n", ret);
>                 return ret;
> --
> 1.8.5.3
>

^ permalink raw reply

* Re: [PATCH v2 0/4] HID: ll transport cleanup: final round
From: David Herrmann @ 2014-03-07  9:52 UTC (permalink / raw)
  To: Benjamin Tissoires
  Cc: Benjamin Tissoires, Jiri Kosina, David Barksdale, Antonio Ospite,
	open list:HID CORE LAYER, linux-kernel
In-Reply-To: <1394054334-20438-1-git-send-email-benjamin.tissoires@redhat.com>

Hi

On Wed, Mar 5, 2014 at 10:18 PM, Benjamin Tissoires
<benjamin.tissoires@redhat.com> wrote:
> Alright, this is the re-spin of the last round of transport cleanup.
>
> Some minor but important modifications are here, but nothing very enthousiastic.
>
> Thanks for the reviews and the tests so far.

Apart from some minor comments on #1 and #2, this is:

Reviewed-by: David Herrmann <dh.herrmann@gmail.com>

Thanks
David

> Cheers,
> Benjamin
>
> Benjamin Tissoires (4):
>   HID: cp2112: remove various hid_out_raw_report calls
>   HID: cp2112: remove the last hid_output_raw_report() call
>   HID: sony: do not rely on hid_output_raw_report
>   HID: remove hid_output_raw_report transport implementations
>
>  drivers/hid/hid-cp2112.c      | 19 +++++++++-----
>  drivers/hid/hid-sony.c        | 60 ++++++++++---------------------------------
>  drivers/hid/hidraw.c          |  3 ++-
>  drivers/hid/i2c-hid/i2c-hid.c | 14 ----------
>  drivers/hid/uhid.c            |  1 -
>  drivers/hid/usbhid/hid-core.c | 19 +++++---------
>  include/linux/hid.h           | 21 ++-------------
>  net/bluetooth/hidp/core.c     | 14 ----------
>  8 files changed, 36 insertions(+), 115 deletions(-)
>
> --
> 1.8.5.3

^ permalink raw reply

* [RFC V1 2/2] input: misc: da9063: OnKey driver
From: Opensource [Steve Twiss] @ 2014-03-07 12:37 UTC (permalink / raw)
  To: Dmitry Torokhov; +Cc: David Dajun Chen, LKML-INPUT, LKML-KERNEL
In-Reply-To: <cover.1394195865.git.stwiss.opensource@diasemi.com>

From: Opensource [Steve Twiss] <stwiss.opensource@diasemi.com>

Add the OnKey driver for DA9063.


Signed-off-by: Opensource [Steve Twiss] <stwiss.opensource@diasemi.com>
---
Checks performed with next-20140307/scripts/checkpatch.pl
 da9063-onkey.c            total: 0 errors, 0 warnings, 202 lines checked
 Kconfig                   total: 0 errors, 11 warnings, 679 lines checked
 Makefile                  total: 0 errors, 0 warnings, 66 lines checked

Hello,

This is a RFC for the Dialog DA9063 Onkey driver.

Dependencies:

This driver makes use of the name field "ONKEY" as part of the
function call: platform_get_irq_byname();

This driver is therefore dependent on this change to the name field
in the properties of the the OnKey IORESOURCE_IRQ resource structure
(part of the mfd_cell Onkey resource inside da9063-core.c). This
change will be added as part of the final set when I submit all patches
as a single PATCH set.

This patch applies against kernel version linux-next next-20140307
 
Regards,
Steve Twiss, Dialog Semiconductor Ltd.



 drivers/input/misc/Kconfig        |   10 ++
 drivers/input/misc/Makefile       |    1 +
 drivers/input/misc/da9063-onkey.c |  202 +++++++++++++++++++++++++++++++++++++
 3 files changed, 213 insertions(+)
 create mode 100644 drivers/input/misc/da9063-onkey.c

diff --git a/drivers/input/misc/Kconfig b/drivers/input/misc/Kconfig
index 762e6d2..3deb008 100644
--- a/drivers/input/misc/Kconfig
+++ b/drivers/input/misc/Kconfig
@@ -522,6 +522,16 @@ config INPUT_DA9055_ONKEY
 	  To compile this driver as a module, choose M here: the module
 	  will be called da9055_onkey.
 
+config INPUT_DA9063_ONKEY
+	tristate "Dialog DA9063 OnKey"
+	depends on MFD_DA9063
+	help
+	  Support the ONKEY of Dialog DA9063 Power Management IC as an
+	  input device reporting power button statue.
+
+	  To compile this driver as a module, choose M here: the module
+	  will be called da9063-onkey.
+
 config INPUT_DM355EVM
 	tristate "TI DaVinci DM355 EVM Keypad and IR Remote"
 	depends on MFD_DM355EVM_MSP
diff --git a/drivers/input/misc/Makefile b/drivers/input/misc/Makefile
index cda71fc..f40caa7 100644
--- a/drivers/input/misc/Makefile
+++ b/drivers/input/misc/Makefile
@@ -25,6 +25,7 @@ obj-$(CONFIG_INPUT_CMA3000_I2C)		+= cma3000_d0x_i2c.o
 obj-$(CONFIG_INPUT_COBALT_BTNS)		+= cobalt_btns.o
 obj-$(CONFIG_INPUT_DA9052_ONKEY)	+= da9052_onkey.o
 obj-$(CONFIG_INPUT_DA9055_ONKEY)	+= da9055_onkey.o
+obj-$(CONFIG_INPUT_DA9063_ONKEY)	+= da9063-onkey.o
 obj-$(CONFIG_INPUT_DM355EVM)		+= dm355evm_keys.o
 obj-$(CONFIG_INPUT_GP2A)		+= gp2ap002a00f.o
 obj-$(CONFIG_INPUT_GPIO_BEEPER)		+= gpio-beeper.o
diff --git a/drivers/input/misc/da9063-onkey.c b/drivers/input/misc/da9063-onkey.c
new file mode 100644
index 0000000..654f52b
--- /dev/null
+++ b/drivers/input/misc/da9063-onkey.c
@@ -0,0 +1,202 @@
+/* da9063-onkey.c - Onkey device driver for DA9063
+ * Copyright (C) 2013  Dialog Semiconductor Ltd.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Library General Public License for more details.
+ */
+
+#include <linux/module.h>
+#include <linux/errno.h>
+#include <linux/input.h>
+#include <linux/interrupt.h>
+#include <linux/platform_device.h>
+#include <linux/workqueue.h>
+#include <linux/regmap.h>
+
+#include <linux/mfd/da9063/core.h>
+#include <linux/mfd/da9063/pdata.h>
+#include <linux/mfd/da9063/registers.h>
+
+struct da9063_onkey {
+	struct	da9063 *hw;
+	struct delayed_work work;
+	struct	input_dev *input;
+	bool key_power;
+};
+
+static void da9063_poll_on(struct work_struct *work)
+{
+	struct da9063_onkey *onkey = container_of(work, struct da9063_onkey,
+						  work.work);
+	unsigned int val;
+	int poll = 1;
+	int ret;
+
+	/* poll to see when the pin is deasserted */
+	ret = regmap_read(onkey->hw->regmap, DA9063_REG_STATUS_A, &val);
+	if (ret < 0) {
+		dev_err(&onkey->input->dev,
+			"Failed to read ON status: %d\n", ret);
+		goto err_poll;
+	}
+
+	if (!(val & DA9063_NONKEY)) {
+		ret = regmap_update_bits(onkey->hw->regmap,
+					 DA9063_REG_CONTROL_B,
+					 DA9063_NONKEY_LOCK, 0);
+		if (ret < 0) {
+			dev_err(&onkey->input->dev,
+				"Failed to reset the Key Delay %d\n", ret);
+			goto err_poll;
+		}
+
+		input_report_key(onkey->input, KEY_POWER, 0);
+		input_sync(onkey->input);
+
+		/* unmask the onkey interrupt again */
+		ret = regmap_update_bits(onkey->hw->regmap,
+					 DA9063_REG_IRQ_MASK_A,
+					 DA9063_NONKEY, 0);
+		if (ret < 0) {
+			dev_err(&onkey->input->dev,
+				"Failed to unmask the onkey IRQ: %d\n", ret);
+			goto err_poll;
+		}
+
+		poll = 0;
+	}
+
+err_poll:
+	if (poll)
+		schedule_delayed_work(&onkey->work, 50);
+}
+
+static irqreturn_t da9063_onkey_irq_handler(int irq, void *data)
+{
+	struct da9063_onkey *onkey = data;
+	unsigned int val;
+	int ret;
+
+	ret = regmap_read(onkey->hw->regmap, DA9063_REG_STATUS_A, &val);
+	if (onkey->key_power && (ret >= 0) && (val & DA9063_NONKEY)) {
+		ret = regmap_update_bits(onkey->hw->regmap,
+					 DA9063_REG_IRQ_MASK_A,
+					 DA9063_NONKEY, 1);
+		if (ret < 0)
+			dev_err(&onkey->input->dev,
+				"Failed to mask the onkey IRQ: %d\n", ret);
+
+		input_report_key(onkey->input, KEY_POWER, 1);
+		input_sync(onkey->input);
+
+		schedule_delayed_work(&onkey->work, 0);
+		dev_dbg(&onkey->input->dev, "KEY_POWER pressed.\n");
+	} else {
+		input_report_key(onkey->input, KEY_SLEEP, 1);
+		input_report_key(onkey->input, KEY_SLEEP, 0);
+		input_sync(onkey->input);
+		dev_dbg(&onkey->input->dev, "KEY_SLEEP pressed.\n");
+	}
+
+	return IRQ_HANDLED;
+}
+
+static int da9063_onkey_probe(struct platform_device *pdev)
+{
+	struct da9063 *da9063 = dev_get_drvdata(pdev->dev.parent);
+	struct da9063_pdata *pdata = dev_get_platdata(da9063->dev);
+	struct da9063_onkey *onkey;
+	bool kp_tmp = true;
+	int ret = 0;
+	int irq;
+
+	if (pdata)
+		kp_tmp = pdata->key_power;
+
+	if (!kp_tmp)
+		dev_err(&pdev->dev,
+			"Software power down key is not set.\n");
+
+	onkey = devm_kzalloc(&pdev->dev, sizeof(struct da9063_onkey),
+			     GFP_KERNEL);
+	if (!onkey) {
+		dev_err(&pdev->dev, "Failed to allocate memory.\n");
+		return -ENOMEM;
+	}
+
+	INIT_DELAYED_WORK(&onkey->work, da9063_poll_on);
+
+	onkey->input = devm_input_allocate_device(&pdev->dev);
+	if (!onkey->input) {
+		dev_err(&pdev->dev, "Failed to allocated input device.\n");
+		return -ENOMEM;
+	}
+
+	irq = platform_get_irq_byname(pdev, "ONKEY");
+	if (irq < 0) {
+		dev_err(&pdev->dev, "Failed to get platform IRQ.\n");
+		return irq;
+	}
+
+	ret = devm_request_threaded_irq(&pdev->dev, irq, NULL,
+					da9063_onkey_irq_handler,
+					IRQF_TRIGGER_LOW | IRQF_ONESHOT,
+					"ONKEY", onkey);
+	if (ret) {
+		dev_err(&pdev->dev,
+			"Failed to request input device IRQ.\n");
+		return ret;
+	}
+
+	onkey->hw = da9063;
+	onkey->key_power = kp_tmp;
+	onkey->input->evbit[0] = BIT_MASK(EV_KEY);
+	onkey->input->name = DA9063_DRVNAME_ONKEY;
+	onkey->input->phys = DA9063_DRVNAME_ONKEY "/input0";
+	onkey->input->dev.parent = &pdev->dev;
+
+	if (onkey->key_power)
+		input_set_capability(onkey->input, EV_KEY, KEY_POWER);
+	input_set_capability(onkey->input, EV_KEY, KEY_SLEEP);
+
+	ret = input_register_device(onkey->input);
+	if (ret) {
+		dev_err(&pdev->dev,
+			"Failed to register input device.\n");
+		return ret;
+	}
+
+	platform_set_drvdata(pdev, onkey);
+	return ret;
+}
+
+static int da9063_onkey_remove(struct platform_device *pdev)
+{
+	struct	da9063_onkey *onkey = platform_get_drvdata(pdev);
+	cancel_delayed_work_sync(&onkey->work);
+	input_unregister_device(onkey->input);
+	return 0;
+}
+
+static struct platform_driver da9063_onkey_driver = {
+	.probe	= da9063_onkey_probe,
+	.remove	= da9063_onkey_remove,
+	.driver	= {
+		.name	= DA9063_DRVNAME_ONKEY,
+		.owner	= THIS_MODULE,
+	},
+};
+
+module_platform_driver(da9063_onkey_driver);
+
+MODULE_AUTHOR("S Twiss <stwiss.opensource@diasemi.com>");
+MODULE_DESCRIPTION("Onkey device driver for Dialog DA9063");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:" DA9063_DRVNAME_ONKEY);
-- 
end-of-patch for RFC V1

^ permalink raw reply related

* Re: [PATCH v2] mfd: max8997: use regmap to access registers
From: Robert Baldyga @ 2014-03-07 14:40 UTC (permalink / raw)
  To: Dmitry Torokhov
  Cc: sameo, lee.jones, myungjoo.ham, cw00.choi, cooloney, rpurdie,
	dbaryshkov, dwmw2, lgirdwood, broonie, a.zummo, paul.gortmaker,
	linux-kernel, linux-input, linux-leds, rtc-linux, m.szyprowski,
	k.kozlowski
In-Reply-To: <20140306162951.GA14318@core.coreip.homeip.net>

On 03/06/2014 05:29 PM, Dmitry Torokhov wrote:
> On Thu, Mar 06, 2014 at 07:42:30AM +0100, Robert Baldyga wrote:
>> Hi,
>>
>> On 03/05/2014 07:54 PM, Dmitry Torokhov wrote:
>>> Hi Robert,
>>>
>>> On Wed, Mar 05, 2014 at 03:58:17PM +0100, Robert Baldyga wrote:
>>>>  
>>>> -int max8997_write_reg(struct i2c_client *i2c, u8 reg, u8 value)
>>>> +int max8997_write_reg(struct regmap *map, u8 reg, u8 value)
>>>
>>> Why don't you make read/write reg to take struct max8997_dev as argument
>>> instead of regmap? regmap seems to be the current implementation du jur,
>>> but that is core's detail, functions do not need to care.
>>>
>>> Thanks.
>>>
>>
>> It's because there are few regmaps in max8997_dev, and read/write reg
>> functions has no way to check which regmap should be used. I think it
>> would be clearer if I remove this functions and use regmap_read and
>> regmap_write instead, because there is no particular purpose for them.
> 
> I see.. Then another question - why do the regmaps belong to the core
> of the driver instead of individual functions? Why haptic regmap, for
> example, can not be initialized in haptic code instead?
> 

It looks like it would not be a big problem. I will try to prepare new
version of this patch including this.

Best regards
Robert Baldyga
Samsung R&D Institute Poland

^ permalink raw reply

* [PATCH] Input: synaptics add manual min/max quirk
From: Benjamin Tissoires @ 2014-03-07 15:49 UTC (permalink / raw)
  To: Dmitry Torokhov, Christopher Heiny, Andrew Duggan, linux-input,
	linux-kernel
  Cc: Peter Hutterer, Stephen Chandler Paul, Hans de Goede

The new Lenovo Haswell series (-40's) contains a new Synaptics touchpad.
However, these new Synaptics devices report bad axis ranges.
Under Windows, it is not a problem because the Windows driver uses RMI4
over SMBus to talk to the device. Under Linux, we are using the PS/2
fallback interface and it occurs the reported ranges are wrong.

Of course, it would be too easy to have only one range for the whole
series, each touchpad seems to be calibrated in a different way.

We can not use SMBus to get the actual range because I suspect the firmware
will switch into the SMBus mode and stop talking through PS/2 (this is the
case for hybrid HID over I2C / PS/2 Synaptics touchpads).

So as a temporary solution (until RMI4 land into upstream), start a new
list of quirks with the min/max manually set.

Signed-off-by: Benjamin Tissoires <benjamin.tissoires@redhat.com>
CC: stable@vger.kernel.org
---

Hi Dmitry,

Well, this work is part of a (big) attempt to support the new touchpads
Lenovo put in its latest series.
Those new laptops have lost the buttons associated to the trackstick and are
what we call "clickpad".
Things would be easy if:
1. the PS/2 firmware did not lied about the actual range of the axis
2. on some install (mine) the DMI matching in udev would not have been broken
3. we did not have to fix a lot of stuff in Xorg / libinput / wayland

This patch fixes 1.

Matthew Garrett fixed 2. -> https://patchwork.kernel.org/patch/3704401/

3. is heavily working as shown by this tracker bug: https://bugs.freedesktop.org/show_bug.cgi?id=73158

I put the 'stable' marker, feel free to remove it if you don't think it should
be there, but I really think this should also be backported to have something
working in current distros.

Cheers,
Benjamin

 drivers/input/mouse/synaptics.c | 43 +++++++++++++++++++++++++++++++++++++++++
 1 file changed, 43 insertions(+)

diff --git a/drivers/input/mouse/synaptics.c b/drivers/input/mouse/synaptics.c
index 26386f9..ff6a4cf 100644
--- a/drivers/input/mouse/synaptics.c
+++ b/drivers/input/mouse/synaptics.c
@@ -67,6 +67,8 @@
 #define X_MAX_POSITIVE 8176
 #define Y_MAX_POSITIVE 8176
 
+static int *quirk_min_max = NULL;
+
 /*****************************************************************************
  *	Stuff we need even when we do not want native Synaptics support
  ****************************************************************************/
@@ -302,6 +304,13 @@ static int synaptics_resolution(struct psmouse *psmouse)
 		}
 	}
 
+	if (quirk_min_max) {
+		priv->x_min = quirk_min_max[0];
+		priv->x_max = quirk_min_max[1];
+		priv->y_min = quirk_min_max[2];
+		priv->y_max = quirk_min_max[3];
+	}
+
 	return 0;
 }
 
@@ -1485,10 +1494,44 @@ static const struct dmi_system_id olpc_dmi_table[] __initconst = {
 	{ }
 };
 
+static const struct dmi_system_id min_max_dmi_table[] __initconst = {
+#if defined(CONFIG_DMI)
+	{
+		/* Lenovo ThinkPad Helix */
+		.matches = {
+			DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"),
+			DMI_MATCH(DMI_PRODUCT_VERSION, "ThinkPad Helix"),
+		},
+		.driver_data = (int []){1024, 5052, 2258, 4832},
+	},
+	{
+		/* Lenovo ThinkPad T440s */
+		.matches = {
+			DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"),
+			DMI_MATCH(DMI_PRODUCT_VERSION, "ThinkPad T440"),
+		},
+		.driver_data = (int []){1024, 5112, 2024, 4832},
+	},
+	{
+		/* Lenovo ThinkPad T540p */
+		.matches = {
+			DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"),
+			DMI_MATCH(DMI_PRODUCT_VERSION, "ThinkPad T540"),
+		},
+		.driver_data = (int []){1024, 5056, 2058, 4832},
+	},
+#endif
+	{ }
+};
+
 void __init synaptics_module_init(void)
 {
+	const struct dmi_system_id *min_max_dmi;
 	impaired_toshiba_kbc = dmi_check_system(toshiba_dmi_table);
 	broken_olpc_ec = dmi_check_system(olpc_dmi_table);
+	min_max_dmi = dmi_first_match(min_max_dmi_table);
+	if (min_max_dmi)
+		quirk_min_max = (int *)min_max_dmi->driver_data;
 }
 
 static int __synaptics_init(struct psmouse *psmouse, bool absolute_mode)
-- 
1.8.5.3


^ permalink raw reply related

* Re: [PATCH 1/2 ] Surface Pro 2 HID sensor, wacom, keyboard/multitouch composite device
From: Derya @ 2014-03-07 15:56 UTC (permalink / raw)
  To: linux-input, benjamin.tissoires, jkosina
In-Reply-To: <5319EB7C.5070209@yahoo.de>

The Surface Pro 2 has a very annoying USB composite device on port 2.3.
It has 3 interfaces, interface 0 is the hid-sensor-hub, interface 1 the 
wacom
digitizer and if a keyboard is attached, then it is on interface 3. This USB
composite device changes it's product id, it depends on if and which 
keyboard
cover is attached. Adding the Type-/Touch Cover 2 to hid_have_special_driver
prevents loading the hid-sensor-hub driver for interface 0, if a cover is
attached (commit 117309c51dca42121f70cacec801511b76acf75c).
All 3 interfaces are loaded with the microsoft special driver. Interface 3,
the keyboard covers, has a HID_DG_CONTACTID. This leads to loading of
hid-multitouch for the keyboard, if we remove it from the special driver 
list.
Neither the keyboard not the touchpad works with hid-multitouch.
I add a check for vendor and product id in hid_scan_input_usage to prevent
the loading of hid-multitouch. By this way it loads hid-generic for
the keyboards without effecting the other two interfaces.
The HID_QUIRK_NOGET is needed for hid-sensor-hub, without it Submit urb 
fails
and it takes some seconds until the devices on this port get responsible
whenever a keyboard cover is (de)attached.

Signed-off-by: Derya <derya.kiran@yahoo.de>
---
  drivers/hid/hid-core.c          | 5 ++---
  drivers/hid/hid-ids.h           | 1 +
  drivers/hid/hid-microsoft.c     | 4 ----
  drivers/hid/usbhid/hid-quirks.c | 3 +++
  4 files changed, 6 insertions(+), 7 deletions(-)

diff --git a/drivers/hid/hid-core.c b/drivers/hid/hid-core.c
index cc32a6f..2d60a1a 100644
--- a/drivers/hid/hid-core.c
+++ b/drivers/hid/hid-core.c
@@ -685,7 +685,8 @@ static void hid_scan_input_usage(struct hid_parser 
*parser, u32 usage)
      struct hid_device *hid = parser->device;

      if (usage == HID_DG_CONTACTID)
-        hid->group = HID_GROUP_MULTITOUCH;
+        if (!(hid->vendor == USB_VENDOR_ID_MICROSOFT && (hid->product 
== USB_DEVICE_ID_MS_TYPE_COVER_2 || hid->product == 
USB_DEVICE_ID_MS_TOUCH_COVER_2)))
+            hid->group = HID_GROUP_MULTITOUCH;
  }

  static void hid_scan_feature_usage(struct hid_parser *parser, u32 usage)
@@ -1780,8 +1781,6 @@ static const struct hid_device_id 
hid_have_special_driver[] = {
      { HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, 
USB_DEVICE_ID_MS_PRESENTER_8K_USB) },
      { HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, 
USB_DEVICE_ID_MS_DIGITAL_MEDIA_3K) },
      { HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, 
USB_DEVICE_ID_WIRELESS_OPTICAL_DESKTOP_3_0) },
-    { HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, 
USB_DEVICE_ID_MS_TYPE_COVER_2) },
-    { HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, 
USB_DEVICE_ID_MS_TOUCH_COVER_2) },
      { HID_USB_DEVICE(USB_VENDOR_ID_MONTEREY, 
USB_DEVICE_ID_GENIUS_KB29E) },
      { HID_USB_DEVICE(USB_VENDOR_ID_NTRIG, 
USB_DEVICE_ID_NTRIG_TOUCH_SCREEN) },
      { HID_USB_DEVICE(USB_VENDOR_ID_NTRIG, 
USB_DEVICE_ID_NTRIG_TOUCH_SCREEN_1) },
diff --git a/drivers/hid/hid-ids.h b/drivers/hid/hid-ids.h
index 22f28d6..0aa9f7e 100644
--- a/drivers/hid/hid-ids.h
+++ b/drivers/hid/hid-ids.h
@@ -624,6 +624,7 @@
  #define USB_DEVICE_ID_MS_PRESENTER_8K_USB    0x0713
  #define USB_DEVICE_ID_MS_DIGITAL_MEDIA_3K    0x0730
  #define USB_DEVICE_ID_MS_COMFORT_MOUSE_4500    0x076c
+#define USB_DEVICE_ID_MS_SURFACE_PRO_2    0x0799        //Surface Pro 
2's MICROSOFT SAM device without a keyboard cover attached, changes 
product id with cover
  #define USB_DEVICE_ID_MS_TOUCH_COVER_2    0x07a7
  #define USB_DEVICE_ID_MS_TYPE_COVER_2    0x07a9

diff --git a/drivers/hid/hid-microsoft.c b/drivers/hid/hid-microsoft.c
index 404a3a8..c6ef6ee 100644
--- a/drivers/hid/hid-microsoft.c
+++ b/drivers/hid/hid-microsoft.c
@@ -208,10 +208,6 @@ static const struct hid_device_id ms_devices[] = {
          .driver_data = MS_NOGET },
      { HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, 
USB_DEVICE_ID_MS_COMFORT_MOUSE_4500),
          .driver_data = MS_DUPLICATE_USAGES },
-    { HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, 
USB_DEVICE_ID_MS_TYPE_COVER_2),
-        .driver_data = 0 },
-    { HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, 
USB_DEVICE_ID_MS_TOUCH_COVER_2),
-        .driver_data = 0 },

      { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_MICROSOFT, 
USB_DEVICE_ID_MS_PRESENTER_8K_BT),
          .driver_data = MS_PRESENTER },
diff --git a/drivers/hid/usbhid/hid-quirks.c 
b/drivers/hid/usbhid/hid-quirks.c
index dbd8387..2b7fd92 100644
--- a/drivers/hid/usbhid/hid-quirks.c
+++ b/drivers/hid/usbhid/hid-quirks.c
@@ -73,6 +73,9 @@ static const struct hid_blacklist {
      { USB_VENDOR_ID_FORMOSA, USB_DEVICE_ID_FORMOSA_IR_RECEIVER, 
HID_QUIRK_NO_INIT_REPORTS },
      { USB_VENDOR_ID_FREESCALE, USB_DEVICE_ID_FREESCALE_MX28, 
HID_QUIRK_NOGET },
      { USB_VENDOR_ID_MGE, USB_DEVICE_ID_MGE_UPS, HID_QUIRK_NOGET },
+    { USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_SURFACE_PRO_2, 
HID_QUIRK_NOGET },
+    { USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_TYPE_COVER_2, 
HID_QUIRK_NOGET },
+    { USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_TOUCH_COVER_2, 
HID_QUIRK_NOGET },
      { USB_VENDOR_ID_MSI, USB_DEVICE_ID_MSI_GX680R_LED_PANEL, 
HID_QUIRK_NO_INIT_REPORTS },
      { USB_VENDOR_ID_NEXIO, USB_DEVICE_ID_NEXIO_MULTITOUCH_PTI0750, 
HID_QUIRK_NO_INIT_REPORTS },
      { USB_VENDOR_ID_NOVATEK, USB_DEVICE_ID_NOVATEK_MOUSE, 
HID_QUIRK_NO_INIT_REPORTS },
-- 
1.8.3.2


^ permalink raw reply related

* [PATCH 0/2 ] Surface Pro 2 HID sensor, wacom, keyboard/multitouch composite device
From: Derya @ 2014-03-07 15:53 UTC (permalink / raw)
  To: linux-input, benjamin.tissoires, jkosina

The MS Surface Pro 2 has a very annoying USB composite device on port 2.3.
It has 3 interfaces:
- Interface 0 is the sensor-hub
- Interface 1 is the wacom digitizer² (it's one without finger input, 
there is an atmel digitizer on port 2.4 for finger input)
- Interface 2 is the keyboard, if a keyboard cover is attached.

This USB composite device changes it product id depending if and which 
keyboard cover is attached.
Each of this hid devices contains several collections, this complicated 
everything.
I have uploaded a lsusb output to: http://pastebin.com/Jun5sa2t

I have to say, that I'm neither a developer nor a programmer, this means 
this things are beyond my ken, please excuse if my remarks aren't accurate.

The Touch and Type Covers (2) didn't work out of the box. Someone one 
the ubuntuforums made a patch which adds them to the microsoft special 
driver, but this leads to loading that driver for all the 3 interfaces 
and prevents loading of hid-sensor-hub for interface 0, if a keyboard 
cover is attached. Sadly this patch is submitted to the upstream kernel 
source.
Without that patch the keyboard covers are loading hid-multitouch 
instead of hid-generic. This misbehaviour is a result of the fancy hid 
collections, that the keyboards have. With hid-multitouch neither the 
keyboard nor the touchpad of the cover works². I added an if clause to 
hid_scan_input_usage to prevent loading of hid-multitouch for the 
keyboards. With hid-generic keyboard and touchpad are working (they come 
up as one input)

We also need the HID_QUIRK_NOGET for this usb composite device, without 
it hid-sensor-hub fails with a submit urb failure evertime a keyboard 
cover is (de)attached and it takes some seconds until the keyboard and 
wacom digitizer responds.

The second patch adds HID_SENSOR_HUB_ENUM_QUIRK for the Surface Pro 2's 
sensor-hub. There is still a bug with the sensors and the Surface Pro 2, 
but I didn't dig into it yet (hid-sensor-magn-3d fails to setup attributes)

Regards,

Derya



1 I'm also working to get the wacom driver working. At the moment the 
stylus works with hid-generic(if my patch is applied, without it use 
hid-microsoft). I got it working with wacom driver without disturbing 
the other interfaces, but the wacom interface contains also some fancy 
collection. The wacom driver doesn't care of them, this leads to losing 
the on device volume and left meta keys. With hid-generic they work, but 
the input events get distorted after the use of the eraser. But, this is 
another story...

2 @Benjamin Tissoires
I have tried your patches for fancy collection in hid-multitouch. It 
seems to be the right way to solve the problem with the keyboard (better 
than my approach to exclude this devices in hid_scan_input_usage), but 
it has the some drawbacks at the moment. It splits the input into 5 
pieces. 2 seperate keyboard inputs that leads into losing the caps lock 
led. The touchpad is mapped as a mouse. There is also a consumer device, 
which gives me no input and an unkown device, but no multitouch device. 
There is a HID_DG_INPUTMODE out of range error in dmesg. Please, contact 
me, if you need some logs.



--
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 2/2 ] Surface Pro 2 HID sensor, wacom, keyboard/multitouch composite device
From: Derya @ 2014-03-07 15:58 UTC (permalink / raw)
  To: linux-input, benjamin.tissoires, jkosina
In-Reply-To: <5319EB7C.5070209@yahoo.de>

Enumeration quirks for Surface Pro 2 sensor-hub

Signed-off-by: Derya <derya.kiran@yahoo.de>
---
  drivers/hid/hid-sensor-hub.c | 9 +++++++++
  1 file changed, 9 insertions(+)

diff --git a/drivers/hid/hid-sensor-hub.c b/drivers/hid/hid-sensor-hub.c
index 9c22e14..16f4bb8 100644
--- a/drivers/hid/hid-sensor-hub.c
+++ b/drivers/hid/hid-sensor-hub.c
@@ -668,6 +668,15 @@ static const struct hid_device_id 
sensor_hub_devices[] = {
      { HID_DEVICE(HID_BUS_ANY, HID_GROUP_SENSOR_HUB, USB_VENDOR_ID_STM_0,
              USB_DEVICE_ID_STM_HID_SENSOR),
              .driver_data = HID_SENSOR_HUB_ENUM_QUIRK},
+    { HID_DEVICE(HID_BUS_ANY, HID_GROUP_SENSOR_HUB, 
USB_VENDOR_ID_MICROSOFT,
+             USB_DEVICE_ID_MS_TOUCH_COVER_2),
+            .driver_data = HID_SENSOR_HUB_ENUM_QUIRK},
+    { HID_DEVICE(HID_BUS_ANY, HID_GROUP_SENSOR_HUB, 
USB_VENDOR_ID_MICROSOFT,
+             USB_DEVICE_ID_MS_TYPE_COVER_2),
+            .driver_data = HID_SENSOR_HUB_ENUM_QUIRK},
+    { HID_DEVICE(HID_BUS_ANY, HID_GROUP_SENSOR_HUB, 
USB_VENDOR_ID_MICROSOFT,
+             USB_DEVICE_ID_MS_SURFACE_PRO_2),
+            .driver_data = HID_SENSOR_HUB_ENUM_QUIRK},
      { HID_DEVICE(HID_BUS_ANY, HID_GROUP_SENSOR_HUB, HID_ANY_ID,
               HID_ANY_ID) },
      { }
-- 
1.8.3.2

^ 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