1*45fe6f7dSsrinivas pandruvada /* 2*45fe6f7dSsrinivas pandruvada * HID Sensors Driver 3*45fe6f7dSsrinivas pandruvada * Copyright (c) 2012, Intel Corporation. 4*45fe6f7dSsrinivas pandruvada * 5*45fe6f7dSsrinivas pandruvada * This program is free software; you can redistribute it and/or modify it 6*45fe6f7dSsrinivas pandruvada * under the terms and conditions of the GNU General Public License, 7*45fe6f7dSsrinivas pandruvada * version 2, as published by the Free Software Foundation. 8*45fe6f7dSsrinivas pandruvada * 9*45fe6f7dSsrinivas pandruvada * This program is distributed in the hope it will be useful, but WITHOUT 10*45fe6f7dSsrinivas pandruvada * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 11*45fe6f7dSsrinivas pandruvada * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for 12*45fe6f7dSsrinivas pandruvada * more details. 13*45fe6f7dSsrinivas pandruvada * 14*45fe6f7dSsrinivas pandruvada * You should have received a copy of the GNU General Public License along with 15*45fe6f7dSsrinivas pandruvada * this program; if not, write to the Free Software Foundation, Inc., 16*45fe6f7dSsrinivas pandruvada * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. 17*45fe6f7dSsrinivas pandruvada * 18*45fe6f7dSsrinivas pandruvada */ 19*45fe6f7dSsrinivas pandruvada #include <linux/device.h> 20*45fe6f7dSsrinivas pandruvada #include <linux/platform_device.h> 21*45fe6f7dSsrinivas pandruvada #include <linux/module.h> 22*45fe6f7dSsrinivas pandruvada #include <linux/interrupt.h> 23*45fe6f7dSsrinivas pandruvada #include <linux/irq.h> 24*45fe6f7dSsrinivas pandruvada #include <linux/slab.h> 25*45fe6f7dSsrinivas pandruvada #include <linux/hid-sensor-hub.h> 26*45fe6f7dSsrinivas pandruvada #include <linux/iio/iio.h> 27*45fe6f7dSsrinivas pandruvada #include <linux/iio/sysfs.h> 28*45fe6f7dSsrinivas pandruvada #include <linux/iio/buffer.h> 29*45fe6f7dSsrinivas pandruvada #include <linux/iio/trigger_consumer.h> 30*45fe6f7dSsrinivas pandruvada #include <linux/iio/triggered_buffer.h> 31*45fe6f7dSsrinivas pandruvada #include "../common/hid-sensors/hid-sensor-attributes.h" 32*45fe6f7dSsrinivas pandruvada #include "../common/hid-sensors/hid-sensor-trigger.h" 33*45fe6f7dSsrinivas pandruvada 34*45fe6f7dSsrinivas pandruvada /*Format: HID-SENSOR-usage_id_in_hex*/ 35*45fe6f7dSsrinivas pandruvada /*Usage ID from spec for Accelerometer-3D: 0x200073*/ 36*45fe6f7dSsrinivas pandruvada #define DRIVER_NAME "HID-SENSOR-200073" 37*45fe6f7dSsrinivas pandruvada 38*45fe6f7dSsrinivas pandruvada enum accel_3d_channel { 39*45fe6f7dSsrinivas pandruvada CHANNEL_SCAN_INDEX_X, 40*45fe6f7dSsrinivas pandruvada CHANNEL_SCAN_INDEX_Y, 41*45fe6f7dSsrinivas pandruvada CHANNEL_SCAN_INDEX_Z, 42*45fe6f7dSsrinivas pandruvada ACCEL_3D_CHANNEL_MAX, 43*45fe6f7dSsrinivas pandruvada }; 44*45fe6f7dSsrinivas pandruvada 45*45fe6f7dSsrinivas pandruvada struct accel_3d_state { 46*45fe6f7dSsrinivas pandruvada struct hid_sensor_hub_callbacks callbacks; 47*45fe6f7dSsrinivas pandruvada struct hid_sensor_iio_common common_attributes; 48*45fe6f7dSsrinivas pandruvada struct hid_sensor_hub_attribute_info accel[ACCEL_3D_CHANNEL_MAX]; 49*45fe6f7dSsrinivas pandruvada u32 accel_val[ACCEL_3D_CHANNEL_MAX]; 50*45fe6f7dSsrinivas pandruvada }; 51*45fe6f7dSsrinivas pandruvada 52*45fe6f7dSsrinivas pandruvada static const u32 accel_3d_addresses[ACCEL_3D_CHANNEL_MAX] = { 53*45fe6f7dSsrinivas pandruvada HID_USAGE_SENSOR_ACCEL_X_AXIS, 54*45fe6f7dSsrinivas pandruvada HID_USAGE_SENSOR_ACCEL_Y_AXIS, 55*45fe6f7dSsrinivas pandruvada HID_USAGE_SENSOR_ACCEL_Z_AXIS 56*45fe6f7dSsrinivas pandruvada }; 57*45fe6f7dSsrinivas pandruvada 58*45fe6f7dSsrinivas pandruvada /* Channel definitions */ 59*45fe6f7dSsrinivas pandruvada static const struct iio_chan_spec accel_3d_channels[] = { 60*45fe6f7dSsrinivas pandruvada { 61*45fe6f7dSsrinivas pandruvada .type = IIO_ACCEL, 62*45fe6f7dSsrinivas pandruvada .modified = 1, 63*45fe6f7dSsrinivas pandruvada .channel2 = IIO_MOD_X, 64*45fe6f7dSsrinivas pandruvada .info_mask = IIO_CHAN_INFO_OFFSET_SHARED_BIT | 65*45fe6f7dSsrinivas pandruvada IIO_CHAN_INFO_SCALE_SHARED_BIT | 66*45fe6f7dSsrinivas pandruvada IIO_CHAN_INFO_SAMP_FREQ_SHARED_BIT | 67*45fe6f7dSsrinivas pandruvada IIO_CHAN_INFO_HYSTERESIS_SHARED_BIT, 68*45fe6f7dSsrinivas pandruvada .scan_index = CHANNEL_SCAN_INDEX_X, 69*45fe6f7dSsrinivas pandruvada }, { 70*45fe6f7dSsrinivas pandruvada .type = IIO_ACCEL, 71*45fe6f7dSsrinivas pandruvada .modified = 1, 72*45fe6f7dSsrinivas pandruvada .channel2 = IIO_MOD_Y, 73*45fe6f7dSsrinivas pandruvada .info_mask = IIO_CHAN_INFO_OFFSET_SHARED_BIT | 74*45fe6f7dSsrinivas pandruvada IIO_CHAN_INFO_SCALE_SHARED_BIT | 75*45fe6f7dSsrinivas pandruvada IIO_CHAN_INFO_SAMP_FREQ_SHARED_BIT | 76*45fe6f7dSsrinivas pandruvada IIO_CHAN_INFO_HYSTERESIS_SHARED_BIT, 77*45fe6f7dSsrinivas pandruvada .scan_index = CHANNEL_SCAN_INDEX_Y, 78*45fe6f7dSsrinivas pandruvada }, { 79*45fe6f7dSsrinivas pandruvada .type = IIO_ACCEL, 80*45fe6f7dSsrinivas pandruvada .modified = 1, 81*45fe6f7dSsrinivas pandruvada .channel2 = IIO_MOD_Z, 82*45fe6f7dSsrinivas pandruvada .info_mask = IIO_CHAN_INFO_OFFSET_SHARED_BIT | 83*45fe6f7dSsrinivas pandruvada IIO_CHAN_INFO_SCALE_SHARED_BIT | 84*45fe6f7dSsrinivas pandruvada IIO_CHAN_INFO_SAMP_FREQ_SHARED_BIT | 85*45fe6f7dSsrinivas pandruvada IIO_CHAN_INFO_HYSTERESIS_SHARED_BIT, 86*45fe6f7dSsrinivas pandruvada .scan_index = CHANNEL_SCAN_INDEX_Z, 87*45fe6f7dSsrinivas pandruvada } 88*45fe6f7dSsrinivas pandruvada }; 89*45fe6f7dSsrinivas pandruvada 90*45fe6f7dSsrinivas pandruvada /* Adjust channel real bits based on report descriptor */ 91*45fe6f7dSsrinivas pandruvada static void accel_3d_adjust_channel_bit_mask(struct iio_chan_spec *channels, 92*45fe6f7dSsrinivas pandruvada int channel, int size) 93*45fe6f7dSsrinivas pandruvada { 94*45fe6f7dSsrinivas pandruvada channels[channel].scan_type.sign = 's'; 95*45fe6f7dSsrinivas pandruvada /* Real storage bits will change based on the report desc. */ 96*45fe6f7dSsrinivas pandruvada channels[channel].scan_type.realbits = size * 8; 97*45fe6f7dSsrinivas pandruvada /* Maximum size of a sample to capture is u32 */ 98*45fe6f7dSsrinivas pandruvada channels[channel].scan_type.storagebits = sizeof(u32) * 8; 99*45fe6f7dSsrinivas pandruvada } 100*45fe6f7dSsrinivas pandruvada 101*45fe6f7dSsrinivas pandruvada /* Channel read_raw handler */ 102*45fe6f7dSsrinivas pandruvada static int accel_3d_read_raw(struct iio_dev *indio_dev, 103*45fe6f7dSsrinivas pandruvada struct iio_chan_spec const *chan, 104*45fe6f7dSsrinivas pandruvada int *val, int *val2, 105*45fe6f7dSsrinivas pandruvada long mask) 106*45fe6f7dSsrinivas pandruvada { 107*45fe6f7dSsrinivas pandruvada struct accel_3d_state *accel_state = iio_priv(indio_dev); 108*45fe6f7dSsrinivas pandruvada int report_id = -1; 109*45fe6f7dSsrinivas pandruvada u32 address; 110*45fe6f7dSsrinivas pandruvada int ret; 111*45fe6f7dSsrinivas pandruvada int ret_type; 112*45fe6f7dSsrinivas pandruvada 113*45fe6f7dSsrinivas pandruvada *val = 0; 114*45fe6f7dSsrinivas pandruvada *val2 = 0; 115*45fe6f7dSsrinivas pandruvada switch (mask) { 116*45fe6f7dSsrinivas pandruvada case 0: 117*45fe6f7dSsrinivas pandruvada report_id = accel_state->accel[chan->scan_index].report_id; 118*45fe6f7dSsrinivas pandruvada address = accel_3d_addresses[chan->scan_index]; 119*45fe6f7dSsrinivas pandruvada if (report_id >= 0) 120*45fe6f7dSsrinivas pandruvada *val = sensor_hub_input_attr_get_raw_value( 121*45fe6f7dSsrinivas pandruvada accel_state->common_attributes.hsdev, 122*45fe6f7dSsrinivas pandruvada HID_USAGE_SENSOR_ACCEL_3D, address, 123*45fe6f7dSsrinivas pandruvada report_id); 124*45fe6f7dSsrinivas pandruvada else { 125*45fe6f7dSsrinivas pandruvada *val = 0; 126*45fe6f7dSsrinivas pandruvada return -EINVAL; 127*45fe6f7dSsrinivas pandruvada } 128*45fe6f7dSsrinivas pandruvada ret_type = IIO_VAL_INT; 129*45fe6f7dSsrinivas pandruvada break; 130*45fe6f7dSsrinivas pandruvada case IIO_CHAN_INFO_SCALE: 131*45fe6f7dSsrinivas pandruvada *val = accel_state->accel[CHANNEL_SCAN_INDEX_X].units; 132*45fe6f7dSsrinivas pandruvada ret_type = IIO_VAL_INT; 133*45fe6f7dSsrinivas pandruvada break; 134*45fe6f7dSsrinivas pandruvada case IIO_CHAN_INFO_OFFSET: 135*45fe6f7dSsrinivas pandruvada *val = hid_sensor_convert_exponent( 136*45fe6f7dSsrinivas pandruvada accel_state->accel[CHANNEL_SCAN_INDEX_X].unit_expo); 137*45fe6f7dSsrinivas pandruvada ret_type = IIO_VAL_INT; 138*45fe6f7dSsrinivas pandruvada break; 139*45fe6f7dSsrinivas pandruvada case IIO_CHAN_INFO_SAMP_FREQ: 140*45fe6f7dSsrinivas pandruvada ret = hid_sensor_read_samp_freq_value( 141*45fe6f7dSsrinivas pandruvada &accel_state->common_attributes, val, val2); 142*45fe6f7dSsrinivas pandruvada ret_type = IIO_VAL_INT_PLUS_MICRO; 143*45fe6f7dSsrinivas pandruvada break; 144*45fe6f7dSsrinivas pandruvada case IIO_CHAN_INFO_HYSTERESIS: 145*45fe6f7dSsrinivas pandruvada ret = hid_sensor_read_raw_hyst_value( 146*45fe6f7dSsrinivas pandruvada &accel_state->common_attributes, val, val2); 147*45fe6f7dSsrinivas pandruvada ret_type = IIO_VAL_INT_PLUS_MICRO; 148*45fe6f7dSsrinivas pandruvada break; 149*45fe6f7dSsrinivas pandruvada default: 150*45fe6f7dSsrinivas pandruvada ret_type = -EINVAL; 151*45fe6f7dSsrinivas pandruvada break; 152*45fe6f7dSsrinivas pandruvada } 153*45fe6f7dSsrinivas pandruvada 154*45fe6f7dSsrinivas pandruvada return ret_type; 155*45fe6f7dSsrinivas pandruvada } 156*45fe6f7dSsrinivas pandruvada 157*45fe6f7dSsrinivas pandruvada /* Channel write_raw handler */ 158*45fe6f7dSsrinivas pandruvada static int accel_3d_write_raw(struct iio_dev *indio_dev, 159*45fe6f7dSsrinivas pandruvada struct iio_chan_spec const *chan, 160*45fe6f7dSsrinivas pandruvada int val, 161*45fe6f7dSsrinivas pandruvada int val2, 162*45fe6f7dSsrinivas pandruvada long mask) 163*45fe6f7dSsrinivas pandruvada { 164*45fe6f7dSsrinivas pandruvada struct accel_3d_state *accel_state = iio_priv(indio_dev); 165*45fe6f7dSsrinivas pandruvada int ret = 0; 166*45fe6f7dSsrinivas pandruvada 167*45fe6f7dSsrinivas pandruvada switch (mask) { 168*45fe6f7dSsrinivas pandruvada case IIO_CHAN_INFO_SAMP_FREQ: 169*45fe6f7dSsrinivas pandruvada ret = hid_sensor_write_samp_freq_value( 170*45fe6f7dSsrinivas pandruvada &accel_state->common_attributes, val, val2); 171*45fe6f7dSsrinivas pandruvada break; 172*45fe6f7dSsrinivas pandruvada case IIO_CHAN_INFO_HYSTERESIS: 173*45fe6f7dSsrinivas pandruvada ret = hid_sensor_write_raw_hyst_value( 174*45fe6f7dSsrinivas pandruvada &accel_state->common_attributes, val, val2); 175*45fe6f7dSsrinivas pandruvada break; 176*45fe6f7dSsrinivas pandruvada default: 177*45fe6f7dSsrinivas pandruvada ret = -EINVAL; 178*45fe6f7dSsrinivas pandruvada } 179*45fe6f7dSsrinivas pandruvada 180*45fe6f7dSsrinivas pandruvada return ret; 181*45fe6f7dSsrinivas pandruvada } 182*45fe6f7dSsrinivas pandruvada 183*45fe6f7dSsrinivas pandruvada static int accel_3d_write_raw_get_fmt(struct iio_dev *indio_dev, 184*45fe6f7dSsrinivas pandruvada struct iio_chan_spec const *chan, 185*45fe6f7dSsrinivas pandruvada long mask) 186*45fe6f7dSsrinivas pandruvada { 187*45fe6f7dSsrinivas pandruvada return IIO_VAL_INT_PLUS_MICRO; 188*45fe6f7dSsrinivas pandruvada } 189*45fe6f7dSsrinivas pandruvada 190*45fe6f7dSsrinivas pandruvada static const struct iio_info accel_3d_info = { 191*45fe6f7dSsrinivas pandruvada .driver_module = THIS_MODULE, 192*45fe6f7dSsrinivas pandruvada .read_raw = &accel_3d_read_raw, 193*45fe6f7dSsrinivas pandruvada .write_raw = &accel_3d_write_raw, 194*45fe6f7dSsrinivas pandruvada .write_raw_get_fmt = &accel_3d_write_raw_get_fmt, 195*45fe6f7dSsrinivas pandruvada }; 196*45fe6f7dSsrinivas pandruvada 197*45fe6f7dSsrinivas pandruvada /* Function to push data to buffer */ 198*45fe6f7dSsrinivas pandruvada static void hid_sensor_push_data(struct iio_dev *indio_dev, u8 *data, int len) 199*45fe6f7dSsrinivas pandruvada { 200*45fe6f7dSsrinivas pandruvada struct iio_buffer *buffer = indio_dev->buffer; 201*45fe6f7dSsrinivas pandruvada s64 timestamp = iio_get_time_ns(); 202*45fe6f7dSsrinivas pandruvada int datum_sz; 203*45fe6f7dSsrinivas pandruvada 204*45fe6f7dSsrinivas pandruvada dev_dbg(&indio_dev->dev, "hid_sensor_push_data\n"); 205*45fe6f7dSsrinivas pandruvada if (!buffer) { 206*45fe6f7dSsrinivas pandruvada dev_err(&indio_dev->dev, "Buffer == NULL\n"); 207*45fe6f7dSsrinivas pandruvada return; 208*45fe6f7dSsrinivas pandruvada } 209*45fe6f7dSsrinivas pandruvada datum_sz = buffer->access->get_bytes_per_datum(buffer); 210*45fe6f7dSsrinivas pandruvada if (len > datum_sz) { 211*45fe6f7dSsrinivas pandruvada dev_err(&indio_dev->dev, "Datum size mismatch %d:%d\n", len, 212*45fe6f7dSsrinivas pandruvada datum_sz); 213*45fe6f7dSsrinivas pandruvada return; 214*45fe6f7dSsrinivas pandruvada } 215*45fe6f7dSsrinivas pandruvada buffer->access->store_to(buffer, (u8 *)data, timestamp); 216*45fe6f7dSsrinivas pandruvada } 217*45fe6f7dSsrinivas pandruvada 218*45fe6f7dSsrinivas pandruvada /* Callback handler to send event after all samples are received and captured */ 219*45fe6f7dSsrinivas pandruvada static int accel_3d_proc_event(struct hid_sensor_hub_device *hsdev, 220*45fe6f7dSsrinivas pandruvada unsigned usage_id, 221*45fe6f7dSsrinivas pandruvada void *priv) 222*45fe6f7dSsrinivas pandruvada { 223*45fe6f7dSsrinivas pandruvada struct iio_dev *indio_dev = platform_get_drvdata(priv); 224*45fe6f7dSsrinivas pandruvada struct accel_3d_state *accel_state = iio_priv(indio_dev); 225*45fe6f7dSsrinivas pandruvada 226*45fe6f7dSsrinivas pandruvada dev_dbg(&indio_dev->dev, "accel_3d_proc_event [%d]\n", 227*45fe6f7dSsrinivas pandruvada accel_state->common_attributes.data_ready); 228*45fe6f7dSsrinivas pandruvada if (accel_state->common_attributes.data_ready) 229*45fe6f7dSsrinivas pandruvada hid_sensor_push_data(indio_dev, 230*45fe6f7dSsrinivas pandruvada (u8 *)accel_state->accel_val, 231*45fe6f7dSsrinivas pandruvada sizeof(accel_state->accel_val)); 232*45fe6f7dSsrinivas pandruvada 233*45fe6f7dSsrinivas pandruvada return 0; 234*45fe6f7dSsrinivas pandruvada } 235*45fe6f7dSsrinivas pandruvada 236*45fe6f7dSsrinivas pandruvada /* Capture samples in local storage */ 237*45fe6f7dSsrinivas pandruvada static int accel_3d_capture_sample(struct hid_sensor_hub_device *hsdev, 238*45fe6f7dSsrinivas pandruvada unsigned usage_id, 239*45fe6f7dSsrinivas pandruvada size_t raw_len, char *raw_data, 240*45fe6f7dSsrinivas pandruvada void *priv) 241*45fe6f7dSsrinivas pandruvada { 242*45fe6f7dSsrinivas pandruvada struct iio_dev *indio_dev = platform_get_drvdata(priv); 243*45fe6f7dSsrinivas pandruvada struct accel_3d_state *accel_state = iio_priv(indio_dev); 244*45fe6f7dSsrinivas pandruvada int offset; 245*45fe6f7dSsrinivas pandruvada int ret = -EINVAL; 246*45fe6f7dSsrinivas pandruvada 247*45fe6f7dSsrinivas pandruvada switch (usage_id) { 248*45fe6f7dSsrinivas pandruvada case HID_USAGE_SENSOR_ACCEL_X_AXIS: 249*45fe6f7dSsrinivas pandruvada case HID_USAGE_SENSOR_ACCEL_Y_AXIS: 250*45fe6f7dSsrinivas pandruvada case HID_USAGE_SENSOR_ACCEL_Z_AXIS: 251*45fe6f7dSsrinivas pandruvada offset = usage_id - HID_USAGE_SENSOR_ACCEL_X_AXIS; 252*45fe6f7dSsrinivas pandruvada accel_state->accel_val[CHANNEL_SCAN_INDEX_X + offset] = 253*45fe6f7dSsrinivas pandruvada *(u32 *)raw_data; 254*45fe6f7dSsrinivas pandruvada ret = 0; 255*45fe6f7dSsrinivas pandruvada break; 256*45fe6f7dSsrinivas pandruvada default: 257*45fe6f7dSsrinivas pandruvada break; 258*45fe6f7dSsrinivas pandruvada } 259*45fe6f7dSsrinivas pandruvada 260*45fe6f7dSsrinivas pandruvada return ret; 261*45fe6f7dSsrinivas pandruvada } 262*45fe6f7dSsrinivas pandruvada 263*45fe6f7dSsrinivas pandruvada /* Parse report which is specific to an usage id*/ 264*45fe6f7dSsrinivas pandruvada static int accel_3d_parse_report(struct platform_device *pdev, 265*45fe6f7dSsrinivas pandruvada struct hid_sensor_hub_device *hsdev, 266*45fe6f7dSsrinivas pandruvada struct iio_chan_spec *channels, 267*45fe6f7dSsrinivas pandruvada unsigned usage_id, 268*45fe6f7dSsrinivas pandruvada struct accel_3d_state *st) 269*45fe6f7dSsrinivas pandruvada { 270*45fe6f7dSsrinivas pandruvada int ret; 271*45fe6f7dSsrinivas pandruvada int i; 272*45fe6f7dSsrinivas pandruvada 273*45fe6f7dSsrinivas pandruvada for (i = 0; i <= CHANNEL_SCAN_INDEX_Z; ++i) { 274*45fe6f7dSsrinivas pandruvada ret = sensor_hub_input_get_attribute_info(hsdev, 275*45fe6f7dSsrinivas pandruvada HID_INPUT_REPORT, 276*45fe6f7dSsrinivas pandruvada usage_id, 277*45fe6f7dSsrinivas pandruvada HID_USAGE_SENSOR_ACCEL_X_AXIS + i, 278*45fe6f7dSsrinivas pandruvada &st->accel[CHANNEL_SCAN_INDEX_X + i]); 279*45fe6f7dSsrinivas pandruvada if (ret < 0) 280*45fe6f7dSsrinivas pandruvada break; 281*45fe6f7dSsrinivas pandruvada accel_3d_adjust_channel_bit_mask(channels, 282*45fe6f7dSsrinivas pandruvada CHANNEL_SCAN_INDEX_X + i, 283*45fe6f7dSsrinivas pandruvada st->accel[CHANNEL_SCAN_INDEX_X + i].size); 284*45fe6f7dSsrinivas pandruvada } 285*45fe6f7dSsrinivas pandruvada dev_dbg(&pdev->dev, "accel_3d %x:%x, %x:%x, %x:%x\n", 286*45fe6f7dSsrinivas pandruvada st->accel[0].index, 287*45fe6f7dSsrinivas pandruvada st->accel[0].report_id, 288*45fe6f7dSsrinivas pandruvada st->accel[1].index, st->accel[1].report_id, 289*45fe6f7dSsrinivas pandruvada st->accel[2].index, st->accel[2].report_id); 290*45fe6f7dSsrinivas pandruvada 291*45fe6f7dSsrinivas pandruvada return ret; 292*45fe6f7dSsrinivas pandruvada } 293*45fe6f7dSsrinivas pandruvada 294*45fe6f7dSsrinivas pandruvada /* Function to initialize the processing for usage id */ 295*45fe6f7dSsrinivas pandruvada static int __devinit hid_accel_3d_probe(struct platform_device *pdev) 296*45fe6f7dSsrinivas pandruvada { 297*45fe6f7dSsrinivas pandruvada int ret = 0; 298*45fe6f7dSsrinivas pandruvada static const char *name = "accel_3d"; 299*45fe6f7dSsrinivas pandruvada struct iio_dev *indio_dev; 300*45fe6f7dSsrinivas pandruvada struct accel_3d_state *accel_state; 301*45fe6f7dSsrinivas pandruvada struct hid_sensor_hub_device *hsdev = pdev->dev.platform_data; 302*45fe6f7dSsrinivas pandruvada struct iio_chan_spec *channels; 303*45fe6f7dSsrinivas pandruvada 304*45fe6f7dSsrinivas pandruvada indio_dev = iio_device_alloc(sizeof(struct accel_3d_state)); 305*45fe6f7dSsrinivas pandruvada if (indio_dev == NULL) { 306*45fe6f7dSsrinivas pandruvada ret = -ENOMEM; 307*45fe6f7dSsrinivas pandruvada goto error_ret; 308*45fe6f7dSsrinivas pandruvada } 309*45fe6f7dSsrinivas pandruvada platform_set_drvdata(pdev, indio_dev); 310*45fe6f7dSsrinivas pandruvada 311*45fe6f7dSsrinivas pandruvada accel_state = iio_priv(indio_dev); 312*45fe6f7dSsrinivas pandruvada accel_state->common_attributes.hsdev = hsdev; 313*45fe6f7dSsrinivas pandruvada accel_state->common_attributes.pdev = pdev; 314*45fe6f7dSsrinivas pandruvada 315*45fe6f7dSsrinivas pandruvada ret = hid_sensor_parse_common_attributes(hsdev, 316*45fe6f7dSsrinivas pandruvada HID_USAGE_SENSOR_ACCEL_3D, 317*45fe6f7dSsrinivas pandruvada &accel_state->common_attributes); 318*45fe6f7dSsrinivas pandruvada if (ret) { 319*45fe6f7dSsrinivas pandruvada dev_err(&pdev->dev, "failed to setup common attributes\n"); 320*45fe6f7dSsrinivas pandruvada goto error_free_dev; 321*45fe6f7dSsrinivas pandruvada } 322*45fe6f7dSsrinivas pandruvada 323*45fe6f7dSsrinivas pandruvada channels = kmemdup(accel_3d_channels, 324*45fe6f7dSsrinivas pandruvada sizeof(accel_3d_channels), 325*45fe6f7dSsrinivas pandruvada GFP_KERNEL); 326*45fe6f7dSsrinivas pandruvada if (!channels) { 327*45fe6f7dSsrinivas pandruvada dev_err(&pdev->dev, "failed to duplicate channels\n"); 328*45fe6f7dSsrinivas pandruvada goto error_free_dev; 329*45fe6f7dSsrinivas pandruvada } 330*45fe6f7dSsrinivas pandruvada 331*45fe6f7dSsrinivas pandruvada ret = accel_3d_parse_report(pdev, hsdev, channels, 332*45fe6f7dSsrinivas pandruvada HID_USAGE_SENSOR_ACCEL_3D, accel_state); 333*45fe6f7dSsrinivas pandruvada if (ret) { 334*45fe6f7dSsrinivas pandruvada dev_err(&pdev->dev, "failed to setup attributes\n"); 335*45fe6f7dSsrinivas pandruvada goto error_free_dev_mem; 336*45fe6f7dSsrinivas pandruvada } 337*45fe6f7dSsrinivas pandruvada 338*45fe6f7dSsrinivas pandruvada indio_dev->channels = channels; 339*45fe6f7dSsrinivas pandruvada indio_dev->num_channels = ARRAY_SIZE(accel_3d_channels); 340*45fe6f7dSsrinivas pandruvada indio_dev->dev.parent = &pdev->dev; 341*45fe6f7dSsrinivas pandruvada indio_dev->info = &accel_3d_info; 342*45fe6f7dSsrinivas pandruvada indio_dev->name = name; 343*45fe6f7dSsrinivas pandruvada indio_dev->modes = INDIO_DIRECT_MODE; 344*45fe6f7dSsrinivas pandruvada 345*45fe6f7dSsrinivas pandruvada ret = iio_triggered_buffer_setup(indio_dev, &iio_pollfunc_store_time, 346*45fe6f7dSsrinivas pandruvada NULL, NULL); 347*45fe6f7dSsrinivas pandruvada if (ret) { 348*45fe6f7dSsrinivas pandruvada dev_err(&pdev->dev, "failed to initialize trigger buffer\n"); 349*45fe6f7dSsrinivas pandruvada goto error_free_dev_mem; 350*45fe6f7dSsrinivas pandruvada } 351*45fe6f7dSsrinivas pandruvada accel_state->common_attributes.data_ready = false; 352*45fe6f7dSsrinivas pandruvada ret = hid_sensor_setup_trigger(indio_dev, name, 353*45fe6f7dSsrinivas pandruvada &accel_state->common_attributes); 354*45fe6f7dSsrinivas pandruvada if (ret < 0) { 355*45fe6f7dSsrinivas pandruvada dev_err(&pdev->dev, "trigger setup failed\n"); 356*45fe6f7dSsrinivas pandruvada goto error_unreg_buffer_funcs; 357*45fe6f7dSsrinivas pandruvada } 358*45fe6f7dSsrinivas pandruvada 359*45fe6f7dSsrinivas pandruvada ret = iio_device_register(indio_dev); 360*45fe6f7dSsrinivas pandruvada if (ret) { 361*45fe6f7dSsrinivas pandruvada dev_err(&pdev->dev, "device register failed\n"); 362*45fe6f7dSsrinivas pandruvada goto error_remove_trigger; 363*45fe6f7dSsrinivas pandruvada } 364*45fe6f7dSsrinivas pandruvada 365*45fe6f7dSsrinivas pandruvada accel_state->callbacks.send_event = accel_3d_proc_event; 366*45fe6f7dSsrinivas pandruvada accel_state->callbacks.capture_sample = accel_3d_capture_sample; 367*45fe6f7dSsrinivas pandruvada accel_state->callbacks.pdev = pdev; 368*45fe6f7dSsrinivas pandruvada ret = sensor_hub_register_callback(hsdev, HID_USAGE_SENSOR_ACCEL_3D, 369*45fe6f7dSsrinivas pandruvada &accel_state->callbacks); 370*45fe6f7dSsrinivas pandruvada if (ret < 0) { 371*45fe6f7dSsrinivas pandruvada dev_err(&pdev->dev, "callback reg failed\n"); 372*45fe6f7dSsrinivas pandruvada goto error_iio_unreg; 373*45fe6f7dSsrinivas pandruvada } 374*45fe6f7dSsrinivas pandruvada 375*45fe6f7dSsrinivas pandruvada return ret; 376*45fe6f7dSsrinivas pandruvada 377*45fe6f7dSsrinivas pandruvada error_iio_unreg: 378*45fe6f7dSsrinivas pandruvada iio_device_unregister(indio_dev); 379*45fe6f7dSsrinivas pandruvada error_remove_trigger: 380*45fe6f7dSsrinivas pandruvada hid_sensor_remove_trigger(indio_dev); 381*45fe6f7dSsrinivas pandruvada error_unreg_buffer_funcs: 382*45fe6f7dSsrinivas pandruvada iio_triggered_buffer_cleanup(indio_dev); 383*45fe6f7dSsrinivas pandruvada error_free_dev_mem: 384*45fe6f7dSsrinivas pandruvada kfree(indio_dev->channels); 385*45fe6f7dSsrinivas pandruvada error_free_dev: 386*45fe6f7dSsrinivas pandruvada iio_device_free(indio_dev); 387*45fe6f7dSsrinivas pandruvada error_ret: 388*45fe6f7dSsrinivas pandruvada return ret; 389*45fe6f7dSsrinivas pandruvada } 390*45fe6f7dSsrinivas pandruvada 391*45fe6f7dSsrinivas pandruvada /* Function to deinitialize the processing for usage id */ 392*45fe6f7dSsrinivas pandruvada static int __devinit hid_accel_3d_remove(struct platform_device *pdev) 393*45fe6f7dSsrinivas pandruvada { 394*45fe6f7dSsrinivas pandruvada struct hid_sensor_hub_device *hsdev = pdev->dev.platform_data; 395*45fe6f7dSsrinivas pandruvada struct iio_dev *indio_dev = platform_get_drvdata(pdev); 396*45fe6f7dSsrinivas pandruvada 397*45fe6f7dSsrinivas pandruvada sensor_hub_remove_callback(hsdev, HID_USAGE_SENSOR_ACCEL_3D); 398*45fe6f7dSsrinivas pandruvada iio_device_unregister(indio_dev); 399*45fe6f7dSsrinivas pandruvada hid_sensor_remove_trigger(indio_dev); 400*45fe6f7dSsrinivas pandruvada iio_triggered_buffer_cleanup(indio_dev); 401*45fe6f7dSsrinivas pandruvada kfree(indio_dev->channels); 402*45fe6f7dSsrinivas pandruvada iio_device_free(indio_dev); 403*45fe6f7dSsrinivas pandruvada 404*45fe6f7dSsrinivas pandruvada return 0; 405*45fe6f7dSsrinivas pandruvada } 406*45fe6f7dSsrinivas pandruvada 407*45fe6f7dSsrinivas pandruvada static struct platform_driver hid_accel_3d_platform_driver = { 408*45fe6f7dSsrinivas pandruvada .driver = { 409*45fe6f7dSsrinivas pandruvada .name = DRIVER_NAME, 410*45fe6f7dSsrinivas pandruvada .owner = THIS_MODULE, 411*45fe6f7dSsrinivas pandruvada }, 412*45fe6f7dSsrinivas pandruvada .probe = hid_accel_3d_probe, 413*45fe6f7dSsrinivas pandruvada .remove = hid_accel_3d_remove, 414*45fe6f7dSsrinivas pandruvada }; 415*45fe6f7dSsrinivas pandruvada module_platform_driver(hid_accel_3d_platform_driver); 416*45fe6f7dSsrinivas pandruvada 417*45fe6f7dSsrinivas pandruvada MODULE_DESCRIPTION("HID Sensor Accel 3D"); 418*45fe6f7dSsrinivas pandruvada MODULE_AUTHOR("Srinivas Pandruvada <srinivas.pandruvada@intel.com>"); 419*45fe6f7dSsrinivas pandruvada MODULE_LICENSE("GPL"); 420