linux/drivers/input/touchscreen/tsc2007_iio.c
H. Nikolaus Schaller f14434040c Input: tsc2007 - add iio interface to read external ADC input and temperature
The tsc2007 chip not only has a resistive touch screen controller but
also an external AUX adc imput which can be used for an ambient
light sensor, battery voltage monitoring or any general purpose.

Additionally it can measure the chip temperature.

This extension provides an iio interface for these adc channels.

Since it is not wasting much resources and is very straightforward,
we simply provide all other adc channels as optional iio interfaces
as weel. This can be used for debugging or special applications.

This patch also splits the tsc2007 driver in several source files:
tsc2007.h -- constants, structs and stubs
tsc2007_core.c -- functional parts of the original driver
tsc2007_iio.c -- the optional iio stuff

Makefile magic allows to conditionally link the iio stuff
if CONFIG_IIO=y or =m in a way that it works with
CONFIG_TOUCHSCREEN_TSC2007=m.

Signed-off-by: H. Nikolaus Schaller <hns@goldelico.com>
Reviewed-by: Jonathan Cameron <jic23@kernel.org>
Signed-off-by: Dmitry Torokhov <dmitry.torokhov@gmail.com>
2017-02-17 13:21:55 -08:00

141 lines
3.4 KiB
C

/*
* Copyright (c) 2016 Golden Delicious Comp. GmbH&Co. KG
* Nikolaus Schaller <hns@goldelico.com>
*
* 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/i2c.h>
#include <linux/iio/iio.h>
#include "tsc2007.h"
struct tsc2007_iio {
struct tsc2007 *ts;
};
#define TSC2007_CHAN_IIO(_chan, _name, _type, _chan_info) \
{ \
.datasheet_name = _name, \
.type = _type, \
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \
BIT(_chan_info), \
.indexed = 1, \
.channel = _chan, \
}
static const struct iio_chan_spec tsc2007_iio_channel[] = {
TSC2007_CHAN_IIO(0, "x", IIO_VOLTAGE, IIO_CHAN_INFO_RAW),
TSC2007_CHAN_IIO(1, "y", IIO_VOLTAGE, IIO_CHAN_INFO_RAW),
TSC2007_CHAN_IIO(2, "z1", IIO_VOLTAGE, IIO_CHAN_INFO_RAW),
TSC2007_CHAN_IIO(3, "z2", IIO_VOLTAGE, IIO_CHAN_INFO_RAW),
TSC2007_CHAN_IIO(4, "adc", IIO_VOLTAGE, IIO_CHAN_INFO_RAW),
TSC2007_CHAN_IIO(5, "rt", IIO_VOLTAGE, IIO_CHAN_INFO_RAW), /* Ohms? */
TSC2007_CHAN_IIO(6, "pen", IIO_PRESSURE, IIO_CHAN_INFO_RAW),
TSC2007_CHAN_IIO(7, "temp0", IIO_TEMP, IIO_CHAN_INFO_RAW),
TSC2007_CHAN_IIO(8, "temp1", IIO_TEMP, IIO_CHAN_INFO_RAW),
};
static int tsc2007_read_raw(struct iio_dev *indio_dev,
struct iio_chan_spec const *chan,
int *val, int *val2, long mask)
{
struct tsc2007_iio *iio = iio_priv(indio_dev);
struct tsc2007 *tsc = iio->ts;
int adc_chan = chan->channel;
int ret = 0;
if (adc_chan >= ARRAY_SIZE(tsc2007_iio_channel))
return -EINVAL;
if (mask != IIO_CHAN_INFO_RAW)
return -EINVAL;
mutex_lock(&tsc->mlock);
switch (chan->channel) {
case 0:
*val = tsc2007_xfer(tsc, READ_X);
break;
case 1:
*val = tsc2007_xfer(tsc, READ_Y);
break;
case 2:
*val = tsc2007_xfer(tsc, READ_Z1);
break;
case 3:
*val = tsc2007_xfer(tsc, READ_Z2);
break;
case 4:
*val = tsc2007_xfer(tsc, (ADC_ON_12BIT | TSC2007_MEASURE_AUX));
break;
case 5: {
struct ts_event tc;
tc.x = tsc2007_xfer(tsc, READ_X);
tc.z1 = tsc2007_xfer(tsc, READ_Z1);
tc.z2 = tsc2007_xfer(tsc, READ_Z2);
*val = tsc2007_calculate_pressure(tsc, &tc);
break;
}
case 6:
*val = tsc2007_is_pen_down(tsc);
break;
case 7:
*val = tsc2007_xfer(tsc,
(ADC_ON_12BIT | TSC2007_MEASURE_TEMP0));
break;
case 8:
*val = tsc2007_xfer(tsc,
(ADC_ON_12BIT | TSC2007_MEASURE_TEMP1));
break;
}
/* Prepare for next touch reading - power down ADC, enable PENIRQ */
tsc2007_xfer(tsc, PWRDOWN);
mutex_unlock(&tsc->mlock);
ret = IIO_VAL_INT;
return ret;
}
static const struct iio_info tsc2007_iio_info = {
.read_raw = tsc2007_read_raw,
.driver_module = THIS_MODULE,
};
int tsc2007_iio_configure(struct tsc2007 *ts)
{
struct iio_dev *indio_dev;
struct tsc2007_iio *iio;
int error;
indio_dev = devm_iio_device_alloc(&ts->client->dev, sizeof(*iio));
if (!indio_dev) {
dev_err(&ts->client->dev, "iio_device_alloc failed\n");
return -ENOMEM;
}
iio = iio_priv(indio_dev);
iio->ts = ts;
indio_dev->name = "tsc2007";
indio_dev->dev.parent = &ts->client->dev;
indio_dev->info = &tsc2007_iio_info;
indio_dev->modes = INDIO_DIRECT_MODE;
indio_dev->channels = tsc2007_iio_channel;
indio_dev->num_channels = ARRAY_SIZE(tsc2007_iio_channel);
error = devm_iio_device_register(&ts->client->dev, indio_dev);
if (error) {
dev_err(&ts->client->dev,
"iio_device_register() failed: %d\n", error);
return error;
}
return 0;
}