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

Commit 1eb3fe1d authored by Marek Vasut's avatar Marek Vasut Committed by Matthew Garrett
Browse files

ACER: Add support for accelerometer sensor



This device is present on Iconia Tab W500.

Signed-off-by: default avatarMarek Vasut <marex@denx.de>
Cc: joeyli <jlee@suse.com>
Signed-off-by: default avatarMatthew Garrett <mjg@redhat.com>
parent 63a78bb1
Loading
Loading
Loading
Loading
+138 −0
Original line number Diff line number Diff line
@@ -95,6 +95,7 @@ MODULE_ALIAS("wmi:676AA15E-6A47-4D9F-A2CC-1E6D18D14026");

enum acer_wmi_event_ids {
	WMID_HOTKEY_EVENT = 0x1,
	WMID_ACCEL_EVENT = 0x5,
};

static const struct key_entry acer_wmi_keymap[] = {
@@ -130,6 +131,7 @@ static const struct key_entry acer_wmi_keymap[] = {
};

static struct input_dev *acer_wmi_input_dev;
static struct input_dev *acer_wmi_accel_dev;

struct event_return_value {
	u8 function;
@@ -200,6 +202,7 @@ struct hotkey_function_type_aa {
#define ACER_CAP_BLUETOOTH		(1<<2)
#define ACER_CAP_BRIGHTNESS		(1<<3)
#define ACER_CAP_THREEG			(1<<4)
#define ACER_CAP_ACCEL			(1<<5)
#define ACER_CAP_ANY			(0xFFFFFFFF)

/*
@@ -1398,6 +1401,60 @@ static void acer_backlight_exit(void)
	backlight_device_unregister(acer_backlight_device);
}

/*
 * Accelerometer device
 */
static acpi_handle gsensor_handle;

static int acer_gsensor_init(void)
{
	acpi_status status;
	struct acpi_buffer output;
	union acpi_object out_obj;

	output.length = sizeof(out_obj);
	output.pointer = &out_obj;
	status = acpi_evaluate_object(gsensor_handle, "_INI", NULL, &output);
	if (ACPI_FAILURE(status))
		return -1;

	return 0;
}

static int acer_gsensor_open(struct input_dev *input)
{
	return acer_gsensor_init();
}

static int acer_gsensor_event(void)
{
	acpi_status status;
	struct acpi_buffer output;
	union acpi_object out_obj[5];

	if (!has_cap(ACER_CAP_ACCEL))
		return -1;

	output.length = sizeof(out_obj);
	output.pointer = out_obj;

	status = acpi_evaluate_object(gsensor_handle, "RDVL", NULL, &output);
	if (ACPI_FAILURE(status))
		return -1;

	if (out_obj->package.count != 4)
		return -1;

	input_report_abs(acer_wmi_accel_dev, ABS_X,
		(s16)out_obj->package.elements[0].integer.value);
	input_report_abs(acer_wmi_accel_dev, ABS_Y,
		(s16)out_obj->package.elements[1].integer.value);
	input_report_abs(acer_wmi_accel_dev, ABS_Z,
		(s16)out_obj->package.elements[2].integer.value);
	input_sync(acer_wmi_accel_dev);
	return 0;
}

/*
 * Rfkill devices
 */
@@ -1673,6 +1730,9 @@ static void acer_wmi_notify(u32 value, void *context)
						   1, true);
		}
		break;
	case WMID_ACCEL_EVENT:
		acer_gsensor_event();
		break;
	default:
		pr_warn("Unknown function number - %d - %d\n",
			return_value.function, return_value.key_num);
@@ -1758,6 +1818,74 @@ static int acer_wmi_enable_lm(void)
	return status;
}

static acpi_status __init acer_wmi_get_handle_cb(acpi_handle ah, u32 level,
						void *ctx, void **retval)
{
	*(acpi_handle *)retval = ah;
	return AE_OK;
}

static int __init acer_wmi_get_handle(const char *name, const char *prop,
					acpi_handle *ah)
{
	acpi_status status;
	acpi_handle handle;

	BUG_ON(!name || !ah);

	handle = 0;
	status = acpi_get_devices(prop, acer_wmi_get_handle_cb,
					(void *)name, &handle);

	if (ACPI_SUCCESS(status)) {
		*ah = handle;
		return 0;
	} else {
		return -ENODEV;
	}
}

static int __init acer_wmi_accel_setup(void)
{
	int err;

	err = acer_wmi_get_handle("SENR", "BST0001", &gsensor_handle);
	if (err)
		return err;

	interface->capability |= ACER_CAP_ACCEL;

	acer_wmi_accel_dev = input_allocate_device();
	if (!acer_wmi_accel_dev)
		return -ENOMEM;

	acer_wmi_accel_dev->open = acer_gsensor_open;

	acer_wmi_accel_dev->name = "Acer BMA150 accelerometer";
	acer_wmi_accel_dev->phys = "wmi/input1";
	acer_wmi_accel_dev->id.bustype = BUS_HOST;
	acer_wmi_accel_dev->evbit[0] = BIT_MASK(EV_ABS);
	input_set_abs_params(acer_wmi_accel_dev, ABS_X, -16384, 16384, 0, 0);
	input_set_abs_params(acer_wmi_accel_dev, ABS_Y, -16384, 16384, 0, 0);
	input_set_abs_params(acer_wmi_accel_dev, ABS_Z, -16384, 16384, 0, 0);

	err = input_register_device(acer_wmi_accel_dev);
	if (err)
		goto err_free_dev;

	return 0;

err_free_dev:
	input_free_device(acer_wmi_accel_dev);
	return err;
}

static void acer_wmi_accel_destroy(void)
{
	input_unregister_device(acer_wmi_accel_dev);
	input_free_device(acer_wmi_accel_dev);
}

static int __init acer_wmi_input_setup(void)
{
	acpi_status status;
@@ -1912,6 +2040,9 @@ static int acer_resume(struct device *dev)
	if (has_cap(ACER_CAP_BRIGHTNESS))
		set_u32(data->brightness, ACER_CAP_BRIGHTNESS);

	if (has_cap(ACER_CAP_ACCEL))
		acer_gsensor_init();

	return 0;
}

@@ -2090,6 +2221,8 @@ static int __init acer_wmi_init(void)
			return err;
	}

	acer_wmi_accel_setup();

	err = platform_driver_register(&acer_platform_driver);
	if (err) {
		pr_err("Unable to register platform driver\n");
@@ -2133,6 +2266,8 @@ static int __init acer_wmi_init(void)
error_platform_register:
	if (wmi_has_guid(ACERWMID_EVENT_GUID))
		acer_wmi_input_destroy();
	if (has_cap(ACER_CAP_ACCEL))
		acer_wmi_accel_destroy();

	return err;
}
@@ -2142,6 +2277,9 @@ static void __exit acer_wmi_exit(void)
	if (wmi_has_guid(ACERWMID_EVENT_GUID))
		acer_wmi_input_destroy();

	if (has_cap(ACER_CAP_ACCEL))
		acer_wmi_accel_destroy();

	remove_sysfs(acer_platform_device);
	remove_debugfs();
	platform_device_unregister(acer_platform_device);