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 17 enum 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 26 struct 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 aligned_s64 timestamp; 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 41 static 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 47 static const u32 incl_3d_sensitivity_addresses[] = { 48 HID_USAGE_SENSOR_DATA_ORIENTATION, 49 HID_USAGE_SENSOR_ORIENT_TILT, 50 }; 51 52 /* Channel definitions */ 53 static 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 */ 89 static 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 */ 100 static 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 */ 160 static 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 185 static 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 */ 191 static 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 */ 214 static 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*/ 247 static 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 */ 300 static int hid_incl_3d_probe(struct platform_device *pdev) 301 { 302 struct hid_sensor_hub_device *hsdev = dev_get_platdata(&pdev->dev); 303 int ret; 304 static char *name = "incli_3d"; 305 struct iio_dev *indio_dev; 306 struct incl_3d_state *incl_state; 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 378 error_iio_unreg: 379 iio_device_unregister(indio_dev); 380 error_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 */ 386 static void hid_incl_3d_remove(struct platform_device *pdev) 387 { 388 struct hid_sensor_hub_device *hsdev = dev_get_platdata(&pdev->dev); 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 397 static const struct platform_device_id hid_incl_3d_ids[] = { 398 { 399 /* Format: HID-SENSOR-usage_id_in_hex_lowercase */ 400 .name = "HID-SENSOR-200086", 401 }, 402 { /* sentinel */ } 403 }; 404 MODULE_DEVICE_TABLE(platform, hid_incl_3d_ids); 405 406 static struct platform_driver hid_incl_3d_platform_driver = { 407 .id_table = hid_incl_3d_ids, 408 .driver = { 409 .name = KBUILD_MODNAME, 410 .pm = &hid_sensor_pm_ops, 411 }, 412 .probe = hid_incl_3d_probe, 413 .remove = hid_incl_3d_remove, 414 }; 415 module_platform_driver(hid_incl_3d_platform_driver); 416 417 MODULE_DESCRIPTION("HID Sensor Inclinometer 3D"); 418 MODULE_AUTHOR("Srinivas Pandruvada <srinivas.pandruvada@linux.intel.com>"); 419 MODULE_LICENSE("GPL"); 420 MODULE_IMPORT_NS(IIO_HID); 421