Commit 83ec9169 authored by Linus Torvalds's avatar Linus Torvalds
Browse files
Pull HID updates from Jiri Kosina:

 - Fix in i2c-hid driver for Elan touchpad quirk regression (Jim
   Broadus)

 - Quirk preventing ASUS Claymore from accidentally suspending whole
   system (Luke D. Jones)

 - Updates to the existing FW reporting mechanism, MP2 FW status checks,
   adding proper power management support for amd-sfh (Basavaraj
   Natikar)

 - Regression fix for an issue in HID core that got uncovered by recent
   USB core cleanup leading to issues when transfer_buffer_length is not
   in line with wLength (Alan Stern)

 - Memory leak fix in USB HID core (Anirudh Rayabharam)

 - Improvement of stylus battery reporting (Dmitry Torokhov)

 - Power management improvement for Goodix driver (Douglas Anderson)

 - High-resolution scroll support for Magicmouse devices (José Expósito)

 - Support for GHLive PS4 dongles (Daniel Nguyen)

 - Support proper EV_MSC emissions to hid-apple (Vincent Lefevre)

* 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/hid/hid: (31 commits)
  HID: usbhid: Simplify code in hid_submit_ctrl()
  HID: usbhid: Fix warning caused by 0-length input reports
  HID: usbhid: Fix flood of "control queue full" messages
  HID: sony: Fix more ShanWan clone gamepads to not rumble when plugged in.
  HID: sony: support for the ghlive ps4 dongles
  HID: thrustmaster: clean up Makefile and adapt quirks
  HID: i2c-hid: Fix Elan touchpad regression
  HID: asus: Prevent Claymore sending suspend event
  HID: amd_sfh: Add dyndbg prints for debugging
  HID: amd_sfh: Add support for PM suspend and resume
  HID: amd_sfh: Move hid probe after sensor is enabled
  HID: amd_sfh: Add command response to check command status
  HID: amd_sfh: Fix period data field to enable sensor
  HID: logitech-hidpp: battery: provide CAPACITY property for newer devices
  HID: thrustmaster: Fix memory leak in thrustmaster_interrupts()
  HID: thrustmaster: Fix memory leak in remove
  HID: thrustmaster: Fix memory leaks in probe
  HID: elo: update the reference count of the usb device structure
  HID: logitech-hidpp: Use 'atomic_inc_return' instead of hand-writing it
  HID: apple: Add missing scan code event for keys handled by hid-apple
  ...
parents c7930112 46a226b5
Loading
Loading
Loading
Loading
+4 −3
Original line number Diff line number Diff line
@@ -259,10 +259,11 @@ config HID_PRODIKEYS
	  and some additional multimedia keys.

config HID_CMEDIA
	tristate "CMedia CM6533 HID audio jack controls"
	tristate "CMedia audio chips"
	depends on HID
	help
	Support for CMedia CM6533 HID audio jack controls.
	Support for CMedia CM6533 HID audio jack controls
        and HS100B mute buttons.

config HID_CP2112
	tristate "Silicon Labs CP2112 HID USB-to-SMBus Bridge support"
@@ -952,7 +953,7 @@ config HID_SONY
	  * Buzz controllers
	  * Sony PS3 Blue-ray Disk Remote Control (Bluetooth)
	  * Logitech Harmony adapter for Sony Playstation 3 (Bluetooth)
	  * Guitar Hero Live PS3 and Wii U guitar dongles
	  * Guitar Hero Live PS3, Wii U and PS4 guitar dongles
	  * Guitar Hero PS3 and PC guitar dongles

config SONY_FF
+0 −1
Original line number Diff line number Diff line
@@ -115,7 +115,6 @@ obj-$(CONFIG_HID_STEELSERIES) += hid-steelseries.o
obj-$(CONFIG_HID_SUNPLUS)	+= hid-sunplus.o
obj-$(CONFIG_HID_GREENASIA)	+= hid-gaff.o
obj-$(CONFIG_HID_THRUSTMASTER)	+= hid-tmff.o hid-thrustmaster.o
obj-$(CONFIG_HID_TMINIT)	+= hid-tminit.o
obj-$(CONFIG_HID_TIVO)		+= hid-tivo.o
obj-$(CONFIG_HID_TOPSEED)	+= hid-topseed.o
obj-$(CONFIG_HID_TWINHAN)	+= hid-twinhan.o
+47 −15
Original line number Diff line number Diff line
@@ -17,7 +17,6 @@
#include "amd_sfh_pcie.h"
#include "amd_sfh_hid.h"

#define AMD_SFH_IDLE_LOOP	200

struct request_list {
	struct hid_device *hid;
@@ -123,14 +122,24 @@ static void amd_sfh_work_buffer(struct work_struct *work)
	int i;

	for (i = 0; i < cli_data->num_hid_devices; i++) {
		report_size = get_input_report(i, cli_data->sensor_idx[i], cli_data->report_id[i],
					       in_data);
		if (cli_data->sensor_sts[i] == SENSOR_ENABLED) {
			report_size = get_input_report
				(i, cli_data->sensor_idx[i], cli_data->report_id[i], in_data);
			hid_input_report(cli_data->hid_sensor_hubs[i], HID_INPUT_REPORT,
					 in_data->input_report[i], report_size, 0);
		}
	}
	schedule_delayed_work(&cli_data->work_buffer, msecs_to_jiffies(AMD_SFH_IDLE_LOOP));
}

u32 amd_sfh_wait_for_response(struct amd_mp2_dev *mp2, u8 sid, u32 sensor_sts)
{
	if (mp2->mp2_ops->response)
		sensor_sts = mp2->mp2_ops->response(mp2, sid, sensor_sts);

	return sensor_sts;
}

int amd_sfh_hid_client_init(struct amd_mp2_dev *privdata)
{
	struct amd_input_data *in_data = &privdata->in_data;
@@ -139,8 +148,8 @@ int amd_sfh_hid_client_init(struct amd_mp2_dev *privdata)
	struct device *dev;
	u32 feature_report_size;
	u32 input_report_size;
	int rc, i, status;
	u8 cl_idx;
	int rc, i;

	dev = &privdata->pdev->dev;

@@ -155,7 +164,7 @@ int amd_sfh_hid_client_init(struct amd_mp2_dev *privdata)
		in_data->sensor_virt_addr[i] = dma_alloc_coherent(dev, sizeof(int) * 8,
								  &cl_data->sensor_dma_addr[i],
								  GFP_KERNEL);
		cl_data->sensor_sts[i] = 0;
		cl_data->sensor_sts[i] = SENSOR_DISABLED;
		cl_data->sensor_requested_cnt[i] = 0;
		cl_data->cur_hid_dev = i;
		cl_idx = cl_data->sensor_idx[i];
@@ -184,7 +193,7 @@ int amd_sfh_hid_client_init(struct amd_mp2_dev *privdata)
			rc = -ENOMEM;
			goto cleanup;
		}
		info.period = msecs_to_jiffies(AMD_SFH_IDLE_LOOP);
		info.period = AMD_SFH_IDLE_LOOP;
		info.sensor_idx = cl_idx;
		info.dma_address = cl_data->sensor_dma_addr[i];

@@ -195,13 +204,27 @@ int amd_sfh_hid_client_init(struct amd_mp2_dev *privdata)
			goto cleanup;
		}
		rc = get_report_descriptor(cl_idx, cl_data->report_descr[i]);
		if (rc)
			return rc;
		rc = amdtp_hid_probe(cl_data->cur_hid_dev, cl_data);
		if (rc)
			return rc;
		privdata->mp2_ops->start(privdata, info);
		cl_data->sensor_sts[i] = 1;
		status = amd_sfh_wait_for_response
				(privdata, cl_data->sensor_idx[i], SENSOR_ENABLED);
		if (status == SENSOR_ENABLED) {
			cl_data->sensor_sts[i] = SENSOR_ENABLED;
			rc = amdtp_hid_probe(cl_data->cur_hid_dev, cl_data);
			if (rc) {
				privdata->mp2_ops->stop(privdata, cl_data->sensor_idx[i]);
				status = amd_sfh_wait_for_response
					(privdata, cl_data->sensor_idx[i], SENSOR_DISABLED);
				if (status != SENSOR_ENABLED)
					cl_data->sensor_sts[i] = SENSOR_DISABLED;
				dev_dbg(dev, "sid 0x%x status 0x%x\n",
					cl_data->sensor_idx[i], cl_data->sensor_sts[i]);
				goto cleanup;
			}
		}
		dev_dbg(dev, "sid 0x%x status 0x%x\n",
			cl_data->sensor_idx[i], cl_data->sensor_sts[i]);
	}
	schedule_delayed_work(&cl_data->work_buffer, msecs_to_jiffies(AMD_SFH_IDLE_LOOP));
	return 0;
@@ -224,10 +247,19 @@ int amd_sfh_hid_client_deinit(struct amd_mp2_dev *privdata)
{
	struct amdtp_cl_data *cl_data = privdata->cl_data;
	struct amd_input_data *in_data = cl_data->in_data;
	int i;
	int i, status;

	for (i = 0; i < cl_data->num_hid_devices; i++)
		privdata->mp2_ops->stop(privdata, i);
	for (i = 0; i < cl_data->num_hid_devices; i++) {
		if (cl_data->sensor_sts[i] == SENSOR_ENABLED) {
			privdata->mp2_ops->stop(privdata, cl_data->sensor_idx[i]);
			status = amd_sfh_wait_for_response
					(privdata, cl_data->sensor_idx[i], SENSOR_DISABLED);
			if (status != SENSOR_ENABLED)
				cl_data->sensor_sts[i] = SENSOR_DISABLED;
			dev_dbg(&privdata->pdev->dev, "stopping sid 0x%x status 0x%x\n",
				cl_data->sensor_idx[i], cl_data->sensor_sts[i]);
		}
	}

	cancel_delayed_work_sync(&cl_data->work);
	cancel_delayed_work_sync(&cl_data->work_buffer);
+69 −0
Original line number Diff line number Diff line
@@ -13,6 +13,7 @@
#include <linux/dmi.h>
#include <linux/interrupt.h>
#include <linux/io-64-nonatomic-lo-hi.h>
#include <linux/iopoll.h>
#include <linux/module.h>
#include <linux/slab.h>

@@ -31,6 +32,20 @@ static int sensor_mask_override = -1;
module_param_named(sensor_mask, sensor_mask_override, int, 0444);
MODULE_PARM_DESC(sensor_mask, "override the detected sensors mask");

static int amd_sfh_wait_response_v2(struct amd_mp2_dev *mp2, u8 sid, u32 sensor_sts)
{
	union cmd_response cmd_resp;

	/* Get response with status within a max of 800 ms timeout */
	if (!readl_poll_timeout(mp2->mmio + AMD_P2C_MSG(0), cmd_resp.resp,
				(cmd_resp.response_v2.response == sensor_sts &&
				cmd_resp.response_v2.status == 0 && (sid == 0xff ||
				cmd_resp.response_v2.sensor_id == sid)), 500, 800000))
		return cmd_resp.response_v2.response;

	return SENSOR_DISABLED;
}

static void amd_start_sensor_v2(struct amd_mp2_dev *privdata, struct amd_mp2_sensor_info info)
{
	union sfh_cmd_base cmd_base;
@@ -183,6 +198,7 @@ static const struct amd_mp2_ops amd_sfh_ops_v2 = {
	.start = amd_start_sensor_v2,
	.stop = amd_stop_sensor_v2,
	.stop_all = amd_stop_all_sensor_v2,
	.response = amd_sfh_wait_response_v2,
};

static const struct amd_mp2_ops amd_sfh_ops = {
@@ -248,6 +264,58 @@ static int amd_mp2_pci_probe(struct pci_dev *pdev, const struct pci_device_id *i
	return amd_sfh_hid_client_init(privdata);
}

static int __maybe_unused amd_mp2_pci_resume(struct device *dev)
{
	struct pci_dev *pdev = to_pci_dev(dev);
	struct amd_mp2_dev *mp2 = pci_get_drvdata(pdev);
	struct amdtp_cl_data *cl_data = mp2->cl_data;
	struct amd_mp2_sensor_info info;
	int i, status;

	for (i = 0; i < cl_data->num_hid_devices; i++) {
		if (cl_data->sensor_sts[i] == SENSOR_DISABLED) {
			info.period = AMD_SFH_IDLE_LOOP;
			info.sensor_idx = cl_data->sensor_idx[i];
			info.dma_address = cl_data->sensor_dma_addr[i];
			mp2->mp2_ops->start(mp2, info);
			status = amd_sfh_wait_for_response
					(mp2, cl_data->sensor_idx[i], SENSOR_ENABLED);
			if (status == SENSOR_ENABLED)
				cl_data->sensor_sts[i] = SENSOR_ENABLED;
			dev_dbg(dev, "resume sid 0x%x status 0x%x\n",
				cl_data->sensor_idx[i], cl_data->sensor_sts[i]);
		}
	}

	return 0;
}

static int __maybe_unused amd_mp2_pci_suspend(struct device *dev)
{
	struct pci_dev *pdev = to_pci_dev(dev);
	struct amd_mp2_dev *mp2 = pci_get_drvdata(pdev);
	struct amdtp_cl_data *cl_data = mp2->cl_data;
	int i, status;

	for (i = 0; i < cl_data->num_hid_devices; i++) {
		if (cl_data->sensor_idx[i] != HPD_IDX &&
		    cl_data->sensor_sts[i] == SENSOR_ENABLED) {
			mp2->mp2_ops->stop(mp2, cl_data->sensor_idx[i]);
			status = amd_sfh_wait_for_response
					(mp2, cl_data->sensor_idx[i], SENSOR_DISABLED);
			if (status != SENSOR_ENABLED)
				cl_data->sensor_sts[i] = SENSOR_DISABLED;
			dev_dbg(dev, "suspend sid 0x%x status 0x%x\n",
				cl_data->sensor_idx[i], cl_data->sensor_sts[i]);
		}
	}

	return 0;
}

static SIMPLE_DEV_PM_OPS(amd_mp2_pm_ops, amd_mp2_pci_suspend,
		amd_mp2_pci_resume);

static const struct pci_device_id amd_mp2_pci_tbl[] = {
	{ PCI_VDEVICE(AMD, PCI_DEVICE_ID_AMD_MP2) },
	{ }
@@ -258,6 +326,7 @@ static struct pci_driver amd_mp2_pci_driver = {
	.name		= DRIVER_NAME,
	.id_table	= amd_mp2_pci_tbl,
	.probe		= amd_mp2_pci_probe,
	.driver.pm	= &amd_mp2_pm_ops,
};
module_pci_driver(amd_mp2_pci_driver);

+23 −0
Original line number Diff line number Diff line
@@ -24,14 +24,20 @@
#define AMD_C2P_MSG2	0x10508

#define AMD_C2P_MSG(regno) (0x10500 + ((regno) * 4))
#define AMD_P2C_MSG(regno) (0x10680 + ((regno) * 4))

/* MP2 P2C Message Registers */
#define AMD_P2C_MSG3	0x1068C /* Supported Sensors info */

#define V2_STATUS	0x2

#define SENSOR_ENABLED     4
#define SENSOR_DISABLED    5

#define HPD_IDX		16

#define AMD_SFH_IDLE_LOOP	200

/* SFH Command register */
union sfh_cmd_base {
	u32 ul;
@@ -51,6 +57,19 @@ union sfh_cmd_base {
	} cmd_v2;
};

union cmd_response {
	u32 resp;
	struct {
		u32 status	: 2;
		u32 out_in_c2p	: 1;
		u32 rsvd1	: 1;
		u32 response	: 4;
		u32 sub_cmd	: 8;
		u32 sensor_id	: 6;
		u32 rsvd2	: 10;
	} response_v2;
};

union sfh_cmd_param {
	u32 ul;
	struct {
@@ -112,10 +131,14 @@ void amd_stop_all_sensors(struct amd_mp2_dev *privdata);
int amd_mp2_get_sensor_num(struct amd_mp2_dev *privdata, u8 *sensor_id);
int amd_sfh_hid_client_init(struct amd_mp2_dev *privdata);
int amd_sfh_hid_client_deinit(struct amd_mp2_dev *privdata);
u32 amd_sfh_wait_for_response(struct amd_mp2_dev *mp2, u8 sid, u32 sensor_sts);
void amd_mp2_suspend(struct amd_mp2_dev *mp2);
void amd_mp2_resume(struct amd_mp2_dev *mp2);

struct amd_mp2_ops {
	 void (*start)(struct amd_mp2_dev *privdata, struct amd_mp2_sensor_info info);
	 void (*stop)(struct amd_mp2_dev *privdata, u16 sensor_idx);
	 void (*stop_all)(struct amd_mp2_dev *privdata);
	 int (*response)(struct amd_mp2_dev *mp2, u8 sid, u32 sensor_sts);
};
#endif
Loading