On 12/19/2012 05:01 PM, Marek Vasut wrote: > Ccing Dmitry. > > Also, Jonathan, can I get your review please? Dratt caught me hiding from this one and hoping it would go away ;) > >> 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: 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 | 469 >> +++++++++++++++++++- 2 files changed, 454 insertions(+), 21 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 >> >> 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..52317ef 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; >> + unsigned int stop_touchscreen:1; >> + unsigned int use_touchbutton:1; >> + >> + 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,272 @@ 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 = 0; >> + >> + /* 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 = 1; >> + >> + /* 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); >> + >> + /* Wait until touchscreen thread finishes any possible remnants. */ >> + cancel_work_sync(&lradc->ts_work); >> +} >> + >> +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_warn(dev, "Failed to allocate TS device, disabling.\n"); >> + lradc->use_touchscreen = MXS_LRADC_TOUCHSCREEN_NONE; >> + return 0; >> + } >> + >> + 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; >> + >> + if (!lradc->ts_input) >> + 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 +550,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 +664,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 +699,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 +747,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; >> + >> + /* Test for attempts to map more channels then available slots. */ >> + if (map_chans + rsvd_chans > LRADC_MAX_MAPPED_CHANS) >> + return 0; >> + >> + return 1; >> } >> >> static const struct iio_buffer_setup_ops mxs_lradc_buffer_ops = { >> @@ -435,15 +822,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); >> @@ -463,9 +864,11 @@ static void mxs_lradc_hw_stop(struct mxs_lradc *lradc) >> static int __devinit 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 +890,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 +942,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 +959,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: >> @@ -550,6 +975,8 @@ static int __devexit 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); > -- > 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 > -- 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