summaryrefslogtreecommitdiff
path: root/drivers/platform/x86/panasonic-laptop.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/platform/x86/panasonic-laptop.c')
-rw-r--r--drivers/platform/x86/panasonic-laptop.c92
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: