|
@@ -35,6 +35,7 @@
|
|
|
#include <linux/completion.h>
|
|
|
#include <linux/delay.h>
|
|
|
#include <linux/input.h>
|
|
|
+#include <linux/clk.h>
|
|
|
|
|
|
#include <linux/iio/iio.h>
|
|
|
#include <linux/iio/buffer.h>
|
|
@@ -129,11 +130,24 @@ enum mxs_lradc_ts {
|
|
|
MXS_LRADC_TOUCHSCREEN_5WIRE,
|
|
|
};
|
|
|
|
|
|
+/*
|
|
|
+ * Touchscreen handling
|
|
|
+ */
|
|
|
+enum lradc_ts_plate {
|
|
|
+ LRADC_TOUCH = 0,
|
|
|
+ LRADC_SAMPLE_X,
|
|
|
+ LRADC_SAMPLE_Y,
|
|
|
+ LRADC_SAMPLE_PRESSURE,
|
|
|
+ LRADC_SAMPLE_VALID,
|
|
|
+};
|
|
|
+
|
|
|
struct mxs_lradc {
|
|
|
struct device *dev;
|
|
|
void __iomem *base;
|
|
|
int irq[13];
|
|
|
|
|
|
+ struct clk *clk;
|
|
|
+
|
|
|
uint32_t *buffer;
|
|
|
struct iio_trigger *trig;
|
|
|
|
|
@@ -169,32 +183,63 @@ struct mxs_lradc {
|
|
|
#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;
|
|
|
+
|
|
|
+ enum mxs_lradc_id soc;
|
|
|
+ enum lradc_ts_plate cur_plate; /* statemachine */
|
|
|
+ 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;
|
|
|
};
|
|
|
|
|
|
#define LRADC_CTRL0 0x00
|
|
|
-#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_CTRL0_MX28_TOUCH_DETECT_ENABLE (1 << 23)
|
|
|
+# define LRADC_CTRL0_MX28_TOUCH_SCREEN_TYPE (1 << 22)
|
|
|
+# define LRADC_CTRL0_MX28_YNNSW /* YM */ (1 << 21)
|
|
|
+# define LRADC_CTRL0_MX28_YPNSW /* YP */ (1 << 20)
|
|
|
+# define LRADC_CTRL0_MX28_YPPSW /* YP */ (1 << 19)
|
|
|
+# define LRADC_CTRL0_MX28_XNNSW /* XM */ (1 << 18)
|
|
|
+# define LRADC_CTRL0_MX28_XNPSW /* XM */ (1 << 17)
|
|
|
+# define LRADC_CTRL0_MX28_XPPSW /* XP */ (1 << 16)
|
|
|
+
|
|
|
+# define LRADC_CTRL0_MX23_TOUCH_DETECT_ENABLE (1 << 20)
|
|
|
+# define LRADC_CTRL0_MX23_YM (1 << 19)
|
|
|
+# define LRADC_CTRL0_MX23_XM (1 << 18)
|
|
|
+# define LRADC_CTRL0_MX23_YP (1 << 17)
|
|
|
+# define LRADC_CTRL0_MX23_XP (1 << 16)
|
|
|
+
|
|
|
+# define LRADC_CTRL0_MX28_PLATE_MASK \
|
|
|
+ (LRADC_CTRL0_MX28_TOUCH_DETECT_ENABLE | \
|
|
|
+ LRADC_CTRL0_MX28_YNNSW | LRADC_CTRL0_MX28_YPNSW | \
|
|
|
+ LRADC_CTRL0_MX28_YPPSW | LRADC_CTRL0_MX28_XNNSW | \
|
|
|
+ LRADC_CTRL0_MX28_XNPSW | LRADC_CTRL0_MX28_XPPSW)
|
|
|
+
|
|
|
+# define LRADC_CTRL0_MX23_PLATE_MASK \
|
|
|
+ (LRADC_CTRL0_MX23_TOUCH_DETECT_ENABLE | \
|
|
|
+ LRADC_CTRL0_MX23_YM | LRADC_CTRL0_MX23_XM | \
|
|
|
+ LRADC_CTRL0_MX23_YP | LRADC_CTRL0_MX23_XP)
|
|
|
|
|
|
#define LRADC_CTRL1 0x10
|
|
|
#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_MX28_LRADC_IRQ_EN_MASK (0x1fff << 16)
|
|
|
+#define LRADC_CTRL1_MX23_LRADC_IRQ_EN_MASK (0x01ff << 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_MX28_LRADC_IRQ_MASK 0x1fff
|
|
|
+#define LRADC_CTRL1_MX23_LRADC_IRQ_MASK 0x01ff
|
|
|
#define LRADC_CTRL1_LRADC_IRQ_OFFSET 0
|
|
|
|
|
|
#define LRADC_CTRL2 0x20
|
|
@@ -207,19 +252,33 @@ struct mxs_lradc {
|
|
|
#define LRADC_CH_ACCUMULATE (1 << 29)
|
|
|
#define LRADC_CH_NUM_SAMPLES_MASK (0x1f << 24)
|
|
|
#define LRADC_CH_NUM_SAMPLES_OFFSET 24
|
|
|
+#define LRADC_CH_NUM_SAMPLES(x) \
|
|
|
+ ((x) << LRADC_CH_NUM_SAMPLES_OFFSET)
|
|
|
#define LRADC_CH_VALUE_MASK 0x3ffff
|
|
|
#define LRADC_CH_VALUE_OFFSET 0
|
|
|
|
|
|
#define LRADC_DELAY(n) (0xd0 + (0x10 * (n)))
|
|
|
#define LRADC_DELAY_TRIGGER_LRADCS_MASK (0xff << 24)
|
|
|
#define LRADC_DELAY_TRIGGER_LRADCS_OFFSET 24
|
|
|
+#define LRADC_DELAY_TRIGGER(x) \
|
|
|
+ (((x) << LRADC_DELAY_TRIGGER_LRADCS_OFFSET) & \
|
|
|
+ LRADC_DELAY_TRIGGER_LRADCS_MASK)
|
|
|
#define LRADC_DELAY_KICK (1 << 20)
|
|
|
#define LRADC_DELAY_TRIGGER_DELAYS_MASK (0xf << 16)
|
|
|
#define LRADC_DELAY_TRIGGER_DELAYS_OFFSET 16
|
|
|
+#define LRADC_DELAY_TRIGGER_DELAYS(x) \
|
|
|
+ (((x) << LRADC_DELAY_TRIGGER_DELAYS_OFFSET) & \
|
|
|
+ LRADC_DELAY_TRIGGER_DELAYS_MASK)
|
|
|
#define LRADC_DELAY_LOOP_COUNT_MASK (0x1f << 11)
|
|
|
#define LRADC_DELAY_LOOP_COUNT_OFFSET 11
|
|
|
+#define LRADC_DELAY_LOOP(x) \
|
|
|
+ (((x) << LRADC_DELAY_LOOP_COUNT_OFFSET) & \
|
|
|
+ LRADC_DELAY_LOOP_COUNT_MASK)
|
|
|
#define LRADC_DELAY_DELAY_MASK 0x7ff
|
|
|
#define LRADC_DELAY_DELAY_OFFSET 0
|
|
|
+#define LRADC_DELAY_DELAY(x) \
|
|
|
+ (((x) << LRADC_DELAY_DELAY_OFFSET) & \
|
|
|
+ LRADC_DELAY_DELAY_MASK)
|
|
|
|
|
|
#define LRADC_CTRL4 0x140
|
|
|
#define LRADC_CTRL4_LRADCSELECT_MASK(n) (0xf << ((n) * 4))
|
|
@@ -228,6 +287,475 @@ struct mxs_lradc {
|
|
|
#define LRADC_RESOLUTION 12
|
|
|
#define LRADC_SINGLE_SAMPLE_MASK ((1 << LRADC_RESOLUTION) - 1)
|
|
|
|
|
|
+static void mxs_lradc_reg_set(struct mxs_lradc *lradc, u32 val, u32 reg)
|
|
|
+{
|
|
|
+ writel(val, lradc->base + reg + STMP_OFFSET_REG_SET);
|
|
|
+}
|
|
|
+
|
|
|
+static void mxs_lradc_reg_clear(struct mxs_lradc *lradc, u32 val, u32 reg)
|
|
|
+{
|
|
|
+ writel(val, lradc->base + reg + STMP_OFFSET_REG_CLR);
|
|
|
+}
|
|
|
+
|
|
|
+static void mxs_lradc_reg_wrt(struct mxs_lradc *lradc, u32 val, u32 reg)
|
|
|
+{
|
|
|
+ writel(val, lradc->base + reg);
|
|
|
+}
|
|
|
+
|
|
|
+static u32 mxs_lradc_plate_mask(struct mxs_lradc *lradc)
|
|
|
+{
|
|
|
+ if (lradc->soc == IMX23_LRADC)
|
|
|
+ return LRADC_CTRL0_MX23_PLATE_MASK;
|
|
|
+ else
|
|
|
+ return LRADC_CTRL0_MX28_PLATE_MASK;
|
|
|
+}
|
|
|
+
|
|
|
+static u32 mxs_lradc_irq_en_mask(struct mxs_lradc *lradc)
|
|
|
+{
|
|
|
+ if (lradc->soc == IMX23_LRADC)
|
|
|
+ return LRADC_CTRL1_MX23_LRADC_IRQ_EN_MASK;
|
|
|
+ else
|
|
|
+ return LRADC_CTRL1_MX28_LRADC_IRQ_EN_MASK;
|
|
|
+}
|
|
|
+
|
|
|
+static u32 mxs_lradc_irq_mask(struct mxs_lradc *lradc)
|
|
|
+{
|
|
|
+ if (lradc->soc == IMX23_LRADC)
|
|
|
+ return LRADC_CTRL1_MX23_LRADC_IRQ_MASK;
|
|
|
+ else
|
|
|
+ return LRADC_CTRL1_MX28_LRADC_IRQ_MASK;
|
|
|
+}
|
|
|
+
|
|
|
+static u32 mxs_lradc_touch_detect_bit(struct mxs_lradc *lradc)
|
|
|
+{
|
|
|
+ if (lradc->soc == IMX23_LRADC)
|
|
|
+ return LRADC_CTRL0_MX23_TOUCH_DETECT_ENABLE;
|
|
|
+ else
|
|
|
+ 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;
|
|
|
+ else
|
|
|
+ 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;
|
|
|
+ else
|
|
|
+ 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;
|
|
|
+ else
|
|
|
+ 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_setup_ts_channel(struct mxs_lradc *lradc, unsigned ch)
|
|
|
+{
|
|
|
+ /*
|
|
|
+ * 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(lradc->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 */
|
|
|
+ mxs_lradc_reg_wrt(lradc, LRADC_DELAY_TRIGGER(1 << ch) |
|
|
|
+ LRADC_DELAY_TRIGGER_DELAYS(0) |
|
|
|
+ LRADC_DELAY_LOOP(lradc->over_sample_cnt - 1) |
|
|
|
+ LRADC_DELAY_DELAY(lradc->over_sample_delay - 1),
|
|
|
+ LRADC_DELAY(3));
|
|
|
+
|
|
|
+ mxs_lradc_reg_clear(lradc, LRADC_CTRL1_LRADC_IRQ(2) |
|
|
|
+ LRADC_CTRL1_LRADC_IRQ(3) | LRADC_CTRL1_LRADC_IRQ(4) |
|
|
|
+ LRADC_CTRL1_LRADC_IRQ(5), LRADC_CTRL1);
|
|
|
+
|
|
|
+ /* wake us again, when the complete conversion is done */
|
|
|
+ mxs_lradc_reg_set(lradc, LRADC_CTRL1_LRADC_IRQ_EN(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(1 << 3) | /* trigger DELAY unit#3 */
|
|
|
+ LRADC_DELAY_KICK |
|
|
|
+ LRADC_DELAY_DELAY(lradc->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 *lradc, unsigned ch1,
|
|
|
+ unsigned ch2)
|
|
|
+{
|
|
|
+ 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(lradc->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(lradc->over_sample_cnt - 1) |
|
|
|
+ LRADC_DELAY_DELAY(lradc->over_sample_delay - 1),
|
|
|
+ LRADC_DELAY(3));
|
|
|
+
|
|
|
+ mxs_lradc_reg_clear(lradc, LRADC_CTRL1_LRADC_IRQ(2) |
|
|
|
+ LRADC_CTRL1_LRADC_IRQ(3) | LRADC_CTRL1_LRADC_IRQ(4) |
|
|
|
+ LRADC_CTRL1_LRADC_IRQ(5), LRADC_CTRL1);
|
|
|
+
|
|
|
+ /* wake us again, when the conversions are done */
|
|
|
+ mxs_lradc_reg_set(lradc, LRADC_CTRL1_LRADC_IRQ_EN(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(1 << 3) | /* trigger DELAY unit#3 */
|
|
|
+ LRADC_DELAY_KICK |
|
|
|
+ LRADC_DELAY_DELAY(lradc->settling_delay), LRADC_DELAY(2));
|
|
|
+}
|
|
|
+
|
|
|
+static unsigned mxs_lradc_read_raw_channel(struct mxs_lradc *lradc,
|
|
|
+ unsigned channel)
|
|
|
+{
|
|
|
+ u32 reg;
|
|
|
+ unsigned num_samples, val;
|
|
|
+
|
|
|
+ reg = readl(lradc->base + LRADC_CH(channel));
|
|
|
+ if (reg & LRADC_CH_ACCUMULATE)
|
|
|
+ num_samples = lradc->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 *lradc,
|
|
|
+ unsigned ch1, unsigned ch2)
|
|
|
+{
|
|
|
+ u32 reg, mask;
|
|
|
+ unsigned pressure, m1, m2;
|
|
|
+
|
|
|
+ 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(lradc->dev, "One channel is still busy: %X\n", reg);
|
|
|
+ }
|
|
|
+
|
|
|
+ m1 = mxs_lradc_read_raw_channel(lradc, ch1);
|
|
|
+ m2 = mxs_lradc_read_raw_channel(lradc, ch2);
|
|
|
+
|
|
|
+ if (m2 == 0) {
|
|
|
+ dev_warn(lradc->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(lradc->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
|
|
|
+
|
|
|
+static int mxs_lradc_read_ts_channel(struct mxs_lradc *lradc)
|
|
|
+{
|
|
|
+ u32 reg;
|
|
|
+ int val;
|
|
|
+
|
|
|
+ reg = readl(lradc->base + LRADC_CTRL1);
|
|
|
+
|
|
|
+ /* only channels 3 to 5 are of interest here */
|
|
|
+ if (reg & LRADC_CTRL1_LRADC_IRQ(TS_CH_YP)) {
|
|
|
+ mxs_lradc_reg_clear(lradc, LRADC_CTRL1_LRADC_IRQ_EN(TS_CH_YP) |
|
|
|
+ LRADC_CTRL1_LRADC_IRQ(TS_CH_YP), LRADC_CTRL1);
|
|
|
+ val = mxs_lradc_read_raw_channel(lradc, TS_CH_YP);
|
|
|
+ } else if (reg & LRADC_CTRL1_LRADC_IRQ(TS_CH_XM)) {
|
|
|
+ mxs_lradc_reg_clear(lradc, LRADC_CTRL1_LRADC_IRQ_EN(TS_CH_XM) |
|
|
|
+ LRADC_CTRL1_LRADC_IRQ(TS_CH_XM), LRADC_CTRL1);
|
|
|
+ val = mxs_lradc_read_raw_channel(lradc, TS_CH_XM);
|
|
|
+ } else if (reg & LRADC_CTRL1_LRADC_IRQ(TS_CH_YM)) {
|
|
|
+ mxs_lradc_reg_clear(lradc, LRADC_CTRL1_LRADC_IRQ_EN(TS_CH_YM) |
|
|
|
+ LRADC_CTRL1_LRADC_IRQ(TS_CH_YM), LRADC_CTRL1);
|
|
|
+ val = mxs_lradc_read_raw_channel(lradc, TS_CH_YM);
|
|
|
+ } else {
|
|
|
+ return -EIO;
|
|
|
+ }
|
|
|
+
|
|
|
+ mxs_lradc_reg_wrt(lradc, 0, LRADC_DELAY(2));
|
|
|
+ mxs_lradc_reg_wrt(lradc, 0, LRADC_DELAY(3));
|
|
|
+
|
|
|
+ return val;
|
|
|
+}
|
|
|
+
|
|
|
+/*
|
|
|
+ * YP(open)--+-------------+
|
|
|
+ * | |--+
|
|
|
+ * | | |
|
|
|
+ * YM(-)--+-------------+ |
|
|
|
+ * +--------------+
|
|
|
+ * | |
|
|
|
+ * XP(weak+) XM(open)
|
|
|
+ *
|
|
|
+ * "weak+" means 200k Ohm VDDIO
|
|
|
+ * (-) means GND
|
|
|
+ */
|
|
|
+static void mxs_lradc_setup_touch_detection(struct mxs_lradc *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 *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);
|
|
|
+
|
|
|
+ lradc->cur_plate = LRADC_SAMPLE_X;
|
|
|
+ mxs_lradc_setup_ts_channel(lradc, TS_CH_YP);
|
|
|
+}
|
|
|
+
|
|
|
+/*
|
|
|
+ * YP(+)--+-------------+
|
|
|
+ * | |--+
|
|
|
+ * | | |
|
|
|
+ * YM(-)--+-------------+ |
|
|
|
+ * +--------------+
|
|
|
+ * | |
|
|
|
+ * XP(open) XM(meas)
|
|
|
+ *
|
|
|
+ * (+) means here 1.85 V
|
|
|
+ * (-) means here GND
|
|
|
+ */
|
|
|
+static void mxs_lradc_prepare_y_pos(struct mxs_lradc *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);
|
|
|
+
|
|
|
+ lradc->cur_plate = LRADC_SAMPLE_Y;
|
|
|
+ mxs_lradc_setup_ts_channel(lradc, TS_CH_XM);
|
|
|
+}
|
|
|
+
|
|
|
+/*
|
|
|
+ * YP(+)--+-------------+
|
|
|
+ * | |--+
|
|
|
+ * | | |
|
|
|
+ * YM(meas)--+-------------+ |
|
|
|
+ * +--------------+
|
|
|
+ * | |
|
|
|
+ * XP(meas) XM(-)
|
|
|
+ *
|
|
|
+ * (+) means here 1.85 V
|
|
|
+ * (-) means here GND
|
|
|
+ */
|
|
|
+static void mxs_lradc_prepare_pressure(struct mxs_lradc *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);
|
|
|
+
|
|
|
+ lradc->cur_plate = LRADC_SAMPLE_PRESSURE;
|
|
|
+ mxs_lradc_setup_ts_pressure(lradc, TS_CH_XP, TS_CH_YM);
|
|
|
+}
|
|
|
+
|
|
|
+static void mxs_lradc_enable_touch_detection(struct mxs_lradc *lradc)
|
|
|
+{
|
|
|
+ mxs_lradc_setup_touch_detection(lradc);
|
|
|
+
|
|
|
+ lradc->cur_plate = LRADC_TOUCH;
|
|
|
+ mxs_lradc_reg_clear(lradc, LRADC_CTRL1_TOUCH_DETECT_IRQ |
|
|
|
+ LRADC_CTRL1_TOUCH_DETECT_IRQ_EN, LRADC_CTRL1);
|
|
|
+ mxs_lradc_reg_set(lradc, LRADC_CTRL1_TOUCH_DETECT_IRQ_EN, LRADC_CTRL1);
|
|
|
+}
|
|
|
+
|
|
|
+static void mxs_lradc_report_ts_event(struct mxs_lradc *lradc)
|
|
|
+{
|
|
|
+ input_report_abs(lradc->ts_input, ABS_X, lradc->ts_x_pos);
|
|
|
+ input_report_abs(lradc->ts_input, ABS_Y, lradc->ts_y_pos);
|
|
|
+ input_report_abs(lradc->ts_input, ABS_PRESSURE, lradc->ts_pressure);
|
|
|
+ input_report_key(lradc->ts_input, BTN_TOUCH, 1);
|
|
|
+ input_sync(lradc->ts_input);
|
|
|
+}
|
|
|
+
|
|
|
+static void mxs_lradc_complete_touch_event(struct mxs_lradc *lradc)
|
|
|
+{
|
|
|
+ mxs_lradc_setup_touch_detection(lradc);
|
|
|
+ lradc->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(5));
|
|
|
+ mxs_lradc_reg_clear(lradc, LRADC_CTRL1_LRADC_IRQ(5), LRADC_CTRL1);
|
|
|
+ mxs_lradc_reg_set(lradc, LRADC_CTRL1_LRADC_IRQ_EN(5), LRADC_CTRL1);
|
|
|
+ mxs_lradc_reg_wrt(lradc, LRADC_DELAY_TRIGGER(1 << 5) |
|
|
|
+ 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 *lradc, bool valid)
|
|
|
+{
|
|
|
+ /* if it is still touched, report the sample */
|
|
|
+ if (valid && mxs_lradc_check_touch_event(lradc)) {
|
|
|
+ lradc->ts_valid = true;
|
|
|
+ mxs_lradc_report_ts_event(lradc);
|
|
|
+ }
|
|
|
+
|
|
|
+ /* if it is even still touched, continue with the next measurement */
|
|
|
+ if (mxs_lradc_check_touch_event(lradc)) {
|
|
|
+ mxs_lradc_prepare_y_pos(lradc);
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (lradc->ts_valid) {
|
|
|
+ /* signal the release */
|
|
|
+ lradc->ts_valid = false;
|
|
|
+ input_report_key(lradc->ts_input, BTN_TOUCH, 0);
|
|
|
+ input_sync(lradc->ts_input);
|
|
|
+ }
|
|
|
+
|
|
|
+ /* if it is released, wait for the next touch via IRQ */
|
|
|
+ mxs_lradc_reg_clear(lradc, LRADC_CTRL1_TOUCH_DETECT_IRQ, 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 *lradc)
|
|
|
+{
|
|
|
+ int val;
|
|
|
+
|
|
|
+ switch (lradc->cur_plate) {
|
|
|
+ case LRADC_TOUCH:
|
|
|
+ /*
|
|
|
+ * start with the Y-pos, because it uses nearly the same plate
|
|
|
+ * settings like the touch detection
|
|
|
+ */
|
|
|
+ if (mxs_lradc_check_touch_event(lradc)) {
|
|
|
+ mxs_lradc_reg_clear(lradc,
|
|
|
+ LRADC_CTRL1_TOUCH_DETECT_IRQ_EN,
|
|
|
+ LRADC_CTRL1);
|
|
|
+ mxs_lradc_prepare_y_pos(lradc);
|
|
|
+ }
|
|
|
+ mxs_lradc_reg_clear(lradc, LRADC_CTRL1_TOUCH_DETECT_IRQ,
|
|
|
+ LRADC_CTRL1);
|
|
|
+ return;
|
|
|
+
|
|
|
+ case LRADC_SAMPLE_Y:
|
|
|
+ val = mxs_lradc_read_ts_channel(lradc);
|
|
|
+ if (val < 0) {
|
|
|
+ mxs_lradc_enable_touch_detection(lradc); /* re-start */
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ lradc->ts_y_pos = val;
|
|
|
+ mxs_lradc_prepare_x_pos(lradc);
|
|
|
+ return;
|
|
|
+
|
|
|
+ case LRADC_SAMPLE_X:
|
|
|
+ val = mxs_lradc_read_ts_channel(lradc);
|
|
|
+ if (val < 0) {
|
|
|
+ mxs_lradc_enable_touch_detection(lradc); /* re-start */
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ lradc->ts_x_pos = val;
|
|
|
+ mxs_lradc_prepare_pressure(lradc);
|
|
|
+ return;
|
|
|
+
|
|
|
+ case LRADC_SAMPLE_PRESSURE:
|
|
|
+ lradc->ts_pressure =
|
|
|
+ mxs_lradc_read_ts_pressure(lradc, TS_CH_XP, TS_CH_YM);
|
|
|
+ mxs_lradc_complete_touch_event(lradc);
|
|
|
+ return;
|
|
|
+
|
|
|
+ case LRADC_SAMPLE_VALID:
|
|
|
+ val = mxs_lradc_read_ts_channel(lradc); /* ignore the value */
|
|
|
+ mxs_lradc_finish_touch_event(lradc, 1);
|
|
|
+ break;
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
/*
|
|
|
* Raw I/O operations
|
|
|
*/
|
|
@@ -262,21 +790,20 @@ static int mxs_lradc_read_raw(struct iio_dev *iio_dev,
|
|
|
* Virtual channel 0 is always used here as the others are always not
|
|
|
* used if doing raw sampling.
|
|
|
*/
|
|
|
- writel(LRADC_CTRL1_LRADC_IRQ_EN_MASK,
|
|
|
- lradc->base + LRADC_CTRL1 + STMP_OFFSET_REG_CLR);
|
|
|
- writel(0xff, lradc->base + LRADC_CTRL0 + STMP_OFFSET_REG_CLR);
|
|
|
+ if (lradc->soc == IMX28_LRADC)
|
|
|
+ mxs_lradc_reg_clear(lradc, LRADC_CTRL1_MX28_LRADC_IRQ_EN_MASK,
|
|
|
+ LRADC_CTRL1);
|
|
|
+ mxs_lradc_reg_clear(lradc, 0xff, LRADC_CTRL0);
|
|
|
|
|
|
/* 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);
|
|
|
+ mxs_lradc_reg_clear(lradc, LRADC_CTRL4_LRADCSELECT_MASK(0), LRADC_CTRL4);
|
|
|
+ mxs_lradc_reg_set(lradc, chan->channel, LRADC_CTRL4);
|
|
|
|
|
|
- writel(0, lradc->base + LRADC_CH(0));
|
|
|
+ mxs_lradc_reg_wrt(lradc, 0, LRADC_CH(0));
|
|
|
|
|
|
/* Enable the IRQ and start sampling the channel. */
|
|
|
- writel(LRADC_CTRL1_LRADC_IRQ_EN(0),
|
|
|
- lradc->base + LRADC_CTRL1 + STMP_OFFSET_REG_SET);
|
|
|
- writel(1 << 0, lradc->base + LRADC_CTRL0 + STMP_OFFSET_REG_SET);
|
|
|
+ mxs_lradc_reg_set(lradc, LRADC_CTRL1_LRADC_IRQ_EN(0), LRADC_CTRL1);
|
|
|
+ mxs_lradc_reg_set(lradc, 1 << 0, LRADC_CTRL0);
|
|
|
|
|
|
/* Wait for completion on the channel, 1 second max. */
|
|
|
ret = wait_for_completion_killable_timeout(&lradc->completion, HZ);
|
|
@@ -290,8 +817,7 @@ static int mxs_lradc_read_raw(struct iio_dev *iio_dev,
|
|
|
ret = IIO_VAL_INT;
|
|
|
|
|
|
err:
|
|
|
- writel(LRADC_CTRL1_LRADC_IRQ_EN(0),
|
|
|
- lradc->base + LRADC_CTRL1 + STMP_OFFSET_REG_CLR);
|
|
|
+ mxs_lradc_reg_clear(lradc, LRADC_CTRL1_LRADC_IRQ_EN(0), LRADC_CTRL1);
|
|
|
|
|
|
mutex_unlock(&lradc->lock);
|
|
|
|
|
@@ -303,220 +829,33 @@ 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);
|
|
|
+ mxs_lradc_enable_touch_detection(lradc);
|
|
|
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
|
-static void mxs_lradc_ts_close(struct input_dev *dev)
|
|
|
+static void mxs_lradc_disable_ts(struct mxs_lradc *lradc)
|
|
|
{
|
|
|
- struct mxs_lradc *lradc = input_get_drvdata(dev);
|
|
|
+ /* stop all interrupts from firing */
|
|
|
+ mxs_lradc_reg_clear(lradc, LRADC_CTRL1_TOUCH_DETECT_IRQ_EN |
|
|
|
+ LRADC_CTRL1_LRADC_IRQ_EN(2) | LRADC_CTRL1_LRADC_IRQ_EN(3) |
|
|
|
+ LRADC_CTRL1_LRADC_IRQ_EN(4) | LRADC_CTRL1_LRADC_IRQ_EN(5),
|
|
|
+ LRADC_CTRL1);
|
|
|
|
|
|
- /* Indicate the touchscreen is stopping. */
|
|
|
- lradc->stop_touchscreen = true;
|
|
|
- mb();
|
|
|
-
|
|
|
- /* Wait until touchscreen thread finishes any possible remnants. */
|
|
|
- cancel_work_sync(&lradc->ts_work);
|
|
|
+ /* Power-down touchscreen touch-detect circuitry. */
|
|
|
+ mxs_lradc_reg_clear(lradc, mxs_lradc_plate_mask(lradc), LRADC_CTRL0);
|
|
|
+}
|
|
|
|
|
|
- /* Disable touchscreen touch-detect IRQ. */
|
|
|
- writel(LRADC_CTRL1_TOUCH_DETECT_IRQ_EN,
|
|
|
- lradc->base + LRADC_CTRL1 + STMP_OFFSET_REG_CLR);
|
|
|
+static void mxs_lradc_ts_close(struct input_dev *dev)
|
|
|
+{
|
|
|
+ struct mxs_lradc *lradc = input_get_drvdata(dev);
|
|
|
|
|
|
- /* Power-down touchscreen touch-detect circuitry. */
|
|
|
- writel(LRADC_CTRL0_TOUCH_DETECT_ENABLE,
|
|
|
- lradc->base + LRADC_CTRL0 + STMP_OFFSET_REG_CLR);
|
|
|
+ mxs_lradc_disable_ts(lradc);
|
|
|
}
|
|
|
|
|
|
static int mxs_lradc_ts_register(struct mxs_lradc *lradc)
|
|
@@ -562,8 +901,7 @@ static void mxs_lradc_ts_unregister(struct mxs_lradc *lradc)
|
|
|
if (!lradc->use_touchscreen)
|
|
|
return;
|
|
|
|
|
|
- cancel_work_sync(&lradc->ts_work);
|
|
|
-
|
|
|
+ mxs_lradc_disable_ts(lradc);
|
|
|
input_unregister_device(lradc->ts_input);
|
|
|
}
|
|
|
|
|
@@ -576,31 +914,24 @@ static irqreturn_t mxs_lradc_handle_irq(int irq, void *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;
|
|
|
+ LRADC_CTRL1_TOUCH_DETECT_IRQ |
|
|
|
+ LRADC_CTRL1_LRADC_IRQ(2) |
|
|
|
+ LRADC_CTRL1_LRADC_IRQ(3) |
|
|
|
+ LRADC_CTRL1_LRADC_IRQ(4) |
|
|
|
+ LRADC_CTRL1_LRADC_IRQ(5);
|
|
|
|
|
|
- if (!(reg & LRADC_CTRL1_LRADC_IRQ_MASK))
|
|
|
+ if (!(reg & mxs_lradc_irq_mask(lradc)))
|
|
|
return IRQ_NONE;
|
|
|
|
|
|
- /*
|
|
|
- * 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 (lradc->use_touchscreen && (reg & ts_irq_mask))
|
|
|
+ mxs_lradc_handle_touch(lradc);
|
|
|
|
|
|
if (iio_buffer_enabled(iio))
|
|
|
iio_trigger_poll(iio->trig, iio_get_time_ns());
|
|
|
else if (reg & LRADC_CTRL1_LRADC_IRQ(0))
|
|
|
complete(&lradc->completion);
|
|
|
|
|
|
- writel(reg & LRADC_CTRL1_LRADC_IRQ_MASK,
|
|
|
- lradc->base + LRADC_CTRL1 + STMP_OFFSET_REG_CLR);
|
|
|
+ mxs_lradc_reg_clear(lradc, reg & mxs_lradc_irq_mask(lradc), LRADC_CTRL1);
|
|
|
|
|
|
return IRQ_HANDLED;
|
|
|
}
|
|
@@ -619,7 +950,7 @@ static irqreturn_t mxs_lradc_trigger_handler(int irq, void *p)
|
|
|
|
|
|
for_each_set_bit(i, iio->active_scan_mask, LRADC_MAX_TOTAL_CHANS) {
|
|
|
lradc->buffer[j] = readl(lradc->base + LRADC_CH(j));
|
|
|
- writel(chan_value, lradc->base + LRADC_CH(j));
|
|
|
+ mxs_lradc_reg_wrt(lradc, chan_value, LRADC_CH(j));
|
|
|
lradc->buffer[j] &= LRADC_CH_VALUE_MASK;
|
|
|
lradc->buffer[j] /= LRADC_DELAY_TIMER_LOOP;
|
|
|
j++;
|
|
@@ -638,7 +969,7 @@ static int mxs_lradc_configure_trigger(struct iio_trigger *trig, bool state)
|
|
|
struct mxs_lradc *lradc = iio_priv(iio);
|
|
|
const uint32_t st = state ? STMP_OFFSET_REG_SET : STMP_OFFSET_REG_CLR;
|
|
|
|
|
|
- writel(LRADC_DELAY_KICK, lradc->base + LRADC_DELAY(0) + st);
|
|
|
+ mxs_lradc_reg_wrt(lradc, LRADC_DELAY_KICK, LRADC_DELAY(0) + st);
|
|
|
|
|
|
return 0;
|
|
|
}
|
|
@@ -714,29 +1045,27 @@ static int mxs_lradc_buffer_preenable(struct iio_dev *iio)
|
|
|
if (ret < 0)
|
|
|
goto err_buf;
|
|
|
|
|
|
- writel(LRADC_CTRL1_LRADC_IRQ_EN_MASK,
|
|
|
- lradc->base + LRADC_CTRL1 + STMP_OFFSET_REG_CLR);
|
|
|
- writel(0xff, lradc->base + LRADC_CTRL0 + STMP_OFFSET_REG_CLR);
|
|
|
+ if (lradc->soc == IMX28_LRADC)
|
|
|
+ mxs_lradc_reg_clear(lradc, LRADC_CTRL1_MX28_LRADC_IRQ_EN_MASK,
|
|
|
+ LRADC_CTRL1);
|
|
|
+ mxs_lradc_reg_clear(lradc, 0xff, LRADC_CTRL0);
|
|
|
|
|
|
for_each_set_bit(chan, iio->active_scan_mask, LRADC_MAX_TOTAL_CHANS) {
|
|
|
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));
|
|
|
+ mxs_lradc_reg_wrt(lradc, chan_value, LRADC_CH(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_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,
|
|
|
- lradc->base + LRADC_DELAY(0) + STMP_OFFSET_REG_SET);
|
|
|
+ mxs_lradc_reg_clear(lradc, LRADC_DELAY_TRIGGER_LRADCS_MASK |
|
|
|
+ LRADC_DELAY_KICK, LRADC_DELAY(0));
|
|
|
+ mxs_lradc_reg_clear(lradc, ctrl4_clr, LRADC_CTRL4);
|
|
|
+ mxs_lradc_reg_set(lradc, ctrl4_set, LRADC_CTRL4);
|
|
|
+ mxs_lradc_reg_set(lradc, ctrl1_irq, LRADC_CTRL1);
|
|
|
+ mxs_lradc_reg_set(lradc, enable << LRADC_DELAY_TRIGGER_LRADCS_OFFSET,
|
|
|
+ LRADC_DELAY(0));
|
|
|
|
|
|
return 0;
|
|
|
|
|
@@ -751,12 +1080,13 @@ static int mxs_lradc_buffer_postdisable(struct iio_dev *iio)
|
|
|
{
|
|
|
struct mxs_lradc *lradc = iio_priv(iio);
|
|
|
|
|
|
- writel(LRADC_DELAY_TRIGGER_LRADCS_MASK | LRADC_DELAY_KICK,
|
|
|
- lradc->base + LRADC_DELAY(0) + STMP_OFFSET_REG_CLR);
|
|
|
+ mxs_lradc_reg_clear(lradc, LRADC_DELAY_TRIGGER_LRADCS_MASK |
|
|
|
+ LRADC_DELAY_KICK, LRADC_DELAY(0));
|
|
|
|
|
|
- writel(0xff, lradc->base + LRADC_CTRL0 + STMP_OFFSET_REG_CLR);
|
|
|
- writel(LRADC_CTRL1_LRADC_IRQ_EN_MASK,
|
|
|
- lradc->base + LRADC_CTRL1 + STMP_OFFSET_REG_CLR);
|
|
|
+ mxs_lradc_reg_clear(lradc, 0xff, LRADC_CTRL0);
|
|
|
+ if (lradc->soc == IMX28_LRADC)
|
|
|
+ mxs_lradc_reg_clear(lradc, LRADC_CTRL1_MX28_LRADC_IRQ_EN_MASK,
|
|
|
+ LRADC_CTRL1);
|
|
|
|
|
|
kfree(lradc->buffer);
|
|
|
mutex_unlock(&lradc->lock);
|
|
@@ -851,24 +1181,25 @@ static int mxs_lradc_hw_init(struct mxs_lradc *lradc)
|
|
|
return ret;
|
|
|
|
|
|
/* Configure DELAY CHANNEL 0 for generic ADC sampling. */
|
|
|
- writel(adc_cfg, lradc->base + LRADC_DELAY(0));
|
|
|
+ mxs_lradc_reg_wrt(lradc, adc_cfg, 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));
|
|
|
+ mxs_lradc_reg_wrt(lradc, 0, LRADC_DELAY(1));
|
|
|
+ mxs_lradc_reg_wrt(lradc, 0, LRADC_DELAY(2));
|
|
|
+ mxs_lradc_reg_wrt(lradc, 0, LRADC_DELAY(3));
|
|
|
|
|
|
/* Configure the touchscreen type */
|
|
|
- writel(LRADC_CTRL0_TOUCH_SCREEN_TYPE,
|
|
|
- lradc->base + LRADC_CTRL0 + STMP_OFFSET_REG_CLR);
|
|
|
+ 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) {
|
|
|
- writel(LRADC_CTRL0_TOUCH_SCREEN_TYPE,
|
|
|
- lradc->base + LRADC_CTRL0 + STMP_OFFSET_REG_SET);
|
|
|
+ if (lradc->use_touchscreen == MXS_LRADC_TOUCHSCREEN_5WIRE)
|
|
|
+ mxs_lradc_reg_set(lradc, LRADC_CTRL0_MX28_TOUCH_SCREEN_TYPE,
|
|
|
+ LRADC_CTRL0);
|
|
|
}
|
|
|
|
|
|
/* Start internal temperature sensing. */
|
|
|
- writel(0, lradc->base + LRADC_CTRL2);
|
|
|
+ mxs_lradc_reg_wrt(lradc, 0, LRADC_CTRL2);
|
|
|
|
|
|
return 0;
|
|
|
}
|
|
@@ -877,11 +1208,10 @@ static void mxs_lradc_hw_stop(struct mxs_lradc *lradc)
|
|
|
{
|
|
|
int i;
|
|
|
|
|
|
- writel(LRADC_CTRL1_LRADC_IRQ_EN_MASK,
|
|
|
- lradc->base + LRADC_CTRL1 + STMP_OFFSET_REG_CLR);
|
|
|
+ mxs_lradc_reg_clear(lradc, mxs_lradc_irq_en_mask(lradc), LRADC_CTRL1);
|
|
|
|
|
|
for (i = 0; i < LRADC_MAX_DELAY_CHANS; i++)
|
|
|
- writel(0, lradc->base + LRADC_DELAY(i));
|
|
|
+ mxs_lradc_reg_wrt(lradc, 0, LRADC_DELAY(i));
|
|
|
}
|
|
|
|
|
|
static const struct of_device_id mxs_lradc_dt_ids[] = {
|
|
@@ -891,6 +1221,52 @@ static const struct of_device_id mxs_lradc_dt_ids[] = {
|
|
|
};
|
|
|
MODULE_DEVICE_TABLE(of, mxs_lradc_dt_ids);
|
|
|
|
|
|
+static int mxs_lradc_probe_touchscreen(struct mxs_lradc *lradc,
|
|
|
+ struct device_node *lradc_node)
|
|
|
+{
|
|
|
+ int ret;
|
|
|
+ u32 ts_wires = 0, adapt;
|
|
|
+
|
|
|
+ ret = of_property_read_u32(lradc_node, "fsl,lradc-touchscreen-wires",
|
|
|
+ &ts_wires);
|
|
|
+ if (ret)
|
|
|
+ return -ENODEV; /* touchscreen feature disabled */
|
|
|
+
|
|
|
+ switch (ts_wires) {
|
|
|
+ case 4:
|
|
|
+ lradc->use_touchscreen = MXS_LRADC_TOUCHSCREEN_4WIRE;
|
|
|
+ break;
|
|
|
+ case 5:
|
|
|
+ if (lradc->soc == IMX28_LRADC) {
|
|
|
+ lradc->use_touchscreen = MXS_LRADC_TOUCHSCREEN_5WIRE;
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ /* fall through an error message for i.MX23 */
|
|
|
+ default:
|
|
|
+ dev_err(lradc->dev,
|
|
|
+ "Unsupported number of touchscreen wires (%d)\n",
|
|
|
+ ts_wires);
|
|
|
+ return -EINVAL;
|
|
|
+ }
|
|
|
+
|
|
|
+ lradc->over_sample_cnt = 4;
|
|
|
+ ret = of_property_read_u32(lradc_node, "fsl,ave-ctrl", &adapt);
|
|
|
+ if (ret == 0)
|
|
|
+ lradc->over_sample_cnt = adapt;
|
|
|
+
|
|
|
+ lradc->over_sample_delay = 2;
|
|
|
+ ret = of_property_read_u32(lradc_node, "fsl,ave-delay", &adapt);
|
|
|
+ if (ret == 0)
|
|
|
+ lradc->over_sample_delay = adapt;
|
|
|
+
|
|
|
+ lradc->settling_delay = 10;
|
|
|
+ ret = of_property_read_u32(lradc_node, "fsl,settling", &adapt);
|
|
|
+ if (ret == 0)
|
|
|
+ lradc->settling_delay = adapt;
|
|
|
+
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
static int mxs_lradc_probe(struct platform_device *pdev)
|
|
|
{
|
|
|
const struct of_device_id *of_id =
|
|
@@ -902,8 +1278,7 @@ static int mxs_lradc_probe(struct platform_device *pdev)
|
|
|
struct mxs_lradc *lradc;
|
|
|
struct iio_dev *iio;
|
|
|
struct resource *iores;
|
|
|
- uint32_t ts_wires = 0;
|
|
|
- int ret = 0;
|
|
|
+ int ret = 0, touch_ret;
|
|
|
int i;
|
|
|
|
|
|
/* Allocate the IIO device. */
|
|
@@ -914,6 +1289,7 @@ static int mxs_lradc_probe(struct platform_device *pdev)
|
|
|
}
|
|
|
|
|
|
lradc = iio_priv(iio);
|
|
|
+ lradc->soc = (enum mxs_lradc_id)of_id->data;
|
|
|
|
|
|
/* Grab the memory area */
|
|
|
iores = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
|
@@ -922,20 +1298,18 @@ static int mxs_lradc_probe(struct platform_device *pdev)
|
|
|
if (IS_ERR(lradc->base))
|
|
|
return PTR_ERR(lradc->base);
|
|
|
|
|
|
- INIT_WORK(&lradc->ts_work, mxs_lradc_ts_work);
|
|
|
+ lradc->clk = devm_clk_get(&pdev->dev, NULL);
|
|
|
+ if (IS_ERR(lradc->clk)) {
|
|
|
+ dev_err(dev, "Failed to get the delay unit clock\n");
|
|
|
+ return PTR_ERR(lradc->clk);
|
|
|
+ }
|
|
|
+ ret = clk_prepare_enable(lradc->clk);
|
|
|
+ if (ret != 0) {
|
|
|
+ dev_err(dev, "Failed to enable the delay unit clock\n");
|
|
|
+ return ret;
|
|
|
+ }
|
|
|
|
|
|
- /* 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);
|
|
|
+ touch_ret = mxs_lradc_probe_touchscreen(lradc, node);
|
|
|
|
|
|
/* Grab all IRQ sources */
|
|
|
for (i = 0; i < of_cfg->irq_count; i++) {
|
|
@@ -979,9 +1353,11 @@ static int mxs_lradc_probe(struct platform_device *pdev)
|
|
|
goto err_dev;
|
|
|
|
|
|
/* Register the touchscreen input device. */
|
|
|
- ret = mxs_lradc_ts_register(lradc);
|
|
|
- if (ret)
|
|
|
- goto err_ts_register;
|
|
|
+ if (touch_ret == 0) {
|
|
|
+ ret = mxs_lradc_ts_register(lradc);
|
|
|
+ if (ret)
|
|
|
+ goto err_ts_register;
|
|
|
+ }
|
|
|
|
|
|
/* Register IIO device. */
|
|
|
ret = iio_device_register(iio);
|
|
@@ -1014,6 +1390,7 @@ static int mxs_lradc_remove(struct platform_device *pdev)
|
|
|
mxs_lradc_trigger_remove(iio);
|
|
|
iio_triggered_buffer_cleanup(iio);
|
|
|
|
|
|
+ clk_disable_unprepare(lradc->clk);
|
|
|
return 0;
|
|
|
}
|
|
|
|