diff options
-rw-r--r-- | Documentation/admin-guide/kernel-parameters.txt | 22 | ||||
-rw-r--r-- | drivers/platform/x86/touchscreen_dmi.c | 79 |
2 files changed, 98 insertions, 3 deletions
diff --git a/Documentation/admin-guide/kernel-parameters.txt b/Documentation/admin-guide/kernel-parameters.txt index 500cfa776225..b600df82669d 100644 --- a/Documentation/admin-guide/kernel-parameters.txt +++ b/Documentation/admin-guide/kernel-parameters.txt @@ -1921,6 +1921,28 @@ Format: <bus_id>,<clkrate> + i2c_touchscreen_props= [HW,ACPI,X86] + Set device-properties for ACPI-enumerated I2C-attached + touchscreen, to e.g. fix coordinates of upside-down + mounted touchscreens. If you need this option please + submit a drivers/platform/x86/touchscreen_dmi.c patch + adding a DMI quirk for this. + + Format: + <ACPI_HW_ID>:<prop_name>=<val>[:prop_name=val][:...] + Where <val> is one of: + Omit "=<val>" entirely Set a boolean device-property + Unsigned number Set a u32 device-property + Anything else Set a string device-property + + Examples (split over multiple lines): + i2c_touchscreen_props=GDIX1001:touchscreen-inverted-x: + touchscreen-inverted-y + + i2c_touchscreen_props=MSSL1680:touchscreen-size-x=1920: + touchscreen-size-y=1080:touchscreen-inverted-y: + firmware-name=gsl1680-vendor-model.fw:silead,home-button + i8042.debug [HW] Toggle i8042 debug mode i8042.unmask_kbd_data [HW] Enable printing of interrupt data from the KBD port diff --git a/drivers/platform/x86/touchscreen_dmi.c b/drivers/platform/x86/touchscreen_dmi.c index c6a10ec2c83f..b021fb9e579e 100644 --- a/drivers/platform/x86/touchscreen_dmi.c +++ b/drivers/platform/x86/touchscreen_dmi.c @@ -9,10 +9,13 @@ */ #include <linux/acpi.h> +#include <linux/ctype.h> #include <linux/device.h> #include <linux/dmi.h> #include <linux/efi_embedded_fw.h> #include <linux/i2c.h> +#include <linux/init.h> +#include <linux/kstrtox.h> #include <linux/notifier.h> #include <linux/property.h> #include <linux/string.h> @@ -1817,7 +1820,7 @@ const struct dmi_system_id touchscreen_dmi_table[] = { { } }; -static const struct ts_dmi_data *ts_data; +static struct ts_dmi_data *ts_data; static void ts_dmi_add_props(struct i2c_client *client) { @@ -1852,6 +1855,64 @@ static int ts_dmi_notifier_call(struct notifier_block *nb, return 0; } +#define MAX_CMDLINE_PROPS 16 + +static struct property_entry ts_cmdline_props[MAX_CMDLINE_PROPS + 1]; + +static struct ts_dmi_data ts_cmdline_data = { + .properties = ts_cmdline_props, +}; + +static int __init ts_parse_props(char *str) +{ + /* Save the original str to show it on syntax errors */ + char orig_str[256]; + char *name, *value; + u32 u32val; + int i, ret; + + strscpy(orig_str, str, sizeof(orig_str)); + + /* + * str is part of the static_command_line from init/main.c and poking + * holes in that by writing 0 to it is allowed, as is taking long + * lasting references to it. + */ + ts_cmdline_data.acpi_name = strsep(&str, ":"); + + for (i = 0; i < MAX_CMDLINE_PROPS; i++) { + name = strsep(&str, ":"); + if (!name || !name[0]) + break; + + /* Replace '=' with 0 and make value point past '=' or NULL */ + value = name; + strsep(&value, "="); + if (!value) { + ts_cmdline_props[i] = PROPERTY_ENTRY_BOOL(name); + } else if (isdigit(value[0])) { + ret = kstrtou32(value, 0, &u32val); + if (ret) + goto syntax_error; + + ts_cmdline_props[i] = PROPERTY_ENTRY_U32(name, u32val); + } else { + ts_cmdline_props[i] = PROPERTY_ENTRY_STRING(name, value); + } + } + + if (!i || str) + goto syntax_error; + + ts_data = &ts_cmdline_data; + return 1; + +syntax_error: + pr_err("Invalid '%s' value for 'i2c_touchscreen_props='\n", orig_str); + return 1; /* "i2c_touchscreen_props=" is still a known parameter */ +} +__setup("i2c_touchscreen_props=", ts_parse_props); + static struct notifier_block ts_dmi_notifier = { .notifier_call = ts_dmi_notifier_call, }; @@ -1859,13 +1920,25 @@ static struct notifier_block ts_dmi_notifier = { static int __init ts_dmi_init(void) { const struct dmi_system_id *dmi_id; + struct ts_dmi_data *ts_data_dmi; int error; dmi_id = dmi_first_match(touchscreen_dmi_table); - if (!dmi_id) + ts_data_dmi = dmi_id ? dmi_id->driver_data : NULL; + + if (ts_data) { + /* + * Kernel cmdline provided data takes precedence, copy over + * DMI efi_embedded_fw info if available. + */ + if (ts_data_dmi) + ts_data->embedded_fw = ts_data_dmi->embedded_fw; + } else if (ts_data_dmi) { + ts_data = ts_data_dmi; + } else { return 0; /* Not an error */ + } - ts_data = dmi_id->driver_data; /* Some dmi table entries only provide an efi_embedded_fw_desc */ if (!ts_data->properties) return 0; |