On Sun, May 29, 2016 at 6:49 PM, Jonathan Cameron <jic23@xxxxxxxxxx> wrote: > On 28/05/16 18:45, Ksenija Stanojević wrote: >> On Sun, May 1, 2016 at 6:47 PM, Jonathan Cameron <jic23@xxxxxxxxxx> wrote: >>> On 29/04/16 12:49, Ksenija Stanojevic wrote: >>>> Add mxs-lradc touchscreen driver. >>>> >>>> Signed-off-by: Ksenija Stanojevic <ksenija.stanojevic@xxxxxxxxx> >>> The only real thing this raises for me is why we are grabbing IRQs that I >>> don't think this driver even cares about... >>> >>> Jonathan >>>> --- >>>> drivers/input/touchscreen/Kconfig | 14 +- >>>> drivers/input/touchscreen/Makefile | 1 + >>>> drivers/input/touchscreen/mxs-lradc-ts.c | 729 +++++++++++++++++++++++++++++++ >>>> 3 files changed, 742 insertions(+), 2 deletions(-) >>>> create mode 100644 drivers/input/touchscreen/mxs-lradc-ts.c >>>> >>>> diff --git a/drivers/input/touchscreen/Kconfig b/drivers/input/touchscreen/Kconfig >>>> index 8ecdc38..d614d248 100644 >>>> --- a/drivers/input/touchscreen/Kconfig >>>> +++ b/drivers/input/touchscreen/Kconfig >>>> @@ -566,7 +566,7 @@ config TOUCHSCREEN_HP600 >>>> depends on SH_HP6XX && SH_ADC >>>> help >>>> Say Y here if you have a HP Jornada 620/660/680/690 and want to >>>> - support the built-in touchscreen. >>>> + support the built-in touchscreen. >>>> >>>> To compile this driver as a module, choose M here: the >>>> module will be called hp680_ts_input. >>>> @@ -685,7 +685,7 @@ config TOUCHSCREEN_UCB1400 >>>> This enables support for the Philips UCB1400 touchscreen interface. >>>> The UCB1400 is an AC97 audio codec. The touchscreen interface >>>> will be initialized only after the ALSA subsystem has been >>>> - brought up and the UCB1400 detected. You therefore have to >>>> + brought up and the UCB1400 detected. You therefore have to >>>> configure ALSA support as well (either built-in or modular, >>>> independently of whether this driver is itself built-in or >>>> modular) for this driver to work. >>>> @@ -842,6 +842,16 @@ config TOUCHSCREEN_MX25 >>>> To compile this driver as a module, choose M here: the >>>> module will be called fsl-imx25-tcq. >>>> >>>> +config TOUCHSCREEN_MXS_LRADC >>>> + tristate "Freescale i.MX23/i.MX28 LRADC touchscreen" >>>> + depends on MFD_MXS_LRADC >>>> + help >>>> + Say Y here if you have a touchscreen connected to the low-resolution >>>> + analog-to-digital converter (LRADC) on an i.MX23 or i.MX28 processor. >>>> + >>>> + To compile this driver as a module, choose M here: the module will be >>>> + called mxs-lradc-ts. >>>> + >>>> config TOUCHSCREEN_MC13783 >>>> tristate "Freescale MC13783 touchscreen input driver" >>>> depends on MFD_MC13XXX >>>> diff --git a/drivers/input/touchscreen/Makefile b/drivers/input/touchscreen/Makefile >>>> index f42975e..513a6ff 100644 >>>> --- a/drivers/input/touchscreen/Makefile >>>> +++ b/drivers/input/touchscreen/Makefile >>>> @@ -54,6 +54,7 @@ obj-$(CONFIG_TOUCHSCREEN_MIGOR) += migor_ts.o >>>> obj-$(CONFIG_TOUCHSCREEN_MMS114) += mms114.o >>>> obj-$(CONFIG_TOUCHSCREEN_MTOUCH) += mtouch.o >>>> obj-$(CONFIG_TOUCHSCREEN_MK712) += mk712.o >>>> +obj-$(CONFIG_TOUCHSCREEN_MXS_LRADC) += mxs-lradc-ts.o >>>> obj-$(CONFIG_TOUCHSCREEN_HP600) += hp680_ts_input.o >>>> obj-$(CONFIG_TOUCHSCREEN_HP7XX) += jornada720_ts.o >>>> obj-$(CONFIG_TOUCHSCREEN_IPAQ_MICRO) += ipaq-micro-ts.o >>>> diff --git a/drivers/input/touchscreen/mxs-lradc-ts.c b/drivers/input/touchscreen/mxs-lradc-ts.c >>>> new file mode 100644 >>>> index 0000000..27abb8e >>>> --- /dev/null >>>> +++ b/drivers/input/touchscreen/mxs-lradc-ts.c >>>> @@ -0,0 +1,729 @@ >>>> +/* >>>> + * Freescale MXS LRADC driver >>>> + * >>>> + * Copyright (c) 2012 DENX Software Engineering, GmbH. >>>> + * Marek Vasut <marex@xxxxxxx> >>>> + * >>>> + * This program is free software; you can redistribute it and/or modify >>>> + * it under the terms of the GNU General Public License as published by >>>> + * the Free Software Foundation; either version 2 of the License, or >>>> + * (at your option) any later version. >>>> + * >>>> + * This program is distributed in the hope that it will be useful, >>>> + * but WITHOUT ANY WARRANTY; without even the implied warranty of >>>> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the >>>> + * GNU General Public License for more details. >>>> + */ >>>> + >>>> +#include <linux/module.h> >>>> +#include <linux/mfd/mxs-lradc.h> >>>> +#include <linux/platform_device.h> >>>> + >>>> +/* >>>> + * Touchscreen handling >>>> + */ >>>> +enum mxs_lradc_ts_plate { >>>> + LRADC_TOUCH = 0, >>>> + LRADC_SAMPLE_X, >>>> + LRADC_SAMPLE_Y, >>>> + LRADC_SAMPLE_PRESSURE, >>>> + LRADC_SAMPLE_VALID, >>>> +}; >>>> + >>>> +struct mxs_lradc_ts { >>>> + struct mxs_lradc *lradc; >>>> + struct device *dev; >>>> + /* >>>> + * When the touchscreen is enabled, we give it two private virtual >>>> + * channels: #6 and #7. This means that only 6 virtual channels (instead >>>> + * of 8) will be available for buffered capture. >>>> + */ >>>> +#define TOUCHSCREEN_VCHANNEL1 7 >>>> +#define TOUCHSCREEN_VCHANNEL2 6 >>>> + >>>> + struct input_dev *ts_input; >>>> + >>>> + enum mxs_lradc_ts_plate cur_plate; /* state machine */ >>>> + bool ts_valid; >>>> + unsigned ts_x_pos; >>>> + unsigned ts_y_pos; >>>> + unsigned ts_pressure; >>>> + >>>> + /* handle touchscreen's physical behaviour */ >>>> + /* samples per coordinate */ >>>> + unsigned over_sample_cnt; >>>> + /* time clocks between samples */ >>>> + unsigned over_sample_delay; >>>> + /* time in clocks to wait after the plates where switched */ >>>> + unsigned settling_delay; >>>> +}; >>>> + >>>> +static u32 mxs_lradc_plate_mask(struct mxs_lradc *lradc) >>>> +{ >>>> + if (lradc->soc == IMX23_LRADC) >>>> + return LRADC_CTRL0_MX23_PLATE_MASK; >>>> + return LRADC_CTRL0_MX28_PLATE_MASK; >>>> +} >>>> + >>>> +static u32 mxs_lradc_touch_detect_bit(struct mxs_lradc *lradc) >>>> +{ >>>> + if (lradc->soc == IMX23_LRADC) >>>> + return LRADC_CTRL0_MX23_TOUCH_DETECT_ENABLE; >>>> + return LRADC_CTRL0_MX28_TOUCH_DETECT_ENABLE; >>>> +} >>>> + >>>> +static u32 mxs_lradc_drive_x_plate(struct mxs_lradc *lradc) >>>> +{ >>>> + if (lradc->soc == IMX23_LRADC) >>>> + return LRADC_CTRL0_MX23_XP | LRADC_CTRL0_MX23_XM; >>>> + return LRADC_CTRL0_MX28_XPPSW | LRADC_CTRL0_MX28_XNNSW; >>>> +} >>>> + >>>> +static u32 mxs_lradc_drive_y_plate(struct mxs_lradc *lradc) >>>> +{ >>>> + if (lradc->soc == IMX23_LRADC) >>>> + return LRADC_CTRL0_MX23_YP | LRADC_CTRL0_MX23_YM; >>>> + return LRADC_CTRL0_MX28_YPPSW | LRADC_CTRL0_MX28_YNNSW; >>>> +} >>>> + >>>> +static u32 mxs_lradc_drive_pressure(struct mxs_lradc *lradc) >>>> +{ >>>> + if (lradc->soc == IMX23_LRADC) >>>> + return LRADC_CTRL0_MX23_YP | LRADC_CTRL0_MX23_XM; >>>> + return LRADC_CTRL0_MX28_YPPSW | LRADC_CTRL0_MX28_XNNSW; >>>> +} >>>> + >>>> +static bool mxs_lradc_check_touch_event(struct mxs_lradc *lradc) >>>> +{ >>>> + return !!(readl(lradc->base + LRADC_STATUS) & >>>> + LRADC_STATUS_TOUCH_DETECT_RAW); >>>> +} >>>> + >>>> +static void mxs_lradc_map_ts_channel(struct mxs_lradc *lradc, unsigned vch, >>>> + unsigned ch) >>>> +{ >>>> + mxs_lradc_reg_clear(lradc, LRADC_CTRL4_LRADCSELECT_MASK(vch), >>>> + LRADC_CTRL4); >>>> + mxs_lradc_reg_set(lradc, LRADC_CTRL4_LRADCSELECT(vch, ch), LRADC_CTRL4); >>>> +} >>>> + >>>> +static void mxs_lradc_setup_ts_channel(struct mxs_lradc_ts *ts, unsigned ch) >>>> +{ >>>> + struct mxs_lradc *lradc = ts->lradc; >>>> + >>>> + /* >>>> + * prepare for oversampling conversion >>>> + * >>>> + * from the datasheet: >>>> + * "The ACCUMULATE bit in the appropriate channel register >>>> + * HW_LRADC_CHn must be set to 1 if NUM_SAMPLES is greater then 0; >>>> + * otherwise, the IRQs will not fire." >>>> + */ >>>> + mxs_lradc_reg_wrt(lradc, LRADC_CH_ACCUMULATE | >>>> + LRADC_CH_NUM_SAMPLES(ts->over_sample_cnt - 1), >>>> + LRADC_CH(ch)); >>>> + >>>> + /* from the datasheet: >>>> + * "Software must clear this register in preparation for a >>>> + * multi-cycle accumulation. >>>> + */ >>>> + mxs_lradc_reg_clear(lradc, LRADC_CH_VALUE_MASK, LRADC_CH(ch)); >>>> + >>>> + /* >>>> + * prepare the delay/loop unit according to the oversampling count >>>> + * >>>> + * from the datasheet: >>>> + * "The DELAY fields in HW_LRADC_DELAY0, HW_LRADC_DELAY1, >>>> + * HW_LRADC_DELAY2, and HW_LRADC_DELAY3 must be non-zero; otherwise, >>>> + * the LRADC will not trigger the delay group." >>>> + */ >>>> + mxs_lradc_reg_wrt(lradc, LRADC_DELAY_TRIGGER(1 << ch) | >>>> + LRADC_DELAY_TRIGGER_DELAYS(0) | >>>> + LRADC_DELAY_LOOP(ts->over_sample_cnt - 1) | >>>> + LRADC_DELAY_DELAY(ts->over_sample_delay - 1), >>>> + LRADC_DELAY(3)); >>>> + >>>> + mxs_lradc_reg_clear(lradc, LRADC_CTRL1_LRADC_IRQ(ch), LRADC_CTRL1); >>>> + >>>> + /* >>>> + * after changing the touchscreen plates setting >>>> + * the signals need some initial time to settle. Start the >>>> + * SoC's delay unit and start the conversion later >>>> + * and automatically. >>>> + */ >>>> + mxs_lradc_reg_wrt( >>>> + lradc, >>>> + LRADC_DELAY_TRIGGER(0) | /* don't trigger ADC */ >>>> + LRADC_DELAY_TRIGGER_DELAYS(BIT(3)) | /* trigger DELAY unit#3 */ >>>> + LRADC_DELAY_KICK | >>>> + LRADC_DELAY_DELAY(ts->settling_delay), >>>> + LRADC_DELAY(2)); >>>> +} >>>> + >>>> +/* >>>> + * Pressure detection is special: >>>> + * We want to do both required measurements for the pressure detection in >>>> + * one turn. Use the hardware features to chain both conversions and let the >>>> + * hardware report one interrupt if both conversions are done >>>> + */ >>>> +static void mxs_lradc_setup_ts_pressure(struct mxs_lradc_ts *ts, unsigned ch1, >>>> + unsigned ch2) >>>> +{ >>>> + struct mxs_lradc *lradc = ts->lradc; >>>> + u32 reg; >>>> + >>>> + /* >>>> + * prepare for oversampling conversion >>>> + * >>>> + * from the datasheet: >>>> + * "The ACCUMULATE bit in the appropriate channel register >>>> + * HW_LRADC_CHn must be set to 1 if NUM_SAMPLES is greater then 0; >>>> + * otherwise, the IRQs will not fire." >>>> + */ >>>> + reg = LRADC_CH_ACCUMULATE | >>>> + LRADC_CH_NUM_SAMPLES(ts->over_sample_cnt - 1); >>>> + mxs_lradc_reg_wrt(lradc, reg, LRADC_CH(ch1)); >>>> + mxs_lradc_reg_wrt(lradc, reg, LRADC_CH(ch2)); >>>> + >>>> + /* from the datasheet: >>>> + * "Software must clear this register in preparation for a >>>> + * multi-cycle accumulation. >>>> + */ >>>> + mxs_lradc_reg_clear(lradc, LRADC_CH_VALUE_MASK, LRADC_CH(ch1)); >>>> + mxs_lradc_reg_clear(lradc, LRADC_CH_VALUE_MASK, LRADC_CH(ch2)); >>>> + >>>> + /* prepare the delay/loop unit according to the oversampling count */ >>>> + mxs_lradc_reg_wrt( >>>> + lradc, >>>> + LRADC_DELAY_TRIGGER(1 << ch1) | >>>> + LRADC_DELAY_TRIGGER(1 << ch2) | /* start both channels */ >>>> + LRADC_DELAY_TRIGGER_DELAYS(0) | >>>> + LRADC_DELAY_LOOP(ts->over_sample_cnt - 1) | >>>> + LRADC_DELAY_DELAY(ts->over_sample_delay - 1), >>>> + LRADC_DELAY(3)); >>>> + >>>> + mxs_lradc_reg_clear(lradc, LRADC_CTRL1_LRADC_IRQ(ch2), LRADC_CTRL1); >>>> + >>>> + /* >>>> + * after changing the touchscreen plates setting >>>> + * the signals need some initial time to settle. Start the >>>> + * SoC's delay unit and start the conversion later >>>> + * and automatically. >>>> + */ >>>> + mxs_lradc_reg_wrt( >>>> + lradc, >>>> + LRADC_DELAY_TRIGGER(0) | /* don't trigger ADC */ >>>> + LRADC_DELAY_TRIGGER_DELAYS(BIT(3)) | /* trigger DELAY unit#3 */ >>>> + LRADC_DELAY_KICK | >>>> + LRADC_DELAY_DELAY(ts->settling_delay), LRADC_DELAY(2)); >>>> +} >>>> + >>>> +static unsigned mxs_lradc_ts_read_raw_channel(struct mxs_lradc_ts *ts, >>>> + unsigned channel) >>>> +{ >>>> + u32 reg; >>>> + unsigned num_samples, val; >>>> + struct mxs_lradc *lradc = ts->lradc; >>>> + >>>> + reg = readl(lradc->base + LRADC_CH(channel)); >>>> + if (reg & LRADC_CH_ACCUMULATE) >>>> + num_samples = ts->over_sample_cnt; >>>> + else >>>> + num_samples = 1; >>>> + >>>> + val = (reg & LRADC_CH_VALUE_MASK) >> LRADC_CH_VALUE_OFFSET; >>>> + return val / num_samples; >>>> +} >>>> + >>>> +static unsigned mxs_lradc_read_ts_pressure(struct mxs_lradc_ts *ts, >>>> + unsigned ch1, unsigned ch2) >>>> +{ >>>> + u32 reg, mask; >>>> + unsigned pressure, m1, m2; >>>> + struct mxs_lradc *lradc = ts->lradc; >>>> + >>>> + mask = LRADC_CTRL1_LRADC_IRQ(ch1) | LRADC_CTRL1_LRADC_IRQ(ch2); >>>> + reg = readl(lradc->base + LRADC_CTRL1) & mask; >>>> + >>>> + while (reg != mask) { >>>> + reg = readl(lradc->base + LRADC_CTRL1) & mask; >>>> + dev_dbg(ts->dev, "One channel is still busy: %X\n", reg); >>>> + } >>>> + >>>> + m1 = mxs_lradc_ts_read_raw_channel(ts, ch1); >>>> + m2 = mxs_lradc_ts_read_raw_channel(ts, ch2); >>>> + >>>> + if (m2 == 0) { >>>> + dev_warn(ts->dev, "Cannot calculate pressure\n"); >>>> + return 1 << (LRADC_RESOLUTION - 1); >>>> + } >>>> + >>>> + /* simply scale the value from 0 ... max ADC resolution */ >>>> + pressure = m1; >>>> + pressure *= (1 << LRADC_RESOLUTION); >>>> + pressure /= m2; >>>> + >>>> + dev_dbg(ts->dev, "Pressure = %u\n", pressure); >>>> + return pressure; >>>> +} >>>> + >>>> +#define TS_CH_XP 2 >>>> +#define TS_CH_YP 3 >>>> +#define TS_CH_XM 4 >>>> +#define TS_CH_YM 5 >>>> + >>>> +/* >>>> + * YP(open)--+-------------+ >>>> + * | |--+ >>>> + * | | | >>>> + * YM(-)--+-------------+ | >>>> + * +--------------+ >>>> + * | | >>>> + * XP(weak+) XM(open) >>>> + * >>>> + * "weak+" means 200k Ohm VDDIO >>>> + * (-) means GND >>>> + */ >>>> +static void mxs_lradc_setup_touch_detection(struct mxs_lradc_ts *ts) >>>> +{ >>>> + struct mxs_lradc *lradc = ts->lradc; >>>> + >>>> + /* >>>> + * In order to detect a touch event the 'touch detect enable' bit >>>> + * enables: >>>> + * - a weak pullup to the X+ connector >>>> + * - a strong ground at the Y- connector >>>> + */ >>>> + mxs_lradc_reg_clear(lradc, mxs_lradc_plate_mask(lradc), LRADC_CTRL0); >>>> + mxs_lradc_reg_set(lradc, mxs_lradc_touch_detect_bit(lradc), >>>> + LRADC_CTRL0); >>>> +} >>>> + >>>> +/* >>>> + * YP(meas)--+-------------+ >>>> + * | |--+ >>>> + * | | | >>>> + * YM(open)--+-------------+ | >>>> + * +--------------+ >>>> + * | | >>>> + * XP(+) XM(-) >>>> + * >>>> + * (+) means here 1.85 V >>>> + * (-) means here GND >>>> + */ >>>> +static void mxs_lradc_prepare_x_pos(struct mxs_lradc_ts *ts) >>>> +{ >>>> + struct mxs_lradc *lradc = ts->lradc; >>>> + >>>> + mxs_lradc_reg_clear(lradc, mxs_lradc_plate_mask(lradc), LRADC_CTRL0); >>>> + mxs_lradc_reg_set(lradc, mxs_lradc_drive_x_plate(lradc), LRADC_CTRL0); >>>> + >>>> + ts->cur_plate = LRADC_SAMPLE_X; >>>> + mxs_lradc_map_ts_channel(lradc, TOUCHSCREEN_VCHANNEL1, TS_CH_YP); >>>> + mxs_lradc_setup_ts_channel(ts, TOUCHSCREEN_VCHANNEL1); >>>> +} >>>> + >>>> +/* >>>> + * YP(+)--+-------------+ >>>> + * | |--+ >>>> + * | | | >>>> + * YM(-)--+-------------+ | >>>> + * +--------------+ >>>> + * | | >>>> + * XP(open) XM(meas) >>>> + * >>>> + * (+) means here 1.85 V >>>> + * (-) means here GND >>>> + */ >>>> +static void mxs_lradc_prepare_y_pos(struct mxs_lradc_ts *ts) >>>> +{ >>>> + struct mxs_lradc *lradc = ts->lradc; >>>> + >>>> + mxs_lradc_reg_clear(lradc, mxs_lradc_plate_mask(lradc), LRADC_CTRL0); >>>> + mxs_lradc_reg_set(lradc, mxs_lradc_drive_y_plate(lradc), LRADC_CTRL0); >>>> + >>>> + ts->cur_plate = LRADC_SAMPLE_Y; >>>> + mxs_lradc_map_ts_channel(lradc, TOUCHSCREEN_VCHANNEL1, TS_CH_XM); >>>> + mxs_lradc_setup_ts_channel(ts, TOUCHSCREEN_VCHANNEL1); >>>> +} >>>> + >>>> +/* >>>> + * YP(+)--+-------------+ >>>> + * | |--+ >>>> + * | | | >>>> + * YM(meas)--+-------------+ | >>>> + * +--------------+ >>>> + * | | >>>> + * XP(meas) XM(-) >>>> + * >>>> + * (+) means here 1.85 V >>>> + * (-) means here GND >>>> + */ >>>> +static void mxs_lradc_prepare_pressure(struct mxs_lradc_ts *ts) >>>> +{ >>>> + struct mxs_lradc *lradc = ts->lradc; >>>> + >>>> + mxs_lradc_reg_clear(lradc, mxs_lradc_plate_mask(lradc), LRADC_CTRL0); >>>> + mxs_lradc_reg_set(lradc, mxs_lradc_drive_pressure(lradc), LRADC_CTRL0); >>>> + >>>> + ts->cur_plate = LRADC_SAMPLE_PRESSURE; >>>> + mxs_lradc_map_ts_channel(lradc, TOUCHSCREEN_VCHANNEL1, TS_CH_YM); >>>> + mxs_lradc_map_ts_channel(lradc, TOUCHSCREEN_VCHANNEL2, TS_CH_XP); >>>> + mxs_lradc_setup_ts_pressure(ts, TOUCHSCREEN_VCHANNEL2, >>>> + TOUCHSCREEN_VCHANNEL1); >>>> +} >>>> + >>>> +static void mxs_lradc_enable_touch_detection(struct mxs_lradc_ts *ts) >>>> +{ >>>> + mxs_lradc_setup_touch_detection(ts); >>>> + >>>> + ts->cur_plate = LRADC_TOUCH; >>>> + mxs_lradc_reg_clear(ts->lradc, LRADC_CTRL1_TOUCH_DETECT_IRQ | >>>> + LRADC_CTRL1_TOUCH_DETECT_IRQ_EN, LRADC_CTRL1); >>>> + mxs_lradc_reg_set(ts->lradc, LRADC_CTRL1_TOUCH_DETECT_IRQ_EN, >>>> + LRADC_CTRL1); >>>> +} >>>> + >>>> +static void mxs_lradc_start_touch_event(struct mxs_lradc_ts *ts) >>>> +{ >>>> + mxs_lradc_reg_clear(ts->lradc, >>>> + LRADC_CTRL1_TOUCH_DETECT_IRQ_EN, >>>> + LRADC_CTRL1); >>>> + mxs_lradc_reg_set(ts->lradc, >>>> + LRADC_CTRL1_LRADC_IRQ_EN(TOUCHSCREEN_VCHANNEL1), >>>> + LRADC_CTRL1); >>>> + /* >>>> + * start with the Y-pos, because it uses nearly the same plate >>>> + * settings like the touch detection >>>> + */ >>>> + mxs_lradc_prepare_y_pos(ts); >>>> +} >>>> + >>>> +static void mxs_lradc_report_ts_event(struct mxs_lradc_ts *ts) >>>> +{ >>>> + input_report_abs(ts->ts_input, ABS_X, ts->ts_x_pos); >>>> + input_report_abs(ts->ts_input, ABS_Y, ts->ts_y_pos); >>>> + input_report_abs(ts->ts_input, ABS_PRESSURE, ts->ts_pressure); >>>> + input_report_key(ts->ts_input, BTN_TOUCH, 1); >>>> + input_sync(ts->ts_input); >>>> +} >>>> + >>>> +static void mxs_lradc_complete_touch_event(struct mxs_lradc_ts *ts) >>>> +{ >>>> + struct mxs_lradc *lradc = ts->lradc; >>>> + >>>> + mxs_lradc_setup_touch_detection(ts); >>>> + ts->cur_plate = LRADC_SAMPLE_VALID; >>>> + /* >>>> + * start a dummy conversion to burn time to settle the signals >>>> + * note: we are not interested in the conversion's value >>>> + */ >>>> + mxs_lradc_reg_wrt(lradc, 0, LRADC_CH(TOUCHSCREEN_VCHANNEL1)); >>>> + mxs_lradc_reg_clear(lradc, >>>> + LRADC_CTRL1_LRADC_IRQ(TOUCHSCREEN_VCHANNEL1) | >>>> + LRADC_CTRL1_LRADC_IRQ(TOUCHSCREEN_VCHANNEL2), >>>> + LRADC_CTRL1); >>>> + mxs_lradc_reg_wrt( >>>> + lradc, >>>> + LRADC_DELAY_TRIGGER(1 << TOUCHSCREEN_VCHANNEL1) | >>>> + LRADC_DELAY_KICK | LRADC_DELAY_DELAY(10), /* waste 5 ms */ >>>> + LRADC_DELAY(2)); >>>> +} >>>> + >>>> +/* >>>> + * in order to avoid false measurements, report only samples where >>>> + * the surface is still touched after the position measurement >>>> + */ >>>> +static void mxs_lradc_finish_touch_event(struct mxs_lradc_ts *ts, bool valid) >>>> +{ >>>> + struct mxs_lradc *lradc = ts->lradc; >>>> + >>>> + /* if it is still touched, report the sample */ >>>> + if (valid && mxs_lradc_check_touch_event(lradc)) { >>>> + ts->ts_valid = true; >>>> + mxs_lradc_report_ts_event(ts); >>>> + } >>>> + >>>> + /* if it is even still touched, continue with the next measurement */ >>>> + if (mxs_lradc_check_touch_event(lradc)) { >>>> + mxs_lradc_prepare_y_pos(ts); >>>> + return; >>>> + } >>>> + >>>> + if (ts->ts_valid) { >>>> + /* signal the release */ >>>> + ts->ts_valid = false; >>>> + input_report_key(ts->ts_input, BTN_TOUCH, 0); >>>> + input_sync(ts->ts_input); >>>> + } >>>> + >>>> + /* if it is released, wait for the next touch via IRQ */ >>>> + ts->cur_plate = LRADC_TOUCH; >>>> + mxs_lradc_reg_wrt(lradc, 0, LRADC_DELAY(2)); >>>> + mxs_lradc_reg_wrt(lradc, 0, LRADC_DELAY(3)); >>>> + mxs_lradc_reg_clear(lradc, >>>> + LRADC_CTRL1_TOUCH_DETECT_IRQ | >>>> + LRADC_CTRL1_LRADC_IRQ_EN(TOUCHSCREEN_VCHANNEL1) | >>>> + LRADC_CTRL1_LRADC_IRQ(TOUCHSCREEN_VCHANNEL1), >>>> + LRADC_CTRL1); >>>> + mxs_lradc_reg_set(lradc, LRADC_CTRL1_TOUCH_DETECT_IRQ_EN, LRADC_CTRL1); >>>> +} >>>> + >>>> +/* touchscreen's state machine */ >>>> +static void mxs_lradc_handle_touch(struct mxs_lradc_ts *ts) >>>> +{ >>>> + struct mxs_lradc *lradc = ts->lradc; >>>> + >>>> + switch (ts->cur_plate) { >>>> + case LRADC_TOUCH: >>>> + if (mxs_lradc_check_touch_event(lradc)) >>>> + mxs_lradc_start_touch_event(ts); >>>> + mxs_lradc_reg_clear(lradc, LRADC_CTRL1_TOUCH_DETECT_IRQ, >>>> + LRADC_CTRL1); >>>> + return; >>>> + >>>> + case LRADC_SAMPLE_Y: >>>> + ts->ts_y_pos = >>>> + mxs_lradc_ts_read_raw_channel(ts, TOUCHSCREEN_VCHANNEL1); >>>> + mxs_lradc_prepare_x_pos(ts); >>>> + return; >>>> + >>>> + case LRADC_SAMPLE_X: >>>> + ts->ts_x_pos = >>>> + mxs_lradc_ts_read_raw_channel(ts, TOUCHSCREEN_VCHANNEL1); >>>> + mxs_lradc_prepare_pressure(ts); >>>> + return; >>>> + >>>> + case LRADC_SAMPLE_PRESSURE: >>>> + ts->ts_pressure = >>>> + mxs_lradc_read_ts_pressure(ts, >>>> + TOUCHSCREEN_VCHANNEL2, >>>> + TOUCHSCREEN_VCHANNEL1); >>>> + mxs_lradc_complete_touch_event(ts); >>>> + return; >>>> + >>>> + case LRADC_SAMPLE_VALID: >>>> + mxs_lradc_finish_touch_event(ts, 1); >>>> + break; >>>> + } >>>> +} >>>> + >>>> +/* >>>> + * IRQ Handling >>>> + */ >>>> +static irqreturn_t mxs_lradc_ts_handle_irq(int irq, void *data) >>>> +{ >>>> + struct mxs_lradc_ts *ts = data; >>>> + struct mxs_lradc *lradc = ts->lradc; >>>> + unsigned long reg = readl(lradc->base + LRADC_CTRL1); >>>> + u32 clr_irq = mxs_lradc_irq_mask(lradc); >>>> + const u32 ts_irq_mask = >>>> + LRADC_CTRL1_TOUCH_DETECT_IRQ | >>>> + LRADC_CTRL1_LRADC_IRQ(TOUCHSCREEN_VCHANNEL1) | >>>> + LRADC_CTRL1_LRADC_IRQ(TOUCHSCREEN_VCHANNEL2); >>>> + >>>> + if (!(reg & mxs_lradc_irq_mask(lradc))) >>>> + return IRQ_NONE; >>>> + >>>> + if (lradc->use_touchscreen && (reg & ts_irq_mask)) { >>>> + mxs_lradc_handle_touch(ts); >>>> + >>>> + /* Make sure we don't clear the next conversion's interrupt. */ >>>> + clr_irq &= ~(LRADC_CTRL1_LRADC_IRQ(TOUCHSCREEN_VCHANNEL1) | >>>> + LRADC_CTRL1_LRADC_IRQ(TOUCHSCREEN_VCHANNEL2)); >>>> + mxs_lradc_reg_clear(lradc, reg & clr_irq, LRADC_CTRL1); >>>> + } >>>> + >>>> + return IRQ_HANDLED; >>>> +} >>>> + >>>> +static int mxs_lradc_ts_open(struct input_dev *dev) >>>> +{ >>>> + struct mxs_lradc_ts *ts = input_get_drvdata(dev); >>>> + >>>> + /* Enable the touch-detect circuitry. */ >>>> + mxs_lradc_enable_touch_detection(ts); >>>> + >>>> + return 0; >>>> +} >>>> + >>>> +static void mxs_lradc_disable_ts(struct mxs_lradc_ts *ts) >>>> +{ >>>> + struct mxs_lradc *lradc = ts->lradc; >>>> + >>>> + /* stop all interrupts from firing */ >>>> + mxs_lradc_reg_clear(lradc, LRADC_CTRL1_TOUCH_DETECT_IRQ_EN | >>>> + LRADC_CTRL1_LRADC_IRQ_EN(TOUCHSCREEN_VCHANNEL1) | >>>> + LRADC_CTRL1_LRADC_IRQ_EN(TOUCHSCREEN_VCHANNEL2), LRADC_CTRL1); >>>> + >>>> + /* Power-down touchscreen touch-detect circuitry. */ >>>> + mxs_lradc_reg_clear(lradc, mxs_lradc_plate_mask(lradc), LRADC_CTRL0); >>>> +} >>>> + >>>> +static void mxs_lradc_ts_close(struct input_dev *dev) >>>> +{ >>>> + struct mxs_lradc_ts *ts = input_get_drvdata(dev); >>>> + >>>> + mxs_lradc_disable_ts(ts); >>>> +} >>>> + >>>> +static void mxs_lradc_ts_hw_init(struct mxs_lradc_ts *ts) >>>> +{ >>>> + struct mxs_lradc *lradc = ts->lradc; >>>> + >>>> + /* Configure the touchscreen type */ >>>> + if (lradc->soc == IMX28_LRADC) { >>>> + mxs_lradc_reg_clear(lradc, LRADC_CTRL0_MX28_TOUCH_SCREEN_TYPE, >>>> + LRADC_CTRL0); >>>> + >>>> + if (lradc->use_touchscreen == MXS_LRADC_TOUCHSCREEN_5WIRE) >>>> + mxs_lradc_reg_set(lradc, >>>> + LRADC_CTRL0_MX28_TOUCH_SCREEN_TYPE, >>>> + LRADC_CTRL0); >>>> + } >>>> +} >>>> + >>>> +static void mxs_lradc_ts_hw_stop(struct mxs_lradc_ts *ts) >>>> +{ >>>> + int i; >>>> + struct mxs_lradc *lradc = ts->lradc; >>>> + >>>> + mxs_lradc_reg_clear(lradc, >>>> + lradc->buffer_vchans << LRADC_CTRL1_LRADC_IRQ_EN_OFFSET, >>>> + LRADC_CTRL1); >>>> + >>>> + for (i = 1; i < LRADC_MAX_DELAY_CHANS; i++) >>>> + mxs_lradc_reg_wrt(ts->lradc, 0, LRADC_DELAY(i)); >>>> +} >>>> + >>>> +static int mxs_lradc_ts_register(struct mxs_lradc_ts *ts) >>>> +{ >>>> + struct input_dev *input = ts->ts_input; >>>> + struct device *dev = ts->dev; >>>> + struct mxs_lradc *lradc = ts->lradc; >>>> + >>>> + if (!lradc->use_touchscreen) >>>> + return 0; >>>> + >>>> + input->name = DRIVER_NAME_TS; >>>> + 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); >>>> + __set_bit(INPUT_PROP_DIRECT, input->propbit); >>>> + input_set_abs_params(input, ABS_X, 0, LRADC_SINGLE_SAMPLE_MASK, 0, 0); >>>> + input_set_abs_params(input, ABS_Y, 0, LRADC_SINGLE_SAMPLE_MASK, 0, 0); >>>> + input_set_abs_params(input, ABS_PRESSURE, 0, LRADC_SINGLE_SAMPLE_MASK, >>>> + 0, 0); >>>> + >>>> + ts->ts_input = input; >>>> + input_set_drvdata(input, ts); >>>> + >>>> + return input_register_device(input); >>>> +} >>>> + >>>> +static int mxs_lradc_ts_probe(struct platform_device *pdev) >>>> +{ >>>> + struct device *dev = &pdev->dev; >>>> + struct device_node *node = dev->parent->of_node; >>>> + struct mxs_lradc *lradc = dev_get_platdata(dev); >>>> + struct mxs_lradc_ts *ts; >>>> + struct input_dev *input; >>>> + int touch_ret, ret, i; >>>> + u32 ts_wires = 0, adapt; >>>> + >>>> + ts = devm_kzalloc(dev, sizeof(*ts), GFP_KERNEL); >>>> + input = devm_input_allocate_device(dev); >>>> + if (!ts || !input) >>>> + return -ENOMEM; >>>> + >>>> + ts->lradc = lradc; >>>> + ts->dev = dev; >>>> + ts->ts_input = input; >>>> + platform_set_drvdata(pdev, ts); >>>> + input_set_drvdata(input, ts); >>>> + >>>> + touch_ret = of_property_read_u32(node, "fsl,lradc-touchscreen-wires", >>>> + &ts_wires); >>>> + >>>> + if (of_property_read_u32(node, "fsl,ave-ctrl", &adapt)) { >>>> + ts->over_sample_cnt = 4; >>>> + } else { >>>> + if (adapt < 1 || adapt > 32) { >>>> + dev_err(ts->dev, "Invalid sample count (%u)\n", >>>> + adapt); >>>> + touch_ret = -EINVAL; >>>> + } else { >>>> + ts->over_sample_cnt = adapt; >>>> + } >>>> + } >>>> + >>>> + if (of_property_read_u32(node, "fsl,ave-delay", &adapt)) { >>>> + ts->over_sample_delay = 2; >>>> + } else { >>>> + if (adapt < 2 || adapt > LRADC_DELAY_DELAY_MASK + 1) { >>>> + dev_err(ts->dev, "Invalid sample delay (%u)\n", >>>> + adapt); >>>> + touch_ret = -EINVAL; >>>> + } else { >>>> + ts->over_sample_delay = adapt; >>>> + } >>>> + } >>>> + >>>> + if (of_property_read_u32(node, "fsl,settling", &adapt)) { >>>> + ts->settling_delay = 10; >>>> + } else { >>>> + if (adapt < 1 || adapt > LRADC_DELAY_DELAY_MASK) { >>>> + dev_err(ts->dev, "Invalid settling delay (%u)\n", >>>> + adapt); >>>> + touch_ret = -EINVAL; >>>> + } else { >>>> + ts->settling_delay = adapt; >>>> + } >>>> + } >>>> + >>>> + mxs_lradc_ts_hw_init(ts); >>>> + for (i = 0; i < lradc->irq_count; i++) { >>>> + ret = devm_request_irq(dev, lradc->irq[i], >>>> + mxs_lradc_ts_handle_irq, >>>> + IRQF_SHARED, lradc->irq_name[i], ts); >>> As with the adc driver, are we actually using all of these? I'd prefer we >>> only grab the ones that are actually relevant. >> >> Only irq lines relevant for touchscreen are: >> mxs-lradc-touchscreen, mxs-lradc-channel6 and mxs-lradc-channel7 >> But not all interrupts are beiing used even when I enabled all remaining >> channels (not used by touchscreen) for bufferd capture via >> echo 1 >/sys/bus/iio/devices/iio\:device0/scan_elements/in_voltagexx_en >> >> So I don't know if it's supposed to work like this... >> (It works the same on the original code) > Certainly should only grab the relevant ones to touch screen use in here.. > Original code probably being overly enthusiastic and we never noticed ;) Sorry for being unclear... The point I was trying to make was that even though touchscreen and adc don't share irq lines, irqs mxs-lradc-channel(2,3,4,5,6) are never being used. So touchscreen is only use 2 out of 3 irqs assigned to it: mxs-lradc-touchscreen and mxs-lradc-channel7 . And adc is only using 2 irq lines mxs-lradc-channel(0,1) during buffered capture, even when all virtal channels are enabled. I found that odd... >> root@cfa100xx:~# cat /proc/interrupts >> CPU0 >> 16: 13108 - 48 Edge MXS Timer Tick >> 17: 4240 - 82 Edge mxs-dma >> 25: 6 - 96 Edge 80010000.ssp >> 196: 0 - 68 Edge mxs-dma >> 210: 13 - 10 Edge mxs-lradc-touchscreen >> 211: 0 - 14 Edge mxs-lradc-thresh0 >> 212: 0 - 15 Edge mxs-lradc-thresh1 >> 213: 10 - 16 Edge mxs-lradc-channel0 >> 214: 10 - 17 Edge mxs-lradc-channel1 >> 215: 0 - 18 Edge mxs-lradc-channel2 >> 216: 0 - 19 Edge mxs-lradc-channel3 >> 217: 0 - 20 Edge mxs-lradc-channel4 >> 218: 0 - 21 Edge mxs-lradc-channel5 >> 219: 0 - 22 Edge mxs-lradc-channel6 >> 220: 412 - 23 Edge mxs-lradc-channel7 >> 221: 0 - 24 Edge mxs-lradc-button0 >> 222: 0 - 25 Edge mxs-lradc-button1 >> 223: 0 - 29 Edge RTC alarm >> 224: 0 - 111 Edge 80058000.i2c >> 228: 174 - 47 Edge uart-pl011 >> 229: 439 - 93 Edge 80080000.usb >> 230: 0 - 92 Edge 80090000.usb >> 231: 3610 - 101 Edge 800f0000.ethernet >> 232: 10 80050000.lradc-dev0 Edge >> Err: 0 >> >>>> + if (ret) >>>> + return ret; >>>> + } >>>> + >>>> + if (!touch_ret) { >>>> + ret = mxs_lradc_ts_register(ts); >>>> + if (!ret) >>>> + goto err_ts_register; >>>> + } >>>> + >>>> + return 0; >>>> + >>>> +err_ts_register: >>>> + mxs_lradc_ts_hw_stop(ts); >>>> + return ret; >>>> +} >>>> + >>>> +static int mxs_lradc_ts_remove(struct platform_device *pdev) >>>> +{ >>>> + struct mxs_lradc_ts *ts = platform_get_drvdata(pdev); >>>> + >>>> + mxs_lradc_ts_hw_stop(ts); >>>> + >>>> + return 0; >>>> +} >>>> + >>>> +static struct platform_driver mxs_lradc_ts_driver = { >>>> + .driver = { >>>> + .name = DRIVER_NAME_TS, >>>> + }, >>>> + .probe = mxs_lradc_ts_probe, >>>> + .remove = mxs_lradc_ts_remove, >>>> +}; >>>> +module_platform_driver(mxs_lradc_ts_driver); >>>> + >>>> +MODULE_LICENSE("GPL v2"); >>>> >>> >> -- >> 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