File Index
Files and directories
Sign in
FILE

fujitsu-laptop.c

File preview with syntax highlighting.

linux-6.19.14/drivers/platform/x86/fujitsu-laptop.c C
   1// SPDX-License-Identifier: GPL-2.0-or-later
   2/*-*-linux-c-*-*/
   3
   4/*
   5  Copyright (C) 2007,2008 Jonathan Woithe <jwoithe@just42.net>
   6  Copyright (C) 2008 Peter Gruber <nokos@gmx.net>
   7  Copyright (C) 2008 Tony Vroon <tony@linx.net>
   8  Based on earlier work:
   9    Copyright (C) 2003 Shane Spencer <shane@bogomip.com>
  10    Adrian Yee <brewt-fujitsu@brewt.org>
  11
  12  Templated from msi-laptop.c and thinkpad_acpi.c which is copyright
  13  by its respective authors.
  14
  15 */
  16
  17/*
  18 * fujitsu-laptop.c - Fujitsu laptop support, providing access to additional
  19 * features made available on a range of Fujitsu laptops including the
  20 * P2xxx/P5xxx/S2xxx/S6xxx/S7xxx series.
  21 *
  22 * This driver implements a vendor-specific backlight control interface for
  23 * Fujitsu laptops and provides support for hotkeys present on certain Fujitsu
  24 * laptops.
  25 *
  26 * This driver has been tested on a Fujitsu Lifebook S2110, S6410, S7020 and
  27 * P8010.  It should work on most P-series and S-series Lifebooks, but
  28 * YMMV.
  29 *
  30 * The module parameter use_alt_lcd_levels switches between different ACPI
  31 * brightness controls which are used by different Fujitsu laptops.  In most
  32 * cases the correct method is automatically detected. "use_alt_lcd_levels=1"
  33 * is applicable for a Fujitsu Lifebook S6410 if autodetection fails.
  34 *
  35 */
  36
  37#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
  38
  39#include <linux/module.h>
  40#include <linux/kernel.h>
  41#include <linux/init.h>
  42#include <linux/acpi.h>
  43#include <linux/bitops.h>
  44#include <linux/dmi.h>
  45#include <linux/backlight.h>
  46#include <linux/input.h>
  47#include <linux/input/sparse-keymap.h>
  48#include <linux/kfifo.h>
  49#include <linux/leds.h>
  50#include <linux/platform_device.h>
  51#include <linux/power_supply.h>
  52#include <acpi/battery.h>
  53#include <acpi/video.h>
  54
  55#define FUJITSU_DRIVER_VERSION		"0.6.0"
  56
  57#define FUJITSU_LCD_N_LEVELS		8
  58
  59#define ACPI_FUJITSU_CLASS		"fujitsu"
  60#define ACPI_FUJITSU_BL_HID		"FUJ02B1"
  61#define ACPI_FUJITSU_BL_DRIVER_NAME	"Fujitsu laptop FUJ02B1 ACPI brightness driver"
  62#define ACPI_FUJITSU_BL_DEVICE_NAME	"Fujitsu FUJ02B1"
  63#define ACPI_FUJITSU_LAPTOP_HID		"FUJ02E3"
  64#define ACPI_FUJITSU_LAPTOP_DRIVER_NAME	"Fujitsu laptop FUJ02E3 ACPI hotkeys driver"
  65#define ACPI_FUJITSU_LAPTOP_DEVICE_NAME	"Fujitsu FUJ02E3"
  66
  67#define ACPI_FUJITSU_NOTIFY_CODE	0x80
  68
  69/* FUNC interface - command values */
  70#define FUNC_FLAGS			BIT(12)
  71#define FUNC_LEDS			(BIT(12) | BIT(0))
  72#define FUNC_BUTTONS			(BIT(12) | BIT(1))
  73#define FUNC_BACKLIGHT			(BIT(12) | BIT(2))
  74
  75/* FUNC interface - responses */
  76#define UNSUPPORTED_CMD			0x80000000
  77
  78/* FUNC interface - status flags */
  79#define FLAG_RFKILL			BIT(5)
  80#define FLAG_LID			BIT(8)
  81#define FLAG_DOCK			BIT(9)
  82#define FLAG_TOUCHPAD_TOGGLE		BIT(26)
  83#define FLAG_MICMUTE			BIT(29)
  84#define FLAG_SOFTKEYS			(FLAG_RFKILL | FLAG_TOUCHPAD_TOGGLE | FLAG_MICMUTE)
  85
  86/* FUNC interface - LED control */
  87#define FUNC_LED_OFF			BIT(0)
  88#define FUNC_LED_ON			(BIT(0) | BIT(16) | BIT(17))
  89#define LOGOLAMP_POWERON		BIT(13)
  90#define LOGOLAMP_ALWAYS			BIT(14)
  91#define KEYBOARD_LAMPS			BIT(8)
  92#define RADIO_LED_ON			BIT(5)
  93#define ECO_LED				BIT(16)
  94#define ECO_LED_ON			BIT(19)
  95
  96/* FUNC interface - backlight power control */
  97#define BACKLIGHT_PARAM_POWER		BIT(2)
  98#define BACKLIGHT_OFF			(BIT(0) | BIT(1))
  99#define BACKLIGHT_ON			0
 100
 101/* FUNC interface - battery control interface */
 102#define FUNC_S006_METHOD		0x1006
 103#define CHARGE_CONTROL_RW		0x21
 104
 105/* Scancodes read from the GIRB register */
 106#define KEY1_CODE			0x410
 107#define KEY2_CODE			0x411
 108#define KEY3_CODE			0x412
 109#define KEY4_CODE			0x413
 110#define KEY5_CODE			0x414
 111#define KEY6_CODE			0x415
 112#define KEY7_CODE			0x416
 113#define KEY8_CODE			0x417
 114#define KEY9_CODE			0x420
 115
 116/* Hotkey ringbuffer limits */
 117#define MAX_HOTKEY_RINGBUFFER_SIZE	100
 118#define RINGBUFFERSIZE			40
 119
 120/* Module parameters */
 121static int use_alt_lcd_levels = -1;
 122static bool disable_brightness_adjust;
 123
 124/* Device controlling the backlight and associated keys */
 125struct fujitsu_bl {
 126	struct input_dev *input;
 127	char phys[32];
 128	struct backlight_device *bl_device;
 129	unsigned int max_brightness;
 130	unsigned int brightness_level;
 131};
 132
 133static struct fujitsu_bl *fujitsu_bl;
 134
 135/* Device used to access hotkeys and other features on the laptop */
 136struct fujitsu_laptop {
 137	struct input_dev *input;
 138	char phys[32];
 139	struct platform_device *pf_device;
 140	struct kfifo fifo;
 141	spinlock_t fifo_lock;
 142	int flags_supported;
 143	int flags_state;
 144	bool charge_control_supported;
 145};
 146
 147static struct acpi_device *fext;
 148
 149/* Fujitsu ACPI interface function */
 150
 151static int call_fext_func(struct acpi_device *device,
 152			  int func, int op, int feature, int state)
 153{
 154	union acpi_object params[4] = {
 155		{ .integer.type = ACPI_TYPE_INTEGER, .integer.value = func },
 156		{ .integer.type = ACPI_TYPE_INTEGER, .integer.value = op },
 157		{ .integer.type = ACPI_TYPE_INTEGER, .integer.value = feature },
 158		{ .integer.type = ACPI_TYPE_INTEGER, .integer.value = state }
 159	};
 160	struct acpi_object_list arg_list = { 4, params };
 161	unsigned long long value;
 162	acpi_status status;
 163
 164	status = acpi_evaluate_integer(device->handle, "FUNC", &arg_list,
 165				       &value);
 166	if (ACPI_FAILURE(status)) {
 167		acpi_handle_err(device->handle, "Failed to evaluate FUNC\n");
 168		return -ENODEV;
 169	}
 170
 171	acpi_handle_debug(device->handle,
 172			  "FUNC 0x%x (args 0x%x, 0x%x, 0x%x) returned 0x%x\n",
 173			  func, op, feature, state, (int)value);
 174	return value;
 175}
 176
 177/* Battery charge control code */
 178static ssize_t charge_control_end_threshold_store(struct device *dev,
 179				struct device_attribute *attr,
 180				const char *buf, size_t count)
 181{
 182	int cc_end_value, s006_cc_return;
 183	unsigned int value;
 184	int ret;
 185
 186	ret = kstrtouint(buf, 10, &value);
 187	if (ret)
 188		return ret;
 189
 190	if (value > 100)
 191		return -EINVAL;
 192
 193	if (value < 50)
 194		value = 50;
 195
 196	cc_end_value = value * 0x100 + 0x20;
 197	s006_cc_return = call_fext_func(fext, FUNC_S006_METHOD,
 198					CHARGE_CONTROL_RW, cc_end_value, 0x0);
 199	if (s006_cc_return < 0)
 200		return s006_cc_return;
 201	/*
 202	 * The S006 0x21 method returns 0x00 in case the provided value
 203	 * is invalid.
 204	 */
 205	if (s006_cc_return == 0x00)
 206		return -EINVAL;
 207
 208	return count;
 209}
 210
 211static ssize_t charge_control_end_threshold_show(struct device *dev,
 212				struct device_attribute *attr,
 213				char *buf)
 214{
 215	int status;
 216
 217	status = call_fext_func(fext, FUNC_S006_METHOD,
 218				CHARGE_CONTROL_RW, 0x21, 0x0);
 219	if (status < 0)
 220		return status;
 221
 222	return sysfs_emit(buf, "%d\n", status);
 223}
 224
 225static DEVICE_ATTR_RW(charge_control_end_threshold);
 226
 227/* ACPI battery hook */
 228static int fujitsu_battery_add_hook(struct power_supply *battery,
 229			       struct acpi_battery_hook *hook)
 230{
 231	return device_create_file(&battery->dev,
 232				  &dev_attr_charge_control_end_threshold);
 233}
 234
 235static int fujitsu_battery_remove_hook(struct power_supply *battery,
 236				  struct acpi_battery_hook *hook)
 237{
 238	device_remove_file(&battery->dev,
 239			   &dev_attr_charge_control_end_threshold);
 240
 241	return 0;
 242}
 243
 244static struct acpi_battery_hook battery_hook = {
 245	.add_battery = fujitsu_battery_add_hook,
 246	.remove_battery = fujitsu_battery_remove_hook,
 247	.name = "Fujitsu Battery Extension",
 248};
 249
 250/*
 251 * These functions are intended to be called from acpi_fujitsu_laptop_add and
 252 * acpi_fujitsu_laptop_remove.
 253 */
 254static int fujitsu_battery_charge_control_add(struct acpi_device *device)
 255{
 256	struct fujitsu_laptop *priv = acpi_driver_data(device);
 257	int s006_cc_return;
 258
 259	priv->charge_control_supported = false;
 260	/*
 261	 * Check if the S006 0x21 method exists by trying to get the current
 262	 * battery charge limit.
 263	 */
 264	s006_cc_return = call_fext_func(fext, FUNC_S006_METHOD,
 265					CHARGE_CONTROL_RW, 0x21, 0x0);
 266	if (s006_cc_return < 0)
 267		return s006_cc_return;
 268	if (s006_cc_return == UNSUPPORTED_CMD)
 269		return -ENODEV;
 270
 271	priv->charge_control_supported = true;
 272	battery_hook_register(&battery_hook);
 273
 274	return 0;
 275}
 276
 277static void fujitsu_battery_charge_control_remove(struct acpi_device *device)
 278{
 279	struct fujitsu_laptop *priv = acpi_driver_data(device);
 280
 281	if (priv->charge_control_supported)
 282		battery_hook_unregister(&battery_hook);
 283}
 284
 285/* Hardware access for LCD brightness control */
 286
 287static int set_lcd_level(struct acpi_device *device, int level)
 288{
 289	struct fujitsu_bl *priv = acpi_driver_data(device);
 290	acpi_status status;
 291	char *method;
 292
 293	switch (use_alt_lcd_levels) {
 294	case -1:
 295		if (acpi_has_method(device->handle, "SBL2"))
 296			method = "SBL2";
 297		else
 298			method = "SBLL";
 299		break;
 300	case 1:
 301		method = "SBL2";
 302		break;
 303	default:
 304		method = "SBLL";
 305		break;
 306	}
 307
 308	acpi_handle_debug(device->handle, "set lcd level via %s [%d]\n", method,
 309			  level);
 310
 311	if (level < 0 || level >= priv->max_brightness)
 312		return -EINVAL;
 313
 314	status = acpi_execute_simple_method(device->handle, method, level);
 315	if (ACPI_FAILURE(status)) {
 316		acpi_handle_err(device->handle, "Failed to evaluate %s\n",
 317				method);
 318		return -ENODEV;
 319	}
 320
 321	priv->brightness_level = level;
 322
 323	return 0;
 324}
 325
 326static int get_lcd_level(struct acpi_device *device)
 327{
 328	struct fujitsu_bl *priv = acpi_driver_data(device);
 329	unsigned long long state = 0;
 330	acpi_status status = AE_OK;
 331
 332	acpi_handle_debug(device->handle, "get lcd level via GBLL\n");
 333
 334	status = acpi_evaluate_integer(device->handle, "GBLL", NULL, &state);
 335	if (ACPI_FAILURE(status))
 336		return 0;
 337
 338	priv->brightness_level = state & 0x0fffffff;
 339
 340	return priv->brightness_level;
 341}
 342
 343static int get_max_brightness(struct acpi_device *device)
 344{
 345	struct fujitsu_bl *priv = acpi_driver_data(device);
 346	unsigned long long state = 0;
 347	acpi_status status = AE_OK;
 348
 349	acpi_handle_debug(device->handle, "get max lcd level via RBLL\n");
 350
 351	status = acpi_evaluate_integer(device->handle, "RBLL", NULL, &state);
 352	if (ACPI_FAILURE(status))
 353		return -1;
 354
 355	priv->max_brightness = state;
 356
 357	return priv->max_brightness;
 358}
 359
 360/* Backlight device stuff */
 361
 362static int bl_get_brightness(struct backlight_device *b)
 363{
 364	struct acpi_device *device = bl_get_data(b);
 365
 366	return b->props.power == BACKLIGHT_POWER_OFF ? 0 : get_lcd_level(device);
 367}
 368
 369static int bl_update_status(struct backlight_device *b)
 370{
 371	struct acpi_device *device = bl_get_data(b);
 372
 373	if (fext) {
 374		if (b->props.power == BACKLIGHT_POWER_OFF)
 375			call_fext_func(fext, FUNC_BACKLIGHT, 0x1,
 376				       BACKLIGHT_PARAM_POWER, BACKLIGHT_OFF);
 377		else
 378			call_fext_func(fext, FUNC_BACKLIGHT, 0x1,
 379				       BACKLIGHT_PARAM_POWER, BACKLIGHT_ON);
 380	}
 381
 382	return set_lcd_level(device, b->props.brightness);
 383}
 384
 385static const struct backlight_ops fujitsu_bl_ops = {
 386	.get_brightness = bl_get_brightness,
 387	.update_status = bl_update_status,
 388};
 389
 390static ssize_t lid_show(struct device *dev, struct device_attribute *attr,
 391			char *buf)
 392{
 393	struct fujitsu_laptop *priv = dev_get_drvdata(dev);
 394
 395	if (!(priv->flags_supported & FLAG_LID))
 396		return sysfs_emit(buf, "unknown\n");
 397	if (priv->flags_state & FLAG_LID)
 398		return sysfs_emit(buf, "open\n");
 399	else
 400		return sysfs_emit(buf, "closed\n");
 401}
 402
 403static ssize_t dock_show(struct device *dev, struct device_attribute *attr,
 404			 char *buf)
 405{
 406	struct fujitsu_laptop *priv = dev_get_drvdata(dev);
 407
 408	if (!(priv->flags_supported & FLAG_DOCK))
 409		return sysfs_emit(buf, "unknown\n");
 410	if (priv->flags_state & FLAG_DOCK)
 411		return sysfs_emit(buf, "docked\n");
 412	else
 413		return sysfs_emit(buf, "undocked\n");
 414}
 415
 416static ssize_t radios_show(struct device *dev, struct device_attribute *attr,
 417			   char *buf)
 418{
 419	struct fujitsu_laptop *priv = dev_get_drvdata(dev);
 420
 421	if (!(priv->flags_supported & FLAG_RFKILL))
 422		return sysfs_emit(buf, "unknown\n");
 423	if (priv->flags_state & FLAG_RFKILL)
 424		return sysfs_emit(buf, "on\n");
 425	else
 426		return sysfs_emit(buf, "killed\n");
 427}
 428
 429static DEVICE_ATTR_RO(lid);
 430static DEVICE_ATTR_RO(dock);
 431static DEVICE_ATTR_RO(radios);
 432
 433static struct attribute *fujitsu_pf_attributes[] = {
 434	&dev_attr_lid.attr,
 435	&dev_attr_dock.attr,
 436	&dev_attr_radios.attr,
 437	NULL
 438};
 439
 440static const struct attribute_group fujitsu_pf_attribute_group = {
 441	.attrs = fujitsu_pf_attributes
 442};
 443
 444static struct platform_driver fujitsu_pf_driver = {
 445	.driver = {
 446		   .name = "fujitsu-laptop",
 447		   }
 448};
 449
 450/* ACPI device for LCD brightness control */
 451
 452static const struct key_entry keymap_backlight[] = {
 453	{ KE_KEY, true, { KEY_BRIGHTNESSUP } },
 454	{ KE_KEY, false, { KEY_BRIGHTNESSDOWN } },
 455	{ KE_END, 0 }
 456};
 457
 458static int acpi_fujitsu_bl_input_setup(struct acpi_device *device)
 459{
 460	struct fujitsu_bl *priv = acpi_driver_data(device);
 461	int ret;
 462
 463	priv->input = devm_input_allocate_device(&device->dev);
 464	if (!priv->input)
 465		return -ENOMEM;
 466
 467	snprintf(priv->phys, sizeof(priv->phys), "%s/video/input0",
 468		 acpi_device_hid(device));
 469
 470	priv->input->name = acpi_device_name(device);
 471	priv->input->phys = priv->phys;
 472	priv->input->id.bustype = BUS_HOST;
 473	priv->input->id.product = 0x06;
 474
 475	ret = sparse_keymap_setup(priv->input, keymap_backlight, NULL);
 476	if (ret)
 477		return ret;
 478
 479	return input_register_device(priv->input);
 480}
 481
 482static int fujitsu_backlight_register(struct acpi_device *device)
 483{
 484	struct fujitsu_bl *priv = acpi_driver_data(device);
 485	const struct backlight_properties props = {
 486		.brightness = priv->brightness_level,
 487		.max_brightness = priv->max_brightness - 1,
 488		.type = BACKLIGHT_PLATFORM
 489	};
 490	struct backlight_device *bd;
 491
 492	bd = devm_backlight_device_register(&device->dev, "fujitsu-laptop",
 493					    &device->dev, device,
 494					    &fujitsu_bl_ops, &props);
 495	if (IS_ERR(bd))
 496		return PTR_ERR(bd);
 497
 498	priv->bl_device = bd;
 499
 500	return 0;
 501}
 502
 503static int acpi_fujitsu_bl_add(struct acpi_device *device)
 504{
 505	struct fujitsu_bl *priv;
 506	int ret;
 507
 508	if (acpi_video_get_backlight_type() != acpi_backlight_vendor)
 509		return -ENODEV;
 510
 511	priv = devm_kzalloc(&device->dev, sizeof(*priv), GFP_KERNEL);
 512	if (!priv)
 513		return -ENOMEM;
 514
 515	fujitsu_bl = priv;
 516	strscpy(acpi_device_name(device), ACPI_FUJITSU_BL_DEVICE_NAME);
 517	strscpy(acpi_device_class(device), ACPI_FUJITSU_CLASS);
 518	device->driver_data = priv;
 519
 520	pr_info("ACPI: %s [%s]\n",
 521		acpi_device_name(device), acpi_device_bid(device));
 522
 523	if (get_max_brightness(device) <= 0)
 524		priv->max_brightness = FUJITSU_LCD_N_LEVELS;
 525	get_lcd_level(device);
 526
 527	ret = acpi_fujitsu_bl_input_setup(device);
 528	if (ret)
 529		return ret;
 530
 531	return fujitsu_backlight_register(device);
 532}
 533
 534/* Brightness notify */
 535
 536static void acpi_fujitsu_bl_notify(struct acpi_device *device, u32 event)
 537{
 538	struct fujitsu_bl *priv = acpi_driver_data(device);
 539	int oldb, newb;
 540
 541	if (event != ACPI_FUJITSU_NOTIFY_CODE) {
 542		acpi_handle_info(device->handle, "unsupported event [0x%x]\n",
 543				 event);
 544		sparse_keymap_report_event(priv->input, -1, 1, true);
 545		return;
 546	}
 547
 548	oldb = priv->brightness_level;
 549	get_lcd_level(device);
 550	newb = priv->brightness_level;
 551
 552	acpi_handle_debug(device->handle,
 553			  "brightness button event [%i -> %i]\n", oldb, newb);
 554
 555	if (oldb == newb)
 556		return;
 557
 558	if (!disable_brightness_adjust)
 559		set_lcd_level(device, newb);
 560
 561	sparse_keymap_report_event(priv->input, oldb < newb, 1, true);
 562}
 563
 564/* ACPI device for hotkey handling */
 565
 566static const struct key_entry keymap_default[] = {
 567	{ KE_KEY, KEY1_CODE,            { KEY_PROG1 } },
 568	{ KE_KEY, KEY2_CODE,            { KEY_PROG2 } },
 569	{ KE_KEY, KEY3_CODE,            { KEY_PROG3 } },
 570	{ KE_KEY, KEY4_CODE,            { KEY_PROG4 } },
 571	{ KE_KEY, KEY9_CODE,            { KEY_RFKILL } },
 572	/* Soft keys read from status flags */
 573	{ KE_KEY, FLAG_RFKILL,          { KEY_RFKILL } },
 574	{ KE_KEY, FLAG_TOUCHPAD_TOGGLE, { KEY_TOUCHPAD_TOGGLE } },
 575	{ KE_KEY, FLAG_MICMUTE,         { KEY_MICMUTE } },
 576	{ KE_END, 0 }
 577};
 578
 579static const struct key_entry keymap_s64x0[] = {
 580	{ KE_KEY, KEY1_CODE, { KEY_SCREENLOCK } },	/* "Lock" */
 581	{ KE_KEY, KEY2_CODE, { KEY_HELP } },		/* "Mobility Center */
 582	{ KE_KEY, KEY3_CODE, { KEY_PROG3 } },
 583	{ KE_KEY, KEY4_CODE, { KEY_PROG4 } },
 584	{ KE_END, 0 }
 585};
 586
 587static const struct key_entry keymap_p8010[] = {
 588	{ KE_KEY, KEY1_CODE, { KEY_HELP } },		/* "Support" */
 589	{ KE_KEY, KEY2_CODE, { KEY_PROG2 } },
 590	{ KE_KEY, KEY3_CODE, { KEY_SWITCHVIDEOMODE } },	/* "Presentation" */
 591	{ KE_KEY, KEY4_CODE, { KEY_WWW } },		/* "WWW" */
 592	{ KE_END, 0 }
 593};
 594
 595static const struct key_entry keymap_s2110[] = {
 596	{ KE_KEY, KEY1_CODE, { KEY_PROG1 } }, /* "A" */
 597	{ KE_KEY, KEY2_CODE, { KEY_PROG2 } }, /* "B" */
 598	{ KE_KEY, KEY3_CODE, { KEY_WWW } },   /* "Internet" */
 599	{ KE_KEY, KEY4_CODE, { KEY_EMAIL } }, /* "E-mail" */
 600	{ KE_KEY, KEY5_CODE, { KEY_STOPCD } },
 601	{ KE_KEY, KEY6_CODE, { KEY_PLAYPAUSE } },
 602	{ KE_KEY, KEY7_CODE, { KEY_PREVIOUSSONG } },
 603	{ KE_KEY, KEY8_CODE, { KEY_NEXTSONG } },
 604	{ KE_END, 0 }
 605};
 606
 607static const struct key_entry *keymap = keymap_default;
 608
 609static int fujitsu_laptop_dmi_keymap_override(const struct dmi_system_id *id)
 610{
 611	pr_info("Identified laptop model '%s'\n", id->ident);
 612	keymap = id->driver_data;
 613	return 1;
 614}
 615
 616static const struct dmi_system_id fujitsu_laptop_dmi_table[] = {
 617	{
 618		.callback = fujitsu_laptop_dmi_keymap_override,
 619		.ident = "Fujitsu Siemens S6410",
 620		.matches = {
 621			DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU SIEMENS"),
 622			DMI_MATCH(DMI_PRODUCT_NAME, "LIFEBOOK S6410"),
 623		},
 624		.driver_data = (void *)keymap_s64x0
 625	},
 626	{
 627		.callback = fujitsu_laptop_dmi_keymap_override,
 628		.ident = "Fujitsu Siemens S6420",
 629		.matches = {
 630			DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU SIEMENS"),
 631			DMI_MATCH(DMI_PRODUCT_NAME, "LIFEBOOK S6420"),
 632		},
 633		.driver_data = (void *)keymap_s64x0
 634	},
 635	{
 636		.callback = fujitsu_laptop_dmi_keymap_override,
 637		.ident = "Fujitsu LifeBook P8010",
 638		.matches = {
 639			DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU"),
 640			DMI_MATCH(DMI_PRODUCT_NAME, "LifeBook P8010"),
 641		},
 642		.driver_data = (void *)keymap_p8010
 643	},
 644	{
 645		.callback = fujitsu_laptop_dmi_keymap_override,
 646		.ident = "Fujitsu LifeBook S2110",
 647		.matches = {
 648			DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU SIEMENS"),
 649			DMI_MATCH(DMI_PRODUCT_NAME, "LIFEBOOK S2110"),
 650		},
 651		.driver_data = (void *)keymap_s2110
 652	},
 653	{}
 654};
 655
 656static int acpi_fujitsu_laptop_input_setup(struct acpi_device *device)
 657{
 658	struct fujitsu_laptop *priv = acpi_driver_data(device);
 659	int ret;
 660
 661	priv->input = devm_input_allocate_device(&device->dev);
 662	if (!priv->input)
 663		return -ENOMEM;
 664
 665	snprintf(priv->phys, sizeof(priv->phys), "%s/input0",
 666		 acpi_device_hid(device));
 667
 668	priv->input->name = acpi_device_name(device);
 669	priv->input->phys = priv->phys;
 670	priv->input->id.bustype = BUS_HOST;
 671
 672	dmi_check_system(fujitsu_laptop_dmi_table);
 673	ret = sparse_keymap_setup(priv->input, keymap, NULL);
 674	if (ret)
 675		return ret;
 676
 677	return input_register_device(priv->input);
 678}
 679
 680static int fujitsu_laptop_platform_add(struct acpi_device *device)
 681{
 682	struct fujitsu_laptop *priv = acpi_driver_data(device);
 683	int ret;
 684
 685	priv->pf_device = platform_device_alloc("fujitsu-laptop", PLATFORM_DEVID_NONE);
 686	if (!priv->pf_device)
 687		return -ENOMEM;
 688
 689	platform_set_drvdata(priv->pf_device, priv);
 690
 691	ret = platform_device_add(priv->pf_device);
 692	if (ret)
 693		goto err_put_platform_device;
 694
 695	ret = sysfs_create_group(&priv->pf_device->dev.kobj,
 696				 &fujitsu_pf_attribute_group);
 697	if (ret)
 698		goto err_del_platform_device;
 699
 700	return 0;
 701
 702err_del_platform_device:
 703	platform_device_del(priv->pf_device);
 704err_put_platform_device:
 705	platform_device_put(priv->pf_device);
 706
 707	return ret;
 708}
 709
 710static void fujitsu_laptop_platform_remove(struct acpi_device *device)
 711{
 712	struct fujitsu_laptop *priv = acpi_driver_data(device);
 713
 714	sysfs_remove_group(&priv->pf_device->dev.kobj,
 715			   &fujitsu_pf_attribute_group);
 716	platform_device_unregister(priv->pf_device);
 717}
 718
 719static int logolamp_set(struct led_classdev *cdev,
 720			enum led_brightness brightness)
 721{
 722	struct acpi_device *device = to_acpi_device(cdev->dev->parent);
 723	int poweron = FUNC_LED_ON, always = FUNC_LED_ON;
 724	int ret;
 725
 726	if (brightness < LED_HALF)
 727		poweron = FUNC_LED_OFF;
 728
 729	if (brightness < LED_FULL)
 730		always = FUNC_LED_OFF;
 731
 732	ret = call_fext_func(device, FUNC_LEDS, 0x1, LOGOLAMP_POWERON, poweron);
 733	if (ret < 0)
 734		return ret;
 735
 736	return call_fext_func(device, FUNC_LEDS, 0x1, LOGOLAMP_ALWAYS, always);
 737}
 738
 739static enum led_brightness logolamp_get(struct led_classdev *cdev)
 740{
 741	struct acpi_device *device = to_acpi_device(cdev->dev->parent);
 742	int ret;
 743
 744	ret = call_fext_func(device, FUNC_LEDS, 0x2, LOGOLAMP_ALWAYS, 0x0);
 745	if (ret == FUNC_LED_ON)
 746		return LED_FULL;
 747
 748	ret = call_fext_func(device, FUNC_LEDS, 0x2, LOGOLAMP_POWERON, 0x0);
 749	if (ret == FUNC_LED_ON)
 750		return LED_HALF;
 751
 752	return LED_OFF;
 753}
 754
 755static int kblamps_set(struct led_classdev *cdev,
 756		       enum led_brightness brightness)
 757{
 758	struct acpi_device *device = to_acpi_device(cdev->dev->parent);
 759
 760	if (brightness >= LED_FULL)
 761		return call_fext_func(device, FUNC_LEDS, 0x1, KEYBOARD_LAMPS,
 762				      FUNC_LED_ON);
 763	else
 764		return call_fext_func(device, FUNC_LEDS, 0x1, KEYBOARD_LAMPS,
 765				      FUNC_LED_OFF);
 766}
 767
 768static enum led_brightness kblamps_get(struct led_classdev *cdev)
 769{
 770	struct acpi_device *device = to_acpi_device(cdev->dev->parent);
 771	enum led_brightness brightness = LED_OFF;
 772
 773	if (call_fext_func(device,
 774			   FUNC_LEDS, 0x2, KEYBOARD_LAMPS, 0x0) == FUNC_LED_ON)
 775		brightness = LED_FULL;
 776
 777	return brightness;
 778}
 779
 780static int radio_led_set(struct led_classdev *cdev,
 781			 enum led_brightness brightness)
 782{
 783	struct acpi_device *device = to_acpi_device(cdev->dev->parent);
 784
 785	if (brightness >= LED_FULL)
 786		return call_fext_func(device, FUNC_FLAGS, 0x5, RADIO_LED_ON,
 787				      RADIO_LED_ON);
 788	else
 789		return call_fext_func(device, FUNC_FLAGS, 0x5, RADIO_LED_ON,
 790				      0x0);
 791}
 792
 793static enum led_brightness radio_led_get(struct led_classdev *cdev)
 794{
 795	struct acpi_device *device = to_acpi_device(cdev->dev->parent);
 796	enum led_brightness brightness = LED_OFF;
 797
 798	if (call_fext_func(device, FUNC_FLAGS, 0x4, 0x0, 0x0) & RADIO_LED_ON)
 799		brightness = LED_FULL;
 800
 801	return brightness;
 802}
 803
 804static int eco_led_set(struct led_classdev *cdev,
 805		       enum led_brightness brightness)
 806{
 807	struct acpi_device *device = to_acpi_device(cdev->dev->parent);
 808	int curr;
 809
 810	curr = call_fext_func(device, FUNC_LEDS, 0x2, ECO_LED, 0x0);
 811	if (brightness >= LED_FULL)
 812		return call_fext_func(device, FUNC_LEDS, 0x1, ECO_LED,
 813				      curr | ECO_LED_ON);
 814	else
 815		return call_fext_func(device, FUNC_LEDS, 0x1, ECO_LED,
 816				      curr & ~ECO_LED_ON);
 817}
 818
 819static enum led_brightness eco_led_get(struct led_classdev *cdev)
 820{
 821	struct acpi_device *device = to_acpi_device(cdev->dev->parent);
 822	enum led_brightness brightness = LED_OFF;
 823
 824	if (call_fext_func(device, FUNC_LEDS, 0x2, ECO_LED, 0x0) & ECO_LED_ON)
 825		brightness = LED_FULL;
 826
 827	return brightness;
 828}
 829
 830static int acpi_fujitsu_laptop_leds_register(struct acpi_device *device)
 831{
 832	struct fujitsu_laptop *priv = acpi_driver_data(device);
 833	struct led_classdev *led;
 834	int ret;
 835
 836	if (call_fext_func(device,
 837			   FUNC_LEDS, 0x0, 0x0, 0x0) & LOGOLAMP_POWERON) {
 838		led = devm_kzalloc(&device->dev, sizeof(*led), GFP_KERNEL);
 839		if (!led)
 840			return -ENOMEM;
 841
 842		led->name = "fujitsu::logolamp";
 843		led->brightness_set_blocking = logolamp_set;
 844		led->brightness_get = logolamp_get;
 845		ret = devm_led_classdev_register(&device->dev, led);
 846		if (ret)
 847			return ret;
 848	}
 849
 850	if ((call_fext_func(device,
 851			    FUNC_LEDS, 0x0, 0x0, 0x0) & KEYBOARD_LAMPS) &&
 852	    (call_fext_func(device, FUNC_BUTTONS, 0x0, 0x0, 0x0) == 0x0)) {
 853		led = devm_kzalloc(&device->dev, sizeof(*led), GFP_KERNEL);
 854		if (!led)
 855			return -ENOMEM;
 856
 857		led->name = "fujitsu::kblamps";
 858		led->brightness_set_blocking = kblamps_set;
 859		led->brightness_get = kblamps_get;
 860		ret = devm_led_classdev_register(&device->dev, led);
 861		if (ret)
 862			return ret;
 863	}
 864
 865	/*
 866	 * Some Fujitsu laptops have a radio toggle button in place of a slide
 867	 * switch and all such machines appear to also have an RF LED.  Based on
 868	 * comparing DSDT tables of four Fujitsu Lifebook models (E744, E751,
 869	 * S7110, S8420; the first one has a radio toggle button, the other
 870	 * three have slide switches), bit 17 of flags_supported (the value
 871	 * returned by method S000 of ACPI device FUJ02E3) seems to indicate
 872	 * whether given model has a radio toggle button.
 873	 */
 874	if (priv->flags_supported & BIT(17)) {
 875		led = devm_kzalloc(&device->dev, sizeof(*led), GFP_KERNEL);
 876		if (!led)
 877			return -ENOMEM;
 878
 879		led->name = "fujitsu::radio_led";
 880		led->brightness_set_blocking = radio_led_set;
 881		led->brightness_get = radio_led_get;
 882		led->default_trigger = "rfkill-any";
 883		ret = devm_led_classdev_register(&device->dev, led);
 884		if (ret)
 885			return ret;
 886	}
 887
 888	/* Support for eco led is not always signaled in bit corresponding
 889	 * to the bit used to control the led. According to the DSDT table,
 890	 * bit 14 seems to indicate presence of said led as well.
 891	 * Confirm by testing the status.
 892	 */
 893	if ((call_fext_func(device, FUNC_LEDS, 0x0, 0x0, 0x0) & BIT(14)) &&
 894	    (call_fext_func(device,
 895			    FUNC_LEDS, 0x2, ECO_LED, 0x0) != UNSUPPORTED_CMD)) {
 896		led = devm_kzalloc(&device->dev, sizeof(*led), GFP_KERNEL);
 897		if (!led)
 898			return -ENOMEM;
 899
 900		led->name = "fujitsu::eco_led";
 901		led->brightness_set_blocking = eco_led_set;
 902		led->brightness_get = eco_led_get;
 903		ret = devm_led_classdev_register(&device->dev, led);
 904		if (ret)
 905			return ret;
 906	}
 907
 908	return 0;
 909}
 910
 911static int acpi_fujitsu_laptop_add(struct acpi_device *device)
 912{
 913	struct fujitsu_laptop *priv;
 914	int ret, i = 0;
 915
 916	priv = devm_kzalloc(&device->dev, sizeof(*priv), GFP_KERNEL);
 917	if (!priv)
 918		return -ENOMEM;
 919
 920	WARN_ONCE(fext, "More than one FUJ02E3 ACPI device was found.  Driver may not work as intended.");
 921	fext = device;
 922
 923	strscpy(acpi_device_name(device), ACPI_FUJITSU_LAPTOP_DEVICE_NAME);
 924	strscpy(acpi_device_class(device), ACPI_FUJITSU_CLASS);
 925	device->driver_data = priv;
 926
 927	/* kfifo */
 928	spin_lock_init(&priv->fifo_lock);
 929	ret = kfifo_alloc(&priv->fifo, RINGBUFFERSIZE * sizeof(int),
 930			  GFP_KERNEL);
 931	if (ret)
 932		return ret;
 933
 934	pr_info("ACPI: %s [%s]\n",
 935		acpi_device_name(device), acpi_device_bid(device));
 936
 937	while (call_fext_func(device, FUNC_BUTTONS, 0x1, 0x0, 0x0) != 0 &&
 938	       i++ < MAX_HOTKEY_RINGBUFFER_SIZE)
 939		; /* No action, result is discarded */
 940	acpi_handle_debug(device->handle, "Discarded %i ringbuffer entries\n",
 941			  i);
 942
 943	priv->flags_supported = call_fext_func(device, FUNC_FLAGS, 0x0, 0x0,
 944					       0x0);
 945
 946	/* Make sure our bitmask of supported functions is cleared if the
 947	   RFKILL function block is not implemented, like on the S7020. */
 948	if (priv->flags_supported == UNSUPPORTED_CMD)
 949		priv->flags_supported = 0;
 950
 951	if (priv->flags_supported)
 952		priv->flags_state = call_fext_func(device, FUNC_FLAGS, 0x4, 0x0,
 953						   0x0);
 954
 955	/* Suspect this is a keymap of the application panel, print it */
 956	acpi_handle_info(device->handle, "BTNI: [0x%x]\n",
 957			 call_fext_func(device, FUNC_BUTTONS, 0x0, 0x0, 0x0));
 958
 959	/* Sync backlight power status */
 960	if (fujitsu_bl && fujitsu_bl->bl_device &&
 961	    acpi_video_get_backlight_type() == acpi_backlight_vendor) {
 962		if (call_fext_func(fext, FUNC_BACKLIGHT, 0x2,
 963				   BACKLIGHT_PARAM_POWER, 0x0) == BACKLIGHT_OFF)
 964			fujitsu_bl->bl_device->props.power = BACKLIGHT_POWER_OFF;
 965		else
 966			fujitsu_bl->bl_device->props.power = BACKLIGHT_POWER_ON;
 967	}
 968
 969	ret = acpi_fujitsu_laptop_input_setup(device);
 970	if (ret)
 971		goto err_free_fifo;
 972
 973	ret = acpi_fujitsu_laptop_leds_register(device);
 974	if (ret)
 975		goto err_free_fifo;
 976
 977	ret = fujitsu_laptop_platform_add(device);
 978	if (ret)
 979		goto err_free_fifo;
 980
 981	ret = fujitsu_battery_charge_control_add(device);
 982	if (ret < 0)
 983		pr_warn("Unable to register battery charge control: %d\n", ret);
 984
 985	return 0;
 986
 987err_free_fifo:
 988	kfifo_free(&priv->fifo);
 989
 990	return ret;
 991}
 992
 993static void acpi_fujitsu_laptop_remove(struct acpi_device *device)
 994{
 995	struct fujitsu_laptop *priv = acpi_driver_data(device);
 996
 997	fujitsu_battery_charge_control_remove(device);
 998
 999	fujitsu_laptop_platform_remove(device);
1000
1001	kfifo_free(&priv->fifo);
1002}
1003
1004static void acpi_fujitsu_laptop_press(struct acpi_device *device, int scancode)
1005{
1006	struct fujitsu_laptop *priv = acpi_driver_data(device);
1007	int ret;
1008
1009	ret = kfifo_in_locked(&priv->fifo, (unsigned char *)&scancode,
1010			      sizeof(scancode), &priv->fifo_lock);
1011	if (ret != sizeof(scancode)) {
1012		dev_info(&priv->input->dev, "Could not push scancode [0x%x]\n",
1013			 scancode);
1014		return;
1015	}
1016	sparse_keymap_report_event(priv->input, scancode, 1, false);
1017	dev_dbg(&priv->input->dev, "Push scancode into ringbuffer [0x%x]\n",
1018		scancode);
1019}
1020
1021static void acpi_fujitsu_laptop_release(struct acpi_device *device)
1022{
1023	struct fujitsu_laptop *priv = acpi_driver_data(device);
1024	int scancode, ret;
1025
1026	while (true) {
1027		ret = kfifo_out_locked(&priv->fifo, (unsigned char *)&scancode,
1028				       sizeof(scancode), &priv->fifo_lock);
1029		if (ret != sizeof(scancode))
1030			return;
1031		sparse_keymap_report_event(priv->input, scancode, 0, false);
1032		dev_dbg(&priv->input->dev,
1033			"Pop scancode from ringbuffer [0x%x]\n", scancode);
1034	}
1035}
1036
1037static void acpi_fujitsu_laptop_notify(struct acpi_device *device, u32 event)
1038{
1039	struct fujitsu_laptop *priv = acpi_driver_data(device);
1040	unsigned long flags;
1041	int scancode, i = 0;
1042	unsigned int irb;
1043
1044	if (event != ACPI_FUJITSU_NOTIFY_CODE) {
1045		acpi_handle_info(device->handle, "Unsupported event [0x%x]\n",
1046				 event);
1047		sparse_keymap_report_event(priv->input, -1, 1, true);
1048		return;
1049	}
1050
1051	if (priv->flags_supported)
1052		priv->flags_state = call_fext_func(device, FUNC_FLAGS, 0x4, 0x0,
1053						   0x0);
1054
1055	while ((irb = call_fext_func(device,
1056				     FUNC_BUTTONS, 0x1, 0x0, 0x0)) != 0 &&
1057	       i++ < MAX_HOTKEY_RINGBUFFER_SIZE) {
1058		scancode = irb & 0x4ff;
1059		if (sparse_keymap_entry_from_scancode(priv->input, scancode))
1060			acpi_fujitsu_laptop_press(device, scancode);
1061		else if (scancode == 0)
1062			acpi_fujitsu_laptop_release(device);
1063		else
1064			acpi_handle_info(device->handle,
1065					 "Unknown GIRB result [%x]\n", irb);
1066	}
1067
1068	/*
1069	 * First seen on the Skylake-based Lifebook E736/E746/E756), the
1070	 * touchpad toggle hotkey (Fn+F4) is handled in software. Other models
1071	 * have since added additional "soft keys". These are reported in the
1072	 * status flags queried using FUNC_FLAGS.
1073	 */
1074	if (priv->flags_supported & (FLAG_SOFTKEYS)) {
1075		flags = call_fext_func(device, FUNC_FLAGS, 0x1, 0x0, 0x0);
1076		flags &= (FLAG_SOFTKEYS);
1077		for_each_set_bit(i, &flags, BITS_PER_LONG)
1078			sparse_keymap_report_event(priv->input, BIT(i), 1, true);
1079	}
1080}
1081
1082/* Initialization */
1083
1084static const struct acpi_device_id fujitsu_bl_device_ids[] = {
1085	{ACPI_FUJITSU_BL_HID, 0},
1086	{"", 0},
1087};
1088
1089static struct acpi_driver acpi_fujitsu_bl_driver = {
1090	.name = ACPI_FUJITSU_BL_DRIVER_NAME,
1091	.class = ACPI_FUJITSU_CLASS,
1092	.ids = fujitsu_bl_device_ids,
1093	.ops = {
1094		.add = acpi_fujitsu_bl_add,
1095		.notify = acpi_fujitsu_bl_notify,
1096		},
1097};
1098
1099static const struct acpi_device_id fujitsu_laptop_device_ids[] = {
1100	{ACPI_FUJITSU_LAPTOP_HID, 0},
1101	{"", 0},
1102};
1103
1104static struct acpi_driver acpi_fujitsu_laptop_driver = {
1105	.name = ACPI_FUJITSU_LAPTOP_DRIVER_NAME,
1106	.class = ACPI_FUJITSU_CLASS,
1107	.ids = fujitsu_laptop_device_ids,
1108	.ops = {
1109		.add = acpi_fujitsu_laptop_add,
1110		.remove = acpi_fujitsu_laptop_remove,
1111		.notify = acpi_fujitsu_laptop_notify,
1112		},
1113};
1114
1115static const struct acpi_device_id fujitsu_ids[] __used = {
1116	{ACPI_FUJITSU_BL_HID, 0},
1117	{ACPI_FUJITSU_LAPTOP_HID, 0},
1118	{"", 0}
1119};
1120MODULE_DEVICE_TABLE(acpi, fujitsu_ids);
1121
1122static int __init fujitsu_init(void)
1123{
1124	int ret;
1125
1126	ret = acpi_bus_register_driver(&acpi_fujitsu_bl_driver);
1127	if (ret)
1128		return ret;
1129
1130	/* Register platform stuff */
1131
1132	ret = platform_driver_register(&fujitsu_pf_driver);
1133	if (ret)
1134		goto err_unregister_acpi;
1135
1136	/* Register laptop driver */
1137
1138	ret = acpi_bus_register_driver(&acpi_fujitsu_laptop_driver);
1139	if (ret)
1140		goto err_unregister_platform_driver;
1141
1142	pr_info("driver " FUJITSU_DRIVER_VERSION " successfully loaded\n");
1143
1144	return 0;
1145
1146err_unregister_platform_driver:
1147	platform_driver_unregister(&fujitsu_pf_driver);
1148err_unregister_acpi:
1149	acpi_bus_unregister_driver(&acpi_fujitsu_bl_driver);
1150
1151	return ret;
1152}
1153
1154static void __exit fujitsu_cleanup(void)
1155{
1156	acpi_bus_unregister_driver(&acpi_fujitsu_laptop_driver);
1157
1158	platform_driver_unregister(&fujitsu_pf_driver);
1159
1160	acpi_bus_unregister_driver(&acpi_fujitsu_bl_driver);
1161
1162	pr_info("driver unloaded\n");
1163}
1164
1165module_init(fujitsu_init);
1166module_exit(fujitsu_cleanup);
1167
1168module_param(use_alt_lcd_levels, int, 0644);
1169MODULE_PARM_DESC(use_alt_lcd_levels, "Interface used for setting LCD brightness level (-1 = auto, 0 = force SBLL, 1 = force SBL2)");
1170module_param(disable_brightness_adjust, bool, 0644);
1171MODULE_PARM_DESC(disable_brightness_adjust, "Disable LCD brightness adjustment");
1172
1173MODULE_AUTHOR("Jonathan Woithe, Peter Gruber, Tony Vroon");
1174MODULE_DESCRIPTION("Fujitsu laptop extras support");
1175MODULE_VERSION(FUJITSU_DRIVER_VERSION);
1176MODULE_LICENSE("GPL");
~/ Read-only file preview.