2019-05-30 02:57:49 +03:00
// SPDX-License-Identifier: GPL-2.0-only
2012-09-05 16:56:00 +04:00
/*
* HID Sensors Driver
* Copyright ( c ) 2012 , Intel Corporation .
*/
# include <linux/device.h>
# include <linux/platform_device.h>
# include <linux/module.h>
2021-06-08 23:55:10 +03:00
# include <linux/mod_devicetable.h>
2012-09-05 16:56:00 +04:00
# include <linux/slab.h>
# include <linux/hid-sensor-hub.h>
# include <linux/iio/iio.h>
# include <linux/iio/buffer.h>
# include "../common/hid-sensors/hid-sensor-trigger.h"
2016-12-16 00:48:21 +03:00
enum {
CHANNEL_SCAN_INDEX_INTENSITY = 0 ,
CHANNEL_SCAN_INDEX_ILLUM = 1 ,
CHANNEL_SCAN_INDEX_MAX
} ;
2012-09-05 16:56:00 +04:00
2021-01-05 12:35:12 +03:00
# define CHANNEL_SCAN_INDEX_TIMESTAMP CHANNEL_SCAN_INDEX_MAX
2012-09-05 16:56:00 +04:00
struct als_state {
struct hid_sensor_hub_callbacks callbacks ;
2012-12-15 16:45:00 +04:00
struct hid_sensor_common common_attributes ;
2012-09-05 16:56:00 +04:00
struct hid_sensor_hub_attribute_info als_illum ;
2021-01-05 12:35:12 +03:00
struct {
u32 illum [ CHANNEL_SCAN_INDEX_MAX ] ;
u64 timestamp __aligned ( 8 ) ;
} scan ;
2014-04-19 03:22:00 +04:00
int scale_pre_decml ;
int scale_post_decml ;
int scale_precision ;
int value_offset ;
2021-01-05 12:35:12 +03:00
s64 timestamp ;
2012-09-05 16:56:00 +04:00
} ;
2021-02-01 08:49:20 +03:00
static const u32 als_sensitivity_addresses [ ] = {
HID_USAGE_SENSOR_DATA_LIGHT ,
2021-02-01 08:49:21 +03:00
HID_USAGE_SENSOR_LIGHT_ILLUM ,
2021-02-01 08:49:20 +03:00
} ;
2012-09-05 16:56:00 +04:00
/* Channel definitions */
static const struct iio_chan_spec als_channels [ ] = {
{
. type = IIO_INTENSITY ,
. modified = 1 ,
. channel2 = IIO_MOD_LIGHT_BOTH ,
2014-04-19 03:22:00 +04:00
. info_mask_separate = BIT ( IIO_CHAN_INFO_RAW ) ,
2013-02-27 23:03:04 +04:00
. info_mask_shared_by_type = BIT ( IIO_CHAN_INFO_OFFSET ) |
BIT ( IIO_CHAN_INFO_SCALE ) |
BIT ( IIO_CHAN_INFO_SAMP_FREQ ) |
2021-02-07 10:00:47 +03:00
BIT ( IIO_CHAN_INFO_HYSTERESIS ) |
BIT ( IIO_CHAN_INFO_HYSTERESIS_RELATIVE ) ,
2016-12-16 00:48:21 +03:00
. scan_index = CHANNEL_SCAN_INDEX_INTENSITY ,
} ,
{
. type = IIO_LIGHT ,
. info_mask_separate = BIT ( IIO_CHAN_INFO_RAW ) ,
. info_mask_shared_by_type = BIT ( IIO_CHAN_INFO_OFFSET ) |
BIT ( IIO_CHAN_INFO_SCALE ) |
BIT ( IIO_CHAN_INFO_SAMP_FREQ ) |
2021-02-07 10:00:47 +03:00
BIT ( IIO_CHAN_INFO_HYSTERESIS ) |
BIT ( IIO_CHAN_INFO_HYSTERESIS_RELATIVE ) ,
2012-09-05 16:56:00 +04:00
. scan_index = CHANNEL_SCAN_INDEX_ILLUM ,
2021-01-05 12:35:12 +03:00
} ,
IIO_CHAN_SOFT_TIMESTAMP ( CHANNEL_SCAN_INDEX_TIMESTAMP )
2012-09-05 16:56:00 +04:00
} ;
/* Adjust channel real bits based on report descriptor */
static void als_adjust_channel_bit_mask ( struct iio_chan_spec * channels ,
int channel , int size )
{
channels [ channel ] . scan_type . sign = ' s ' ;
/* Real storage bits will change based on the report desc. */
channels [ channel ] . scan_type . realbits = size * 8 ;
/* Maximum size of a sample to capture is u32 */
channels [ channel ] . scan_type . storagebits = sizeof ( u32 ) * 8 ;
}
/* Channel read_raw handler */
static int als_read_raw ( struct iio_dev * indio_dev ,
struct iio_chan_spec const * chan ,
int * val , int * val2 ,
long mask )
{
struct als_state * als_state = iio_priv ( indio_dev ) ;
int report_id = - 1 ;
u32 address ;
int ret_type ;
2018-10-31 17:20:05 +03:00
s32 min ;
2012-09-05 16:56:00 +04:00
* val = 0 ;
* val2 = 0 ;
switch ( mask ) {
2018-03-06 15:02:57 +03:00
case IIO_CHAN_INFO_RAW :
2012-09-05 16:56:00 +04:00
switch ( chan - > scan_index ) {
2016-12-16 00:48:21 +03:00
case CHANNEL_SCAN_INDEX_INTENSITY :
2012-09-05 16:56:00 +04:00
case CHANNEL_SCAN_INDEX_ILLUM :
report_id = als_state - > als_illum . report_id ;
2018-10-31 17:20:05 +03:00
min = als_state - > als_illum . logical_minimum ;
address = HID_USAGE_SENSOR_LIGHT_ILLUM ;
2012-09-05 16:56:00 +04:00
break ;
default :
report_id = - 1 ;
break ;
}
2014-04-19 03:22:00 +04:00
if ( report_id > = 0 ) {
hid_sensor_power_state ( & als_state - > common_attributes ,
true ) ;
2012-09-05 16:56:00 +04:00
* val = sensor_hub_input_attr_get_raw_value (
2014-04-19 03:22:00 +04:00
als_state - > common_attributes . hsdev ,
HID_USAGE_SENSOR_ALS , address ,
2015-02-20 02:33:56 +03:00
report_id ,
2018-10-31 17:20:05 +03:00
SENSOR_HUB_SYNC ,
min < 0 ) ;
2014-04-19 03:22:00 +04:00
hid_sensor_power_state ( & als_state - > common_attributes ,
false ) ;
} else {
2012-09-05 16:56:00 +04:00
* val = 0 ;
return - EINVAL ;
}
ret_type = IIO_VAL_INT ;
break ;
case IIO_CHAN_INFO_SCALE :
2014-04-19 03:22:00 +04:00
* val = als_state - > scale_pre_decml ;
* val2 = als_state - > scale_post_decml ;
ret_type = als_state - > scale_precision ;
2012-09-05 16:56:00 +04:00
break ;
case IIO_CHAN_INFO_OFFSET :
2014-04-19 03:22:00 +04:00
* val = als_state - > value_offset ;
2012-09-05 16:56:00 +04:00
ret_type = IIO_VAL_INT ;
break ;
case IIO_CHAN_INFO_SAMP_FREQ :
2014-03-07 11:44:00 +04:00
ret_type = hid_sensor_read_samp_freq_value (
2012-09-05 16:56:00 +04:00
& als_state - > common_attributes , val , val2 ) ;
break ;
case IIO_CHAN_INFO_HYSTERESIS :
2014-03-07 11:44:00 +04:00
ret_type = hid_sensor_read_raw_hyst_value (
2012-09-05 16:56:00 +04:00
& als_state - > common_attributes , val , val2 ) ;
break ;
2021-02-07 10:00:47 +03:00
case IIO_CHAN_INFO_HYSTERESIS_RELATIVE :
ret_type = hid_sensor_read_raw_hyst_rel_value (
& als_state - > common_attributes , val , val2 ) ;
break ;
2012-09-05 16:56:00 +04:00
default :
ret_type = - EINVAL ;
break ;
}
return ret_type ;
}
/* Channel write_raw handler */
static int als_write_raw ( struct iio_dev * indio_dev ,
struct iio_chan_spec const * chan ,
int val ,
int val2 ,
long mask )
{
struct als_state * als_state = iio_priv ( indio_dev ) ;
int ret = 0 ;
switch ( mask ) {
case IIO_CHAN_INFO_SAMP_FREQ :
ret = hid_sensor_write_samp_freq_value (
& als_state - > common_attributes , val , val2 ) ;
break ;
case IIO_CHAN_INFO_HYSTERESIS :
ret = hid_sensor_write_raw_hyst_value (
& als_state - > common_attributes , val , val2 ) ;
break ;
2021-02-07 10:00:47 +03:00
case IIO_CHAN_INFO_HYSTERESIS_RELATIVE :
ret = hid_sensor_write_raw_hyst_rel_value (
& als_state - > common_attributes , val , val2 ) ;
break ;
2012-09-05 16:56:00 +04:00
default :
ret = - EINVAL ;
}
return ret ;
}
static const struct iio_info als_info = {
. read_raw = & als_read_raw ,
. write_raw = & als_write_raw ,
} ;
/* Callback handler to send event after all samples are received and captured */
static int als_proc_event ( struct hid_sensor_hub_device * hsdev ,
unsigned usage_id ,
void * priv )
{
struct iio_dev * indio_dev = platform_get_drvdata ( priv ) ;
struct als_state * als_state = iio_priv ( indio_dev ) ;
2014-04-19 03:22:00 +04:00
dev_dbg ( & indio_dev - > dev , " als_proc_event \n " ) ;
2021-01-05 12:35:12 +03:00
if ( atomic_read ( & als_state - > common_attributes . data_ready ) ) {
if ( ! als_state - > timestamp )
als_state - > timestamp = iio_get_time_ns ( indio_dev ) ;
iio_push_to_buffers_with_timestamp ( indio_dev , & als_state - > scan ,
als_state - > timestamp ) ;
als_state - > timestamp = 0 ;
}
2012-09-05 16:56:00 +04:00
return 0 ;
}
/* Capture samples in local storage */
static int als_capture_sample ( struct hid_sensor_hub_device * hsdev ,
unsigned usage_id ,
size_t raw_len , char * raw_data ,
void * priv )
{
struct iio_dev * indio_dev = platform_get_drvdata ( priv ) ;
struct als_state * als_state = iio_priv ( indio_dev ) ;
int ret = - EINVAL ;
2016-12-16 00:48:21 +03:00
u32 sample_data = * ( u32 * ) raw_data ;
2012-09-05 16:56:00 +04:00
switch ( usage_id ) {
case HID_USAGE_SENSOR_LIGHT_ILLUM :
2021-01-05 12:35:12 +03:00
als_state - > scan . illum [ CHANNEL_SCAN_INDEX_INTENSITY ] = sample_data ;
als_state - > scan . illum [ CHANNEL_SCAN_INDEX_ILLUM ] = sample_data ;
2012-09-05 16:56:00 +04:00
ret = 0 ;
break ;
2021-01-05 12:35:12 +03:00
case HID_USAGE_SENSOR_TIME_TIMESTAMP :
als_state - > timestamp = hid_sensor_convert_timestamp ( & als_state - > common_attributes ,
* ( s64 * ) raw_data ) ;
break ;
2012-09-05 16:56:00 +04:00
default :
break ;
}
return ret ;
}
/* Parse report which is specific to an usage id*/
static int als_parse_report ( struct platform_device * pdev ,
struct hid_sensor_hub_device * hsdev ,
struct iio_chan_spec * channels ,
unsigned usage_id ,
struct als_state * st )
{
int ret ;
ret = sensor_hub_input_get_attribute_info ( hsdev , HID_INPUT_REPORT ,
usage_id ,
HID_USAGE_SENSOR_LIGHT_ILLUM ,
& st - > als_illum ) ;
if ( ret < 0 )
return ret ;
2016-12-16 00:48:21 +03:00
als_adjust_channel_bit_mask ( channels , CHANNEL_SCAN_INDEX_INTENSITY ,
st - > als_illum . size ) ;
2012-09-05 16:56:00 +04:00
als_adjust_channel_bit_mask ( channels , CHANNEL_SCAN_INDEX_ILLUM ,
st - > als_illum . size ) ;
dev_dbg ( & pdev - > dev , " als %x:%x \n " , st - > als_illum . index ,
st - > als_illum . report_id ) ;
2014-04-19 03:22:00 +04:00
st - > scale_precision = hid_sensor_format_scale (
HID_USAGE_SENSOR_ALS ,
& st - > als_illum ,
& st - > scale_pre_decml , & st - > scale_post_decml ) ;
2012-09-05 16:56:00 +04:00
return ret ;
}
/* Function to initialize the processing for usage id */
2012-12-22 01:21:43 +04:00
static int hid_als_probe ( struct platform_device * pdev )
2012-09-05 16:56:00 +04:00
{
int ret = 0 ;
static const char * name = " als " ;
struct iio_dev * indio_dev ;
struct als_state * als_state ;
struct hid_sensor_hub_device * hsdev = pdev - > dev . platform_data ;
2013-07-30 12:44:00 +04:00
indio_dev = devm_iio_device_alloc ( & pdev - > dev , sizeof ( struct als_state ) ) ;
if ( ! indio_dev )
return - ENOMEM ;
2012-09-05 16:56:00 +04:00
platform_set_drvdata ( pdev , indio_dev ) ;
als_state = iio_priv ( indio_dev ) ;
als_state - > common_attributes . hsdev = hsdev ;
als_state - > common_attributes . pdev = pdev ;
ret = hid_sensor_parse_common_attributes ( hsdev , HID_USAGE_SENSOR_ALS ,
2021-02-01 08:49:20 +03:00
& als_state - > common_attributes ,
als_sensitivity_addresses ,
ARRAY_SIZE ( als_sensitivity_addresses ) ) ;
2012-09-05 16:56:00 +04:00
if ( ret ) {
dev_err ( & pdev - > dev , " failed to setup common attributes \n " ) ;
2013-07-30 12:44:00 +04:00
return ret ;
2012-09-05 16:56:00 +04:00
}
2021-06-30 15:30:29 +03:00
indio_dev - > channels = devm_kmemdup ( & pdev - > dev , als_channels ,
sizeof ( als_channels ) , GFP_KERNEL ) ;
2015-05-13 21:06:11 +03:00
if ( ! indio_dev - > channels ) {
2012-09-05 16:56:00 +04:00
dev_err ( & pdev - > dev , " failed to duplicate channels \n " ) ;
2013-07-30 12:44:00 +04:00
return - ENOMEM ;
2012-09-05 16:56:00 +04:00
}
2015-05-13 21:06:11 +03:00
ret = als_parse_report ( pdev , hsdev ,
( struct iio_chan_spec * ) indio_dev - > channels ,
HID_USAGE_SENSOR_ALS , als_state ) ;
2012-09-05 16:56:00 +04:00
if ( ret ) {
dev_err ( & pdev - > dev , " failed to setup attributes \n " ) ;
2021-06-30 15:30:29 +03:00
return ret ;
2012-09-05 16:56:00 +04:00
}
indio_dev - > num_channels =
ARRAY_SIZE ( als_channels ) ;
indio_dev - > info = & als_info ;
indio_dev - > name = name ;
indio_dev - > modes = INDIO_DIRECT_MODE ;
2014-04-19 03:22:00 +04:00
atomic_set ( & als_state - > common_attributes . data_ready , 0 ) ;
2020-04-24 07:34:18 +03:00
2012-09-05 16:56:00 +04:00
ret = hid_sensor_setup_trigger ( indio_dev , name ,
& als_state - > common_attributes ) ;
if ( ret < 0 ) {
dev_err ( & pdev - > dev , " trigger setup failed \n " ) ;
2021-06-30 15:30:29 +03:00
return ret ;
2012-09-05 16:56:00 +04:00
}
ret = iio_device_register ( indio_dev ) ;
if ( ret ) {
dev_err ( & pdev - > dev , " device register failed \n " ) ;
goto error_remove_trigger ;
}
als_state - > callbacks . send_event = als_proc_event ;
als_state - > callbacks . capture_sample = als_capture_sample ;
als_state - > callbacks . pdev = pdev ;
ret = sensor_hub_register_callback ( hsdev , HID_USAGE_SENSOR_ALS ,
& als_state - > callbacks ) ;
if ( ret < 0 ) {
dev_err ( & pdev - > dev , " callback reg failed \n " ) ;
goto error_iio_unreg ;
}
return ret ;
error_iio_unreg :
iio_device_unregister ( indio_dev ) ;
error_remove_trigger :
2020-04-24 07:34:18 +03:00
hid_sensor_remove_trigger ( indio_dev , & als_state - > common_attributes ) ;
2012-09-05 16:56:00 +04:00
return ret ;
}
/* Function to deinitialize the processing for usage id */
2012-12-22 01:21:43 +04:00
static int hid_als_remove ( struct platform_device * pdev )
2012-09-05 16:56:00 +04:00
{
struct hid_sensor_hub_device * hsdev = pdev - > dev . platform_data ;
struct iio_dev * indio_dev = platform_get_drvdata ( pdev ) ;
2013-10-31 02:48:00 +04:00
struct als_state * als_state = iio_priv ( indio_dev ) ;
2012-09-05 16:56:00 +04:00
sensor_hub_remove_callback ( hsdev , HID_USAGE_SENSOR_ALS ) ;
iio_device_unregister ( indio_dev ) ;
2020-04-24 07:34:18 +03:00
hid_sensor_remove_trigger ( indio_dev , & als_state - > common_attributes ) ;
2012-09-05 16:56:00 +04:00
return 0 ;
}
2015-05-01 18:53:33 +03:00
static const struct platform_device_id hid_als_ids [ ] = {
2013-07-10 12:31:00 +04:00
{
/* Format: HID-SENSOR-usage_id_in_hex_lowercase */
. name = " HID-SENSOR-200041 " ,
} ,
{ /* sentinel */ }
} ;
MODULE_DEVICE_TABLE ( platform , hid_als_ids ) ;
2012-09-05 16:56:00 +04:00
static struct platform_driver hid_als_platform_driver = {
2013-07-10 12:31:00 +04:00
. id_table = hid_als_ids ,
2012-09-05 16:56:00 +04:00
. driver = {
2013-07-10 12:31:00 +04:00
. name = KBUILD_MODNAME ,
2015-01-07 21:55:56 +03:00
. pm = & hid_sensor_pm_ops ,
2012-09-05 16:56:00 +04:00
} ,
. probe = hid_als_probe ,
. remove = hid_als_remove ,
} ;
module_platform_driver ( hid_als_platform_driver ) ;
MODULE_DESCRIPTION ( " HID Sensor ALS " ) ;
MODULE_AUTHOR ( " Srinivas Pandruvada <srinivas.pandruvada@intel.com> " ) ;
MODULE_LICENSE ( " GPL " ) ;
2021-06-14 19:24:47 +03:00
MODULE_IMPORT_NS ( IIO_HID ) ;