diff options
author | Sandeep Singh <sandeep.singh@amd.com> | 2020-10-09 23:01:37 +0300 |
---|---|---|
committer | Jiri Kosina <jkosina@suse.cz> | 2020-10-22 13:05:03 +0300 |
commit | 4b2c53d93a4bc9d52cc0ec354629cfc9dc217f93 (patch) | |
tree | 8d5e1218a0d3cd5b310737e05a2190aa2f754d32 /drivers/hid/amd-sfh-hid/amd_sfh_client.c | |
parent | 4f567b9f8141a86c7d878fadf136e5d1408e3e61 (diff) | |
download | linux-4b2c53d93a4bc9d52cc0ec354629cfc9dc217f93.tar.xz |
SFH:Transport Driver to add support of AMD Sensor Fusion Hub (SFH)
To support AMD Sensor Fusion Hub (SFH) via the HID client's we need to
register the client with the HID framework. Here we mostly address on
how to register the client with the framework and define the interfaces
for communication.
Co-developed-by: Nehal Shah <Nehal-bakulchandra.Shah@amd.com>
Signed-off-by: Nehal Shah <Nehal-bakulchandra.Shah@amd.com>
Signed-off-by: Sandeep Singh <sandeep.singh@amd.com>
Signed-off-by: Jiri Kosina <jkosina@suse.cz>
Diffstat (limited to 'drivers/hid/amd-sfh-hid/amd_sfh_client.c')
-rw-r--r-- | drivers/hid/amd-sfh-hid/amd_sfh_client.c | 246 |
1 files changed, 246 insertions, 0 deletions
diff --git a/drivers/hid/amd-sfh-hid/amd_sfh_client.c b/drivers/hid/amd-sfh-hid/amd_sfh_client.c new file mode 100644 index 000000000000..3d1ccac5d99a --- /dev/null +++ b/drivers/hid/amd-sfh-hid/amd_sfh_client.c @@ -0,0 +1,246 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * AMD SFH Client Layer + * Copyright 2020 Advanced Micro Devices, Inc. + * Authors: Nehal Bakulchandra Shah <Nehal-Bakulchandra.Shah@amd.com> + * Sandeep Singh <Sandeep.singh@amd.com> + */ + +#include <linux/dma-mapping.h> +#include <linux/hid.h> +#include <linux/list.h> +#include <linux/slab.h> +#include <linux/workqueue.h> +#include <linux/errno.h> + +#include "hid_descriptor/amd_sfh_hid_desc.h" +#include "amd_sfh_pcie.h" +#include "amd_sfh_hid.h" + +#define AMD_SFH_IDLE_LOOP 200 + +struct request_list { + struct hid_device *hid; + struct list_head list; + u8 report_id; + u8 sensor_idx; + u8 report_type; + u8 current_index; +}; + +static struct request_list req_list; + +void amd_sfh_set_report(struct hid_device *hid, int report_id, + int report_type) +{ + struct amdtp_hid_data *hid_data = hid->driver_data; + struct amdtp_cl_data *cli_data = hid_data->cli_data; + int i; + + for (i = 0; i < cli_data->num_hid_devices; i++) { + if (cli_data->hid_sensor_hubs[i] == hid) { + cli_data->cur_hid_dev = i; + break; + } + } + amdtp_hid_wakeup(hid); +} + +int amd_sfh_get_report(struct hid_device *hid, int report_id, int report_type) +{ + struct amdtp_hid_data *hid_data = hid->driver_data; + struct amdtp_cl_data *cli_data = hid_data->cli_data; + int i; + + for (i = 0; i < cli_data->num_hid_devices; i++) { + if (cli_data->hid_sensor_hubs[i] == hid) { + struct request_list *new = kzalloc(sizeof(*new), GFP_KERNEL); + + if (!new) + return -ENOMEM; + + new->current_index = i; + new->sensor_idx = cli_data->sensor_idx[i]; + new->hid = hid; + new->report_type = report_type; + new->report_id = report_id; + cli_data->report_id[i] = report_id; + cli_data->request_done[i] = false; + list_add(&new->list, &req_list.list); + break; + } + } + schedule_delayed_work(&cli_data->work, 0); + return 0; +} + +static void amd_sfh_work(struct work_struct *work) +{ + struct amdtp_cl_data *cli_data = container_of(work, struct amdtp_cl_data, work.work); + struct request_list *req_node; + u8 current_index, sensor_index; + u8 report_id, node_type; + u8 report_size = 0; + + req_node = list_last_entry(&req_list.list, struct request_list, list); + list_del(&req_node->list); + current_index = req_node->current_index; + sensor_index = req_node->sensor_idx; + report_id = req_node->report_id; + node_type = req_node->report_type; + + if (node_type == HID_FEATURE_REPORT) { + report_size = get_feature_report(sensor_index, report_id, + cli_data->feature_report[current_index]); + if (report_size) + hid_input_report(cli_data->hid_sensor_hubs[current_index], + cli_data->report_type[current_index], + cli_data->feature_report[current_index], report_size, 0); + else + pr_err("AMDSFH: Invalid report size\n"); + + } else if (node_type == HID_INPUT_REPORT) { + report_size = get_input_report(sensor_index, report_id, + cli_data->input_report[current_index], + cli_data->sensor_virt_addr[current_index]); + if (report_size) + hid_input_report(cli_data->hid_sensor_hubs[current_index], + cli_data->report_type[current_index], + cli_data->input_report[current_index], report_size, 0); + else + pr_err("AMDSFH: Invalid report size\n"); + } + cli_data->cur_hid_dev = current_index; + cli_data->sensor_requested_cnt[current_index] = 0; + amdtp_hid_wakeup(cli_data->hid_sensor_hubs[current_index]); +} + +static void amd_sfh_work_buffer(struct work_struct *work) +{ + struct amdtp_cl_data *cli_data = container_of(work, struct amdtp_cl_data, work_buffer.work); + u8 report_size; + int i; + + for (i = 0; i < cli_data->num_hid_devices; i++) { + report_size = get_input_report(cli_data->sensor_idx[i], cli_data->report_id[i], + cli_data->input_report[i], + cli_data->sensor_virt_addr[i]); + hid_input_report(cli_data->hid_sensor_hubs[i], HID_INPUT_REPORT, + cli_data->input_report[i], report_size, 0); + } + schedule_delayed_work(&cli_data->work_buffer, msecs_to_jiffies(AMD_SFH_IDLE_LOOP)); +} + +int amd_sfh_hid_client_init(struct amd_mp2_dev *privdata) +{ + struct amdtp_cl_data *cl_data = privdata->cl_data; + struct amd_mp2_sensor_info info; + struct device *dev; + u32 feature_report_size; + u32 input_report_size; + u8 cl_idx; + int rc, i; + + dev = &privdata->pdev->dev; + cl_data = kzalloc(sizeof(*cl_data), GFP_KERNEL); + if (!cl_data) + return -ENOMEM; + + cl_data->num_hid_devices = amd_mp2_get_sensor_num(privdata, &cl_data->sensor_idx[0]); + + INIT_DELAYED_WORK(&cl_data->work, amd_sfh_work); + INIT_DELAYED_WORK(&cl_data->work_buffer, amd_sfh_work_buffer); + INIT_LIST_HEAD(&req_list.list); + + for (i = 0; i < cl_data->num_hid_devices; i++) { + cl_data->sensor_virt_addr[i] = dma_alloc_coherent(dev, sizeof(int) * 8, + &cl_data->sensor_phys_addr[i], + GFP_KERNEL); + cl_data->sensor_sts[i] = 0; + cl_data->sensor_requested_cnt[i] = 0; + cl_data->cur_hid_dev = i; + cl_idx = cl_data->sensor_idx[i]; + cl_data->report_descr_sz[i] = get_descr_sz(cl_idx, descr_size); + if (!cl_data->report_descr_sz[i]) { + rc = -EINVAL; + goto cleanup; + } + feature_report_size = get_descr_sz(cl_idx, feature_size); + if (!feature_report_size) { + rc = -EINVAL; + goto cleanup; + } + input_report_size = get_descr_sz(cl_idx, input_size); + if (!input_report_size) { + rc = -EINVAL; + goto cleanup; + } + cl_data->feature_report[i] = kzalloc(feature_report_size, GFP_KERNEL); + if (!cl_data->feature_report[i]) { + rc = -ENOMEM; + goto cleanup; + } + cl_data->input_report[i] = kzalloc(input_report_size, GFP_KERNEL); + if (!cl_data->input_report[i]) { + rc = -ENOMEM; + goto cleanup; + } + info.period = msecs_to_jiffies(AMD_SFH_IDLE_LOOP); + info.sensor_idx = cl_idx; + info.phys_address = cl_data->sensor_phys_addr[i]; + + cl_data->report_descr[i] = kzalloc(cl_data->report_descr_sz[i], GFP_KERNEL); + if (!cl_data->report_descr[i]) { + rc = -ENOMEM; + goto cleanup; + } + rc = get_report_descriptor(cl_idx, cl_data->report_descr[i]); + if (rc) + return rc; + rc = amdtp_hid_probe(cl_data->cur_hid_dev, cl_data); + if (rc) + return rc; + amd_start_sensor(privdata, info); + cl_data->sensor_sts[i] = 1; + } + privdata->cl_data = cl_data; + schedule_delayed_work(&cl_data->work_buffer, msecs_to_jiffies(AMD_SFH_IDLE_LOOP)); + return 0; + +cleanup: + for (i = 0; i < cl_data->num_hid_devices; i++) { + if (cl_data->sensor_virt_addr[i]) { + dma_free_coherent(&privdata->pdev->dev, 8 * sizeof(int), + cl_data->sensor_virt_addr[i], + cl_data->sensor_phys_addr[i]); + } + kfree(cl_data->feature_report[i]); + kfree(cl_data->input_report[i]); + kfree(cl_data->report_descr[i]); + } + kfree(cl_data); + return rc; +} + +int amd_sfh_hid_client_deinit(struct amd_mp2_dev *privdata) +{ + struct amdtp_cl_data *cl_data = privdata->cl_data; + int i; + + for (i = 0; i < cl_data->num_hid_devices; i++) + amd_stop_sensor(privdata, i); + + cancel_delayed_work_sync(&cl_data->work); + cancel_delayed_work_sync(&cl_data->work_buffer); + amdtp_hid_remove(cl_data); + + for (i = 0; i < cl_data->num_hid_devices; i++) { + if (cl_data->sensor_virt_addr[i]) { + dma_free_coherent(&privdata->pdev->dev, 8 * sizeof(int), + cl_data->sensor_virt_addr[i], + cl_data->sensor_phys_addr[i]); + } + } + kfree(cl_data); + return 0; +} |