Message ID | 20210905124547.31567-1-hdegoede@redhat.com (mailing list archive) |
---|---|
State | New, archived |
Headers | show |
Series | [regression,fix,1/2] Input: silead - Add support for EFI-embedded fw using different min/max coordinates | expand |
Hi, These 2 patches obviously are not regression-fixes, I accidentally still had a subjectprefix set when sending these, sorry about that. Other then that these are ready for merging :) Regards, Hans On 9/5/21 2:45 PM, Hans de Goede wrote: > Unfortunately, at the time of writing this commit message, we have been > unable to get permission from Silead, or from device OEMs, to distribute > the necessary Silead firmware files in linux-firmware. > > On a whole bunch of devices the UEFI BIOS code contains a touchscreen > driver, which contains an embedded copy of the firmware. The fw-loader > code has a "platform" fallback mechanism, which together with info on the > firmware from drivers/platform/x86/touchscreen_dmi.c will use the firmware > from the UEFI driver when the firmware is missing from /lib/firmware. This > makes the touchscreen work OOTB without users needing to manually download > the firmware. > > The firmware bundled with the original Windows/Android is usually newer > then the firmware in the UEFI driver and it is better calibrated. This > better calibration can lead to significant differences in the reported > min/max coordinates. > > Add support for a new (optional) "silead,efi-fw-min-max" property which > provides a set of alternative min/max values to use for the x/y axis when > the EFI embedded firmware is used. > > The new property is only used on (x86) devices which do not use devicetree, > IOW it is not used in actual devicetree files. The devicetree-bindings > maintainers have requested properties like these to not be added to the > devicetree-bindings, so the new property is deliberately not added to the > existing silead devicetree-bindings documentation. > > Signed-off-by: Hans de Goede <hdegoede@redhat.com> > --- > drivers/input/touchscreen/silead.c | 73 ++++++++++++++++++++++++++++-- > 1 file changed, 68 insertions(+), 5 deletions(-) > > diff --git a/drivers/input/touchscreen/silead.c b/drivers/input/touchscreen/silead.c > index 1ee760bac0cf..caa25af53e6e 100644 > --- a/drivers/input/touchscreen/silead.c > +++ b/drivers/input/touchscreen/silead.c > @@ -75,6 +75,8 @@ struct silead_ts_data { > struct input_mt_pos pos[SILEAD_MAX_FINGERS]; > int slots[SILEAD_MAX_FINGERS]; > int id[SILEAD_MAX_FINGERS]; > + u32 efi_fw_min_max[4]; > + bool efi_fw_min_max_set; > }; > > struct silead_fw_data { > @@ -82,6 +84,35 @@ struct silead_fw_data { > u32 val; > }; > > +static void silead_apply_efi_fw_min_max(struct silead_ts_data *data) > +{ > + struct input_absinfo *absinfo_x = &data->input->absinfo[ABS_MT_POSITION_X]; > + struct input_absinfo *absinfo_y = &data->input->absinfo[ABS_MT_POSITION_Y]; > + > + if (!data->efi_fw_min_max_set) > + return; > + > + absinfo_x->minimum = data->efi_fw_min_max[0]; > + absinfo_x->maximum = data->efi_fw_min_max[1]; > + absinfo_y->minimum = data->efi_fw_min_max[2]; > + absinfo_y->maximum = data->efi_fw_min_max[3]; > + > + if (data->prop.invert_x) { > + absinfo_x->maximum -= absinfo_x->minimum; > + absinfo_x->minimum = 0; > + } > + > + if (data->prop.invert_y) { > + absinfo_y->maximum -= absinfo_y->minimum; > + absinfo_y->minimum = 0; > + } > + > + if (data->prop.swap_x_y) { > + swap(absinfo_x->minimum, absinfo_y->minimum); > + swap(absinfo_x->maximum, absinfo_y->maximum); > + } > +} > + > static int silead_ts_request_input_dev(struct silead_ts_data *data) > { > struct device *dev = &data->client->dev; > @@ -97,6 +128,7 @@ static int silead_ts_request_input_dev(struct silead_ts_data *data) > input_set_abs_params(data->input, ABS_MT_POSITION_X, 0, 4095, 0, 0); > input_set_abs_params(data->input, ABS_MT_POSITION_Y, 0, 4095, 0, 0); > touchscreen_parse_properties(data->input, true, &data->prop); > + silead_apply_efi_fw_min_max(data); > > input_mt_init_slots(data->input, data->max_fingers, > INPUT_MT_DIRECT | INPUT_MT_DROP_UNUSED | > @@ -282,17 +314,48 @@ static int silead_ts_load_fw(struct i2c_client *client) > { > struct device *dev = &client->dev; > struct silead_ts_data *data = i2c_get_clientdata(client); > - unsigned int fw_size, i; > - const struct firmware *fw; > + const struct firmware *fw = NULL; > struct silead_fw_data *fw_data; > + unsigned int fw_size, i; > int error; > > dev_dbg(dev, "Firmware file name: %s", data->fw_name); > > - error = firmware_request_platform(&fw, data->fw_name, dev); > + /* > + * Unfortunately, at the time of writing this comment, we have been unable to > + * get permission from Silead, or from device OEMs, to distribute the necessary > + * Silead firmware files in linux-firmware. > + * > + * On a whole bunch of devices the UEFI BIOS code contains a touchscreen driver, > + * which contains an embedded copy of the firmware. The fw-loader code has a > + * "platform" fallback mechanism, which together with info on the firmware > + * from drivers/platform/x86/touchscreen_dmi.c will use the firmware from the > + * UEFI driver when the firmware is missing from /lib/firmware. This makes the > + * touchscreen work OOTB without users needing to manually download the firmware. > + * > + * The firmware bundled with the original Windows/Android is usually newer then > + * the firmware in the UEFI driver and it is better calibrated. This better > + * calibration can lead to significant differences in the reported min/max > + * coordinates. > + * > + * To deal with this we first try to load the firmware without "platform" > + * fallback. If that fails we retry with "platform" fallback and if that > + * succeeds we apply an (optional) set of alternative min/max values from the > + * "silead,efi-fw-min-max" property. > + */ > + error = firmware_request_nowarn(&fw, data->fw_name, dev); > if (error) { > - dev_err(dev, "Firmware request error %d\n", error); > - return error; > + error = firmware_request_platform(&fw, data->fw_name, dev); > + if (error) { > + dev_err(dev, "Firmware request error %d\n", error); > + return error; > + } > + > + error = device_property_read_u32_array(dev, "silead,efi-fw-min-max", > + data->efi_fw_min_max, > + ARRAY_SIZE(data->efi_fw_min_max)); > + if (!error) > + data->efi_fw_min_max_set = true; > } > > fw_size = fw->size / sizeof(*fw_data); >
diff --git a/drivers/input/touchscreen/silead.c b/drivers/input/touchscreen/silead.c index 1ee760bac0cf..caa25af53e6e 100644 --- a/drivers/input/touchscreen/silead.c +++ b/drivers/input/touchscreen/silead.c @@ -75,6 +75,8 @@ struct silead_ts_data { struct input_mt_pos pos[SILEAD_MAX_FINGERS]; int slots[SILEAD_MAX_FINGERS]; int id[SILEAD_MAX_FINGERS]; + u32 efi_fw_min_max[4]; + bool efi_fw_min_max_set; }; struct silead_fw_data { @@ -82,6 +84,35 @@ struct silead_fw_data { u32 val; }; +static void silead_apply_efi_fw_min_max(struct silead_ts_data *data) +{ + struct input_absinfo *absinfo_x = &data->input->absinfo[ABS_MT_POSITION_X]; + struct input_absinfo *absinfo_y = &data->input->absinfo[ABS_MT_POSITION_Y]; + + if (!data->efi_fw_min_max_set) + return; + + absinfo_x->minimum = data->efi_fw_min_max[0]; + absinfo_x->maximum = data->efi_fw_min_max[1]; + absinfo_y->minimum = data->efi_fw_min_max[2]; + absinfo_y->maximum = data->efi_fw_min_max[3]; + + if (data->prop.invert_x) { + absinfo_x->maximum -= absinfo_x->minimum; + absinfo_x->minimum = 0; + } + + if (data->prop.invert_y) { + absinfo_y->maximum -= absinfo_y->minimum; + absinfo_y->minimum = 0; + } + + if (data->prop.swap_x_y) { + swap(absinfo_x->minimum, absinfo_y->minimum); + swap(absinfo_x->maximum, absinfo_y->maximum); + } +} + static int silead_ts_request_input_dev(struct silead_ts_data *data) { struct device *dev = &data->client->dev; @@ -97,6 +128,7 @@ static int silead_ts_request_input_dev(struct silead_ts_data *data) input_set_abs_params(data->input, ABS_MT_POSITION_X, 0, 4095, 0, 0); input_set_abs_params(data->input, ABS_MT_POSITION_Y, 0, 4095, 0, 0); touchscreen_parse_properties(data->input, true, &data->prop); + silead_apply_efi_fw_min_max(data); input_mt_init_slots(data->input, data->max_fingers, INPUT_MT_DIRECT | INPUT_MT_DROP_UNUSED | @@ -282,17 +314,48 @@ static int silead_ts_load_fw(struct i2c_client *client) { struct device *dev = &client->dev; struct silead_ts_data *data = i2c_get_clientdata(client); - unsigned int fw_size, i; - const struct firmware *fw; + const struct firmware *fw = NULL; struct silead_fw_data *fw_data; + unsigned int fw_size, i; int error; dev_dbg(dev, "Firmware file name: %s", data->fw_name); - error = firmware_request_platform(&fw, data->fw_name, dev); + /* + * Unfortunately, at the time of writing this comment, we have been unable to + * get permission from Silead, or from device OEMs, to distribute the necessary + * Silead firmware files in linux-firmware. + * + * On a whole bunch of devices the UEFI BIOS code contains a touchscreen driver, + * which contains an embedded copy of the firmware. The fw-loader code has a + * "platform" fallback mechanism, which together with info on the firmware + * from drivers/platform/x86/touchscreen_dmi.c will use the firmware from the + * UEFI driver when the firmware is missing from /lib/firmware. This makes the + * touchscreen work OOTB without users needing to manually download the firmware. + * + * The firmware bundled with the original Windows/Android is usually newer then + * the firmware in the UEFI driver and it is better calibrated. This better + * calibration can lead to significant differences in the reported min/max + * coordinates. + * + * To deal with this we first try to load the firmware without "platform" + * fallback. If that fails we retry with "platform" fallback and if that + * succeeds we apply an (optional) set of alternative min/max values from the + * "silead,efi-fw-min-max" property. + */ + error = firmware_request_nowarn(&fw, data->fw_name, dev); if (error) { - dev_err(dev, "Firmware request error %d\n", error); - return error; + error = firmware_request_platform(&fw, data->fw_name, dev); + if (error) { + dev_err(dev, "Firmware request error %d\n", error); + return error; + } + + error = device_property_read_u32_array(dev, "silead,efi-fw-min-max", + data->efi_fw_min_max, + ARRAY_SIZE(data->efi_fw_min_max)); + if (!error) + data->efi_fw_min_max_set = true; } fw_size = fw->size / sizeof(*fw_data);
Unfortunately, at the time of writing this commit message, we have been unable to get permission from Silead, or from device OEMs, to distribute the necessary Silead firmware files in linux-firmware. On a whole bunch of devices the UEFI BIOS code contains a touchscreen driver, which contains an embedded copy of the firmware. The fw-loader code has a "platform" fallback mechanism, which together with info on the firmware from drivers/platform/x86/touchscreen_dmi.c will use the firmware from the UEFI driver when the firmware is missing from /lib/firmware. This makes the touchscreen work OOTB without users needing to manually download the firmware. The firmware bundled with the original Windows/Android is usually newer then the firmware in the UEFI driver and it is better calibrated. This better calibration can lead to significant differences in the reported min/max coordinates. Add support for a new (optional) "silead,efi-fw-min-max" property which provides a set of alternative min/max values to use for the x/y axis when the EFI embedded firmware is used. The new property is only used on (x86) devices which do not use devicetree, IOW it is not used in actual devicetree files. The devicetree-bindings maintainers have requested properties like these to not be added to the devicetree-bindings, so the new property is deliberately not added to the existing silead devicetree-bindings documentation. Signed-off-by: Hans de Goede <hdegoede@redhat.com> --- drivers/input/touchscreen/silead.c | 73 ++++++++++++++++++++++++++++-- 1 file changed, 68 insertions(+), 5 deletions(-)