// SPDX-License-Identifier: GPL-2.0-or-later /* * ACPI Sony Notebook Control Driver (SNC and SPIC) * * Copyright (C) 2004-2005 Stelian Pop <stelian@popies.net> * Copyright (C) 2007-2009 Mattia Dongili <malattia@linux.it> * * Parts of this driver inspired from asus_acpi.c and ibm_acpi.c * which are copyrighted by their respective authors. * * The SNY6001 driver part is based on the sonypi driver which includes * material from: * * Copyright (C) 2001-2005 Stelian Pop <stelian@popies.net> * * Copyright (C) 2005 Narayanan R S <nars@kadamba.org> * * Copyright (C) 2001-2002 Alcôve <www.alcove.com> * * Copyright (C) 2001 Michael Ashley <m.ashley@unsw.edu.au> * * Copyright (C) 2001 Junichi Morita <jun1m@mars.dti.ne.jp> * * Copyright (C) 2000 Takaya Kinjo <t-kinjo@tc4.so-net.ne.jp> * * Copyright (C) 2000 Andrew Tridgell <tridge@valinux.com> * * Earlier work by Werner Almesberger, Paul `Rusty' Russell and Paul Mackerras.
*/
staticint debug;
module_param(debug, int, 0);
MODULE_PARM_DESC(debug, "set this to 1 (and RTFM) if you want to help " "the development of this driver");
staticint no_spic; /* = 0 */
module_param(no_spic, int, 0444);
MODULE_PARM_DESC(no_spic, "set this if you don't want to enable the SPIC device");
staticint compat; /* = 0 */
module_param(compat, int, 0444);
MODULE_PARM_DESC(compat, "set this if you want to enable backward compatibility mode");
staticunsignedlong mask = 0xffffffff;
module_param(mask, ulong, 0644);
MODULE_PARM_DESC(mask, "set this to the mask of event you want to enable (see doc)");
staticint camera; /* = 0 */
module_param(camera, int, 0444);
MODULE_PARM_DESC(camera, "set this to 1 to enable Motion Eye camera controls " "(only use it if you have a C1VE or C1VN model)");
#ifdef CONFIG_SONYPI_COMPAT staticint minor = -1;
module_param(minor, int, 0);
MODULE_PARM_DESC(minor, "minor number of the misc device for the SPIC compatibility code, " "default is -1 (automatic)"); #endif
staticint kbd_backlight = -1;
module_param(kbd_backlight, int, 0444);
MODULE_PARM_DESC(kbd_backlight, "set this to 0 to disable keyboard backlight, " "1 to enable it with automatic control and 2 to have it always " "on (default: no change from current value)");
staticint kbd_backlight_timeout = -1;
module_param(kbd_backlight_timeout, int, 0444);
MODULE_PARM_DESC(kbd_backlight_timeout, "meaningful values vary from 0 to 3 and their meaning depends " "on the model (default: no change from current value)");
/* release buttons after a short delay if pressed */ staticvoid do_sony_laptop_release_key(struct timer_list *unused)
{ struct sony_laptop_keypress kp; unsignedlong flags;
/* If there is something in the fifo schedule next release. */ if (kfifo_len(&sony_laptop_input.fifo) != 0)
mod_timer(&sony_laptop_input.release_key_timer,
jiffies + msecs_to_jiffies(10));
default: if (event >= ARRAY_SIZE(sony_laptop_input_index)) {
dprintk("sony_laptop_report_input_event, event not known: %d\n", event); break;
} if ((scancode = sony_laptop_input_index[event]) != -1) {
kp.key = sony_laptop_input_keycode_map[scancode]; if (kp.key != KEY_UNKNOWN)
kp.dev = key_dev;
} break;
}
if (kp.dev) { /* if we have a scancode we emit it so we can always
remap the key */ if (scancode != -1)
input_event(kp.dev, EV_MSC, MSC_SCAN, scancode);
input_report_key(kp.dev, kp.key, 1);
input_sync(kp.dev);
/* * Generate key-up events for remaining keys. Note that we don't * need locking since nobody is adding new events to the kfifo.
*/ while (kfifo_out(&sony_laptop_input.fifo,
(unsignedchar *)&kp, sizeof(kp)) == sizeof(kp)) {
input_report_key(kp.dev, kp.key, 0);
input_sync(kp.dev);
}
struct sony_nc_value { char *name; /* name of the entry */ char **acpiget; /* names of the ACPI get function */ char **acpiset; /* names of the ACPI set function */ int (*validate)(constint, constint); /* input/output validation */ int value; /* current setting */ int valid; /* Has ever been set */ int debug; /* active only in debug mode ? */ struct device_attribute devattr; /* sysfs attribute */
};
static ssize_t sony_nc_handles_show(struct device *dev, struct device_attribute *attr, char *buffer)
{
ssize_t len = 0; int i;
for (i = 0; i < ARRAY_SIZE(handles->cap); i++) {
len += sysfs_emit_at(buffer, len, "0x%.4x ", handles->cap[i]);
}
len += sysfs_emit_at(buffer, len, "\n");
return len;
}
staticint sony_nc_handles_setup(struct platform_device *pd)
{ int i, r, result, arg;
handles = kzalloc(sizeof(*handles), GFP_KERNEL); if (!handles) return -ENOMEM;
for (i = 0; i < ARRAY_SIZE(handles->cap); i++) {
arg = i + 0x20;
r = sony_nc_int_call(sony_nc_acpi_handle, "SN00", &arg,
&result); if (!r) {
dprintk("caching handle 0x%.4x (offset: 0x%.2x)\n",
result, i);
handles->cap[i] = result;
}
}
/* brightness_default_validate: * * manipulate input output values to keep consistency with the * backlight framework for which brightness values are 0-based.
*/ staticint brightness_default_validate(constint direction, constint value)
{ switch (direction) { case SNC_VALIDATE_OUT: return value - 1; case SNC_VALIDATE_IN: if (value >= 0 && value < SONY_MAX_BRIGHTNESS) return value + 1;
} return -EINVAL;
}
/* boolean_validate: * * on input validate boolean values 0/1, on output just pass the * received value.
*/ staticint boolean_validate(constint direction, constint value)
{ if (direction == SNC_VALIDATE_IN) { if (value != 0 && value != 1) return -EINVAL;
} return value;
}
/* * Sysfs show/store common to all sony_nc_values
*/ static ssize_t sony_nc_sysfs_show(struct device *dev, struct device_attribute *attr, char *buffer)
{ int value, ret = 0; struct sony_nc_value *item =
container_of(attr, struct sony_nc_value, devattr);
if (!*item->acpiget) return -EIO;
ret = sony_nc_int_call(sony_nc_acpi_handle, *item->acpiget, NULL,
&value); if (ret < 0) return -EIO;
if (item->validate)
value = item->validate(SNC_VALIDATE_OUT, value);
return sysfs_emit(buffer, "%d\n", value);
}
static ssize_t sony_nc_sysfs_store(struct device *dev, struct device_attribute *attr, constchar *buffer, size_t count)
{ int value; int ret = 0; struct sony_nc_value *item =
container_of(attr, struct sony_nc_value, devattr);
if (!item->acpiset) return -EIO;
if (count > 31) return -EINVAL;
if (kstrtoint(buffer, 10, &value)) return -EINVAL;
if (item->validate)
value = item->validate(SNC_VALIDATE_IN, value);
if (value < 0) return value;
ret = sony_nc_int_call(sony_nc_acpi_handle, *item->acpiset,
&value, NULL); if (ret < 0) return -EIO;
staticint sony_backlight_get_brightness(struct backlight_device *bd)
{ int value;
if (sony_nc_int_call(sony_nc_acpi_handle, "GBRT", NULL, &value)) return 0; /* brightness levels are 1-based, while backlight ones are 0-based */ return value - 1;
}
if (offset >= ARRAY_SIZE(handles->cap)) {
pr_err("Event 0x%x outside of capabilities list\n",
event); return;
}
handle = handles->cap[offset];
/* list of handles known for generating events */ switch (handle) { /* hotkey event */ case 0x0100: case 0x0127:
ev_type = HOTKEY;
ret = sony_nc_hotkeys_decode(event, handle);
if (ret > 0) {
sony_laptop_report_input_event(ret);
real_ev = ret;
}
break;
/* wlan switch */ case 0x0124: case 0x0135: /* events on this handle are reported when the * switch changes position or for battery * events. We'll notify both of them but only * update the rfkill device status when the * switch is moved.
*/
ev_type = KILLSWITCH;
sony_call_snc_handle(handle, 0x0100, &result);
real_ev = result & 0x03;
/* hw switch event */ if (real_ev == 1)
sony_nc_rfkill_update();
/* setup found handles here */ for (i = 0; i < ARRAY_SIZE(handles->cap); i++) { unsignedint handle = handles->cap[i];
if (!handle) continue;
dprintk("setting up handle 0x%.4x\n", handle);
switch (handle) { case 0x0100: case 0x0101: case 0x0127: /* setup hotkeys */
sony_call_snc_handle(handle, 0, &result); break; case 0x0102: /* setup hotkeys */
sony_call_snc_handle(handle, 0x100, &result); break; case 0x0105: case 0x0148: /* touchpad enable/disable */
result = sony_nc_touchpad_setup(pf_device, handle); if (result)
pr_err("couldn't set up touchpad control function (%d)\n",
result); break; case 0x0115: case 0x0136: case 0x013f:
result = sony_nc_battery_care_setup(pf_device, handle); if (result)
pr_err("couldn't set up battery care function (%d)\n",
result); break; case 0x0119: case 0x015D:
result = sony_nc_lid_resume_setup(pf_device, handle); if (result)
pr_err("couldn't set up lid resume function (%d)\n",
result); break; case 0x0122:
result = sony_nc_thermal_setup(pf_device); if (result)
pr_err("couldn't set up thermal profile function (%d)\n",
result); break; case 0x0128: case 0x0146: case 0x015B:
result = sony_nc_gfx_switch_setup(pf_device, handle); if (result)
pr_err("couldn't set up GFX Switch status (%d)\n",
result); break; case 0x0131:
result = sony_nc_highspeed_charging_setup(pf_device); if (result)
pr_err("couldn't set up high speed charging function (%d)\n",
result); break; case 0x0124: case 0x0135:
result = sony_nc_rfkill_setup(device, handle); if (result)
pr_err("couldn't set up rfkill support (%d)\n",
result); break; case 0x0137: case 0x0143: case 0x014b: case 0x014c: case 0x0153: case 0x0163:
result = sony_nc_kbd_backlight_setup(pf_device, handle); if (result)
pr_err("couldn't set up keyboard backlight function (%d)\n",
result); break; case 0x0121:
result = sony_nc_lowbatt_setup(pf_device); if (result)
pr_err("couldn't set up low battery function (%d)\n",
result); break; case 0x0149:
result = sony_nc_fanspeed_setup(pf_device); if (result)
pr_err("couldn't set up fan speed function (%d)\n",
result); break; case 0x0155:
result = sony_nc_usb_charge_setup(pf_device); if (result)
pr_err("couldn't set up USB charge support (%d)\n",
result); break; case 0x011D:
result = sony_nc_panelid_setup(pf_device); if (result)
pr_err("couldn't set up panel ID function (%d)\n",
result); break; case 0x0168:
result = sony_nc_smart_conn_setup(pf_device); if (result)
pr_err("couldn't set up smart connect support (%d)\n",
result); break; default: continue;
}
}
/* Enable all events */
arg = 0x10; if (!sony_nc_int_call(sony_nc_acpi_handle, "SN00", &arg, &bitmask))
sony_nc_int_call(sony_nc_acpi_handle, "SN02", &bitmask,
&result);
}
staticvoid sony_nc_function_cleanup(struct platform_device *pd)
{ unsignedint i, result, bitmask, handle;
if (!handles) return;
/* get enabled events and disable them */
sony_nc_int_call(sony_nc_acpi_handle, "SN01", NULL, &bitmask);
sony_nc_int_call(sony_nc_acpi_handle, "SN03", &bitmask, &result);
/* cleanup handles here */ for (i = 0; i < ARRAY_SIZE(handles->cap); i++) {
handle = handles->cap[i];
if (!handle) continue;
switch (handle) { case 0x0105: case 0x0148:
sony_nc_touchpad_cleanup(pd); break; case 0x0115: case 0x0136: case 0x013f:
sony_nc_battery_care_cleanup(pd); break; case 0x0119: case 0x015D:
sony_nc_lid_resume_cleanup(pd); break; case 0x0122:
sony_nc_thermal_cleanup(pd); break; case 0x0128: case 0x0146: case 0x015B:
sony_nc_gfx_switch_cleanup(pd); break; case 0x0131:
sony_nc_highspeed_charging_cleanup(pd); break; case 0x0124: case 0x0135:
sony_nc_rfkill_cleanup(); break; case 0x0137: case 0x0143: case 0x014b: case 0x014c: case 0x0153: case 0x0163:
sony_nc_kbd_backlight_cleanup(pd, handle); break; case 0x0121:
sony_nc_lowbatt_cleanup(pd); break; case 0x0149:
sony_nc_fanspeed_cleanup(pd); break; case 0x0155:
sony_nc_usb_charge_cleanup(pd); break; case 0x011D:
sony_nc_panelid_cleanup(pd); break; case 0x0168:
sony_nc_smart_conn_cleanup(pd); break; default: continue;
}
}
/* finally cleanup the handles list */
sony_nc_handles_cleanup(pd);
}
#ifdef CONFIG_PM_SLEEP staticvoid sony_nc_function_resume(void)
{ unsignedint i, result, bitmask, arg;
dprintk("Resuming SNC device\n");
for (i = 0; i < ARRAY_SIZE(handles->cap); i++) { unsignedint handle = handles->cap[i];
if (!handle) continue;
switch (handle) { case 0x0100: case 0x0101: case 0x0127: /* re-enable hotkeys */
sony_call_snc_handle(handle, 0, &result); break; case 0x0102: /* re-enable hotkeys */
sony_call_snc_handle(handle, 0x100, &result); break; case 0x0122:
sony_nc_thermal_resume(); break; case 0x0124: case 0x0135:
sony_nc_rfkill_update(); break; default: continue;
}
}
/* Enable all events */
arg = 0x10; if (!sony_nc_int_call(sony_nc_acpi_handle, "SN00", &arg, &bitmask))
sony_nc_int_call(sony_nc_acpi_handle, "SN02", &bitmask,
&result);
}
for (i = 0; i < N_SONY_RFKILL; i++) { if (sony_rfkill_devices[i]) {
rfkill_unregister(sony_rfkill_devices[i]);
rfkill_destroy(sony_rfkill_devices[i]);
}
}
}
staticint sony_nc_rfkill_set(void *data, bool blocked)
{ int result; int argument = sony_rfkill_address[(long) data] + 0x100;
switch (nc_type) { case SONY_WIFI:
type = RFKILL_TYPE_WLAN;
name = "sony-wifi"; break; case SONY_BLUETOOTH:
type = RFKILL_TYPE_BLUETOOTH;
name = "sony-bluetooth"; break; case SONY_WWAN:
type = RFKILL_TYPE_WWAN;
name = "sony-wwan"; break; case SONY_WIMAX:
type = RFKILL_TYPE_WIMAX;
name = "sony-wimax"; break; default: return -EINVAL;
}
staticint sony_nc_kbd_backlight_setup(struct platform_device *pd, unsignedint handle)
{ int result; int probe_base = 0; int ctl_base = 0; int ret = 0;
if (kbdbl_ctl) {
pr_warn("handle 0x%.4x: keyboard backlight setup already done for 0x%.4x\n",
handle, kbdbl_ctl->handle); return -EBUSY;
}
/* verify the kbd backlight presence, some of these handles are not used * for keyboard backlight only
*/ switch (handle) { case 0x0153:
probe_base = 0x0;
ctl_base = 0x0; break; case 0x0137:
probe_base = 0x0B00;
ctl_base = 0x0C00; break; default:
probe_base = 0x0100;
ctl_base = 0x4000; break;
}
/* * Only probe if there is a separate probe_base, otherwise the probe call * is equivalent to __sony_nc_kbd_backlight_mode_set(0), resulting in * the keyboard backlight being turned off.
*/ if (probe_base) {
ret = sony_call_snc_handle(handle, probe_base, &result); if (ret) return ret;
/* limit values (2 bits): * 00 - none * 01 - 80% * 10 - 50% * 11 - 100% * * bit 0: 0 disable BCL, 1 enable BCL * bit 1: 1 tell to store the battery limit (see bits 6,7) too * bits 2,3: reserved * bits 4,5: store the limit into the EC * bits 6,7: store the limit into the battery
*/
cmd = 0;
if (value > 0) { if (value <= 50)
cmd = 0x20;
elseif (value <= 80)
cmd = 0x10;
elseif (value <= 100)
cmd = 0x30;
else return -EINVAL;
/* * handle 0x0115 should allow storing on battery too; * handle 0x0136 same as 0x0115 + health status; * handle 0x013f, same as 0x0136 but no storing on the battery
*/ if (bcare_ctl->handle != 0x013f)
cmd = cmd | (cmd << 2);
cmd = (cmd | 0x1) << 0x10;
}
if (sony_call_snc_handle(bcare_ctl->handle, cmd | 0x0100, &result)) return -EIO;
if (sony_call_snc_handle(bcare_ctl->handle, 0x0000, &result)) return -EIO;
status = (result & 0x01) ? ((result & 0x30) >> 0x04) : 0; switch (status) { case 1:
status = 80; break; case 2:
status = 50; break; case 3:
status = 100; break; default:
status = 0; break;
}
/* the thermal profile seems to be a two bit bitmask: * lsb -> silent * msb -> performance * no bit set is the normal operation and is always valid * Some vaio models only have "balanced" and "performance"
*/ if ((mode && !(th_handle->profiles & mode)) || mode >= THM_PROFILE_MAX) return -EINVAL;
if (sony_call_snc_handle(0x0122, mode << 0x10 | 0x0200, &result)) return -EIO;
if (kstrtoul(buffer, 10, &value) || value > 1) return -EINVAL;
/* the value we have to write to SNC is a bitmask: * +--------------+ * | S3 | S4 | S5 | * +--------------+ * 2 1 0
*/ while (pos < LID_RESUME_MAX) { if (&lid_ctl->attrs[pos].attr == &attr->attr) break;
pos++;
} if (pos == LID_RESUME_MAX) return -EINVAL;
if (value)
value = lid_ctl->status | (1 << pos); else
value = lid_ctl->status & ~(1 << pos);
if (sony_call_snc_handle(lid_ctl->handle, value << 0x10 | 0x0100,
&result)) return -EIO;
if (sony_call_snc_handle(0x0131, 0x0000, &result) || !(result & 0x01)) { /* some models advertise the handle but have no implementation * for it
*/
pr_info("No High Speed Charging capability found\n"); return 0;
}
hsc_handle = kzalloc(sizeof(struct device_attribute), GFP_KERNEL); if (!hsc_handle) return -ENOMEM;
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.