355 lines · c
1// SPDX-License-Identifier: GPL-2.0-or-later2/*3 * AMD SFH Client Layer4 * Copyright 2020-2021 Advanced Micro Devices, Inc.5 * Authors: Nehal Bakulchandra Shah <Nehal-Bakulchandra.Shah@amd.com>6 * Sandeep Singh <Sandeep.singh@amd.com>7 * Basavaraj Natikar <Basavaraj.Natikar@amd.com>8 */9 10#include <linux/dma-mapping.h>11#include <linux/hid.h>12#include <linux/list.h>13#include <linux/slab.h>14#include <linux/workqueue.h>15#include <linux/errno.h>16 17#include "hid_descriptor/amd_sfh_hid_desc.h"18#include "amd_sfh_pcie.h"19#include "amd_sfh_hid.h"20 21void amd_sfh_set_report(struct hid_device *hid, int report_id,22 int report_type)23{24 struct amdtp_hid_data *hid_data = hid->driver_data;25 struct amdtp_cl_data *cli_data = hid_data->cli_data;26 int i;27 28 for (i = 0; i < cli_data->num_hid_devices; i++) {29 if (cli_data->hid_sensor_hubs[i] == hid) {30 cli_data->cur_hid_dev = i;31 break;32 }33 }34 amdtp_hid_wakeup(hid);35}36 37int amd_sfh_get_report(struct hid_device *hid, int report_id, int report_type)38{39 struct amdtp_hid_data *hid_data = hid->driver_data;40 struct amdtp_cl_data *cli_data = hid_data->cli_data;41 struct request_list *req_list = &cli_data->req_list;42 int i;43 44 for (i = 0; i < cli_data->num_hid_devices; i++) {45 if (cli_data->hid_sensor_hubs[i] == hid) {46 struct request_list *new = kzalloc(sizeof(*new), GFP_KERNEL);47 48 if (!new)49 return -ENOMEM;50 51 new->current_index = i;52 new->sensor_idx = cli_data->sensor_idx[i];53 new->hid = hid;54 new->report_type = report_type;55 new->report_id = report_id;56 cli_data->report_id[i] = report_id;57 cli_data->request_done[i] = false;58 list_add(&new->list, &req_list->list);59 break;60 }61 }62 schedule_delayed_work(&cli_data->work, 0);63 return 0;64}65 66void amd_sfh_work(struct work_struct *work)67{68 struct amdtp_cl_data *cli_data = container_of(work, struct amdtp_cl_data, work.work);69 struct request_list *req_list = &cli_data->req_list;70 struct amd_input_data *in_data = cli_data->in_data;71 struct request_list *req_node;72 u8 current_index, sensor_index;73 struct amd_mp2_ops *mp2_ops;74 struct amd_mp2_dev *mp2;75 u8 report_id, node_type;76 u8 report_size = 0;77 78 req_node = list_last_entry(&req_list->list, struct request_list, list);79 list_del(&req_node->list);80 current_index = req_node->current_index;81 sensor_index = req_node->sensor_idx;82 report_id = req_node->report_id;83 node_type = req_node->report_type;84 kfree(req_node);85 86 mp2 = container_of(in_data, struct amd_mp2_dev, in_data);87 mp2_ops = mp2->mp2_ops;88 if (node_type == HID_FEATURE_REPORT) {89 report_size = mp2_ops->get_feat_rep(sensor_index, report_id,90 cli_data->feature_report[current_index]);91 if (report_size)92 hid_input_report(cli_data->hid_sensor_hubs[current_index],93 cli_data->report_type[current_index],94 cli_data->feature_report[current_index], report_size, 0);95 else96 pr_err("AMDSFH: Invalid report size\n");97 98 } else if (node_type == HID_INPUT_REPORT) {99 report_size = mp2_ops->get_in_rep(current_index, sensor_index, report_id, in_data);100 if (report_size)101 hid_input_report(cli_data->hid_sensor_hubs[current_index],102 cli_data->report_type[current_index],103 in_data->input_report[current_index], report_size, 0);104 else105 pr_err("AMDSFH: Invalid report size\n");106 }107 cli_data->cur_hid_dev = current_index;108 cli_data->sensor_requested_cnt[current_index] = 0;109 amdtp_hid_wakeup(cli_data->hid_sensor_hubs[current_index]);110}111 112void amd_sfh_work_buffer(struct work_struct *work)113{114 struct amdtp_cl_data *cli_data = container_of(work, struct amdtp_cl_data, work_buffer.work);115 struct amd_input_data *in_data = cli_data->in_data;116 struct amd_mp2_dev *mp2;117 u8 report_size;118 int i;119 120 for (i = 0; i < cli_data->num_hid_devices; i++) {121 if (cli_data->sensor_sts[i] == SENSOR_ENABLED) {122 mp2 = container_of(in_data, struct amd_mp2_dev, in_data);123 report_size = mp2->mp2_ops->get_in_rep(i, cli_data->sensor_idx[i],124 cli_data->report_id[i], in_data);125 hid_input_report(cli_data->hid_sensor_hubs[i], HID_INPUT_REPORT,126 in_data->input_report[i], report_size, 0);127 }128 }129 schedule_delayed_work(&cli_data->work_buffer, msecs_to_jiffies(AMD_SFH_IDLE_LOOP));130}131 132static u32 amd_sfh_wait_for_response(struct amd_mp2_dev *mp2, u8 sid, u32 sensor_sts)133{134 if (mp2->mp2_ops->response)135 sensor_sts = mp2->mp2_ops->response(mp2, sid, sensor_sts);136 137 return sensor_sts;138}139 140static const char *get_sensor_name(int idx)141{142 switch (idx) {143 case accel_idx:144 return "accelerometer";145 case gyro_idx:146 return "gyroscope";147 case mag_idx:148 return "magnetometer";149 case als_idx:150 case ACS_IDX: /* ambient color sensor */151 return "ALS";152 case HPD_IDX:153 return "HPD";154 default:155 return "unknown sensor type";156 }157}158 159static void amd_sfh_resume(struct amd_mp2_dev *mp2)160{161 struct amdtp_cl_data *cl_data = mp2->cl_data;162 struct amd_mp2_sensor_info info;163 int i, status;164 165 for (i = 0; i < cl_data->num_hid_devices; i++) {166 if (cl_data->sensor_sts[i] == SENSOR_DISABLED) {167 info.period = AMD_SFH_IDLE_LOOP;168 info.sensor_idx = cl_data->sensor_idx[i];169 info.dma_address = cl_data->sensor_dma_addr[i];170 mp2->mp2_ops->start(mp2, info);171 status = amd_sfh_wait_for_response172 (mp2, cl_data->sensor_idx[i], SENSOR_ENABLED);173 if (status == SENSOR_ENABLED)174 cl_data->sensor_sts[i] = SENSOR_ENABLED;175 dev_dbg(&mp2->pdev->dev, "resume sid 0x%x (%s) status 0x%x\n",176 cl_data->sensor_idx[i], get_sensor_name(cl_data->sensor_idx[i]),177 cl_data->sensor_sts[i]);178 }179 }180 181 schedule_delayed_work(&cl_data->work_buffer, msecs_to_jiffies(AMD_SFH_IDLE_LOOP));182 amd_sfh_clear_intr(mp2);183}184 185static void amd_sfh_suspend(struct amd_mp2_dev *mp2)186{187 struct amdtp_cl_data *cl_data = mp2->cl_data;188 int i, status;189 190 for (i = 0; i < cl_data->num_hid_devices; i++) {191 if (cl_data->sensor_idx[i] != HPD_IDX &&192 cl_data->sensor_sts[i] == SENSOR_ENABLED) {193 mp2->mp2_ops->stop(mp2, cl_data->sensor_idx[i]);194 status = amd_sfh_wait_for_response195 (mp2, cl_data->sensor_idx[i], SENSOR_DISABLED);196 if (status != SENSOR_ENABLED)197 cl_data->sensor_sts[i] = SENSOR_DISABLED;198 dev_dbg(&mp2->pdev->dev, "suspend sid 0x%x (%s) status 0x%x\n",199 cl_data->sensor_idx[i], get_sensor_name(cl_data->sensor_idx[i]),200 cl_data->sensor_sts[i]);201 }202 }203 204 cancel_delayed_work_sync(&cl_data->work_buffer);205 amd_sfh_clear_intr(mp2);206}207 208int amd_sfh_hid_client_init(struct amd_mp2_dev *privdata)209{210 struct amd_input_data *in_data = &privdata->in_data;211 struct amdtp_cl_data *cl_data = privdata->cl_data;212 struct amd_mp2_ops *mp2_ops = privdata->mp2_ops;213 struct amd_mp2_sensor_info info;214 struct request_list *req_list;215 struct device *dev;216 u32 feature_report_size;217 u32 input_report_size;218 int rc, i;219 u8 cl_idx;220 221 req_list = &cl_data->req_list;222 dev = &privdata->pdev->dev;223 amd_sfh_set_desc_ops(mp2_ops);224 225 mp2_ops->suspend = amd_sfh_suspend;226 mp2_ops->resume = amd_sfh_resume;227 228 cl_data->num_hid_devices = amd_mp2_get_sensor_num(privdata, &cl_data->sensor_idx[0]);229 if (cl_data->num_hid_devices == 0)230 return -ENODEV;231 cl_data->is_any_sensor_enabled = false;232 233 INIT_DELAYED_WORK(&cl_data->work, amd_sfh_work);234 INIT_DELAYED_WORK(&cl_data->work_buffer, amd_sfh_work_buffer);235 INIT_LIST_HEAD(&req_list->list);236 cl_data->in_data = in_data;237 238 for (i = 0; i < cl_data->num_hid_devices; i++) {239 in_data->sensor_virt_addr[i] = dmam_alloc_coherent(dev, sizeof(int) * 8,240 &cl_data->sensor_dma_addr[i],241 GFP_KERNEL);242 if (!in_data->sensor_virt_addr[i]) {243 rc = -ENOMEM;244 goto cleanup;245 }246 cl_data->sensor_sts[i] = SENSOR_DISABLED;247 cl_data->sensor_requested_cnt[i] = 0;248 cl_data->cur_hid_dev = i;249 cl_idx = cl_data->sensor_idx[i];250 cl_data->report_descr_sz[i] = mp2_ops->get_desc_sz(cl_idx, descr_size);251 if (!cl_data->report_descr_sz[i]) {252 rc = -EINVAL;253 goto cleanup;254 }255 feature_report_size = mp2_ops->get_desc_sz(cl_idx, feature_size);256 if (!feature_report_size) {257 rc = -EINVAL;258 goto cleanup;259 }260 input_report_size = mp2_ops->get_desc_sz(cl_idx, input_size);261 if (!input_report_size) {262 rc = -EINVAL;263 goto cleanup;264 }265 cl_data->feature_report[i] = devm_kzalloc(dev, feature_report_size, GFP_KERNEL);266 if (!cl_data->feature_report[i]) {267 rc = -ENOMEM;268 goto cleanup;269 }270 in_data->input_report[i] = devm_kzalloc(dev, input_report_size, GFP_KERNEL);271 if (!in_data->input_report[i]) {272 rc = -ENOMEM;273 goto cleanup;274 }275 info.period = AMD_SFH_IDLE_LOOP;276 info.sensor_idx = cl_idx;277 info.dma_address = cl_data->sensor_dma_addr[i];278 279 cl_data->report_descr[i] =280 devm_kzalloc(dev, cl_data->report_descr_sz[i], GFP_KERNEL);281 if (!cl_data->report_descr[i]) {282 rc = -ENOMEM;283 goto cleanup;284 }285 rc = mp2_ops->get_rep_desc(cl_idx, cl_data->report_descr[i]);286 if (rc)287 goto cleanup;288 mp2_ops->start(privdata, info);289 cl_data->sensor_sts[i] = amd_sfh_wait_for_response290 (privdata, cl_data->sensor_idx[i], SENSOR_ENABLED);291 292 if (cl_data->sensor_sts[i] == SENSOR_ENABLED)293 cl_data->is_any_sensor_enabled = true;294 }295 296 if (!cl_data->is_any_sensor_enabled ||297 (mp2_ops->discovery_status && mp2_ops->discovery_status(privdata) == 0)) {298 dev_warn(dev, "Failed to discover, sensors not enabled is %d\n",299 cl_data->is_any_sensor_enabled);300 rc = -EOPNOTSUPP;301 goto cleanup;302 }303 304 for (i = 0; i < cl_data->num_hid_devices; i++) {305 cl_data->cur_hid_dev = i;306 if (cl_data->sensor_sts[i] == SENSOR_ENABLED) {307 rc = amdtp_hid_probe(i, cl_data);308 if (rc)309 goto cleanup;310 } else {311 cl_data->sensor_sts[i] = SENSOR_DISABLED;312 }313 dev_dbg(dev, "sid 0x%x (%s) status 0x%x\n",314 cl_data->sensor_idx[i], get_sensor_name(cl_data->sensor_idx[i]),315 cl_data->sensor_sts[i]);316 }317 318 schedule_delayed_work(&cl_data->work_buffer, msecs_to_jiffies(AMD_SFH_IDLE_LOOP));319 return 0;320 321cleanup:322 amd_sfh_hid_client_deinit(privdata);323 for (i = 0; i < cl_data->num_hid_devices; i++) {324 devm_kfree(dev, cl_data->feature_report[i]);325 devm_kfree(dev, in_data->input_report[i]);326 devm_kfree(dev, cl_data->report_descr[i]);327 }328 return rc;329}330 331int amd_sfh_hid_client_deinit(struct amd_mp2_dev *privdata)332{333 struct amdtp_cl_data *cl_data = privdata->cl_data;334 int i, status;335 336 for (i = 0; i < cl_data->num_hid_devices; i++) {337 if (cl_data->sensor_sts[i] == SENSOR_ENABLED) {338 privdata->mp2_ops->stop(privdata, cl_data->sensor_idx[i]);339 status = amd_sfh_wait_for_response340 (privdata, cl_data->sensor_idx[i], SENSOR_DISABLED);341 if (status != SENSOR_ENABLED)342 cl_data->sensor_sts[i] = SENSOR_DISABLED;343 dev_dbg(&privdata->pdev->dev, "stopping sid 0x%x (%s) status 0x%x\n",344 cl_data->sensor_idx[i], get_sensor_name(cl_data->sensor_idx[i]),345 cl_data->sensor_sts[i]);346 }347 }348 349 cancel_delayed_work_sync(&cl_data->work);350 cancel_delayed_work_sync(&cl_data->work_buffer);351 amdtp_hid_remove(cl_data);352 353 return 0;354}355