585 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2/*3 * HID Sensors Driver4 * Copyright (c) 2012, Intel Corporation.5 */6#include <linux/device.h>7#include <linux/platform_device.h>8#include <linux/module.h>9#include <linux/mod_devicetable.h>10#include <linux/hid-sensor-hub.h>11#include <linux/iio/iio.h>12#include <linux/iio/buffer.h>13#include "../common/hid-sensors/hid-sensor-trigger.h"14 15enum magn_3d_channel {16 CHANNEL_SCAN_INDEX_X,17 CHANNEL_SCAN_INDEX_Y,18 CHANNEL_SCAN_INDEX_Z,19 CHANNEL_SCAN_INDEX_NORTH_MAGN_TILT_COMP,20 CHANNEL_SCAN_INDEX_NORTH_TRUE_TILT_COMP,21 CHANNEL_SCAN_INDEX_NORTH_MAGN,22 CHANNEL_SCAN_INDEX_NORTH_TRUE,23 CHANNEL_SCAN_INDEX_TIMESTAMP,24 MAGN_3D_CHANNEL_MAX,25};26 27struct common_attributes {28 int scale_pre_decml;29 int scale_post_decml;30 int scale_precision;31 int value_offset;32};33 34struct magn_3d_state {35 struct hid_sensor_hub_callbacks callbacks;36 struct hid_sensor_common magn_flux_attributes;37 struct hid_sensor_common rot_attributes;38 struct hid_sensor_hub_attribute_info magn[MAGN_3D_CHANNEL_MAX];39 40 /* dynamically sized array to hold sensor values */41 u32 *iio_vals;42 /* array of pointers to sensor value */43 u32 *magn_val_addr[MAGN_3D_CHANNEL_MAX];44 45 struct common_attributes magn_flux_attr;46 struct common_attributes rot_attr;47 s64 timestamp;48};49 50static const u32 magn_3d_addresses[MAGN_3D_CHANNEL_MAX] = {51 HID_USAGE_SENSOR_ORIENT_MAGN_FLUX_X_AXIS,52 HID_USAGE_SENSOR_ORIENT_MAGN_FLUX_Y_AXIS,53 HID_USAGE_SENSOR_ORIENT_MAGN_FLUX_Z_AXIS,54 HID_USAGE_SENSOR_ORIENT_COMP_MAGN_NORTH,55 HID_USAGE_SENSOR_ORIENT_COMP_TRUE_NORTH,56 HID_USAGE_SENSOR_ORIENT_MAGN_NORTH,57 HID_USAGE_SENSOR_ORIENT_TRUE_NORTH,58 HID_USAGE_SENSOR_TIME_TIMESTAMP,59};60 61static const u32 magn_3d_sensitivity_addresses[] = {62 HID_USAGE_SENSOR_DATA_ORIENTATION,63 HID_USAGE_SENSOR_ORIENT_MAGN_FLUX,64};65 66/* Channel definitions */67static const struct iio_chan_spec magn_3d_channels[] = {68 {69 .type = IIO_MAGN,70 .modified = 1,71 .channel2 = IIO_MOD_X,72 .info_mask_separate = BIT(IIO_CHAN_INFO_RAW),73 .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_OFFSET) |74 BIT(IIO_CHAN_INFO_SCALE) |75 BIT(IIO_CHAN_INFO_SAMP_FREQ) |76 BIT(IIO_CHAN_INFO_HYSTERESIS),77 }, {78 .type = IIO_MAGN,79 .modified = 1,80 .channel2 = IIO_MOD_Y,81 .info_mask_separate = BIT(IIO_CHAN_INFO_RAW),82 .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_OFFSET) |83 BIT(IIO_CHAN_INFO_SCALE) |84 BIT(IIO_CHAN_INFO_SAMP_FREQ) |85 BIT(IIO_CHAN_INFO_HYSTERESIS),86 }, {87 .type = IIO_MAGN,88 .modified = 1,89 .channel2 = IIO_MOD_Z,90 .info_mask_separate = BIT(IIO_CHAN_INFO_RAW),91 .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_OFFSET) |92 BIT(IIO_CHAN_INFO_SCALE) |93 BIT(IIO_CHAN_INFO_SAMP_FREQ) |94 BIT(IIO_CHAN_INFO_HYSTERESIS),95 }, {96 .type = IIO_ROT,97 .modified = 1,98 .channel2 = IIO_MOD_NORTH_MAGN_TILT_COMP,99 .info_mask_separate = BIT(IIO_CHAN_INFO_RAW),100 .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_OFFSET) |101 BIT(IIO_CHAN_INFO_SCALE) |102 BIT(IIO_CHAN_INFO_SAMP_FREQ) |103 BIT(IIO_CHAN_INFO_HYSTERESIS),104 }, {105 .type = IIO_ROT,106 .modified = 1,107 .channel2 = IIO_MOD_NORTH_TRUE_TILT_COMP,108 .info_mask_separate = BIT(IIO_CHAN_INFO_RAW),109 .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_OFFSET) |110 BIT(IIO_CHAN_INFO_SCALE) |111 BIT(IIO_CHAN_INFO_SAMP_FREQ) |112 BIT(IIO_CHAN_INFO_HYSTERESIS),113 }, {114 .type = IIO_ROT,115 .modified = 1,116 .channel2 = IIO_MOD_NORTH_MAGN,117 .info_mask_separate = BIT(IIO_CHAN_INFO_RAW),118 .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_OFFSET) |119 BIT(IIO_CHAN_INFO_SCALE) |120 BIT(IIO_CHAN_INFO_SAMP_FREQ) |121 BIT(IIO_CHAN_INFO_HYSTERESIS),122 }, {123 .type = IIO_ROT,124 .modified = 1,125 .channel2 = IIO_MOD_NORTH_TRUE,126 .info_mask_separate = BIT(IIO_CHAN_INFO_RAW),127 .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_OFFSET) |128 BIT(IIO_CHAN_INFO_SCALE) |129 BIT(IIO_CHAN_INFO_SAMP_FREQ) |130 BIT(IIO_CHAN_INFO_HYSTERESIS),131 },132 IIO_CHAN_SOFT_TIMESTAMP(7)133};134 135/* Adjust channel real bits based on report descriptor */136static void magn_3d_adjust_channel_bit_mask(struct iio_chan_spec *channels,137 int channel, int size)138{139 channels[channel].scan_type.sign = 's';140 /* Real storage bits will change based on the report desc. */141 channels[channel].scan_type.realbits = size * 8;142 /* Maximum size of a sample to capture is u32 */143 channels[channel].scan_type.storagebits = sizeof(u32) * 8;144}145 146/* Channel read_raw handler */147static int magn_3d_read_raw(struct iio_dev *indio_dev,148 struct iio_chan_spec const *chan,149 int *val, int *val2,150 long mask)151{152 struct magn_3d_state *magn_state = iio_priv(indio_dev);153 int report_id = -1;154 u32 address;155 int ret_type;156 s32 min;157 158 *val = 0;159 *val2 = 0;160 switch (mask) {161 case IIO_CHAN_INFO_RAW:162 hid_sensor_power_state(&magn_state->magn_flux_attributes, true);163 report_id = magn_state->magn[chan->address].report_id;164 min = magn_state->magn[chan->address].logical_minimum;165 address = magn_3d_addresses[chan->address];166 if (report_id >= 0)167 *val = sensor_hub_input_attr_get_raw_value(168 magn_state->magn_flux_attributes.hsdev,169 HID_USAGE_SENSOR_COMPASS_3D, address,170 report_id,171 SENSOR_HUB_SYNC,172 min < 0);173 else {174 *val = 0;175 hid_sensor_power_state(176 &magn_state->magn_flux_attributes,177 false);178 return -EINVAL;179 }180 hid_sensor_power_state(&magn_state->magn_flux_attributes,181 false);182 ret_type = IIO_VAL_INT;183 break;184 case IIO_CHAN_INFO_SCALE:185 switch (chan->type) {186 case IIO_MAGN:187 *val = magn_state->magn_flux_attr.scale_pre_decml;188 *val2 = magn_state->magn_flux_attr.scale_post_decml;189 ret_type = magn_state->magn_flux_attr.scale_precision;190 break;191 case IIO_ROT:192 *val = magn_state->rot_attr.scale_pre_decml;193 *val2 = magn_state->rot_attr.scale_post_decml;194 ret_type = magn_state->rot_attr.scale_precision;195 break;196 default:197 ret_type = -EINVAL;198 }199 break;200 case IIO_CHAN_INFO_OFFSET:201 switch (chan->type) {202 case IIO_MAGN:203 *val = magn_state->magn_flux_attr.value_offset;204 ret_type = IIO_VAL_INT;205 break;206 case IIO_ROT:207 *val = magn_state->rot_attr.value_offset;208 ret_type = IIO_VAL_INT;209 break;210 default:211 ret_type = -EINVAL;212 }213 break;214 case IIO_CHAN_INFO_SAMP_FREQ:215 ret_type = hid_sensor_read_samp_freq_value(216 &magn_state->magn_flux_attributes, val, val2);217 break;218 case IIO_CHAN_INFO_HYSTERESIS:219 switch (chan->type) {220 case IIO_MAGN:221 ret_type = hid_sensor_read_raw_hyst_value(222 &magn_state->magn_flux_attributes, val, val2);223 break;224 case IIO_ROT:225 ret_type = hid_sensor_read_raw_hyst_value(226 &magn_state->rot_attributes, val, val2);227 break;228 default:229 ret_type = -EINVAL;230 }231 break;232 default:233 ret_type = -EINVAL;234 break;235 }236 237 return ret_type;238}239 240/* Channel write_raw handler */241static int magn_3d_write_raw(struct iio_dev *indio_dev,242 struct iio_chan_spec const *chan,243 int val,244 int val2,245 long mask)246{247 struct magn_3d_state *magn_state = iio_priv(indio_dev);248 int ret = 0;249 250 switch (mask) {251 case IIO_CHAN_INFO_SAMP_FREQ:252 ret = hid_sensor_write_samp_freq_value(253 &magn_state->magn_flux_attributes, val, val2);254 break;255 case IIO_CHAN_INFO_HYSTERESIS:256 switch (chan->type) {257 case IIO_MAGN:258 ret = hid_sensor_write_raw_hyst_value(259 &magn_state->magn_flux_attributes, val, val2);260 break;261 case IIO_ROT:262 ret = hid_sensor_write_raw_hyst_value(263 &magn_state->rot_attributes, val, val2);264 break;265 default:266 ret = -EINVAL;267 }268 break;269 default:270 ret = -EINVAL;271 }272 273 return ret;274}275 276static const struct iio_info magn_3d_info = {277 .read_raw = &magn_3d_read_raw,278 .write_raw = &magn_3d_write_raw,279};280 281/* Callback handler to send event after all samples are received and captured */282static int magn_3d_proc_event(struct hid_sensor_hub_device *hsdev,283 unsigned usage_id,284 void *priv)285{286 struct iio_dev *indio_dev = platform_get_drvdata(priv);287 struct magn_3d_state *magn_state = iio_priv(indio_dev);288 289 dev_dbg(&indio_dev->dev, "magn_3d_proc_event\n");290 if (atomic_read(&magn_state->magn_flux_attributes.data_ready)) {291 if (!magn_state->timestamp)292 magn_state->timestamp = iio_get_time_ns(indio_dev);293 294 iio_push_to_buffers_with_timestamp(indio_dev,295 magn_state->iio_vals,296 magn_state->timestamp);297 magn_state->timestamp = 0;298 }299 300 return 0;301}302 303/* Capture samples in local storage */304static int magn_3d_capture_sample(struct hid_sensor_hub_device *hsdev,305 unsigned usage_id,306 size_t raw_len, char *raw_data,307 void *priv)308{309 struct iio_dev *indio_dev = platform_get_drvdata(priv);310 struct magn_3d_state *magn_state = iio_priv(indio_dev);311 int offset;312 int ret = 0;313 u32 *iio_val = NULL;314 315 switch (usage_id) {316 case HID_USAGE_SENSOR_ORIENT_MAGN_FLUX_X_AXIS:317 case HID_USAGE_SENSOR_ORIENT_MAGN_FLUX_Y_AXIS:318 case HID_USAGE_SENSOR_ORIENT_MAGN_FLUX_Z_AXIS:319 offset = (usage_id - HID_USAGE_SENSOR_ORIENT_MAGN_FLUX_X_AXIS)320 + CHANNEL_SCAN_INDEX_X;321 break;322 case HID_USAGE_SENSOR_ORIENT_COMP_MAGN_NORTH:323 case HID_USAGE_SENSOR_ORIENT_COMP_TRUE_NORTH:324 case HID_USAGE_SENSOR_ORIENT_MAGN_NORTH:325 case HID_USAGE_SENSOR_ORIENT_TRUE_NORTH:326 offset = (usage_id - HID_USAGE_SENSOR_ORIENT_COMP_MAGN_NORTH)327 + CHANNEL_SCAN_INDEX_NORTH_MAGN_TILT_COMP;328 break;329 case HID_USAGE_SENSOR_TIME_TIMESTAMP:330 magn_state->timestamp =331 hid_sensor_convert_timestamp(&magn_state->magn_flux_attributes,332 *(s64 *)raw_data);333 return ret;334 default:335 return -EINVAL;336 }337 338 iio_val = magn_state->magn_val_addr[offset];339 340 if (iio_val != NULL)341 *iio_val = *((u32 *)raw_data);342 else343 ret = -EINVAL;344 345 return ret;346}347 348/* Parse report which is specific to an usage id*/349static int magn_3d_parse_report(struct platform_device *pdev,350 struct hid_sensor_hub_device *hsdev,351 struct iio_chan_spec **channels,352 int *chan_count,353 unsigned usage_id,354 struct magn_3d_state *st)355{356 int i;357 int attr_count = 0;358 struct iio_chan_spec *_channels;359 360 /* Scan for each usage attribute supported */361 for (i = 0; i < MAGN_3D_CHANNEL_MAX; i++) {362 int status;363 u32 address = magn_3d_addresses[i];364 365 /* Check if usage attribute exists in the sensor hub device */366 status = sensor_hub_input_get_attribute_info(hsdev,367 HID_INPUT_REPORT,368 usage_id,369 address,370 &(st->magn[i]));371 if (!status)372 attr_count++;373 }374 375 if (attr_count <= 0) {376 dev_err(&pdev->dev,377 "failed to find any supported usage attributes in report\n");378 return -EINVAL;379 }380 381 dev_dbg(&pdev->dev, "magn_3d Found %d usage attributes\n",382 attr_count);383 dev_dbg(&pdev->dev, "magn_3d X: %x:%x Y: %x:%x Z: %x:%x\n",384 st->magn[0].index,385 st->magn[0].report_id,386 st->magn[1].index, st->magn[1].report_id,387 st->magn[2].index, st->magn[2].report_id);388 389 /* Setup IIO channel array */390 _channels = devm_kcalloc(&pdev->dev, attr_count,391 sizeof(struct iio_chan_spec),392 GFP_KERNEL);393 if (!_channels) {394 dev_err(&pdev->dev,395 "failed to allocate space for iio channels\n");396 return -ENOMEM;397 }398 399 /* attr_count include timestamp channel, and the iio_vals should be aligned to 8byte */400 st->iio_vals = devm_kcalloc(&pdev->dev,401 ((attr_count + 1) % 2 + (attr_count + 1) / 2) * 2,402 sizeof(u32), GFP_KERNEL);403 if (!st->iio_vals) {404 dev_err(&pdev->dev,405 "failed to allocate space for iio values array\n");406 return -ENOMEM;407 }408 409 for (i = 0, *chan_count = 0;410 i < MAGN_3D_CHANNEL_MAX && *chan_count < attr_count;411 i++){412 if (st->magn[i].index >= 0) {413 /* Setup IIO channel struct */414 (_channels[*chan_count]) = magn_3d_channels[i];415 (_channels[*chan_count]).scan_index = *chan_count;416 (_channels[*chan_count]).address = i;417 418 if (i != CHANNEL_SCAN_INDEX_TIMESTAMP) {419 /* Set magn_val_addr to iio value address */420 st->magn_val_addr[i] = &st->iio_vals[*chan_count];421 magn_3d_adjust_channel_bit_mask(_channels,422 *chan_count,423 st->magn[i].size);424 }425 (*chan_count)++;426 }427 }428 429 if (*chan_count <= 0) {430 dev_err(&pdev->dev,431 "failed to find any magnetic channels setup\n");432 return -EINVAL;433 }434 435 *channels = _channels;436 437 dev_dbg(&pdev->dev, "magn_3d Setup %d IIO channels\n",438 *chan_count);439 440 st->magn_flux_attr.scale_precision = hid_sensor_format_scale(441 HID_USAGE_SENSOR_COMPASS_3D,442 &st->magn[CHANNEL_SCAN_INDEX_X],443 &st->magn_flux_attr.scale_pre_decml,444 &st->magn_flux_attr.scale_post_decml);445 st->rot_attr.scale_precision446 = hid_sensor_format_scale(447 HID_USAGE_SENSOR_ORIENT_COMP_MAGN_NORTH,448 &st->magn[CHANNEL_SCAN_INDEX_NORTH_MAGN_TILT_COMP],449 &st->rot_attr.scale_pre_decml,450 &st->rot_attr.scale_post_decml);451 452 if (st->rot_attributes.sensitivity.index < 0) {453 sensor_hub_input_get_attribute_info(hsdev,454 HID_FEATURE_REPORT, usage_id,455 HID_USAGE_SENSOR_DATA_MOD_CHANGE_SENSITIVITY_ABS |456 HID_USAGE_SENSOR_ORIENT_COMP_MAGN_NORTH,457 &st->rot_attributes.sensitivity);458 dev_dbg(&pdev->dev, "Sensitivity index:report %d:%d\n",459 st->rot_attributes.sensitivity.index,460 st->rot_attributes.sensitivity.report_id);461 }462 463 return 0;464}465 466/* Function to initialize the processing for usage id */467static int hid_magn_3d_probe(struct platform_device *pdev)468{469 int ret = 0;470 static char *name = "magn_3d";471 struct iio_dev *indio_dev;472 struct magn_3d_state *magn_state;473 struct hid_sensor_hub_device *hsdev = pdev->dev.platform_data;474 struct iio_chan_spec *channels;475 int chan_count = 0;476 477 indio_dev = devm_iio_device_alloc(&pdev->dev,478 sizeof(struct magn_3d_state));479 if (indio_dev == NULL)480 return -ENOMEM;481 482 platform_set_drvdata(pdev, indio_dev);483 484 magn_state = iio_priv(indio_dev);485 magn_state->magn_flux_attributes.hsdev = hsdev;486 magn_state->magn_flux_attributes.pdev = pdev;487 488 ret = hid_sensor_parse_common_attributes(hsdev,489 HID_USAGE_SENSOR_COMPASS_3D,490 &magn_state->magn_flux_attributes,491 magn_3d_sensitivity_addresses,492 ARRAY_SIZE(magn_3d_sensitivity_addresses));493 if (ret) {494 dev_err(&pdev->dev, "failed to setup common attributes\n");495 return ret;496 }497 magn_state->rot_attributes = magn_state->magn_flux_attributes;498 /* sensitivity of rot_attribute is not the same as magn_flux_attributes */499 magn_state->rot_attributes.sensitivity.index = -1;500 501 ret = magn_3d_parse_report(pdev, hsdev,502 &channels, &chan_count,503 HID_USAGE_SENSOR_COMPASS_3D, magn_state);504 if (ret) {505 dev_err(&pdev->dev, "failed to parse report\n");506 return ret;507 }508 509 indio_dev->channels = channels;510 indio_dev->num_channels = chan_count;511 indio_dev->info = &magn_3d_info;512 indio_dev->name = name;513 indio_dev->modes = INDIO_DIRECT_MODE;514 515 atomic_set(&magn_state->magn_flux_attributes.data_ready, 0);516 517 ret = hid_sensor_setup_trigger(indio_dev, name,518 &magn_state->magn_flux_attributes);519 if (ret < 0) {520 dev_err(&pdev->dev, "trigger setup failed\n");521 return ret;522 }523 524 ret = iio_device_register(indio_dev);525 if (ret) {526 dev_err(&pdev->dev, "device register failed\n");527 goto error_remove_trigger;528 }529 530 magn_state->callbacks.send_event = magn_3d_proc_event;531 magn_state->callbacks.capture_sample = magn_3d_capture_sample;532 magn_state->callbacks.pdev = pdev;533 ret = sensor_hub_register_callback(hsdev, HID_USAGE_SENSOR_COMPASS_3D,534 &magn_state->callbacks);535 if (ret < 0) {536 dev_err(&pdev->dev, "callback reg failed\n");537 goto error_iio_unreg;538 }539 540 return ret;541 542error_iio_unreg:543 iio_device_unregister(indio_dev);544error_remove_trigger:545 hid_sensor_remove_trigger(indio_dev, &magn_state->magn_flux_attributes);546 return ret;547}548 549/* Function to deinitialize the processing for usage id */550static void hid_magn_3d_remove(struct platform_device *pdev)551{552 struct hid_sensor_hub_device *hsdev = pdev->dev.platform_data;553 struct iio_dev *indio_dev = platform_get_drvdata(pdev);554 struct magn_3d_state *magn_state = iio_priv(indio_dev);555 556 sensor_hub_remove_callback(hsdev, HID_USAGE_SENSOR_COMPASS_3D);557 iio_device_unregister(indio_dev);558 hid_sensor_remove_trigger(indio_dev, &magn_state->magn_flux_attributes);559}560 561static const struct platform_device_id hid_magn_3d_ids[] = {562 {563 /* Format: HID-SENSOR-usage_id_in_hex_lowercase */564 .name = "HID-SENSOR-200083",565 },566 { /* sentinel */ }567};568MODULE_DEVICE_TABLE(platform, hid_magn_3d_ids);569 570static struct platform_driver hid_magn_3d_platform_driver = {571 .id_table = hid_magn_3d_ids,572 .driver = {573 .name = KBUILD_MODNAME,574 .pm = &hid_sensor_pm_ops,575 },576 .probe = hid_magn_3d_probe,577 .remove_new = hid_magn_3d_remove,578};579module_platform_driver(hid_magn_3d_platform_driver);580 581MODULE_DESCRIPTION("HID Sensor Magnetometer 3D");582MODULE_AUTHOR("Srinivas Pandruvada <srinivas.pandruvada@intel.com>");583MODULE_LICENSE("GPL");584MODULE_IMPORT_NS(IIO_HID);585