unplugged-kernel/drivers/misc/mediatek/sensors-1.0/sensorfusion/fusion.c

734 lines
18 KiB
C

// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (C) 2019 MediaTek Inc.
*/
#define pr_fmt(fmt) "<FUSION> " fmt
#include "fusion.h"
static struct fusion_context *fusion_context_obj;
static struct fusion_init_info *fusion_init_list[max_fusion_support] = { 0 };
static struct fusion_context *fusion_context_alloc_object(void)
{
int index = 0;
struct fusion_context *obj = kzalloc(sizeof(*obj), GFP_KERNEL);
pr_debug("%s start\n", __func__);
if (!obj) {
pr_err("Alloc fusion object error!\n");
return NULL;
}
mutex_init(&obj->fusion_op_mutex);
for (index = orientation; index < max_fusion_support; ++index) {
obj->fusion_context[index].is_first_data_after_enable = false;
obj->fusion_context[index].is_polling_run = false;
obj->fusion_context[index].is_batch_enable = false;
obj->fusion_context[index].power = 0;
obj->fusion_context[index].enable = 0;
obj->fusion_context[index].delay_ns = -1;
obj->fusion_context[index].latency_ns = -1;
}
pr_debug("%s end\n", __func__);
return obj;
}
static int handle_to_index(int handle)
{
int index = -1;
switch (handle) {
case ID_ORIENTATION:
index = orientation;
break;
case ID_GAME_ROTATION_VECTOR:
index = grv;
break;
case ID_GEOMAGNETIC_ROTATION_VECTOR:
index = gmrv;
break;
case ID_ROTATION_VECTOR:
index = rv;
break;
case ID_LINEAR_ACCELERATION:
index = la;
break;
case ID_GRAVITY:
index = grav;
break;
case ID_ACCELEROMETER_UNCALIBRATED:
index = unacc;
break;
case ID_GYROSCOPE_UNCALIBRATED:
index = ungyro;
break;
case ID_MAGNETIC_UNCALIBRATED:
index = unmag;
break;
case ID_PDR:
index = pdr;
break;
case ID_GYRO_TEMPERATURE:
index = ungyro_temperature;
break;
default:
index = -1;
pr_err("%s invalid handle:%d, index:%d\n", __func__,
handle, index);
return index;
}
return index;
}
#ifndef CONFIG_NANOHUB
static int fusion_enable_and_batch(int index)
{
struct fusion_context *cxt = fusion_context_obj;
int err;
/* power on -> power off */
if (cxt->fusion_context[index].power == 1 &&
cxt->fusion_context[index].enable == 0) {
pr_debug("FUSION disable\n");
/* turn off the power */
err = cxt->fusion_context[index].fusion_ctl.enable_nodata(0);
if (err) {
pr_err("fusion turn off power err = %d\n", err);
return -1;
}
cxt->fusion_context[index].power = 0;
cxt->fusion_context[index].delay_ns = -1;
return 0;
}
/* power off -> power on */
if (cxt->fusion_context[index].power == 0 &&
cxt->fusion_context[index].enable == 1) {
pr_debug("FUSION power on\n");
err = cxt->fusion_context[index].fusion_ctl.enable_nodata(1);
if (err) {
pr_err("fusion turn on power err = %d\n", err);
return -1;
}
cxt->fusion_context[index].power = 1;
}
/* rate change */
if (cxt->fusion_context[index].power == 1 &&
cxt->fusion_context[index].delay_ns >= 0) {
pr_debug("FUSION set batch\n");
/* set ODR, fifo timeout latency */
if (cxt->fusion_context[index].fusion_ctl.is_support_batch)
err = cxt->fusion_context[index].fusion_ctl.batch(0,
cxt->fusion_context[index].delay_ns,
cxt->fusion_context[index].latency_ns);
else
err = cxt->fusion_context[index].fusion_ctl.batch(0,
cxt->fusion_context[index].delay_ns, 0);
if (err) {
pr_err("fusion set batch(ODR) err %d\n", err);
return -1;
}
}
return 0;
}
#endif
static ssize_t fusionactive_store(struct device *dev,
struct device_attribute *attr, const char *buf, size_t count)
{
struct fusion_context *cxt = fusion_context_obj;
int err = 0, handle = -1, en = 0, index = -1;
err = sscanf(buf, "%d,%d", &handle, &en);
if (err < 0) {
pr_err("%s param error: err = %d\n", __func__, err);
return err;
}
pr_debug("%s handle=%d, en=%d\n", __func__, handle, en);
index = handle_to_index(handle);
if (index < 0) {
pr_err("[%s] invalid handle\n", __func__);
return -1;
}
if (cxt->fusion_context[index].fusion_ctl.enable_nodata == NULL) {
pr_err("[%s] ctl not registered\n", __func__);
return -1;
}
mutex_lock(&fusion_context_obj->fusion_op_mutex);
if (en == 1)
cxt->fusion_context[index].enable = 1;
else if (en == 0)
cxt->fusion_context[index].enable = 0;
else {
pr_err("%s error !!\n", __func__);
err = -1;
goto err_out;
}
#ifdef CONFIG_NANOHUB
if (cxt->fusion_context[index].enable == 1) {
err = cxt->fusion_context[index].fusion_ctl.enable_nodata(1);
if (err) {
pr_err("fusion turn on power err = %d\n", err);
goto err_out;
}
} else {
err = cxt->fusion_context[index].fusion_ctl.enable_nodata(0);
if (err) {
pr_err("fusion turn off power err = %d\n", err);
goto err_out;
}
}
#else
err = fusion_enable_and_batch(index);
#endif
pr_debug("%s done\n", __func__);
err_out:
mutex_unlock(&fusion_context_obj->fusion_op_mutex);
if (err)
return err;
else
return count;
}
/*----------------------------------------------------------------------------*/
static ssize_t fusionactive_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
int vendor_div[max_fusion_support];
int index = 0;
struct fusion_context *cxt = fusion_context_obj;
for (index = orientation; index < max_fusion_support; ++index) {
vendor_div[index] =
cxt->fusion_context[index].fusion_data.vender_div;
pr_debug("fusion index:%d vender_div: %d\n",
index, vendor_div[index]);
}
return snprintf(buf, PAGE_SIZE, "%d,%d,%d,%d,%d,%d,%d,%d,%d,%d\n",
vendor_div[orientation], vendor_div[grv],
vendor_div[gmrv], vendor_div[rv],
vendor_div[la], vendor_div[grav], vendor_div[unacc],
vendor_div[ungyro], vendor_div[unmag], vendor_div[pdr]);
}
static ssize_t fusiondevnum_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
return snprintf(buf, PAGE_SIZE, "%d\n", 0);
}
static ssize_t fusionbatch_store(struct device *dev,
struct device_attribute *attr, const char *buf, size_t count)
{
struct fusion_context *cxt = fusion_context_obj;
int index = -1, handle = 0, flag = 0, err = 0;
int64_t samplingPeriodNs = 0, maxBatchReportLatencyNs = 0;
err = sscanf(buf, "%d,%d,%lld,%lld",
&handle, &flag, &samplingPeriodNs, &maxBatchReportLatencyNs);
if (err != 4) {
pr_err("%s param error: err = %d\n", __func__, err);
return err;
}
index = handle_to_index(handle);
if (index < 0) {
pr_err("[%s] invalid handle\n", __func__);
return -1;
}
if (cxt->fusion_context[index].fusion_ctl.batch == NULL) {
pr_debug("[%s] ctl not registered\n", __func__);
return -1;
}
pr_debug("handle %d, flag:%d, PeriodNs:%lld, LatencyNs: %lld\n",
handle, flag, samplingPeriodNs, maxBatchReportLatencyNs);
cxt->fusion_context[index].delay_ns = samplingPeriodNs;
cxt->fusion_context[index].latency_ns = maxBatchReportLatencyNs;
mutex_lock(&fusion_context_obj->fusion_op_mutex);
#ifdef CONFIG_NANOHUB
if (cxt->fusion_context[index].delay_ns >= 0) {
if (cxt->fusion_context[index].fusion_ctl.is_support_batch)
err = cxt->fusion_context[index].fusion_ctl.batch(0,
cxt->fusion_context[index].delay_ns,
cxt->fusion_context[index].latency_ns);
else
err = cxt->fusion_context[index].fusion_ctl.batch(0,
cxt->fusion_context[index].delay_ns, 0);
if (err) {
pr_err("fusion set batch(ODR) err %d\n", err);
goto err_out;
}
} else
pr_info("batch state no need change\n");
#else
err = fusion_enable_and_batch(index);
#endif
pr_debug("%s done\n", __func__);
err_out:
mutex_unlock(&fusion_context_obj->fusion_op_mutex);
if (err)
return err;
else
return count;
}
static ssize_t fusionbatch_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
return snprintf(buf, PAGE_SIZE, "%d\n", 0);
}
static ssize_t fusionflush_store(struct device *dev,
struct device_attribute *attr, const char *buf, size_t count)
{
struct fusion_context *cxt = NULL;
int index = -1, handle = 0, err = 0;
err = kstrtoint(buf, 10, &handle);
if (err != 0)
pr_err("%s param error: err = %d\n", __func__, err);
pr_debug("%s param: handle %d\n", __func__, handle);
mutex_lock(&fusion_context_obj->fusion_op_mutex);
cxt = fusion_context_obj;
index = handle_to_index(handle);
if (index < 0) {
pr_err("[%s] invalid index\n", __func__);
mutex_unlock(&fusion_context_obj->fusion_op_mutex);
return -1;
}
if (cxt->fusion_context[index].fusion_ctl.flush != NULL)
err = cxt->fusion_context[index].fusion_ctl.flush();
else
pr_err("FUSION OLD ARCH NOT SUPPORT COMMON VRS FLUSH\n");
if (err < 0)
pr_err("fusion enable flush err %d\n", err);
mutex_unlock(&fusion_context_obj->fusion_op_mutex);
if (err)
return err;
else
return count;
}
static ssize_t fusionflush_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
return snprintf(buf, PAGE_SIZE, "%d\n", 0);
}
static int fusion_real_driver_init(void)
{
int index = 0;
int err = -1;
pr_debug("%s start\n", __func__);
for (index = 0; index < max_fusion_support; index++) {
pr_debug("index = %d\n", index);
if (fusion_init_list[index] != NULL) {
pr_debug("fusion try to init driver %s\n",
fusion_init_list[index]->name);
err = fusion_init_list[index]->init();
if (err == 0)
pr_debug("fusion real driver %s probe ok\n",
fusion_init_list[index]->name);
}
}
return err;
}
static int fusion_open(struct inode *inode, struct file *file)
{
nonseekable_open(inode, file);
return 0;
}
static ssize_t fusion_read(struct file *file, char __user *buffer,
size_t count, loff_t *ppos)
{
ssize_t read_cnt = 0;
read_cnt = sensor_event_read(fusion_context_obj->mdev.minor,
file, buffer, count, ppos);
return read_cnt;
}
static unsigned int fusion_poll(struct file *file, poll_table *wait)
{
return sensor_event_poll(fusion_context_obj->mdev.minor, file, wait);
}
static const struct file_operations fusion_fops = {
.owner = THIS_MODULE,
.open = fusion_open,
.read = fusion_read,
.poll = fusion_poll,
};
static int fusion_misc_init(struct fusion_context *cxt)
{
int err = 0;
cxt->mdev.minor = ID_GAME_ROTATION_VECTOR;
cxt->mdev.name = FUSION_MISC_DEV_NAME;
cxt->mdev.fops = &fusion_fops;
err = sensor_attr_register(&cxt->mdev);
if (err)
pr_err("unable to register fusion misc device!!\n");
/* dev_set_drvdata(cxt->mdev.this_device, cxt); */
return err;
}
DEVICE_ATTR_RW(fusionactive);
DEVICE_ATTR_RW(fusionbatch);
DEVICE_ATTR_RW(fusionflush);
DEVICE_ATTR_RO(fusiondevnum);
static struct attribute *fusion_attributes[] = {
&dev_attr_fusionactive.attr,
&dev_attr_fusionbatch.attr,
&dev_attr_fusionflush.attr,
&dev_attr_fusiondevnum.attr,
NULL
};
static struct attribute_group fusion_attribute_group = {
.attrs = fusion_attributes
};
int fusion_register_data_path(struct fusion_data_path *data, int handle)
{
int index = -1;
struct fusion_context *cxt = NULL;
if (data == NULL) {
pr_err("fail\n");
return -1;
}
index = handle_to_index(handle);
if (index < 0) {
pr_err("[%s] invalid handle\n", __func__);
return -1;
}
cxt = fusion_context_obj;
cxt->fusion_context[index].fusion_data.get_data = data->get_data;
cxt->fusion_context[index].fusion_data.vender_div = data->vender_div;
pr_debug("fusion handle:%d vender_div: %d\n",
handle, cxt->fusion_context[index].fusion_data.vender_div);
return 0;
}
int fusion_register_control_path(struct fusion_control_path *ctl,
int handle)
{
struct fusion_context *cxt = NULL;
int index = -1;
if (NULL == ctl || NULL == ctl->set_delay
|| NULL == ctl->open_report_data
|| NULL == ctl->enable_nodata
|| NULL == ctl->batch || NULL == ctl->flush) {
pr_err("fusion handle:%d register control path fail\n",
handle);
return -1;
}
index = handle_to_index(handle);
if (index < 0) {
pr_err("[%s] invalid handle\n", __func__);
return -1;
}
cxt = fusion_context_obj;
cxt->fusion_context[index].fusion_ctl.set_delay =
ctl->set_delay;
cxt->fusion_context[index].fusion_ctl.open_report_data =
ctl->open_report_data;
cxt->fusion_context[index].fusion_ctl.enable_nodata =
ctl->enable_nodata;
cxt->fusion_context[index].fusion_ctl.batch = ctl->batch;
cxt->fusion_context[index].fusion_ctl.flush = ctl->flush;
cxt->fusion_context[index].fusion_ctl.is_support_batch =
ctl->is_support_batch;
cxt->fusion_context[index].fusion_ctl.is_report_input_direct =
ctl->is_report_input_direct;
return 0;
}
static int fusion_data_report(int x, int y, int z,
int scalar, int status, int64_t nt, int handle)
{
/* pr_debug("+fusion_data_report! %d, %d, %d, %d\n",x,y,z,status); */
struct sensor_event event;
int err = 0;
memset(&event, 0, sizeof(struct sensor_event));
event.handle = handle;
event.flush_action = DATA_ACTION;
event.time_stamp = nt;
event.status = status;
event.word[0] = x;
event.word[1] = y;
event.word[2] = z;
event.word[3] = scalar;
err = sensor_input_event(fusion_context_obj->mdev.minor, &event);
return err;
}
static int fusion_flush_report(int handle)
{
struct sensor_event event;
int err = 0;
memset(&event, 0, sizeof(struct sensor_event));
pr_debug("flush\n");
event.handle = handle;
event.flush_action = FLUSH_ACTION;
err = sensor_input_event(fusion_context_obj->mdev.minor, &event);
return err;
}
static int uncali_sensor_data_report(int *data,
int status, int64_t nt, int handle)
{
struct sensor_event event;
int err = 0;
memset(&event, 0, sizeof(struct sensor_event));
event.handle = handle;
event.flush_action = DATA_ACTION;
event.time_stamp = nt;
event.status = status;
event.word[0] = data[0];
event.word[1] = data[1];
event.word[2] = data[2];
event.word[3] = data[3];
event.word[4] = data[4];
event.word[5] = data[5];
err = sensor_input_event(fusion_context_obj->mdev.minor, &event);
return err;
}
static int uncali_sensor_flush_report(int handle)
{
struct sensor_event event;
int err = 0;
memset(&event, 0, sizeof(struct sensor_event));
pr_debug_ratelimited("flush handle:%d\n", handle);
event.handle = handle;
event.flush_action = FLUSH_ACTION;
err = sensor_input_event(fusion_context_obj->mdev.minor, &event);
return err;
}
int rv_data_report(int x, int y, int z, int scalar, int status, int64_t nt)
{
return fusion_data_report(x, y, z, scalar, status, nt,
ID_ROTATION_VECTOR);
}
int rv_flush_report(void)
{
return fusion_flush_report(ID_ROTATION_VECTOR);
}
int grv_data_report(int x, int y, int z, int scalar, int status, int64_t nt)
{
return fusion_data_report(x, y, z, scalar, status, nt,
ID_GAME_ROTATION_VECTOR);
}
int grv_flush_report(void)
{
return fusion_flush_report(ID_GAME_ROTATION_VECTOR);
}
int gmrv_data_report(int x, int y, int z,
int scalar, int status, int64_t nt)
{
return fusion_data_report(x, y, z, scalar, status, nt,
ID_GEOMAGNETIC_ROTATION_VECTOR);
}
int gmrv_flush_report(void)
{
return fusion_flush_report(ID_GEOMAGNETIC_ROTATION_VECTOR);
}
int grav_data_report(int x, int y, int z, int status, int64_t nt)
{
return fusion_data_report(x, y, z, 0, status, nt, ID_GRAVITY);
}
int grav_flush_report(void)
{
return fusion_flush_report(ID_GRAVITY);
}
int la_data_report(int x, int y, int z, int status, int64_t nt)
{
return fusion_data_report(x, y, z, 0, status, nt,
ID_LINEAR_ACCELERATION);
}
int la_flush_report(void)
{
return fusion_flush_report(ID_LINEAR_ACCELERATION);
}
int orientation_data_report(int x, int y, int z, int status, int64_t nt)
{
return fusion_data_report(x, y, z, 0, status, nt, ID_ORIENTATION);
}
int orientation_flush_report(void)
{
return fusion_flush_report(ID_ORIENTATION);
}
int uncali_acc_data_report(int *data, int status, int64_t nt)
{
return uncali_sensor_data_report(data,
status, nt, ID_ACCELEROMETER_UNCALIBRATED);
}
int uncali_acc_flush_report(void)
{
return uncali_sensor_flush_report(ID_ACCELEROMETER_UNCALIBRATED);
}
int uncali_gyro_data_report(int *data, int status, int64_t nt)
{
return uncali_sensor_data_report(data,
status, nt, ID_GYROSCOPE_UNCALIBRATED);
}
int uncali_gyro_temperature_data_report(int *data, int status, int64_t nt)
{
return uncali_sensor_data_report(data, status, nt, ID_GYRO_TEMPERATURE);
}
int uncali_gyro_temperature_flush_report(void)
{
return uncali_sensor_flush_report(ID_GYRO_TEMPERATURE);
}
int uncali_gyro_flush_report(void)
{
return uncali_sensor_flush_report(ID_GYROSCOPE_UNCALIBRATED);
}
int uncali_mag_data_report(int *data, int status, int64_t nt)
{
return uncali_sensor_data_report(data,
status, nt, ID_MAGNETIC_UNCALIBRATED);
}
int uncali_mag_flush_report(void)
{
return uncali_sensor_flush_report(ID_MAGNETIC_UNCALIBRATED);
}
static int fusion_probe(void)
{
int err;
pr_debug("%s+++!!\n", __func__);
fusion_context_obj = fusion_context_alloc_object();
if (!fusion_context_obj) {
err = -ENOMEM;
pr_err("unable to allocate devobj!\n");
goto exit_alloc_data_failed;
}
/* init real fusioneleration driver */
err = fusion_real_driver_init();
if (err) {
pr_err("fusion real driver init fail\n");
goto real_driver_init_fail;
}
/* add misc dev for sensor hal control cmd */
err = fusion_misc_init(fusion_context_obj);
if (err) {
pr_err("unable to register fusion misc device!!\n");
goto real_driver_init_fail;
}
err = sysfs_create_group(&fusion_context_obj->mdev.this_device->kobj,
&fusion_attribute_group);
if (err < 0) {
pr_err("unable to create fusion attribute file\n");
goto real_driver_init_fail;
}
kobject_uevent(&fusion_context_obj->mdev.this_device->kobj, KOBJ_ADD);
pr_debug("%s--- OK !!\n", __func__);
return 0;
real_driver_init_fail:
kfree(fusion_context_obj);
exit_alloc_data_failed:
pr_debug("%s---- fail !!!\n", __func__);
return err;
}
static int fusion_remove(void)
{
int err = 0;
pr_debug("%s\n", __func__);
sysfs_remove_group(&fusion_context_obj->mdev.this_device->kobj,
&fusion_attribute_group);
err = sensor_attr_deregister(&fusion_context_obj->mdev);
if (err)
pr_err("misc_deregister fail: %d\n", err);
kfree(fusion_context_obj);
return 0;
}
int fusion_driver_add(struct fusion_init_info *obj, int handle)
{
int err = 0;
int index = 0;
pr_debug("handle:%d\n", handle);
if (!obj) {
pr_err("FUSION handle: %d, driver add fail\n", handle);
return -1;
}
index = handle_to_index(handle);
if (index < 0) {
pr_err("[%s] invalid index\n", __func__);
return -1;
}
if (fusion_init_list[index] == NULL)
fusion_init_list[index] = obj;
else
pr_err("fusion_init_list handle:%d already exist\n",
handle);
return err;
}
static int __init fusion_init(void)
{
pr_debug("%s\n", __func__);
if (fusion_probe()) {
pr_err("failed to register fusion driver\n");
return -ENODEV;
}
return 0;
}
static void __exit fusion_exit(void)
{
fusion_remove();
}
late_initcall(fusion_init);
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("FUSION device driver");
MODULE_AUTHOR("Mediatek");