Even Xu 5f420e8215 HID: intel-thc-hid: intel-quicki2c: Add PM implementation
Implement THC QuickI2C driver power management callbacks.

Co-developed-by: Xinpeng Sun <xinpeng.sun@intel.com>
Signed-off-by: Xinpeng Sun <xinpeng.sun@intel.com>
Signed-off-by: Even Xu <even.xu@intel.com>
Tested-by: Rui Zhang <rui1.zhang@intel.com>
Tested-by: Mark Pearson <mpearson-lenovo@squebb.ca>
Reviewed-by: Srinivas Pandruvada <srinivas.pandruvada@linux.intel.com>
Reviewed-by: Mark Pearson <mpearson-lenovo@squebb.ca>
Tested-by: Aaron Ma <aaron.ma@canonical.com>
Signed-off-by: Jiri Kosina <jkosina@suse.com>
2025-01-09 10:14:16 +01:00

167 lines
3.7 KiB
C

/* SPDX-License-Identifier: GPL-2.0 */
/* Copyright (c) 2024 Intel Corporation */
#include <linux/hid.h>
#include <linux/input.h>
#include <linux/pm_runtime.h>
#include "quicki2c-dev.h"
#include "quicki2c-hid.h"
#include "quicki2c-protocol.h"
/**
* quicki2c_hid_parse() - HID core parse() callback
*
* @hid: HID device instance
*
* This function gets called during call to hid_add_device
*
* Return: 0 on success and non zero on error.
*/
static int quicki2c_hid_parse(struct hid_device *hid)
{
struct quicki2c_device *qcdev = hid->driver_data;
if (qcdev->report_descriptor)
return hid_parse_report(hid, qcdev->report_descriptor,
le16_to_cpu(qcdev->dev_desc.report_desc_len));
dev_err_once(qcdev->dev, "invalid report descriptor\n");
return -EINVAL;
}
static int quicki2c_hid_start(struct hid_device *hid)
{
return 0;
}
static void quicki2c_hid_stop(struct hid_device *hid)
{
}
static int quicki2c_hid_open(struct hid_device *hid)
{
return 0;
}
static void quicki2c_hid_close(struct hid_device *hid)
{
}
static int quicki2c_hid_raw_request(struct hid_device *hid,
unsigned char reportnum,
__u8 *buf, size_t len,
unsigned char rtype, int reqtype)
{
struct quicki2c_device *qcdev = hid->driver_data;
int ret = 0;
ret = pm_runtime_resume_and_get(qcdev->dev);
if (ret)
return ret;
switch (reqtype) {
case HID_REQ_GET_REPORT:
ret = quicki2c_get_report(qcdev, rtype, reportnum, buf, len);
break;
case HID_REQ_SET_REPORT:
ret = quicki2c_set_report(qcdev, rtype, reportnum, buf, len);
break;
default:
dev_err(qcdev->dev, "Not supported request type %d\n", reqtype);
break;
}
pm_runtime_mark_last_busy(qcdev->dev);
pm_runtime_put_autosuspend(qcdev->dev);
return ret;
}
static int quicki2c_hid_power(struct hid_device *hid, int lvl)
{
return 0;
}
static struct hid_ll_driver quicki2c_hid_ll_driver = {
.parse = quicki2c_hid_parse,
.start = quicki2c_hid_start,
.stop = quicki2c_hid_stop,
.open = quicki2c_hid_open,
.close = quicki2c_hid_close,
.power = quicki2c_hid_power,
.raw_request = quicki2c_hid_raw_request,
};
/**
* quicki2c_hid_probe() - Register HID low level driver
*
* @qcdev: point to quicki2c device
*
* This function is used to allocate and add HID device.
*
* Return: 0 on success, non zero on error.
*/
int quicki2c_hid_probe(struct quicki2c_device *qcdev)
{
struct hid_device *hid;
int ret;
hid = hid_allocate_device();
if (IS_ERR(hid))
return PTR_ERR(hid);
hid->ll_driver = &quicki2c_hid_ll_driver;
hid->bus = BUS_PCI;
hid->dev.parent = qcdev->dev;
hid->driver_data = qcdev;
hid->version = le16_to_cpu(qcdev->dev_desc.version_id);
hid->vendor = le16_to_cpu(qcdev->dev_desc.vendor_id);
hid->product = le16_to_cpu(qcdev->dev_desc.product_id);
snprintf(hid->name, sizeof(hid->name), "%s %04X:%04X", "quicki2c-hid",
hid->vendor, hid->product);
ret = hid_add_device(hid);
if (ret) {
hid_destroy_device(hid);
return ret;
}
qcdev->hid_dev = hid;
return 0;
}
/**
* quicki2c_hid_remove() - Destroy HID device
*
* @qcdev: point to quicki2c device
*
* Return: 0 on success, non zero on error.
*/
void quicki2c_hid_remove(struct quicki2c_device *qcdev)
{
hid_destroy_device(qcdev->hid_dev);
}
/**
* quicki2c_hid_send_report() - Send HID input report data to HID core
*
* @qcdev: point to quicki2c device
* @data: point to input report data buffer
* @data_len: the length of input report data
*
* Return: 0 on success, non zero on error.
*/
int quicki2c_hid_send_report(struct quicki2c_device *qcdev,
void *data, size_t data_len)
{
int ret;
ret = hid_input_report(qcdev->hid_dev, HID_INPUT_REPORT, data, data_len, 1);
if (ret)
dev_err(qcdev->dev, "Failed to send HID input report, ret = %d.\n", ret);
return ret;
}