hid-sensor-incl-3d.c (11633B)
1// SPDX-License-Identifier: GPL-2.0-only 2/* 3 * HID Sensors Driver 4 * Copyright (c) 2013, Intel Corporation. 5 */ 6 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 17enum incl_3d_channel { 18 CHANNEL_SCAN_INDEX_X, 19 CHANNEL_SCAN_INDEX_Y, 20 CHANNEL_SCAN_INDEX_Z, 21 INCLI_3D_CHANNEL_MAX, 22}; 23 24#define CHANNEL_SCAN_INDEX_TIMESTAMP INCLI_3D_CHANNEL_MAX 25 26struct incl_3d_state { 27 struct hid_sensor_hub_callbacks callbacks; 28 struct hid_sensor_common common_attributes; 29 struct hid_sensor_hub_attribute_info incl[INCLI_3D_CHANNEL_MAX]; 30 struct { 31 u32 incl_val[INCLI_3D_CHANNEL_MAX]; 32 u64 timestamp __aligned(8); 33 } scan; 34 int scale_pre_decml; 35 int scale_post_decml; 36 int scale_precision; 37 int value_offset; 38 s64 timestamp; 39}; 40 41static const u32 incl_3d_addresses[INCLI_3D_CHANNEL_MAX] = { 42 HID_USAGE_SENSOR_ORIENT_TILT_X, 43 HID_USAGE_SENSOR_ORIENT_TILT_Y, 44 HID_USAGE_SENSOR_ORIENT_TILT_Z 45}; 46 47static const u32 incl_3d_sensitivity_addresses[] = { 48 HID_USAGE_SENSOR_DATA_ORIENTATION, 49 HID_USAGE_SENSOR_ORIENT_TILT, 50}; 51 52/* Channel definitions */ 53static const struct iio_chan_spec incl_3d_channels[] = { 54 { 55 .type = IIO_INCLI, 56 .modified = 1, 57 .channel2 = IIO_MOD_X, 58 .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), 59 .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_OFFSET) | 60 BIT(IIO_CHAN_INFO_SCALE) | 61 BIT(IIO_CHAN_INFO_SAMP_FREQ) | 62 BIT(IIO_CHAN_INFO_HYSTERESIS), 63 .scan_index = CHANNEL_SCAN_INDEX_X, 64 }, { 65 .type = IIO_INCLI, 66 .modified = 1, 67 .channel2 = IIO_MOD_Y, 68 .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), 69 .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_OFFSET) | 70 BIT(IIO_CHAN_INFO_SCALE) | 71 BIT(IIO_CHAN_INFO_SAMP_FREQ) | 72 BIT(IIO_CHAN_INFO_HYSTERESIS), 73 .scan_index = CHANNEL_SCAN_INDEX_Y, 74 }, { 75 .type = IIO_INCLI, 76 .modified = 1, 77 .channel2 = IIO_MOD_Z, 78 .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), 79 .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_OFFSET) | 80 BIT(IIO_CHAN_INFO_SCALE) | 81 BIT(IIO_CHAN_INFO_SAMP_FREQ) | 82 BIT(IIO_CHAN_INFO_HYSTERESIS), 83 .scan_index = CHANNEL_SCAN_INDEX_Z, 84 }, 85 IIO_CHAN_SOFT_TIMESTAMP(CHANNEL_SCAN_INDEX_TIMESTAMP), 86}; 87 88/* Adjust channel real bits based on report descriptor */ 89static void incl_3d_adjust_channel_bit_mask(struct iio_chan_spec *chan, 90 int size) 91{ 92 chan->scan_type.sign = 's'; 93 /* Real storage bits will change based on the report desc. */ 94 chan->scan_type.realbits = size * 8; 95 /* Maximum size of a sample to capture is u32 */ 96 chan->scan_type.storagebits = sizeof(u32) * 8; 97} 98 99/* Channel read_raw handler */ 100static int incl_3d_read_raw(struct iio_dev *indio_dev, 101 struct iio_chan_spec const *chan, 102 int *val, int *val2, 103 long mask) 104{ 105 struct incl_3d_state *incl_state = iio_priv(indio_dev); 106 int report_id = -1; 107 u32 address; 108 int ret_type; 109 s32 min; 110 111 *val = 0; 112 *val2 = 0; 113 switch (mask) { 114 case IIO_CHAN_INFO_RAW: 115 hid_sensor_power_state(&incl_state->common_attributes, true); 116 report_id = incl_state->incl[chan->scan_index].report_id; 117 min = incl_state->incl[chan->scan_index].logical_minimum; 118 address = incl_3d_addresses[chan->scan_index]; 119 if (report_id >= 0) 120 *val = sensor_hub_input_attr_get_raw_value( 121 incl_state->common_attributes.hsdev, 122 HID_USAGE_SENSOR_INCLINOMETER_3D, address, 123 report_id, 124 SENSOR_HUB_SYNC, 125 min < 0); 126 else { 127 hid_sensor_power_state(&incl_state->common_attributes, 128 false); 129 return -EINVAL; 130 } 131 hid_sensor_power_state(&incl_state->common_attributes, false); 132 ret_type = IIO_VAL_INT; 133 break; 134 case IIO_CHAN_INFO_SCALE: 135 *val = incl_state->scale_pre_decml; 136 *val2 = incl_state->scale_post_decml; 137 ret_type = incl_state->scale_precision; 138 break; 139 case IIO_CHAN_INFO_OFFSET: 140 *val = incl_state->value_offset; 141 ret_type = IIO_VAL_INT; 142 break; 143 case IIO_CHAN_INFO_SAMP_FREQ: 144 ret_type = hid_sensor_read_samp_freq_value( 145 &incl_state->common_attributes, val, val2); 146 break; 147 case IIO_CHAN_INFO_HYSTERESIS: 148 ret_type = hid_sensor_read_raw_hyst_value( 149 &incl_state->common_attributes, val, val2); 150 break; 151 default: 152 ret_type = -EINVAL; 153 break; 154 } 155 156 return ret_type; 157} 158 159/* Channel write_raw handler */ 160static int incl_3d_write_raw(struct iio_dev *indio_dev, 161 struct iio_chan_spec const *chan, 162 int val, 163 int val2, 164 long mask) 165{ 166 struct incl_3d_state *incl_state = iio_priv(indio_dev); 167 int ret; 168 169 switch (mask) { 170 case IIO_CHAN_INFO_SAMP_FREQ: 171 ret = hid_sensor_write_samp_freq_value( 172 &incl_state->common_attributes, val, val2); 173 break; 174 case IIO_CHAN_INFO_HYSTERESIS: 175 ret = hid_sensor_write_raw_hyst_value( 176 &incl_state->common_attributes, val, val2); 177 break; 178 default: 179 ret = -EINVAL; 180 } 181 182 return ret; 183} 184 185static const struct iio_info incl_3d_info = { 186 .read_raw = &incl_3d_read_raw, 187 .write_raw = &incl_3d_write_raw, 188}; 189 190/* Callback handler to send event after all samples are received and captured */ 191static int incl_3d_proc_event(struct hid_sensor_hub_device *hsdev, 192 unsigned usage_id, 193 void *priv) 194{ 195 struct iio_dev *indio_dev = platform_get_drvdata(priv); 196 struct incl_3d_state *incl_state = iio_priv(indio_dev); 197 198 dev_dbg(&indio_dev->dev, "incl_3d_proc_event\n"); 199 if (atomic_read(&incl_state->common_attributes.data_ready)) { 200 if (!incl_state->timestamp) 201 incl_state->timestamp = iio_get_time_ns(indio_dev); 202 203 iio_push_to_buffers_with_timestamp(indio_dev, 204 &incl_state->scan, 205 incl_state->timestamp); 206 207 incl_state->timestamp = 0; 208 } 209 210 return 0; 211} 212 213/* Capture samples in local storage */ 214static int incl_3d_capture_sample(struct hid_sensor_hub_device *hsdev, 215 unsigned usage_id, 216 size_t raw_len, char *raw_data, 217 void *priv) 218{ 219 struct iio_dev *indio_dev = platform_get_drvdata(priv); 220 struct incl_3d_state *incl_state = iio_priv(indio_dev); 221 int ret = 0; 222 223 switch (usage_id) { 224 case HID_USAGE_SENSOR_ORIENT_TILT_X: 225 incl_state->scan.incl_val[CHANNEL_SCAN_INDEX_X] = *(u32 *)raw_data; 226 break; 227 case HID_USAGE_SENSOR_ORIENT_TILT_Y: 228 incl_state->scan.incl_val[CHANNEL_SCAN_INDEX_Y] = *(u32 *)raw_data; 229 break; 230 case HID_USAGE_SENSOR_ORIENT_TILT_Z: 231 incl_state->scan.incl_val[CHANNEL_SCAN_INDEX_Z] = *(u32 *)raw_data; 232 break; 233 case HID_USAGE_SENSOR_TIME_TIMESTAMP: 234 incl_state->timestamp = 235 hid_sensor_convert_timestamp(&incl_state->common_attributes, 236 *(s64 *)raw_data); 237 break; 238 default: 239 ret = -EINVAL; 240 break; 241 } 242 243 return ret; 244} 245 246/* Parse report which is specific to an usage id*/ 247static int incl_3d_parse_report(struct platform_device *pdev, 248 struct hid_sensor_hub_device *hsdev, 249 struct iio_chan_spec *channels, 250 unsigned usage_id, 251 struct incl_3d_state *st) 252{ 253 int ret; 254 255 ret = sensor_hub_input_get_attribute_info(hsdev, 256 HID_INPUT_REPORT, 257 usage_id, 258 HID_USAGE_SENSOR_ORIENT_TILT_X, 259 &st->incl[CHANNEL_SCAN_INDEX_X]); 260 if (ret) 261 return ret; 262 incl_3d_adjust_channel_bit_mask(&channels[CHANNEL_SCAN_INDEX_X], 263 st->incl[CHANNEL_SCAN_INDEX_X].size); 264 265 ret = sensor_hub_input_get_attribute_info(hsdev, 266 HID_INPUT_REPORT, 267 usage_id, 268 HID_USAGE_SENSOR_ORIENT_TILT_Y, 269 &st->incl[CHANNEL_SCAN_INDEX_Y]); 270 if (ret) 271 return ret; 272 incl_3d_adjust_channel_bit_mask(&channels[CHANNEL_SCAN_INDEX_Y], 273 st->incl[CHANNEL_SCAN_INDEX_Y].size); 274 275 ret = sensor_hub_input_get_attribute_info(hsdev, 276 HID_INPUT_REPORT, 277 usage_id, 278 HID_USAGE_SENSOR_ORIENT_TILT_Z, 279 &st->incl[CHANNEL_SCAN_INDEX_Z]); 280 if (ret) 281 return ret; 282 incl_3d_adjust_channel_bit_mask(&channels[CHANNEL_SCAN_INDEX_Z], 283 st->incl[CHANNEL_SCAN_INDEX_Z].size); 284 285 dev_dbg(&pdev->dev, "incl_3d %x:%x, %x:%x, %x:%x\n", 286 st->incl[0].index, 287 st->incl[0].report_id, 288 st->incl[1].index, st->incl[1].report_id, 289 st->incl[2].index, st->incl[2].report_id); 290 291 st->scale_precision = hid_sensor_format_scale( 292 HID_USAGE_SENSOR_INCLINOMETER_3D, 293 &st->incl[CHANNEL_SCAN_INDEX_X], 294 &st->scale_pre_decml, &st->scale_post_decml); 295 296 return ret; 297} 298 299/* Function to initialize the processing for usage id */ 300static int hid_incl_3d_probe(struct platform_device *pdev) 301{ 302 int ret; 303 static char *name = "incli_3d"; 304 struct iio_dev *indio_dev; 305 struct incl_3d_state *incl_state; 306 struct hid_sensor_hub_device *hsdev = pdev->dev.platform_data; 307 308 indio_dev = devm_iio_device_alloc(&pdev->dev, 309 sizeof(struct incl_3d_state)); 310 if (indio_dev == NULL) 311 return -ENOMEM; 312 313 platform_set_drvdata(pdev, indio_dev); 314 315 incl_state = iio_priv(indio_dev); 316 incl_state->common_attributes.hsdev = hsdev; 317 incl_state->common_attributes.pdev = pdev; 318 319 ret = hid_sensor_parse_common_attributes(hsdev, 320 HID_USAGE_SENSOR_INCLINOMETER_3D, 321 &incl_state->common_attributes, 322 incl_3d_sensitivity_addresses, 323 ARRAY_SIZE(incl_3d_sensitivity_addresses)); 324 if (ret) { 325 dev_err(&pdev->dev, "failed to setup common attributes\n"); 326 return ret; 327 } 328 329 indio_dev->channels = devm_kmemdup(&pdev->dev, incl_3d_channels, 330 sizeof(incl_3d_channels), GFP_KERNEL); 331 if (!indio_dev->channels) { 332 dev_err(&pdev->dev, "failed to duplicate channels\n"); 333 return -ENOMEM; 334 } 335 336 ret = incl_3d_parse_report(pdev, hsdev, 337 (struct iio_chan_spec *)indio_dev->channels, 338 HID_USAGE_SENSOR_INCLINOMETER_3D, 339 incl_state); 340 if (ret) { 341 dev_err(&pdev->dev, "failed to setup attributes\n"); 342 return ret; 343 } 344 345 indio_dev->num_channels = ARRAY_SIZE(incl_3d_channels); 346 indio_dev->info = &incl_3d_info; 347 indio_dev->name = name; 348 indio_dev->modes = INDIO_DIRECT_MODE; 349 350 atomic_set(&incl_state->common_attributes.data_ready, 0); 351 352 ret = hid_sensor_setup_trigger(indio_dev, name, 353 &incl_state->common_attributes); 354 if (ret) { 355 dev_err(&pdev->dev, "trigger setup failed\n"); 356 return ret; 357 } 358 359 ret = iio_device_register(indio_dev); 360 if (ret) { 361 dev_err(&pdev->dev, "device register failed\n"); 362 goto error_remove_trigger; 363 } 364 365 incl_state->callbacks.send_event = incl_3d_proc_event; 366 incl_state->callbacks.capture_sample = incl_3d_capture_sample; 367 incl_state->callbacks.pdev = pdev; 368 ret = sensor_hub_register_callback(hsdev, 369 HID_USAGE_SENSOR_INCLINOMETER_3D, 370 &incl_state->callbacks); 371 if (ret) { 372 dev_err(&pdev->dev, "callback reg failed\n"); 373 goto error_iio_unreg; 374 } 375 376 return 0; 377 378error_iio_unreg: 379 iio_device_unregister(indio_dev); 380error_remove_trigger: 381 hid_sensor_remove_trigger(indio_dev, &incl_state->common_attributes); 382 return ret; 383} 384 385/* Function to deinitialize the processing for usage id */ 386static int hid_incl_3d_remove(struct platform_device *pdev) 387{ 388 struct hid_sensor_hub_device *hsdev = pdev->dev.platform_data; 389 struct iio_dev *indio_dev = platform_get_drvdata(pdev); 390 struct incl_3d_state *incl_state = iio_priv(indio_dev); 391 392 sensor_hub_remove_callback(hsdev, HID_USAGE_SENSOR_INCLINOMETER_3D); 393 iio_device_unregister(indio_dev); 394 hid_sensor_remove_trigger(indio_dev, &incl_state->common_attributes); 395 396 return 0; 397} 398 399static const struct platform_device_id hid_incl_3d_ids[] = { 400 { 401 /* Format: HID-SENSOR-usage_id_in_hex_lowercase */ 402 .name = "HID-SENSOR-200086", 403 }, 404 { /* sentinel */ } 405}; 406MODULE_DEVICE_TABLE(platform, hid_incl_3d_ids); 407 408static struct platform_driver hid_incl_3d_platform_driver = { 409 .id_table = hid_incl_3d_ids, 410 .driver = { 411 .name = KBUILD_MODNAME, 412 .pm = &hid_sensor_pm_ops, 413 }, 414 .probe = hid_incl_3d_probe, 415 .remove = hid_incl_3d_remove, 416}; 417module_platform_driver(hid_incl_3d_platform_driver); 418 419MODULE_DESCRIPTION("HID Sensor Inclinometer 3D"); 420MODULE_AUTHOR("Srinivas Pandruvada <srinivas.pandruvada@linux.intel.com>"); 421MODULE_LICENSE("GPL"); 422MODULE_IMPORT_NS(IIO_HID);