Path: blob/master/drivers/hid/amd-sfh-hid/amd_sfh_client.c
26282 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;41int i;4243for (i = 0; i < cli_data->num_hid_devices; i++) {44if (cli_data->hid_sensor_hubs[i] == hid) {45struct request_list *new = kzalloc(sizeof(*new), GFP_KERNEL);4647if (!new)48return -ENOMEM;4950new->current_index = i;51new->sensor_idx = cli_data->sensor_idx[i];52new->hid = hid;53new->report_type = report_type;54new->report_id = report_id;55cli_data->report_id[i] = report_id;56cli_data->request_done[i] = false;57list_add(&new->list, &req_list->list);58break;59}60}61schedule_delayed_work(&cli_data->work, 0);62return 0;63}6465void amd_sfh_work(struct work_struct *work)66{67struct amdtp_cl_data *cli_data = container_of(work, struct amdtp_cl_data, work.work);68struct request_list *req_list = &cli_data->req_list;69struct amd_input_data *in_data = cli_data->in_data;70struct request_list *req_node;71u8 current_index, sensor_index;72struct amd_mp2_ops *mp2_ops;73struct amd_mp2_dev *mp2;74u8 report_id, node_type;75u8 report_size = 0;7677req_node = list_last_entry(&req_list->list, struct request_list, list);78list_del(&req_node->list);79current_index = req_node->current_index;80sensor_index = req_node->sensor_idx;81report_id = req_node->report_id;82node_type = req_node->report_type;83kfree(req_node);8485mp2 = container_of(in_data, struct amd_mp2_dev, in_data);86mp2_ops = mp2->mp2_ops;87if (node_type == HID_FEATURE_REPORT) {88report_size = mp2_ops->get_feat_rep(sensor_index, report_id,89cli_data->feature_report[current_index]);90if (report_size)91hid_input_report(cli_data->hid_sensor_hubs[current_index],92cli_data->report_type[current_index],93cli_data->feature_report[current_index], report_size, 0);94else95pr_err("AMDSFH: Invalid report size\n");9697} else if (node_type == HID_INPUT_REPORT) {98report_size = mp2_ops->get_in_rep(current_index, sensor_index, report_id, in_data);99if (report_size)100hid_input_report(cli_data->hid_sensor_hubs[current_index],101cli_data->report_type[current_index],102in_data->input_report[current_index], report_size, 0);103else104pr_err("AMDSFH: Invalid report size\n");105}106cli_data->cur_hid_dev = current_index;107cli_data->sensor_requested_cnt[current_index] = 0;108amdtp_hid_wakeup(cli_data->hid_sensor_hubs[current_index]);109}110111void amd_sfh_work_buffer(struct work_struct *work)112{113struct amdtp_cl_data *cli_data = container_of(work, struct amdtp_cl_data, work_buffer.work);114struct amd_input_data *in_data = cli_data->in_data;115struct amd_mp2_dev *mp2;116u8 report_size;117int i;118119for (i = 0; i < cli_data->num_hid_devices; i++) {120if (cli_data->sensor_sts[i] == SENSOR_ENABLED) {121mp2 = container_of(in_data, struct amd_mp2_dev, in_data);122report_size = mp2->mp2_ops->get_in_rep(i, cli_data->sensor_idx[i],123cli_data->report_id[i], in_data);124hid_input_report(cli_data->hid_sensor_hubs[i], HID_INPUT_REPORT,125in_data->input_report[i], report_size, 0);126}127}128schedule_delayed_work(&cli_data->work_buffer, msecs_to_jiffies(AMD_SFH_IDLE_LOOP));129}130131static u32 amd_sfh_wait_for_response(struct amd_mp2_dev *mp2, u8 sid, u32 sensor_sts)132{133if (mp2->mp2_ops->response)134sensor_sts = mp2->mp2_ops->response(mp2, sid, sensor_sts);135136return sensor_sts;137}138139static const char *get_sensor_name(int idx)140{141switch (idx) {142case accel_idx:143return "accelerometer";144case gyro_idx:145return "gyroscope";146case mag_idx:147return "magnetometer";148case op_idx:149return "operating-mode";150case als_idx:151case ACS_IDX: /* ambient color sensor */152return "ALS";153case HPD_IDX:154return "HPD";155default:156return "unknown sensor type";157}158}159160static void amd_sfh_resume(struct amd_mp2_dev *mp2)161{162struct amdtp_cl_data *cl_data = mp2->cl_data;163struct amd_mp2_sensor_info info;164int i, status;165166for (i = 0; i < cl_data->num_hid_devices; i++) {167if (cl_data->sensor_sts[i] == SENSOR_DISABLED) {168info.period = AMD_SFH_IDLE_LOOP;169info.sensor_idx = cl_data->sensor_idx[i];170info.dma_address = cl_data->sensor_dma_addr[i];171mp2->mp2_ops->start(mp2, info);172status = amd_sfh_wait_for_response173(mp2, cl_data->sensor_idx[i], SENSOR_ENABLED);174if (status == SENSOR_ENABLED)175cl_data->sensor_sts[i] = SENSOR_ENABLED;176dev_dbg(&mp2->pdev->dev, "resume sid 0x%x (%s) status 0x%x\n",177cl_data->sensor_idx[i], get_sensor_name(cl_data->sensor_idx[i]),178cl_data->sensor_sts[i]);179}180}181182schedule_delayed_work(&cl_data->work_buffer, msecs_to_jiffies(AMD_SFH_IDLE_LOOP));183amd_sfh_clear_intr(mp2);184}185186static void amd_sfh_suspend(struct amd_mp2_dev *mp2)187{188struct amdtp_cl_data *cl_data = mp2->cl_data;189int i, status;190191for (i = 0; i < cl_data->num_hid_devices; i++) {192if (cl_data->sensor_idx[i] != HPD_IDX &&193cl_data->sensor_sts[i] == SENSOR_ENABLED) {194mp2->mp2_ops->stop(mp2, cl_data->sensor_idx[i]);195status = amd_sfh_wait_for_response196(mp2, cl_data->sensor_idx[i], SENSOR_DISABLED);197if (status != SENSOR_ENABLED)198cl_data->sensor_sts[i] = SENSOR_DISABLED;199dev_dbg(&mp2->pdev->dev, "suspend sid 0x%x (%s) status 0x%x\n",200cl_data->sensor_idx[i], get_sensor_name(cl_data->sensor_idx[i]),201cl_data->sensor_sts[i]);202}203}204205cancel_delayed_work_sync(&cl_data->work_buffer);206amd_sfh_clear_intr(mp2);207}208209int amd_sfh_hid_client_init(struct amd_mp2_dev *privdata)210{211struct amd_input_data *in_data = &privdata->in_data;212struct amdtp_cl_data *cl_data = privdata->cl_data;213struct amd_mp2_ops *mp2_ops = privdata->mp2_ops;214struct amd_mp2_sensor_info info;215struct request_list *req_list;216struct device *dev;217u32 feature_report_size;218u32 input_report_size;219int rc, i;220u8 cl_idx;221222req_list = &cl_data->req_list;223dev = &privdata->pdev->dev;224amd_sfh_set_desc_ops(mp2_ops);225226mp2_ops->suspend = amd_sfh_suspend;227mp2_ops->resume = amd_sfh_resume;228229cl_data->num_hid_devices = amd_mp2_get_sensor_num(privdata, &cl_data->sensor_idx[0]);230if (cl_data->num_hid_devices == 0)231return -ENODEV;232cl_data->is_any_sensor_enabled = false;233234INIT_DELAYED_WORK(&cl_data->work, amd_sfh_work);235INIT_DELAYED_WORK(&cl_data->work_buffer, amd_sfh_work_buffer);236INIT_LIST_HEAD(&req_list->list);237cl_data->in_data = in_data;238239for (i = 0; i < cl_data->num_hid_devices; i++) {240in_data->sensor_virt_addr[i] = dmam_alloc_coherent(dev, sizeof(int) * 8,241&cl_data->sensor_dma_addr[i],242GFP_KERNEL);243if (!in_data->sensor_virt_addr[i]) {244rc = -ENOMEM;245goto cleanup;246}247248if (cl_data->sensor_idx[i] == op_idx) {249info.period = AMD_SFH_IDLE_LOOP;250info.sensor_idx = cl_data->sensor_idx[i];251info.dma_address = cl_data->sensor_dma_addr[i];252mp2_ops->start(privdata, info);253cl_data->sensor_sts[i] = amd_sfh_wait_for_response(privdata,254cl_data->sensor_idx[i],255SENSOR_ENABLED);256if (cl_data->sensor_sts[i] == SENSOR_ENABLED)257cl_data->is_any_sensor_enabled = true;258continue;259}260261cl_data->sensor_sts[i] = SENSOR_DISABLED;262cl_data->sensor_requested_cnt[i] = 0;263cl_data->cur_hid_dev = i;264cl_idx = cl_data->sensor_idx[i];265cl_data->report_descr_sz[i] = mp2_ops->get_desc_sz(cl_idx, descr_size);266if (!cl_data->report_descr_sz[i]) {267rc = -EINVAL;268goto cleanup;269}270feature_report_size = mp2_ops->get_desc_sz(cl_idx, feature_size);271if (!feature_report_size) {272rc = -EINVAL;273goto cleanup;274}275input_report_size = mp2_ops->get_desc_sz(cl_idx, input_size);276if (!input_report_size) {277rc = -EINVAL;278goto cleanup;279}280cl_data->feature_report[i] = devm_kzalloc(dev, feature_report_size, GFP_KERNEL);281if (!cl_data->feature_report[i]) {282rc = -ENOMEM;283goto cleanup;284}285in_data->input_report[i] = devm_kzalloc(dev, input_report_size, GFP_KERNEL);286if (!in_data->input_report[i]) {287rc = -ENOMEM;288goto cleanup;289}290info.period = AMD_SFH_IDLE_LOOP;291info.sensor_idx = cl_idx;292info.dma_address = cl_data->sensor_dma_addr[i];293294cl_data->report_descr[i] =295devm_kzalloc(dev, cl_data->report_descr_sz[i], GFP_KERNEL);296if (!cl_data->report_descr[i]) {297rc = -ENOMEM;298goto cleanup;299}300rc = mp2_ops->get_rep_desc(cl_idx, cl_data->report_descr[i]);301if (rc)302goto cleanup;303mp2_ops->start(privdata, info);304cl_data->sensor_sts[i] = amd_sfh_wait_for_response305(privdata, cl_data->sensor_idx[i], SENSOR_ENABLED);306307if (cl_data->sensor_sts[i] == SENSOR_ENABLED)308cl_data->is_any_sensor_enabled = true;309}310311if (!cl_data->is_any_sensor_enabled ||312(mp2_ops->discovery_status && mp2_ops->discovery_status(privdata) == 0)) {313dev_warn(dev, "Failed to discover, sensors not enabled is %d\n",314cl_data->is_any_sensor_enabled);315rc = -EOPNOTSUPP;316goto cleanup;317}318319for (i = 0; i < cl_data->num_hid_devices; i++) {320cl_data->cur_hid_dev = i;321if (cl_data->sensor_idx[i] == op_idx) {322dev_dbg(dev, "sid 0x%x (%s) status 0x%x\n",323cl_data->sensor_idx[i], get_sensor_name(cl_data->sensor_idx[i]),324cl_data->sensor_sts[i]);325continue;326}327328if (cl_data->sensor_sts[i] == SENSOR_ENABLED) {329rc = amdtp_hid_probe(i, cl_data);330if (rc)331goto cleanup;332} else {333cl_data->sensor_sts[i] = SENSOR_DISABLED;334}335dev_dbg(dev, "sid 0x%x (%s) status 0x%x\n",336cl_data->sensor_idx[i], get_sensor_name(cl_data->sensor_idx[i]),337cl_data->sensor_sts[i]);338}339340schedule_delayed_work(&cl_data->work_buffer, msecs_to_jiffies(AMD_SFH_IDLE_LOOP));341return 0;342343cleanup:344amd_sfh_hid_client_deinit(privdata);345for (i = 0; i < cl_data->num_hid_devices; i++) {346devm_kfree(dev, cl_data->feature_report[i]);347devm_kfree(dev, in_data->input_report[i]);348devm_kfree(dev, cl_data->report_descr[i]);349}350return rc;351}352353int amd_sfh_hid_client_deinit(struct amd_mp2_dev *privdata)354{355struct amdtp_cl_data *cl_data = privdata->cl_data;356int i, status;357358for (i = 0; i < cl_data->num_hid_devices; i++) {359if (cl_data->sensor_sts[i] == SENSOR_ENABLED) {360privdata->mp2_ops->stop(privdata, cl_data->sensor_idx[i]);361status = amd_sfh_wait_for_response362(privdata, cl_data->sensor_idx[i], SENSOR_DISABLED);363if (status != SENSOR_ENABLED)364cl_data->sensor_sts[i] = SENSOR_DISABLED;365dev_dbg(&privdata->pdev->dev, "stopping sid 0x%x (%s) status 0x%x\n",366cl_data->sensor_idx[i], get_sensor_name(cl_data->sensor_idx[i]),367cl_data->sensor_sts[i]);368}369}370371cancel_delayed_work_sync(&cl_data->work);372cancel_delayed_work_sync(&cl_data->work_buffer);373amdtp_hid_remove(cl_data);374375return 0;376}377378379