brintos

brintos / linux-shallow public Read only

0
0
Text · 16.0 KiB · 5c795a4 Raw
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