diff options
Diffstat (limited to 'drivers/platform/x86/panasonic-laptop.c')
-rw-r--r-- | drivers/platform/x86/panasonic-laptop.c | 92 |
1 files changed, 72 insertions, 20 deletions
diff --git a/drivers/platform/x86/panasonic-laptop.c b/drivers/platform/x86/panasonic-laptop.c index cf845ee1c7b1..255317e6fec8 100644 --- a/drivers/platform/x86/panasonic-laptop.c +++ b/drivers/platform/x86/panasonic-laptop.c @@ -121,6 +121,7 @@ #include <linux/acpi.h> #include <linux/backlight.h> +#include <linux/bits.h> #include <linux/ctype.h> #include <linux/i8042.h> #include <linux/init.h> @@ -224,6 +225,17 @@ static const struct key_entry panasonic_keymap[] = { { KE_KEY, 8, { KEY_PROG1 } }, /* Change CPU boost */ { KE_KEY, 9, { KEY_BATTERY } }, { KE_KEY, 10, { KEY_SUSPEND } }, + { KE_KEY, 21, { KEY_MACRO1 } }, + { KE_KEY, 22, { KEY_MACRO2 } }, + { KE_KEY, 24, { KEY_MACRO3 } }, + { KE_KEY, 25, { KEY_MACRO4 } }, + { KE_KEY, 34, { KEY_MACRO5 } }, + { KE_KEY, 35, { KEY_MACRO6 } }, + { KE_KEY, 36, { KEY_MACRO7 } }, + { KE_KEY, 37, { KEY_MACRO8 } }, + { KE_KEY, 41, { KEY_MACRO9 } }, + { KE_KEY, 42, { KEY_MACRO10 } }, + { KE_KEY, 43, { KEY_MACRO11 } }, { KE_END, 0 } }; @@ -248,7 +260,7 @@ struct pcc_acpi { * keypress events over the PS/2 kbd interface, filter these out. */ static bool panasonic_i8042_filter(unsigned char data, unsigned char str, - struct serio *port) + struct serio *port, void *context) { static bool extended; @@ -337,7 +349,8 @@ static int acpi_pcc_retrieve_biosdata(struct pcc_acpi *pcc) } if (pcc->num_sifr < hkey->package.count) { - pr_err("SQTY reports bad SINF length\n"); + pr_err("SQTY reports bad SINF length SQTY: %lu SINF-pkg-count: %u\n", + pcc->num_sifr, hkey->package.count); status = AE_ERROR; goto end; } @@ -601,8 +614,7 @@ static ssize_t eco_mode_show(struct device *dev, struct device_attribute *attr, result = 1; break; default: - result = -EIO; - break; + return -EIO; } return sysfs_emit(buf, "%u\n", result); } @@ -748,7 +760,12 @@ static ssize_t current_brightness_store(struct device *dev, struct device_attrib static ssize_t cdpower_show(struct device *dev, struct device_attribute *attr, char *buf) { - return sysfs_emit(buf, "%d\n", get_optd_power_state()); + int state = get_optd_power_state(); + + if (state < 0) + return state; + + return sysfs_emit(buf, "%d\n", state); } static ssize_t cdpower_store(struct device *dev, struct device_attribute *attr, @@ -773,6 +790,24 @@ static DEVICE_ATTR_RW(dc_brightness); static DEVICE_ATTR_RW(current_brightness); static DEVICE_ATTR_RW(cdpower); +static umode_t pcc_sysfs_is_visible(struct kobject *kobj, struct attribute *attr, int idx) +{ + struct device *dev = kobj_to_dev(kobj); + struct acpi_device *acpi = to_acpi_device(dev); + struct pcc_acpi *pcc = acpi_driver_data(acpi); + + if (attr == &dev_attr_mute.attr) + return (pcc->num_sifr > SINF_MUTE) ? attr->mode : 0; + + if (attr == &dev_attr_eco_mode.attr) + return (pcc->num_sifr > SINF_ECO_MODE) ? attr->mode : 0; + + if (attr == &dev_attr_current_brightness.attr) + return (pcc->num_sifr > SINF_CUR_BRIGHT) ? attr->mode : 0; + + return attr->mode; +} + static struct attribute *pcc_sysfs_entries[] = { &dev_attr_numbatt.attr, &dev_attr_lcdtype.attr, @@ -787,8 +822,9 @@ static struct attribute *pcc_sysfs_entries[] = { }; static const struct attribute_group pcc_attr_group = { - .name = NULL, /* put in device directory */ - .attrs = pcc_sysfs_entries, + .name = NULL, /* put in device directory */ + .attrs = pcc_sysfs_entries, + .is_visible = pcc_sysfs_is_visible, }; @@ -810,8 +846,8 @@ static void acpi_pcc_generate_keyinput(struct pcc_acpi *pcc) return; } - key = result & 0xf; - updown = result & 0x80; /* 0x80 == key down; 0x00 = key up */ + key = result & GENMASK(6, 0); + updown = result & BIT(7); /* 0x80 == key down; 0x00 = key up */ /* hack: some firmware sends no key down for sleep / hibernate */ if (key == 7 || key == 10) { @@ -941,12 +977,15 @@ static int acpi_pcc_hotkey_resume(struct device *dev) if (!pcc) return -EINVAL; - acpi_pcc_write_sset(pcc, SINF_MUTE, pcc->mute); - acpi_pcc_write_sset(pcc, SINF_ECO_MODE, pcc->eco_mode); + if (pcc->num_sifr > SINF_MUTE) + acpi_pcc_write_sset(pcc, SINF_MUTE, pcc->mute); + if (pcc->num_sifr > SINF_ECO_MODE) + acpi_pcc_write_sset(pcc, SINF_ECO_MODE, pcc->eco_mode); acpi_pcc_write_sset(pcc, SINF_STICKY_KEY, pcc->sticky_key); acpi_pcc_write_sset(pcc, SINF_AC_CUR_BRIGHT, pcc->ac_brightness); acpi_pcc_write_sset(pcc, SINF_DC_CUR_BRIGHT, pcc->dc_brightness); - acpi_pcc_write_sset(pcc, SINF_CUR_BRIGHT, pcc->current_brightness); + if (pcc->num_sifr > SINF_CUR_BRIGHT) + acpi_pcc_write_sset(pcc, SINF_CUR_BRIGHT, pcc->current_brightness); return 0; } @@ -963,11 +1002,21 @@ static int acpi_pcc_hotkey_add(struct acpi_device *device) num_sifr = acpi_pcc_get_sqty(device); - if (num_sifr < 0 || num_sifr > 255) { - pr_err("num_sifr out of range"); + /* + * pcc->sinf is expected to at least have the AC+DC brightness entries. + * Accesses to higher SINF entries are checked against num_sifr. + */ + if (num_sifr <= SINF_DC_CUR_BRIGHT || num_sifr > 255) { + pr_err("num_sifr %d out of range %d - 255\n", num_sifr, SINF_DC_CUR_BRIGHT + 1); return -ENODEV; } + /* + * Some DSDT-s have an off-by-one bug where the SINF package count is + * one higher than the SQTY reported value, allocate 1 entry extra. + */ + num_sifr++; + pcc = kzalloc(sizeof(struct pcc_acpi), GFP_KERNEL); if (!pcc) { pr_err("Couldn't allocate mem for pcc"); @@ -984,8 +1033,8 @@ static int acpi_pcc_hotkey_add(struct acpi_device *device) pcc->handle = device->handle; pcc->num_sifr = num_sifr; device->driver_data = pcc; - strcpy(acpi_device_name(device), ACPI_PCC_DEVICE_NAME); - strcpy(acpi_device_class(device), ACPI_PCC_CLASS); + strscpy(acpi_device_name(device), ACPI_PCC_DEVICE_NAME); + strscpy(acpi_device_class(device), ACPI_PCC_CLASS); result = acpi_pcc_init_input(pcc); if (result) { @@ -1020,11 +1069,14 @@ static int acpi_pcc_hotkey_add(struct acpi_device *device) acpi_pcc_write_sset(pcc, SINF_STICKY_KEY, 0); pcc->sticky_key = 0; - pcc->eco_mode = pcc->sinf[SINF_ECO_MODE]; - pcc->mute = pcc->sinf[SINF_MUTE]; pcc->ac_brightness = pcc->sinf[SINF_AC_CUR_BRIGHT]; pcc->dc_brightness = pcc->sinf[SINF_DC_CUR_BRIGHT]; - pcc->current_brightness = pcc->sinf[SINF_CUR_BRIGHT]; + if (pcc->num_sifr > SINF_MUTE) + pcc->mute = pcc->sinf[SINF_MUTE]; + if (pcc->num_sifr > SINF_ECO_MODE) + pcc->eco_mode = pcc->sinf[SINF_ECO_MODE]; + if (pcc->num_sifr > SINF_CUR_BRIGHT) + pcc->current_brightness = pcc->sinf[SINF_CUR_BRIGHT]; /* add sysfs attributes */ result = sysfs_create_group(&device->dev.kobj, &pcc_attr_group); @@ -1048,7 +1100,7 @@ static int acpi_pcc_hotkey_add(struct acpi_device *device) pcc->platform = NULL; } - i8042_install_filter(panasonic_i8042_filter); + i8042_install_filter(panasonic_i8042_filter, NULL); return 0; out_platform: |