linux/drivers/iio/common/hid-sensors/hid-sensor-trigger.c
Srinivas Pandruvada 2b89635e9a iio: hid_sensor_hub: Common PM functions
To improvement power and performance, both regular and run time callbacks
are introduced. Because of auto suspend delay, two consecutive read
don't have to go through full power on/off procedure. The auto suspend
time can be adjusted using regular power attributes of PM sysfs.

Signed-off-by: Srinivas Pandruvada <srinivas.pandruvada@linux.intel.com>
Signed-off-by: Jonathan Cameron <jic23@kernel.org>
2015-01-25 22:55:39 +00:00

211 lines
5.7 KiB
C

/*
* HID Sensors Driver
* Copyright (c) 2012, Intel Corporation.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along with
* this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
*
*/
#include <linux/device.h>
#include <linux/platform_device.h>
#include <linux/module.h>
#include <linux/interrupt.h>
#include <linux/irq.h>
#include <linux/slab.h>
#include <linux/delay.h>
#include <linux/hid-sensor-hub.h>
#include <linux/iio/iio.h>
#include <linux/iio/trigger.h>
#include <linux/iio/sysfs.h>
#include "hid-sensor-trigger.h"
static int _hid_sensor_power_state(struct hid_sensor_common *st, bool state)
{
int state_val;
int report_val;
s32 poll_value = 0;
if (state) {
if (sensor_hub_device_open(st->hsdev))
return -EIO;
atomic_inc(&st->data_ready);
state_val = hid_sensor_get_usage_index(st->hsdev,
st->power_state.report_id,
st->power_state.index,
HID_USAGE_SENSOR_PROP_POWER_STATE_D0_FULL_POWER_ENUM);
report_val = hid_sensor_get_usage_index(st->hsdev,
st->report_state.report_id,
st->report_state.index,
HID_USAGE_SENSOR_PROP_REPORTING_STATE_ALL_EVENTS_ENUM);
poll_value = hid_sensor_read_poll_value(st);
} else {
if (!atomic_dec_and_test(&st->data_ready))
return 0;
sensor_hub_device_close(st->hsdev);
state_val = hid_sensor_get_usage_index(st->hsdev,
st->power_state.report_id,
st->power_state.index,
HID_USAGE_SENSOR_PROP_POWER_STATE_D4_POWER_OFF_ENUM);
report_val = hid_sensor_get_usage_index(st->hsdev,
st->report_state.report_id,
st->report_state.index,
HID_USAGE_SENSOR_PROP_REPORTING_STATE_NO_EVENTS_ENUM);
}
if (state_val >= 0) {
state_val += st->power_state.logical_minimum;
sensor_hub_set_feature(st->hsdev, st->power_state.report_id,
st->power_state.index,
(s32)state_val);
}
if (report_val >= 0) {
report_val += st->report_state.logical_minimum;
sensor_hub_set_feature(st->hsdev, st->report_state.report_id,
st->report_state.index,
(s32)report_val);
}
sensor_hub_get_feature(st->hsdev, st->power_state.report_id,
st->power_state.index,
&state_val);
if (state && poll_value)
msleep_interruptible(poll_value * 2);
return 0;
}
EXPORT_SYMBOL(hid_sensor_power_state);
int hid_sensor_power_state(struct hid_sensor_common *st, bool state)
{
#ifdef CONFIG_PM
int ret;
if (state)
ret = pm_runtime_get_sync(&st->pdev->dev);
else {
pm_runtime_mark_last_busy(&st->pdev->dev);
ret = pm_runtime_put_autosuspend(&st->pdev->dev);
}
if (ret < 0) {
if (state)
pm_runtime_put_noidle(&st->pdev->dev);
return ret;
}
return 0;
#else
return _hid_sensor_power_state(st, state);
#endif
}
static int hid_sensor_data_rdy_trigger_set_state(struct iio_trigger *trig,
bool state)
{
return hid_sensor_power_state(iio_trigger_get_drvdata(trig), state);
}
void hid_sensor_remove_trigger(struct hid_sensor_common *attrb)
{
iio_trigger_unregister(attrb->trigger);
iio_trigger_free(attrb->trigger);
}
EXPORT_SYMBOL(hid_sensor_remove_trigger);
static const struct iio_trigger_ops hid_sensor_trigger_ops = {
.owner = THIS_MODULE,
.set_trigger_state = &hid_sensor_data_rdy_trigger_set_state,
};
int hid_sensor_setup_trigger(struct iio_dev *indio_dev, const char *name,
struct hid_sensor_common *attrb)
{
int ret;
struct iio_trigger *trig;
trig = iio_trigger_alloc("%s-dev%d", name, indio_dev->id);
if (trig == NULL) {
dev_err(&indio_dev->dev, "Trigger Allocate Failed\n");
ret = -ENOMEM;
goto error_ret;
}
trig->dev.parent = indio_dev->dev.parent;
iio_trigger_set_drvdata(trig, attrb);
trig->ops = &hid_sensor_trigger_ops;
ret = iio_trigger_register(trig);
if (ret) {
dev_err(&indio_dev->dev, "Trigger Register Failed\n");
goto error_free_trig;
}
attrb->trigger = trig;
indio_dev->trig = iio_trigger_get(trig);
ret = pm_runtime_set_active(&indio_dev->dev);
if (ret)
goto error_unreg_trigger;
iio_device_set_drvdata(indio_dev, attrb);
pm_suspend_ignore_children(&attrb->pdev->dev, true);
pm_runtime_enable(&attrb->pdev->dev);
/* Default to 3 seconds, but can be changed from sysfs */
pm_runtime_set_autosuspend_delay(&attrb->pdev->dev,
3000);
pm_runtime_use_autosuspend(&attrb->pdev->dev);
return ret;
error_unreg_trigger:
iio_trigger_unregister(trig);
error_free_trig:
iio_trigger_free(trig);
error_ret:
return ret;
}
EXPORT_SYMBOL(hid_sensor_setup_trigger);
#ifdef CONFIG_PM
static int hid_sensor_suspend(struct device *dev)
{
struct platform_device *pdev = to_platform_device(dev);
struct iio_dev *indio_dev = platform_get_drvdata(pdev);
struct hid_sensor_common *attrb = iio_device_get_drvdata(indio_dev);
return _hid_sensor_power_state(attrb, false);
}
static int hid_sensor_resume(struct device *dev)
{
struct platform_device *pdev = to_platform_device(dev);
struct iio_dev *indio_dev = platform_get_drvdata(pdev);
struct hid_sensor_common *attrb = iio_device_get_drvdata(indio_dev);
return _hid_sensor_power_state(attrb, true);
}
#endif
const struct dev_pm_ops hid_sensor_pm_ops = {
SET_SYSTEM_SLEEP_PM_OPS(hid_sensor_suspend, hid_sensor_resume)
SET_RUNTIME_PM_OPS(hid_sensor_suspend,
hid_sensor_resume, NULL)
};
EXPORT_SYMBOL(hid_sensor_pm_ops);
MODULE_AUTHOR("Srinivas Pandruvada <srinivas.pandruvada@intel.com>");
MODULE_DESCRIPTION("HID Sensor trigger processing");
MODULE_LICENSE("GPL");