Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Unverified Commit 0968cc98 authored by derfelot's avatar derfelot
Browse files

misc: Add ToF sensor driver from Sony kernel

Taken from Sony 47.2.A.10.107 stock kernel
parent 65fec3ae
Loading
Loading
Loading
Loading
+9 −0
Original line number Diff line number Diff line
@@ -657,6 +657,15 @@ config LDO_VIBRATOR
	  on the LDO line. The vibrator is controlled using the
	  timed output class.

config TOF_SENSOR
	tristate "ToF Sensor"
	depends on I2C
	---help---
	  Say Y here if you want to use ToF sensor.

	  To compile this driver as a module, choose M here: the
	  module will be called tof_sensor.

config SIM_DETECT
	tristate "SIM_DETECT driver support"
	default n
+1 −0
Original line number Diff line number Diff line
@@ -72,6 +72,7 @@ obj-y += qcom/
obj-$(CONFIG_QPNP_MISC) 	+= qpnp-misc.o
obj-$(CONFIG_MEMORY_STATE_TIME) += memory_state_time.o
obj-$(CONFIG_LDO_VIBRATOR) += ldo_vibrator.o
obj-$(CONFIG_TOF_SENSOR) += tof_sensor.o
obj-$(CONFIG_RAMDUMP_TAGS) += rdtags.o
obj-$(CONFIG_RAMDUMP_MEMDESC) += ramdump_mem_desc.o
obj-$(CONFIG_POWERKEY_FORCECRASH) += powerkey_forcecrash.o
+646 −0
Original line number Diff line number Diff line
/*
 * Tof sensor driver
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License.
 */
/*
 * Copyright (C) 2016 Sony Mobile Communications Inc.
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License version 2, as
 * published by the Free Software Foundation.
 */

#include <linux/uaccess.h>
#include <linux/kobject.h>

#include <linux/platform_device.h>
#include <linux/irq.h>
#include <linux/module.h>
#include <linux/interrupt.h>
#include <linux/of_gpio.h>
#include <linux/gpio.h>

#include <linux/init.h>
#include <linux/input.h>
#include <linux/regulator/consumer.h>
#include <linux/i2c.h>
#include <linux/delay.h>
#include <linux/slab.h>
#include <linux/tof_sensor.h>

#define ENABLE_LOGE

#ifdef ENABLE_LOGE
#define LOGE(dev, f, a...)	dev_err(dev, "%s: " f, __func__, ##a)
#else
#define LOGE(dev, f, a...)
#endif

#ifdef ENABLE_LOGI
#define LOGI(dev, f, a...)	dev_info(dev, "%s: " f, __func__, ##a)
#else
#define LOGI(dev, f, a...)
#endif

#ifdef ENABLE_LOGD
#define LOGD(dev, f, a...)	dev_dbg(dev, "%s: " f, __func__, ##a)
#else
#define LOGD(dev, f, a...)
#endif

#define TOF_SENSOR_SYSFS_LINK_NAME "tof_sensor"
#define TOF_SENSOR_PINCTRL_IRQ_ACTIVE "tof_irq_active"
#define TOF_SENSOR_PINCTRL_IRQ_SUSPEND "tof_irq_suspend"
#define TOF_SENSOR_REGULATOR_NOTIFY_PRIORITY (1000)

struct tof_sensor_info {
	char name[8];
	uint32_t i2c_client_id;
	uint32_t need_camera_on;
	uint32_t facing;
	uint32_t vdd_supply_enable;
	uint32_t gpio_avdd_enable;
};

struct tof_sensor_data {
	struct i2c_client *client;
	struct regulator *avdd;
	struct regulator *gpio_avdd;
	uint32_t min_voltage;
	uint32_t max_voltage;
	uint8_t power_up;
	bool is_avdd_enabled;
	bool is_register_regulator_cam_vio_notify;
	struct input_dev *input;
	const char *dev_name;
	struct pinctrl *pinctrl;
	struct pinctrl_state *gpio_state_active;
	struct pinctrl_state *gpio_state_suspend;
	struct tof_sensor_info info;
	struct notifier_block cam_io_regulator_cb;
	int tof_reset_gpio;
	uint32_t ref_cnt;
};

static int tof_sensor_cam_io_notifier_call(struct notifier_block *self,
	unsigned long event, void *data)
{
	struct tof_sensor_data *tof_data;
	uint32_t rc;

	if (event & REGULATOR_EVENT_DISABLE) {
		tof_data = container_of(self, struct tof_sensor_data,
			cam_io_regulator_cb);
		LOGD(&tof_data->client->dev,
			"%s REGULATOR_EVENT_DISABLE occurred event \n", __func__);
		if (tof_data->is_avdd_enabled && tof_data->avdd) {
			rc = regulator_disable(tof_data->avdd);
			tof_data->is_avdd_enabled = false;
			if (!rc)
				LOGD(&tof_data->client->dev,
					"%s: Regulator avdd disable OK",
					__func__);
			else
				LOGE(&tof_data->client->dev,
					"%s: Regulator avdd disable failed ",
					__func__);
		}
	}
	return NOTIFY_OK;
}

static int tof_sensor_power_init(struct tof_sensor_data *data)
{
	int rc = 0;

	if ((data->avdd == NULL) && data->info.vdd_supply_enable) {
		data->avdd = regulator_get(&data->client->dev, "tof_avdd");
		if (IS_ERR(data->avdd)) {
			rc = PTR_ERR(data->avdd);
			LOGE(&data->client->dev,
				"Regulator get failed, avdd, rc = %d\n", rc);
			return rc;
		}
	}
	if ((data->gpio_avdd == NULL) && data->info.gpio_avdd_enable) {
		data->gpio_avdd = regulator_get(&data->client->dev, "tof-gpio-avdd");
		if (IS_ERR(data->gpio_avdd)) {
			rc = PTR_ERR(data->gpio_avdd);
			LOGE(&data->client->dev,
				"Regulator get failed, avdd, rc = %d\n", rc);
			return rc;
		}
	}
	data->is_avdd_enabled = false;
	data->is_register_regulator_cam_vio_notify = false;
	LOGI(&data->client->dev, "%s: power init, regulator get OK",
		__func__);
	return rc;
}

static int tof_sensor_power_deinit(struct tof_sensor_data *data)
{
	int rc = 0;
	if (!data)
		return -EINVAL;
	if (data->info.vdd_supply_enable && data->avdd)
		regulator_put(data->avdd);
	if (data->is_register_regulator_cam_vio_notify) {
		regulator_unregister_notifier(data->gpio_avdd, &(data->cam_io_regulator_cb));
		data->is_register_regulator_cam_vio_notify = false;
	}
	if (data->info.gpio_avdd_enable && data->gpio_avdd)
		regulator_put(data->gpio_avdd);
	return rc;
}

static int tof_sensor_pinctrl_init(struct tof_sensor_data *data)
{

	data->pinctrl = devm_pinctrl_get(&data->client->dev);
	if (IS_ERR_OR_NULL(data->pinctrl)) {
		LOGE(&data->client->dev,
			"%s:%d Getting pinctrl handle failed\n",
			__func__, __LINE__);
		return -EINVAL;
	}
	data->gpio_state_active =
		pinctrl_lookup_state(data->pinctrl,
				TOF_SENSOR_PINCTRL_IRQ_ACTIVE);
	if (IS_ERR_OR_NULL(data->gpio_state_active)) {
		LOGE(&data->client->dev,
			"%s:Failed to get the active state pinctrl handle\n",
			__func__);
		return -EINVAL;
	}
	data->gpio_state_suspend
		= pinctrl_lookup_state(data->pinctrl,
				TOF_SENSOR_PINCTRL_IRQ_SUSPEND);
	if (IS_ERR_OR_NULL(data->gpio_state_suspend)) {
		LOGE(&data->client->dev,
			"%s:Failed to get the suspend state pinctrl handle\n",
			__func__);
		return -EINVAL;
	}
	return 0;
}

static int tof_sensor_gpio_init(struct tof_sensor_data *data)
{
	int rc = 0;
	int flag = 0;

	rc = gpio_request_one(data->tof_reset_gpio, flag, "tof-reset-gpio");
	if (rc) {
		LOGE(&data->client->dev,
			"%s:%d Getting reset gpio failed\n",
			__func__, __LINE__);
		return -EINVAL;
	} else {
		return 0;
	}
}

static int tof_sensor_parse_dt(struct tof_sensor_data *data)
{
	int rc = 0;
	const char *name = NULL;
	uint32_t val_u32 = 0;

	rc = of_property_read_string_index(data->client->dev.of_node,
		"sony,tof-sensor-name", 0, (const char **)(&name));
	if (rc < 0) {
		LOGE(&data->client->dev, "%s failed %d\n", __func__, __LINE__);
		goto fail;
	}
	memcpy(data->info.name, name, 8);

	rc = of_property_read_u32(data->client->dev.of_node,
		"sony,tof-need-cam-on", &val_u32);
	if (rc < 0) {
		LOGE(&data->client->dev, "%s failed %d\n", __func__, __LINE__);
		goto fail;
	}
	data->info.need_camera_on = val_u32;

	rc = of_property_read_u32(data->client->dev.of_node,
		"sony,tof-sensor-facing", &val_u32);
	if (rc < 0) {
		LOGE(&data->client->dev, "%s failed %d\n", __func__, __LINE__);
		goto fail;
	}
	data->info.facing = val_u32;

	data->tof_reset_gpio = of_get_named_gpio(data->client->dev.of_node,
		"tof-reset-gpio", 0);

	rc = of_property_read_u32(data->client->dev.of_node,
		"sony,tof-avdd-supply", &val_u32);
	if (rc < 0) {
		LOGE(&data->client->dev, "%s failed %d\n", __func__, __LINE__);
		goto fail;
	}
	data->info.vdd_supply_enable = val_u32;

	rc = of_property_read_u32(data->client->dev.of_node,
		"sony,tof-gpio-avdd", &val_u32);
	if (rc < 0) {
		LOGE(&data->client->dev, "%s failed %d\n", __func__, __LINE__);
		goto fail;
	}
	data->info.gpio_avdd_enable = val_u32;


	LOGD(&data->client->dev, "%s name %s, need_camera_on %d, facing %d\n",
		__func__, data->info.name, data->info.need_camera_on,
		data->info.facing);

fail:
	return rc;
}

static irqreturn_t tof_sensor_irq(int irq, void *handle)
{
	struct tof_sensor_data *data = handle;

	LOGI(&data->client->dev, "Tof sensor irq");
	sysfs_notify(&data->input->dev.kobj, NULL, "tof_ranging_notify");

	return IRQ_HANDLED;
}

static ssize_t tof_sensor_power_ctl(struct device *dev,
	struct device_attribute *attr, const char *buf, size_t count)
{
	struct tof_sensor_data *data = dev_get_drvdata(dev);
	int rc = 0;
	unsigned long value = 0;

	LOGD(&data->client->dev,
		"%s: Power control data", __func__);
	rc = kstrtoul(buf, sizeof(value), &value);
	if (rc) {
		LOGE(&data->client->dev,
			"%s: covert error. rc = %d", __func__, rc);
		return count;
	}
	if (value == TOF_SENSOR_POWER_CONTROL_ON) {
		if (!data->ref_cnt) {
			/* power on */
			if (!rc &&
				data->pinctrl && data->gpio_state_active) {
				rc = pinctrl_select_state(data->pinctrl,
					data->gpio_state_active);
				if (rc)
					LOGE(&data->client->dev,
						"%s: cannot set pin to active state",
						__func__);
			}
			if (!rc) {
				rc = request_threaded_irq(data->client->irq, NULL,
					&tof_sensor_irq,
					IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
					dev_name(&data->client->dev), data);
				if (rc) {
					LOGE(&data->client->dev,
						"Failed to request irq %d\n",
						data->client->irq);
					(void)pinctrl_select_state(data->pinctrl,
						data->gpio_state_suspend);
				}
			}
			if (!rc && data->is_register_regulator_cam_vio_notify) {
				regulator_unregister_notifier(data->gpio_avdd,
					&(data->cam_io_regulator_cb));
				data->is_register_regulator_cam_vio_notify = false;
			}
			if (!rc && data->info.vdd_supply_enable) {
				rc = regulator_enable(data->avdd);
				if (rc) {
					LOGE(&data->client->dev,
						"Regulator avdd enable failed rc=%d",
						rc);
					pinctrl_select_state(data->pinctrl,
						data->gpio_state_suspend);
					free_irq(data->client->irq, data);
					return count;
				}
				data->is_avdd_enabled = true;
				usleep_range(3000, 4000);
			}
			if (!rc && data->info.gpio_avdd_enable) {
				rc = regulator_enable(data->gpio_avdd);
				if (rc) {
					LOGE(&data->client->dev,
						"Regulator gpio_avdd enable failed rc=%d", rc);
					pinctrl_select_state(data->pinctrl,
						data->gpio_state_suspend);
					free_irq(data->client->irq, data);
					return count;
				}
			}
			if (!rc && data->info.need_camera_on) {
				rc = gpio_direction_output(data->tof_reset_gpio, 1);
				if (rc) {
					LOGE(&data->client->dev,
						"%s: reset enable failed",
						__func__);
					regulator_disable(data->avdd);
					free_irq(data->client->irq, data);
					pinctrl_select_state(data->pinctrl,
						data->gpio_state_suspend);
					return count;
				}
				LOGD(&data->client->dev,
					"%s: power up regulator enable OK",
					__func__);
					usleep_range(3000, 4000);
					data->power_up = 1;
			}
		}
		data->ref_cnt++;
	} else if (value == TOF_SENSOR_POWER_CONTROL_OFF) {
		/* power off */
		if (data->ref_cnt > 0) {
			data->ref_cnt--;
			if (!data->ref_cnt) {
				if (data->info.need_camera_on) {
					rc = gpio_direction_output(data->tof_reset_gpio, 0);
					if (rc)
						LOGE(&data->client->dev,
							"%s: reset disable failed",
							__func__);
					else {
						data->power_up = 0;
						LOGD(&data->client->dev,
							"%s: reset disable ok",
							__func__);
					}
				}
				if (data->info.gpio_avdd_enable && data->info.vdd_supply_enable &&
					!data->is_register_regulator_cam_vio_notify) {
					data->cam_io_regulator_cb.notifier_call =
						tof_sensor_cam_io_notifier_call;
					data->cam_io_regulator_cb.priority =
						TOF_SENSOR_REGULATOR_NOTIFY_PRIORITY;
					if (regulator_register_notifier(data->gpio_avdd,
						&(data->cam_io_regulator_cb)))
						LOGE(&data->client->dev,
							"%s Regulator notification registration failed!\n",
							__func__);
					else
						data->is_register_regulator_cam_vio_notify = true;
				}
				if (data->info.gpio_avdd_enable) {
					rc = regulator_disable(data->gpio_avdd);
					if (rc) {
						LOGE(&data->client->dev,
							"%s: Regulator gpio_avdd disable failed ",
							__func__);
					} else {
						LOGE(&data->client->dev,
							"%s: power up gpio_regulator disable OK",
							__func__);
					}
				}
				if (data->info.vdd_supply_enable) {
					if (!data->is_register_regulator_cam_vio_notify) {
						rc = regulator_disable(data->avdd);
						data->is_avdd_enabled = 0;
						if (!rc)
							LOGD(&data->client->dev,
								"%s: Regulator avdd disable OK",
								__func__);
						else
							LOGE(&data->client->dev,
								"%s: Regulator avdd disable failed ",
								__func__);
					}
				}
				free_irq(data->client->irq, data);
				if (data->pinctrl && data->gpio_state_suspend) {
					rc = pinctrl_select_state(data->pinctrl,
						data->gpio_state_suspend);
					if (rc)
						LOGE(&data->client->dev,
							"%s: cannot set pin to suspend state",
							__func__);
				}
			}
		}
	} else
		LOGE(&data->client->dev,
			"%s: Power control data error [%ld]", __func__,
			value);

	return count;
}

static ssize_t tof_sensor_show_power_status(struct device *dev,
	struct device_attribute *attr, char *buf)
{
	struct tof_sensor_data *data = dev_get_drvdata(dev);

	LOGI(dev, "Get power stat is called");

	return snprintf(buf, PAGE_SIZE, "%d\n", data->power_up);
}

static ssize_t tof_sensor_ranging_notify(struct device *dev,
	struct device_attribute *attr, char *buf)
{
	LOGI(dev, "Ranging notify is called");

	return snprintf(buf, PAGE_SIZE, "%d", 1);
}

static ssize_t tof_sensor_get_info(struct device *dev,
	struct device_attribute *attr, char *buf)
{
	struct tof_sensor_data *data = dev_get_drvdata(dev);

	LOGI(dev, "Get information is called");

	return snprintf(buf, PAGE_SIZE, "%s,%u,%u,%u", data->info.name,
		data->info.i2c_client_id, data->info.need_camera_on,
		data->info.facing);
}

static DEVICE_ATTR(tof_get_info, S_IRUGO, tof_sensor_get_info, NULL);
static DEVICE_ATTR(tof_ranging_notify, S_IRUGO, tof_sensor_ranging_notify,
	NULL);
static DEVICE_ATTR(tof_power_ctl, S_IRUGO | S_IWUSR | S_IWGRP,
	tof_sensor_show_power_status, tof_sensor_power_ctl);
static struct attribute *tof_sensor_attributes[] = {
	&dev_attr_tof_get_info.attr,
	&dev_attr_tof_ranging_notify.attr,
	&dev_attr_tof_power_ctl.attr,
	NULL
};

static const struct attribute_group tof_sensor_attr_group = {
	.attrs = tof_sensor_attributes,
};

static int tof_sensor_probe(struct i2c_client *client,
	const struct i2c_device_id *id)
{
	struct tof_sensor_data *data;
	int rc = 0;

	LOGI(&client->dev, "start probing stmdata\n");

	if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
		LOGE(&client->dev,
			"%s: check_functionality failed.", __func__);
		return -EIO;
	}

	data = kzalloc(sizeof(struct tof_sensor_data), GFP_KERNEL);
	if (!data) {
		LOGE(&client->dev, "failed: no memory to alloc data %p", data);
		return -ENOMEM;
	}

	i2c_set_clientdata(client, data);
	data->client = client;
	if (data->client->dev.of_node) {
		rc = tof_sensor_pinctrl_init(data);
		if (rc) {
			LOGE(&client->dev,
				"%s: failed to pinctrl init", __func__);
			goto exit_free_i2c;
		}
		rc = tof_sensor_parse_dt(data);
		if (rc) {
			LOGE(&client->dev,
				"%s: failed to parse dt", __func__);
			goto exit_free_i2c;
		}
	}
	rc = tof_sensor_power_init(data);
	if (rc) {
		LOGE(&client->dev,
			"%s: failed to power init", __func__);
		goto exit_free_i2c;
	}
	tof_sensor_gpio_init(data);
	data->ref_cnt = 0;

	data->dev_name = dev_name(&client->dev);
	data->input = input_allocate_device();
	if (!data->input) {
		rc = -ENOMEM;
		LOGE(&client->dev,
			"%s: failed to allocate input device", __func__);
		goto exit_free_i2c;
	}
	data->input->name = "ToF Sensor";
	rc = input_register_device(data->input);
	if (rc) {
		LOGE(&client->dev, "Failed to register input device");
		goto exit_free_dev;
	}
	input_set_drvdata(data->input, data);

	LOGI(&client->dev, "tof sensor: i2c nr%d\n", client->adapter->nr);
	data->info.i2c_client_id = client->adapter->nr;
	data->power_up = 0;

	/* sysfs */
	rc = sysfs_create_group(&data->input->dev.kobj,
		&tof_sensor_attr_group);
	if (rc) {
		rc = -ENOMEM;
		LOGE(&client->dev,
			"%s: failed to create sysfs group", __func__);
		goto exit_unregister_sysfs;
	}

	rc = sysfs_create_link(data->input->dev.kobj.parent,
		&data->input->dev.kobj,
		TOF_SENSOR_SYSFS_LINK_NAME);
	if (rc) {
		rc = -ENOMEM;
		LOGE(&client->dev,
			"%s: failed to create sysfs link", __func__);
		goto exit_unregister_sysfs;
	}
	return rc;

exit_unregister_sysfs:
	input_unregister_device(data->input);
exit_free_dev:
	input_free_device(data->input);
exit_free_i2c:
	tof_sensor_power_deinit(data);
	i2c_set_clientdata(data->client, NULL);
	kfree(data);

	return rc;
}

static int tof_sensor_remove(struct i2c_client *client)
{
	struct tof_sensor_data *data = i2c_get_clientdata(client);

	gpio_free(data->tof_reset_gpio);
	tof_sensor_power_deinit(data);
	sysfs_remove_link(&data->input->dev.kobj,
		TOF_SENSOR_SYSFS_LINK_NAME);
	input_unregister_device(data->input);
	input_free_device(data->input);
	LOGI(&data->client->dev, "Removed.");
	i2c_set_clientdata(data->client, NULL);
	kfree(data);

	return 0;
}

static struct i2c_device_id tof_sensor_id[] = {
	{ "tof_sensor", 0 },
	{ },
};
MODULE_DEVICE_TABLE(i2c, tof_sensor_id);

static struct of_device_id st_tof_sensor_dt_match[] = {
	{ .compatible = "tof_sensor", },
	{ },
};

static struct i2c_driver tof_sensor_i2c_driver = {
	.driver = {
		.name = "tof_sensor",
		.owner = THIS_MODULE,
		.of_match_table = st_tof_sensor_dt_match,
	},
	.probe = tof_sensor_probe,
	.remove = tof_sensor_remove,
	.id_table = tof_sensor_id,
};

static int __init tof_sensor_init(void)
{
	int rc = 0;

	pr_info("%s: Initialize i2c driver", __func__);
	rc = i2c_add_driver(&tof_sensor_i2c_driver);
	pr_info("%s: Added i2c driver rc = %d", __func__, rc);

	return rc;
}

static void __exit tof_sensor_exit(void)
{
	pr_info("Delete i2c driver");
	i2c_del_driver(&tof_sensor_i2c_driver);
}

module_init(tof_sensor_init);
module_exit(tof_sensor_exit);

MODULE_DESCRIPTION("Tof sensor driver");
MODULE_LICENSE("GPL v2");
+26 −0
Original line number Diff line number Diff line
/*
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License version 2, as
 * published by the Free Software Foundation; either version 2
 * of the License, or (at your option) any later version.
 */
/*
 * Copyright (C) 2016 Sony Mobile Communications Inc.
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License version 2, as
 * published by the Free Software Foundation.
 */

#ifndef TOF_SENSOR_H
#define TOF_SENSOR_H

/*
 *  tof sensor power control.
 */
enum tof_sensor_power_control {
	TOF_SENSOR_POWER_CONTROL_OFF,
	TOF_SENSOR_POWER_CONTROL_ON,
};

#endif /* TOF_SENSOR_H */
+1 −0
Original line number Diff line number Diff line
@@ -527,6 +527,7 @@ header-y += msm_dsps.h
header-y += msm-core-interface.h
header-y += msm_thermal_ioctl.h
header-y += android_pmem.h
header-y += tof_sensor.h
header-y += ipa_qmi_service_v01.h
header-y += rmnet_ipa_fd_ioctl.h
header-y += msm_ipa.h