Commit | Line | Data |
---|---|---|
4b2c53d9 SS |
1 | // SPDX-License-Identifier: GPL-2.0-or-later |
2 | /* | |
3 | * AMD SFH Client Layer | |
f75203cd | 4 | * Copyright 2020-2021 Advanced Micro Devices, Inc. |
4b2c53d9 SS |
5 | * Authors: Nehal Bakulchandra Shah <Nehal-Bakulchandra.Shah@amd.com> |
6 | * Sandeep Singh <Sandeep.singh@amd.com> | |
f75203cd | 7 | * Basavaraj Natikar <Basavaraj.Natikar@amd.com> |
4b2c53d9 SS |
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 | ||
4b2c53d9 SS |
21 | void 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 | ||
37 | int 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; | |
c092e274 | 41 | struct request_list *req_list = &cli_data->req_list; |
4b2c53d9 SS |
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; | |
c092e274 | 58 | list_add(&new->list, &req_list->list); |
4b2c53d9 SS |
59 | break; |
60 | } | |
61 | } | |
62 | schedule_delayed_work(&cli_data->work, 0); | |
63 | return 0; | |
64 | } | |
65 | ||
e7f535ea | 66 | void amd_sfh_work(struct work_struct *work) |
4b2c53d9 SS |
67 | { |
68 | struct amdtp_cl_data *cli_data = container_of(work, struct amdtp_cl_data, work.work); | |
c092e274 | 69 | struct request_list *req_list = &cli_data->req_list; |
0aad9c95 | 70 | struct amd_input_data *in_data = cli_data->in_data; |
4b2c53d9 SS |
71 | struct request_list *req_node; |
72 | u8 current_index, sensor_index; | |
786aa1b9 BN |
73 | struct amd_mp2_ops *mp2_ops; |
74 | struct amd_mp2_dev *mp2; | |
4b2c53d9 SS |
75 | u8 report_id, node_type; |
76 | u8 report_size = 0; | |
77 | ||
c092e274 | 78 | req_node = list_last_entry(&req_list->list, struct request_list, list); |
4b2c53d9 SS |
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; | |
5ad755fd | 84 | kfree(req_node); |
4b2c53d9 | 85 | |
786aa1b9 BN |
86 | mp2 = container_of(in_data, struct amd_mp2_dev, in_data); |
87 | mp2_ops = mp2->mp2_ops; | |
4b2c53d9 | 88 | if (node_type == HID_FEATURE_REPORT) { |
786aa1b9 BN |
89 | report_size = mp2_ops->get_feat_rep(sensor_index, report_id, |
90 | cli_data->feature_report[current_index]); | |
4b2c53d9 SS |
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 | else | |
96 | pr_err("AMDSFH: Invalid report size\n"); | |
97 | ||
98 | } else if (node_type == HID_INPUT_REPORT) { | |
786aa1b9 | 99 | report_size = mp2_ops->get_in_rep(current_index, sensor_index, report_id, in_data); |
4b2c53d9 SS |
100 | if (report_size) |
101 | hid_input_report(cli_data->hid_sensor_hubs[current_index], | |
102 | cli_data->report_type[current_index], | |
0aad9c95 | 103 | in_data->input_report[current_index], report_size, 0); |
4b2c53d9 SS |
104 | else |
105 | 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 | ||
e7f535ea | 112 | void amd_sfh_work_buffer(struct work_struct *work) |
4b2c53d9 SS |
113 | { |
114 | struct amdtp_cl_data *cli_data = container_of(work, struct amdtp_cl_data, work_buffer.work); | |
0aad9c95 | 115 | struct amd_input_data *in_data = cli_data->in_data; |
786aa1b9 | 116 | struct amd_mp2_dev *mp2; |
4b2c53d9 SS |
117 | u8 report_size; |
118 | int i; | |
119 | ||
120 | for (i = 0; i < cli_data->num_hid_devices; i++) { | |
173709f5 | 121 | if (cli_data->sensor_sts[i] == SENSOR_ENABLED) { |
786aa1b9 BN |
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); | |
173709f5 BN |
125 | hid_input_report(cli_data->hid_sensor_hubs[i], HID_INPUT_REPORT, |
126 | in_data->input_report[i], report_size, 0); | |
127 | } | |
4b2c53d9 SS |
128 | } |
129 | schedule_delayed_work(&cli_data->work_buffer, msecs_to_jiffies(AMD_SFH_IDLE_LOOP)); | |
130 | } | |
131 | ||
87cb7952 | 132 | static u32 amd_sfh_wait_for_response(struct amd_mp2_dev *mp2, u8 sid, u32 sensor_sts) |
173709f5 BN |
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 | ||
87cb7952 | 140 | static const char *get_sensor_name(int idx) |
696455e9 BN |
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 | return "ALS"; | |
151 | case HPD_IDX: | |
152 | return "HPD"; | |
153 | default: | |
154 | return "unknown sensor type"; | |
155 | } | |
156 | } | |
157 | ||
9acadc72 BN |
158 | static void amd_sfh_resume(struct amd_mp2_dev *mp2) |
159 | { | |
160 | struct amdtp_cl_data *cl_data = mp2->cl_data; | |
161 | struct amd_mp2_sensor_info info; | |
162 | int i, status; | |
163 | ||
164 | for (i = 0; i < cl_data->num_hid_devices; i++) { | |
165 | if (cl_data->sensor_sts[i] == SENSOR_DISABLED) { | |
166 | info.period = AMD_SFH_IDLE_LOOP; | |
167 | info.sensor_idx = cl_data->sensor_idx[i]; | |
168 | info.dma_address = cl_data->sensor_dma_addr[i]; | |
169 | mp2->mp2_ops->start(mp2, info); | |
170 | status = amd_sfh_wait_for_response | |
171 | (mp2, cl_data->sensor_idx[i], SENSOR_ENABLED); | |
172 | if (status == SENSOR_ENABLED) | |
173 | cl_data->sensor_sts[i] = SENSOR_ENABLED; | |
174 | dev_dbg(&mp2->pdev->dev, "resume sid 0x%x (%s) status 0x%x\n", | |
175 | cl_data->sensor_idx[i], get_sensor_name(cl_data->sensor_idx[i]), | |
176 | cl_data->sensor_sts[i]); | |
177 | } | |
178 | } | |
179 | ||
180 | schedule_delayed_work(&cl_data->work_buffer, msecs_to_jiffies(AMD_SFH_IDLE_LOOP)); | |
014730c4 | 181 | amd_sfh_clear_intr(mp2); |
9acadc72 BN |
182 | } |
183 | ||
184 | static void amd_sfh_suspend(struct amd_mp2_dev *mp2) | |
185 | { | |
186 | struct amdtp_cl_data *cl_data = mp2->cl_data; | |
187 | int i, status; | |
188 | ||
189 | for (i = 0; i < cl_data->num_hid_devices; i++) { | |
190 | if (cl_data->sensor_idx[i] != HPD_IDX && | |
191 | cl_data->sensor_sts[i] == SENSOR_ENABLED) { | |
192 | mp2->mp2_ops->stop(mp2, cl_data->sensor_idx[i]); | |
193 | status = amd_sfh_wait_for_response | |
194 | (mp2, cl_data->sensor_idx[i], SENSOR_DISABLED); | |
195 | if (status != SENSOR_ENABLED) | |
196 | cl_data->sensor_sts[i] = SENSOR_DISABLED; | |
197 | dev_dbg(&mp2->pdev->dev, "suspend sid 0x%x (%s) status 0x%x\n", | |
198 | cl_data->sensor_idx[i], get_sensor_name(cl_data->sensor_idx[i]), | |
199 | cl_data->sensor_sts[i]); | |
200 | } | |
201 | } | |
202 | ||
203 | cancel_delayed_work_sync(&cl_data->work_buffer); | |
014730c4 | 204 | amd_sfh_clear_intr(mp2); |
9acadc72 BN |
205 | } |
206 | ||
4b2c53d9 SS |
207 | int amd_sfh_hid_client_init(struct amd_mp2_dev *privdata) |
208 | { | |
0aad9c95 | 209 | struct amd_input_data *in_data = &privdata->in_data; |
4b2c53d9 | 210 | struct amdtp_cl_data *cl_data = privdata->cl_data; |
786aa1b9 | 211 | struct amd_mp2_ops *mp2_ops = privdata->mp2_ops; |
4b2c53d9 | 212 | struct amd_mp2_sensor_info info; |
c092e274 | 213 | struct request_list *req_list; |
4b2c53d9 SS |
214 | struct device *dev; |
215 | u32 feature_report_size; | |
216 | u32 input_report_size; | |
173709f5 | 217 | int rc, i, status; |
4b2c53d9 | 218 | u8 cl_idx; |
4b2c53d9 | 219 | |
c092e274 | 220 | req_list = &cl_data->req_list; |
4b2c53d9 | 221 | dev = &privdata->pdev->dev; |
786aa1b9 | 222 | amd_sfh_set_desc_ops(mp2_ops); |
4b2c53d9 | 223 | |
9acadc72 BN |
224 | mp2_ops->suspend = amd_sfh_suspend; |
225 | mp2_ops->resume = amd_sfh_resume; | |
226 | ||
4b2c53d9 | 227 | cl_data->num_hid_devices = amd_mp2_get_sensor_num(privdata, &cl_data->sensor_idx[0]); |
5d4d0f15 BN |
228 | if (cl_data->num_hid_devices == 0) |
229 | return -ENODEV; | |
7bcfdab3 | 230 | cl_data->is_any_sensor_enabled = false; |
4b2c53d9 SS |
231 | |
232 | INIT_DELAYED_WORK(&cl_data->work, amd_sfh_work); | |
233 | INIT_DELAYED_WORK(&cl_data->work_buffer, amd_sfh_work_buffer); | |
c092e274 | 234 | INIT_LIST_HEAD(&req_list->list); |
0aad9c95 | 235 | cl_data->in_data = in_data; |
4b2c53d9 SS |
236 | |
237 | for (i = 0; i < cl_data->num_hid_devices; i++) { | |
0aad9c95 | 238 | in_data->sensor_virt_addr[i] = dma_alloc_coherent(dev, sizeof(int) * 8, |
de30491e | 239 | &cl_data->sensor_dma_addr[i], |
4b2c53d9 | 240 | GFP_KERNEL); |
53ffa6a9 JJ |
241 | if (!in_data->sensor_virt_addr[i]) { |
242 | rc = -ENOMEM; | |
243 | goto cleanup; | |
244 | } | |
173709f5 | 245 | cl_data->sensor_sts[i] = SENSOR_DISABLED; |
4b2c53d9 SS |
246 | cl_data->sensor_requested_cnt[i] = 0; |
247 | cl_data->cur_hid_dev = i; | |
248 | cl_idx = cl_data->sensor_idx[i]; | |
786aa1b9 | 249 | cl_data->report_descr_sz[i] = mp2_ops->get_desc_sz(cl_idx, descr_size); |
4b2c53d9 SS |
250 | if (!cl_data->report_descr_sz[i]) { |
251 | rc = -EINVAL; | |
252 | goto cleanup; | |
253 | } | |
786aa1b9 | 254 | feature_report_size = mp2_ops->get_desc_sz(cl_idx, feature_size); |
4b2c53d9 SS |
255 | if (!feature_report_size) { |
256 | rc = -EINVAL; | |
257 | goto cleanup; | |
258 | } | |
786aa1b9 | 259 | input_report_size = mp2_ops->get_desc_sz(cl_idx, input_size); |
4b2c53d9 SS |
260 | if (!input_report_size) { |
261 | rc = -EINVAL; | |
262 | goto cleanup; | |
263 | } | |
e3d6a599 | 264 | cl_data->feature_report[i] = devm_kzalloc(dev, feature_report_size, GFP_KERNEL); |
4b2c53d9 SS |
265 | if (!cl_data->feature_report[i]) { |
266 | rc = -ENOMEM; | |
267 | goto cleanup; | |
268 | } | |
0aad9c95 BN |
269 | in_data->input_report[i] = devm_kzalloc(dev, input_report_size, GFP_KERNEL); |
270 | if (!in_data->input_report[i]) { | |
4b2c53d9 SS |
271 | rc = -ENOMEM; |
272 | goto cleanup; | |
273 | } | |
3978f548 | 274 | info.period = AMD_SFH_IDLE_LOOP; |
4b2c53d9 | 275 | info.sensor_idx = cl_idx; |
de30491e | 276 | info.dma_address = cl_data->sensor_dma_addr[i]; |
4b2c53d9 | 277 | |
e3d6a599 BN |
278 | cl_data->report_descr[i] = |
279 | devm_kzalloc(dev, cl_data->report_descr_sz[i], GFP_KERNEL); | |
4b2c53d9 SS |
280 | if (!cl_data->report_descr[i]) { |
281 | rc = -ENOMEM; | |
282 | goto cleanup; | |
283 | } | |
786aa1b9 | 284 | rc = mp2_ops->get_rep_desc(cl_idx, cl_data->report_descr[i]); |
4b2c53d9 | 285 | if (rc) |
2a33ad4a | 286 | goto cleanup; |
786aa1b9 | 287 | mp2_ops->start(privdata, info); |
173709f5 BN |
288 | status = amd_sfh_wait_for_response |
289 | (privdata, cl_data->sensor_idx[i], SENSOR_ENABLED); | |
ac15e919 | 290 | if (status == SENSOR_ENABLED) { |
7bcfdab3 | 291 | cl_data->is_any_sensor_enabled = true; |
173709f5 | 292 | cl_data->sensor_sts[i] = SENSOR_ENABLED; |
ac15e919 BN |
293 | rc = amdtp_hid_probe(cl_data->cur_hid_dev, cl_data); |
294 | if (rc) { | |
786aa1b9 | 295 | mp2_ops->stop(privdata, cl_data->sensor_idx[i]); |
ac15e919 BN |
296 | status = amd_sfh_wait_for_response |
297 | (privdata, cl_data->sensor_idx[i], SENSOR_DISABLED); | |
298 | if (status != SENSOR_ENABLED) | |
299 | cl_data->sensor_sts[i] = SENSOR_DISABLED; | |
696455e9 BN |
300 | dev_dbg(dev, "sid 0x%x (%s) status 0x%x\n", |
301 | cl_data->sensor_idx[i], | |
302 | get_sensor_name(cl_data->sensor_idx[i]), | |
303 | cl_data->sensor_sts[i]); | |
ac15e919 BN |
304 | goto cleanup; |
305 | } | |
7bcfdab3 ML |
306 | } else { |
307 | cl_data->sensor_sts[i] = SENSOR_DISABLED; | |
308 | dev_dbg(dev, "sid 0x%x (%s) status 0x%x\n", | |
309 | cl_data->sensor_idx[i], | |
310 | get_sensor_name(cl_data->sensor_idx[i]), | |
311 | cl_data->sensor_sts[i]); | |
ac15e919 | 312 | } |
696455e9 BN |
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]); | |
4b2c53d9 | 316 | } |
7bcfdab3 ML |
317 | if (!cl_data->is_any_sensor_enabled || |
318 | (mp2_ops->discovery_status && mp2_ops->discovery_status(privdata) == 0)) { | |
b5d7f43e BN |
319 | amd_sfh_hid_client_deinit(privdata); |
320 | for (i = 0; i < cl_data->num_hid_devices; i++) { | |
321 | devm_kfree(dev, cl_data->feature_report[i]); | |
322 | devm_kfree(dev, in_data->input_report[i]); | |
323 | devm_kfree(dev, cl_data->report_descr[i]); | |
324 | } | |
7bcfdab3 | 325 | dev_warn(dev, "Failed to discover, sensors not enabled is %d\n", cl_data->is_any_sensor_enabled); |
b5d7f43e BN |
326 | return -EOPNOTSUPP; |
327 | } | |
4b2c53d9 SS |
328 | schedule_delayed_work(&cl_data->work_buffer, msecs_to_jiffies(AMD_SFH_IDLE_LOOP)); |
329 | return 0; | |
330 | ||
331 | cleanup: | |
332 | for (i = 0; i < cl_data->num_hid_devices; i++) { | |
0aad9c95 | 333 | if (in_data->sensor_virt_addr[i]) { |
4b2c53d9 | 334 | dma_free_coherent(&privdata->pdev->dev, 8 * sizeof(int), |
0aad9c95 | 335 | in_data->sensor_virt_addr[i], |
de30491e | 336 | cl_data->sensor_dma_addr[i]); |
4b2c53d9 | 337 | } |
e3d6a599 | 338 | devm_kfree(dev, cl_data->feature_report[i]); |
0aad9c95 | 339 | devm_kfree(dev, in_data->input_report[i]); |
e3d6a599 | 340 | devm_kfree(dev, cl_data->report_descr[i]); |
4b2c53d9 | 341 | } |
4b2c53d9 SS |
342 | return rc; |
343 | } | |
344 | ||
345 | int amd_sfh_hid_client_deinit(struct amd_mp2_dev *privdata) | |
346 | { | |
347 | struct amdtp_cl_data *cl_data = privdata->cl_data; | |
0aad9c95 | 348 | struct amd_input_data *in_data = cl_data->in_data; |
173709f5 | 349 | int i, status; |
4b2c53d9 | 350 | |
173709f5 BN |
351 | for (i = 0; i < cl_data->num_hid_devices; i++) { |
352 | if (cl_data->sensor_sts[i] == SENSOR_ENABLED) { | |
353 | privdata->mp2_ops->stop(privdata, cl_data->sensor_idx[i]); | |
354 | status = amd_sfh_wait_for_response | |
355 | (privdata, cl_data->sensor_idx[i], SENSOR_DISABLED); | |
356 | if (status != SENSOR_ENABLED) | |
357 | cl_data->sensor_sts[i] = SENSOR_DISABLED; | |
696455e9 BN |
358 | dev_dbg(&privdata->pdev->dev, "stopping sid 0x%x (%s) status 0x%x\n", |
359 | cl_data->sensor_idx[i], get_sensor_name(cl_data->sensor_idx[i]), | |
360 | cl_data->sensor_sts[i]); | |
173709f5 BN |
361 | } |
362 | } | |
4b2c53d9 SS |
363 | |
364 | cancel_delayed_work_sync(&cl_data->work); | |
365 | cancel_delayed_work_sync(&cl_data->work_buffer); | |
366 | amdtp_hid_remove(cl_data); | |
367 | ||
368 | for (i = 0; i < cl_data->num_hid_devices; i++) { | |
0aad9c95 | 369 | if (in_data->sensor_virt_addr[i]) { |
4b2c53d9 | 370 | dma_free_coherent(&privdata->pdev->dev, 8 * sizeof(int), |
0aad9c95 | 371 | in_data->sensor_virt_addr[i], |
de30491e | 372 | cl_data->sensor_dma_addr[i]); |
4b2c53d9 SS |
373 | } |
374 | } | |
4b2c53d9 SS |
375 | return 0; |
376 | } |