|
@@ -32,6 +32,8 @@
|
|
|
#include <linux/stmp_device.h>
|
|
|
#include <linux/bitops.h>
|
|
|
#include <linux/completion.h>
|
|
|
+#include <linux/delay.h>
|
|
|
+#include <linux/input.h>
|
|
|
|
|
|
#include <mach/mxs.h>
|
|
|
#include <mach/common.h>
|
|
@@ -59,6 +61,21 @@
|
|
|
#define LRADC_DELAY_TIMER_PER 200
|
|
|
#define LRADC_DELAY_TIMER_LOOP 5
|
|
|
|
|
|
+/*
|
|
|
+ * Once the pen touches the touchscreen, the touchscreen switches from
|
|
|
+ * IRQ-driven mode to polling mode to prevent interrupt storm. The polling
|
|
|
+ * is realized by worker thread, which is called every 20 or so milliseconds.
|
|
|
+ * This gives the touchscreen enough fluence and does not strain the system
|
|
|
+ * too much.
|
|
|
+ */
|
|
|
+#define LRADC_TS_SAMPLE_DELAY_MS 5
|
|
|
+
|
|
|
+/*
|
|
|
+ * The LRADC reads the following amount of samples from each touchscreen
|
|
|
+ * channel and the driver then computes avarage of these.
|
|
|
+ */
|
|
|
+#define LRADC_TS_SAMPLE_AMOUNT 4
|
|
|
+
|
|
|
static const char * const mxs_lradc_irq_name[] = {
|
|
|
"mxs-lradc-touchscreen",
|
|
|
"mxs-lradc-thresh0",
|
|
@@ -75,6 +92,12 @@ static const char * const mxs_lradc_irq_name[] = {
|
|
|
"mxs-lradc-button1",
|
|
|
};
|
|
|
|
|
|
+enum mxs_lradc_ts {
|
|
|
+ MXS_LRADC_TOUCHSCREEN_NONE = 0,
|
|
|
+ MXS_LRADC_TOUCHSCREEN_4WIRE,
|
|
|
+ MXS_LRADC_TOUCHSCREEN_5WIRE,
|
|
|
+};
|
|
|
+
|
|
|
struct mxs_lradc {
|
|
|
struct device *dev;
|
|
|
void __iomem *base;
|
|
@@ -86,21 +109,69 @@ struct mxs_lradc {
|
|
|
struct mutex lock;
|
|
|
|
|
|
struct completion completion;
|
|
|
+
|
|
|
+ /*
|
|
|
+ * Touchscreen LRADC channels receives a private slot in the CTRL4
|
|
|
+ * register, the slot #7. Therefore only 7 slots instead of 8 in the
|
|
|
+ * CTRL4 register can be mapped to LRADC channels when using the
|
|
|
+ * touchscreen.
|
|
|
+ *
|
|
|
+ * Furthermore, certain LRADC channels are shared between touchscreen
|
|
|
+ * and/or touch-buttons and generic LRADC block. Therefore when using
|
|
|
+ * either of these, these channels are not available for the regular
|
|
|
+ * sampling. The shared channels are as follows:
|
|
|
+ *
|
|
|
+ * CH0 -- Touch button #0
|
|
|
+ * CH1 -- Touch button #1
|
|
|
+ * CH2 -- Touch screen XPUL
|
|
|
+ * CH3 -- Touch screen YPLL
|
|
|
+ * CH4 -- Touch screen XNUL
|
|
|
+ * CH5 -- Touch screen YNLR
|
|
|
+ * CH6 -- Touch screen WIPER (5-wire only)
|
|
|
+ *
|
|
|
+ * The bitfields below represents which parts of the LRADC block are
|
|
|
+ * switched into special mode of operation. These channels can not
|
|
|
+ * be sampled as regular LRADC channels. The driver will refuse any
|
|
|
+ * attempt to sample these channels.
|
|
|
+ */
|
|
|
+#define CHAN_MASK_TOUCHBUTTON (0x3 << 0)
|
|
|
+#define CHAN_MASK_TOUCHSCREEN_4WIRE (0xf << 2)
|
|
|
+#define CHAN_MASK_TOUCHSCREEN_5WIRE (0x1f << 2)
|
|
|
+ enum mxs_lradc_ts use_touchscreen;
|
|
|
+ bool stop_touchscreen;
|
|
|
+ bool use_touchbutton;
|
|
|
+
|
|
|
+ struct input_dev *ts_input;
|
|
|
+ struct work_struct ts_work;
|
|
|
};
|
|
|
|
|
|
#define LRADC_CTRL0 0x00
|
|
|
-#define LRADC_CTRL0_TOUCH_DETECT_ENABLE (1 << 23)
|
|
|
-#define LRADC_CTRL0_TOUCH_SCREEN_TYPE (1 << 22)
|
|
|
+#define LRADC_CTRL0_TOUCH_DETECT_ENABLE (1 << 23)
|
|
|
+#define LRADC_CTRL0_TOUCH_SCREEN_TYPE (1 << 22)
|
|
|
+#define LRADC_CTRL0_YNNSW /* YM */ (1 << 21)
|
|
|
+#define LRADC_CTRL0_YPNSW /* YP */ (1 << 20)
|
|
|
+#define LRADC_CTRL0_YPPSW /* YP */ (1 << 19)
|
|
|
+#define LRADC_CTRL0_XNNSW /* XM */ (1 << 18)
|
|
|
+#define LRADC_CTRL0_XNPSW /* XM */ (1 << 17)
|
|
|
+#define LRADC_CTRL0_XPPSW /* XP */ (1 << 16)
|
|
|
+#define LRADC_CTRL0_PLATE_MASK (0x3f << 16)
|
|
|
|
|
|
#define LRADC_CTRL1 0x10
|
|
|
-#define LRADC_CTRL1_LRADC_IRQ(n) (1 << (n))
|
|
|
-#define LRADC_CTRL1_LRADC_IRQ_MASK 0x1fff
|
|
|
+#define LRADC_CTRL1_TOUCH_DETECT_IRQ_EN (1 << 24)
|
|
|
#define LRADC_CTRL1_LRADC_IRQ_EN(n) (1 << ((n) + 16))
|
|
|
#define LRADC_CTRL1_LRADC_IRQ_EN_MASK (0x1fff << 16)
|
|
|
+#define LRADC_CTRL1_LRADC_IRQ_EN_OFFSET 16
|
|
|
+#define LRADC_CTRL1_TOUCH_DETECT_IRQ (1 << 8)
|
|
|
+#define LRADC_CTRL1_LRADC_IRQ(n) (1 << (n))
|
|
|
+#define LRADC_CTRL1_LRADC_IRQ_MASK 0x1fff
|
|
|
+#define LRADC_CTRL1_LRADC_IRQ_OFFSET 0
|
|
|
|
|
|
#define LRADC_CTRL2 0x20
|
|
|
#define LRADC_CTRL2_TEMPSENSE_PWD (1 << 15)
|
|
|
|
|
|
+#define LRADC_STATUS 0x40
|
|
|
+#define LRADC_STATUS_TOUCH_DETECT_RAW (1 << 0)
|
|
|
+
|
|
|
#define LRADC_CH(n) (0x50 + (0x10 * (n)))
|
|
|
#define LRADC_CH_ACCUMULATE (1 << 29)
|
|
|
#define LRADC_CH_NUM_SAMPLES_MASK (0x1f << 24)
|
|
@@ -132,6 +203,7 @@ static int mxs_lradc_read_raw(struct iio_dev *iio_dev,
|
|
|
{
|
|
|
struct mxs_lradc *lradc = iio_priv(iio_dev);
|
|
|
int ret;
|
|
|
+ unsigned long mask;
|
|
|
|
|
|
if (m != IIO_CHAN_INFO_RAW)
|
|
|
return -EINVAL;
|
|
@@ -140,6 +212,12 @@ static int mxs_lradc_read_raw(struct iio_dev *iio_dev,
|
|
|
if (chan->channel > LRADC_MAX_TOTAL_CHANS)
|
|
|
return -EINVAL;
|
|
|
|
|
|
+ /* Validate the channel if it doesn't intersect with reserved chans. */
|
|
|
+ bitmap_set(&mask, chan->channel, 1);
|
|
|
+ ret = iio_validate_scan_mask_onehot(iio_dev, &mask);
|
|
|
+ if (ret)
|
|
|
+ return -EINVAL;
|
|
|
+
|
|
|
/*
|
|
|
* See if there is no buffered operation in progess. If there is, simply
|
|
|
* bail out. This can be improved to support both buffered and raw IO at
|
|
@@ -161,7 +239,11 @@ static int mxs_lradc_read_raw(struct iio_dev *iio_dev,
|
|
|
lradc->base + LRADC_CTRL1 + STMP_OFFSET_REG_CLR);
|
|
|
writel(0xff, lradc->base + LRADC_CTRL0 + STMP_OFFSET_REG_CLR);
|
|
|
|
|
|
- writel(chan->channel, lradc->base + LRADC_CTRL4);
|
|
|
+ /* Clean the slot's previous content, then set new one. */
|
|
|
+ writel(LRADC_CTRL4_LRADCSELECT_MASK(0),
|
|
|
+ lradc->base + LRADC_CTRL4 + STMP_OFFSET_REG_CLR);
|
|
|
+ writel(chan->channel, lradc->base + LRADC_CTRL4 + STMP_OFFSET_REG_SET);
|
|
|
+
|
|
|
writel(0, lradc->base + LRADC_CH(0));
|
|
|
|
|
|
/* Enable the IRQ and start sampling the channel. */
|
|
@@ -194,6 +276,269 @@ static const struct iio_info mxs_lradc_iio_info = {
|
|
|
.read_raw = mxs_lradc_read_raw,
|
|
|
};
|
|
|
|
|
|
+/*
|
|
|
+ * Touchscreen handling
|
|
|
+ */
|
|
|
+enum lradc_ts_plate {
|
|
|
+ LRADC_SAMPLE_X,
|
|
|
+ LRADC_SAMPLE_Y,
|
|
|
+ LRADC_SAMPLE_PRESSURE,
|
|
|
+};
|
|
|
+
|
|
|
+static int mxs_lradc_ts_touched(struct mxs_lradc *lradc)
|
|
|
+{
|
|
|
+ uint32_t reg;
|
|
|
+
|
|
|
+ /* Enable touch detection. */
|
|
|
+ writel(LRADC_CTRL0_PLATE_MASK,
|
|
|
+ lradc->base + LRADC_CTRL0 + STMP_OFFSET_REG_CLR);
|
|
|
+ writel(LRADC_CTRL0_TOUCH_DETECT_ENABLE,
|
|
|
+ lradc->base + LRADC_CTRL0 + STMP_OFFSET_REG_SET);
|
|
|
+
|
|
|
+ msleep(LRADC_TS_SAMPLE_DELAY_MS);
|
|
|
+
|
|
|
+ reg = readl(lradc->base + LRADC_STATUS);
|
|
|
+
|
|
|
+ return reg & LRADC_STATUS_TOUCH_DETECT_RAW;
|
|
|
+}
|
|
|
+
|
|
|
+static int32_t mxs_lradc_ts_sample(struct mxs_lradc *lradc,
|
|
|
+ enum lradc_ts_plate plate, int change)
|
|
|
+{
|
|
|
+ unsigned long delay, jiff;
|
|
|
+ uint32_t reg, ctrl0 = 0, chan = 0;
|
|
|
+ /* The touchscreen always uses CTRL4 slot #7. */
|
|
|
+ const uint8_t slot = 7;
|
|
|
+ uint32_t val;
|
|
|
+
|
|
|
+ /*
|
|
|
+ * There are three correct configurations of the controller sampling
|
|
|
+ * the touchscreen, each of these configuration provides different
|
|
|
+ * information from the touchscreen.
|
|
|
+ *
|
|
|
+ * The following table describes the sampling configurations:
|
|
|
+ * +-------------+-------+-------+-------+
|
|
|
+ * | Wire \ Axis | X | Y | Z |
|
|
|
+ * +---------------------+-------+-------+
|
|
|
+ * | X+ (CH2) | HI | TS | TS |
|
|
|
+ * +-------------+-------+-------+-------+
|
|
|
+ * | X- (CH4) | LO | SH | HI |
|
|
|
+ * +-------------+-------+-------+-------+
|
|
|
+ * | Y+ (CH3) | SH | HI | HI |
|
|
|
+ * +-------------+-------+-------+-------+
|
|
|
+ * | Y- (CH5) | TS | LO | SH |
|
|
|
+ * +-------------+-------+-------+-------+
|
|
|
+ *
|
|
|
+ * HI ... strong '1' ; LO ... strong '0'
|
|
|
+ * SH ... sample here ; TS ... tri-state
|
|
|
+ *
|
|
|
+ * There are a few other ways of obtaining the Z coordinate
|
|
|
+ * (aka. pressure), but the one in the table seems to be the
|
|
|
+ * most reliable one.
|
|
|
+ */
|
|
|
+ switch (plate) {
|
|
|
+ case LRADC_SAMPLE_X:
|
|
|
+ ctrl0 = LRADC_CTRL0_XPPSW | LRADC_CTRL0_XNNSW;
|
|
|
+ chan = 3;
|
|
|
+ break;
|
|
|
+ case LRADC_SAMPLE_Y:
|
|
|
+ ctrl0 = LRADC_CTRL0_YPPSW | LRADC_CTRL0_YNNSW;
|
|
|
+ chan = 4;
|
|
|
+ break;
|
|
|
+ case LRADC_SAMPLE_PRESSURE:
|
|
|
+ ctrl0 = LRADC_CTRL0_YPPSW | LRADC_CTRL0_XNNSW;
|
|
|
+ chan = 5;
|
|
|
+ break;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (change) {
|
|
|
+ writel(LRADC_CTRL0_PLATE_MASK,
|
|
|
+ lradc->base + LRADC_CTRL0 + STMP_OFFSET_REG_CLR);
|
|
|
+ writel(ctrl0, lradc->base + LRADC_CTRL0 + STMP_OFFSET_REG_SET);
|
|
|
+
|
|
|
+ writel(LRADC_CTRL4_LRADCSELECT_MASK(slot),
|
|
|
+ lradc->base + LRADC_CTRL4 + STMP_OFFSET_REG_CLR);
|
|
|
+ writel(chan << LRADC_CTRL4_LRADCSELECT_OFFSET(slot),
|
|
|
+ lradc->base + LRADC_CTRL4 + STMP_OFFSET_REG_SET);
|
|
|
+ }
|
|
|
+
|
|
|
+ writel(0xffffffff, lradc->base + LRADC_CH(slot) + STMP_OFFSET_REG_CLR);
|
|
|
+ writel(1 << slot, lradc->base + LRADC_CTRL0 + STMP_OFFSET_REG_SET);
|
|
|
+
|
|
|
+ delay = jiffies + msecs_to_jiffies(LRADC_TS_SAMPLE_DELAY_MS);
|
|
|
+ do {
|
|
|
+ jiff = jiffies;
|
|
|
+ reg = readl_relaxed(lradc->base + LRADC_CTRL1);
|
|
|
+ if (reg & LRADC_CTRL1_LRADC_IRQ(slot))
|
|
|
+ break;
|
|
|
+ } while (time_before(jiff, delay));
|
|
|
+
|
|
|
+ writel(LRADC_CTRL1_LRADC_IRQ(slot),
|
|
|
+ lradc->base + LRADC_CTRL1 + STMP_OFFSET_REG_CLR);
|
|
|
+
|
|
|
+ if (time_after_eq(jiff, delay))
|
|
|
+ return -ETIMEDOUT;
|
|
|
+
|
|
|
+ val = readl(lradc->base + LRADC_CH(slot));
|
|
|
+ val &= LRADC_CH_VALUE_MASK;
|
|
|
+
|
|
|
+ return val;
|
|
|
+}
|
|
|
+
|
|
|
+static int32_t mxs_lradc_ts_sample_filter(struct mxs_lradc *lradc,
|
|
|
+ enum lradc_ts_plate plate)
|
|
|
+{
|
|
|
+ int32_t val, tot = 0;
|
|
|
+ int i;
|
|
|
+
|
|
|
+ val = mxs_lradc_ts_sample(lradc, plate, 1);
|
|
|
+
|
|
|
+ /* Delay a bit so the touchscreen is stable. */
|
|
|
+ mdelay(2);
|
|
|
+
|
|
|
+ for (i = 0; i < LRADC_TS_SAMPLE_AMOUNT; i++) {
|
|
|
+ val = mxs_lradc_ts_sample(lradc, plate, 0);
|
|
|
+ tot += val;
|
|
|
+ }
|
|
|
+
|
|
|
+ return tot / LRADC_TS_SAMPLE_AMOUNT;
|
|
|
+}
|
|
|
+
|
|
|
+static void mxs_lradc_ts_work(struct work_struct *ts_work)
|
|
|
+{
|
|
|
+ struct mxs_lradc *lradc = container_of(ts_work,
|
|
|
+ struct mxs_lradc, ts_work);
|
|
|
+ int val_x, val_y, val_p;
|
|
|
+ bool valid = false;
|
|
|
+
|
|
|
+ while (mxs_lradc_ts_touched(lradc)) {
|
|
|
+ /* Disable touch detector so we can sample the touchscreen. */
|
|
|
+ writel(LRADC_CTRL0_TOUCH_DETECT_ENABLE,
|
|
|
+ lradc->base + LRADC_CTRL0 + STMP_OFFSET_REG_CLR);
|
|
|
+
|
|
|
+ if (likely(valid)) {
|
|
|
+ input_report_abs(lradc->ts_input, ABS_X, val_x);
|
|
|
+ input_report_abs(lradc->ts_input, ABS_Y, val_y);
|
|
|
+ input_report_abs(lradc->ts_input, ABS_PRESSURE, val_p);
|
|
|
+ input_report_key(lradc->ts_input, BTN_TOUCH, 1);
|
|
|
+ input_sync(lradc->ts_input);
|
|
|
+ }
|
|
|
+
|
|
|
+ valid = false;
|
|
|
+
|
|
|
+ val_x = mxs_lradc_ts_sample_filter(lradc, LRADC_SAMPLE_X);
|
|
|
+ if (val_x < 0)
|
|
|
+ continue;
|
|
|
+ val_y = mxs_lradc_ts_sample_filter(lradc, LRADC_SAMPLE_Y);
|
|
|
+ if (val_y < 0)
|
|
|
+ continue;
|
|
|
+ val_p = mxs_lradc_ts_sample_filter(lradc, LRADC_SAMPLE_PRESSURE);
|
|
|
+ if (val_p < 0)
|
|
|
+ continue;
|
|
|
+
|
|
|
+ valid = true;
|
|
|
+ }
|
|
|
+
|
|
|
+ input_report_abs(lradc->ts_input, ABS_PRESSURE, 0);
|
|
|
+ input_report_key(lradc->ts_input, BTN_TOUCH, 0);
|
|
|
+ input_sync(lradc->ts_input);
|
|
|
+
|
|
|
+ /* Do not restart the TS IRQ if the driver is shutting down. */
|
|
|
+ if (lradc->stop_touchscreen)
|
|
|
+ return;
|
|
|
+
|
|
|
+ /* Restart the touchscreen interrupts. */
|
|
|
+ writel(LRADC_CTRL1_TOUCH_DETECT_IRQ,
|
|
|
+ lradc->base + LRADC_CTRL1 + STMP_OFFSET_REG_CLR);
|
|
|
+ writel(LRADC_CTRL1_TOUCH_DETECT_IRQ_EN,
|
|
|
+ lradc->base + LRADC_CTRL1 + STMP_OFFSET_REG_SET);
|
|
|
+}
|
|
|
+
|
|
|
+static int mxs_lradc_ts_open(struct input_dev *dev)
|
|
|
+{
|
|
|
+ struct mxs_lradc *lradc = input_get_drvdata(dev);
|
|
|
+
|
|
|
+ /* The touchscreen is starting. */
|
|
|
+ lradc->stop_touchscreen = false;
|
|
|
+
|
|
|
+ /* Enable the touch-detect circuitry. */
|
|
|
+ writel(LRADC_CTRL0_TOUCH_DETECT_ENABLE,
|
|
|
+ lradc->base + LRADC_CTRL0 + STMP_OFFSET_REG_SET);
|
|
|
+
|
|
|
+ /* Enable the touch-detect IRQ. */
|
|
|
+ writel(LRADC_CTRL1_TOUCH_DETECT_IRQ_EN,
|
|
|
+ lradc->base + LRADC_CTRL1 + STMP_OFFSET_REG_SET);
|
|
|
+
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
+static void mxs_lradc_ts_close(struct input_dev *dev)
|
|
|
+{
|
|
|
+ struct mxs_lradc *lradc = input_get_drvdata(dev);
|
|
|
+
|
|
|
+ /* Indicate the touchscreen is stopping. */
|
|
|
+ lradc->stop_touchscreen = true;
|
|
|
+ mb();
|
|
|
+
|
|
|
+ /* Wait until touchscreen thread finishes any possible remnants. */
|
|
|
+ cancel_work_sync(&lradc->ts_work);
|
|
|
+
|
|
|
+ /* Disable touchscreen touch-detect IRQ. */
|
|
|
+ writel(LRADC_CTRL1_TOUCH_DETECT_IRQ_EN,
|
|
|
+ lradc->base + LRADC_CTRL1 + STMP_OFFSET_REG_CLR);
|
|
|
+
|
|
|
+ /* Power-down touchscreen touch-detect circuitry. */
|
|
|
+ writel(LRADC_CTRL0_TOUCH_DETECT_ENABLE,
|
|
|
+ lradc->base + LRADC_CTRL0 + STMP_OFFSET_REG_CLR);
|
|
|
+}
|
|
|
+
|
|
|
+static int mxs_lradc_ts_register(struct mxs_lradc *lradc)
|
|
|
+{
|
|
|
+ struct input_dev *input;
|
|
|
+ struct device *dev = lradc->dev;
|
|
|
+ int ret;
|
|
|
+
|
|
|
+ if (!lradc->use_touchscreen)
|
|
|
+ return 0;
|
|
|
+
|
|
|
+ input = input_allocate_device();
|
|
|
+ if (!input) {
|
|
|
+ dev_err(dev, "Failed to allocate TS device!\n");
|
|
|
+ return -ENOMEM;
|
|
|
+ }
|
|
|
+
|
|
|
+ input->name = DRIVER_NAME;
|
|
|
+ input->id.bustype = BUS_HOST;
|
|
|
+ input->dev.parent = dev;
|
|
|
+ input->open = mxs_lradc_ts_open;
|
|
|
+ input->close = mxs_lradc_ts_close;
|
|
|
+
|
|
|
+ __set_bit(EV_ABS, input->evbit);
|
|
|
+ __set_bit(EV_KEY, input->evbit);
|
|
|
+ __set_bit(BTN_TOUCH, input->keybit);
|
|
|
+ input_set_abs_params(input, ABS_X, 0, LRADC_CH_VALUE_MASK, 0, 0);
|
|
|
+ input_set_abs_params(input, ABS_Y, 0, LRADC_CH_VALUE_MASK, 0, 0);
|
|
|
+ input_set_abs_params(input, ABS_PRESSURE, 0, LRADC_CH_VALUE_MASK, 0, 0);
|
|
|
+
|
|
|
+ lradc->ts_input = input;
|
|
|
+ input_set_drvdata(input, lradc);
|
|
|
+ ret = input_register_device(input);
|
|
|
+ if (ret)
|
|
|
+ input_free_device(lradc->ts_input);
|
|
|
+
|
|
|
+ return ret;
|
|
|
+}
|
|
|
+
|
|
|
+static void mxs_lradc_ts_unregister(struct mxs_lradc *lradc)
|
|
|
+{
|
|
|
+ if (!lradc->use_touchscreen)
|
|
|
+ return;
|
|
|
+
|
|
|
+ cancel_work_sync(&lradc->ts_work);
|
|
|
+
|
|
|
+ input_unregister_device(lradc->ts_input);
|
|
|
+}
|
|
|
+
|
|
|
/*
|
|
|
* IRQ Handling
|
|
|
*/
|
|
@@ -202,14 +547,24 @@ static irqreturn_t mxs_lradc_handle_irq(int irq, void *data)
|
|
|
struct iio_dev *iio = data;
|
|
|
struct mxs_lradc *lradc = iio_priv(iio);
|
|
|
unsigned long reg = readl(lradc->base + LRADC_CTRL1);
|
|
|
+ const uint32_t ts_irq_mask =
|
|
|
+ LRADC_CTRL1_TOUCH_DETECT_IRQ_EN |
|
|
|
+ LRADC_CTRL1_TOUCH_DETECT_IRQ;
|
|
|
|
|
|
if (!(reg & LRADC_CTRL1_LRADC_IRQ_MASK))
|
|
|
return IRQ_NONE;
|
|
|
|
|
|
/*
|
|
|
- * Touchscreen IRQ handling code shall probably have priority
|
|
|
- * and therefore shall be placed here.
|
|
|
+ * Touchscreen IRQ handling code has priority and therefore
|
|
|
+ * is placed here. In case touchscreen IRQ arrives, disable
|
|
|
+ * it ASAP
|
|
|
*/
|
|
|
+ if (reg & LRADC_CTRL1_TOUCH_DETECT_IRQ) {
|
|
|
+ writel(ts_irq_mask,
|
|
|
+ lradc->base + LRADC_CTRL1 + STMP_OFFSET_REG_CLR);
|
|
|
+ if (!lradc->stop_touchscreen)
|
|
|
+ schedule_work(&lradc->ts_work);
|
|
|
+ }
|
|
|
|
|
|
if (iio_buffer_enabled(iio))
|
|
|
iio_trigger_poll(iio->trig, iio_get_time_ns());
|
|
@@ -305,8 +660,10 @@ static int mxs_lradc_buffer_preenable(struct iio_dev *iio)
|
|
|
{
|
|
|
struct mxs_lradc *lradc = iio_priv(iio);
|
|
|
struct iio_buffer *buffer = iio->buffer;
|
|
|
- int ret = 0, chan, ofs = 0, enable = 0;
|
|
|
- uint32_t ctrl4 = 0;
|
|
|
+ int ret = 0, chan, ofs = 0;
|
|
|
+ unsigned long enable = 0;
|
|
|
+ uint32_t ctrl4_set = 0;
|
|
|
+ uint32_t ctrl4_clr = 0;
|
|
|
uint32_t ctrl1_irq = 0;
|
|
|
const uint32_t chan_value = LRADC_CH_ACCUMULATE |
|
|
|
((LRADC_DELAY_TIMER_LOOP - 1) << LRADC_CH_NUM_SAMPLES_OFFSET);
|
|
@@ -338,17 +695,20 @@ static int mxs_lradc_buffer_preenable(struct iio_dev *iio)
|
|
|
writel(0xff, lradc->base + LRADC_CTRL0 + STMP_OFFSET_REG_CLR);
|
|
|
|
|
|
for_each_set_bit(chan, buffer->scan_mask, LRADC_MAX_TOTAL_CHANS) {
|
|
|
- ctrl4 |= chan << LRADC_CTRL4_LRADCSELECT_OFFSET(ofs);
|
|
|
+ ctrl4_set |= chan << LRADC_CTRL4_LRADCSELECT_OFFSET(ofs);
|
|
|
+ ctrl4_clr |= LRADC_CTRL4_LRADCSELECT_MASK(ofs);
|
|
|
ctrl1_irq |= LRADC_CTRL1_LRADC_IRQ_EN(ofs);
|
|
|
writel(chan_value, lradc->base + LRADC_CH(ofs));
|
|
|
- enable |= 1 << ofs;
|
|
|
+ bitmap_set(&enable, ofs, 1);
|
|
|
ofs++;
|
|
|
}
|
|
|
|
|
|
writel(LRADC_DELAY_TRIGGER_LRADCS_MASK | LRADC_DELAY_KICK,
|
|
|
lradc->base + LRADC_DELAY(0) + STMP_OFFSET_REG_CLR);
|
|
|
|
|
|
- writel(ctrl4, lradc->base + LRADC_CTRL4);
|
|
|
+ writel(ctrl4_clr, lradc->base + LRADC_CTRL4 + STMP_OFFSET_REG_CLR);
|
|
|
+ writel(ctrl4_set, lradc->base + LRADC_CTRL4 + STMP_OFFSET_REG_SET);
|
|
|
+
|
|
|
writel(ctrl1_irq, lradc->base + LRADC_CTRL1 + STMP_OFFSET_REG_SET);
|
|
|
|
|
|
writel(enable << LRADC_DELAY_TRIGGER_LRADCS_OFFSET,
|
|
@@ -383,9 +743,33 @@ static int mxs_lradc_buffer_postdisable(struct iio_dev *iio)
|
|
|
static bool mxs_lradc_validate_scan_mask(struct iio_dev *iio,
|
|
|
const unsigned long *mask)
|
|
|
{
|
|
|
- const int mw = bitmap_weight(mask, iio->masklength);
|
|
|
-
|
|
|
- return mw <= LRADC_MAX_MAPPED_CHANS;
|
|
|
+ struct mxs_lradc *lradc = iio_priv(iio);
|
|
|
+ const int len = iio->masklength;
|
|
|
+ const int map_chans = bitmap_weight(mask, len);
|
|
|
+ int rsvd_chans = 0;
|
|
|
+ unsigned long rsvd_mask = 0;
|
|
|
+
|
|
|
+ if (lradc->use_touchbutton)
|
|
|
+ rsvd_mask |= CHAN_MASK_TOUCHBUTTON;
|
|
|
+ if (lradc->use_touchscreen == MXS_LRADC_TOUCHSCREEN_4WIRE)
|
|
|
+ rsvd_mask |= CHAN_MASK_TOUCHSCREEN_4WIRE;
|
|
|
+ if (lradc->use_touchscreen == MXS_LRADC_TOUCHSCREEN_5WIRE)
|
|
|
+ rsvd_mask |= CHAN_MASK_TOUCHSCREEN_5WIRE;
|
|
|
+
|
|
|
+ if (lradc->use_touchbutton)
|
|
|
+ rsvd_chans++;
|
|
|
+ if (lradc->use_touchscreen)
|
|
|
+ rsvd_chans++;
|
|
|
+
|
|
|
+ /* Test for attempts to map channels with special mode of operation. */
|
|
|
+ if (bitmap_intersects(mask, &rsvd_mask, len))
|
|
|
+ return false;
|
|
|
+
|
|
|
+ /* Test for attempts to map more channels then available slots. */
|
|
|
+ if (map_chans + rsvd_chans > LRADC_MAX_MAPPED_CHANS)
|
|
|
+ return false;
|
|
|
+
|
|
|
+ return true;
|
|
|
}
|
|
|
|
|
|
static const struct iio_buffer_setup_ops mxs_lradc_buffer_ops = {
|
|
@@ -434,15 +818,29 @@ static const struct iio_chan_spec mxs_lradc_chan_spec[] = {
|
|
|
|
|
|
static void mxs_lradc_hw_init(struct mxs_lradc *lradc)
|
|
|
{
|
|
|
- int i;
|
|
|
- const uint32_t cfg =
|
|
|
+ /* The ADC always uses DELAY CHANNEL 0. */
|
|
|
+ const uint32_t adc_cfg =
|
|
|
+ (1 << (LRADC_DELAY_TRIGGER_DELAYS_OFFSET + 0)) |
|
|
|
(LRADC_DELAY_TIMER_PER << LRADC_DELAY_DELAY_OFFSET);
|
|
|
|
|
|
stmp_reset_block(lradc->base);
|
|
|
|
|
|
- for (i = 0; i < LRADC_MAX_DELAY_CHANS; i++)
|
|
|
- writel(cfg | (1 << (LRADC_DELAY_TRIGGER_DELAYS_OFFSET + i)),
|
|
|
- lradc->base + LRADC_DELAY(i));
|
|
|
+ /* Configure DELAY CHANNEL 0 for generic ADC sampling. */
|
|
|
+ writel(adc_cfg, lradc->base + LRADC_DELAY(0));
|
|
|
+
|
|
|
+ /* Disable remaining DELAY CHANNELs */
|
|
|
+ writel(0, lradc->base + LRADC_DELAY(1));
|
|
|
+ writel(0, lradc->base + LRADC_DELAY(2));
|
|
|
+ writel(0, lradc->base + LRADC_DELAY(3));
|
|
|
+
|
|
|
+ /* Configure the touchscreen type */
|
|
|
+ writel(LRADC_CTRL0_TOUCH_SCREEN_TYPE,
|
|
|
+ lradc->base + LRADC_CTRL0 + STMP_OFFSET_REG_CLR);
|
|
|
+
|
|
|
+ if (lradc->use_touchscreen == MXS_LRADC_TOUCHSCREEN_5WIRE) {
|
|
|
+ writel(LRADC_CTRL0_TOUCH_SCREEN_TYPE,
|
|
|
+ lradc->base + LRADC_CTRL0 + STMP_OFFSET_REG_SET);
|
|
|
+ }
|
|
|
|
|
|
/* Start internal temperature sensing. */
|
|
|
writel(0, lradc->base + LRADC_CTRL2);
|
|
@@ -462,9 +860,11 @@ static void mxs_lradc_hw_stop(struct mxs_lradc *lradc)
|
|
|
static int mxs_lradc_probe(struct platform_device *pdev)
|
|
|
{
|
|
|
struct device *dev = &pdev->dev;
|
|
|
+ struct device_node *node = dev->of_node;
|
|
|
struct mxs_lradc *lradc;
|
|
|
struct iio_dev *iio;
|
|
|
struct resource *iores;
|
|
|
+ uint32_t ts_wires = 0;
|
|
|
int ret = 0;
|
|
|
int i;
|
|
|
|
|
@@ -486,6 +886,21 @@ static int mxs_lradc_probe(struct platform_device *pdev)
|
|
|
goto err_addr;
|
|
|
}
|
|
|
|
|
|
+ INIT_WORK(&lradc->ts_work, mxs_lradc_ts_work);
|
|
|
+
|
|
|
+ /* Check if touchscreen is enabled in DT. */
|
|
|
+ ret = of_property_read_u32(node, "fsl,lradc-touchscreen-wires",
|
|
|
+ &ts_wires);
|
|
|
+ if (ret)
|
|
|
+ dev_info(dev, "Touchscreen not enabled.\n");
|
|
|
+ else if (ts_wires == 4)
|
|
|
+ lradc->use_touchscreen = MXS_LRADC_TOUCHSCREEN_4WIRE;
|
|
|
+ else if (ts_wires == 5)
|
|
|
+ lradc->use_touchscreen = MXS_LRADC_TOUCHSCREEN_5WIRE;
|
|
|
+ else
|
|
|
+ dev_warn(dev, "Unsupported number of touchscreen wires (%d)\n",
|
|
|
+ ts_wires);
|
|
|
+
|
|
|
/* Grab all IRQ sources */
|
|
|
for (i = 0; i < 13; i++) {
|
|
|
lradc->irq[i] = platform_get_irq(pdev, i);
|
|
@@ -523,11 +938,16 @@ static int mxs_lradc_probe(struct platform_device *pdev)
|
|
|
if (ret)
|
|
|
goto err_trig;
|
|
|
|
|
|
+ /* Register the touchscreen input device. */
|
|
|
+ ret = mxs_lradc_ts_register(lradc);
|
|
|
+ if (ret)
|
|
|
+ goto err_dev;
|
|
|
+
|
|
|
/* Register IIO device. */
|
|
|
ret = iio_device_register(iio);
|
|
|
if (ret) {
|
|
|
dev_err(dev, "Failed to register IIO device\n");
|
|
|
- goto err_dev;
|
|
|
+ goto err_ts;
|
|
|
}
|
|
|
|
|
|
/* Configure the hardware. */
|
|
@@ -535,6 +955,8 @@ static int mxs_lradc_probe(struct platform_device *pdev)
|
|
|
|
|
|
return 0;
|
|
|
|
|
|
+err_ts:
|
|
|
+ mxs_lradc_ts_unregister(lradc);
|
|
|
err_dev:
|
|
|
mxs_lradc_trigger_remove(iio);
|
|
|
err_trig:
|
|
@@ -549,6 +971,8 @@ static int mxs_lradc_remove(struct platform_device *pdev)
|
|
|
struct iio_dev *iio = platform_get_drvdata(pdev);
|
|
|
struct mxs_lradc *lradc = iio_priv(iio);
|
|
|
|
|
|
+ mxs_lradc_ts_unregister(lradc);
|
|
|
+
|
|
|
mxs_lradc_hw_stop(lradc);
|
|
|
|
|
|
iio_device_unregister(iio);
|