brintos

brintos / linux-shallow public Read only

0
0
Text · 26.4 KiB · cb6fce6 Raw
1142 lines · c
1// SPDX-License-Identifier: GPL-2.0-or-later2/*3 *  Copyright (C) 2009  Thadeu Lima de Souza Cascardo <cascardo@holoscopio.com>4 */5 6 7#include <linux/init.h>8#include <linux/module.h>9#include <linux/slab.h>10#include <linux/workqueue.h>11#include <linux/acpi.h>12#include <linux/backlight.h>13#include <linux/input.h>14#include <linux/rfkill.h>15 16struct cmpc_accel {17	int sensitivity;18	int g_select;19	int inputdev_state;20};21 22#define CMPC_ACCEL_DEV_STATE_CLOSED	023#define CMPC_ACCEL_DEV_STATE_OPEN	124 25#define CMPC_ACCEL_SENSITIVITY_DEFAULT		526#define CMPC_ACCEL_G_SELECT_DEFAULT		027 28#define CMPC_ACCEL_HID		"ACCE0000"29#define CMPC_ACCEL_HID_V4	"ACCE0001"30#define CMPC_TABLET_HID		"TBLT0000"31#define CMPC_IPML_HID	"IPML200"32#define CMPC_KEYS_HID		"FNBT0000"33 34/*35 * Generic input device code.36 */37 38typedef void (*input_device_init)(struct input_dev *dev);39 40static int cmpc_add_acpi_notify_device(struct acpi_device *acpi, char *name,41				       input_device_init idev_init)42{43	struct input_dev *inputdev;44	int error;45 46	inputdev = input_allocate_device();47	if (!inputdev)48		return -ENOMEM;49	inputdev->name = name;50	inputdev->dev.parent = &acpi->dev;51	idev_init(inputdev);52	error = input_register_device(inputdev);53	if (error) {54		input_free_device(inputdev);55		return error;56	}57	dev_set_drvdata(&acpi->dev, inputdev);58	return 0;59}60 61static int cmpc_remove_acpi_notify_device(struct acpi_device *acpi)62{63	struct input_dev *inputdev = dev_get_drvdata(&acpi->dev);64	input_unregister_device(inputdev);65	return 0;66}67 68/*69 * Accelerometer code for Classmate V470 */71static acpi_status cmpc_start_accel_v4(acpi_handle handle)72{73	union acpi_object param[4];74	struct acpi_object_list input;75	acpi_status status;76 77	param[0].type = ACPI_TYPE_INTEGER;78	param[0].integer.value = 0x3;79	param[1].type = ACPI_TYPE_INTEGER;80	param[1].integer.value = 0;81	param[2].type = ACPI_TYPE_INTEGER;82	param[2].integer.value = 0;83	param[3].type = ACPI_TYPE_INTEGER;84	param[3].integer.value = 0;85	input.count = 4;86	input.pointer = param;87	status = acpi_evaluate_object(handle, "ACMD", &input, NULL);88	return status;89}90 91static acpi_status cmpc_stop_accel_v4(acpi_handle handle)92{93	union acpi_object param[4];94	struct acpi_object_list input;95	acpi_status status;96 97	param[0].type = ACPI_TYPE_INTEGER;98	param[0].integer.value = 0x4;99	param[1].type = ACPI_TYPE_INTEGER;100	param[1].integer.value = 0;101	param[2].type = ACPI_TYPE_INTEGER;102	param[2].integer.value = 0;103	param[3].type = ACPI_TYPE_INTEGER;104	param[3].integer.value = 0;105	input.count = 4;106	input.pointer = param;107	status = acpi_evaluate_object(handle, "ACMD", &input, NULL);108	return status;109}110 111static acpi_status cmpc_accel_set_sensitivity_v4(acpi_handle handle, int val)112{113	union acpi_object param[4];114	struct acpi_object_list input;115 116	param[0].type = ACPI_TYPE_INTEGER;117	param[0].integer.value = 0x02;118	param[1].type = ACPI_TYPE_INTEGER;119	param[1].integer.value = val;120	param[2].type = ACPI_TYPE_INTEGER;121	param[2].integer.value = 0;122	param[3].type = ACPI_TYPE_INTEGER;123	param[3].integer.value = 0;124	input.count = 4;125	input.pointer = param;126	return acpi_evaluate_object(handle, "ACMD", &input, NULL);127}128 129static acpi_status cmpc_accel_set_g_select_v4(acpi_handle handle, int val)130{131	union acpi_object param[4];132	struct acpi_object_list input;133 134	param[0].type = ACPI_TYPE_INTEGER;135	param[0].integer.value = 0x05;136	param[1].type = ACPI_TYPE_INTEGER;137	param[1].integer.value = val;138	param[2].type = ACPI_TYPE_INTEGER;139	param[2].integer.value = 0;140	param[3].type = ACPI_TYPE_INTEGER;141	param[3].integer.value = 0;142	input.count = 4;143	input.pointer = param;144	return acpi_evaluate_object(handle, "ACMD", &input, NULL);145}146 147static acpi_status cmpc_get_accel_v4(acpi_handle handle,148				     int16_t *x,149				     int16_t *y,150				     int16_t *z)151{152	union acpi_object param[4];153	struct acpi_object_list input;154	struct acpi_buffer output = { ACPI_ALLOCATE_BUFFER, NULL };155	int16_t *locs;156	acpi_status status;157 158	param[0].type = ACPI_TYPE_INTEGER;159	param[0].integer.value = 0x01;160	param[1].type = ACPI_TYPE_INTEGER;161	param[1].integer.value = 0;162	param[2].type = ACPI_TYPE_INTEGER;163	param[2].integer.value = 0;164	param[3].type = ACPI_TYPE_INTEGER;165	param[3].integer.value = 0;166	input.count = 4;167	input.pointer = param;168	status = acpi_evaluate_object(handle, "ACMD", &input, &output);169	if (ACPI_SUCCESS(status)) {170		union acpi_object *obj;171		obj = output.pointer;172		locs = (int16_t *) obj->buffer.pointer;173		*x = locs[0];174		*y = locs[1];175		*z = locs[2];176		kfree(output.pointer);177	}178	return status;179}180 181static void cmpc_accel_handler_v4(struct acpi_device *dev, u32 event)182{183	if (event == 0x81) {184		int16_t x, y, z;185		acpi_status status;186 187		status = cmpc_get_accel_v4(dev->handle, &x, &y, &z);188		if (ACPI_SUCCESS(status)) {189			struct input_dev *inputdev = dev_get_drvdata(&dev->dev);190 191			input_report_abs(inputdev, ABS_X, x);192			input_report_abs(inputdev, ABS_Y, y);193			input_report_abs(inputdev, ABS_Z, z);194			input_sync(inputdev);195		}196	}197}198 199static ssize_t cmpc_accel_sensitivity_show_v4(struct device *dev,200					      struct device_attribute *attr,201					      char *buf)202{203	struct acpi_device *acpi;204	struct input_dev *inputdev;205	struct cmpc_accel *accel;206 207	acpi = to_acpi_device(dev);208	inputdev = dev_get_drvdata(&acpi->dev);209	accel = dev_get_drvdata(&inputdev->dev);210 211	return sprintf(buf, "%d\n", accel->sensitivity);212}213 214static ssize_t cmpc_accel_sensitivity_store_v4(struct device *dev,215					       struct device_attribute *attr,216					       const char *buf, size_t count)217{218	struct acpi_device *acpi;219	struct input_dev *inputdev;220	struct cmpc_accel *accel;221	unsigned long sensitivity;222	int r;223 224	acpi = to_acpi_device(dev);225	inputdev = dev_get_drvdata(&acpi->dev);226	accel = dev_get_drvdata(&inputdev->dev);227 228	r = kstrtoul(buf, 0, &sensitivity);229	if (r)230		return r;231 232	/* sensitivity must be between 1 and 127 */233	if (sensitivity < 1 || sensitivity > 127)234		return -EINVAL;235 236	accel->sensitivity = sensitivity;237	cmpc_accel_set_sensitivity_v4(acpi->handle, sensitivity);238 239	return strnlen(buf, count);240}241 242static struct device_attribute cmpc_accel_sensitivity_attr_v4 = {243	.attr = { .name = "sensitivity", .mode = 0660 },244	.show = cmpc_accel_sensitivity_show_v4,245	.store = cmpc_accel_sensitivity_store_v4246};247 248static ssize_t cmpc_accel_g_select_show_v4(struct device *dev,249					   struct device_attribute *attr,250					   char *buf)251{252	struct acpi_device *acpi;253	struct input_dev *inputdev;254	struct cmpc_accel *accel;255 256	acpi = to_acpi_device(dev);257	inputdev = dev_get_drvdata(&acpi->dev);258	accel = dev_get_drvdata(&inputdev->dev);259 260	return sprintf(buf, "%d\n", accel->g_select);261}262 263static ssize_t cmpc_accel_g_select_store_v4(struct device *dev,264					    struct device_attribute *attr,265					    const char *buf, size_t count)266{267	struct acpi_device *acpi;268	struct input_dev *inputdev;269	struct cmpc_accel *accel;270	unsigned long g_select;271	int r;272 273	acpi = to_acpi_device(dev);274	inputdev = dev_get_drvdata(&acpi->dev);275	accel = dev_get_drvdata(&inputdev->dev);276 277	r = kstrtoul(buf, 0, &g_select);278	if (r)279		return r;280 281	/* 0 means 1.5g, 1 means 6g, everything else is wrong */282	if (g_select != 0 && g_select != 1)283		return -EINVAL;284 285	accel->g_select = g_select;286	cmpc_accel_set_g_select_v4(acpi->handle, g_select);287 288	return strnlen(buf, count);289}290 291static struct device_attribute cmpc_accel_g_select_attr_v4 = {292	.attr = { .name = "g_select", .mode = 0660 },293	.show = cmpc_accel_g_select_show_v4,294	.store = cmpc_accel_g_select_store_v4295};296 297static int cmpc_accel_open_v4(struct input_dev *input)298{299	struct acpi_device *acpi;300	struct cmpc_accel *accel;301 302	acpi = to_acpi_device(input->dev.parent);303	accel = dev_get_drvdata(&input->dev);304 305	cmpc_accel_set_sensitivity_v4(acpi->handle, accel->sensitivity);306	cmpc_accel_set_g_select_v4(acpi->handle, accel->g_select);307 308	if (ACPI_SUCCESS(cmpc_start_accel_v4(acpi->handle))) {309		accel->inputdev_state = CMPC_ACCEL_DEV_STATE_OPEN;310		return 0;311	}312	return -EIO;313}314 315static void cmpc_accel_close_v4(struct input_dev *input)316{317	struct acpi_device *acpi;318	struct cmpc_accel *accel;319 320	acpi = to_acpi_device(input->dev.parent);321	accel = dev_get_drvdata(&input->dev);322 323	cmpc_stop_accel_v4(acpi->handle);324	accel->inputdev_state = CMPC_ACCEL_DEV_STATE_CLOSED;325}326 327static void cmpc_accel_idev_init_v4(struct input_dev *inputdev)328{329	set_bit(EV_ABS, inputdev->evbit);330	input_set_abs_params(inputdev, ABS_X, -255, 255, 16, 0);331	input_set_abs_params(inputdev, ABS_Y, -255, 255, 16, 0);332	input_set_abs_params(inputdev, ABS_Z, -255, 255, 16, 0);333	inputdev->open = cmpc_accel_open_v4;334	inputdev->close = cmpc_accel_close_v4;335}336 337#ifdef CONFIG_PM_SLEEP338static int cmpc_accel_suspend_v4(struct device *dev)339{340	struct input_dev *inputdev;341	struct cmpc_accel *accel;342 343	inputdev = dev_get_drvdata(dev);344	accel = dev_get_drvdata(&inputdev->dev);345 346	if (accel->inputdev_state == CMPC_ACCEL_DEV_STATE_OPEN)347		return cmpc_stop_accel_v4(to_acpi_device(dev)->handle);348 349	return 0;350}351 352static int cmpc_accel_resume_v4(struct device *dev)353{354	struct input_dev *inputdev;355	struct cmpc_accel *accel;356 357	inputdev = dev_get_drvdata(dev);358	accel = dev_get_drvdata(&inputdev->dev);359 360	if (accel->inputdev_state == CMPC_ACCEL_DEV_STATE_OPEN) {361		cmpc_accel_set_sensitivity_v4(to_acpi_device(dev)->handle,362					      accel->sensitivity);363		cmpc_accel_set_g_select_v4(to_acpi_device(dev)->handle,364					   accel->g_select);365 366		if (ACPI_FAILURE(cmpc_start_accel_v4(to_acpi_device(dev)->handle)))367			return -EIO;368	}369 370	return 0;371}372#endif373 374static int cmpc_accel_add_v4(struct acpi_device *acpi)375{376	int error;377	struct input_dev *inputdev;378	struct cmpc_accel *accel;379 380	accel = kmalloc(sizeof(*accel), GFP_KERNEL);381	if (!accel)382		return -ENOMEM;383 384	accel->inputdev_state = CMPC_ACCEL_DEV_STATE_CLOSED;385 386	accel->sensitivity = CMPC_ACCEL_SENSITIVITY_DEFAULT;387	cmpc_accel_set_sensitivity_v4(acpi->handle, accel->sensitivity);388 389	error = device_create_file(&acpi->dev, &cmpc_accel_sensitivity_attr_v4);390	if (error)391		goto failed_sensitivity;392 393	accel->g_select = CMPC_ACCEL_G_SELECT_DEFAULT;394	cmpc_accel_set_g_select_v4(acpi->handle, accel->g_select);395 396	error = device_create_file(&acpi->dev, &cmpc_accel_g_select_attr_v4);397	if (error)398		goto failed_g_select;399 400	error = cmpc_add_acpi_notify_device(acpi, "cmpc_accel_v4",401					    cmpc_accel_idev_init_v4);402	if (error)403		goto failed_input;404 405	inputdev = dev_get_drvdata(&acpi->dev);406	dev_set_drvdata(&inputdev->dev, accel);407 408	return 0;409 410failed_input:411	device_remove_file(&acpi->dev, &cmpc_accel_g_select_attr_v4);412failed_g_select:413	device_remove_file(&acpi->dev, &cmpc_accel_sensitivity_attr_v4);414failed_sensitivity:415	kfree(accel);416	return error;417}418 419static void cmpc_accel_remove_v4(struct acpi_device *acpi)420{421	device_remove_file(&acpi->dev, &cmpc_accel_sensitivity_attr_v4);422	device_remove_file(&acpi->dev, &cmpc_accel_g_select_attr_v4);423	cmpc_remove_acpi_notify_device(acpi);424}425 426static SIMPLE_DEV_PM_OPS(cmpc_accel_pm, cmpc_accel_suspend_v4,427			 cmpc_accel_resume_v4);428 429static const struct acpi_device_id cmpc_accel_device_ids_v4[] = {430	{CMPC_ACCEL_HID_V4, 0},431	{"", 0}432};433 434static struct acpi_driver cmpc_accel_acpi_driver_v4 = {435	.name = "cmpc_accel_v4",436	.class = "cmpc_accel_v4",437	.ids = cmpc_accel_device_ids_v4,438	.ops = {439		.add = cmpc_accel_add_v4,440		.remove = cmpc_accel_remove_v4,441		.notify = cmpc_accel_handler_v4,442	},443	.drv.pm = &cmpc_accel_pm,444};445 446 447/*448 * Accelerometer code for Classmate versions prior to V4449 */450static acpi_status cmpc_start_accel(acpi_handle handle)451{452	union acpi_object param[2];453	struct acpi_object_list input;454	acpi_status status;455 456	param[0].type = ACPI_TYPE_INTEGER;457	param[0].integer.value = 0x3;458	param[1].type = ACPI_TYPE_INTEGER;459	input.count = 2;460	input.pointer = param;461	status = acpi_evaluate_object(handle, "ACMD", &input, NULL);462	return status;463}464 465static acpi_status cmpc_stop_accel(acpi_handle handle)466{467	union acpi_object param[2];468	struct acpi_object_list input;469	acpi_status status;470 471	param[0].type = ACPI_TYPE_INTEGER;472	param[0].integer.value = 0x4;473	param[1].type = ACPI_TYPE_INTEGER;474	input.count = 2;475	input.pointer = param;476	status = acpi_evaluate_object(handle, "ACMD", &input, NULL);477	return status;478}479 480static acpi_status cmpc_accel_set_sensitivity(acpi_handle handle, int val)481{482	union acpi_object param[2];483	struct acpi_object_list input;484 485	param[0].type = ACPI_TYPE_INTEGER;486	param[0].integer.value = 0x02;487	param[1].type = ACPI_TYPE_INTEGER;488	param[1].integer.value = val;489	input.count = 2;490	input.pointer = param;491	return acpi_evaluate_object(handle, "ACMD", &input, NULL);492}493 494static acpi_status cmpc_get_accel(acpi_handle handle,495				  unsigned char *x,496				  unsigned char *y,497				  unsigned char *z)498{499	union acpi_object param[2];500	struct acpi_object_list input;501	struct acpi_buffer output = { ACPI_ALLOCATE_BUFFER, NULL };502	unsigned char *locs;503	acpi_status status;504 505	param[0].type = ACPI_TYPE_INTEGER;506	param[0].integer.value = 0x01;507	param[1].type = ACPI_TYPE_INTEGER;508	input.count = 2;509	input.pointer = param;510	status = acpi_evaluate_object(handle, "ACMD", &input, &output);511	if (ACPI_SUCCESS(status)) {512		union acpi_object *obj;513		obj = output.pointer;514		locs = obj->buffer.pointer;515		*x = locs[0];516		*y = locs[1];517		*z = locs[2];518		kfree(output.pointer);519	}520	return status;521}522 523static void cmpc_accel_handler(struct acpi_device *dev, u32 event)524{525	if (event == 0x81) {526		unsigned char x, y, z;527		acpi_status status;528 529		status = cmpc_get_accel(dev->handle, &x, &y, &z);530		if (ACPI_SUCCESS(status)) {531			struct input_dev *inputdev = dev_get_drvdata(&dev->dev);532 533			input_report_abs(inputdev, ABS_X, x);534			input_report_abs(inputdev, ABS_Y, y);535			input_report_abs(inputdev, ABS_Z, z);536			input_sync(inputdev);537		}538	}539}540 541static ssize_t cmpc_accel_sensitivity_show(struct device *dev,542					   struct device_attribute *attr,543					   char *buf)544{545	struct acpi_device *acpi;546	struct input_dev *inputdev;547	struct cmpc_accel *accel;548 549	acpi = to_acpi_device(dev);550	inputdev = dev_get_drvdata(&acpi->dev);551	accel = dev_get_drvdata(&inputdev->dev);552 553	return sprintf(buf, "%d\n", accel->sensitivity);554}555 556static ssize_t cmpc_accel_sensitivity_store(struct device *dev,557					    struct device_attribute *attr,558					    const char *buf, size_t count)559{560	struct acpi_device *acpi;561	struct input_dev *inputdev;562	struct cmpc_accel *accel;563	unsigned long sensitivity;564	int r;565 566	acpi = to_acpi_device(dev);567	inputdev = dev_get_drvdata(&acpi->dev);568	accel = dev_get_drvdata(&inputdev->dev);569 570	r = kstrtoul(buf, 0, &sensitivity);571	if (r)572		return r;573 574	accel->sensitivity = sensitivity;575	cmpc_accel_set_sensitivity(acpi->handle, sensitivity);576 577	return strnlen(buf, count);578}579 580static struct device_attribute cmpc_accel_sensitivity_attr = {581	.attr = { .name = "sensitivity", .mode = 0660 },582	.show = cmpc_accel_sensitivity_show,583	.store = cmpc_accel_sensitivity_store584};585 586static int cmpc_accel_open(struct input_dev *input)587{588	struct acpi_device *acpi;589 590	acpi = to_acpi_device(input->dev.parent);591	if (ACPI_SUCCESS(cmpc_start_accel(acpi->handle)))592		return 0;593	return -EIO;594}595 596static void cmpc_accel_close(struct input_dev *input)597{598	struct acpi_device *acpi;599 600	acpi = to_acpi_device(input->dev.parent);601	cmpc_stop_accel(acpi->handle);602}603 604static void cmpc_accel_idev_init(struct input_dev *inputdev)605{606	set_bit(EV_ABS, inputdev->evbit);607	input_set_abs_params(inputdev, ABS_X, 0, 255, 8, 0);608	input_set_abs_params(inputdev, ABS_Y, 0, 255, 8, 0);609	input_set_abs_params(inputdev, ABS_Z, 0, 255, 8, 0);610	inputdev->open = cmpc_accel_open;611	inputdev->close = cmpc_accel_close;612}613 614static int cmpc_accel_add(struct acpi_device *acpi)615{616	int error;617	struct input_dev *inputdev;618	struct cmpc_accel *accel;619 620	accel = kmalloc(sizeof(*accel), GFP_KERNEL);621	if (!accel)622		return -ENOMEM;623 624	accel->sensitivity = CMPC_ACCEL_SENSITIVITY_DEFAULT;625	cmpc_accel_set_sensitivity(acpi->handle, accel->sensitivity);626 627	error = device_create_file(&acpi->dev, &cmpc_accel_sensitivity_attr);628	if (error)629		goto failed_file;630 631	error = cmpc_add_acpi_notify_device(acpi, "cmpc_accel",632					    cmpc_accel_idev_init);633	if (error)634		goto failed_input;635 636	inputdev = dev_get_drvdata(&acpi->dev);637	dev_set_drvdata(&inputdev->dev, accel);638 639	return 0;640 641failed_input:642	device_remove_file(&acpi->dev, &cmpc_accel_sensitivity_attr);643failed_file:644	kfree(accel);645	return error;646}647 648static void cmpc_accel_remove(struct acpi_device *acpi)649{650	device_remove_file(&acpi->dev, &cmpc_accel_sensitivity_attr);651	cmpc_remove_acpi_notify_device(acpi);652}653 654static const struct acpi_device_id cmpc_accel_device_ids[] = {655	{CMPC_ACCEL_HID, 0},656	{"", 0}657};658 659static struct acpi_driver cmpc_accel_acpi_driver = {660	.name = "cmpc_accel",661	.class = "cmpc_accel",662	.ids = cmpc_accel_device_ids,663	.ops = {664		.add = cmpc_accel_add,665		.remove = cmpc_accel_remove,666		.notify = cmpc_accel_handler,667	}668};669 670 671/*672 * Tablet mode code.673 */674static acpi_status cmpc_get_tablet(acpi_handle handle,675				   unsigned long long *value)676{677	union acpi_object param;678	struct acpi_object_list input;679	unsigned long long output;680	acpi_status status;681 682	param.type = ACPI_TYPE_INTEGER;683	param.integer.value = 0x01;684	input.count = 1;685	input.pointer = &param;686	status = acpi_evaluate_integer(handle, "TCMD", &input, &output);687	if (ACPI_SUCCESS(status))688		*value = output;689	return status;690}691 692static void cmpc_tablet_handler(struct acpi_device *dev, u32 event)693{694	unsigned long long val = 0;695	struct input_dev *inputdev = dev_get_drvdata(&dev->dev);696 697	if (event == 0x81) {698		if (ACPI_SUCCESS(cmpc_get_tablet(dev->handle, &val))) {699			input_report_switch(inputdev, SW_TABLET_MODE, !val);700			input_sync(inputdev);701		}702	}703}704 705static void cmpc_tablet_idev_init(struct input_dev *inputdev)706{707	unsigned long long val = 0;708	struct acpi_device *acpi;709 710	set_bit(EV_SW, inputdev->evbit);711	set_bit(SW_TABLET_MODE, inputdev->swbit);712 713	acpi = to_acpi_device(inputdev->dev.parent);714	if (ACPI_SUCCESS(cmpc_get_tablet(acpi->handle, &val))) {715		input_report_switch(inputdev, SW_TABLET_MODE, !val);716		input_sync(inputdev);717	}718}719 720static int cmpc_tablet_add(struct acpi_device *acpi)721{722	return cmpc_add_acpi_notify_device(acpi, "cmpc_tablet",723					   cmpc_tablet_idev_init);724}725 726static void cmpc_tablet_remove(struct acpi_device *acpi)727{728	cmpc_remove_acpi_notify_device(acpi);729}730 731#ifdef CONFIG_PM_SLEEP732static int cmpc_tablet_resume(struct device *dev)733{734	struct input_dev *inputdev = dev_get_drvdata(dev);735 736	unsigned long long val = 0;737	if (ACPI_SUCCESS(cmpc_get_tablet(to_acpi_device(dev)->handle, &val))) {738		input_report_switch(inputdev, SW_TABLET_MODE, !val);739		input_sync(inputdev);740	}741	return 0;742}743#endif744 745static SIMPLE_DEV_PM_OPS(cmpc_tablet_pm, NULL, cmpc_tablet_resume);746 747static const struct acpi_device_id cmpc_tablet_device_ids[] = {748	{CMPC_TABLET_HID, 0},749	{"", 0}750};751 752static struct acpi_driver cmpc_tablet_acpi_driver = {753	.name = "cmpc_tablet",754	.class = "cmpc_tablet",755	.ids = cmpc_tablet_device_ids,756	.ops = {757		.add = cmpc_tablet_add,758		.remove = cmpc_tablet_remove,759		.notify = cmpc_tablet_handler,760	},761	.drv.pm = &cmpc_tablet_pm,762};763 764 765/*766 * Backlight code.767 */768 769static acpi_status cmpc_get_brightness(acpi_handle handle,770				       unsigned long long *value)771{772	union acpi_object param;773	struct acpi_object_list input;774	unsigned long long output;775	acpi_status status;776 777	param.type = ACPI_TYPE_INTEGER;778	param.integer.value = 0xC0;779	input.count = 1;780	input.pointer = &param;781	status = acpi_evaluate_integer(handle, "GRDI", &input, &output);782	if (ACPI_SUCCESS(status))783		*value = output;784	return status;785}786 787static acpi_status cmpc_set_brightness(acpi_handle handle,788				       unsigned long long value)789{790	union acpi_object param[2];791	struct acpi_object_list input;792	acpi_status status;793	unsigned long long output;794 795	param[0].type = ACPI_TYPE_INTEGER;796	param[0].integer.value = 0xC0;797	param[1].type = ACPI_TYPE_INTEGER;798	param[1].integer.value = value;799	input.count = 2;800	input.pointer = param;801	status = acpi_evaluate_integer(handle, "GWRI", &input, &output);802	return status;803}804 805static int cmpc_bl_get_brightness(struct backlight_device *bd)806{807	acpi_status status;808	acpi_handle handle;809	unsigned long long brightness;810 811	handle = bl_get_data(bd);812	status = cmpc_get_brightness(handle, &brightness);813	if (ACPI_SUCCESS(status))814		return brightness;815	else816		return -1;817}818 819static int cmpc_bl_update_status(struct backlight_device *bd)820{821	acpi_status status;822	acpi_handle handle;823 824	handle = bl_get_data(bd);825	status = cmpc_set_brightness(handle, bd->props.brightness);826	if (ACPI_SUCCESS(status))827		return 0;828	else829		return -1;830}831 832static const struct backlight_ops cmpc_bl_ops = {833	.get_brightness = cmpc_bl_get_brightness,834	.update_status = cmpc_bl_update_status835};836 837/*838 * RFKILL code.839 */840 841static acpi_status cmpc_get_rfkill_wlan(acpi_handle handle,842					unsigned long long *value)843{844	union acpi_object param;845	struct acpi_object_list input;846	unsigned long long output;847	acpi_status status;848 849	param.type = ACPI_TYPE_INTEGER;850	param.integer.value = 0xC1;851	input.count = 1;852	input.pointer = &param;853	status = acpi_evaluate_integer(handle, "GRDI", &input, &output);854	if (ACPI_SUCCESS(status))855		*value = output;856	return status;857}858 859static acpi_status cmpc_set_rfkill_wlan(acpi_handle handle,860					unsigned long long value)861{862	union acpi_object param[2];863	struct acpi_object_list input;864	acpi_status status;865	unsigned long long output;866 867	param[0].type = ACPI_TYPE_INTEGER;868	param[0].integer.value = 0xC1;869	param[1].type = ACPI_TYPE_INTEGER;870	param[1].integer.value = value;871	input.count = 2;872	input.pointer = param;873	status = acpi_evaluate_integer(handle, "GWRI", &input, &output);874	return status;875}876 877static void cmpc_rfkill_query(struct rfkill *rfkill, void *data)878{879	acpi_status status;880	acpi_handle handle;881	unsigned long long state;882	bool blocked;883 884	handle = data;885	status = cmpc_get_rfkill_wlan(handle, &state);886	if (ACPI_SUCCESS(status)) {887		blocked = state & 1 ? false : true;888		rfkill_set_sw_state(rfkill, blocked);889	}890}891 892static int cmpc_rfkill_block(void *data, bool blocked)893{894	acpi_status status;895	acpi_handle handle;896	unsigned long long state;897	bool is_blocked;898 899	handle = data;900	status = cmpc_get_rfkill_wlan(handle, &state);901	if (ACPI_FAILURE(status))902		return -ENODEV;903	/* Check if we really need to call cmpc_set_rfkill_wlan */904	is_blocked = state & 1 ? false : true;905	if (is_blocked != blocked) {906		state = blocked ? 0 : 1;907		status = cmpc_set_rfkill_wlan(handle, state);908		if (ACPI_FAILURE(status))909			return -ENODEV;910	}911	return 0;912}913 914static const struct rfkill_ops cmpc_rfkill_ops = {915	.query = cmpc_rfkill_query,916	.set_block = cmpc_rfkill_block,917};918 919/*920 * Common backlight and rfkill code.921 */922 923struct ipml200_dev {924	struct backlight_device *bd;925	struct rfkill *rf;926};927 928static int cmpc_ipml_add(struct acpi_device *acpi)929{930	int retval;931	struct ipml200_dev *ipml;932	struct backlight_properties props;933 934	ipml = kmalloc(sizeof(*ipml), GFP_KERNEL);935	if (ipml == NULL)936		return -ENOMEM;937 938	memset(&props, 0, sizeof(struct backlight_properties));939	props.type = BACKLIGHT_PLATFORM;940	props.max_brightness = 7;941	ipml->bd = backlight_device_register("cmpc_bl", &acpi->dev,942					     acpi->handle, &cmpc_bl_ops,943					     &props);944	if (IS_ERR(ipml->bd)) {945		retval = PTR_ERR(ipml->bd);946		goto out_bd;947	}948 949	ipml->rf = rfkill_alloc("cmpc_rfkill", &acpi->dev, RFKILL_TYPE_WLAN,950				&cmpc_rfkill_ops, acpi->handle);951	/*952	 * If RFKILL is disabled, rfkill_alloc will return ERR_PTR(-ENODEV).953	 * This is OK, however, since all other uses of the device will not954	 * dereference it.955	 */956	if (ipml->rf) {957		retval = rfkill_register(ipml->rf);958		if (retval) {959			rfkill_destroy(ipml->rf);960			ipml->rf = NULL;961		}962	}963 964	dev_set_drvdata(&acpi->dev, ipml);965	return 0;966 967out_bd:968	kfree(ipml);969	return retval;970}971 972static void cmpc_ipml_remove(struct acpi_device *acpi)973{974	struct ipml200_dev *ipml;975 976	ipml = dev_get_drvdata(&acpi->dev);977 978	backlight_device_unregister(ipml->bd);979 980	if (ipml->rf) {981		rfkill_unregister(ipml->rf);982		rfkill_destroy(ipml->rf);983	}984 985	kfree(ipml);986}987 988static const struct acpi_device_id cmpc_ipml_device_ids[] = {989	{CMPC_IPML_HID, 0},990	{"", 0}991};992 993static struct acpi_driver cmpc_ipml_acpi_driver = {994	.name = "cmpc",995	.class = "cmpc",996	.ids = cmpc_ipml_device_ids,997	.ops = {998		.add = cmpc_ipml_add,999		.remove = cmpc_ipml_remove1000	}1001};1002 1003 1004/*1005 * Extra keys code.1006 */1007static int cmpc_keys_codes[] = {1008	KEY_UNKNOWN,1009	KEY_WLAN,1010	KEY_SWITCHVIDEOMODE,1011	KEY_BRIGHTNESSDOWN,1012	KEY_BRIGHTNESSUP,1013	KEY_VENDOR,1014	KEY_UNKNOWN,1015	KEY_CAMERA,1016	KEY_BACK,1017	KEY_FORWARD,1018	KEY_UNKNOWN,1019	KEY_WLAN, /* NL3: 0x8b (press), 0x9b (release) */1020	KEY_MAX1021};1022 1023static void cmpc_keys_handler(struct acpi_device *dev, u32 event)1024{1025	struct input_dev *inputdev;1026	int code = KEY_MAX;1027 1028	if ((event & 0x0F) < ARRAY_SIZE(cmpc_keys_codes))1029		code = cmpc_keys_codes[event & 0x0F];1030	inputdev = dev_get_drvdata(&dev->dev);1031	input_report_key(inputdev, code, !(event & 0x10));1032	input_sync(inputdev);1033}1034 1035static void cmpc_keys_idev_init(struct input_dev *inputdev)1036{1037	int i;1038 1039	set_bit(EV_KEY, inputdev->evbit);1040	for (i = 0; cmpc_keys_codes[i] != KEY_MAX; i++)1041		set_bit(cmpc_keys_codes[i], inputdev->keybit);1042}1043 1044static int cmpc_keys_add(struct acpi_device *acpi)1045{1046	return cmpc_add_acpi_notify_device(acpi, "cmpc_keys",1047					   cmpc_keys_idev_init);1048}1049 1050static void cmpc_keys_remove(struct acpi_device *acpi)1051{1052	cmpc_remove_acpi_notify_device(acpi);1053}1054 1055static const struct acpi_device_id cmpc_keys_device_ids[] = {1056	{CMPC_KEYS_HID, 0},1057	{"", 0}1058};1059 1060static struct acpi_driver cmpc_keys_acpi_driver = {1061	.name = "cmpc_keys",1062	.class = "cmpc_keys",1063	.ids = cmpc_keys_device_ids,1064	.ops = {1065		.add = cmpc_keys_add,1066		.remove = cmpc_keys_remove,1067		.notify = cmpc_keys_handler,1068	}1069};1070 1071 1072/*1073 * General init/exit code.1074 */1075 1076static int cmpc_init(void)1077{1078	int r;1079 1080	r = acpi_bus_register_driver(&cmpc_keys_acpi_driver);1081	if (r)1082		goto failed_keys;1083 1084	r = acpi_bus_register_driver(&cmpc_ipml_acpi_driver);1085	if (r)1086		goto failed_bl;1087 1088	r = acpi_bus_register_driver(&cmpc_tablet_acpi_driver);1089	if (r)1090		goto failed_tablet;1091 1092	r = acpi_bus_register_driver(&cmpc_accel_acpi_driver);1093	if (r)1094		goto failed_accel;1095 1096	r = acpi_bus_register_driver(&cmpc_accel_acpi_driver_v4);1097	if (r)1098		goto failed_accel_v4;1099 1100	return r;1101 1102failed_accel_v4:1103	acpi_bus_unregister_driver(&cmpc_accel_acpi_driver);1104 1105failed_accel:1106	acpi_bus_unregister_driver(&cmpc_tablet_acpi_driver);1107 1108failed_tablet:1109	acpi_bus_unregister_driver(&cmpc_ipml_acpi_driver);1110 1111failed_bl:1112	acpi_bus_unregister_driver(&cmpc_keys_acpi_driver);1113 1114failed_keys:1115	return r;1116}1117 1118static void cmpc_exit(void)1119{1120	acpi_bus_unregister_driver(&cmpc_accel_acpi_driver_v4);1121	acpi_bus_unregister_driver(&cmpc_accel_acpi_driver);1122	acpi_bus_unregister_driver(&cmpc_tablet_acpi_driver);1123	acpi_bus_unregister_driver(&cmpc_ipml_acpi_driver);1124	acpi_bus_unregister_driver(&cmpc_keys_acpi_driver);1125}1126 1127module_init(cmpc_init);1128module_exit(cmpc_exit);1129 1130static const struct acpi_device_id cmpc_device_ids[] __maybe_unused = {1131	{CMPC_ACCEL_HID, 0},1132	{CMPC_ACCEL_HID_V4, 0},1133	{CMPC_TABLET_HID, 0},1134	{CMPC_IPML_HID, 0},1135	{CMPC_KEYS_HID, 0},1136	{"", 0}1137};1138 1139MODULE_DEVICE_TABLE(acpi, cmpc_device_ids);1140MODULE_DESCRIPTION("Support for Intel Classmate PC ACPI devices");1141MODULE_LICENSE("GPL");1142