Hi Marek, On Fri, Jan 11, 2013 at 12:43:50AM +0100, Marek Vasut wrote: > This patch implements support for sampling of a touchscreen into > the MXS LRADC driver. The LRADC block allows configuring some of > it's channels into special mode where they either output the drive > voltage or sample it, allowing it to operate a 4-wire or 5-wire > resistive touchscreen. > > In case the touchscreen mode is enabled, the LRADC slot #7 is > reserved for touchscreen only, therefore it is not possible to > sample 8 LRADC channels at time, but only 7 channels. > > The touchscreen controller is configured such that the PENDOWN event > disables touchscreen interrupts and triggers execution of worker > thread, which then polls the touchscreen controller for X, Y and > Pressure values. This reduces the overhead of interrupt-driven > operation. Upon the PENUP event, the worker thread re-enables the > PENDOWN detection interrupt and exits. > > Signed-off-by: Marek Vasut <marex@xxxxxxx> > Cc: Dmitry Torokhov <dmitry.torokhov@xxxxxxxxx> > Cc: Fabio Estevam <fabio.estevam@xxxxxxxxxxxxx> > Cc: Jonathan Cameron <jic23@xxxxxxxxxx> > Cc: Shawn Guo <shawn.guo@xxxxxxxxxx> > --- > .../bindings/staging/iio/adc/mxs-lradc.txt | 6 + > drivers/staging/iio/adc/mxs-lradc.c | 473 +++++++++++++++++++- > 2 files changed, 455 insertions(+), 24 deletions(-) > > V2: - Replace the use_touchscreen* with enum mxs_lradc_ts, which now tracks > touchscreen state (OFF, 4-wire, 5-wire) > - Add "stop_touchscreen" bit, which indicates the touchscreen is going down > and makes the driver not re-enable touchscreen interrupts and start any > new touchscreen works. > - Make all bitfields "unsigned int" instead of plain "int" > - Use switch(plate) in mxs_lradc_ts_sample() > - Cancel touchscreen thread in mxs_lradc_ts_close(), do this only after > we are sure touchscreen interrupt is off and will never be re-enabled. > This avoids serious race condition. > - Call input_free_device() if input_register_device() fails to avoid memory > leak. > - Do not call input_free_device() after input_unregister_device() in > mxs_lradc_ts_unregister() to avoid double-free > > V3: - Replace bitfields with bools > - Make sure touchscreen workQ doesn't cause race condition upon closing > the input device > - Make touchscreen allocation failure fatal > - Remove redundant check for lradc->ts_input in mxs_lradc_ts_unregister() > - Drop __devinit and __devexit > > diff --git a/Documentation/devicetree/bindings/staging/iio/adc/mxs-lradc.txt b/Documentation/devicetree/bindings/staging/iio/adc/mxs-lradc.txt > index 801d58c..4688205 100644 > --- a/Documentation/devicetree/bindings/staging/iio/adc/mxs-lradc.txt > +++ b/Documentation/devicetree/bindings/staging/iio/adc/mxs-lradc.txt > @@ -5,6 +5,12 @@ Required properties: > - reg: Address and length of the register set for the device > - interrupts: Should contain the LRADC interrupts > > +Optional properties: > +- fsl,lradc-touchscreen-wires: Number of wires used to connect the touchscreen > + to LRADC. Valid value is either 4 or 5. If this > + property is not present, then the touchscreen is > + disabled. > + > Examples: > > lradc@80050000 { > diff --git a/drivers/staging/iio/adc/mxs-lradc.c b/drivers/staging/iio/adc/mxs-lradc.c > index 6249957..2db344b 100644 > --- a/drivers/staging/iio/adc/mxs-lradc.c > +++ b/drivers/staging/iio/adc/mxs-lradc.c > @@ -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. */ > @@ -195,6 +277,270 @@ static const struct iio_info mxs_lradc_iio_info = { > }; > > /* > + * 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"); > + lradc->use_touchscreen = MXS_LRADC_TOUCHSCREEN_NONE; > + return -ENOMEM; Since errors are now fatal you do not need to modify "use_touchscreen". > + } > + > + 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 > */ > static irqreturn_t mxs_lradc_handle_irq(int irq, void *data) > @@ -202,14 +548,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()); > @@ -306,8 +662,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); > @@ -339,17 +697,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, > @@ -384,9 +745,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 0; Since the fucntion's return type is bool it is better to do return false; > + > + /* Test for attempts to map more channels then available slots. */ > + if (map_chans + rsvd_chans > LRADC_MAX_MAPPED_CHANS) > + return 0; return false; > + > + return 1; return true; > } > > static const struct iio_buffer_setup_ops mxs_lradc_buffer_ops = { > @@ -435,15 +820,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); > @@ -460,12 +859,14 @@ static void mxs_lradc_hw_stop(struct mxs_lradc *lradc) > writel(0, lradc->base + LRADC_DELAY(i)); > } > > -static int __devinit mxs_lradc_probe(struct platform_device *pdev) > +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; > > @@ -487,6 +888,21 @@ static int __devinit 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); > @@ -524,11 +940,16 @@ static int __devinit 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. */ > @@ -536,6 +957,8 @@ static int __devinit 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: > @@ -545,11 +968,13 @@ err_addr: > return ret; > } > > -static int __devexit mxs_lradc_remove(struct platform_device *pdev) > +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); > @@ -573,7 +998,7 @@ static struct platform_driver mxs_lradc_driver = { > .of_match_table = mxs_lradc_dt_ids, > }, > .probe = mxs_lradc_probe, > - .remove = __devexit_p(mxs_lradc_remove), > + .remove = mxs_lradc_remove, > }; > > module_platform_driver(mxs_lradc_driver); > -- > 1.7.10.4 > Besides the minor nits above, Acked-by: Dmitry Torokhov <dmitry.torokhov@xxxxxxxxx> Thanks! -- Dmitry -- To unsubscribe from this list: send the line "unsubscribe linux-iio" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html