-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
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>
- Loading branch information
Sandeep Singh
authored and
Jiri Kosina
committed
Oct 22, 2020
1 parent
4f567b9
commit 4b2c53d
Showing
3 changed files
with
487 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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; | ||
} |
Oops, something went wrong.