1 | // SPDX-License-Identifier: GPL-2.0-or-later |
2 | /* |
3 | * AMD SFH Client Layer |
4 | * 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 | |
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; |
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(size: 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: &new->list, head: &req_list->list); |
59 | break; |
60 | } |
61 | } |
62 | schedule_delayed_work(dwork: &cli_data->work, delay: 0); |
63 | return 0; |
64 | } |
65 | |
66 | void 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(entry: &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(objp: 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(hid: cli_data->hid_sensor_hubs[current_index], |
93 | type: cli_data->report_type[current_index], |
94 | data: cli_data->feature_report[current_index], size: report_size, interrupt: 0); |
95 | else |
96 | 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(hid: cli_data->hid_sensor_hubs[current_index], |
102 | type: cli_data->report_type[current_index], |
103 | data: in_data->input_report[current_index], size: report_size, interrupt: 0); |
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(hid: cli_data->hid_sensor_hubs[current_index]); |
110 | } |
111 | |
112 | void 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(hid: cli_data->hid_sensor_hubs[i], type: HID_INPUT_REPORT, |
126 | data: in_data->input_report[i], size: report_size, interrupt: 0); |
127 | } |
128 | } |
129 | schedule_delayed_work(dwork: &cli_data->work_buffer, delay: msecs_to_jiffies(AMD_SFH_IDLE_LOOP)); |
130 | } |
131 | |
132 | static 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 | |
140 | static 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 | |
159 | static 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_response |
172 | (mp2, sid: 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(dwork: &cl_data->work_buffer, delay: msecs_to_jiffies(AMD_SFH_IDLE_LOOP)); |
182 | amd_sfh_clear_intr(privdata: mp2); |
183 | } |
184 | |
185 | static 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_response |
195 | (mp2, sid: 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(dwork: &cl_data->work_buffer); |
205 | amd_sfh_clear_intr(privdata: mp2); |
206 | } |
207 | |
208 | int 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, sensor_id: &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(list: &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] = dma_alloc_coherent(dev, size: sizeof(int) * 8, |
240 | dma_handle: &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, size: 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, size: 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, size: 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_response |
290 | (mp2: privdata, sid: cl_data->sensor_idx[i], SENSOR_ENABLED); |
291 | } |
292 | |
293 | for (i = 0; i < cl_data->num_hid_devices; i++) { |
294 | cl_data->cur_hid_dev = i; |
295 | if (cl_data->sensor_sts[i] == SENSOR_ENABLED) { |
296 | cl_data->is_any_sensor_enabled = true; |
297 | rc = amdtp_hid_probe(cur_hid_dev: i, cli_data: cl_data); |
298 | if (rc) |
299 | goto cleanup; |
300 | } else { |
301 | cl_data->sensor_sts[i] = SENSOR_DISABLED; |
302 | } |
303 | dev_dbg(dev, "sid 0x%x (%s) status 0x%x\n" , |
304 | cl_data->sensor_idx[i], get_sensor_name(cl_data->sensor_idx[i]), |
305 | cl_data->sensor_sts[i]); |
306 | } |
307 | |
308 | if (!cl_data->is_any_sensor_enabled || |
309 | (mp2_ops->discovery_status && mp2_ops->discovery_status(privdata) == 0)) { |
310 | dev_warn(dev, "Failed to discover, sensors not enabled is %d\n" , cl_data->is_any_sensor_enabled); |
311 | rc = -EOPNOTSUPP; |
312 | goto cleanup; |
313 | } |
314 | schedule_delayed_work(dwork: &cl_data->work_buffer, delay: msecs_to_jiffies(AMD_SFH_IDLE_LOOP)); |
315 | return 0; |
316 | |
317 | cleanup: |
318 | amd_sfh_hid_client_deinit(privdata); |
319 | for (i = 0; i < cl_data->num_hid_devices; i++) { |
320 | devm_kfree(dev, p: cl_data->feature_report[i]); |
321 | devm_kfree(dev, p: in_data->input_report[i]); |
322 | devm_kfree(dev, p: cl_data->report_descr[i]); |
323 | } |
324 | return rc; |
325 | } |
326 | |
327 | int amd_sfh_hid_client_deinit(struct amd_mp2_dev *privdata) |
328 | { |
329 | struct amdtp_cl_data *cl_data = privdata->cl_data; |
330 | struct amd_input_data *in_data = cl_data->in_data; |
331 | int i, status; |
332 | |
333 | for (i = 0; i < cl_data->num_hid_devices; i++) { |
334 | if (cl_data->sensor_sts[i] == SENSOR_ENABLED) { |
335 | privdata->mp2_ops->stop(privdata, cl_data->sensor_idx[i]); |
336 | status = amd_sfh_wait_for_response |
337 | (mp2: privdata, sid: cl_data->sensor_idx[i], SENSOR_DISABLED); |
338 | if (status != SENSOR_ENABLED) |
339 | cl_data->sensor_sts[i] = SENSOR_DISABLED; |
340 | dev_dbg(&privdata->pdev->dev, "stopping sid 0x%x (%s) status 0x%x\n" , |
341 | cl_data->sensor_idx[i], get_sensor_name(cl_data->sensor_idx[i]), |
342 | cl_data->sensor_sts[i]); |
343 | } |
344 | } |
345 | |
346 | cancel_delayed_work_sync(dwork: &cl_data->work); |
347 | cancel_delayed_work_sync(dwork: &cl_data->work_buffer); |
348 | amdtp_hid_remove(cli_data: cl_data); |
349 | |
350 | for (i = 0; i < cl_data->num_hid_devices; i++) { |
351 | if (in_data->sensor_virt_addr[i]) { |
352 | dma_free_coherent(dev: &privdata->pdev->dev, size: 8 * sizeof(int), |
353 | cpu_addr: in_data->sensor_virt_addr[i], |
354 | dma_handle: cl_data->sensor_dma_addr[i]); |
355 | } |
356 | } |
357 | return 0; |
358 | } |
359 | |