xref: /openbmc/linux/drivers/input/misc/ad714x.c (revision c0a150ee)
180503b23SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later
231a62963SBryan Wu /*
36c04d7b3SBarry Song  * AD714X CapTouch Programmable Controller driver supporting AD7142/3/7/8/7A
431a62963SBryan Wu  *
59eff794bSMichael Hennerich  * Copyright 2009-2011 Analog Devices Inc.
631a62963SBryan Wu  */
731a62963SBryan Wu 
831a62963SBryan Wu #include <linux/device.h>
931a62963SBryan Wu #include <linux/input.h>
1031a62963SBryan Wu #include <linux/interrupt.h>
1131a62963SBryan Wu #include <linux/slab.h>
1231a62963SBryan Wu #include <linux/input/ad714x.h>
13d2d8442dSPaul Gortmaker #include <linux/module.h>
1431a62963SBryan Wu #include "ad714x.h"
1531a62963SBryan Wu 
1631a62963SBryan Wu #define AD714X_PWR_CTRL           0x0
1731a62963SBryan Wu #define AD714X_STG_CAL_EN_REG     0x1
1831a62963SBryan Wu #define AD714X_AMB_COMP_CTRL0_REG 0x2
1931a62963SBryan Wu #define AD714X_PARTID_REG         0x17
2031a62963SBryan Wu #define AD7142_PARTID             0xE620
216c04d7b3SBarry Song #define AD7143_PARTID             0xE630
226c04d7b3SBarry Song #define AD7147_PARTID             0x1470
236c04d7b3SBarry Song #define AD7148_PARTID             0x1480
2431a62963SBryan Wu #define AD714X_STAGECFG_REG       0x80
2531a62963SBryan Wu #define AD714X_SYSCFG_REG         0x0
2631a62963SBryan Wu 
2731a62963SBryan Wu #define STG_LOW_INT_EN_REG     0x5
2831a62963SBryan Wu #define STG_HIGH_INT_EN_REG    0x6
2931a62963SBryan Wu #define STG_COM_INT_EN_REG     0x7
3031a62963SBryan Wu #define STG_LOW_INT_STA_REG    0x8
3131a62963SBryan Wu #define STG_HIGH_INT_STA_REG   0x9
3231a62963SBryan Wu #define STG_COM_INT_STA_REG    0xA
3331a62963SBryan Wu 
3431a62963SBryan Wu #define CDC_RESULT_S0          0xB
3531a62963SBryan Wu #define CDC_RESULT_S1          0xC
3631a62963SBryan Wu #define CDC_RESULT_S2          0xD
3731a62963SBryan Wu #define CDC_RESULT_S3          0xE
3831a62963SBryan Wu #define CDC_RESULT_S4          0xF
3931a62963SBryan Wu #define CDC_RESULT_S5          0x10
4031a62963SBryan Wu #define CDC_RESULT_S6          0x11
4131a62963SBryan Wu #define CDC_RESULT_S7          0x12
4231a62963SBryan Wu #define CDC_RESULT_S8          0x13
4331a62963SBryan Wu #define CDC_RESULT_S9          0x14
4431a62963SBryan Wu #define CDC_RESULT_S10         0x15
4531a62963SBryan Wu #define CDC_RESULT_S11         0x16
4631a62963SBryan Wu 
4731a62963SBryan Wu #define STAGE0_AMBIENT		0xF1
4831a62963SBryan Wu #define STAGE1_AMBIENT		0x115
4931a62963SBryan Wu #define STAGE2_AMBIENT		0x139
5031a62963SBryan Wu #define STAGE3_AMBIENT		0x15D
5131a62963SBryan Wu #define STAGE4_AMBIENT		0x181
5231a62963SBryan Wu #define STAGE5_AMBIENT		0x1A5
5331a62963SBryan Wu #define STAGE6_AMBIENT		0x1C9
5431a62963SBryan Wu #define STAGE7_AMBIENT		0x1ED
5531a62963SBryan Wu #define STAGE8_AMBIENT		0x211
5631a62963SBryan Wu #define STAGE9_AMBIENT		0x234
5731a62963SBryan Wu #define STAGE10_AMBIENT		0x259
5831a62963SBryan Wu #define STAGE11_AMBIENT		0x27D
5931a62963SBryan Wu 
6031a62963SBryan Wu #define PER_STAGE_REG_NUM      36
6131a62963SBryan Wu #define STAGE_CFGREG_NUM       8
6231a62963SBryan Wu #define SYS_CFGREG_NUM         8
6331a62963SBryan Wu 
6431a62963SBryan Wu /*
6531a62963SBryan Wu  * driver information which will be used to maintain the software flow
6631a62963SBryan Wu  */
6731a62963SBryan Wu enum ad714x_device_state { IDLE, JITTER, ACTIVE, SPACE };
6831a62963SBryan Wu 
6931a62963SBryan Wu struct ad714x_slider_drv {
7031a62963SBryan Wu 	int highest_stage;
7131a62963SBryan Wu 	int abs_pos;
7231a62963SBryan Wu 	int flt_pos;
7331a62963SBryan Wu 	enum ad714x_device_state state;
7431a62963SBryan Wu 	struct input_dev *input;
7531a62963SBryan Wu };
7631a62963SBryan Wu 
7731a62963SBryan Wu struct ad714x_wheel_drv {
7831a62963SBryan Wu 	int abs_pos;
7931a62963SBryan Wu 	int flt_pos;
8031a62963SBryan Wu 	int pre_highest_stage;
8131a62963SBryan Wu 	int highest_stage;
8231a62963SBryan Wu 	enum ad714x_device_state state;
8331a62963SBryan Wu 	struct input_dev *input;
8431a62963SBryan Wu };
8531a62963SBryan Wu 
8631a62963SBryan Wu struct ad714x_touchpad_drv {
8731a62963SBryan Wu 	int x_highest_stage;
8831a62963SBryan Wu 	int x_flt_pos;
8931a62963SBryan Wu 	int x_abs_pos;
9031a62963SBryan Wu 	int y_highest_stage;
9131a62963SBryan Wu 	int y_flt_pos;
9231a62963SBryan Wu 	int y_abs_pos;
9331a62963SBryan Wu 	int left_ep;
9431a62963SBryan Wu 	int left_ep_val;
9531a62963SBryan Wu 	int right_ep;
9631a62963SBryan Wu 	int right_ep_val;
9731a62963SBryan Wu 	int top_ep;
9831a62963SBryan Wu 	int top_ep_val;
9931a62963SBryan Wu 	int bottom_ep;
10031a62963SBryan Wu 	int bottom_ep_val;
10131a62963SBryan Wu 	enum ad714x_device_state state;
10231a62963SBryan Wu 	struct input_dev *input;
10331a62963SBryan Wu };
10431a62963SBryan Wu 
10531a62963SBryan Wu struct ad714x_button_drv {
10631a62963SBryan Wu 	enum ad714x_device_state state;
10731a62963SBryan Wu 	/*
10831a62963SBryan Wu 	 * Unlike slider/wheel/touchpad, all buttons point to
10931a62963SBryan Wu 	 * same input_dev instance
11031a62963SBryan Wu 	 */
11131a62963SBryan Wu 	struct input_dev *input;
11231a62963SBryan Wu };
11331a62963SBryan Wu 
11431a62963SBryan Wu struct ad714x_driver_data {
11531a62963SBryan Wu 	struct ad714x_slider_drv *slider;
11631a62963SBryan Wu 	struct ad714x_wheel_drv *wheel;
11731a62963SBryan Wu 	struct ad714x_touchpad_drv *touchpad;
11831a62963SBryan Wu 	struct ad714x_button_drv *button;
11931a62963SBryan Wu };
12031a62963SBryan Wu 
12131a62963SBryan Wu /*
12231a62963SBryan Wu  * information to integrate all things which will be private data
12331a62963SBryan Wu  * of spi/i2c device
12431a62963SBryan Wu  */
1259eff794bSMichael Hennerich 
ad714x_use_com_int(struct ad714x_chip * ad714x,int start_stage,int end_stage)12631a62963SBryan Wu static void ad714x_use_com_int(struct ad714x_chip *ad714x,
12731a62963SBryan Wu 				int start_stage, int end_stage)
12831a62963SBryan Wu {
12931a62963SBryan Wu 	unsigned short data;
13031a62963SBryan Wu 	unsigned short mask;
13131a62963SBryan Wu 
132e223cc7eSMichael Hennerich 	mask = ((1 << (end_stage + 1)) - 1) - ((1 << start_stage) - 1);
13331a62963SBryan Wu 
1349eff794bSMichael Hennerich 	ad714x->read(ad714x, STG_COM_INT_EN_REG, &data, 1);
135e223cc7eSMichael Hennerich 	data |= 1 << end_stage;
136c0409febSDmitry Torokhov 	ad714x->write(ad714x, STG_COM_INT_EN_REG, data);
13731a62963SBryan Wu 
1389eff794bSMichael Hennerich 	ad714x->read(ad714x, STG_HIGH_INT_EN_REG, &data, 1);
13931a62963SBryan Wu 	data &= ~mask;
140c0409febSDmitry Torokhov 	ad714x->write(ad714x, STG_HIGH_INT_EN_REG, data);
14131a62963SBryan Wu }
14231a62963SBryan Wu 
ad714x_use_thr_int(struct ad714x_chip * ad714x,int start_stage,int end_stage)14331a62963SBryan Wu static void ad714x_use_thr_int(struct ad714x_chip *ad714x,
14431a62963SBryan Wu 				int start_stage, int end_stage)
14531a62963SBryan Wu {
14631a62963SBryan Wu 	unsigned short data;
14731a62963SBryan Wu 	unsigned short mask;
14831a62963SBryan Wu 
149e223cc7eSMichael Hennerich 	mask = ((1 << (end_stage + 1)) - 1) - ((1 << start_stage) - 1);
15031a62963SBryan Wu 
1519eff794bSMichael Hennerich 	ad714x->read(ad714x, STG_COM_INT_EN_REG, &data, 1);
152e223cc7eSMichael Hennerich 	data &= ~(1 << end_stage);
153c0409febSDmitry Torokhov 	ad714x->write(ad714x, STG_COM_INT_EN_REG, data);
15431a62963SBryan Wu 
1559eff794bSMichael Hennerich 	ad714x->read(ad714x, STG_HIGH_INT_EN_REG, &data, 1);
15631a62963SBryan Wu 	data |= mask;
157c0409febSDmitry Torokhov 	ad714x->write(ad714x, STG_HIGH_INT_EN_REG, data);
15831a62963SBryan Wu }
15931a62963SBryan Wu 
ad714x_cal_highest_stage(struct ad714x_chip * ad714x,int start_stage,int end_stage)16031a62963SBryan Wu static int ad714x_cal_highest_stage(struct ad714x_chip *ad714x,
16131a62963SBryan Wu 					int start_stage, int end_stage)
16231a62963SBryan Wu {
16331a62963SBryan Wu 	int max_res = 0;
16431a62963SBryan Wu 	int max_idx = 0;
16531a62963SBryan Wu 	int i;
16631a62963SBryan Wu 
16731a62963SBryan Wu 	for (i = start_stage; i <= end_stage; i++) {
16831a62963SBryan Wu 		if (ad714x->sensor_val[i] > max_res) {
16931a62963SBryan Wu 			max_res = ad714x->sensor_val[i];
17031a62963SBryan Wu 			max_idx = i;
17131a62963SBryan Wu 		}
17231a62963SBryan Wu 	}
17331a62963SBryan Wu 
17431a62963SBryan Wu 	return max_idx;
17531a62963SBryan Wu }
17631a62963SBryan Wu 
ad714x_cal_abs_pos(struct ad714x_chip * ad714x,int start_stage,int end_stage,int highest_stage,int max_coord)17731a62963SBryan Wu static int ad714x_cal_abs_pos(struct ad714x_chip *ad714x,
17831a62963SBryan Wu 				int start_stage, int end_stage,
17931a62963SBryan Wu 				int highest_stage, int max_coord)
18031a62963SBryan Wu {
18131a62963SBryan Wu 	int a_param, b_param;
18231a62963SBryan Wu 
18331a62963SBryan Wu 	if (highest_stage == start_stage) {
18431a62963SBryan Wu 		a_param = ad714x->sensor_val[start_stage + 1];
18531a62963SBryan Wu 		b_param = ad714x->sensor_val[start_stage] +
18631a62963SBryan Wu 			ad714x->sensor_val[start_stage + 1];
18731a62963SBryan Wu 	} else if (highest_stage == end_stage) {
18831a62963SBryan Wu 		a_param = ad714x->sensor_val[end_stage] *
18931a62963SBryan Wu 			(end_stage - start_stage) +
19031a62963SBryan Wu 			ad714x->sensor_val[end_stage - 1] *
19131a62963SBryan Wu 			(end_stage - start_stage - 1);
19231a62963SBryan Wu 		b_param = ad714x->sensor_val[end_stage] +
19331a62963SBryan Wu 			ad714x->sensor_val[end_stage - 1];
19431a62963SBryan Wu 	} else {
19531a62963SBryan Wu 		a_param = ad714x->sensor_val[highest_stage] *
19631a62963SBryan Wu 			(highest_stage - start_stage) +
19731a62963SBryan Wu 			ad714x->sensor_val[highest_stage - 1] *
19831a62963SBryan Wu 			(highest_stage - start_stage - 1) +
19931a62963SBryan Wu 			ad714x->sensor_val[highest_stage + 1] *
20031a62963SBryan Wu 			(highest_stage - start_stage + 1);
20131a62963SBryan Wu 		b_param = ad714x->sensor_val[highest_stage] +
20231a62963SBryan Wu 			ad714x->sensor_val[highest_stage - 1] +
20331a62963SBryan Wu 			ad714x->sensor_val[highest_stage + 1];
20431a62963SBryan Wu 	}
20531a62963SBryan Wu 
20631a62963SBryan Wu 	return (max_coord / (end_stage - start_stage)) * a_param / b_param;
20731a62963SBryan Wu }
20831a62963SBryan Wu 
20931a62963SBryan Wu /*
21031a62963SBryan Wu  * One button can connect to multi positive and negative of CDCs
21131a62963SBryan Wu  * Multi-buttons can connect to same positive/negative of one CDC
21231a62963SBryan Wu  */
ad714x_button_state_machine(struct ad714x_chip * ad714x,int idx)21331a62963SBryan Wu static void ad714x_button_state_machine(struct ad714x_chip *ad714x, int idx)
21431a62963SBryan Wu {
21531a62963SBryan Wu 	struct ad714x_button_plat *hw = &ad714x->hw->button[idx];
21631a62963SBryan Wu 	struct ad714x_button_drv *sw = &ad714x->sw->button[idx];
21731a62963SBryan Wu 
21831a62963SBryan Wu 	switch (sw->state) {
21931a62963SBryan Wu 	case IDLE:
22031a62963SBryan Wu 		if (((ad714x->h_state & hw->h_mask) == hw->h_mask) &&
22131a62963SBryan Wu 		    ((ad714x->l_state & hw->l_mask) == hw->l_mask)) {
22231a62963SBryan Wu 			dev_dbg(ad714x->dev, "button %d touched\n", idx);
22331a62963SBryan Wu 			input_report_key(sw->input, hw->keycode, 1);
22431a62963SBryan Wu 			input_sync(sw->input);
22531a62963SBryan Wu 			sw->state = ACTIVE;
22631a62963SBryan Wu 		}
22731a62963SBryan Wu 		break;
22831a62963SBryan Wu 
22931a62963SBryan Wu 	case ACTIVE:
23031a62963SBryan Wu 		if (((ad714x->h_state & hw->h_mask) != hw->h_mask) ||
23131a62963SBryan Wu 		    ((ad714x->l_state & hw->l_mask) != hw->l_mask)) {
23231a62963SBryan Wu 			dev_dbg(ad714x->dev, "button %d released\n", idx);
23331a62963SBryan Wu 			input_report_key(sw->input, hw->keycode, 0);
23431a62963SBryan Wu 			input_sync(sw->input);
23531a62963SBryan Wu 			sw->state = IDLE;
23631a62963SBryan Wu 		}
23731a62963SBryan Wu 		break;
23831a62963SBryan Wu 
23931a62963SBryan Wu 	default:
24031a62963SBryan Wu 		break;
24131a62963SBryan Wu 	}
24231a62963SBryan Wu }
24331a62963SBryan Wu 
24431a62963SBryan Wu /*
24531a62963SBryan Wu  * The response of a sensor is defined by the absolute number of codes
24631a62963SBryan Wu  * between the current CDC value and the ambient value.
24731a62963SBryan Wu  */
ad714x_slider_cal_sensor_val(struct ad714x_chip * ad714x,int idx)24831a62963SBryan Wu static void ad714x_slider_cal_sensor_val(struct ad714x_chip *ad714x, int idx)
24931a62963SBryan Wu {
25031a62963SBryan Wu 	struct ad714x_slider_plat *hw = &ad714x->hw->slider[idx];
25131a62963SBryan Wu 	int i;
25231a62963SBryan Wu 
2539eff794bSMichael Hennerich 	ad714x->read(ad714x, CDC_RESULT_S0 + hw->start_stage,
2549eff794bSMichael Hennerich 			&ad714x->adc_reg[hw->start_stage],
2559eff794bSMichael Hennerich 			hw->end_stage - hw->start_stage + 1);
25631a62963SBryan Wu 
2579eff794bSMichael Hennerich 	for (i = hw->start_stage; i <= hw->end_stage; i++) {
2589eff794bSMichael Hennerich 		ad714x->read(ad714x, STAGE0_AMBIENT + i * PER_STAGE_REG_NUM,
2599eff794bSMichael Hennerich 				&ad714x->amb_reg[i], 1);
2609eff794bSMichael Hennerich 
2619eff794bSMichael Hennerich 		ad714x->sensor_val[i] =
2629eff794bSMichael Hennerich 			abs(ad714x->adc_reg[i] - ad714x->amb_reg[i]);
26331a62963SBryan Wu 	}
26431a62963SBryan Wu }
26531a62963SBryan Wu 
ad714x_slider_cal_highest_stage(struct ad714x_chip * ad714x,int idx)26631a62963SBryan Wu static void ad714x_slider_cal_highest_stage(struct ad714x_chip *ad714x, int idx)
26731a62963SBryan Wu {
26831a62963SBryan Wu 	struct ad714x_slider_plat *hw = &ad714x->hw->slider[idx];
26931a62963SBryan Wu 	struct ad714x_slider_drv *sw = &ad714x->sw->slider[idx];
27031a62963SBryan Wu 
27131a62963SBryan Wu 	sw->highest_stage = ad714x_cal_highest_stage(ad714x, hw->start_stage,
27231a62963SBryan Wu 			hw->end_stage);
27331a62963SBryan Wu 
27431a62963SBryan Wu 	dev_dbg(ad714x->dev, "slider %d highest_stage:%d\n", idx,
27531a62963SBryan Wu 		sw->highest_stage);
27631a62963SBryan Wu }
27731a62963SBryan Wu 
27831a62963SBryan Wu /*
27931a62963SBryan Wu  * The formulae are very straight forward. It uses the sensor with the
28031a62963SBryan Wu  * highest response and the 2 adjacent ones.
28131a62963SBryan Wu  * When Sensor 0 has the highest response, only sensor 0 and sensor 1
28231a62963SBryan Wu  * are used in the calculations. Similarly when the last sensor has the
28331a62963SBryan Wu  * highest response, only the last sensor and the second last sensors
28431a62963SBryan Wu  * are used in the calculations.
28531a62963SBryan Wu  *
28631a62963SBryan Wu  * For i= idx_of_peak_Sensor-1 to i= idx_of_peak_Sensor+1
28731a62963SBryan Wu  *         v += Sensor response(i)*i
28831a62963SBryan Wu  *         w += Sensor response(i)
28931a62963SBryan Wu  * POS=(Number_of_Positions_Wanted/(Number_of_Sensors_Used-1)) *(v/w)
29031a62963SBryan Wu  */
ad714x_slider_cal_abs_pos(struct ad714x_chip * ad714x,int idx)29131a62963SBryan Wu static void ad714x_slider_cal_abs_pos(struct ad714x_chip *ad714x, int idx)
29231a62963SBryan Wu {
29331a62963SBryan Wu 	struct ad714x_slider_plat *hw = &ad714x->hw->slider[idx];
29431a62963SBryan Wu 	struct ad714x_slider_drv *sw = &ad714x->sw->slider[idx];
29531a62963SBryan Wu 
29631a62963SBryan Wu 	sw->abs_pos = ad714x_cal_abs_pos(ad714x, hw->start_stage, hw->end_stage,
29731a62963SBryan Wu 		sw->highest_stage, hw->max_coord);
29831a62963SBryan Wu 
29931a62963SBryan Wu 	dev_dbg(ad714x->dev, "slider %d absolute position:%d\n", idx,
30031a62963SBryan Wu 		sw->abs_pos);
30131a62963SBryan Wu }
30231a62963SBryan Wu 
30331a62963SBryan Wu /*
30431a62963SBryan Wu  * To minimise the Impact of the noise on the algorithm, ADI developed a
30531a62963SBryan Wu  * routine that filters the CDC results after they have been read by the
30631a62963SBryan Wu  * host processor.
30731a62963SBryan Wu  * The filter used is an Infinite Input Response(IIR) filter implemented
30831a62963SBryan Wu  * in firmware and attenuates the noise on the CDC results after they've
30931a62963SBryan Wu  * been read by the host processor.
31031a62963SBryan Wu  * Filtered_CDC_result = (Filtered_CDC_result * (10 - Coefficient) +
31131a62963SBryan Wu  *				Latest_CDC_result * Coefficient)/10
31231a62963SBryan Wu  */
ad714x_slider_cal_flt_pos(struct ad714x_chip * ad714x,int idx)31331a62963SBryan Wu static void ad714x_slider_cal_flt_pos(struct ad714x_chip *ad714x, int idx)
31431a62963SBryan Wu {
31531a62963SBryan Wu 	struct ad714x_slider_drv *sw = &ad714x->sw->slider[idx];
31631a62963SBryan Wu 
31731a62963SBryan Wu 	sw->flt_pos = (sw->flt_pos * (10 - 4) +
31831a62963SBryan Wu 			sw->abs_pos * 4)/10;
31931a62963SBryan Wu 
32031a62963SBryan Wu 	dev_dbg(ad714x->dev, "slider %d filter position:%d\n", idx,
32131a62963SBryan Wu 		sw->flt_pos);
32231a62963SBryan Wu }
32331a62963SBryan Wu 
ad714x_slider_use_com_int(struct ad714x_chip * ad714x,int idx)32431a62963SBryan Wu static void ad714x_slider_use_com_int(struct ad714x_chip *ad714x, int idx)
32531a62963SBryan Wu {
32631a62963SBryan Wu 	struct ad714x_slider_plat *hw = &ad714x->hw->slider[idx];
32731a62963SBryan Wu 
32831a62963SBryan Wu 	ad714x_use_com_int(ad714x, hw->start_stage, hw->end_stage);
32931a62963SBryan Wu }
33031a62963SBryan Wu 
ad714x_slider_use_thr_int(struct ad714x_chip * ad714x,int idx)33131a62963SBryan Wu static void ad714x_slider_use_thr_int(struct ad714x_chip *ad714x, int idx)
33231a62963SBryan Wu {
33331a62963SBryan Wu 	struct ad714x_slider_plat *hw = &ad714x->hw->slider[idx];
33431a62963SBryan Wu 
33531a62963SBryan Wu 	ad714x_use_thr_int(ad714x, hw->start_stage, hw->end_stage);
33631a62963SBryan Wu }
33731a62963SBryan Wu 
ad714x_slider_state_machine(struct ad714x_chip * ad714x,int idx)33831a62963SBryan Wu static void ad714x_slider_state_machine(struct ad714x_chip *ad714x, int idx)
33931a62963SBryan Wu {
34031a62963SBryan Wu 	struct ad714x_slider_plat *hw = &ad714x->hw->slider[idx];
34131a62963SBryan Wu 	struct ad714x_slider_drv *sw = &ad714x->sw->slider[idx];
34231a62963SBryan Wu 	unsigned short h_state, c_state;
34331a62963SBryan Wu 	unsigned short mask;
34431a62963SBryan Wu 
34531a62963SBryan Wu 	mask = ((1 << (hw->end_stage + 1)) - 1) - ((1 << hw->start_stage) - 1);
34631a62963SBryan Wu 
34731a62963SBryan Wu 	h_state = ad714x->h_state & mask;
34831a62963SBryan Wu 	c_state = ad714x->c_state & mask;
34931a62963SBryan Wu 
35031a62963SBryan Wu 	switch (sw->state) {
35131a62963SBryan Wu 	case IDLE:
35231a62963SBryan Wu 		if (h_state) {
35331a62963SBryan Wu 			sw->state = JITTER;
35431a62963SBryan Wu 			/* In End of Conversion interrupt mode, the AD714X
35531a62963SBryan Wu 			 * continuously generates hardware interrupts.
35631a62963SBryan Wu 			 */
35731a62963SBryan Wu 			ad714x_slider_use_com_int(ad714x, idx);
35831a62963SBryan Wu 			dev_dbg(ad714x->dev, "slider %d touched\n", idx);
35931a62963SBryan Wu 		}
36031a62963SBryan Wu 		break;
36131a62963SBryan Wu 
36231a62963SBryan Wu 	case JITTER:
36331a62963SBryan Wu 		if (c_state == mask) {
36431a62963SBryan Wu 			ad714x_slider_cal_sensor_val(ad714x, idx);
36531a62963SBryan Wu 			ad714x_slider_cal_highest_stage(ad714x, idx);
36631a62963SBryan Wu 			ad714x_slider_cal_abs_pos(ad714x, idx);
36731a62963SBryan Wu 			sw->flt_pos = sw->abs_pos;
36831a62963SBryan Wu 			sw->state = ACTIVE;
36931a62963SBryan Wu 		}
37031a62963SBryan Wu 		break;
37131a62963SBryan Wu 
37231a62963SBryan Wu 	case ACTIVE:
37331a62963SBryan Wu 		if (c_state == mask) {
37431a62963SBryan Wu 			if (h_state) {
37531a62963SBryan Wu 				ad714x_slider_cal_sensor_val(ad714x, idx);
37631a62963SBryan Wu 				ad714x_slider_cal_highest_stage(ad714x, idx);
37731a62963SBryan Wu 				ad714x_slider_cal_abs_pos(ad714x, idx);
37831a62963SBryan Wu 				ad714x_slider_cal_flt_pos(ad714x, idx);
37931a62963SBryan Wu 				input_report_abs(sw->input, ABS_X, sw->flt_pos);
38031a62963SBryan Wu 				input_report_key(sw->input, BTN_TOUCH, 1);
38131a62963SBryan Wu 			} else {
38231a62963SBryan Wu 				/* When the user lifts off the sensor, configure
38331a62963SBryan Wu 				 * the AD714X back to threshold interrupt mode.
38431a62963SBryan Wu 				 */
38531a62963SBryan Wu 				ad714x_slider_use_thr_int(ad714x, idx);
38631a62963SBryan Wu 				sw->state = IDLE;
38731a62963SBryan Wu 				input_report_key(sw->input, BTN_TOUCH, 0);
38831a62963SBryan Wu 				dev_dbg(ad714x->dev, "slider %d released\n",
38931a62963SBryan Wu 					idx);
39031a62963SBryan Wu 			}
39131a62963SBryan Wu 			input_sync(sw->input);
39231a62963SBryan Wu 		}
39331a62963SBryan Wu 		break;
39431a62963SBryan Wu 
39531a62963SBryan Wu 	default:
39631a62963SBryan Wu 		break;
39731a62963SBryan Wu 	}
39831a62963SBryan Wu }
39931a62963SBryan Wu 
40031a62963SBryan Wu /*
40131a62963SBryan Wu  * When the scroll wheel is activated, we compute the absolute position based
40231a62963SBryan Wu  * on the sensor values. To calculate the position, we first determine the
40331a62963SBryan Wu  * sensor that has the greatest response among the 8 sensors that constitutes
40431a62963SBryan Wu  * the scrollwheel. Then we determined the 2 sensors on either sides of the
40531a62963SBryan Wu  * sensor with the highest response and we apply weights to these sensors.
40631a62963SBryan Wu  */
ad714x_wheel_cal_highest_stage(struct ad714x_chip * ad714x,int idx)40731a62963SBryan Wu static void ad714x_wheel_cal_highest_stage(struct ad714x_chip *ad714x, int idx)
40831a62963SBryan Wu {
40931a62963SBryan Wu 	struct ad714x_wheel_plat *hw = &ad714x->hw->wheel[idx];
41031a62963SBryan Wu 	struct ad714x_wheel_drv *sw = &ad714x->sw->wheel[idx];
41131a62963SBryan Wu 
41231a62963SBryan Wu 	sw->pre_highest_stage = sw->highest_stage;
41331a62963SBryan Wu 	sw->highest_stage = ad714x_cal_highest_stage(ad714x, hw->start_stage,
41431a62963SBryan Wu 			hw->end_stage);
41531a62963SBryan Wu 
41631a62963SBryan Wu 	dev_dbg(ad714x->dev, "wheel %d highest_stage:%d\n", idx,
41731a62963SBryan Wu 		sw->highest_stage);
41831a62963SBryan Wu }
41931a62963SBryan Wu 
ad714x_wheel_cal_sensor_val(struct ad714x_chip * ad714x,int idx)42031a62963SBryan Wu static void ad714x_wheel_cal_sensor_val(struct ad714x_chip *ad714x, int idx)
42131a62963SBryan Wu {
42231a62963SBryan Wu 	struct ad714x_wheel_plat *hw = &ad714x->hw->wheel[idx];
42331a62963SBryan Wu 	int i;
42431a62963SBryan Wu 
4259eff794bSMichael Hennerich 	ad714x->read(ad714x, CDC_RESULT_S0 + hw->start_stage,
4269eff794bSMichael Hennerich 			&ad714x->adc_reg[hw->start_stage],
4279eff794bSMichael Hennerich 			hw->end_stage - hw->start_stage + 1);
4289eff794bSMichael Hennerich 
42931a62963SBryan Wu 	for (i = hw->start_stage; i <= hw->end_stage; i++) {
430c0409febSDmitry Torokhov 		ad714x->read(ad714x, STAGE0_AMBIENT + i * PER_STAGE_REG_NUM,
4319eff794bSMichael Hennerich 				&ad714x->amb_reg[i], 1);
43231a62963SBryan Wu 		if (ad714x->adc_reg[i] > ad714x->amb_reg[i])
4339eff794bSMichael Hennerich 			ad714x->sensor_val[i] =
4349eff794bSMichael Hennerich 				ad714x->adc_reg[i] - ad714x->amb_reg[i];
43531a62963SBryan Wu 		else
43631a62963SBryan Wu 			ad714x->sensor_val[i] = 0;
43731a62963SBryan Wu 	}
43831a62963SBryan Wu }
43931a62963SBryan Wu 
44031a62963SBryan Wu /*
44131a62963SBryan Wu  * When the scroll wheel is activated, we compute the absolute position based
44231a62963SBryan Wu  * on the sensor values. To calculate the position, we first determine the
443f1e430e6SMichael Hennerich  * sensor that has the greatest response among the sensors that constitutes
444f1e430e6SMichael Hennerich  * the scrollwheel. Then we determined the sensors on either sides of the
44531a62963SBryan Wu  * sensor with the highest response and we apply weights to these sensors. The
446f1e430e6SMichael Hennerich  * result of this computation gives us the mean value.
44731a62963SBryan Wu  */
44831a62963SBryan Wu 
ad714x_wheel_cal_abs_pos(struct ad714x_chip * ad714x,int idx)44931a62963SBryan Wu static void ad714x_wheel_cal_abs_pos(struct ad714x_chip *ad714x, int idx)
45031a62963SBryan Wu {
45131a62963SBryan Wu 	struct ad714x_wheel_plat *hw = &ad714x->hw->wheel[idx];
45231a62963SBryan Wu 	struct ad714x_wheel_drv *sw = &ad714x->sw->wheel[idx];
45331a62963SBryan Wu 	int stage_num = hw->end_stage - hw->start_stage + 1;
454f1e430e6SMichael Hennerich 	int first_before, highest, first_after;
45531a62963SBryan Wu 	int a_param, b_param;
45631a62963SBryan Wu 
45731a62963SBryan Wu 	first_before = (sw->highest_stage + stage_num - 1) % stage_num;
45831a62963SBryan Wu 	highest = sw->highest_stage;
45931a62963SBryan Wu 	first_after = (sw->highest_stage + stage_num + 1) % stage_num;
46031a62963SBryan Wu 
461f1e430e6SMichael Hennerich 	a_param = ad714x->sensor_val[highest] *
462f1e430e6SMichael Hennerich 		(highest - hw->start_stage) +
46331a62963SBryan Wu 		ad714x->sensor_val[first_before] *
464f1e430e6SMichael Hennerich 		(highest - hw->start_stage - 1) +
46531a62963SBryan Wu 		ad714x->sensor_val[first_after] *
466f1e430e6SMichael Hennerich 		(highest - hw->start_stage + 1);
467f1e430e6SMichael Hennerich 	b_param = ad714x->sensor_val[highest] +
46831a62963SBryan Wu 		ad714x->sensor_val[first_before] +
469f1e430e6SMichael Hennerich 		ad714x->sensor_val[first_after];
47031a62963SBryan Wu 
471f1e430e6SMichael Hennerich 	sw->abs_pos = ((hw->max_coord / (hw->end_stage - hw->start_stage)) *
472f1e430e6SMichael Hennerich 			a_param) / b_param;
47331a62963SBryan Wu 
47431a62963SBryan Wu 	if (sw->abs_pos > hw->max_coord)
47531a62963SBryan Wu 		sw->abs_pos = hw->max_coord;
476f1e430e6SMichael Hennerich 	else if (sw->abs_pos < 0)
477f1e430e6SMichael Hennerich 		sw->abs_pos = 0;
47831a62963SBryan Wu }
47931a62963SBryan Wu 
ad714x_wheel_cal_flt_pos(struct ad714x_chip * ad714x,int idx)48031a62963SBryan Wu static void ad714x_wheel_cal_flt_pos(struct ad714x_chip *ad714x, int idx)
48131a62963SBryan Wu {
48231a62963SBryan Wu 	struct ad714x_wheel_plat *hw = &ad714x->hw->wheel[idx];
48331a62963SBryan Wu 	struct ad714x_wheel_drv *sw = &ad714x->sw->wheel[idx];
48431a62963SBryan Wu 	if (((sw->pre_highest_stage == hw->end_stage) &&
48531a62963SBryan Wu 			(sw->highest_stage == hw->start_stage)) ||
48631a62963SBryan Wu 	    ((sw->pre_highest_stage == hw->start_stage) &&
48731a62963SBryan Wu 			(sw->highest_stage == hw->end_stage)))
48831a62963SBryan Wu 		sw->flt_pos = sw->abs_pos;
48931a62963SBryan Wu 	else
49031a62963SBryan Wu 		sw->flt_pos = ((sw->flt_pos * 30) + (sw->abs_pos * 71)) / 100;
49131a62963SBryan Wu 
49231a62963SBryan Wu 	if (sw->flt_pos > hw->max_coord)
49331a62963SBryan Wu 		sw->flt_pos = hw->max_coord;
49431a62963SBryan Wu }
49531a62963SBryan Wu 
ad714x_wheel_use_com_int(struct ad714x_chip * ad714x,int idx)49631a62963SBryan Wu static void ad714x_wheel_use_com_int(struct ad714x_chip *ad714x, int idx)
49731a62963SBryan Wu {
49831a62963SBryan Wu 	struct ad714x_wheel_plat *hw = &ad714x->hw->wheel[idx];
49931a62963SBryan Wu 
50031a62963SBryan Wu 	ad714x_use_com_int(ad714x, hw->start_stage, hw->end_stage);
50131a62963SBryan Wu }
50231a62963SBryan Wu 
ad714x_wheel_use_thr_int(struct ad714x_chip * ad714x,int idx)50331a62963SBryan Wu static void ad714x_wheel_use_thr_int(struct ad714x_chip *ad714x, int idx)
50431a62963SBryan Wu {
50531a62963SBryan Wu 	struct ad714x_wheel_plat *hw = &ad714x->hw->wheel[idx];
50631a62963SBryan Wu 
50731a62963SBryan Wu 	ad714x_use_thr_int(ad714x, hw->start_stage, hw->end_stage);
50831a62963SBryan Wu }
50931a62963SBryan Wu 
ad714x_wheel_state_machine(struct ad714x_chip * ad714x,int idx)51031a62963SBryan Wu static void ad714x_wheel_state_machine(struct ad714x_chip *ad714x, int idx)
51131a62963SBryan Wu {
51231a62963SBryan Wu 	struct ad714x_wheel_plat *hw = &ad714x->hw->wheel[idx];
51331a62963SBryan Wu 	struct ad714x_wheel_drv *sw = &ad714x->sw->wheel[idx];
51431a62963SBryan Wu 	unsigned short h_state, c_state;
51531a62963SBryan Wu 	unsigned short mask;
51631a62963SBryan Wu 
51731a62963SBryan Wu 	mask = ((1 << (hw->end_stage + 1)) - 1) - ((1 << hw->start_stage) - 1);
51831a62963SBryan Wu 
51931a62963SBryan Wu 	h_state = ad714x->h_state & mask;
52031a62963SBryan Wu 	c_state = ad714x->c_state & mask;
52131a62963SBryan Wu 
52231a62963SBryan Wu 	switch (sw->state) {
52331a62963SBryan Wu 	case IDLE:
52431a62963SBryan Wu 		if (h_state) {
52531a62963SBryan Wu 			sw->state = JITTER;
52631a62963SBryan Wu 			/* In End of Conversion interrupt mode, the AD714X
52731a62963SBryan Wu 			 * continuously generates hardware interrupts.
52831a62963SBryan Wu 			 */
52931a62963SBryan Wu 			ad714x_wheel_use_com_int(ad714x, idx);
53031a62963SBryan Wu 			dev_dbg(ad714x->dev, "wheel %d touched\n", idx);
53131a62963SBryan Wu 		}
53231a62963SBryan Wu 		break;
53331a62963SBryan Wu 
53431a62963SBryan Wu 	case JITTER:
53531a62963SBryan Wu 		if (c_state == mask)	{
53631a62963SBryan Wu 			ad714x_wheel_cal_sensor_val(ad714x, idx);
53731a62963SBryan Wu 			ad714x_wheel_cal_highest_stage(ad714x, idx);
53831a62963SBryan Wu 			ad714x_wheel_cal_abs_pos(ad714x, idx);
53931a62963SBryan Wu 			sw->flt_pos = sw->abs_pos;
54031a62963SBryan Wu 			sw->state = ACTIVE;
54131a62963SBryan Wu 		}
54231a62963SBryan Wu 		break;
54331a62963SBryan Wu 
54431a62963SBryan Wu 	case ACTIVE:
54531a62963SBryan Wu 		if (c_state == mask) {
54631a62963SBryan Wu 			if (h_state) {
54731a62963SBryan Wu 				ad714x_wheel_cal_sensor_val(ad714x, idx);
54831a62963SBryan Wu 				ad714x_wheel_cal_highest_stage(ad714x, idx);
54931a62963SBryan Wu 				ad714x_wheel_cal_abs_pos(ad714x, idx);
55031a62963SBryan Wu 				ad714x_wheel_cal_flt_pos(ad714x, idx);
55131a62963SBryan Wu 				input_report_abs(sw->input, ABS_WHEEL,
552f1e430e6SMichael Hennerich 					sw->flt_pos);
55331a62963SBryan Wu 				input_report_key(sw->input, BTN_TOUCH, 1);
55431a62963SBryan Wu 			} else {
55531a62963SBryan Wu 				/* When the user lifts off the sensor, configure
55631a62963SBryan Wu 				 * the AD714X back to threshold interrupt mode.
55731a62963SBryan Wu 				 */
55831a62963SBryan Wu 				ad714x_wheel_use_thr_int(ad714x, idx);
55931a62963SBryan Wu 				sw->state = IDLE;
56031a62963SBryan Wu 				input_report_key(sw->input, BTN_TOUCH, 0);
56131a62963SBryan Wu 
56231a62963SBryan Wu 				dev_dbg(ad714x->dev, "wheel %d released\n",
56331a62963SBryan Wu 					idx);
56431a62963SBryan Wu 			}
56531a62963SBryan Wu 			input_sync(sw->input);
56631a62963SBryan Wu 		}
56731a62963SBryan Wu 		break;
56831a62963SBryan Wu 
56931a62963SBryan Wu 	default:
57031a62963SBryan Wu 		break;
57131a62963SBryan Wu 	}
57231a62963SBryan Wu }
57331a62963SBryan Wu 
touchpad_cal_sensor_val(struct ad714x_chip * ad714x,int idx)57431a62963SBryan Wu static void touchpad_cal_sensor_val(struct ad714x_chip *ad714x, int idx)
57531a62963SBryan Wu {
57631a62963SBryan Wu 	struct ad714x_touchpad_plat *hw = &ad714x->hw->touchpad[idx];
57731a62963SBryan Wu 	int i;
57831a62963SBryan Wu 
5799eff794bSMichael Hennerich 	ad714x->read(ad714x, CDC_RESULT_S0 + hw->x_start_stage,
5809eff794bSMichael Hennerich 			&ad714x->adc_reg[hw->x_start_stage],
5819eff794bSMichael Hennerich 			hw->x_end_stage - hw->x_start_stage + 1);
5829eff794bSMichael Hennerich 
58331a62963SBryan Wu 	for (i = hw->x_start_stage; i <= hw->x_end_stage; i++) {
584c0409febSDmitry Torokhov 		ad714x->read(ad714x, STAGE0_AMBIENT + i * PER_STAGE_REG_NUM,
5859eff794bSMichael Hennerich 				&ad714x->amb_reg[i], 1);
58631a62963SBryan Wu 		if (ad714x->adc_reg[i] > ad714x->amb_reg[i])
5879eff794bSMichael Hennerich 			ad714x->sensor_val[i] =
5889eff794bSMichael Hennerich 				ad714x->adc_reg[i] - ad714x->amb_reg[i];
58931a62963SBryan Wu 		else
59031a62963SBryan Wu 			ad714x->sensor_val[i] = 0;
59131a62963SBryan Wu 	}
59231a62963SBryan Wu }
59331a62963SBryan Wu 
touchpad_cal_highest_stage(struct ad714x_chip * ad714x,int idx)59431a62963SBryan Wu static void touchpad_cal_highest_stage(struct ad714x_chip *ad714x, int idx)
59531a62963SBryan Wu {
59631a62963SBryan Wu 	struct ad714x_touchpad_plat *hw = &ad714x->hw->touchpad[idx];
59731a62963SBryan Wu 	struct ad714x_touchpad_drv *sw = &ad714x->sw->touchpad[idx];
59831a62963SBryan Wu 
59931a62963SBryan Wu 	sw->x_highest_stage = ad714x_cal_highest_stage(ad714x,
60031a62963SBryan Wu 		hw->x_start_stage, hw->x_end_stage);
60131a62963SBryan Wu 	sw->y_highest_stage = ad714x_cal_highest_stage(ad714x,
60231a62963SBryan Wu 		hw->y_start_stage, hw->y_end_stage);
60331a62963SBryan Wu 
60431a62963SBryan Wu 	dev_dbg(ad714x->dev,
60531a62963SBryan Wu 		"touchpad %d x_highest_stage:%d, y_highest_stage:%d\n",
60631a62963SBryan Wu 		idx, sw->x_highest_stage, sw->y_highest_stage);
60731a62963SBryan Wu }
60831a62963SBryan Wu 
60931a62963SBryan Wu /*
61031a62963SBryan Wu  * If 2 fingers are touching the sensor then 2 peaks can be observed in the
61131a62963SBryan Wu  * distribution.
61231a62963SBryan Wu  * The arithmetic doesn't support to get absolute coordinates for multi-touch
61331a62963SBryan Wu  * yet.
61431a62963SBryan Wu  */
touchpad_check_second_peak(struct ad714x_chip * ad714x,int idx)61531a62963SBryan Wu static int touchpad_check_second_peak(struct ad714x_chip *ad714x, int idx)
61631a62963SBryan Wu {
61731a62963SBryan Wu 	struct ad714x_touchpad_plat *hw = &ad714x->hw->touchpad[idx];
61831a62963SBryan Wu 	struct ad714x_touchpad_drv *sw = &ad714x->sw->touchpad[idx];
61931a62963SBryan Wu 	int i;
62031a62963SBryan Wu 
62131a62963SBryan Wu 	for (i = hw->x_start_stage; i < sw->x_highest_stage; i++) {
62231a62963SBryan Wu 		if ((ad714x->sensor_val[i] - ad714x->sensor_val[i + 1])
62331a62963SBryan Wu 			> (ad714x->sensor_val[i + 1] / 10))
62431a62963SBryan Wu 			return 1;
62531a62963SBryan Wu 	}
62631a62963SBryan Wu 
62731a62963SBryan Wu 	for (i = sw->x_highest_stage; i < hw->x_end_stage; i++) {
62831a62963SBryan Wu 		if ((ad714x->sensor_val[i + 1] - ad714x->sensor_val[i])
62931a62963SBryan Wu 			> (ad714x->sensor_val[i] / 10))
63031a62963SBryan Wu 			return 1;
63131a62963SBryan Wu 	}
63231a62963SBryan Wu 
63331a62963SBryan Wu 	for (i = hw->y_start_stage; i < sw->y_highest_stage; i++) {
63431a62963SBryan Wu 		if ((ad714x->sensor_val[i] - ad714x->sensor_val[i + 1])
63531a62963SBryan Wu 			> (ad714x->sensor_val[i + 1] / 10))
63631a62963SBryan Wu 			return 1;
63731a62963SBryan Wu 	}
63831a62963SBryan Wu 
63931a62963SBryan Wu 	for (i = sw->y_highest_stage; i < hw->y_end_stage; i++) {
64031a62963SBryan Wu 		if ((ad714x->sensor_val[i + 1] - ad714x->sensor_val[i])
64131a62963SBryan Wu 			> (ad714x->sensor_val[i] / 10))
64231a62963SBryan Wu 			return 1;
64331a62963SBryan Wu 	}
64431a62963SBryan Wu 
64531a62963SBryan Wu 	return 0;
64631a62963SBryan Wu }
64731a62963SBryan Wu 
64831a62963SBryan Wu /*
64931a62963SBryan Wu  * If only one finger is used to activate the touch pad then only 1 peak will be
65031a62963SBryan Wu  * registered in the distribution. This peak and the 2 adjacent sensors will be
65131a62963SBryan Wu  * used in the calculation of the absolute position. This will prevent hand
65231a62963SBryan Wu  * shadows to affect the absolute position calculation.
65331a62963SBryan Wu  */
touchpad_cal_abs_pos(struct ad714x_chip * ad714x,int idx)65431a62963SBryan Wu static void touchpad_cal_abs_pos(struct ad714x_chip *ad714x, int idx)
65531a62963SBryan Wu {
65631a62963SBryan Wu 	struct ad714x_touchpad_plat *hw = &ad714x->hw->touchpad[idx];
65731a62963SBryan Wu 	struct ad714x_touchpad_drv *sw = &ad714x->sw->touchpad[idx];
65831a62963SBryan Wu 
65931a62963SBryan Wu 	sw->x_abs_pos = ad714x_cal_abs_pos(ad714x, hw->x_start_stage,
66031a62963SBryan Wu 			hw->x_end_stage, sw->x_highest_stage, hw->x_max_coord);
66131a62963SBryan Wu 	sw->y_abs_pos = ad714x_cal_abs_pos(ad714x, hw->y_start_stage,
66231a62963SBryan Wu 			hw->y_end_stage, sw->y_highest_stage, hw->y_max_coord);
66331a62963SBryan Wu 
66431a62963SBryan Wu 	dev_dbg(ad714x->dev, "touchpad %d absolute position:(%d, %d)\n", idx,
66531a62963SBryan Wu 			sw->x_abs_pos, sw->y_abs_pos);
66631a62963SBryan Wu }
66731a62963SBryan Wu 
touchpad_cal_flt_pos(struct ad714x_chip * ad714x,int idx)66831a62963SBryan Wu static void touchpad_cal_flt_pos(struct ad714x_chip *ad714x, int idx)
66931a62963SBryan Wu {
67031a62963SBryan Wu 	struct ad714x_touchpad_drv *sw = &ad714x->sw->touchpad[idx];
67131a62963SBryan Wu 
67231a62963SBryan Wu 	sw->x_flt_pos = (sw->x_flt_pos * (10 - 4) +
67331a62963SBryan Wu 			sw->x_abs_pos * 4)/10;
67431a62963SBryan Wu 	sw->y_flt_pos = (sw->y_flt_pos * (10 - 4) +
67531a62963SBryan Wu 			sw->y_abs_pos * 4)/10;
67631a62963SBryan Wu 
67731a62963SBryan Wu 	dev_dbg(ad714x->dev, "touchpad %d filter position:(%d, %d)\n",
67831a62963SBryan Wu 			idx, sw->x_flt_pos, sw->y_flt_pos);
67931a62963SBryan Wu }
68031a62963SBryan Wu 
68131a62963SBryan Wu /*
68231a62963SBryan Wu  * To prevent distortion from showing in the absolute position, it is
68331a62963SBryan Wu  * necessary to detect the end points. When endpoints are detected, the
68431a62963SBryan Wu  * driver stops updating the status variables with absolute positions.
68531a62963SBryan Wu  * End points are detected on the 4 edges of the touchpad sensor. The
68631a62963SBryan Wu  * method to detect them is the same for all 4.
68731a62963SBryan Wu  * To detect the end points, the firmware computes the difference in
68831a62963SBryan Wu  * percent between the sensor on the edge and the adjacent one. The
68931a62963SBryan Wu  * difference is calculated in percent in order to make the end point
69031a62963SBryan Wu  * detection independent of the pressure.
69131a62963SBryan Wu  */
69231a62963SBryan Wu 
69331a62963SBryan Wu #define LEFT_END_POINT_DETECTION_LEVEL                  550
69431a62963SBryan Wu #define RIGHT_END_POINT_DETECTION_LEVEL                 750
69531a62963SBryan Wu #define LEFT_RIGHT_END_POINT_DEAVTIVALION_LEVEL         850
69631a62963SBryan Wu #define TOP_END_POINT_DETECTION_LEVEL                   550
69731a62963SBryan Wu #define BOTTOM_END_POINT_DETECTION_LEVEL                950
69831a62963SBryan Wu #define TOP_BOTTOM_END_POINT_DEAVTIVALION_LEVEL         700
touchpad_check_endpoint(struct ad714x_chip * ad714x,int idx)69931a62963SBryan Wu static int touchpad_check_endpoint(struct ad714x_chip *ad714x, int idx)
70031a62963SBryan Wu {
70131a62963SBryan Wu 	struct ad714x_touchpad_plat *hw = &ad714x->hw->touchpad[idx];
70231a62963SBryan Wu 	struct ad714x_touchpad_drv *sw  = &ad714x->sw->touchpad[idx];
70331a62963SBryan Wu 	int percent_sensor_diff;
70431a62963SBryan Wu 
70531a62963SBryan Wu 	/* left endpoint detect */
70631a62963SBryan Wu 	percent_sensor_diff = (ad714x->sensor_val[hw->x_start_stage] -
70731a62963SBryan Wu 			ad714x->sensor_val[hw->x_start_stage + 1]) * 100 /
70831a62963SBryan Wu 			ad714x->sensor_val[hw->x_start_stage + 1];
70931a62963SBryan Wu 	if (!sw->left_ep) {
71031a62963SBryan Wu 		if (percent_sensor_diff >= LEFT_END_POINT_DETECTION_LEVEL)  {
71131a62963SBryan Wu 			sw->left_ep = 1;
71231a62963SBryan Wu 			sw->left_ep_val =
71331a62963SBryan Wu 				ad714x->sensor_val[hw->x_start_stage + 1];
71431a62963SBryan Wu 		}
71531a62963SBryan Wu 	} else {
71631a62963SBryan Wu 		if ((percent_sensor_diff < LEFT_END_POINT_DETECTION_LEVEL) &&
71731a62963SBryan Wu 		    (ad714x->sensor_val[hw->x_start_stage + 1] >
71831a62963SBryan Wu 		     LEFT_RIGHT_END_POINT_DEAVTIVALION_LEVEL + sw->left_ep_val))
71931a62963SBryan Wu 			sw->left_ep = 0;
72031a62963SBryan Wu 	}
72131a62963SBryan Wu 
72231a62963SBryan Wu 	/* right endpoint detect */
72331a62963SBryan Wu 	percent_sensor_diff = (ad714x->sensor_val[hw->x_end_stage] -
72431a62963SBryan Wu 			ad714x->sensor_val[hw->x_end_stage - 1]) * 100 /
72531a62963SBryan Wu 			ad714x->sensor_val[hw->x_end_stage - 1];
72631a62963SBryan Wu 	if (!sw->right_ep) {
72731a62963SBryan Wu 		if (percent_sensor_diff >= RIGHT_END_POINT_DETECTION_LEVEL)  {
72831a62963SBryan Wu 			sw->right_ep = 1;
72931a62963SBryan Wu 			sw->right_ep_val =
73031a62963SBryan Wu 				ad714x->sensor_val[hw->x_end_stage - 1];
73131a62963SBryan Wu 		}
73231a62963SBryan Wu 	} else {
73331a62963SBryan Wu 		if ((percent_sensor_diff < RIGHT_END_POINT_DETECTION_LEVEL) &&
73431a62963SBryan Wu 		(ad714x->sensor_val[hw->x_end_stage - 1] >
73531a62963SBryan Wu 		LEFT_RIGHT_END_POINT_DEAVTIVALION_LEVEL + sw->right_ep_val))
73631a62963SBryan Wu 			sw->right_ep = 0;
73731a62963SBryan Wu 	}
73831a62963SBryan Wu 
73931a62963SBryan Wu 	/* top endpoint detect */
74031a62963SBryan Wu 	percent_sensor_diff = (ad714x->sensor_val[hw->y_start_stage] -
74131a62963SBryan Wu 			ad714x->sensor_val[hw->y_start_stage + 1]) * 100 /
74231a62963SBryan Wu 			ad714x->sensor_val[hw->y_start_stage + 1];
74331a62963SBryan Wu 	if (!sw->top_ep) {
74431a62963SBryan Wu 		if (percent_sensor_diff >= TOP_END_POINT_DETECTION_LEVEL)  {
74531a62963SBryan Wu 			sw->top_ep = 1;
74631a62963SBryan Wu 			sw->top_ep_val =
74731a62963SBryan Wu 				ad714x->sensor_val[hw->y_start_stage + 1];
74831a62963SBryan Wu 		}
74931a62963SBryan Wu 	} else {
75031a62963SBryan Wu 		if ((percent_sensor_diff < TOP_END_POINT_DETECTION_LEVEL) &&
75131a62963SBryan Wu 		(ad714x->sensor_val[hw->y_start_stage + 1] >
75231a62963SBryan Wu 		TOP_BOTTOM_END_POINT_DEAVTIVALION_LEVEL + sw->top_ep_val))
75331a62963SBryan Wu 			sw->top_ep = 0;
75431a62963SBryan Wu 	}
75531a62963SBryan Wu 
75631a62963SBryan Wu 	/* bottom endpoint detect */
75731a62963SBryan Wu 	percent_sensor_diff = (ad714x->sensor_val[hw->y_end_stage] -
75831a62963SBryan Wu 		ad714x->sensor_val[hw->y_end_stage - 1]) * 100 /
75931a62963SBryan Wu 		ad714x->sensor_val[hw->y_end_stage - 1];
76031a62963SBryan Wu 	if (!sw->bottom_ep) {
76131a62963SBryan Wu 		if (percent_sensor_diff >= BOTTOM_END_POINT_DETECTION_LEVEL)  {
76231a62963SBryan Wu 			sw->bottom_ep = 1;
76331a62963SBryan Wu 			sw->bottom_ep_val =
76431a62963SBryan Wu 				ad714x->sensor_val[hw->y_end_stage - 1];
76531a62963SBryan Wu 		}
76631a62963SBryan Wu 	} else {
76731a62963SBryan Wu 		if ((percent_sensor_diff < BOTTOM_END_POINT_DETECTION_LEVEL) &&
76831a62963SBryan Wu 		(ad714x->sensor_val[hw->y_end_stage - 1] >
76931a62963SBryan Wu 		 TOP_BOTTOM_END_POINT_DEAVTIVALION_LEVEL + sw->bottom_ep_val))
77031a62963SBryan Wu 			sw->bottom_ep = 0;
77131a62963SBryan Wu 	}
77231a62963SBryan Wu 
77331a62963SBryan Wu 	return sw->left_ep || sw->right_ep || sw->top_ep || sw->bottom_ep;
77431a62963SBryan Wu }
77531a62963SBryan Wu 
touchpad_use_com_int(struct ad714x_chip * ad714x,int idx)77631a62963SBryan Wu static void touchpad_use_com_int(struct ad714x_chip *ad714x, int idx)
77731a62963SBryan Wu {
77831a62963SBryan Wu 	struct ad714x_touchpad_plat *hw = &ad714x->hw->touchpad[idx];
77931a62963SBryan Wu 
78031a62963SBryan Wu 	ad714x_use_com_int(ad714x, hw->x_start_stage, hw->x_end_stage);
78131a62963SBryan Wu }
78231a62963SBryan Wu 
touchpad_use_thr_int(struct ad714x_chip * ad714x,int idx)78331a62963SBryan Wu static void touchpad_use_thr_int(struct ad714x_chip *ad714x, int idx)
78431a62963SBryan Wu {
78531a62963SBryan Wu 	struct ad714x_touchpad_plat *hw = &ad714x->hw->touchpad[idx];
78631a62963SBryan Wu 
78731a62963SBryan Wu 	ad714x_use_thr_int(ad714x, hw->x_start_stage, hw->x_end_stage);
78831a62963SBryan Wu 	ad714x_use_thr_int(ad714x, hw->y_start_stage, hw->y_end_stage);
78931a62963SBryan Wu }
79031a62963SBryan Wu 
ad714x_touchpad_state_machine(struct ad714x_chip * ad714x,int idx)79131a62963SBryan Wu static void ad714x_touchpad_state_machine(struct ad714x_chip *ad714x, int idx)
79231a62963SBryan Wu {
79331a62963SBryan Wu 	struct ad714x_touchpad_plat *hw = &ad714x->hw->touchpad[idx];
79431a62963SBryan Wu 	struct ad714x_touchpad_drv *sw = &ad714x->sw->touchpad[idx];
79531a62963SBryan Wu 	unsigned short h_state, c_state;
79631a62963SBryan Wu 	unsigned short mask;
79731a62963SBryan Wu 
79831a62963SBryan Wu 	mask = (((1 << (hw->x_end_stage + 1)) - 1) -
79931a62963SBryan Wu 		((1 << hw->x_start_stage) - 1)) +
80031a62963SBryan Wu 		(((1 << (hw->y_end_stage + 1)) - 1) -
80131a62963SBryan Wu 		((1 << hw->y_start_stage) - 1));
80231a62963SBryan Wu 
80331a62963SBryan Wu 	h_state = ad714x->h_state & mask;
80431a62963SBryan Wu 	c_state = ad714x->c_state & mask;
80531a62963SBryan Wu 
80631a62963SBryan Wu 	switch (sw->state) {
80731a62963SBryan Wu 	case IDLE:
80831a62963SBryan Wu 		if (h_state) {
80931a62963SBryan Wu 			sw->state = JITTER;
81031a62963SBryan Wu 			/* In End of Conversion interrupt mode, the AD714X
81131a62963SBryan Wu 			 * continuously generates hardware interrupts.
81231a62963SBryan Wu 			 */
81331a62963SBryan Wu 			touchpad_use_com_int(ad714x, idx);
81431a62963SBryan Wu 			dev_dbg(ad714x->dev, "touchpad %d touched\n", idx);
81531a62963SBryan Wu 		}
81631a62963SBryan Wu 		break;
81731a62963SBryan Wu 
81831a62963SBryan Wu 	case JITTER:
81931a62963SBryan Wu 		if (c_state == mask) {
82031a62963SBryan Wu 			touchpad_cal_sensor_val(ad714x, idx);
82131a62963SBryan Wu 			touchpad_cal_highest_stage(ad714x, idx);
82231a62963SBryan Wu 			if ((!touchpad_check_second_peak(ad714x, idx)) &&
82331a62963SBryan Wu 				(!touchpad_check_endpoint(ad714x, idx))) {
82431a62963SBryan Wu 				dev_dbg(ad714x->dev,
82531a62963SBryan Wu 					"touchpad%d, 2 fingers or endpoint\n",
82631a62963SBryan Wu 					idx);
82731a62963SBryan Wu 				touchpad_cal_abs_pos(ad714x, idx);
82831a62963SBryan Wu 				sw->x_flt_pos = sw->x_abs_pos;
82931a62963SBryan Wu 				sw->y_flt_pos = sw->y_abs_pos;
83031a62963SBryan Wu 				sw->state = ACTIVE;
83131a62963SBryan Wu 			}
83231a62963SBryan Wu 		}
83331a62963SBryan Wu 		break;
83431a62963SBryan Wu 
83531a62963SBryan Wu 	case ACTIVE:
83631a62963SBryan Wu 		if (c_state == mask) {
83731a62963SBryan Wu 			if (h_state) {
83831a62963SBryan Wu 				touchpad_cal_sensor_val(ad714x, idx);
83931a62963SBryan Wu 				touchpad_cal_highest_stage(ad714x, idx);
84031a62963SBryan Wu 				if ((!touchpad_check_second_peak(ad714x, idx))
84131a62963SBryan Wu 				  && (!touchpad_check_endpoint(ad714x, idx))) {
84231a62963SBryan Wu 					touchpad_cal_abs_pos(ad714x, idx);
84331a62963SBryan Wu 					touchpad_cal_flt_pos(ad714x, idx);
84431a62963SBryan Wu 					input_report_abs(sw->input, ABS_X,
84531a62963SBryan Wu 						sw->x_flt_pos);
84631a62963SBryan Wu 					input_report_abs(sw->input, ABS_Y,
84731a62963SBryan Wu 						sw->y_flt_pos);
84831a62963SBryan Wu 					input_report_key(sw->input, BTN_TOUCH,
84931a62963SBryan Wu 						1);
85031a62963SBryan Wu 				}
85131a62963SBryan Wu 			} else {
85231a62963SBryan Wu 				/* When the user lifts off the sensor, configure
85331a62963SBryan Wu 				 * the AD714X back to threshold interrupt mode.
85431a62963SBryan Wu 				 */
85531a62963SBryan Wu 				touchpad_use_thr_int(ad714x, idx);
85631a62963SBryan Wu 				sw->state = IDLE;
85731a62963SBryan Wu 				input_report_key(sw->input, BTN_TOUCH, 0);
85831a62963SBryan Wu 				dev_dbg(ad714x->dev, "touchpad %d released\n",
85931a62963SBryan Wu 					idx);
86031a62963SBryan Wu 			}
86131a62963SBryan Wu 			input_sync(sw->input);
86231a62963SBryan Wu 		}
86331a62963SBryan Wu 		break;
86431a62963SBryan Wu 
86531a62963SBryan Wu 	default:
86631a62963SBryan Wu 		break;
86731a62963SBryan Wu 	}
86831a62963SBryan Wu }
86931a62963SBryan Wu 
ad714x_hw_detect(struct ad714x_chip * ad714x)87031a62963SBryan Wu static int ad714x_hw_detect(struct ad714x_chip *ad714x)
87131a62963SBryan Wu {
87231a62963SBryan Wu 	unsigned short data;
87331a62963SBryan Wu 
8749eff794bSMichael Hennerich 	ad714x->read(ad714x, AD714X_PARTID_REG, &data, 1);
87531a62963SBryan Wu 	switch (data & 0xFFF0) {
87631a62963SBryan Wu 	case AD7142_PARTID:
87731a62963SBryan Wu 		ad714x->product = 0x7142;
87831a62963SBryan Wu 		ad714x->version = data & 0xF;
87931a62963SBryan Wu 		dev_info(ad714x->dev, "found AD7142 captouch, rev:%d\n",
88031a62963SBryan Wu 				ad714x->version);
88131a62963SBryan Wu 		return 0;
88231a62963SBryan Wu 
8836c04d7b3SBarry Song 	case AD7143_PARTID:
8846c04d7b3SBarry Song 		ad714x->product = 0x7143;
8856c04d7b3SBarry Song 		ad714x->version = data & 0xF;
8866c04d7b3SBarry Song 		dev_info(ad714x->dev, "found AD7143 captouch, rev:%d\n",
8876c04d7b3SBarry Song 				ad714x->version);
8886c04d7b3SBarry Song 		return 0;
8896c04d7b3SBarry Song 
8906c04d7b3SBarry Song 	case AD7147_PARTID:
8916c04d7b3SBarry Song 		ad714x->product = 0x7147;
8926c04d7b3SBarry Song 		ad714x->version = data & 0xF;
8936c04d7b3SBarry Song 		dev_info(ad714x->dev, "found AD7147(A) captouch, rev:%d\n",
8946c04d7b3SBarry Song 				ad714x->version);
8956c04d7b3SBarry Song 		return 0;
8966c04d7b3SBarry Song 
8976c04d7b3SBarry Song 	case AD7148_PARTID:
8986c04d7b3SBarry Song 		ad714x->product = 0x7148;
8996c04d7b3SBarry Song 		ad714x->version = data & 0xF;
9006c04d7b3SBarry Song 		dev_info(ad714x->dev, "found AD7148 captouch, rev:%d\n",
9016c04d7b3SBarry Song 				ad714x->version);
9026c04d7b3SBarry Song 		return 0;
9036c04d7b3SBarry Song 
90431a62963SBryan Wu 	default:
90531a62963SBryan Wu 		dev_err(ad714x->dev,
90631a62963SBryan Wu 			"fail to detect AD714X captouch, read ID is %04x\n",
90731a62963SBryan Wu 			data);
90831a62963SBryan Wu 		return -ENODEV;
90931a62963SBryan Wu 	}
91031a62963SBryan Wu }
91131a62963SBryan Wu 
ad714x_hw_init(struct ad714x_chip * ad714x)91231a62963SBryan Wu static void ad714x_hw_init(struct ad714x_chip *ad714x)
91331a62963SBryan Wu {
91431a62963SBryan Wu 	int i, j;
91531a62963SBryan Wu 	unsigned short reg_base;
91631a62963SBryan Wu 	unsigned short data;
91731a62963SBryan Wu 
91831a62963SBryan Wu 	/* configuration CDC and interrupts */
91931a62963SBryan Wu 
92031a62963SBryan Wu 	for (i = 0; i < STAGE_NUM; i++) {
92131a62963SBryan Wu 		reg_base = AD714X_STAGECFG_REG + i * STAGE_CFGREG_NUM;
92231a62963SBryan Wu 		for (j = 0; j < STAGE_CFGREG_NUM; j++)
923c0409febSDmitry Torokhov 			ad714x->write(ad714x, reg_base + j,
92431a62963SBryan Wu 					ad714x->hw->stage_cfg_reg[i][j]);
92531a62963SBryan Wu 	}
92631a62963SBryan Wu 
92731a62963SBryan Wu 	for (i = 0; i < SYS_CFGREG_NUM; i++)
928c0409febSDmitry Torokhov 		ad714x->write(ad714x, AD714X_SYSCFG_REG + i,
92931a62963SBryan Wu 			ad714x->hw->sys_cfg_reg[i]);
93031a62963SBryan Wu 	for (i = 0; i < SYS_CFGREG_NUM; i++)
9319eff794bSMichael Hennerich 		ad714x->read(ad714x, AD714X_SYSCFG_REG + i, &data, 1);
93231a62963SBryan Wu 
933c0409febSDmitry Torokhov 	ad714x->write(ad714x, AD714X_STG_CAL_EN_REG, 0xFFF);
93431a62963SBryan Wu 
93531a62963SBryan Wu 	/* clear all interrupts */
9369eff794bSMichael Hennerich 	ad714x->read(ad714x, STG_LOW_INT_STA_REG, &ad714x->l_state, 3);
93731a62963SBryan Wu }
93831a62963SBryan Wu 
ad714x_interrupt_thread(int irq,void * data)93931a62963SBryan Wu static irqreturn_t ad714x_interrupt_thread(int irq, void *data)
94031a62963SBryan Wu {
94131a62963SBryan Wu 	struct ad714x_chip *ad714x = data;
94231a62963SBryan Wu 	int i;
94331a62963SBryan Wu 
94431a62963SBryan Wu 	mutex_lock(&ad714x->mutex);
94531a62963SBryan Wu 
9469eff794bSMichael Hennerich 	ad714x->read(ad714x, STG_LOW_INT_STA_REG, &ad714x->l_state, 3);
94731a62963SBryan Wu 
94831a62963SBryan Wu 	for (i = 0; i < ad714x->hw->button_num; i++)
94931a62963SBryan Wu 		ad714x_button_state_machine(ad714x, i);
95031a62963SBryan Wu 	for (i = 0; i < ad714x->hw->slider_num; i++)
95131a62963SBryan Wu 		ad714x_slider_state_machine(ad714x, i);
95231a62963SBryan Wu 	for (i = 0; i < ad714x->hw->wheel_num; i++)
95331a62963SBryan Wu 		ad714x_wheel_state_machine(ad714x, i);
95431a62963SBryan Wu 	for (i = 0; i < ad714x->hw->touchpad_num; i++)
95531a62963SBryan Wu 		ad714x_touchpad_state_machine(ad714x, i);
95631a62963SBryan Wu 
95731a62963SBryan Wu 	mutex_unlock(&ad714x->mutex);
95831a62963SBryan Wu 
95931a62963SBryan Wu 	return IRQ_HANDLED;
96031a62963SBryan Wu }
96131a62963SBryan Wu 
ad714x_probe(struct device * dev,u16 bus_type,int irq,ad714x_read_t read,ad714x_write_t write)96231a62963SBryan Wu struct ad714x_chip *ad714x_probe(struct device *dev, u16 bus_type, int irq,
96331a62963SBryan Wu 				 ad714x_read_t read, ad714x_write_t write)
96431a62963SBryan Wu {
965c5c18a06SVaishali Thakkar 	int i;
96631a62963SBryan Wu 	int error;
967c5c18a06SVaishali Thakkar 	struct input_dev *input;
96831a62963SBryan Wu 
969c838cb3dSJingoo Han 	struct ad714x_platform_data *plat_data = dev_get_platdata(dev);
97031a62963SBryan Wu 	struct ad714x_chip *ad714x;
97131a62963SBryan Wu 	void *drv_mem;
9729b7e31bbSLars-Peter Clausen 	unsigned long irqflags;
97331a62963SBryan Wu 
97431a62963SBryan Wu 	struct ad714x_button_drv *bt_drv;
97531a62963SBryan Wu 	struct ad714x_slider_drv *sd_drv;
97631a62963SBryan Wu 	struct ad714x_wheel_drv *wl_drv;
97731a62963SBryan Wu 	struct ad714x_touchpad_drv *tp_drv;
97831a62963SBryan Wu 
97931a62963SBryan Wu 
98031a62963SBryan Wu 	if (irq <= 0) {
98131a62963SBryan Wu 		dev_err(dev, "IRQ not configured!\n");
98231a62963SBryan Wu 		error = -EINVAL;
983c5c18a06SVaishali Thakkar 		return ERR_PTR(error);
98431a62963SBryan Wu 	}
98531a62963SBryan Wu 
986c838cb3dSJingoo Han 	if (dev_get_platdata(dev) == NULL) {
98731a62963SBryan Wu 		dev_err(dev, "platform data for ad714x doesn't exist\n");
98831a62963SBryan Wu 		error = -EINVAL;
989c5c18a06SVaishali Thakkar 		return ERR_PTR(error);
99031a62963SBryan Wu 	}
99131a62963SBryan Wu 
992c5c18a06SVaishali Thakkar 	ad714x = devm_kzalloc(dev, sizeof(*ad714x) + sizeof(*ad714x->sw) +
99331a62963SBryan Wu 				   sizeof(*sd_drv) * plat_data->slider_num +
99431a62963SBryan Wu 				   sizeof(*wl_drv) * plat_data->wheel_num +
99531a62963SBryan Wu 				   sizeof(*tp_drv) * plat_data->touchpad_num +
996c5c18a06SVaishali Thakkar 				   sizeof(*bt_drv) * plat_data->button_num,
997c5c18a06SVaishali Thakkar 			      GFP_KERNEL);
99831a62963SBryan Wu 	if (!ad714x) {
99931a62963SBryan Wu 		error = -ENOMEM;
1000c5c18a06SVaishali Thakkar 		return ERR_PTR(error);
100131a62963SBryan Wu 	}
100231a62963SBryan Wu 	ad714x->hw = plat_data;
100331a62963SBryan Wu 
100431a62963SBryan Wu 	drv_mem = ad714x + 1;
100531a62963SBryan Wu 	ad714x->sw = drv_mem;
100631a62963SBryan Wu 	drv_mem += sizeof(*ad714x->sw);
100731a62963SBryan Wu 	ad714x->sw->slider = sd_drv = drv_mem;
100831a62963SBryan Wu 	drv_mem += sizeof(*sd_drv) * ad714x->hw->slider_num;
100931a62963SBryan Wu 	ad714x->sw->wheel = wl_drv = drv_mem;
101031a62963SBryan Wu 	drv_mem += sizeof(*wl_drv) * ad714x->hw->wheel_num;
101131a62963SBryan Wu 	ad714x->sw->touchpad = tp_drv = drv_mem;
101231a62963SBryan Wu 	drv_mem += sizeof(*tp_drv) * ad714x->hw->touchpad_num;
101331a62963SBryan Wu 	ad714x->sw->button = bt_drv = drv_mem;
101431a62963SBryan Wu 	drv_mem += sizeof(*bt_drv) * ad714x->hw->button_num;
101531a62963SBryan Wu 
101631a62963SBryan Wu 	ad714x->read = read;
101731a62963SBryan Wu 	ad714x->write = write;
101831a62963SBryan Wu 	ad714x->irq = irq;
101931a62963SBryan Wu 	ad714x->dev = dev;
102031a62963SBryan Wu 
102131a62963SBryan Wu 	error = ad714x_hw_detect(ad714x);
102231a62963SBryan Wu 	if (error)
1023c5c18a06SVaishali Thakkar 		return ERR_PTR(error);
102431a62963SBryan Wu 
1025421f91d2SUwe Kleine-König 	/* initialize and request sw/hw resources */
102631a62963SBryan Wu 
102731a62963SBryan Wu 	ad714x_hw_init(ad714x);
102831a62963SBryan Wu 	mutex_init(&ad714x->mutex);
102931a62963SBryan Wu 
103031a62963SBryan Wu 	/* a slider uses one input_dev instance */
103131a62963SBryan Wu 	if (ad714x->hw->slider_num > 0) {
103231a62963SBryan Wu 		struct ad714x_slider_plat *sd_plat = ad714x->hw->slider;
103331a62963SBryan Wu 
103431a62963SBryan Wu 		for (i = 0; i < ad714x->hw->slider_num; i++) {
1035c5c18a06SVaishali Thakkar 			input = devm_input_allocate_device(dev);
1036c5c18a06SVaishali Thakkar 			if (!input)
1037c5c18a06SVaishali Thakkar 				return ERR_PTR(-ENOMEM);
103831a62963SBryan Wu 
1039c5c18a06SVaishali Thakkar 			__set_bit(EV_ABS, input->evbit);
1040c5c18a06SVaishali Thakkar 			__set_bit(EV_KEY, input->evbit);
1041c5c18a06SVaishali Thakkar 			__set_bit(ABS_X, input->absbit);
1042c5c18a06SVaishali Thakkar 			__set_bit(BTN_TOUCH, input->keybit);
1043c5c18a06SVaishali Thakkar 			input_set_abs_params(input,
104431a62963SBryan Wu 				ABS_X, 0, sd_plat->max_coord, 0, 0);
104531a62963SBryan Wu 
1046c5c18a06SVaishali Thakkar 			input->id.bustype = bus_type;
1047c5c18a06SVaishali Thakkar 			input->id.product = ad714x->product;
1048c5c18a06SVaishali Thakkar 			input->id.version = ad714x->version;
1049c5c18a06SVaishali Thakkar 			input->name = "ad714x_captouch_slider";
1050c5c18a06SVaishali Thakkar 			input->dev.parent = dev;
105131a62963SBryan Wu 
1052c5c18a06SVaishali Thakkar 			error = input_register_device(input);
105331a62963SBryan Wu 			if (error)
1054c5c18a06SVaishali Thakkar 				return ERR_PTR(error);
105531a62963SBryan Wu 
1056c5c18a06SVaishali Thakkar 			sd_drv[i].input = input;
105731a62963SBryan Wu 		}
105831a62963SBryan Wu 	}
105931a62963SBryan Wu 
106031a62963SBryan Wu 	/* a wheel uses one input_dev instance */
106131a62963SBryan Wu 	if (ad714x->hw->wheel_num > 0) {
106231a62963SBryan Wu 		struct ad714x_wheel_plat *wl_plat = ad714x->hw->wheel;
106331a62963SBryan Wu 
106431a62963SBryan Wu 		for (i = 0; i < ad714x->hw->wheel_num; i++) {
1065c5c18a06SVaishali Thakkar 			input = devm_input_allocate_device(dev);
1066c5c18a06SVaishali Thakkar 			if (!input)
1067c5c18a06SVaishali Thakkar 				return ERR_PTR(-ENOMEM);
106831a62963SBryan Wu 
1069c5c18a06SVaishali Thakkar 			__set_bit(EV_KEY, input->evbit);
1070c5c18a06SVaishali Thakkar 			__set_bit(EV_ABS, input->evbit);
1071c5c18a06SVaishali Thakkar 			__set_bit(ABS_WHEEL, input->absbit);
1072c5c18a06SVaishali Thakkar 			__set_bit(BTN_TOUCH, input->keybit);
1073c5c18a06SVaishali Thakkar 			input_set_abs_params(input,
107431a62963SBryan Wu 				ABS_WHEEL, 0, wl_plat->max_coord, 0, 0);
107531a62963SBryan Wu 
1076c5c18a06SVaishali Thakkar 			input->id.bustype = bus_type;
1077c5c18a06SVaishali Thakkar 			input->id.product = ad714x->product;
1078c5c18a06SVaishali Thakkar 			input->id.version = ad714x->version;
1079c5c18a06SVaishali Thakkar 			input->name = "ad714x_captouch_wheel";
1080c5c18a06SVaishali Thakkar 			input->dev.parent = dev;
108131a62963SBryan Wu 
1082c5c18a06SVaishali Thakkar 			error = input_register_device(input);
108331a62963SBryan Wu 			if (error)
1084c5c18a06SVaishali Thakkar 				return ERR_PTR(error);
108531a62963SBryan Wu 
1086c5c18a06SVaishali Thakkar 			wl_drv[i].input = input;
108731a62963SBryan Wu 		}
108831a62963SBryan Wu 	}
108931a62963SBryan Wu 
109031a62963SBryan Wu 	/* a touchpad uses one input_dev instance */
109131a62963SBryan Wu 	if (ad714x->hw->touchpad_num > 0) {
109231a62963SBryan Wu 		struct ad714x_touchpad_plat *tp_plat = ad714x->hw->touchpad;
109331a62963SBryan Wu 
109431a62963SBryan Wu 		for (i = 0; i < ad714x->hw->touchpad_num; i++) {
1095c5c18a06SVaishali Thakkar 			input = devm_input_allocate_device(dev);
1096c5c18a06SVaishali Thakkar 			if (!input)
1097c5c18a06SVaishali Thakkar 				return ERR_PTR(-ENOMEM);
109831a62963SBryan Wu 
1099c5c18a06SVaishali Thakkar 			__set_bit(EV_ABS, input->evbit);
1100c5c18a06SVaishali Thakkar 			__set_bit(EV_KEY, input->evbit);
1101c5c18a06SVaishali Thakkar 			__set_bit(ABS_X, input->absbit);
1102c5c18a06SVaishali Thakkar 			__set_bit(ABS_Y, input->absbit);
1103c5c18a06SVaishali Thakkar 			__set_bit(BTN_TOUCH, input->keybit);
1104c5c18a06SVaishali Thakkar 			input_set_abs_params(input,
110531a62963SBryan Wu 				ABS_X, 0, tp_plat->x_max_coord, 0, 0);
1106c5c18a06SVaishali Thakkar 			input_set_abs_params(input,
110731a62963SBryan Wu 				ABS_Y, 0, tp_plat->y_max_coord, 0, 0);
110831a62963SBryan Wu 
1109c5c18a06SVaishali Thakkar 			input->id.bustype = bus_type;
1110c5c18a06SVaishali Thakkar 			input->id.product = ad714x->product;
1111c5c18a06SVaishali Thakkar 			input->id.version = ad714x->version;
1112c5c18a06SVaishali Thakkar 			input->name = "ad714x_captouch_pad";
1113c5c18a06SVaishali Thakkar 			input->dev.parent = dev;
111431a62963SBryan Wu 
1115c5c18a06SVaishali Thakkar 			error = input_register_device(input);
111631a62963SBryan Wu 			if (error)
1117c5c18a06SVaishali Thakkar 				return ERR_PTR(error);
111831a62963SBryan Wu 
1119c5c18a06SVaishali Thakkar 			tp_drv[i].input = input;
112031a62963SBryan Wu 		}
112131a62963SBryan Wu 	}
112231a62963SBryan Wu 
112331a62963SBryan Wu 	/* all buttons use one input node */
112431a62963SBryan Wu 	if (ad714x->hw->button_num > 0) {
112531a62963SBryan Wu 		struct ad714x_button_plat *bt_plat = ad714x->hw->button;
112631a62963SBryan Wu 
1127c5c18a06SVaishali Thakkar 		input = devm_input_allocate_device(dev);
1128c5c18a06SVaishali Thakkar 		if (!input) {
112931a62963SBryan Wu 			error = -ENOMEM;
1130c5c18a06SVaishali Thakkar 			return ERR_PTR(error);
113131a62963SBryan Wu 		}
113231a62963SBryan Wu 
1133c5c18a06SVaishali Thakkar 		__set_bit(EV_KEY, input->evbit);
113431a62963SBryan Wu 		for (i = 0; i < ad714x->hw->button_num; i++) {
1135c5c18a06SVaishali Thakkar 			bt_drv[i].input = input;
1136c5c18a06SVaishali Thakkar 			__set_bit(bt_plat[i].keycode, input->keybit);
113731a62963SBryan Wu 		}
113831a62963SBryan Wu 
1139c5c18a06SVaishali Thakkar 		input->id.bustype = bus_type;
1140c5c18a06SVaishali Thakkar 		input->id.product = ad714x->product;
1141c5c18a06SVaishali Thakkar 		input->id.version = ad714x->version;
1142c5c18a06SVaishali Thakkar 		input->name = "ad714x_captouch_button";
1143c5c18a06SVaishali Thakkar 		input->dev.parent = dev;
114431a62963SBryan Wu 
1145c5c18a06SVaishali Thakkar 		error = input_register_device(input);
114631a62963SBryan Wu 		if (error)
1147c5c18a06SVaishali Thakkar 			return ERR_PTR(error);
114831a62963SBryan Wu 	}
114931a62963SBryan Wu 
11509b7e31bbSLars-Peter Clausen 	irqflags = plat_data->irqflags ?: IRQF_TRIGGER_FALLING;
11519b7e31bbSLars-Peter Clausen 	irqflags |= IRQF_ONESHOT;
11529b7e31bbSLars-Peter Clausen 
1153c5c18a06SVaishali Thakkar 	error = devm_request_threaded_irq(dev, ad714x->irq, NULL,
1154c5c18a06SVaishali Thakkar 					  ad714x_interrupt_thread,
11559b7e31bbSLars-Peter Clausen 					  irqflags, "ad714x_captouch", ad714x);
115631a62963SBryan Wu 	if (error) {
115731a62963SBryan Wu 		dev_err(dev, "can't allocate irq %d\n", ad714x->irq);
1158c5c18a06SVaishali Thakkar 		return ERR_PTR(error);
115931a62963SBryan Wu 	}
116031a62963SBryan Wu 
116131a62963SBryan Wu 	return ad714x;
116231a62963SBryan Wu }
116331a62963SBryan Wu EXPORT_SYMBOL(ad714x_probe);
116431a62963SBryan Wu 
ad714x_suspend(struct device * dev)1165*c0a150eeSJonathan Cameron static int ad714x_suspend(struct device *dev)
116631a62963SBryan Wu {
1167*c0a150eeSJonathan Cameron 	struct ad714x_chip *ad714x = dev_get_drvdata(dev);
116831a62963SBryan Wu 	unsigned short data;
116931a62963SBryan Wu 
117031a62963SBryan Wu 	dev_dbg(ad714x->dev, "%s enter\n", __func__);
117131a62963SBryan Wu 
117231a62963SBryan Wu 	mutex_lock(&ad714x->mutex);
117331a62963SBryan Wu 
117431a62963SBryan Wu 	data = ad714x->hw->sys_cfg_reg[AD714X_PWR_CTRL] | 0x3;
1175c0409febSDmitry Torokhov 	ad714x->write(ad714x, AD714X_PWR_CTRL, data);
117631a62963SBryan Wu 
117731a62963SBryan Wu 	mutex_unlock(&ad714x->mutex);
117831a62963SBryan Wu 
117931a62963SBryan Wu 	return 0;
118031a62963SBryan Wu }
118131a62963SBryan Wu 
ad714x_resume(struct device * dev)1182*c0a150eeSJonathan Cameron static int ad714x_resume(struct device *dev)
118331a62963SBryan Wu {
1184*c0a150eeSJonathan Cameron 	struct ad714x_chip *ad714x = dev_get_drvdata(dev);
118531a62963SBryan Wu 	dev_dbg(ad714x->dev, "%s enter\n", __func__);
118631a62963SBryan Wu 
118731a62963SBryan Wu 	mutex_lock(&ad714x->mutex);
118831a62963SBryan Wu 
118931a62963SBryan Wu 	/* resume to non-shutdown mode */
119031a62963SBryan Wu 
1191c0409febSDmitry Torokhov 	ad714x->write(ad714x, AD714X_PWR_CTRL,
119231a62963SBryan Wu 			ad714x->hw->sys_cfg_reg[AD714X_PWR_CTRL]);
119331a62963SBryan Wu 
119431a62963SBryan Wu 	/* make sure the interrupt output line is not low level after resume,
119531a62963SBryan Wu 	 * otherwise we will get no chance to enter falling-edge irq again
119631a62963SBryan Wu 	 */
119731a62963SBryan Wu 
11989eff794bSMichael Hennerich 	ad714x->read(ad714x, STG_LOW_INT_STA_REG, &ad714x->l_state, 3);
119931a62963SBryan Wu 
120031a62963SBryan Wu 	mutex_unlock(&ad714x->mutex);
120131a62963SBryan Wu 
120231a62963SBryan Wu 	return 0;
120331a62963SBryan Wu }
1204*c0a150eeSJonathan Cameron 
1205*c0a150eeSJonathan Cameron EXPORT_SIMPLE_DEV_PM_OPS(ad714x_pm, ad714x_suspend, ad714x_resume);
120631a62963SBryan Wu 
120731a62963SBryan Wu MODULE_DESCRIPTION("Analog Devices AD714X Capacitance Touch Sensor Driver");
120831a62963SBryan Wu MODULE_AUTHOR("Barry Song <21cnbao@gmail.com>");
120931a62963SBryan Wu MODULE_LICENSE("GPL");
1210