Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 92241e67 authored by Mika Westerberg's avatar Mika Westerberg Committed by Jiri Kosina
Browse files

HID: i2c-hid: add ACPI support



The HID over I2C protocol specification states that when the device is
enumerated from ACPI the HID descriptor address can be obtained by
executing "_DSM" for the device with function 1. Enable this.

Signed-off-by: default avatarMika Westerberg <mika.westerberg@linux.intel.com>
Reviewed-by: default avatarBenjamin Tissoires <benjamin.tissoires@redhat.com>
Signed-off-by: default avatarJiri Kosina <jkosina@suse.cz>
parent fd62c545
Loading
Loading
Loading
Loading
+81 −6
Original line number Diff line number Diff line
@@ -34,6 +34,7 @@
#include <linux/kernel.h>
#include <linux/hid.h>
#include <linux/mutex.h>
#include <linux/acpi.h>

#include <linux/i2c/i2c-hid.h>

@@ -139,6 +140,8 @@ struct i2c_hid {
	unsigned long		flags;		/* device flags */

	wait_queue_head_t	wait;		/* For waiting the interrupt */

	struct i2c_hid_platform_data pdata;
};

static int __i2c_hid_command(struct i2c_client *client,
@@ -810,6 +813,70 @@ static int __devinit i2c_hid_fetch_hid_descriptor(struct i2c_hid *ihid)
	return 0;
}

#ifdef CONFIG_ACPI
static int i2c_hid_acpi_pdata(struct i2c_client *client,
		struct i2c_hid_platform_data *pdata)
{
	static u8 i2c_hid_guid[] = {
		0xF7, 0xF6, 0xDF, 0x3C, 0x67, 0x42, 0x55, 0x45,
		0xAD, 0x05, 0xB3, 0x0A, 0x3D, 0x89, 0x38, 0xDE,
	};
	struct acpi_buffer buf = { ACPI_ALLOCATE_BUFFER, NULL };
	union acpi_object params[4], *obj;
	struct acpi_object_list input;
	struct acpi_device *adev;
	acpi_handle handle;

	handle = ACPI_HANDLE(&client->dev);
	if (!handle || acpi_bus_get_device(handle, &adev))
		return -ENODEV;

	input.count = ARRAY_SIZE(params);
	input.pointer = params;

	params[0].type = ACPI_TYPE_BUFFER;
	params[0].buffer.length = sizeof(i2c_hid_guid);
	params[0].buffer.pointer = i2c_hid_guid;
	params[1].type = ACPI_TYPE_INTEGER;
	params[1].integer.value = 1;
	params[2].type = ACPI_TYPE_INTEGER;
	params[2].integer.value = 1; /* HID function */
	params[3].type = ACPI_TYPE_INTEGER;
	params[3].integer.value = 0;

	if (ACPI_FAILURE(acpi_evaluate_object(handle, "_DSM", &input, &buf))) {
		dev_err(&client->dev, "device _DSM execution failed\n");
		return -ENODEV;
	}

	obj = (union acpi_object *)buf.pointer;
	if (obj->type != ACPI_TYPE_INTEGER) {
		dev_err(&client->dev, "device _DSM returned invalid type: %d\n",
			obj->type);
		kfree(buf.pointer);
		return -EINVAL;
	}

	pdata->hid_descriptor_address = obj->integer.value;

	kfree(buf.pointer);
	return 0;
}

static const struct acpi_device_id i2c_hid_acpi_match[] = {
	{"ACPI0C50", 0 },
	{"PNP0C50", 0 },
	{ },
};
MODULE_DEVICE_TABLE(acpi, i2c_hid_acpi_match);
#else
static inline int i2c_hid_acpi_pdata(struct i2c_client *client,
		struct i2c_hid_platform_data *pdata)
{
	return -ENODEV;
}
#endif

static int __devinit i2c_hid_probe(struct i2c_client *client,
		const struct i2c_device_id *dev_id)
{
@@ -821,11 +888,6 @@ static int __devinit i2c_hid_probe(struct i2c_client *client,

	dbg_hid("HID probe called for i2c 0x%02x\n", client->addr);

	if (!platform_data) {
		dev_err(&client->dev, "HID register address not provided\n");
		return -EINVAL;
	}

	if (!client->irq) {
		dev_err(&client->dev,
			"HID over i2c has not been provided an Int IRQ\n");
@@ -836,11 +898,22 @@ static int __devinit i2c_hid_probe(struct i2c_client *client,
	if (!ihid)
		return -ENOMEM;

	if (!platform_data) {
		ret = i2c_hid_acpi_pdata(client, &ihid->pdata);
		if (ret) {
			dev_err(&client->dev,
				"HID register address not provided\n");
			goto err;
		}
	} else {
		ihid->pdata = *platform_data;
	}

	i2c_set_clientdata(client, ihid);

	ihid->client = client;

	hidRegister = platform_data->hid_descriptor_address;
	hidRegister = ihid->pdata.hid_descriptor_address;
	ihid->wHIDDescRegister = cpu_to_le16(hidRegister);

	init_waitqueue_head(&ihid->wait);
@@ -873,6 +946,7 @@ static int __devinit i2c_hid_probe(struct i2c_client *client,
	hid->hid_get_raw_report = i2c_hid_get_raw_report;
	hid->hid_output_raw_report = i2c_hid_output_raw_report;
	hid->dev.parent = &client->dev;
	ACPI_HANDLE_SET(&hid->dev, ACPI_HANDLE(&client->dev));
	hid->bus = BUS_I2C;
	hid->version = le16_to_cpu(ihid->hdesc.bcdVersion);
	hid->vendor = le16_to_cpu(ihid->hdesc.wVendorID);
@@ -964,6 +1038,7 @@ static struct i2c_driver i2c_hid_driver = {
		.name	= "i2c_hid",
		.owner	= THIS_MODULE,
		.pm	= &i2c_hid_pm,
		.acpi_match_table = ACPI_PTR(i2c_hid_acpi_match),
	},

	.probe		= i2c_hid_probe,