Path: blob/master/drivers/hid/amd-sfh-hid/amd_sfh_client.c
29269 views
// SPDX-License-Identifier: GPL-2.0-or-later1/*2* AMD SFH Client Layer3* Copyright 2020-2021 Advanced Micro Devices, Inc.4* Authors: Nehal Bakulchandra Shah <[email protected]>5* Sandeep Singh <[email protected]>6* Basavaraj Natikar <[email protected]>7*/89#include <linux/dma-mapping.h>10#include <linux/hid.h>11#include <linux/list.h>12#include <linux/slab.h>13#include <linux/workqueue.h>14#include <linux/errno.h>1516#include "hid_descriptor/amd_sfh_hid_desc.h"17#include "amd_sfh_pcie.h"18#include "amd_sfh_hid.h"1920void amd_sfh_set_report(struct hid_device *hid, int report_id,21int report_type)22{23struct amdtp_hid_data *hid_data = hid->driver_data;24struct amdtp_cl_data *cli_data = hid_data->cli_data;25int i;2627for (i = 0; i < cli_data->num_hid_devices; i++) {28if (cli_data->hid_sensor_hubs[i] == hid) {29cli_data->cur_hid_dev = i;30break;31}32}33amdtp_hid_wakeup(hid);34}3536int amd_sfh_get_report(struct hid_device *hid, int report_id, int report_type)37{38struct amdtp_hid_data *hid_data = hid->driver_data;39struct amdtp_cl_data *cli_data = hid_data->cli_data;40struct request_list *req_list = &cli_data->req_list;41struct amd_input_data *in_data = cli_data->in_data;42struct amd_mp2_dev *mp2;43int i;4445mp2 = container_of(in_data, struct amd_mp2_dev, in_data);46guard(mutex)(&mp2->lock);47for (i = 0; i < cli_data->num_hid_devices; i++) {48if (cli_data->hid_sensor_hubs[i] == hid) {49struct request_list *new = kzalloc(sizeof(*new), GFP_KERNEL);5051if (!new)52return -ENOMEM;5354new->current_index = i;55new->sensor_idx = cli_data->sensor_idx[i];56new->hid = hid;57new->report_type = report_type;58new->report_id = report_id;59cli_data->report_id[i] = report_id;60cli_data->request_done[i] = false;61list_add(&new->list, &req_list->list);62break;63}64}65schedule_delayed_work(&cli_data->work, 0);66return 0;67}6869void amd_sfh_work(struct work_struct *work)70{71struct amdtp_cl_data *cli_data = container_of(work, struct amdtp_cl_data, work.work);72struct request_list *req_list = &cli_data->req_list;73struct amd_input_data *in_data = cli_data->in_data;74struct request_list *req_node;75u8 current_index, sensor_index;76struct amd_mp2_ops *mp2_ops;77struct amd_mp2_dev *mp2;78u8 report_id, node_type;79u8 report_size = 0;8081mp2 = container_of(in_data, struct amd_mp2_dev, in_data);82guard(mutex)(&mp2->lock);83req_node = list_last_entry(&req_list->list, struct request_list, list);84list_del(&req_node->list);85current_index = req_node->current_index;86sensor_index = req_node->sensor_idx;87report_id = req_node->report_id;88node_type = req_node->report_type;89kfree(req_node);9091mp2_ops = mp2->mp2_ops;92if (node_type == HID_FEATURE_REPORT) {93report_size = mp2_ops->get_feat_rep(sensor_index, report_id,94cli_data->feature_report[current_index]);95if (report_size)96hid_input_report(cli_data->hid_sensor_hubs[current_index],97cli_data->report_type[current_index],98cli_data->feature_report[current_index], report_size, 0);99else100pr_err("AMDSFH: Invalid report size\n");101102} else if (node_type == HID_INPUT_REPORT) {103report_size = mp2_ops->get_in_rep(current_index, sensor_index, report_id, in_data);104if (report_size)105hid_input_report(cli_data->hid_sensor_hubs[current_index],106cli_data->report_type[current_index],107in_data->input_report[current_index], report_size, 0);108else109pr_err("AMDSFH: Invalid report size\n");110}111cli_data->cur_hid_dev = current_index;112cli_data->sensor_requested_cnt[current_index] = 0;113amdtp_hid_wakeup(cli_data->hid_sensor_hubs[current_index]);114if (!list_empty(&req_list->list))115schedule_delayed_work(&cli_data->work, 0);116}117118void amd_sfh_work_buffer(struct work_struct *work)119{120struct amdtp_cl_data *cli_data = container_of(work, struct amdtp_cl_data, work_buffer.work);121struct amd_input_data *in_data = cli_data->in_data;122struct amd_mp2_dev *mp2;123u8 report_size;124int i;125126mp2 = container_of(in_data, struct amd_mp2_dev, in_data);127guard(mutex)(&mp2->lock);128for (i = 0; i < cli_data->num_hid_devices; i++) {129if (cli_data->sensor_sts[i] == SENSOR_ENABLED) {130report_size = mp2->mp2_ops->get_in_rep(i, cli_data->sensor_idx[i],131cli_data->report_id[i], in_data);132hid_input_report(cli_data->hid_sensor_hubs[i], HID_INPUT_REPORT,133in_data->input_report[i], report_size, 0);134}135}136schedule_delayed_work(&cli_data->work_buffer, msecs_to_jiffies(AMD_SFH_IDLE_LOOP));137}138139static u32 amd_sfh_wait_for_response(struct amd_mp2_dev *mp2, u8 sid, u32 sensor_sts)140{141if (mp2->mp2_ops->response)142sensor_sts = mp2->mp2_ops->response(mp2, sid, sensor_sts);143144return sensor_sts;145}146147static const char *get_sensor_name(int idx)148{149switch (idx) {150case accel_idx:151return "accelerometer";152case gyro_idx:153return "gyroscope";154case mag_idx:155return "magnetometer";156case op_idx:157return "operating-mode";158case als_idx:159case ACS_IDX: /* ambient color sensor */160return "ALS";161case HPD_IDX:162return "HPD";163default:164return "unknown sensor type";165}166}167168static void amd_sfh_resume(struct amd_mp2_dev *mp2)169{170struct amdtp_cl_data *cl_data = mp2->cl_data;171struct amd_mp2_sensor_info info;172int i, status;173174for (i = 0; i < cl_data->num_hid_devices; i++) {175if (cl_data->sensor_sts[i] == SENSOR_DISABLED) {176info.period = AMD_SFH_IDLE_LOOP;177info.sensor_idx = cl_data->sensor_idx[i];178info.dma_address = cl_data->sensor_dma_addr[i];179mp2->mp2_ops->start(mp2, info);180status = amd_sfh_wait_for_response181(mp2, cl_data->sensor_idx[i], SENSOR_ENABLED);182if (status == SENSOR_ENABLED)183cl_data->sensor_sts[i] = SENSOR_ENABLED;184dev_dbg(&mp2->pdev->dev, "resume sid 0x%x (%s) status 0x%x\n",185cl_data->sensor_idx[i], get_sensor_name(cl_data->sensor_idx[i]),186cl_data->sensor_sts[i]);187}188}189190schedule_delayed_work(&cl_data->work_buffer, msecs_to_jiffies(AMD_SFH_IDLE_LOOP));191amd_sfh_clear_intr(mp2);192}193194static void amd_sfh_suspend(struct amd_mp2_dev *mp2)195{196struct amdtp_cl_data *cl_data = mp2->cl_data;197int i, status;198199for (i = 0; i < cl_data->num_hid_devices; i++) {200if (cl_data->sensor_idx[i] != HPD_IDX &&201cl_data->sensor_sts[i] == SENSOR_ENABLED) {202mp2->mp2_ops->stop(mp2, cl_data->sensor_idx[i]);203status = amd_sfh_wait_for_response204(mp2, cl_data->sensor_idx[i], SENSOR_DISABLED);205if (status != SENSOR_ENABLED)206cl_data->sensor_sts[i] = SENSOR_DISABLED;207dev_dbg(&mp2->pdev->dev, "suspend sid 0x%x (%s) status 0x%x\n",208cl_data->sensor_idx[i], get_sensor_name(cl_data->sensor_idx[i]),209cl_data->sensor_sts[i]);210}211}212213cancel_delayed_work_sync(&cl_data->work_buffer);214amd_sfh_clear_intr(mp2);215}216217int amd_sfh_hid_client_init(struct amd_mp2_dev *privdata)218{219struct amd_input_data *in_data = &privdata->in_data;220struct amdtp_cl_data *cl_data = privdata->cl_data;221struct amd_mp2_ops *mp2_ops = privdata->mp2_ops;222struct amd_mp2_sensor_info info;223struct request_list *req_list;224struct device *dev;225u32 feature_report_size;226u32 input_report_size;227int rc, i;228u8 cl_idx;229230req_list = &cl_data->req_list;231dev = &privdata->pdev->dev;232amd_sfh_set_desc_ops(mp2_ops);233234mp2_ops->suspend = amd_sfh_suspend;235mp2_ops->resume = amd_sfh_resume;236237cl_data->num_hid_devices = amd_mp2_get_sensor_num(privdata, &cl_data->sensor_idx[0]);238if (cl_data->num_hid_devices == 0)239return -ENODEV;240cl_data->is_any_sensor_enabled = false;241242INIT_DELAYED_WORK(&cl_data->work, amd_sfh_work);243INIT_DELAYED_WORK(&cl_data->work_buffer, amd_sfh_work_buffer);244INIT_LIST_HEAD(&req_list->list);245cl_data->in_data = in_data;246247for (i = 0; i < cl_data->num_hid_devices; i++) {248in_data->sensor_virt_addr[i] = dmam_alloc_coherent(dev, sizeof(int) * 8,249&cl_data->sensor_dma_addr[i],250GFP_KERNEL);251if (!in_data->sensor_virt_addr[i]) {252rc = -ENOMEM;253goto cleanup;254}255256if (cl_data->sensor_idx[i] == op_idx) {257info.period = AMD_SFH_IDLE_LOOP;258info.sensor_idx = cl_data->sensor_idx[i];259info.dma_address = cl_data->sensor_dma_addr[i];260mp2_ops->start(privdata, info);261cl_data->sensor_sts[i] = amd_sfh_wait_for_response(privdata,262cl_data->sensor_idx[i],263SENSOR_ENABLED);264if (cl_data->sensor_sts[i] == SENSOR_ENABLED)265cl_data->is_any_sensor_enabled = true;266continue;267}268269cl_data->sensor_sts[i] = SENSOR_DISABLED;270cl_data->sensor_requested_cnt[i] = 0;271cl_data->cur_hid_dev = i;272cl_idx = cl_data->sensor_idx[i];273cl_data->report_descr_sz[i] = mp2_ops->get_desc_sz(cl_idx, descr_size);274if (!cl_data->report_descr_sz[i]) {275rc = -EINVAL;276goto cleanup;277}278feature_report_size = mp2_ops->get_desc_sz(cl_idx, feature_size);279if (!feature_report_size) {280rc = -EINVAL;281goto cleanup;282}283input_report_size = mp2_ops->get_desc_sz(cl_idx, input_size);284if (!input_report_size) {285rc = -EINVAL;286goto cleanup;287}288cl_data->feature_report[i] = devm_kzalloc(dev, feature_report_size, GFP_KERNEL);289if (!cl_data->feature_report[i]) {290rc = -ENOMEM;291goto cleanup;292}293in_data->input_report[i] = devm_kzalloc(dev, input_report_size, GFP_KERNEL);294if (!in_data->input_report[i]) {295rc = -ENOMEM;296goto cleanup;297}298info.period = AMD_SFH_IDLE_LOOP;299info.sensor_idx = cl_idx;300info.dma_address = cl_data->sensor_dma_addr[i];301302cl_data->report_descr[i] =303devm_kzalloc(dev, cl_data->report_descr_sz[i], GFP_KERNEL);304if (!cl_data->report_descr[i]) {305rc = -ENOMEM;306goto cleanup;307}308rc = mp2_ops->get_rep_desc(cl_idx, cl_data->report_descr[i]);309if (rc)310goto cleanup;311mp2_ops->start(privdata, info);312cl_data->sensor_sts[i] = amd_sfh_wait_for_response313(privdata, cl_data->sensor_idx[i], SENSOR_ENABLED);314315if (cl_data->sensor_sts[i] == SENSOR_ENABLED)316cl_data->is_any_sensor_enabled = true;317}318319if (!cl_data->is_any_sensor_enabled ||320(mp2_ops->discovery_status && mp2_ops->discovery_status(privdata) == 0)) {321dev_warn(dev, "Failed to discover, sensors not enabled is %d\n",322cl_data->is_any_sensor_enabled);323rc = -EOPNOTSUPP;324goto cleanup;325}326327for (i = 0; i < cl_data->num_hid_devices; i++) {328cl_data->cur_hid_dev = i;329if (cl_data->sensor_idx[i] == op_idx) {330dev_dbg(dev, "sid 0x%x (%s) status 0x%x\n",331cl_data->sensor_idx[i], get_sensor_name(cl_data->sensor_idx[i]),332cl_data->sensor_sts[i]);333continue;334}335336if (cl_data->sensor_sts[i] == SENSOR_ENABLED) {337rc = amdtp_hid_probe(i, cl_data);338if (rc)339goto cleanup;340} else {341cl_data->sensor_sts[i] = SENSOR_DISABLED;342}343dev_dbg(dev, "sid 0x%x (%s) status 0x%x\n",344cl_data->sensor_idx[i], get_sensor_name(cl_data->sensor_idx[i]),345cl_data->sensor_sts[i]);346}347348schedule_delayed_work(&cl_data->work_buffer, msecs_to_jiffies(AMD_SFH_IDLE_LOOP));349return 0;350351cleanup:352amd_sfh_hid_client_deinit(privdata);353for (i = 0; i < cl_data->num_hid_devices; i++) {354devm_kfree(dev, cl_data->feature_report[i]);355devm_kfree(dev, in_data->input_report[i]);356devm_kfree(dev, cl_data->report_descr[i]);357}358return rc;359}360361int amd_sfh_hid_client_deinit(struct amd_mp2_dev *privdata)362{363struct amdtp_cl_data *cl_data = privdata->cl_data;364int i, status;365366for (i = 0; i < cl_data->num_hid_devices; i++) {367if (cl_data->sensor_sts[i] == SENSOR_ENABLED) {368privdata->mp2_ops->stop(privdata, cl_data->sensor_idx[i]);369status = amd_sfh_wait_for_response370(privdata, cl_data->sensor_idx[i], SENSOR_DISABLED);371if (status != SENSOR_ENABLED)372cl_data->sensor_sts[i] = SENSOR_DISABLED;373dev_dbg(&privdata->pdev->dev, "stopping sid 0x%x (%s) status 0x%x\n",374cl_data->sensor_idx[i], get_sensor_name(cl_data->sensor_idx[i]),375cl_data->sensor_sts[i]);376}377}378379cancel_delayed_work_sync(&cl_data->work);380cancel_delayed_work_sync(&cl_data->work_buffer);381amdtp_hid_remove(cl_data);382383return 0;384}385386387