diff mbox series

iio: light: apds9960: Add proximity and gesture offset calibration

Message ID 20240702115648.160511-1-abhashkumarjha123@gmail.com (mailing list archive)
State New
Headers show
Series iio: light: apds9960: Add proximity and gesture offset calibration | expand

Commit Message

Abhash Jha July 2, 2024, 11:56 a.m. UTC
Proximity and gesture offset registers perform offset correction to improve
cross-talk performance. Add support for reading from and writing to
proximity and gesture offset registers. Create sysfs attributes for
them via iio to expose them to userspace.

Signed-off-by: Abhash Jha <abhashkumarjha123@gmail.com>
---
 drivers/iio/light/apds9960.c | 245 +++++++++++++++++++++++++++++++++++
 1 file changed, 245 insertions(+)
diff mbox series

Patch

diff --git a/drivers/iio/light/apds9960.c b/drivers/iio/light/apds9960.c
index 1065a340b..bccf9a8c7 100644
--- a/drivers/iio/light/apds9960.c
+++ b/drivers/iio/light/apds9960.c
@@ -101,6 +101,10 @@ 
 #define APDS9960_MAX_ALS_THRES_VAL	0xffff
 #define APDS9960_MAX_INT_TIME_IN_US	1000000
 
+/* MIN and MAX offset from pg: 26 of the datasheet */
+#define MIN_OFFSET -127
+#define MAX_OFFSET 127
+
 enum apds9960_als_channel_idx {
 	IDX_ALS_CLEAR, IDX_ALS_RED, IDX_ALS_GREEN, IDX_ALS_BLUE,
 };
@@ -316,6 +320,234 @@  static const struct iio_chan_spec apds9960_channels[] = {
 	APDS9960_INTENSITY_CHANNEL(BLUE),
 };
 
+static ssize_t apds9960_proximity_offset_ur_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
+{
+	struct iio_dev *indio_dev = dev_to_iio_dev(dev);
+	struct apds9960_data *data = iio_priv(indio_dev);
+	int offset;
+	int ret;
+
+	if (kstrtoint(buf, 10, &offset))
+		return -EINVAL;
+
+	if (offset < MIN_OFFSET || offset > MAX_OFFSET)
+		return -EINVAL;
+
+	ret = regmap_write(data->regmap, APDS9960_REG_POFFSET_UR, offset);
+	if (ret < 0) {
+		dev_err(&data->client->dev, "proximity offset reg write failed\n");
+		return ret;
+	}
+
+	return count;
+}
+
+static ssize_t apds9960_proximity_offset_dl_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
+{
+	struct iio_dev *indio_dev = dev_to_iio_dev(dev);
+	struct apds9960_data *data = iio_priv(indio_dev);
+	int offset;
+	int ret;
+
+	if (kstrtoint(buf, 10, &offset))
+		return -EINVAL;
+
+	if (offset < MIN_OFFSET || offset > MAX_OFFSET)
+		return -EINVAL;
+
+	ret = regmap_write(data->regmap, APDS9960_REG_POFFSET_DL, offset);
+	if (ret < 0) {
+		dev_err(&data->client->dev, "proximity offset reg write failed\n");
+		return ret;
+	}
+
+	return count;
+}
+
+static ssize_t apds9960_proximity_offset_ur_show(struct device *dev, struct device_attribute *attr, char *buf)
+{
+	struct iio_dev *indio_dev = dev_to_iio_dev(dev);
+	struct apds9960_data *data = iio_priv(indio_dev);
+	int offset;
+	int ret;
+
+	ret = regmap_read(data->regmap, APDS9960_REG_POFFSET_UR, &offset);
+	if (ret < 0) {
+		dev_err(&data->client->dev, "proximity offset reg read failed\n");
+		return ret;
+	}
+
+	return scnprintf(buf, PAGE_SIZE, "%d\n", offset);
+}
+
+static ssize_t apds9960_proximity_offset_dl_show(struct device *dev, struct device_attribute *attr, char *buf)
+{
+	struct iio_dev *indio_dev = dev_to_iio_dev(dev);
+	struct apds9960_data *data = iio_priv(indio_dev);
+	int offset;
+	int ret;
+
+	ret = regmap_read(data->regmap, APDS9960_REG_POFFSET_DL, &offset);
+	if (ret < 0) {
+		dev_err(&data->client->dev, "proximity offset reg read failed\n");
+		return ret;
+	}
+
+	return scnprintf(buf, PAGE_SIZE, "%d\n", offset);
+}
+
+static ssize_t apds9960_gesture_offset_u_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
+{
+	struct iio_dev *indio_dev = dev_to_iio_dev(dev);
+	struct apds9960_data *data = iio_priv(indio_dev);
+	int offset;
+	int ret;
+
+	if (kstrtoint(buf, 10, &offset))
+		return -EINVAL;
+
+	if (offset < MIN_OFFSET || offset > MAX_OFFSET)
+		return -EINVAL;
+
+	ret = regmap_write(data->regmap, APDS9960_REG_GOFFSET_U, offset);
+	if (ret < 0) {
+		dev_err(&data->client->dev, "gesture offset reg write failed\n");
+		return ret;
+	}
+
+	return count;
+}
+
+static ssize_t apds9960_gesture_offset_d_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
+{
+	struct iio_dev *indio_dev = dev_to_iio_dev(dev);
+	struct apds9960_data *data = iio_priv(indio_dev);
+	int offset;
+	int ret;
+
+	if (kstrtoint(buf, 10, &offset))
+		return -EINVAL;
+
+	if (offset < MIN_OFFSET || offset > MAX_OFFSET)
+		return -EINVAL;
+
+	ret = regmap_write(data->regmap, APDS9960_REG_GOFFSET_D, offset);
+	if (ret < 0) {
+		dev_err(&data->client->dev, "gesture offset reg write failed\n");
+		return ret;
+	}
+
+	return count;
+}
+
+static ssize_t apds9960_gesture_offset_l_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
+{
+	struct iio_dev *indio_dev = dev_to_iio_dev(dev);
+	struct apds9960_data *data = iio_priv(indio_dev);
+	int offset;
+	int ret;
+
+	if (kstrtoint(buf, 10, &offset))
+		return -EINVAL;
+
+	if (offset < MIN_OFFSET || offset > MAX_OFFSET)
+		return -EINVAL;
+
+	ret = regmap_write(data->regmap, APDS9960_REG_GOFFSET_L, offset);
+	if (ret < 0) {
+		dev_err(&data->client->dev, "gesture offset reg write failed\n");
+		return ret;
+	}
+
+	return count;
+}
+
+static ssize_t apds9960_gesture_offset_r_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
+{
+	struct iio_dev *indio_dev = dev_to_iio_dev(dev);
+	struct apds9960_data *data = iio_priv(indio_dev);
+	int offset;
+	int ret;
+
+	if (kstrtoint(buf, 10, &offset))
+		return -EINVAL;
+
+	if (offset < MIN_OFFSET || offset > MAX_OFFSET)
+		return -EINVAL;
+
+	ret = regmap_write(data->regmap, APDS9960_REG_GOFFSET_R, offset);
+	if (ret < 0) {
+		dev_err(&data->client->dev, "gesture offset reg write failed\n");
+		return ret;
+	}
+
+	return count;
+}
+
+static ssize_t apds9960_gesture_offset_u_show(struct device *dev, struct device_attribute *attr, char *buf)
+{
+	struct iio_dev *indio_dev = dev_to_iio_dev(dev);
+	struct apds9960_data *data = iio_priv(indio_dev);
+	int offset;
+	int ret;
+
+	ret = regmap_read(data->regmap, APDS9960_REG_GOFFSET_U, &offset);
+	if (ret < 0) {
+		dev_err(&data->client->dev, "gesture offset reg read failed\n");
+		return ret;
+	}
+
+	return scnprintf(buf, PAGE_SIZE, "%d\n", offset);
+}
+
+static ssize_t apds9960_gesture_offset_d_show(struct device *dev, struct device_attribute *attr, char *buf)
+{
+	struct iio_dev *indio_dev = dev_to_iio_dev(dev);
+	struct apds9960_data *data = iio_priv(indio_dev);
+	int offset;
+	int ret;
+
+	ret = regmap_read(data->regmap, APDS9960_REG_GOFFSET_D, &offset);
+	if (ret < 0) {
+		dev_err(&data->client->dev, "gesture offset reg read failed\n");
+		return ret;
+	}
+
+	return scnprintf(buf, PAGE_SIZE, "%d\n", offset);
+}
+
+static ssize_t apds9960_gesture_offset_l_show(struct device *dev, struct device_attribute *attr, char *buf)
+{
+	struct iio_dev *indio_dev = dev_to_iio_dev(dev);
+	struct apds9960_data *data = iio_priv(indio_dev);
+	int offset;
+	int ret;
+
+	ret = regmap_read(data->regmap, APDS9960_REG_GOFFSET_L, &offset);
+	if (ret < 0) {
+		dev_err(&data->client->dev, "gesture offset reg read failed\n");
+		return ret;
+	}
+
+	return scnprintf(buf, PAGE_SIZE, "%d\n", offset);
+}
+
+static ssize_t apds9960_gesture_offset_r_show(struct device *dev, struct device_attribute *attr, char *buf)
+{
+	struct iio_dev *indio_dev = dev_to_iio_dev(dev);
+	struct apds9960_data *data = iio_priv(indio_dev);
+	int offset;
+	int ret;
+
+	ret = regmap_read(data->regmap, APDS9960_REG_GOFFSET_R, &offset);
+	if (ret < 0) {
+		dev_err(&data->client->dev, "gesture offset reg read failed\n");
+		return ret;
+	}
+
+	return scnprintf(buf, PAGE_SIZE, "%d\n", offset);
+}
+
 /* integration time in us */
 static const int apds9960_int_time[][2] = {
 	{ 28000, 246},
@@ -332,10 +564,23 @@  static IIO_CONST_ATTR(proximity_scale_available, "1 2 4 8");
 static IIO_CONST_ATTR(intensity_scale_available, "1 4 16 64");
 static IIO_CONST_ATTR_INT_TIME_AVAIL("0.028 0.1 0.2 0.7");
 
+static IIO_DEVICE_ATTR(proximity_offset_ur, S_IRUGO | S_IWUSR, apds9960_proximity_offset_ur_show, apds9960_proximity_offset_ur_store, 0);
+static IIO_DEVICE_ATTR(proximity_offset_dl, S_IRUGO | S_IWUSR, apds9960_proximity_offset_dl_show, apds9960_proximity_offset_dl_store, 0);
+static IIO_DEVICE_ATTR(gesture_offset_u, S_IRUGO | S_IWUSR, apds9960_gesture_offset_u_show, apds9960_gesture_offset_u_store, 0);
+static IIO_DEVICE_ATTR(gesture_offset_d, S_IRUGO | S_IWUSR, apds9960_gesture_offset_d_show, apds9960_gesture_offset_d_store, 0);
+static IIO_DEVICE_ATTR(gesture_offset_l, S_IRUGO | S_IWUSR, apds9960_gesture_offset_l_show, apds9960_gesture_offset_l_store, 0);
+static IIO_DEVICE_ATTR(gesture_offset_r, S_IRUGO | S_IWUSR, apds9960_gesture_offset_r_show, apds9960_gesture_offset_r_store, 0);
+
 static struct attribute *apds9960_attributes[] = {
 	&iio_const_attr_proximity_scale_available.dev_attr.attr,
 	&iio_const_attr_intensity_scale_available.dev_attr.attr,
 	&iio_const_attr_integration_time_available.dev_attr.attr,
+	&iio_dev_attr_proximity_offset_ur.dev_attr.attr,
+	&iio_dev_attr_proximity_offset_dl.dev_attr.attr,
+	&iio_dev_attr_gesture_offset_u.dev_attr.attr,
+	&iio_dev_attr_gesture_offset_d.dev_attr.attr,
+	&iio_dev_attr_gesture_offset_l.dev_attr.attr,
+	&iio_dev_attr_gesture_offset_r.dev_attr.attr,
 	NULL,
 };