Commit 061d1af7 authored by Linus Torvalds's avatar Linus Torvalds

Merge tag 'for-linus-2024060801' of git://git.kernel.org/pub/scm/linux/kernel/git/hid/hid

Pull HID fixes from Benjamin Tissoires:

 - fix potential read out of bounds in hid-asus (Andrew Ballance)

 - fix endian-conversion on little endian systems in intel-ish-hid (Arnd
   Bergmann)

 - A couple of new input event codes (Aseda Aboagye)

 - errors handling fixes in hid-nvidia-shield (Chen Ni), hid-nintendo
   (Christophe JAILLET), hid-logitech-dj (José Expósito)

 - current leakage fix while the device is in suspend on a i2c-hid
   laptop (Johan Hovold)

 - other assorted smaller fixes and device ID / quirk entry additions

* tag 'for-linus-2024060801' of git://git.kernel.org/pub/scm/linux/kernel/git/hid/hid:
  HID: Ignore battery for ELAN touchscreens 2F2C and 4116
  HID: i2c-hid: elan: fix reset suspend current leakage
  dt-bindings: HID: i2c-hid: elan: add 'no-reset-on-power-off' property
  dt-bindings: HID: i2c-hid: elan: add Elan eKTH5015M
  dt-bindings: HID: i2c-hid: add dedicated Ilitek ILI2901 schema
  input: Add support for "Do Not Disturb"
  input: Add event code for accessibility key
  hid: asus: asus_report_fixup: fix potential read out of bounds
  HID: logitech-hidpp: add missing MODULE_DESCRIPTION() macro
  HID: intel-ish-hid: fix endian-conversion
  HID: nintendo: Fix an error handling path in nintendo_hid_probe()
  HID: logitech-dj: Fix memory leak in logi_dj_recv_switch_to_dj_mode()
  HID: core: remove unnecessary WARN_ON() in implement()
  HID: nvidia-shield: Add missing check for input_ff_create_memless
  HID: intel-ish-hid: Fix build error for COMPILE_TEST
parents 329f70c5 a3a5a37e
...@@ -18,9 +18,12 @@ allOf: ...@@ -18,9 +18,12 @@ allOf:
properties: properties:
compatible: compatible:
enum: oneOf:
- elan,ekth6915 - items:
- ilitek,ili2901 - enum:
- elan,ekth5015m
- const: elan,ekth6915
- const: elan,ekth6915
reg: reg:
const: 0x10 const: 0x10
...@@ -33,6 +36,12 @@ properties: ...@@ -33,6 +36,12 @@ properties:
reset-gpios: reset-gpios:
description: Reset GPIO; not all touchscreens using eKTH6915 hook this up. description: Reset GPIO; not all touchscreens using eKTH6915 hook this up.
no-reset-on-power-off:
type: boolean
description:
Reset line is wired so that it can (and should) be left deasserted when
the power supply is off.
vcc33-supply: vcc33-supply:
description: The 3.3V supply to the touchscreen. description: The 3.3V supply to the touchscreen.
...@@ -58,8 +67,8 @@ examples: ...@@ -58,8 +67,8 @@ examples:
#address-cells = <1>; #address-cells = <1>;
#size-cells = <0>; #size-cells = <0>;
ap_ts: touchscreen@10 { touchscreen@10 {
compatible = "elan,ekth6915"; compatible = "elan,ekth5015m", "elan,ekth6915";
reg = <0x10>; reg = <0x10>;
interrupt-parent = <&tlmm>; interrupt-parent = <&tlmm>;
......
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
%YAML 1.2
---
$id: http://devicetree.org/schemas/input/ilitek,ili2901.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: Ilitek ILI2901 touchscreen controller
maintainers:
- Jiri Kosina <jkosina@suse.com>
description:
Supports the Ilitek ILI2901 touchscreen controller.
This touchscreen controller uses the i2c-hid protocol with a reset GPIO.
allOf:
- $ref: /schemas/input/touchscreen/touchscreen.yaml#
properties:
compatible:
enum:
- ilitek,ili2901
reg:
maxItems: 1
interrupts:
maxItems: 1
panel: true
reset-gpios:
maxItems: 1
vcc33-supply: true
vccio-supply: true
required:
- compatible
- reg
- interrupts
- vcc33-supply
additionalProperties: false
examples:
- |
#include <dt-bindings/gpio/gpio.h>
#include <dt-bindings/interrupt-controller/irq.h>
i2c {
#address-cells = <1>;
#size-cells = <0>;
touchscreen@41 {
compatible = "ilitek,ili2901";
reg = <0x41>;
interrupt-parent = <&tlmm>;
interrupts = <9 IRQ_TYPE_LEVEL_LOW>;
reset-gpios = <&tlmm 8 GPIO_ACTIVE_LOW>;
vcc33-supply = <&pp3300_ts>;
};
};
...@@ -1204,8 +1204,8 @@ static __u8 *asus_report_fixup(struct hid_device *hdev, __u8 *rdesc, ...@@ -1204,8 +1204,8 @@ static __u8 *asus_report_fixup(struct hid_device *hdev, __u8 *rdesc,
} }
/* match many more n-key devices */ /* match many more n-key devices */
if (drvdata->quirks & QUIRK_ROG_NKEY_KEYBOARD) { if (drvdata->quirks & QUIRK_ROG_NKEY_KEYBOARD && *rsize > 15) {
for (int i = 0; i < *rsize + 1; i++) { for (int i = 0; i < *rsize - 15; i++) {
/* offset to the count from 0x5a report part always 14 */ /* offset to the count from 0x5a report part always 14 */
if (rdesc[i] == 0x85 && rdesc[i + 1] == 0x5a && if (rdesc[i] == 0x85 && rdesc[i + 1] == 0x5a &&
rdesc[i + 14] == 0x95 && rdesc[i + 15] == 0x05) { rdesc[i + 14] == 0x95 && rdesc[i + 15] == 0x05) {
......
...@@ -1448,7 +1448,6 @@ static void implement(const struct hid_device *hid, u8 *report, ...@@ -1448,7 +1448,6 @@ static void implement(const struct hid_device *hid, u8 *report,
hid_warn(hid, hid_warn(hid,
"%s() called with too large value %d (n: %d)! (%s)\n", "%s() called with too large value %d (n: %d)! (%s)\n",
__func__, value, n, current->comm); __func__, value, n, current->comm);
WARN_ON(1);
value &= m; value &= m;
} }
} }
......
...@@ -3366,6 +3366,8 @@ static const char *keys[KEY_MAX + 1] = { ...@@ -3366,6 +3366,8 @@ static const char *keys[KEY_MAX + 1] = {
[KEY_CAMERA_ACCESS_ENABLE] = "CameraAccessEnable", [KEY_CAMERA_ACCESS_ENABLE] = "CameraAccessEnable",
[KEY_CAMERA_ACCESS_DISABLE] = "CameraAccessDisable", [KEY_CAMERA_ACCESS_DISABLE] = "CameraAccessDisable",
[KEY_CAMERA_ACCESS_TOGGLE] = "CameraAccessToggle", [KEY_CAMERA_ACCESS_TOGGLE] = "CameraAccessToggle",
[KEY_ACCESSIBILITY] = "Accessibility",
[KEY_DO_NOT_DISTURB] = "DoNotDisturb",
[KEY_DICTATE] = "Dictate", [KEY_DICTATE] = "Dictate",
[KEY_MICMUTE] = "MicrophoneMute", [KEY_MICMUTE] = "MicrophoneMute",
[KEY_BRIGHTNESS_MIN] = "BrightnessMin", [KEY_BRIGHTNESS_MIN] = "BrightnessMin",
......
...@@ -423,6 +423,8 @@ ...@@ -423,6 +423,8 @@
#define I2C_DEVICE_ID_HP_SPECTRE_X360_13_AW0020NG 0x29DF #define I2C_DEVICE_ID_HP_SPECTRE_X360_13_AW0020NG 0x29DF
#define I2C_DEVICE_ID_ASUS_TP420IA_TOUCHSCREEN 0x2BC8 #define I2C_DEVICE_ID_ASUS_TP420IA_TOUCHSCREEN 0x2BC8
#define I2C_DEVICE_ID_ASUS_GV301RA_TOUCHSCREEN 0x2C82 #define I2C_DEVICE_ID_ASUS_GV301RA_TOUCHSCREEN 0x2C82
#define I2C_DEVICE_ID_ASUS_UX3402_TOUCHSCREEN 0x2F2C
#define I2C_DEVICE_ID_ASUS_UX6404_TOUCHSCREEN 0x4116
#define USB_DEVICE_ID_ASUS_UX550VE_TOUCHSCREEN 0x2544 #define USB_DEVICE_ID_ASUS_UX550VE_TOUCHSCREEN 0x2544
#define USB_DEVICE_ID_ASUS_UX550_TOUCHSCREEN 0x2706 #define USB_DEVICE_ID_ASUS_UX550_TOUCHSCREEN 0x2706
#define I2C_DEVICE_ID_SURFACE_GO_TOUCHSCREEN 0x261A #define I2C_DEVICE_ID_SURFACE_GO_TOUCHSCREEN 0x261A
......
...@@ -377,6 +377,10 @@ static const struct hid_device_id hid_battery_quirks[] = { ...@@ -377,6 +377,10 @@ static const struct hid_device_id hid_battery_quirks[] = {
HID_BATTERY_QUIRK_IGNORE }, HID_BATTERY_QUIRK_IGNORE },
{ HID_I2C_DEVICE(USB_VENDOR_ID_ELAN, I2C_DEVICE_ID_ASUS_GV301RA_TOUCHSCREEN), { HID_I2C_DEVICE(USB_VENDOR_ID_ELAN, I2C_DEVICE_ID_ASUS_GV301RA_TOUCHSCREEN),
HID_BATTERY_QUIRK_IGNORE }, HID_BATTERY_QUIRK_IGNORE },
{ HID_I2C_DEVICE(USB_VENDOR_ID_ELAN, I2C_DEVICE_ID_ASUS_UX3402_TOUCHSCREEN),
HID_BATTERY_QUIRK_IGNORE },
{ HID_I2C_DEVICE(USB_VENDOR_ID_ELAN, I2C_DEVICE_ID_ASUS_UX6404_TOUCHSCREEN),
HID_BATTERY_QUIRK_IGNORE },
{ HID_USB_DEVICE(USB_VENDOR_ID_ELAN, USB_DEVICE_ID_ASUS_UX550_TOUCHSCREEN), { HID_USB_DEVICE(USB_VENDOR_ID_ELAN, USB_DEVICE_ID_ASUS_UX550_TOUCHSCREEN),
HID_BATTERY_QUIRK_IGNORE }, HID_BATTERY_QUIRK_IGNORE },
{ HID_USB_DEVICE(USB_VENDOR_ID_ELAN, USB_DEVICE_ID_ASUS_UX550VE_TOUCHSCREEN), { HID_USB_DEVICE(USB_VENDOR_ID_ELAN, USB_DEVICE_ID_ASUS_UX550VE_TOUCHSCREEN),
...@@ -833,9 +837,18 @@ static void hidinput_configure_usage(struct hid_input *hidinput, struct hid_fiel ...@@ -833,9 +837,18 @@ static void hidinput_configure_usage(struct hid_input *hidinput, struct hid_fiel
break; break;
} }
if ((usage->hid & 0xf0) == 0x90) { /* SystemControl*/
switch (usage->hid & 0xf) {
case 0xb: map_key_clear(KEY_DO_NOT_DISTURB); break;
default: goto ignore;
}
break;
}
if ((usage->hid & 0xf0) == 0xa0) { /* SystemControl */ if ((usage->hid & 0xf0) == 0xa0) { /* SystemControl */
switch (usage->hid & 0xf) { switch (usage->hid & 0xf) {
case 0x9: map_key_clear(KEY_MICMUTE); break; case 0x9: map_key_clear(KEY_MICMUTE); break;
case 0xa: map_key_clear(KEY_ACCESSIBILITY); break;
default: goto ignore; default: goto ignore;
} }
break; break;
......
...@@ -1284,9 +1284,11 @@ static int logi_dj_recv_switch_to_dj_mode(struct dj_receiver_dev *djrcv_dev, ...@@ -1284,9 +1284,11 @@ static int logi_dj_recv_switch_to_dj_mode(struct dj_receiver_dev *djrcv_dev,
*/ */
msleep(50); msleep(50);
if (retval) if (retval) {
kfree(dj_report);
return retval; return retval;
} }
}
/* /*
* Magical bits to set up hidpp notifications when the dj devices * Magical bits to set up hidpp notifications when the dj devices
......
...@@ -27,6 +27,7 @@ ...@@ -27,6 +27,7 @@
#include "usbhid/usbhid.h" #include "usbhid/usbhid.h"
#include "hid-ids.h" #include "hid-ids.h"
MODULE_DESCRIPTION("Support for Logitech devices relying on the HID++ specification");
MODULE_LICENSE("GPL"); MODULE_LICENSE("GPL");
MODULE_AUTHOR("Benjamin Tissoires <benjamin.tissoires@gmail.com>"); MODULE_AUTHOR("Benjamin Tissoires <benjamin.tissoires@gmail.com>");
MODULE_AUTHOR("Nestor Lopez Casado <nlopezcasad@logitech.com>"); MODULE_AUTHOR("Nestor Lopez Casado <nlopezcasad@logitech.com>");
......
...@@ -2725,13 +2725,13 @@ static int nintendo_hid_probe(struct hid_device *hdev, ...@@ -2725,13 +2725,13 @@ static int nintendo_hid_probe(struct hid_device *hdev,
ret = joycon_power_supply_create(ctlr); ret = joycon_power_supply_create(ctlr);
if (ret) { if (ret) {
hid_err(hdev, "Failed to create power_supply; ret=%d\n", ret); hid_err(hdev, "Failed to create power_supply; ret=%d\n", ret);
goto err_close; goto err_ida;
} }
ret = joycon_input_create(ctlr); ret = joycon_input_create(ctlr);
if (ret) { if (ret) {
hid_err(hdev, "Failed to create input device; ret=%d\n", ret); hid_err(hdev, "Failed to create input device; ret=%d\n", ret);
goto err_close; goto err_ida;
} }
ctlr->ctlr_state = JOYCON_CTLR_STATE_READ; ctlr->ctlr_state = JOYCON_CTLR_STATE_READ;
...@@ -2739,6 +2739,8 @@ static int nintendo_hid_probe(struct hid_device *hdev, ...@@ -2739,6 +2739,8 @@ static int nintendo_hid_probe(struct hid_device *hdev,
hid_dbg(hdev, "probe - success\n"); hid_dbg(hdev, "probe - success\n");
return 0; return 0;
err_ida:
ida_free(&nintendo_player_id_allocator, ctlr->player_id);
err_close: err_close:
hid_hw_close(hdev); hid_hw_close(hdev);
err_stop: err_stop:
......
...@@ -283,7 +283,9 @@ static struct input_dev *shield_haptics_create( ...@@ -283,7 +283,9 @@ static struct input_dev *shield_haptics_create(
return haptics; return haptics;
input_set_capability(haptics, EV_FF, FF_RUMBLE); input_set_capability(haptics, EV_FF, FF_RUMBLE);
input_ff_create_memless(haptics, NULL, play_effect); ret = input_ff_create_memless(haptics, NULL, play_effect);
if (ret)
goto err;
ret = input_register_device(haptics); ret = input_register_device(haptics);
if (ret) if (ret)
......
...@@ -31,6 +31,7 @@ struct i2c_hid_of_elan { ...@@ -31,6 +31,7 @@ struct i2c_hid_of_elan {
struct regulator *vcc33; struct regulator *vcc33;
struct regulator *vccio; struct regulator *vccio;
struct gpio_desc *reset_gpio; struct gpio_desc *reset_gpio;
bool no_reset_on_power_off;
const struct elan_i2c_hid_chip_data *chip_data; const struct elan_i2c_hid_chip_data *chip_data;
}; };
...@@ -40,17 +41,17 @@ static int elan_i2c_hid_power_up(struct i2chid_ops *ops) ...@@ -40,17 +41,17 @@ static int elan_i2c_hid_power_up(struct i2chid_ops *ops)
container_of(ops, struct i2c_hid_of_elan, ops); container_of(ops, struct i2c_hid_of_elan, ops);
int ret; int ret;
gpiod_set_value_cansleep(ihid_elan->reset_gpio, 1);
if (ihid_elan->vcc33) { if (ihid_elan->vcc33) {
ret = regulator_enable(ihid_elan->vcc33); ret = regulator_enable(ihid_elan->vcc33);
if (ret) if (ret)
return ret; goto err_deassert_reset;
} }
ret = regulator_enable(ihid_elan->vccio); ret = regulator_enable(ihid_elan->vccio);
if (ret) { if (ret)
regulator_disable(ihid_elan->vcc33); goto err_disable_vcc33;
return ret;
}
if (ihid_elan->chip_data->post_power_delay_ms) if (ihid_elan->chip_data->post_power_delay_ms)
msleep(ihid_elan->chip_data->post_power_delay_ms); msleep(ihid_elan->chip_data->post_power_delay_ms);
...@@ -60,6 +61,15 @@ static int elan_i2c_hid_power_up(struct i2chid_ops *ops) ...@@ -60,6 +61,15 @@ static int elan_i2c_hid_power_up(struct i2chid_ops *ops)
msleep(ihid_elan->chip_data->post_gpio_reset_on_delay_ms); msleep(ihid_elan->chip_data->post_gpio_reset_on_delay_ms);
return 0; return 0;
err_disable_vcc33:
if (ihid_elan->vcc33)
regulator_disable(ihid_elan->vcc33);
err_deassert_reset:
if (ihid_elan->no_reset_on_power_off)
gpiod_set_value_cansleep(ihid_elan->reset_gpio, 0);
return ret;
} }
static void elan_i2c_hid_power_down(struct i2chid_ops *ops) static void elan_i2c_hid_power_down(struct i2chid_ops *ops)
...@@ -67,7 +77,14 @@ static void elan_i2c_hid_power_down(struct i2chid_ops *ops) ...@@ -67,7 +77,14 @@ static void elan_i2c_hid_power_down(struct i2chid_ops *ops)
struct i2c_hid_of_elan *ihid_elan = struct i2c_hid_of_elan *ihid_elan =
container_of(ops, struct i2c_hid_of_elan, ops); container_of(ops, struct i2c_hid_of_elan, ops);
/*
* Do not assert reset when the hardware allows for it to remain
* deasserted regardless of the state of the (shared) power supply to
* avoid wasting power when the supply is left on.
*/
if (!ihid_elan->no_reset_on_power_off)
gpiod_set_value_cansleep(ihid_elan->reset_gpio, 1); gpiod_set_value_cansleep(ihid_elan->reset_gpio, 1);
if (ihid_elan->chip_data->post_gpio_reset_off_delay_ms) if (ihid_elan->chip_data->post_gpio_reset_off_delay_ms)
msleep(ihid_elan->chip_data->post_gpio_reset_off_delay_ms); msleep(ihid_elan->chip_data->post_gpio_reset_off_delay_ms);
...@@ -79,6 +96,7 @@ static void elan_i2c_hid_power_down(struct i2chid_ops *ops) ...@@ -79,6 +96,7 @@ static void elan_i2c_hid_power_down(struct i2chid_ops *ops)
static int i2c_hid_of_elan_probe(struct i2c_client *client) static int i2c_hid_of_elan_probe(struct i2c_client *client)
{ {
struct i2c_hid_of_elan *ihid_elan; struct i2c_hid_of_elan *ihid_elan;
int ret;
ihid_elan = devm_kzalloc(&client->dev, sizeof(*ihid_elan), GFP_KERNEL); ihid_elan = devm_kzalloc(&client->dev, sizeof(*ihid_elan), GFP_KERNEL);
if (!ihid_elan) if (!ihid_elan)
...@@ -93,21 +111,38 @@ static int i2c_hid_of_elan_probe(struct i2c_client *client) ...@@ -93,21 +111,38 @@ static int i2c_hid_of_elan_probe(struct i2c_client *client)
if (IS_ERR(ihid_elan->reset_gpio)) if (IS_ERR(ihid_elan->reset_gpio))
return PTR_ERR(ihid_elan->reset_gpio); return PTR_ERR(ihid_elan->reset_gpio);
ihid_elan->no_reset_on_power_off = of_property_read_bool(client->dev.of_node,
"no-reset-on-power-off");
ihid_elan->vccio = devm_regulator_get(&client->dev, "vccio"); ihid_elan->vccio = devm_regulator_get(&client->dev, "vccio");
if (IS_ERR(ihid_elan->vccio)) if (IS_ERR(ihid_elan->vccio)) {
return PTR_ERR(ihid_elan->vccio); ret = PTR_ERR(ihid_elan->vccio);
goto err_deassert_reset;
}
ihid_elan->chip_data = device_get_match_data(&client->dev); ihid_elan->chip_data = device_get_match_data(&client->dev);
if (ihid_elan->chip_data->main_supply_name) { if (ihid_elan->chip_data->main_supply_name) {
ihid_elan->vcc33 = devm_regulator_get(&client->dev, ihid_elan->vcc33 = devm_regulator_get(&client->dev,
ihid_elan->chip_data->main_supply_name); ihid_elan->chip_data->main_supply_name);
if (IS_ERR(ihid_elan->vcc33)) if (IS_ERR(ihid_elan->vcc33)) {
return PTR_ERR(ihid_elan->vcc33); ret = PTR_ERR(ihid_elan->vcc33);
goto err_deassert_reset;
}
} }
return i2c_hid_core_probe(client, &ihid_elan->ops, ret = i2c_hid_core_probe(client, &ihid_elan->ops,
ihid_elan->chip_data->hid_descriptor_address, 0); ihid_elan->chip_data->hid_descriptor_address, 0);
if (ret)
goto err_deassert_reset;
return 0;
err_deassert_reset:
if (ihid_elan->no_reset_on_power_off)
gpiod_set_value_cansleep(ihid_elan->reset_gpio, 0);
return ret;
} }
static const struct elan_i2c_hid_chip_data elan_ekth6915_chip_data = { static const struct elan_i2c_hid_chip_data elan_ekth6915_chip_data = {
......
...@@ -84,8 +84,8 @@ static int loader_write_message(struct ishtp_device *dev, void *buf, int len) ...@@ -84,8 +84,8 @@ static int loader_write_message(struct ishtp_device *dev, void *buf, int len)
static int loader_xfer_cmd(struct ishtp_device *dev, void *req, int req_len, static int loader_xfer_cmd(struct ishtp_device *dev, void *req, int req_len,
void *resp, int resp_len) void *resp, int resp_len)
{ {
struct loader_msg_header *req_hdr = req; union loader_msg_header req_hdr;
struct loader_msg_header *resp_hdr = resp; union loader_msg_header resp_hdr;
struct device *devc = dev->devc; struct device *devc = dev->devc;
int rv; int rv;
...@@ -93,34 +93,37 @@ static int loader_xfer_cmd(struct ishtp_device *dev, void *req, int req_len, ...@@ -93,34 +93,37 @@ static int loader_xfer_cmd(struct ishtp_device *dev, void *req, int req_len,
dev->fw_loader_rx_size = resp_len; dev->fw_loader_rx_size = resp_len;
rv = loader_write_message(dev, req, req_len); rv = loader_write_message(dev, req, req_len);
req_hdr.val32 = le32_to_cpup(req);
if (rv < 0) { if (rv < 0) {
dev_err(devc, "write cmd %u failed:%d\n", req_hdr->command, rv); dev_err(devc, "write cmd %u failed:%d\n", req_hdr.command, rv);
return rv; return rv;
} }
/* Wait the ACK */ /* Wait the ACK */
wait_event_interruptible_timeout(dev->wait_loader_recvd_msg, dev->fw_loader_received, wait_event_interruptible_timeout(dev->wait_loader_recvd_msg, dev->fw_loader_received,
ISHTP_LOADER_TIMEOUT); ISHTP_LOADER_TIMEOUT);
resp_hdr.val32 = le32_to_cpup(resp);
dev->fw_loader_rx_size = 0; dev->fw_loader_rx_size = 0;
dev->fw_loader_rx_buf = NULL; dev->fw_loader_rx_buf = NULL;
if (!dev->fw_loader_received) { if (!dev->fw_loader_received) {
dev_err(devc, "wait response of cmd %u timeout\n", req_hdr->command); dev_err(devc, "wait response of cmd %u timeout\n", req_hdr.command);
return -ETIMEDOUT; return -ETIMEDOUT;
} }
if (!resp_hdr->is_response) { if (!resp_hdr.is_response) {
dev_err(devc, "not a response for %u\n", req_hdr->command); dev_err(devc, "not a response for %u\n", req_hdr.command);
return -EBADMSG; return -EBADMSG;
} }
if (req_hdr->command != resp_hdr->command) { if (req_hdr.command != resp_hdr.command) {
dev_err(devc, "unexpected cmd response %u:%u\n", req_hdr->command, dev_err(devc, "unexpected cmd response %u:%u\n", req_hdr.command,
resp_hdr->command); resp_hdr.command);
return -EBADMSG; return -EBADMSG;
} }
if (resp_hdr->status) { if (resp_hdr.status) {
dev_err(devc, "cmd %u failed %u\n", req_hdr->command, resp_hdr->status); dev_err(devc, "cmd %u failed %u\n", req_hdr.command, resp_hdr.status);
return -EIO; return -EIO;
} }
...@@ -138,12 +141,13 @@ static void release_dma_bufs(struct ishtp_device *dev, ...@@ -138,12 +141,13 @@ static void release_dma_bufs(struct ishtp_device *dev,
struct loader_xfer_dma_fragment *fragment, struct loader_xfer_dma_fragment *fragment,
void **dma_bufs, u32 fragment_size) void **dma_bufs, u32 fragment_size)
{ {
dma_addr_t dma_addr;
int i; int i;
for (i = 0; i < FRAGMENT_MAX_NUM; i++) { for (i = 0; i < FRAGMENT_MAX_NUM; i++) {
if (dma_bufs[i]) { if (dma_bufs[i]) {
dma_free_coherent(dev->devc, fragment_size, dma_bufs[i], dma_addr = le64_to_cpu(fragment->fragment_tbl[i].ddr_adrs);
fragment->fragment_tbl[i].ddr_adrs); dma_free_coherent(dev->devc, fragment_size, dma_bufs[i], dma_addr);
dma_bufs[i] = NULL; dma_bufs[i] = NULL;
} }
} }
...@@ -156,29 +160,33 @@ static void release_dma_bufs(struct ishtp_device *dev, ...@@ -156,29 +160,33 @@ static void release_dma_bufs(struct ishtp_device *dev,
* @fragment: The ISHTP firmware fragment descriptor * @fragment: The ISHTP firmware fragment descriptor
* @dma_bufs: The array of DMA fragment buffers * @dma_bufs: The array of DMA fragment buffers
* @fragment_size: The size of a single DMA fragment * @fragment_size: The size of a single DMA fragment
* @fragment_count: Number of fragments
* *
* Return: 0 on success, negative error code on failure * Return: 0 on success, negative error code on failure
*/ */
static int prepare_dma_bufs(struct ishtp_device *dev, static int prepare_dma_bufs(struct ishtp_device *dev,
const struct firmware *ish_fw, const struct firmware *ish_fw,
struct loader_xfer_dma_fragment *fragment, struct loader_xfer_dma_fragment *fragment,
void **dma_bufs, u32 fragment_size) void **dma_bufs, u32 fragment_size, u32 fragment_count)
{ {
dma_addr_t dma_addr;
u32 offset = 0; u32 offset = 0;
u32 length;
int i; int i;
for (i = 0; i < fragment->fragment_cnt && offset < ish_fw->size; i++) { for (i = 0; i < fragment_count && offset < ish_fw->size; i++) {
dma_bufs[i] = dma_alloc_coherent(dev->devc, fragment_size, dma_bufs[i] = dma_alloc_coherent(dev->devc, fragment_size, &dma_addr, GFP_KERNEL);
&fragment->fragment_tbl[i].ddr_adrs, GFP_KERNEL);
if (!dma_bufs[i]) if (!dma_bufs[i])
return -ENOMEM; return -ENOMEM;
fragment->fragment_tbl[i].length = clamp(ish_fw->size - offset, 0, fragment_size); fragment->fragment_tbl[i].ddr_adrs = cpu_to_le64(dma_addr);
fragment->fragment_tbl[i].fw_off = offset; length = clamp(ish_fw->size - offset, 0, fragment_size);
memcpy(dma_bufs[i], ish_fw->data + offset, fragment->fragment_tbl[i].length); fragment->fragment_tbl[i].length = cpu_to_le32(length);
fragment->fragment_tbl[i].fw_off = cpu_to_le32(offset);
memcpy(dma_bufs[i], ish_fw->data + offset, length);
clflush_cache_range(dma_bufs[i], fragment_size); clflush_cache_range(dma_bufs[i], fragment_size);
offset += fragment->fragment_tbl[i].length; offset += length;
} }
return 0; return 0;
...@@ -206,17 +214,17 @@ void ishtp_loader_work(struct work_struct *work) ...@@ -206,17 +214,17 @@ void ishtp_loader_work(struct work_struct *work)
{ {
DEFINE_RAW_FLEX(struct loader_xfer_dma_fragment, fragment, fragment_tbl, FRAGMENT_MAX_NUM); DEFINE_RAW_FLEX(struct loader_xfer_dma_fragment, fragment, fragment_tbl, FRAGMENT_MAX_NUM);
struct ishtp_device *dev = container_of(work, struct ishtp_device, work_fw_loader); struct ishtp_device *dev = container_of(work, struct ishtp_device, work_fw_loader);
struct loader_xfer_query query = { union loader_msg_header query_hdr = { .command = LOADER_CMD_XFER_QUERY, };
.header.command = LOADER_CMD_XFER_QUERY, union loader_msg_header start_hdr = { .command = LOADER_CMD_START, };
}; union loader_msg_header fragment_hdr = { .command = LOADER_CMD_XFER_FRAGMENT, };
struct loader_start start = { struct loader_xfer_query query = { .header = cpu_to_le32(query_hdr.val32), };
.header.command = LOADER_CMD_START, struct loader_start start = { .header = cpu_to_le32(start_hdr.val32), };
};
union loader_recv_message recv_msg; union loader_recv_message recv_msg;
char *filename = dev->driver_data->fw_filename; char *filename = dev->driver_data->fw_filename;
const struct firmware *ish_fw; const struct firmware *ish_fw;
void *dma_bufs[FRAGMENT_MAX_NUM] = {}; void *dma_bufs[FRAGMENT_MAX_NUM] = {};
u32 fragment_size; u32 fragment_size;
u32 fragment_count;
int retry = ISHTP_LOADER_RETRY_TIMES; int retry = ISHTP_LOADER_RETRY_TIMES;
int rv; int rv;
...@@ -226,23 +234,24 @@ void ishtp_loader_work(struct work_struct *work) ...@@ -226,23 +234,24 @@ void ishtp_loader_work(struct work_struct *work)
return; return;
} }
fragment->fragment.header.command = LOADER_CMD_XFER_FRAGMENT; fragment->fragment.header = cpu_to_le32(fragment_hdr.val32);
fragment->fragment.xfer_mode = LOADER_XFER_MODE_DMA; fragment->fragment.xfer_mode = cpu_to_le32(LOADER_XFER_MODE_DMA);
fragment->fragment.is_last = 1; fragment->fragment.is_last = cpu_to_le32(1);
fragment->fragment.size = ish_fw->size; fragment->fragment.size = cpu_to_le32(ish_fw->size);
/* Calculate the size of a single DMA fragment */ /* Calculate the size of a single DMA fragment */
fragment_size = PFN_ALIGN(DIV_ROUND_UP(ish_fw->size, FRAGMENT_MAX_NUM)); fragment_size = PFN_ALIGN(DIV_ROUND_UP(ish_fw->size, FRAGMENT_MAX_NUM));
/* Calculate the count of DMA fragments */ /* Calculate the count of DMA fragments */
fragment->fragment_cnt = DIV_ROUND_UP(ish_fw->size, fragment_size); fragment_count = DIV_ROUND_UP(ish_fw->size, fragment_size);
fragment->fragment_cnt = cpu_to_le32(fragment_count);
rv = prepare_dma_bufs(dev, ish_fw, fragment, dma_bufs, fragment_size); rv = prepare_dma_bufs(dev, ish_fw, fragment, dma_bufs, fragment_size, fragment_count);
if (rv) { if (rv) {
dev_err(dev->devc, "prepare DMA buffer failed.\n"); dev_err(dev->devc, "prepare DMA buffer failed.\n");
goto out; goto out;
} }
do { do {
query.image_size = ish_fw->size; query.image_size = cpu_to_le32(ish_fw->size);
rv = loader_xfer_cmd(dev, &query, sizeof(query), recv_msg.raw_data, rv = loader_xfer_cmd(dev, &query, sizeof(query), recv_msg.raw_data,
sizeof(struct loader_xfer_query_ack)); sizeof(struct loader_xfer_query_ack));
if (rv) if (rv)
...@@ -255,7 +264,7 @@ void ishtp_loader_work(struct work_struct *work) ...@@ -255,7 +264,7 @@ void ishtp_loader_work(struct work_struct *work)
recv_msg.query_ack.version_build); recv_msg.query_ack.version_build);
rv = loader_xfer_cmd(dev, fragment, rv = loader_xfer_cmd(dev, fragment,
struct_size(fragment, fragment_tbl, fragment->fragment_cnt), struct_size(fragment, fragment_tbl, fragment_count),
recv_msg.raw_data, sizeof(struct loader_xfer_fragment_ack)); recv_msg.raw_data, sizeof(struct loader_xfer_fragment_ack));
if (rv) if (rv)
continue; /* try again if failed */ continue; /* try again if failed */
......
...@@ -30,19 +30,23 @@ struct work_struct; ...@@ -30,19 +30,23 @@ struct work_struct;
#define LOADER_XFER_MODE_DMA BIT(0) #define LOADER_XFER_MODE_DMA BIT(0)
/** /**
* struct loader_msg_header - ISHTP firmware loader message header * union loader_msg_header - ISHTP firmware loader message header
* @command: Command type * @command: Command type
* @is_response: Indicates if the message is a response * @is_response: Indicates if the message is a response
* @has_next: Indicates if there is a next message * @has_next: Indicates if there is a next message
* @reserved: Reserved for future use * @reserved: Reserved for future use
* @status: Status of the message * @status: Status of the message
* @val32: entire header as a 32-bit value
*/ */
struct loader_msg_header { union loader_msg_header {
__le32 command:7; struct {
__le32 is_response:1; __u32 command:7;
__le32 has_next:1; __u32 is_response:1;
__le32 reserved:15; __u32 has_next:1;
__le32 status:8; __u32 reserved:15;
__u32 status:8;
};
__u32 val32;
}; };
/** /**
...@@ -51,7 +55,7 @@ struct loader_msg_header { ...@@ -51,7 +55,7 @@ struct loader_msg_header {
* @image_size: Size of the image * @image_size: Size of the image
*/ */
struct loader_xfer_query { struct loader_xfer_query {
struct loader_msg_header header; __le32 header;
__le32 image_size; __le32 image_size;
}; };
...@@ -103,7 +107,7 @@ struct loader_capability { ...@@ -103,7 +107,7 @@ struct loader_capability {
* @capability: Loader capability * @capability: Loader capability
*/ */
struct loader_xfer_query_ack { struct loader_xfer_query_ack {
struct loader_msg_header header; __le32 header;
__le16 version_major; __le16 version_major;
__le16 version_minor; __le16 version_minor;
__le16 version_hotfix; __le16 version_hotfix;
...@@ -122,7 +126,7 @@ struct loader_xfer_query_ack { ...@@ -122,7 +126,7 @@ struct loader_xfer_query_ack {
* @is_last: Is last * @is_last: Is last
*/ */
struct loader_xfer_fragment { struct loader_xfer_fragment {
struct loader_msg_header header; __le32 header;
__le32 xfer_mode; __le32 xfer_mode;
__le32 offset; __le32 offset;
__le32 size; __le32 size;
...@@ -134,7 +138,7 @@ struct loader_xfer_fragment { ...@@ -134,7 +138,7 @@ struct loader_xfer_fragment {
* @header: Header of the message * @header: Header of the message
*/ */
struct loader_xfer_fragment_ack { struct loader_xfer_fragment_ack {
struct loader_msg_header header; __le32 header;
}; };
/** /**
...@@ -170,7 +174,7 @@ struct loader_xfer_dma_fragment { ...@@ -170,7 +174,7 @@ struct loader_xfer_dma_fragment {
* @header: Header of the message * @header: Header of the message
*/ */
struct loader_start { struct loader_start {
struct loader_msg_header header; __le32 header;
}; };
/** /**
...@@ -178,10 +182,11 @@ struct loader_start { ...@@ -178,10 +182,11 @@ struct loader_start {
* @header: Header of the message * @header: Header of the message
*/ */
struct loader_start_ack { struct loader_start_ack {
struct loader_msg_header header; __le32 header;
}; };
union loader_recv_message { union loader_recv_message {
__le32 header;
struct loader_xfer_query_ack query_ack; struct loader_xfer_query_ack query_ack;
struct loader_xfer_fragment_ack fragment_ack; struct loader_xfer_fragment_ack fragment_ack;
struct loader_start_ack start_ack; struct loader_start_ack start_ack;
......
...@@ -618,6 +618,8 @@ ...@@ -618,6 +618,8 @@
#define KEY_CAMERA_ACCESS_ENABLE 0x24b /* Enables programmatic access to camera devices. (HUTRR72) */ #define KEY_CAMERA_ACCESS_ENABLE 0x24b /* Enables programmatic access to camera devices. (HUTRR72) */
#define KEY_CAMERA_ACCESS_DISABLE 0x24c /* Disables programmatic access to camera devices. (HUTRR72) */ #define KEY_CAMERA_ACCESS_DISABLE 0x24c /* Disables programmatic access to camera devices. (HUTRR72) */
#define KEY_CAMERA_ACCESS_TOGGLE 0x24d /* Toggles the current state of the camera access control. (HUTRR72) */ #define KEY_CAMERA_ACCESS_TOGGLE 0x24d /* Toggles the current state of the camera access control. (HUTRR72) */
#define KEY_ACCESSIBILITY 0x24e /* Toggles the system bound accessibility UI/command (HUTRR116) */
#define KEY_DO_NOT_DISTURB 0x24f /* Toggles the system-wide "Do Not Disturb" control (HUTRR94)*/
#define KEY_BRIGHTNESS_MIN 0x250 /* Set Brightness to Minimum */ #define KEY_BRIGHTNESS_MIN 0x250 /* Set Brightness to Minimum */
#define KEY_BRIGHTNESS_MAX 0x251 /* Set Brightness to Maximum */ #define KEY_BRIGHTNESS_MAX 0x251 /* Set Brightness to Maximum */
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment