summaryrefslogtreecommitdiff
path: root/drivers/hid
diff options
context:
space:
mode:
authorJiri Kosina <jkosina@suse.com>2026-02-09 19:36:30 +0300
committerJiri Kosina <jkosina@suse.com>2026-02-09 19:36:30 +0300
commit9f2975f1bff07dc68e26f2f3ddc5c48047b2f5c3 (patch)
tree59a46f24e02264a1e65a1aa79dda50f12c2b5365 /drivers/hid
parent05bc4583b89122dadd099ad15d275465b37af9a4 (diff)
parent6761f9ccd6247682430413274e7af955d39cfee4 (diff)
downloadlinux-9f2975f1bff07dc68e26f2f3ddc5c48047b2f5c3.tar.xz
Merge branch 'for-6.20/intel-thc' into for-linus
- support for HID output reports in the Quicki2c (Even Xu)
Diffstat (limited to 'drivers/hid')
-rw-r--r--drivers/hid/intel-thc-hid/intel-quicki2c/pci-quicki2c.c14
-rw-r--r--drivers/hid/intel-thc-hid/intel-quicki2c/quicki2c-dev.h2
-rw-r--r--drivers/hid/intel-thc-hid/intel-quicki2c/quicki2c-hid.c8
-rw-r--r--drivers/hid/intel-thc-hid/intel-quicki2c/quicki2c-protocol.c95
-rw-r--r--drivers/hid/intel-thc-hid/intel-quicki2c/quicki2c-protocol.h5
5 files changed, 81 insertions, 43 deletions
diff --git a/drivers/hid/intel-thc-hid/intel-quicki2c/pci-quicki2c.c b/drivers/hid/intel-thc-hid/intel-quicki2c/pci-quicki2c.c
index cfda66ee4895..f178017352ba 100644
--- a/drivers/hid/intel-thc-hid/intel-quicki2c/pci-quicki2c.c
+++ b/drivers/hid/intel-thc-hid/intel-quicki2c/pci-quicki2c.c
@@ -807,6 +807,12 @@ static int quicki2c_suspend(struct device *device)
if (!qcdev)
return -ENODEV;
+ if (!device_may_wakeup(qcdev->dev)) {
+ ret = quicki2c_set_power(qcdev, HIDI2C_SLEEP);
+ if (ret)
+ return ret;
+ }
+
/*
* As I2C is THC subsystem, no register auto save/restore support,
* need driver to do that explicitly for every D3 case.
@@ -856,6 +862,9 @@ static int quicki2c_resume(struct device *device)
if (ret)
return ret;
+ if (!device_may_wakeup(qcdev->dev))
+ return quicki2c_set_power(qcdev, HIDI2C_ON);
+
return 0;
}
@@ -913,6 +922,9 @@ static int quicki2c_poweroff(struct device *device)
if (!qcdev)
return -ENODEV;
+ /* Ignore the return value as platform will be poweroff soon */
+ quicki2c_set_power(qcdev, HIDI2C_SLEEP);
+
ret = thc_interrupt_quiesce(qcdev->thc_hw, true);
if (ret)
return ret;
@@ -966,7 +978,7 @@ static int quicki2c_restore(struct device *device)
thc_change_ltr_mode(qcdev->thc_hw, THC_LTR_MODE_ACTIVE);
- return 0;
+ return quicki2c_set_power(qcdev, HIDI2C_ON);
}
static int quicki2c_runtime_suspend(struct device *device)
diff --git a/drivers/hid/intel-thc-hid/intel-quicki2c/quicki2c-dev.h b/drivers/hid/intel-thc-hid/intel-quicki2c/quicki2c-dev.h
index 2cb5471a8133..33a1e3db1cb2 100644
--- a/drivers/hid/intel-thc-hid/intel-quicki2c/quicki2c-dev.h
+++ b/drivers/hid/intel-thc-hid/intel-quicki2c/quicki2c-dev.h
@@ -213,7 +213,7 @@ struct quicki2c_device {
u8 *report_descriptor;
u8 *input_buf;
u8 *report_buf;
- u32 report_len;
+ size_t report_len;
wait_queue_head_t reset_ack_wq;
bool reset_ack;
diff --git a/drivers/hid/intel-thc-hid/intel-quicki2c/quicki2c-hid.c b/drivers/hid/intel-thc-hid/intel-quicki2c/quicki2c-hid.c
index 834a537b6780..f9fcb398673b 100644
--- a/drivers/hid/intel-thc-hid/intel-quicki2c/quicki2c-hid.c
+++ b/drivers/hid/intel-thc-hid/intel-quicki2c/quicki2c-hid.c
@@ -82,6 +82,13 @@ static int quicki2c_hid_power(struct hid_device *hid, int lvl)
return 0;
}
+static int quicki2c_hid_output_report(struct hid_device *hid, u8 *buf, size_t count)
+{
+ struct quicki2c_device *qcdev = hid->driver_data;
+
+ return quicki2c_output_report(qcdev, buf, count);
+}
+
static struct hid_ll_driver quicki2c_hid_ll_driver = {
.parse = quicki2c_hid_parse,
.start = quicki2c_hid_start,
@@ -90,6 +97,7 @@ static struct hid_ll_driver quicki2c_hid_ll_driver = {
.close = quicki2c_hid_close,
.power = quicki2c_hid_power,
.raw_request = quicki2c_hid_raw_request,
+ .output_report = quicki2c_hid_output_report,
};
/**
diff --git a/drivers/hid/intel-thc-hid/intel-quicki2c/quicki2c-protocol.c b/drivers/hid/intel-thc-hid/intel-quicki2c/quicki2c-protocol.c
index a63f8c833252..41271301215a 100644
--- a/drivers/hid/intel-thc-hid/intel-quicki2c/quicki2c-protocol.c
+++ b/drivers/hid/intel-thc-hid/intel-quicki2c/quicki2c-protocol.c
@@ -13,11 +13,11 @@
#include "quicki2c-hid.h"
#include "quicki2c-protocol.h"
-static int quicki2c_init_write_buf(struct quicki2c_device *qcdev, u32 cmd, int cmd_len,
- bool append_data_reg, u8 *data, int data_len,
- u8 *write_buf, int write_buf_len)
+static ssize_t quicki2c_init_write_buf(struct quicki2c_device *qcdev, u32 cmd, size_t cmd_len,
+ bool append_data_reg, u8 *data, size_t data_len,
+ u8 *write_buf, size_t write_buf_len)
{
- int buf_len, offset = 0;
+ size_t buf_len, offset = 0;
buf_len = HIDI2C_REG_LEN + cmd_len;
@@ -30,20 +30,23 @@ static int quicki2c_init_write_buf(struct quicki2c_device *qcdev, u32 cmd, int c
if (buf_len > write_buf_len)
return -EINVAL;
- memcpy(write_buf, &qcdev->dev_desc.cmd_reg, HIDI2C_REG_LEN);
- offset += HIDI2C_REG_LEN;
- memcpy(write_buf + offset, &cmd, cmd_len);
- offset += cmd_len;
+ if (cmd_len) {
+ memcpy(write_buf, &qcdev->dev_desc.cmd_reg, HIDI2C_REG_LEN);
+ offset += HIDI2C_REG_LEN;
+ memcpy(write_buf + offset, &cmd, cmd_len);
+ offset += cmd_len;
- if (append_data_reg) {
- memcpy(write_buf + offset, &qcdev->dev_desc.data_reg, HIDI2C_REG_LEN);
+ if (append_data_reg) {
+ memcpy(write_buf + offset, &qcdev->dev_desc.data_reg, HIDI2C_REG_LEN);
+ offset += HIDI2C_REG_LEN;
+ }
+ } else {
+ memcpy(write_buf, &qcdev->dev_desc.output_reg, HIDI2C_REG_LEN);
offset += HIDI2C_REG_LEN;
}
if (data && data_len) {
- __le16 len = cpu_to_le16(data_len + HIDI2C_LENGTH_LEN);
-
- memcpy(write_buf + offset, &len, HIDI2C_LENGTH_LEN);
+ put_unaligned_le16(data_len + HIDI2C_LENGTH_LEN, write_buf + offset);
offset += HIDI2C_LENGTH_LEN;
memcpy(write_buf + offset, data, data_len);
}
@@ -51,10 +54,10 @@ static int quicki2c_init_write_buf(struct quicki2c_device *qcdev, u32 cmd, int c
return buf_len;
}
-static int quicki2c_encode_cmd(struct quicki2c_device *qcdev, u32 *cmd_buf,
- u8 opcode, u8 report_type, u8 report_id)
+static size_t quicki2c_encode_cmd(struct quicki2c_device *qcdev, u32 *cmd_buf,
+ u8 opcode, u8 report_type, u8 report_id)
{
- int cmd_len;
+ size_t cmd_len;
*cmd_buf = FIELD_PREP(HIDI2C_CMD_OPCODE, opcode) |
FIELD_PREP(HIDI2C_CMD_REPORT_TYPE, report_type);
@@ -72,22 +75,20 @@ static int quicki2c_encode_cmd(struct quicki2c_device *qcdev, u32 *cmd_buf,
}
static int write_cmd_to_txdma(struct quicki2c_device *qcdev, int opcode,
- int report_type, int report_id, u8 *buf, int buf_len)
+ int report_type, int report_id, u8 *buf, size_t buf_len)
{
- size_t write_buf_len;
- int cmd_len, ret;
+ size_t cmd_len;
+ ssize_t len;
u32 cmd;
cmd_len = quicki2c_encode_cmd(qcdev, &cmd, opcode, report_type, report_id);
- ret = quicki2c_init_write_buf(qcdev, cmd, cmd_len, buf ? true : false, buf,
+ len = quicki2c_init_write_buf(qcdev, cmd, cmd_len, buf ? true : false, buf,
buf_len, qcdev->report_buf, qcdev->report_len);
- if (ret < 0)
- return ret;
-
- write_buf_len = ret;
+ if (len < 0)
+ return len;
- return thc_dma_write(qcdev->thc_hw, qcdev->report_buf, write_buf_len);
+ return thc_dma_write(qcdev->thc_hw, qcdev->report_buf, len);
}
int quicki2c_set_power(struct quicki2c_device *qcdev, enum hidi2c_power_state power_state)
@@ -126,13 +127,13 @@ int quicki2c_get_report_descriptor(struct quicki2c_device *qcdev)
}
int quicki2c_get_report(struct quicki2c_device *qcdev, u8 report_type,
- unsigned int reportnum, void *buf, u32 buf_len)
+ unsigned int reportnum, void *buf, size_t buf_len)
{
struct hidi2c_report_packet *rpt;
- size_t write_buf_len, read_len = 0;
- int cmd_len, rep_type;
+ size_t cmd_len, read_len = 0;
+ int rep_type, ret;
+ ssize_t len;
u32 cmd;
- int ret;
if (report_type == HID_INPUT_REPORT) {
rep_type = HIDI2C_INPUT;
@@ -145,25 +146,22 @@ int quicki2c_get_report(struct quicki2c_device *qcdev, u8 report_type,
cmd_len = quicki2c_encode_cmd(qcdev, &cmd, HIDI2C_GET_REPORT, rep_type, reportnum);
- ret = quicki2c_init_write_buf(qcdev, cmd, cmd_len, true, NULL, 0,
+ len = quicki2c_init_write_buf(qcdev, cmd, cmd_len, true, NULL, 0,
qcdev->report_buf, qcdev->report_len);
- if (ret < 0)
- return ret;
-
- write_buf_len = ret;
+ if (len < 0)
+ return len;
rpt = (struct hidi2c_report_packet *)qcdev->input_buf;
- ret = thc_swdma_read(qcdev->thc_hw, qcdev->report_buf, write_buf_len,
- NULL, rpt, &read_len);
+ ret = thc_swdma_read(qcdev->thc_hw, qcdev->report_buf, len, NULL, rpt, &read_len);
if (ret) {
- dev_err_once(qcdev->dev, "Get report failed, ret %d, read len (%zu vs %d)\n",
+ dev_err_once(qcdev->dev, "Get report failed, ret %d, read len (%zu vs %zu)\n",
ret, read_len, buf_len);
return ret;
}
if (HIDI2C_DATA_LEN(le16_to_cpu(rpt->len)) != buf_len || rpt->data[0] != reportnum) {
- dev_err_once(qcdev->dev, "Invalid packet, len (%d vs %d) report id (%d vs %d)\n",
+ dev_err_once(qcdev->dev, "Invalid packet, len (%d vs %zu) report id (%d vs %d)\n",
le16_to_cpu(rpt->len), buf_len, rpt->data[0], reportnum);
return -EINVAL;
}
@@ -174,7 +172,7 @@ int quicki2c_get_report(struct quicki2c_device *qcdev, u8 report_type,
}
int quicki2c_set_report(struct quicki2c_device *qcdev, u8 report_type,
- unsigned int reportnum, void *buf, u32 buf_len)
+ unsigned int reportnum, void *buf, size_t buf_len)
{
int rep_type;
int ret;
@@ -197,6 +195,25 @@ int quicki2c_set_report(struct quicki2c_device *qcdev, u8 report_type,
return buf_len;
}
+int quicki2c_output_report(struct quicki2c_device *qcdev, void *buf, size_t buf_len)
+{
+ ssize_t len;
+ int ret;
+
+ len = quicki2c_init_write_buf(qcdev, 0, 0, false, buf, buf_len,
+ qcdev->report_buf, qcdev->report_len);
+ if (len < 0)
+ return -EINVAL;
+
+ ret = thc_dma_write(qcdev->thc_hw, qcdev->report_buf, len);
+ if (ret) {
+ dev_err(qcdev->dev, "Output Report failed, ret %d\n", ret);
+ return ret;
+ }
+
+ return buf_len;
+}
+
#define HIDI2C_RESET_TIMEOUT 5
int quicki2c_reset(struct quicki2c_device *qcdev)
diff --git a/drivers/hid/intel-thc-hid/intel-quicki2c/quicki2c-protocol.h b/drivers/hid/intel-thc-hid/intel-quicki2c/quicki2c-protocol.h
index bf4908cce59c..6642cefb8a67 100644
--- a/drivers/hid/intel-thc-hid/intel-quicki2c/quicki2c-protocol.h
+++ b/drivers/hid/intel-thc-hid/intel-quicki2c/quicki2c-protocol.h
@@ -10,9 +10,10 @@ struct quicki2c_device;
int quicki2c_set_power(struct quicki2c_device *qcdev, enum hidi2c_power_state power_state);
int quicki2c_get_report(struct quicki2c_device *qcdev, u8 report_type,
- unsigned int reportnum, void *buf, u32 buf_len);
+ unsigned int reportnum, void *buf, size_t buf_len);
int quicki2c_set_report(struct quicki2c_device *qcdev, u8 report_type,
- unsigned int reportnum, void *buf, u32 buf_len);
+ unsigned int reportnum, void *buf, size_t buf_len);
+int quicki2c_output_report(struct quicki2c_device *qcdev, void *buf, size_t buf_len);
int quicki2c_get_device_descriptor(struct quicki2c_device *qcdev);
int quicki2c_get_report_descriptor(struct quicki2c_device *qcdev);
int quicki2c_reset(struct quicki2c_device *qcdev);