xref: /linux/drivers/iio/accel/hid-sensor-accel-3d.c (revision 9825cf2cb59fac7480e7fac9eee13ab9af3f1ea8)
1 // SPDX-License-Identifier: GPL-2.0-only
2 /*
3  * HID Sensors Driver
4  * Copyright (c) 2012, Intel Corporation.
5  */
6 #include <linux/bitops.h>
7 #include <linux/device.h>
8 #include <linux/platform_device.h>
9 #include <linux/module.h>
10 #include <linux/mod_devicetable.h>
11 #include <linux/slab.h>
12 #include <linux/hid-sensor-hub.h>
13 #include <linux/iio/iio.h>
14 #include <linux/iio/buffer.h>
15 #include "../common/hid-sensors/hid-sensor-trigger.h"
16 
17 enum accel_3d_channel {
18 	CHANNEL_SCAN_INDEX_X,
19 	CHANNEL_SCAN_INDEX_Y,
20 	CHANNEL_SCAN_INDEX_Z,
21 	ACCEL_3D_CHANNEL_MAX,
22 };
23 
24 #define CHANNEL_SCAN_INDEX_TIMESTAMP ACCEL_3D_CHANNEL_MAX
25 struct accel_3d_state {
26 	struct hid_sensor_hub_callbacks callbacks;
27 	struct hid_sensor_common common_attributes;
28 	struct hid_sensor_hub_attribute_info accel[ACCEL_3D_CHANNEL_MAX];
29 	/* Ensure timestamp is naturally aligned */
30 	struct {
31 		u32 accel_val[3];
32 		aligned_s64 timestamp;
33 	} scan;
34 	int scale_pre_decml;
35 	int scale_post_decml;
36 	int scale_precision;
37 	int value_offset;
38 	int64_t timestamp;
39 };
40 
41 static const u32 accel_3d_addresses[ACCEL_3D_CHANNEL_MAX] = {
42 	HID_USAGE_SENSOR_ACCEL_X_AXIS,
43 	HID_USAGE_SENSOR_ACCEL_Y_AXIS,
44 	HID_USAGE_SENSOR_ACCEL_Z_AXIS
45 };
46 
47 static const u32 accel_3d_sensitivity_addresses[] = {
48 	HID_USAGE_SENSOR_DATA_ACCELERATION,
49 };
50 
51 /* Channel definitions */
52 static const struct iio_chan_spec accel_3d_channels[] = {
53 	{
54 		.type = IIO_ACCEL,
55 		.modified = 1,
56 		.channel2 = IIO_MOD_X,
57 		.info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
58 		.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_OFFSET) |
59 		BIT(IIO_CHAN_INFO_SCALE) |
60 		BIT(IIO_CHAN_INFO_SAMP_FREQ) |
61 		BIT(IIO_CHAN_INFO_HYSTERESIS),
62 		.scan_index = CHANNEL_SCAN_INDEX_X,
63 	}, {
64 		.type = IIO_ACCEL,
65 		.modified = 1,
66 		.channel2 = IIO_MOD_Y,
67 		.info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
68 		.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_OFFSET) |
69 		BIT(IIO_CHAN_INFO_SCALE) |
70 		BIT(IIO_CHAN_INFO_SAMP_FREQ) |
71 		BIT(IIO_CHAN_INFO_HYSTERESIS),
72 		.scan_index = CHANNEL_SCAN_INDEX_Y,
73 	}, {
74 		.type = IIO_ACCEL,
75 		.modified = 1,
76 		.channel2 = IIO_MOD_Z,
77 		.info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
78 		.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_OFFSET) |
79 		BIT(IIO_CHAN_INFO_SCALE) |
80 		BIT(IIO_CHAN_INFO_SAMP_FREQ) |
81 		BIT(IIO_CHAN_INFO_HYSTERESIS),
82 		.scan_index = CHANNEL_SCAN_INDEX_Z,
83 	},
84 	IIO_CHAN_SOFT_TIMESTAMP(CHANNEL_SCAN_INDEX_TIMESTAMP)
85 };
86 
87 /* Channel definitions */
88 static const struct iio_chan_spec gravity_channels[] = {
89 	{
90 		.type = IIO_GRAVITY,
91 		.modified = 1,
92 		.channel2 = IIO_MOD_X,
93 		.info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
94 		.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_OFFSET) |
95 		BIT(IIO_CHAN_INFO_SCALE) |
96 		BIT(IIO_CHAN_INFO_SAMP_FREQ) |
97 		BIT(IIO_CHAN_INFO_HYSTERESIS),
98 		.scan_index = CHANNEL_SCAN_INDEX_X,
99 	}, {
100 		.type = IIO_GRAVITY,
101 		.modified = 1,
102 		.channel2 = IIO_MOD_Y,
103 		.info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
104 		.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_OFFSET) |
105 		BIT(IIO_CHAN_INFO_SCALE) |
106 		BIT(IIO_CHAN_INFO_SAMP_FREQ) |
107 		BIT(IIO_CHAN_INFO_HYSTERESIS),
108 		.scan_index = CHANNEL_SCAN_INDEX_Y,
109 	}, {
110 		.type = IIO_GRAVITY,
111 		.modified = 1,
112 		.channel2 = IIO_MOD_Z,
113 		.info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
114 		.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_OFFSET) |
115 		BIT(IIO_CHAN_INFO_SCALE) |
116 		BIT(IIO_CHAN_INFO_SAMP_FREQ) |
117 		BIT(IIO_CHAN_INFO_HYSTERESIS),
118 		.scan_index = CHANNEL_SCAN_INDEX_Z,
119 	},
120 	IIO_CHAN_SOFT_TIMESTAMP(CHANNEL_SCAN_INDEX_TIMESTAMP),
121 };
122 
123 /* Channel read_raw handler */
124 static int accel_3d_read_raw(struct iio_dev *indio_dev,
125 			      struct iio_chan_spec const *chan,
126 			      int *val, int *val2,
127 			      long mask)
128 {
129 	struct accel_3d_state *accel_state = iio_priv(indio_dev);
130 	int report_id = -1;
131 	u32 address;
132 	int ret_type;
133 	s32 min;
134 	struct hid_sensor_hub_device *hsdev =
135 					accel_state->common_attributes.hsdev;
136 
137 	*val = 0;
138 	*val2 = 0;
139 	switch (mask) {
140 	case IIO_CHAN_INFO_RAW:
141 		hid_sensor_power_state(&accel_state->common_attributes, true);
142 		report_id = accel_state->accel[chan->scan_index].report_id;
143 		min = accel_state->accel[chan->scan_index].logical_minimum;
144 		address = accel_3d_addresses[chan->scan_index];
145 		if (report_id >= 0)
146 			*val = sensor_hub_input_attr_get_raw_value(
147 					accel_state->common_attributes.hsdev,
148 					hsdev->usage, address, report_id,
149 					SENSOR_HUB_SYNC,
150 					min < 0);
151 		else {
152 			*val = 0;
153 			hid_sensor_power_state(&accel_state->common_attributes,
154 						 false);
155 			return -EINVAL;
156 		}
157 		hid_sensor_power_state(&accel_state->common_attributes, false);
158 		ret_type = IIO_VAL_INT;
159 		break;
160 	case IIO_CHAN_INFO_SCALE:
161 		*val = accel_state->scale_pre_decml;
162 		*val2 = accel_state->scale_post_decml;
163 		ret_type = accel_state->scale_precision;
164 		break;
165 	case IIO_CHAN_INFO_OFFSET:
166 		*val = accel_state->value_offset;
167 		ret_type = IIO_VAL_INT;
168 		break;
169 	case IIO_CHAN_INFO_SAMP_FREQ:
170 		ret_type = hid_sensor_read_samp_freq_value(
171 			&accel_state->common_attributes, val, val2);
172 		break;
173 	case IIO_CHAN_INFO_HYSTERESIS:
174 		ret_type = hid_sensor_read_raw_hyst_value(
175 			&accel_state->common_attributes, val, val2);
176 		break;
177 	default:
178 		ret_type = -EINVAL;
179 		break;
180 	}
181 
182 	return ret_type;
183 }
184 
185 /* Channel write_raw handler */
186 static int accel_3d_write_raw(struct iio_dev *indio_dev,
187 			       struct iio_chan_spec const *chan,
188 			       int val,
189 			       int val2,
190 			       long mask)
191 {
192 	struct accel_3d_state *accel_state = iio_priv(indio_dev);
193 	int ret = 0;
194 
195 	switch (mask) {
196 	case IIO_CHAN_INFO_SAMP_FREQ:
197 		ret = hid_sensor_write_samp_freq_value(
198 				&accel_state->common_attributes, val, val2);
199 		break;
200 	case IIO_CHAN_INFO_HYSTERESIS:
201 		ret = hid_sensor_write_raw_hyst_value(
202 				&accel_state->common_attributes, val, val2);
203 		break;
204 	default:
205 		ret = -EINVAL;
206 	}
207 
208 	return ret;
209 }
210 
211 static const struct iio_info accel_3d_info = {
212 	.read_raw = &accel_3d_read_raw,
213 	.write_raw = &accel_3d_write_raw,
214 };
215 
216 /* Function to push data to buffer */
217 static void hid_sensor_push_data(struct iio_dev *indio_dev, void *data,
218 				 int len, int64_t timestamp)
219 {
220 	dev_dbg(&indio_dev->dev, "hid_sensor_push_data\n");
221 	iio_push_to_buffers_with_ts(indio_dev, data, len, timestamp);
222 }
223 
224 /* Callback handler to send event after all samples are received and captured */
225 static int accel_3d_proc_event(struct hid_sensor_hub_device *hsdev,
226 				unsigned usage_id,
227 				void *priv)
228 {
229 	struct iio_dev *indio_dev = platform_get_drvdata(priv);
230 	struct accel_3d_state *accel_state = iio_priv(indio_dev);
231 
232 	dev_dbg(&indio_dev->dev, "accel_3d_proc_event\n");
233 	if (atomic_read(&accel_state->common_attributes.data_ready)) {
234 		if (!accel_state->timestamp)
235 			accel_state->timestamp = iio_get_time_ns(indio_dev);
236 
237 		hid_sensor_push_data(indio_dev,
238 				     &accel_state->scan,
239 				     sizeof(accel_state->scan),
240 				     accel_state->timestamp);
241 
242 		accel_state->timestamp = 0;
243 	}
244 
245 	return 0;
246 }
247 
248 /* Capture samples in local storage */
249 static int accel_3d_capture_sample(struct hid_sensor_hub_device *hsdev,
250 				unsigned usage_id,
251 				size_t raw_len, char *raw_data,
252 				void *priv)
253 {
254 	struct iio_dev *indio_dev = platform_get_drvdata(priv);
255 	struct accel_3d_state *accel_state = iio_priv(indio_dev);
256 	int offset;
257 	int ret = -EINVAL;
258 
259 	switch (usage_id) {
260 	case HID_USAGE_SENSOR_ACCEL_X_AXIS:
261 	case HID_USAGE_SENSOR_ACCEL_Y_AXIS:
262 	case HID_USAGE_SENSOR_ACCEL_Z_AXIS:
263 		offset = usage_id - HID_USAGE_SENSOR_ACCEL_X_AXIS;
264 		accel_state->scan.accel_val[CHANNEL_SCAN_INDEX_X + offset] =
265 						*(u32 *)raw_data;
266 		ret = 0;
267 	break;
268 	case HID_USAGE_SENSOR_TIME_TIMESTAMP:
269 		accel_state->timestamp =
270 			hid_sensor_convert_timestamp(
271 					&accel_state->common_attributes,
272 					*(int64_t *)raw_data);
273 		ret = 0;
274 	break;
275 	default:
276 		break;
277 	}
278 
279 	return ret;
280 }
281 
282 /* Parse report which is specific to an usage id*/
283 static int accel_3d_parse_report(struct platform_device *pdev,
284 				struct hid_sensor_hub_device *hsdev,
285 				struct iio_chan_spec *channels,
286 				unsigned usage_id,
287 				struct accel_3d_state *st)
288 {
289 	int ret;
290 
291 	for (unsigned int ch = CHANNEL_SCAN_INDEX_X; ch <= CHANNEL_SCAN_INDEX_Z; ch++) {
292 		ret = sensor_hub_input_get_attribute_info(hsdev,
293 				HID_INPUT_REPORT,
294 				usage_id,
295 				HID_USAGE_SENSOR_ACCEL_X_AXIS + ch,
296 				&st->accel[ch]);
297 		if (ret < 0)
298 			break;
299 		channels[ch].scan_type = (struct iio_scan_type) {
300 			.format = 's',
301 			.realbits = BYTES_TO_BITS(st->accel[ch].size),
302 			.storagebits = BITS_PER_TYPE(u32),
303 		};
304 	}
305 	dev_dbg(&pdev->dev, "accel_3d %x:%x, %x:%x, %x:%x\n",
306 			st->accel[0].index,
307 			st->accel[0].report_id,
308 			st->accel[1].index, st->accel[1].report_id,
309 			st->accel[2].index, st->accel[2].report_id);
310 
311 	st->scale_precision = hid_sensor_format_scale(
312 				hsdev->usage,
313 				&st->accel[CHANNEL_SCAN_INDEX_X],
314 				&st->scale_pre_decml, &st->scale_post_decml);
315 
316 	return ret;
317 }
318 
319 /* Function to initialize the processing for usage id */
320 static int hid_accel_3d_probe(struct platform_device *pdev)
321 {
322 	struct hid_sensor_hub_device *hsdev = dev_get_platdata(&pdev->dev);
323 	int ret = 0;
324 	const char *name;
325 	struct iio_dev *indio_dev;
326 	struct accel_3d_state *accel_state;
327 	const struct iio_chan_spec *channel_spec;
328 	int channel_size;
329 
330 	indio_dev = devm_iio_device_alloc(&pdev->dev,
331 					  sizeof(struct accel_3d_state));
332 	if (indio_dev == NULL)
333 		return -ENOMEM;
334 
335 	platform_set_drvdata(pdev, indio_dev);
336 
337 	accel_state = iio_priv(indio_dev);
338 	accel_state->common_attributes.hsdev = hsdev;
339 	accel_state->common_attributes.pdev = pdev;
340 
341 	if (hsdev->usage == HID_USAGE_SENSOR_ACCEL_3D) {
342 		name = "accel_3d";
343 		channel_spec = accel_3d_channels;
344 		channel_size = sizeof(accel_3d_channels);
345 		indio_dev->num_channels = ARRAY_SIZE(accel_3d_channels);
346 	} else {
347 		name = "gravity";
348 		channel_spec = gravity_channels;
349 		channel_size = sizeof(gravity_channels);
350 		indio_dev->num_channels = ARRAY_SIZE(gravity_channels);
351 	}
352 	ret = hid_sensor_parse_common_attributes(hsdev,
353 						 hsdev->usage,
354 						 &accel_state->common_attributes,
355 						 accel_3d_sensitivity_addresses,
356 						 ARRAY_SIZE(accel_3d_sensitivity_addresses));
357 	if (ret) {
358 		dev_err(&pdev->dev, "failed to setup common attributes\n");
359 		return ret;
360 	}
361 	indio_dev->channels = devm_kmemdup(&pdev->dev, channel_spec,
362 					   channel_size, GFP_KERNEL);
363 
364 	if (!indio_dev->channels) {
365 		dev_err(&pdev->dev, "failed to duplicate channels\n");
366 		return -ENOMEM;
367 	}
368 	ret = accel_3d_parse_report(pdev, hsdev,
369 				(struct iio_chan_spec *)indio_dev->channels,
370 				hsdev->usage, accel_state);
371 	if (ret) {
372 		dev_err(&pdev->dev, "failed to setup attributes\n");
373 		return ret;
374 	}
375 
376 	indio_dev->info = &accel_3d_info;
377 	indio_dev->name = name;
378 	indio_dev->modes = INDIO_DIRECT_MODE;
379 
380 	atomic_set(&accel_state->common_attributes.data_ready, 0);
381 
382 	ret = hid_sensor_setup_trigger(indio_dev, name,
383 					&accel_state->common_attributes);
384 	if (ret < 0) {
385 		dev_err(&pdev->dev, "trigger setup failed\n");
386 		return ret;
387 	}
388 
389 	ret = iio_device_register(indio_dev);
390 	if (ret) {
391 		dev_err(&pdev->dev, "device register failed\n");
392 		goto error_remove_trigger;
393 	}
394 
395 	accel_state->callbacks.send_event = accel_3d_proc_event;
396 	accel_state->callbacks.capture_sample = accel_3d_capture_sample;
397 	accel_state->callbacks.pdev = pdev;
398 	ret = sensor_hub_register_callback(hsdev, hsdev->usage,
399 					&accel_state->callbacks);
400 	if (ret < 0) {
401 		dev_err(&pdev->dev, "callback reg failed\n");
402 		goto error_iio_unreg;
403 	}
404 
405 	return ret;
406 
407 error_iio_unreg:
408 	iio_device_unregister(indio_dev);
409 error_remove_trigger:
410 	hid_sensor_remove_trigger(indio_dev, &accel_state->common_attributes);
411 	return ret;
412 }
413 
414 /* Function to deinitialize the processing for usage id */
415 static void hid_accel_3d_remove(struct platform_device *pdev)
416 {
417 	struct hid_sensor_hub_device *hsdev = dev_get_platdata(&pdev->dev);
418 	struct iio_dev *indio_dev = platform_get_drvdata(pdev);
419 	struct accel_3d_state *accel_state = iio_priv(indio_dev);
420 
421 	sensor_hub_remove_callback(hsdev, hsdev->usage);
422 	iio_device_unregister(indio_dev);
423 	hid_sensor_remove_trigger(indio_dev, &accel_state->common_attributes);
424 }
425 
426 static const struct platform_device_id hid_accel_3d_ids[] = {
427 	{
428 		/* Format: HID-SENSOR-usage_id_in_hex_lowercase */
429 		.name = "HID-SENSOR-200073",
430 	},
431 	{	/* gravity sensor */
432 		.name = "HID-SENSOR-20007b",
433 	},
434 	{ }
435 };
436 MODULE_DEVICE_TABLE(platform, hid_accel_3d_ids);
437 
438 static struct platform_driver hid_accel_3d_platform_driver = {
439 	.id_table = hid_accel_3d_ids,
440 	.driver = {
441 		.name	= KBUILD_MODNAME,
442 		.pm	= &hid_sensor_pm_ops,
443 	},
444 	.probe		= hid_accel_3d_probe,
445 	.remove		= hid_accel_3d_remove,
446 };
447 module_platform_driver(hid_accel_3d_platform_driver);
448 
449 MODULE_DESCRIPTION("HID Sensor Accel 3D");
450 MODULE_AUTHOR("Srinivas Pandruvada <srinivas.pandruvada@intel.com>");
451 MODULE_LICENSE("GPL");
452 MODULE_IMPORT_NS("IIO_HID");
453