From 3fce6110ad1197e7d7c365d48c8468b7d4e858f6 Mon Sep 17 00:00:00 2001 From: srt Date: Fri, 10 Sep 2010 15:36:05 +0800 Subject: [PATCH] add touchscreen calibration function --- arch/arm/configs/rk2818_info_defconfig | 4 +- arch/arm/configs/rk2818_info_it50_defconfig | 4 +- drivers/input/touchscreen/calibration_ts.c | 501 +++++++++ drivers/input/touchscreen/calibration_ts.h | 51 + drivers/input/touchscreen/largenum_ts.c | 602 +++++++++++ drivers/input/touchscreen/largenum_ts.h | 152 +++ drivers/input/touchscreen/xpt2046_cbn_ts.c | 1060 +++++++++++++++++++ drivers/input/touchscreen/xpt2046_cbn_ts.h | 56 + 8 files changed, 2426 insertions(+), 4 deletions(-) create mode 100644 drivers/input/touchscreen/calibration_ts.c create mode 100644 drivers/input/touchscreen/calibration_ts.h create mode 100644 drivers/input/touchscreen/largenum_ts.c create mode 100644 drivers/input/touchscreen/largenum_ts.h create mode 100644 drivers/input/touchscreen/xpt2046_cbn_ts.c create mode 100644 drivers/input/touchscreen/xpt2046_cbn_ts.h diff --git a/arch/arm/configs/rk2818_info_defconfig b/arch/arm/configs/rk2818_info_defconfig index f5fa5e576021..ad028e9cc6a5 100755 --- a/arch/arm/configs/rk2818_info_defconfig +++ b/arch/arm/configs/rk2818_info_defconfig @@ -725,8 +725,8 @@ CONFIG_KEYBOARD_RK28ADC=y CONFIG_INPUT_TOUCHSCREEN=y # CONFIG_TOUCHSCREEN_ADS7846 is not set # CONFIG_TOUCHSCREEN_AD7877 is not set -CONFIG_TOUCHSCREEN_XPT2046_SPI=y -# CONFIG_TOUCHSCREEN_XPT2046_CBN_SPI is not set +# CONFIG_TOUCHSCREEN_XPT2046_SPI is not set +CONFIG_TOUCHSCREEN_XPT2046_CBN_SPI=y # CONFIG_TOUCHSCREEN_XPT2046_320X480_SPI is not set # CONFIG_TOUCHSCREEN_XPT2046_320X480_CBN_SPI is not set # CONFIG_TOUCHSCREEN_IT7250 is not set diff --git a/arch/arm/configs/rk2818_info_it50_defconfig b/arch/arm/configs/rk2818_info_it50_defconfig index 502950265c54..be31d586f88b 100644 --- a/arch/arm/configs/rk2818_info_it50_defconfig +++ b/arch/arm/configs/rk2818_info_it50_defconfig @@ -699,8 +699,8 @@ CONFIG_KEYBOARD_RK28ADC_IT50=y CONFIG_INPUT_TOUCHSCREEN=y # CONFIG_TOUCHSCREEN_ADS7846 is not set # CONFIG_TOUCHSCREEN_AD7877 is not set -CONFIG_TOUCHSCREEN_XPT2046_SPI=y -# CONFIG_TOUCHSCREEN_XPT2046_CBN_SPI is not set +# CONFIG_TOUCHSCREEN_XPT2046_SPI is not set +CONFIG_TOUCHSCREEN_XPT2046_CBN_SPI=y # CONFIG_TOUCHSCREEN_XPT2046_320X480_SPI is not set # CONFIG_TOUCHSCREEN_XPT2046_320X480_CBN_SPI is not set # CONFIG_TOUCHSCREEN_IT7250 is not set diff --git a/drivers/input/touchscreen/calibration_ts.c b/drivers/input/touchscreen/calibration_ts.c new file mode 100644 index 000000000000..b39c5e6b02dd --- /dev/null +++ b/drivers/input/touchscreen/calibration_ts.c @@ -0,0 +1,501 @@ +/* + * drivers/input/touchscreen/calibration_ts.c - calibration for rk2818 spi xpt2046 device and console + * + * Copyright (C) 2010 ROCKCHIP, Inc. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ +#include +#include + +#include "calibration_ts.h" +#include "largenum_ts.h" + +#define MAX_POINT_ERROR 6 + +typedef struct { + PLARGENUM pa11, pa12, pa13; + PLARGENUM pa21, pa22, pa23; + PLARGENUM pa31, pa32, pa33; +} MATRIX33, *PMATRIX33; + +typedef struct { + int a1; + int b1; + int c1; + int a2; + int b2; + int c2; + int delta; +} +CALIBRATION_PARAMETER, *PCALIBRATION_PARAMETER; + +static unsigned char v_Calibrated = 0; +static CALIBRATION_PARAMETER v_CalcParam; + +unsigned char +ErrorAnalysis( + int cCalibrationPoints, //@PARM The number of calibration points + int *pScreenXBuffer, //@PARM List of screen X coords displayed + int *pScreenYBuffer, //@PARM List of screen Y coords displayed + int *pUncalXBuffer, //@PARM List of X coords collected + int *pUncalYBuffer //@PARM List of Y coords collected + ); + +void +ComputeMatrix33( + PLARGENUM pResult, + PMATRIX33 pMatrix + ); + +unsigned char +TouchPanelSetCalibration( + int cCalibrationPoints, //@PARM The number of calibration points + int *pScreenXBuffer, //@PARM List of screen X coords displayed + int *pScreenYBuffer, //@PARM List of screen Y coords displayed + int *pUncalXBuffer, //@PARM List of X coords collected + int *pUncalYBuffer //@PARM List of Y coords collected + ) +{ + LARGENUM a11; + LARGENUM a21, a22; + LARGENUM a31, a32, a33; + LARGENUM b11, b12, b13; + LARGENUM b21, b22, b23; + LARGENUM lnScreenX; + LARGENUM lnScreenY; + LARGENUM lnTouchX; + LARGENUM lnTouchY; + LARGENUM lnTemp; + LARGENUM delta; + LARGENUM a1, b1, c1; + LARGENUM a2, b2, c2; + MATRIX33 Matrix; + int cShift; + int minShift; + int i; + + + //DEBUGMSG(1,(__TEXT("calibrating %d point set\r\n"), cCalibrationPoints)); + + // + // If the calibration data is being cleared, set the flag so + // that the conversion operation is a noop. + // + + if ( cCalibrationPoints == 0 ) + { + v_Calibrated = 0; + return 1; + } + + // + // Compute these large numbers + // + LargeNumSet(&a11, 0); + LargeNumSet(&a21, 0); + LargeNumSet(&a31, 0); + LargeNumSet(&a22, 0); + LargeNumSet(&a32, 0); + LargeNumSet(&a33, cCalibrationPoints); + LargeNumSet(&b11, 0); + LargeNumSet(&b12, 0); + LargeNumSet(&b13, 0); + LargeNumSet(&b21, 0); + LargeNumSet(&b22, 0); + LargeNumSet(&b23, 0); + for(i=0; i minShift){ + minShift = cShift; + } + cShift = LargeNumBits(&b1) - MAX_COEFF_PRECISION; + if(cShift > minShift){ + minShift = cShift; + } + cShift = LargeNumBits(&a2) - MAX_COEFF_PRECISION; + if(cShift > minShift){ + minShift = cShift; + } + cShift = LargeNumBits(&b2) - MAX_COEFF_PRECISION; + if(cShift > minShift){ + minShift = cShift; + } + cShift = LargeNumBits(&c1) - MAX_TERM_PRECISION; + if(cShift > minShift){ + minShift = cShift; + } + cShift = LargeNumBits(&c2) - MAX_TERM_PRECISION; + if(cShift > minShift){ + minShift = cShift; + } + cShift = LargeNumBits(&delta) - 31; + if(cShift > minShift){ + minShift = cShift; + } + + // + // Now, shift count is determined, shift all the numbers + // right to obtain the 32-bit signed values + // + if(minShift){ + LargeNumRAShift(&a1, minShift); + LargeNumRAShift(&a2, minShift); + LargeNumRAShift(&b1, minShift); + LargeNumRAShift(&b2, minShift); + LargeNumRAShift(&c1, minShift); + LargeNumRAShift(&c2, minShift); + LargeNumRAShift(&delta, minShift); + } + v_CalcParam.a1 = a1.u.s32.u[0]; + v_CalcParam.b1 = b1.u.s32.u[0]; + v_CalcParam.c1 = c1.u.s32.u[0]; + v_CalcParam.a2 = a2.u.s32.u[0]; + v_CalcParam.b2 = b2.u.s32.u[0]; + v_CalcParam.c2 = c2.u.s32.u[0]; + v_CalcParam.delta = delta.u.s32.u[0]; + + // Don't allow delta to be zero, since it gets used as a divisor + if( ! v_CalcParam.delta ) + { + //RETAILMSG(1,(__TEXT("TouchPanelSetCalibration: delta of 0 invalid\r\n"))); + //RETAILMSG(1,(__TEXT("\tCalibration failed.\r\n"))); + v_CalcParam.delta = 1; // any non-zero value to prevents DivByZero traps later + v_Calibrated = 0; + } + else + v_Calibrated = 1; + + return ErrorAnalysis( + cCalibrationPoints, + pScreenXBuffer, + pScreenYBuffer, + pUncalXBuffer, + pUncalYBuffer + ); +} + +void +ComputeMatrix33( + PLARGENUM pResult, + PMATRIX33 pMatrix + ) +{ + LARGENUM lnTemp; + + LargeNumMult(pMatrix->pa11, pMatrix->pa22, &lnTemp); + LargeNumMult(pMatrix->pa33, &lnTemp, pResult); + LargeNumMult(pMatrix->pa21, pMatrix->pa32, &lnTemp); + LargeNumMult(pMatrix->pa13, &lnTemp, &lnTemp); + LargeNumAdd(pResult, &lnTemp, pResult); + LargeNumMult(pMatrix->pa12, pMatrix->pa23, &lnTemp); + LargeNumMult(pMatrix->pa31, &lnTemp, &lnTemp); + LargeNumAdd(pResult, &lnTemp, pResult); + LargeNumMult(pMatrix->pa13, pMatrix->pa22, &lnTemp); + LargeNumMult(pMatrix->pa31, &lnTemp, &lnTemp); + LargeNumSub(pResult, &lnTemp, pResult); + LargeNumMult(pMatrix->pa12, pMatrix->pa21, &lnTemp); + LargeNumMult(pMatrix->pa33, &lnTemp, &lnTemp); + LargeNumSub(pResult, &lnTemp, pResult); + LargeNumMult(pMatrix->pa23, pMatrix->pa32, &lnTemp); + LargeNumMult(pMatrix->pa11, &lnTemp, &lnTemp); + LargeNumSub(pResult, &lnTemp, pResult); +} + +void +TouchPanelCalibrateAPoint( + int UncalX, //@PARM The uncalibrated X coordinate + int UncalY, //@PARM The uncalibrated Y coordinate + int *pCalX, //@PARM The calibrated X coordinate + int *pCalY //@PARM The calibrated Y coordinate + ) +{ + int x, y; + + if ( !v_Calibrated ) + { + *pCalX = 200; //UncalX; + *pCalY = 160; //UncalY; + return; + } + // + // Note the *4 in the expression below. This is a workaround + // on behalf of gwe. It provides a form of + // sub-pixel accuracy desirable for inking + // + x = (v_CalcParam.a1 * UncalX + v_CalcParam.b1 * UncalY + + v_CalcParam.c1) * 4 / v_CalcParam.delta; + y = (v_CalcParam.a2 * UncalX + v_CalcParam.b2 * UncalY + + v_CalcParam.c2) * 4 / v_CalcParam.delta; + if ( x < 0 ){ + x = 0; + } + + if ( y < 0 ){ + y = 0; + } + + *pCalX = x; + *pCalY = y; +} + +unsigned char +ErrorAnalysis( + int cCalibrationPoints, //@PARM The number of calibration points + int *pScreenXBuffer, //@PARM List of screen X coords displayed + int *pScreenYBuffer, //@PARM List of screen Y coords displayed + int *pUncalXBuffer, //@PARM List of X coords collected + int *pUncalYBuffer //@PARM List of Y coords collected + ) +{ + int i; + unsigned int maxErr, err; + int x,y; + int dx,dy; + unsigned int errThreshold = MAX_POINT_ERROR; // Can be overridden by registry entry +#if 0 + unsigned int status, ValType, ValLen; + + //HKEY regKey; + + + // See if there is a Maximum Calibration Error specified in the registry + //status = RegOpenKeyEx( + // HKEY_LOCAL_MACHINE, + // __TEXT("HARDWARE\\DEVICEMAP\\TOUCH"), + // 0, + // 0, + // ®Key); + if ( status == ERROR_SUCCESS ) { + ValLen = sizeof(errThreshold); + status = RegQueryValueEx( + regKey, + __TEXT("MaxCalError"), + NULL, + &ValType, + (PUCHAR)&errThreshold, + &ValLen); + // We don't care what happened. Either we have a new value or we have the default value. + RegCloseKey(regKey); + } + + RETAILMSG(1,(__TEXT("Maximum Allowed Error %d:\r\n"), + errThreshold)); + DEBUGMSG(1,(__TEXT("Calibration Results:\r\n"))); +#endif + + maxErr = 0; + //DEBUGMSG(1,(__TEXT(" Screen => Mapped\r\n"))); + for(i=0; i (%4d, %4d)\n", + //DEBUGMSG(1,(__TEXT("(%4d, %4d) => (%4d, %4d)\r\n"), + pScreenXBuffer[i], + pScreenYBuffer[i], + x, + y + ); + dx = x - pScreenXBuffer[i]; + dy = y - pScreenYBuffer[i]; + err = dx * dx + dy * dy; + if(err > maxErr){ + maxErr = err; + } + } + //DEBUGMSG(1,(__TEXT("Maximum error (square of Euclidean distance in screen units) = %u\r\n"), + // maxErr + // )); + + if (maxErr < (errThreshold * errThreshold)) + { + return 1; + } + else + { + memset(&v_CalcParam, 0, sizeof(v_CalcParam)); + v_Calibrated = 0; + + return 0; + } +} + +#if 0 +int main(void) +{ + unsigned char ret; + int cali_num = 4; + int screen_x[4], screen_y[4]; + int uncali_x[4], uncali_y[4]; + int tst_uncali_x, tst_uncali_y, tst_cali_x, tst_cali_y; + + screen_x[0] = 15; screen_y[0] = 15; + screen_x[1] = 15; screen_y[1] = 465; + screen_x[2] = 785; screen_y[2] = 15; + screen_x[3] = 785; screen_y[3] = 465; + + uncali_x[0] = 173; uncali_y[0] = 417; + uncali_x[1] = 148; uncali_y[1] = 3867; + uncali_x[2] = 3903; uncali_y[2] = 365; + uncali_x[3] = 3924; uncali_y[3] = 3863; + + ret = TouchPanelSetCalibration(4, screen_x, + screen_y, uncali_x, uncali_y); + if (ret == 1) + printf("TouchPanelSetCalibration OK.\n"); + else + printf("TouchPanelSetCalibration FAIL.\n"); + + tst_uncali_x = 2033; + tst_uncali_y = 2132; + + TouchPanelCalibrateAPoint(tst_uncali_x, tst_uncali_y, + &tst_cali_x, &tst_cali_y); + + printf("(%d, %d) >> (%d, %d)\n", tst_uncali_x, tst_uncali_y, + tst_cali_x/4, tst_cali_y/4); + + tst_uncali_x = 170; + tst_uncali_y = 418; + + TouchPanelCalibrateAPoint(tst_uncali_x, tst_uncali_y, + &tst_cali_x, &tst_cali_y); + + printf("(%d, %d) >> (%d, %d)\n", tst_uncali_x, tst_uncali_y, + tst_cali_x/4, tst_cali_y/4); + + tst_uncali_x = 500; + tst_uncali_y = 707; + + TouchPanelCalibrateAPoint(tst_uncali_x, tst_uncali_y, + &tst_cali_x, &tst_cali_y); + + printf("(%d, %d) >> (%d, %d)\n", tst_uncali_x, tst_uncali_y, + tst_cali_x/4, tst_cali_y/4); + + tst_uncali_x = 3636; + tst_uncali_y = 2150; + + TouchPanelCalibrateAPoint(tst_uncali_x, tst_uncali_y, + &tst_cali_x, &tst_cali_y); + + printf("(%d, %d) >> (%d, %d)\n", tst_uncali_x, tst_uncali_y, + tst_cali_x/4, tst_cali_y/4); + + return 0; +} +#endif + diff --git a/drivers/input/touchscreen/calibration_ts.h b/drivers/input/touchscreen/calibration_ts.h new file mode 100644 index 000000000000..8c3b6e948662 --- /dev/null +++ b/drivers/input/touchscreen/calibration_ts.h @@ -0,0 +1,51 @@ +/* + * drivers/input/touchscreen/calibration_ts.h + * + * Copyright (C) 2010 ROCKCHIP, Inc. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef __DRIVERS_TOUCHSCREEN_CALIBRATION_TS_H +#define __DRIVERS_TOUCHSCREEN_CALIBRATION_TS_H + +#define TWO_DIMENSIONAL_CALIBRATION 1 + +#define ADC_PRECISION 12 // Precision of ADC output (in bits) +#define MAX_TERM_PRECISION 27 // Reserve 1 bit for sign and two bits for + // three terms (there are three terms in + // each of x and y mapping functions.) + +// +// All a1, a2, b1, and b2 must have less than MAX_COEFF_PRECISION bits since +// they all are multiplied with either an X or a Y to form a term. +// Both c1 and c2 can have up to MAX_TERM_PRECISION since they each alone +// forms a term. +// +#define MAX_COEFF_PRECISION (MAX_TERM_PRECISION - ADC_PRECISION) + +unsigned char +TouchPanelSetCalibration( + int cCalibrationPoints, //@PARM The number of calibration points + int *pScreenXBuffer, //@PARM List of screen X coords displayed + int *pScreenYBuffer, //@PARM List of screen Y coords displayed + int *pUncalXBuffer, //@PARM List of X coords collected + int *pUncalYBuffer //@PARM List of Y coords collected + ); + +void +TouchPanelCalibrateAPoint( + int UncalX, //@PARM The uncalibrated X coordinate + int UncalY, //@PARM The uncalibrated Y coordinate + int *pCalX, //@PARM The calibrated X coordinate + int *pCalY //@PARM The calibrated Y coordinate + ); + +#endif diff --git a/drivers/input/touchscreen/largenum_ts.c b/drivers/input/touchscreen/largenum_ts.c new file mode 100644 index 000000000000..eacae6988d0c --- /dev/null +++ b/drivers/input/touchscreen/largenum_ts.c @@ -0,0 +1,602 @@ +/* + * drivers/input/touchscreen/largenum_ts.c - largenum for rk2818 spi xpt2046 device and console + * + * Copyright (C) 2010 ROCKCHIP, Inc. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ +#include + +#include "largenum_ts.h" + +unsigned int +LargeNumSignedFormat( + PLARGENUM pNum + ); + +PLARGENUM +LargeNumSet( + PLARGENUM pNum, + int n + ) +{ + int i; + + if(n < 0){ + pNum->u.s32.u[0] = -n; + pNum->fNegative = 1; + } else{ + pNum->u.s32.u[0] = n; + pNum->fNegative=0; + } + for(i=1; iu.s32.u[i] = 0; + } + return pNum; +} + +unsigned char +IsLargeNumNotZero( + PLARGENUM pNum + ) +{ + int i; + + for(i=0; iu.s32.u[i]){ + return 1; + } + } + return 0; +} + +unsigned char +IsLargeNumNegative( + PLARGENUM pNum + ) +{ + return (pNum->fNegative ? 1 : 0); + +} + +unsigned char +IsLargeNumMagGreaterThan( + PLARGENUM pNum1, + PLARGENUM pNum2 + ) +{ + int i; + + for(i=SIZE_OF_LARGENUM-1; i>=0; i--){ + if(pNum1->u.s32.u[i] > pNum2->u.s32.u[i]){ + return 1; + } else if(pNum1->u.s32.u[i] < pNum2->u.s32.u[i]){ + return 0; + } + } + return 0; +} + +unsigned char +IsLargeNumMagLessThan( + PLARGENUM pNum1, + PLARGENUM pNum2 + ) +{ + int i; + + for(i=SIZE_OF_LARGENUM-1; i>=0; i--){ + if(pNum1->u.s32.u[i] < pNum2->u.s32.u[i]){ + return 1; + } else if(pNum1->u.s32.u[i] > pNum2->u.s32.u[i]){ + return 0; + } + } + return 0; +} + +PLARGENUM +LargeNumMagInc( + PLARGENUM pNum + ) +{ + unsigned int c; + int i; + + c = 1; + for(i=0; iu.s32.u[i] += c; + if(pNum->u.s32.u[i]){ + c = 0; + } + } + return pNum; +} + +PLARGENUM +LargeNumMagAdd( + PLARGENUM pNum1, + PLARGENUM pNum2, + PLARGENUM pResult + ) +{ + unsigned int c; + unsigned int i; + unsigned int a; + unsigned int b; + + c = 0; + for(i=0; iu.s32.u[i]; + b = pNum2->u.s32.u[i]; + pResult->u.s32.u[i] = a + b + c; + if(c){ + if(pResult->u.s32.u[i] <= a){ + c = 1; + } else { + c = 0; + } + + } else { + if(pResult->u.s32.u[i] < a){ + c = 1; + } else { + c = 0; + } + + } + } + return pResult; +} + +PLARGENUM +LargeNumMagSub( + PLARGENUM pNum1, + PLARGENUM pNum2, + PLARGENUM pResult + ) +{ + unsigned int c; + unsigned int i; + unsigned int a; + unsigned int b; + + c = 1; + for(i=0; iu.s32.u[i]; + b = ~(pNum2->u.s32.u[i]); + pResult->u.s32.u[i] = a + b + c; + if(c){ + if(pResult->u.s32.u[i] <= a){ + c = 1; + } else { + c = 0; + } + + } else { + if(pResult->u.s32.u[i] < a){ + c = 1; + } else { + c = 0; + } + + } + } + return pResult; +} + +PLARGENUM +LargeNumAdd( + PLARGENUM pNum1, + PLARGENUM pNum2, + PLARGENUM pResult + ) +{ + unsigned char fNegative1; + unsigned char fNegative2; + + fNegative1 = IsLargeNumNegative(pNum1); + fNegative2 = IsLargeNumNegative(pNum2); + + if(fNegative1 != fNegative2){ + if(IsLargeNumMagGreaterThan(pNum1, pNum2)){ + LargeNumMagSub(pNum1, pNum2, pResult); + } else { + LargeNumMagSub(pNum2, pNum1, pResult); + fNegative1 = !fNegative1; + } + } else { + LargeNumMagAdd(pNum1, pNum2, pResult); + } + if(!IsLargeNumNotZero(pResult)){ + pResult->fNegative = 0; + } else { + pResult->fNegative = fNegative1; + } + return pResult; +} + +PLARGENUM +LargeNumSub( + PLARGENUM pNum1, + PLARGENUM pNum2, + PLARGENUM pResult + ) +{ + unsigned char fNegative1; + unsigned char fNegative2; + + fNegative1 = IsLargeNumNegative(pNum1); + fNegative2 = IsLargeNumNegative(pNum2); + + if(fNegative1 == fNegative2){ + if(IsLargeNumMagGreaterThan(pNum1, pNum2)){ + LargeNumMagSub(pNum1, pNum2, pResult); + } else { + LargeNumMagSub(pNum2, pNum1, pResult); + fNegative1 = !fNegative1; + } + } else { + LargeNumMagAdd(pNum1, pNum2, pResult); + } + if(!IsLargeNumNotZero(pResult)){ + pResult->fNegative = 0; + } else { + pResult->fNegative = fNegative1; + } + return pResult; +} + +PLARGENUM +LargeNumMulUint32( + unsigned int a, + unsigned int b, + PLARGENUM pResult + ) +{ + unsigned int a1, a0; + unsigned int b1, b0; + unsigned int r0; + unsigned int r1; + unsigned int r2; + unsigned int c; + int i; + + a1 = a >> 16; + a0 = a & 0xffff; + b1 = b >> 16; + b0 = b & 0xffff; + + r0 = a0 * b0; + r1 = a1 * b0 + a0 * b1; + r2 = a1 * b1; + + pResult->u.s32.u[0] = (r1 << 16) + r0; + if(pResult->u.s32.u[0] < r0){ + c = 1; + } else { + c = 0; + } + pResult->u.s32.u[1] = r2 + (r1 >> 16) + c; + for(i=2; iu.s32.u[i] = 0; + } + pResult->fNegative = 0; + + return pResult; +} + +PLARGENUM +LargeNumMulInt32( + int a, + int b, + PLARGENUM pResult + ) +{ + unsigned char fNegativeA; + unsigned char fNegativeB; + + if(a < 0){ + fNegativeA = 1; + a = -a; + } else { + fNegativeA = 0; + } + + if(b < 0){ + fNegativeB = 1; + b = -b; + } else { + fNegativeB = 0; + } + + LargeNumMulUint32(a, b, pResult); + + if(!IsLargeNumNotZero(pResult)){ + pResult->fNegative = 0; + } else { + if(fNegativeA != fNegativeB){ + pResult->fNegative = 1; + } + } + return pResult; +} + +PLARGENUM +LargeNumMult( + PLARGENUM pNum1, + PLARGENUM pNum2, + PLARGENUM pResult + ) +{ + LARGENUM lNumTemp; + LARGENUM lNumSum; + LARGENUM lNumCarry; + int i; + int j; + + LargeNumSet(&lNumCarry, 0); + for(i=0; iu.s32.u[j], pNum2->u.s32.u[i-j], &lNumTemp); + LargeNumMagAdd(&lNumTemp, &lNumSum, &lNumSum); + } + LargeNumMagAdd(&lNumCarry, &lNumSum, &lNumSum); + for(j=0; ju.s32.u[i] = lNumSum.u.s32.u[0]; + } + + if(!IsLargeNumNotZero(pResult)){ + pResult->fNegative = 0; + } else { + pResult->fNegative = (pNum1->fNegative != pNum2->fNegative); + } + return pResult; +} + +unsigned int +LargeNumSignedFormat( + PLARGENUM pNum + ) +{ + int i; + unsigned int c; + + if(IsLargeNumNegative(pNum)){ + c = 1; + for(i=0; iu.s32.u[i] = ~(pNum->u.s32.u[i]) + c; + if(pNum->u.s32.u[i]){ + c = 0; + } + } + return 0xffffffff; + } else { + return 0; + } +} + +void +LargeNumRAShift( + PLARGENUM pNum, + int count + ) +{ + int shift32; + int countLeft; + unsigned int filler; + int i; + int j; + + filler = LargeNumSignedFormat(pNum); + + shift32 = count / 32; + + if(shift32 > (SIZE_OF_LARGENUM - 1)){ + for(i=0; iu.s32.u[i] = filler; + } + return; + } + + count %= 32; + countLeft = 32 - count; + for(i=0, j=shift32;;){ + pNum->u.s32.u[i] = (pNum->u.s32.u[j] >> count); + if(j<(SIZE_OF_LARGENUM-1)){ + j++; + if (countLeft < 32) { + // Shifting by >= 32 is undefined. + pNum->u.s32.u[i] |= pNum->u.s32.u[j] << countLeft; + } + i++; + } else { + if (countLeft < 32) { + // Shifting by >= 32 is undefined. + pNum->u.s32.u[i] |= filler << countLeft; + } + i++; + break; + } + } + + for(; iu.s32.u[i] = filler; + } +} + +unsigned int +LargeNumDivInt32( + PLARGENUM pNum, + int divisor, + PLARGENUM pResult + ) +{ + unsigned int s[2*SIZE_OF_LARGENUM]; + unsigned int r; + unsigned int q; + unsigned int d; + unsigned char sd; + int i; + + for(i=0; i<2*SIZE_OF_LARGENUM; i++){ + s[i] = pNum->u.s16.s[i]; + } + + if(divisor < 0){ + divisor = -divisor; + sd = 1; + } else if(divisor == 0){ + // + // This is a divide-by-zero error + // + for(i=0; iu.s32.u[i] = 0xffffffff; + } + return 0xffffffff; + } else { + sd = 0; + } + + r = 0; + for(i=(2*SIZE_OF_LARGENUM-1); i>=0; i--){ + d = (r << 16) + s[i]; + q = d / divisor; + r = d - q * divisor; + s[i] = q; + } + + for(i=0; i<2*SIZE_OF_LARGENUM; i++){ + pResult->u.s16.s[i] = s[i]; + } + + if(pNum->fNegative){ + LargeNumMagInc(pResult); + r = divisor - r; + if(sd == 0 && IsLargeNumNotZero(pResult)){ + pResult->fNegative = 1; + } else { + pResult->fNegative = 0; + } + + } else { + if(sd && IsLargeNumNotZero(pResult)){ + pResult->fNegative = 1; + } else { + pResult->fNegative = 0; + } + } + + return r; +} + +int +LargeNumBits( + PLARGENUM pNum + ) +{ + static unsigned int LargeNumMask[32] = { + 0x00000001, + 0x00000002, + 0x00000004, + 0x00000008, + 0x00000010, + 0x00000020, + 0x00000040, + 0x00000080, + 0x00000100, + 0x00000200, + 0x00000400, + 0x00000800, + 0x00001000, + 0x00002000, + 0x00004000, + 0x00008000, + 0x00010000, + 0x00020000, + 0x00040000, + 0x00080000, + 0x00100000, + 0x00200000, + 0x00400000, + 0x00800000, + 0x01000000, + 0x02000000, + 0x04000000, + 0x08000000, + 0x10000000, + 0x20000000, + 0x40000000, + 0x80000000, + }; + + int i; + int j; + unsigned int u; + + for(i=(SIZE_OF_LARGENUM-1); i>=0; i--){ + u = pNum->u.s32.u[i]; + if(u){ + for(j=31; j>=0; j--){ + if(u & (LargeNumMask[j])){ + return i * 32 + j + 1; + } + } + } + } + return 0; +} + +char * +LargeNumToAscii( + PLARGENUM pNum + ) +{ + static char buf[SIZE_OF_LARGENUM * 10 + 2]; + LARGENUM lNum; + char *p; + char *q; + unsigned int r; + int s; + + p = buf + sizeof(buf) - 1; + *p= 0; + + lNum = *pNum; + + s = pNum->fNegative; + lNum.fNegative = 0; + + while(IsLargeNumNotZero(&lNum)){ + r = LargeNumDivInt32(&lNum, 10, &lNum); + p--; + *p = r + '0'; + } + + q = buf; + + if(s){ + *q++='-'; + } + while(*p){ + //ASSERT(q <= p); + //PREFAST_SUPPRESS(394, "q is <= p"); + *q++ = *p++; + } + + if((q == buf) || (s && q == &(buf[1]))){ + *q++ = '0'; + } + *q = 0; + return buf; +} diff --git a/drivers/input/touchscreen/largenum_ts.h b/drivers/input/touchscreen/largenum_ts.h new file mode 100644 index 000000000000..60e3e098144b --- /dev/null +++ b/drivers/input/touchscreen/largenum_ts.h @@ -0,0 +1,152 @@ +/* + * drivers/input/touchscreen/largenum_ts.h + * + * Copyright (C) 2010 ROCKCHIP, Inc. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef __DRIVERS_TOUCHSCREEN_LARGENUM_TS_H +#define __DRIVERS_TOUCHSCREEN_LARGENUM_TS_H + +#define SIZE_OF_LARGENUM 3 + +typedef struct +{ + unsigned char fNegative; + union + { + struct + { + unsigned short s[2 * SIZE_OF_LARGENUM]; + } + s16; + + struct + { + unsigned int u[SIZE_OF_LARGENUM]; + } + s32; + + } + u; +} +LARGENUM, *PLARGENUM; + +// +// Function prototypes +// +PLARGENUM +LargeNumSet( + PLARGENUM pNum, + int n + ); + +unsigned char +IsLargeNumNotZero( + PLARGENUM pNum + ); + +unsigned char +IsLargeNumNegative( + PLARGENUM pNum + ); + +unsigned char +IsLargeNumMagGreaterThan( + PLARGENUM pNum1, + PLARGENUM pNum2 + ); + +unsigned char +IsLargeNumMagLessThan( + PLARGENUM pNum1, + PLARGENUM pNum2 + ); + +PLARGENUM +LargeNumMagInc( + PLARGENUM pNum + ); + +char * +LargeNumToAscii( + PLARGENUM pNum + ); + + +PLARGENUM +LargeNumMagAdd( + PLARGENUM pNum1, + PLARGENUM pNum2, + PLARGENUM pResult + ); + +PLARGENUM +LargeNumMagSub( + PLARGENUM pNum1, + PLARGENUM pNum2, + PLARGENUM pResult + ); + +PLARGENUM +LargeNumAdd( + PLARGENUM pNum1, + PLARGENUM pNum2, + PLARGENUM pResult + ); + +PLARGENUM +LargeNumSub( + PLARGENUM pNum1, + PLARGENUM pNum2, + PLARGENUM pResult + ); + +PLARGENUM +LargeNumMulUint32( + unsigned int a, + unsigned int b, + PLARGENUM pResult + ); + +PLARGENUM +LargeNumMulInt32( + int a, + int b, + PLARGENUM pResult + ); + +PLARGENUM +LargeNumMult( + PLARGENUM pNum1, + PLARGENUM pNum2, + PLARGENUM pResult + ); + +void +LargeNumRAShift( + PLARGENUM pNum, + int count + ); + +unsigned int +LargeNumDivInt32( + PLARGENUM pNum, + int divisor, + PLARGENUM pResult + ); + +int +LargeNumBits( + PLARGENUM pNum + ); + +#endif diff --git a/drivers/input/touchscreen/xpt2046_cbn_ts.c b/drivers/input/touchscreen/xpt2046_cbn_ts.c new file mode 100644 index 000000000000..8a6b9b8575e6 --- /dev/null +++ b/drivers/input/touchscreen/xpt2046_cbn_ts.c @@ -0,0 +1,1060 @@ +/* + * drivers/input/touchscreen/xpt2046_cbn_ts.c - driver for rk2818 spi xpt2046 calibration device and console + * + * Copyright (C) 2010 ROCKCHIP, Inc. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "xpt2046_cbn_ts.h" +#include "calibration_ts.h" +/* + * This code has been heavily tested on a Nokia 770, and lightly + * tested on other xpt2046 devices (OSK/Mistral, Lubbock). + * TSC2046 is just newer xpt2046 silicon. + * Support for ads7843 tested on Atmel at91sam926x-EK. + * Support for ads7845 has only been stubbed in. + * + * IRQ handling needs a workaround because of a shortcoming in handling + * edge triggered IRQs on some platforms like the OMAP1/2. These + * platforms don't handle the ARM lazy IRQ disabling properly, thus we + * have to maintain our own SW IRQ disabled status. This should be + * removed as soon as the affected platform's IRQ handling is fixed. + * + * app note sbaa036 talks in more detail about accurate sampling... + * that ought to help in situations like LCDs inducing noise (which + * can also be helped by using synch signals) and more generally. + * This driver tries to utilize the measures described in the app + * note. The strength of filtering can be set in the board-* specific + * files. + */ +#define XPT2046_DEBUG 0 +#if XPT2046_DEBUG + #define xpt2046printk(msg...) printk(msg); +#else + #define xpt2046printk(msg...) +#endif + +//#define TS_POLL_DELAY (15 * 1000000) /* ns delay before the first sample */ +//#define TS_POLL_PERIOD (15 * 1000000) /* ns delay between samples */ +#define TS_POLL_DELAY (10 * 1000000) /* ns delay before the first sample */ +#define TS_POLL_PERIOD (20 * 1000000) /* ns delay between samples */ + + +#define DEBOUNCE_REPTIME 3 +/* this driver doesn't aim at the peak continuous sample rate */ +#define SAMPLE_BITS (8 /*cmd*/ + 16 /*sample*/ + 2 /* before, after */) + +struct ts_event { + /* For portability, we can't read 12 bit values using SPI (which + * would make the controller deliver them as native byteorder u16 + * with msbs zeroed). Instead, we read them as two 8-bit values, + * *** WHICH NEED BYTESWAPPING *** and range adjustment. + */ + u16 x; + u16 y; + int ignore; +}; + +/* + * We allocate this separately to avoid cache line sharing issues when + * driver is used with DMA-based SPI controllers (like atmel_spi) on + * systems where main memory is not DMA-coherent (most non-x86 boards). + */ +struct xpt2046_packet { + u8 read_x, read_y, pwrdown; + u16 dummy; /* for the pwrdown read */ + struct ts_event tc; +}; + +struct xpt2046 { + struct input_dev *input; + char phys[32]; + char name[32]; + + struct spi_device *spi; + + u16 model; + bool swap_xy; + + struct xpt2046_packet *packet; + + struct spi_transfer xfer[18]; + struct spi_message msg[5]; + struct spi_message *last_msg; + int msg_idx; + int read_cnt; + int read_rep; + int last_read; + + u16 debounce_max; + u16 debounce_tol; + u16 debounce_rep; + + u16 penirq_recheck_delay_usecs; + + spinlock_t lock; + struct hrtimer timer; + unsigned pendown:1; /* P: lock */ + unsigned pending:1; /* P: lock */ +// FIXME remove "irq_disabled" + unsigned irq_disabled:1; /* P: lock */ + unsigned disabled:1; + unsigned is_suspended:1; + + int (*filter)(void *data, int data_idx, int *val); + void *filter_data; + void (*filter_cleanup)(void *data); + int (*get_pendown_state)(void); + int gpio_pendown; + + void (*wait_for_sync)(void); +}; + +/* leave chip selected when we're done, for quicker re-select? */ +#if 0 +#define CS_CHANGE(xfer) ((xfer).cs_change = 1) +#else +#define CS_CHANGE(xfer) ((xfer).cs_change = 0) +#endif + +/*--------------------------------------------------------------------------*/ + +/* The xpt2046 has touchscreen and other sensors. + * Earlier xpt2046 chips are somewhat compatible. + */ +#define XPT2046_START (1 << 7) +#define XPT2046_A2A1A0_d_y (1 << 4) /* differential */ +#define XPT2046_A2A1A0_d_z1 (3 << 4) /* differential */ +#define XPT2046_A2A1A0_d_z2 (4 << 4) /* differential */ +#define XPT2046_A2A1A0_d_x (5 << 4) /* differential */ +#define XPT2046_A2A1A0_temp0 (0 << 4) /* non-differential */ +#define XPT2046_A2A1A0_vbatt (2 << 4) /* non-differential */ +#define XPT2046_A2A1A0_vaux (6 << 4) /* non-differential */ +#define XPT2046_A2A1A0_temp1 (7 << 4) /* non-differential */ +#define XPT2046_8_BIT (1 << 3) +#define XPT2046_12_BIT (0 << 3) +#define XPT2046_SER (1 << 2) /* non-differential */ +#define XPT2046_DFR (0 << 2) /* differential */ +#define XPT2046_PD10_PDOWN (0 << 0) /* lowpower mode + penirq */ +#define XPT2046_PD10_ADC_ON (1 << 0) /* ADC on */ +#define XPT2046_PD10_REF_ON (2 << 0) /* vREF on + penirq */ +#define XPT2046_PD10_ALL_ON (3 << 0) /* ADC + vREF on */ + +#define MAX_12BIT ((1<<12)-1) + +/* leave ADC powered up (disables penirq) between differential samples */ +#define READ_12BIT_DFR(x, adc, vref) (XPT2046_START | XPT2046_A2A1A0_d_ ## x \ + | XPT2046_12_BIT | XPT2046_DFR | \ + (adc ? XPT2046_PD10_ADC_ON : 0) | (vref ? XPT2046_PD10_REF_ON : 0)) + +#define READ_Y(vref) (READ_12BIT_DFR(y, 1, vref)) +#define READ_Z1(vref) (READ_12BIT_DFR(z1, 1, vref)) +#define READ_Z2(vref) (READ_12BIT_DFR(z2, 1, vref)) + +#define READ_X(vref) (READ_12BIT_DFR(x, 1, vref)) +#define PWRDOWN (READ_12BIT_DFR(y, 0, 0)) /* LAST */ + +/* single-ended samples need to first power up reference voltage; + * we leave both ADC and VREF powered + */ +#define READ_12BIT_SER(x) (XPT2046_START | XPT2046_A2A1A0_ ## x \ + | XPT2046_12_BIT | XPT2046_SER) + +#define REF_ON (READ_12BIT_DFR(x, 1, 1)) +#define REF_OFF (READ_12BIT_DFR(y, 0, 0)) + +/*--------------------------------------------------------------------------*/ +/* + * touchscreen sensors use differential conversions. + */ + +struct dfr_req { + u8 command; + u8 pwrdown; + u16 dummy; /* for the pwrdown read */ + __be16 sample; + struct spi_message msg; + struct spi_transfer xfer[4]; +}; +typedef struct +{ + s16 x; + s16 y; +}POINT; + +#if (0) +static struct xpt2046_platform_data xpt2046_info = { + .model = 2046, + .keep_vref_on = 1, + .swap_xy = 0, + .x_min = 0, + .x_max = 800, + .y_min = 0, + .y_max = 480, + .debounce_max = 7, + .debounce_rep = DEBOUNCE_REPTIME, + .debounce_tol = 20, + .gpio_pendown = RK2818_PIN_PE3, + .penirq_recheck_delay_usecs = 1, + +}; +#endif /* (0) */ +static void xpt2046_enable(struct xpt2046 *ts); +static void xpt2046_disable(struct xpt2046 *ts); + +static POINT gADPoint; +int screen_x[] = { 50, 750, 50, 750, 400}; +int screen_y[] = { 40, 40, 440, 440, 240}; +int uncali_x[] = {329, 3750, 331, 3757, 2046}; +int uncali_y[] = {593, 532, 3675, 3655, 2121}; + +// This code is touch check +static ssize_t xpt2046_mode_show(struct device_driver *drv,char *buf) +{ + int count; + + count = sprintf(buf,"xpt2046_mode_show:%d,%d,%d,%d,%d,%d,%d,%d,%d,%d\n", + uncali_x[0], uncali_y[0], + uncali_x[1], uncali_y[1], + uncali_x[2], uncali_y[2], + uncali_x[3], uncali_y[3], + uncali_x[4], uncali_y[4]); + + printk("buf: %s", buf); + + return count; +} + +static ssize_t xpt2046_mode_store(struct device_driver * drv, const char * buf, size_t count) +{ + int i, j = 0; + char temp[5]; + + printk("xpt2046_mode_store: %s\n", buf); + + for (i = 0; i < 5; i++) + { + strncpy(temp, buf + 5 * (j++), 4); + uncali_x[i] = simple_strtol(temp, NULL, 10); + strncpy(temp, buf + 5 * (j++), 4); + uncali_y[i] = simple_strtol(temp, NULL, 10); + printk("SN=%d uncali_x=%d uncali_y=%d\n", + i, uncali_x[i], uncali_y[i]); + } + + return count; +} + +//This code is Touch adc simple value +static ssize_t xpt2046_adc_show(struct device_driver *drv,char *buf) +{ + printk("xpt2046_adc_show: x=%d y=%d\n", gADPoint.x, gADPoint.y); + + return sprintf(buf, "%d,%d\n", gADPoint.x, gADPoint.y); +} + +static ssize_t xpt2046_cali_status(struct device_driver *drv, char *buf) +{ + int ret; + + ret = TouchPanelSetCalibration(4, screen_x, screen_y, uncali_x, uncali_y); + if (ret == 1) + ret = sprintf(buf, "successful\n"); + else + ret = sprintf(buf, "fail\n"); + + printk("xpt2046_cali_status: buf=<%s", buf); + + return ret; +} + +//static DEVICE_ATTR(adc, 0666, xpt2046_adc_show, NULL); +//static DEVICE_ATTR(calistatus, 0666, xpt2046_cali_status, NULL); +//static DEVICE_ATTR(mode, 0666, xpt2046_mode_show, xpt2046_mode_store); +static DRIVER_ATTR(touchadc, 0666, xpt2046_adc_show, NULL); +static DRIVER_ATTR(calistatus, 0666, xpt2046_cali_status, NULL); +static DRIVER_ATTR(touchcheck, 0666, xpt2046_mode_show, xpt2046_mode_store); +#if (0) +static struct attribute *xpt2046_attributes[] = { + &dev_attr_touchadc.attr, + &dev_attr_calistatus.attr, + &dev_attr_touchcheck.attr, + NULL, +}; + +static struct attribute_group xpt2046_attr_group = { + .attrs = xpt2046_attributes, +}; +#endif /* (0) */ + +static int device_suspended(struct device *dev) +{ + struct xpt2046 *ts = dev_get_drvdata(dev); + return ts->is_suspended || ts->disabled; +} + +static int xpt2046_read12_dfr(struct device *dev, unsigned command) +{ + struct spi_device *spi = to_spi_device(dev); + struct xpt2046 *ts = dev_get_drvdata(dev); + struct dfr_req *req = kzalloc(sizeof *req, GFP_KERNEL); + int status; + + if (!req) + return -ENOMEM; + + spi_message_init(&req->msg); + + /* take sample */ + req->command = (u8) command; + req->xfer[0].tx_buf = &req->command; + req->xfer[0].len = 1; + spi_message_add_tail(&req->xfer[0], &req->msg); + + req->xfer[1].rx_buf = &req->sample; + req->xfer[1].len = 2; + spi_message_add_tail(&req->xfer[1], &req->msg); + + /* converter in low power mode & enable PENIRQ */ + req->pwrdown= PWRDOWN; + req->xfer[2].tx_buf = &req->pwrdown; + req->xfer[2].len = 1; + spi_message_add_tail(&req->xfer[2], &req->msg); + + req->xfer[3].rx_buf = &req->dummy; + req->xfer[3].len = 2; + CS_CHANGE(req->xfer[3]); + spi_message_add_tail(&req->xfer[3], &req->msg); + + ts->irq_disabled = 1; + disable_irq(spi->irq); + status = spi_sync(spi, &req->msg); + ts->irq_disabled = 0; + enable_irq(spi->irq); + + if (status == 0) { + /* on-wire is a must-ignore bit, a BE12 value, then padding */ + status = be16_to_cpu(req->sample); + status = status >> 3; + status &= 0x0fff; + xpt2046printk("***>%s:status=%d\n",__FUNCTION__,status); + } + + kfree(req); + return status; +} + + + +/*--------------------------------------------------------------------------*/ + +static int get_pendown_state(struct xpt2046 *ts) +{ + if (ts->get_pendown_state) + return ts->get_pendown_state(); + + return !gpio_get_value(ts->gpio_pendown); +} + +static void null_wait_for_sync(void) +{ + +} + +/* + * PENIRQ only kicks the timer. The timer only reissues the SPI transfer, + * to retrieve touchscreen status. + * + * The SPI transfer completion callback does the real work. It reports + * touchscreen events and reactivates the timer (or IRQ) as appropriate. + */ + +static void xpt2046_rx(void *xpt) +{ + struct xpt2046 *ts = xpt; + struct xpt2046_packet *packet = ts->packet; + unsigned Rt = 1; + u16 x, y; + int xd,yd; + /* xpt2046_rx_val() did in-place conversion (including byteswap) from + * on-the-wire format as part of debouncing to get stable readings. + */ + x = packet->tc.x; + y = packet->tc.y; + + xpt2046printk("***>%s:x=%d,y=%d\n",__FUNCTION__,x,y); + + /* range filtering */ + if (x == MAX_12BIT) + x = 0; + + /* Sample found inconsistent by debouncing or pressure is beyond + * the maximum. Don't report it to user space, repeat at least + * once more the measurement + */ + if (packet->tc.ignore) { + + xpt2046printk("***>%s:ignored=%d\n",__FUNCTION__,packet->tc.ignore); + + hrtimer_start(&ts->timer, ktime_set(0, TS_POLL_PERIOD), + HRTIMER_MODE_REL); + return; + } + + /* Maybe check the pendown state before reporting. This discards + * false readings when the pen is lifted. + */ + if (ts->penirq_recheck_delay_usecs) { + udelay(ts->penirq_recheck_delay_usecs); + if (!get_pendown_state(ts)) + { + xpt2046printk("***>%s:get_pendown_state(ts)==0,discard false reading\n",__FUNCTION__); + Rt = 0; + } + } + + /* NOTE: We can't rely on the pressure to determine the pen down + * state, even this controller has a pressure sensor. The pressure + * value can fluctuate for quite a while after lifting the pen and + * in some cases may not even settle at the expected value. + * + * The only safe way to check for the pen up condition is in the + * timer by reading the pen signal state (it's a GPIO _and_ IRQ). + */ + if (Rt) { + struct input_dev *input = ts->input; + if (!ts->pendown) { + input_report_key(input, BTN_TOUCH, 1); + ts->pendown = 1; + xpt2046printk("***>%s:input_report_key(pen down)\n",__FUNCTION__); + } + + TouchPanelCalibrateAPoint(x, y, &xd, &yd); + + xd = xd / 4; + yd = yd / 4; + gADPoint.x = x; + gADPoint.y = y; + + if (ts->swap_xy) + swap(x, y); + + + input_report_abs(input, ABS_X, xd); + input_report_abs(input, ABS_Y, yd); + + input_sync(input); + xpt2046printk("***>%s:input_report_abs(%4d/%4d)\n",__FUNCTION__,xd, yd); + } + + hrtimer_start(&ts->timer, ktime_set(0, TS_POLL_PERIOD), + HRTIMER_MODE_REL); +} + +static int xpt2046_debounce(void *xpt, int data_idx, int *val) +{ + struct xpt2046 *ts = xpt; + static int average_val[2]; + + + xpt2046printk("***>%s:%d,%d,%d,%d,%d,%d,%d,%d\n",__FUNCTION__, + data_idx,ts->last_read, + ts->read_cnt,ts->debounce_max, + abs(ts->last_read - *val),ts->debounce_tol, + ts->read_rep,ts->debounce_rep); + + if(*val == 4095 || *val == 0) + { + ts->read_cnt = 0; + ts->last_read = 0; + memset(average_val,0,sizeof(average_val)); + xpt2046printk("***>%s:*val == 4095 || *val == 0\n",__FUNCTION__); + return XPT2046_FILTER_IGNORE; + } + /* discard the first sample. */ + if(!ts->read_cnt) + { + //udelay(100); + ts->read_cnt++; + return XPT2046_FILTER_REPEAT; + } + + if (ts->read_cnt==1 || (abs(ts->last_read - *val) > ts->debounce_tol)) { + /* Start over collecting consistent readings. */ + ts->read_rep = 1; + average_val[data_idx] = *val; + /* Repeat it, if this was the first read or the read + * wasn't consistent enough. */ + if (ts->read_cnt < ts->debounce_max) { + ts->last_read = *val; + ts->read_cnt++; + return XPT2046_FILTER_REPEAT; + } else { + /* Maximum number of debouncing reached and still + * not enough number of consistent readings. Abort + * the whole sample, repeat it in the next sampling + * period. + */ + ts->read_cnt = 0; + ts->last_read = 0; + memset(average_val,0,sizeof(average_val)); + xpt2046printk("***>%s:XPT2046_FILTER_IGNORE\n",__FUNCTION__); + return XPT2046_FILTER_IGNORE; + } + } + else { + average_val[data_idx] += *val; + + if (++ts->read_rep >= ts->debounce_rep) { + /* Got a good reading for this coordinate, + * go for the next one. */ + ts->read_cnt = 0; + ts->read_rep = 0; + ts->last_read = 0; + *val = average_val[data_idx]/(ts->debounce_rep); + return XPT2046_FILTER_OK; + } else { + /* Read more values that are consistent. */ + ts->read_cnt++; + + return XPT2046_FILTER_REPEAT; + } + } +} + +static int xpt2046_no_filter(void *xpt, int data_idx, int *val) +{ + return XPT2046_FILTER_OK; +} + +static void xpt2046_rx_val(void *xpt) +{ + struct xpt2046 *ts = xpt; + struct xpt2046_packet *packet = ts->packet; + struct spi_message *m; + struct spi_transfer *t; + int val; + int action; + int status; + + m = &ts->msg[ts->msg_idx]; + t = list_entry(m->transfers.prev, struct spi_transfer, transfer_list); + + /* adjust: on-wire is a must-ignore bit, a BE12 value, then padding; + * built from two 8 bit values written msb-first. + */ + val = (be16_to_cpup((__be16 *)t->rx_buf) >> 3) & 0x0fff; + + xpt2046printk("***>%s:value=%d\n",__FUNCTION__,val); + + action = ts->filter(ts->filter_data, ts->msg_idx, &val); + switch (action) { + case XPT2046_FILTER_REPEAT: + break; + case XPT2046_FILTER_IGNORE: + packet->tc.ignore = 1; + /* Last message will contain xpt2046_rx() as the + * completion function. + */ + m = ts->last_msg; + break; + case XPT2046_FILTER_OK: + *(u16 *)t->rx_buf = val; + packet->tc.ignore = 0; + m = &ts->msg[++ts->msg_idx]; + break; + default: + BUG(); + } + ts->wait_for_sync(); + status = spi_async(ts->spi, m); + if (status) + dev_err(&ts->spi->dev, "spi_async --> %d\n", + status); +} + +static enum hrtimer_restart xpt2046_timer(struct hrtimer *handle) +{ + struct xpt2046 *ts = container_of(handle, struct xpt2046, timer); + int status = 0; + + spin_lock(&ts->lock); + + if (unlikely(!get_pendown_state(ts) || + device_suspended(&ts->spi->dev))) { + if (ts->pendown) { + struct input_dev *input = ts->input; + input_report_key(input, BTN_TOUCH, 0); + input_sync(input); + + ts->pendown = 0; + + xpt2046printk("***>%s:input_report_key(The touchscreen up)\n",__FUNCTION__); + } + + /* measurement cycle ended */ + if (!device_suspended(&ts->spi->dev)) { + xpt2046printk("***>%s:device_suspended==0\n",__FUNCTION__); + ts->irq_disabled = 0; + enable_irq(ts->spi->irq); + } + ts->pending = 0; + } else { + /* pen is still down, continue with the measurement */ + xpt2046printk("***>%s:pen is still down, continue with the measurement\n",__FUNCTION__); + ts->msg_idx = 0; + ts->wait_for_sync(); + status = spi_async(ts->spi, &ts->msg[0]); + if (status) + dev_err(&ts->spi->dev, "spi_async --> %d\n", status); + } + + spin_unlock(&ts->lock); + return HRTIMER_NORESTART; +} + +static irqreturn_t xpt2046_irq(int irq, void *handle) +{ + struct xpt2046 *ts = handle; + unsigned long flags; + + xpt2046printk("***>%s.....%s.....%d\n",__FILE__,__FUNCTION__,__LINE__); + + spin_lock_irqsave(&ts->lock, flags); + + if (likely(get_pendown_state(ts))) { + if (!ts->irq_disabled) { + /* The ARM do_simple_IRQ() dispatcher doesn't act + * like the other dispatchers: it will report IRQs + * even after they've been disabled. We work around + * that here. (The "generic irq" framework may help...) + */ + ts->irq_disabled = 1; + disable_irq_nosync(ts->spi->irq); + ts->pending = 1; + hrtimer_start(&ts->timer, ktime_set(0, TS_POLL_DELAY), + HRTIMER_MODE_REL); + } + } + spin_unlock_irqrestore(&ts->lock, flags); + + return IRQ_HANDLED; +} + +/*--------------------------------------------------------------------------*/ + +/* Must be called with ts->lock held */ +static void xpt2046_disable(struct xpt2046 *ts) +{ + if (ts->disabled) + return; + + ts->disabled = 1; + + /* are we waiting for IRQ, or polling? */ + if (!ts->pending) { + ts->irq_disabled = 1; + disable_irq(ts->spi->irq); + } else { + /* the timer will run at least once more, and + * leave everything in a clean state, IRQ disabled + */ + while (ts->pending) { + spin_unlock_irq(&ts->lock); + msleep(1); + spin_lock_irq(&ts->lock); + } + } + + /* we know the chip's in lowpower mode since we always + * leave it that way after every request + */ +} + +/* Must be called with ts->lock held */ +static void xpt2046_enable(struct xpt2046 *ts) +{ + if (!ts->disabled) + return; + + ts->disabled = 0; + ts->irq_disabled = 0; + enable_irq(ts->spi->irq); +} + +static int xpt2046_suspend(struct spi_device *spi, pm_message_t message) +{ + struct xpt2046 *ts = dev_get_drvdata(&spi->dev); + + spin_lock_irq(&ts->lock); + + ts->is_suspended = 1; + xpt2046_disable(ts); + + spin_unlock_irq(&ts->lock); + + return 0; + +} + +static int xpt2046_resume(struct spi_device *spi) +{ + struct xpt2046 *ts = dev_get_drvdata(&spi->dev); + + spin_lock_irq(&ts->lock); + + ts->is_suspended = 0; + xpt2046_enable(ts); + + spin_unlock_irq(&ts->lock); + + return 0; +} + +static int __devinit setup_pendown(struct spi_device *spi, struct xpt2046 *ts) +{ + struct xpt2046_platform_data *pdata = spi->dev.platform_data; + int err; + + /* REVISIT when the irq can be triggered active-low, or if for some + * reason the touchscreen isn't hooked up, we don't need to access + * the pendown state. + */ + if (!pdata->get_pendown_state && !gpio_is_valid(pdata->gpio_pendown)) { + dev_err(&spi->dev, "no get_pendown_state nor gpio_pendown?\n"); + return -EINVAL; + } + + if (pdata->get_pendown_state) { + ts->get_pendown_state = pdata->get_pendown_state; + return 0; + } + + if (pdata->io_init) { + err = pdata->io_init(); + if (err) + dev_err(&spi->dev, "xpt2046 io_init fail\n"); + } + + err = gpio_request(pdata->gpio_pendown, "xpt2046_pendown"); + if (err) { + dev_err(&spi->dev, "failed to request pendown GPIO%d\n", + pdata->gpio_pendown); + return err; + } + + ts->gpio_pendown = pdata->gpio_pendown; + return 0; +} + +static int __devinit xpt2046_probe(struct spi_device *spi) +{ + struct xpt2046 *ts; + struct xpt2046_packet *packet; + struct input_dev *input_dev; + struct xpt2046_platform_data *pdata = spi->dev.platform_data; + struct spi_message *m; + struct spi_transfer *x; + int vref; + int err; + + + + if (!spi->irq) { + dev_dbg(&spi->dev, "no IRQ?\n"); + return -ENODEV; + } + else{ + spi->irq = gpio_to_irq(spi->irq); + dev_dbg(&spi->dev, "no IRQ?\n"); + } + + if (!pdata) { + dev_err(&spi->dev, "empty platform_data\n"); + return -EFAULT; + } + + /* don't exceed max specified sample rate */ + if (spi->max_speed_hz > (125000 * SAMPLE_BITS)) { + dev_dbg(&spi->dev, "f(sample) %d KHz?\n", + (spi->max_speed_hz/SAMPLE_BITS)/1000); + return -EINVAL; + } + + /* We'd set TX wordsize 8 bits and RX wordsize to 13 bits ... except + * that even if the hardware can do that, the SPI controller driver + * may not. So we stick to very-portable 8 bit words, both RX and TX. + */ + spi->bits_per_word = 8; + spi->mode = SPI_MODE_0; + err = spi_setup(spi); + if (err < 0) + return err; + + ts = kzalloc(sizeof(struct xpt2046), GFP_KERNEL); + packet = kzalloc(sizeof(struct xpt2046_packet), GFP_KERNEL); + input_dev = input_allocate_device(); + if (!ts || !packet || !input_dev) { + err = -ENOMEM; + goto err_free_mem; + } + + dev_set_drvdata(&spi->dev, ts); + + ts->packet = packet; + ts->spi = spi; + ts->input = input_dev; + ts->swap_xy = pdata->swap_xy; + + hrtimer_init(&ts->timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL); + ts->timer.function = xpt2046_timer; + + spin_lock_init(&ts->lock); + + ts->model = pdata->model ? : 2046; + + if (pdata->filter != NULL) { + if (pdata->filter_init != NULL) { + err = pdata->filter_init(pdata, &ts->filter_data); + if (err < 0) + goto err_free_mem; + } + ts->filter = pdata->filter; + ts->filter_cleanup = pdata->filter_cleanup; + } else if (pdata->debounce_max) { + ts->debounce_max = pdata->debounce_max; + if (ts->debounce_max < pdata->debounce_rep) + ts->debounce_max = pdata->debounce_rep; + ts->debounce_tol = pdata->debounce_tol; + ts->debounce_rep = pdata->debounce_rep; + ts->filter = xpt2046_debounce; + ts->filter_data = ts; + } else + ts->filter = xpt2046_no_filter; + + err = setup_pendown(spi, ts); + if (err) + goto err_cleanup_filter; + + if (pdata->penirq_recheck_delay_usecs) + ts->penirq_recheck_delay_usecs = + pdata->penirq_recheck_delay_usecs; + + ts->wait_for_sync = pdata->wait_for_sync ? : null_wait_for_sync; + + snprintf(ts->phys, sizeof(ts->phys), "%s/input0", dev_name(&spi->dev)); + snprintf(ts->name, sizeof(ts->name), "XPT%d Touchscreen", ts->model); + + input_dev->name = ts->name; + input_dev->phys = ts->phys; + input_dev->dev.parent = &spi->dev; + + input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS); + input_dev->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH); + input_set_abs_params(input_dev, ABS_X, + pdata->x_min ? : 0, + pdata->x_max ? : MAX_12BIT, + 0, 0); + input_set_abs_params(input_dev, ABS_Y, + pdata->y_min ? : 0, + pdata->y_max ? : MAX_12BIT, + 0, 0); + + vref = pdata->keep_vref_on; + + /* set up the transfers to read touchscreen state; this assumes we + * use formula #2 for pressure, not #3. + */ + m = &ts->msg[0]; + x = ts->xfer; + + spi_message_init(m); + + /* y- still on; turn on only y+ (and ADC) */ + packet->read_y = READ_Y(vref); + x->tx_buf = &packet->read_y; + x->len = 1; + spi_message_add_tail(x, m); + + x++; + x->rx_buf = &packet->tc.y; + x->len = 2; + spi_message_add_tail(x, m); + + m->complete = xpt2046_rx_val; + m->context = ts; + + m++; + spi_message_init(m); + + /* turn y- off, x+ on, then leave in lowpower */ + x++; + packet->read_x = READ_X(vref); + x->tx_buf = &packet->read_x; + x->len = 1; + spi_message_add_tail(x, m); + + x++; + x->rx_buf = &packet->tc.x; + x->len = 2; + spi_message_add_tail(x, m); + + m->complete = xpt2046_rx_val; + m->context = ts; + + /* power down */ + m++; + spi_message_init(m); + + x++; + packet->pwrdown = PWRDOWN; + x->tx_buf = &packet->pwrdown; + x->len = 1; + spi_message_add_tail(x, m); + + x++; + x->rx_buf = &packet->dummy; + x->len = 2; + CS_CHANGE(*x); + spi_message_add_tail(x, m); + + m->complete = xpt2046_rx; + m->context = ts; + + ts->last_msg = m; + + if (request_irq(spi->irq, xpt2046_irq, IRQF_TRIGGER_FALLING, + spi->dev.driver->name, ts)) { + printk("%s:trying pin change workaround on irq %d\n",__FUNCTION__,spi->irq); + err = request_irq(spi->irq, xpt2046_irq, + IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING, + spi->dev.driver->name, ts); + if (err) { + dev_dbg(&spi->dev, "irq %d busy?\n", spi->irq); + goto err_free_gpio; + } + } + xpt2046printk("***>%s:touchscreen irq %d\n",__FUNCTION__,spi->irq); + + /* take a first sample, leaving nPENIRQ active and vREF off; avoid + * the touchscreen, in case it's not connected. + */ + xpt2046_read12_dfr(&spi->dev,READ_X(1)); + + //err = sysfs_create_group(&spi->dev.kobj, &xpt2046_attr_group); + if (err) + goto err_remove_hwmon; + + err = input_register_device(input_dev); + if (err) + goto err_remove_attr_group; + printk("xpt2046_ts: driver initialized\n"); + return 0; + + err_remove_attr_group: + //sysfs_remove_group(&spi->dev.kobj, &xpt2046_attr_group); + err_remove_hwmon: + free_irq(spi->irq, ts); + err_free_gpio: + if (ts->gpio_pendown != -1) + gpio_free(ts->gpio_pendown); + err_cleanup_filter: + if (ts->filter_cleanup) + ts->filter_cleanup(ts->filter_data); + err_free_mem: + input_free_device(input_dev); + kfree(packet); + kfree(ts); + return err; +} + +static int __devexit xpt2046_remove(struct spi_device *spi) +{ + struct xpt2046 *ts = dev_get_drvdata(&spi->dev); + + input_unregister_device(ts->input); + + xpt2046_suspend(spi, PMSG_SUSPEND); + + //sysfs_remove_group(&spi->dev.kobj, &xpt2046_attr_group); + + free_irq(ts->spi->irq, ts); + /* suspend left the IRQ disabled */ + enable_irq(ts->spi->irq); + + if (ts->gpio_pendown != -1) + gpio_free(ts->gpio_pendown); + + if (ts->filter_cleanup) + ts->filter_cleanup(ts->filter_data); + + kfree(ts->packet); + kfree(ts); + + dev_dbg(&spi->dev, "unregistered touchscreen\n"); + return 0; +} + +static struct spi_driver xpt2046_driver = { + .driver = { + .name = "xpt2046_ts", + .bus = &spi_bus_type, + .owner = THIS_MODULE, + }, + .probe = xpt2046_probe, + .remove = __devexit_p(xpt2046_remove), + .suspend = xpt2046_suspend, + .resume = xpt2046_resume, +}; + +static int __init xpt2046_init(void) +{ + //return spi_register_driver(&xpt2046_driver); + int ret = spi_register_driver(&xpt2046_driver); + + if (ret == 0) + { + gADPoint.x = 0; + gADPoint.y = 0; + ret = driver_create_file(&xpt2046_driver.driver, &driver_attr_touchcheck); + ret += driver_create_file(&xpt2046_driver.driver, &driver_attr_touchadc); + ret += driver_create_file(&xpt2046_driver.driver, &driver_attr_calistatus); + } + + return ret; +} +module_init(xpt2046_init); + +static void __exit xpt2046_exit(void) +{ + //spi_unregister_driver(&xpt2046_driver); + driver_remove_file(&xpt2046_driver.driver, &driver_attr_touchcheck); + driver_remove_file(&xpt2046_driver.driver, &driver_attr_touchadc); + driver_remove_file(&xpt2046_driver.driver, &driver_attr_calistatus); + + spi_unregister_driver(&xpt2046_driver); +} +module_exit(xpt2046_exit); + +MODULE_DESCRIPTION("rk2818 spi xpt2046 TouchScreen Driver"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("spi:xpt2046"); diff --git a/drivers/input/touchscreen/xpt2046_cbn_ts.h b/drivers/input/touchscreen/xpt2046_cbn_ts.h new file mode 100644 index 000000000000..73f84c19e0cc --- /dev/null +++ b/drivers/input/touchscreen/xpt2046_cbn_ts.h @@ -0,0 +1,56 @@ +/* + * drivers/input/touchscreen/xpt2046_cbn_ts.h + * + * Copyright (C) 2010 ROCKCHIP, Inc. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef __DRIVERS_TOUCHSCREEN_XPT2046_CBN_TS_H +#define __DRIVERS_TOUCHSCREEN_XPT2046_CBN_TS_H +enum xpt2046_filter { + XPT2046_FILTER_OK, + XPT2046_FILTER_REPEAT, + XPT2046_FILTER_IGNORE, +}; + +struct xpt2046_platform_data { + u16 model; /* 2046. */ + bool keep_vref_on; /* set to keep vref on for differential + * measurements as well */ + bool swap_xy; /* swap x and y axes */ + + /* If set to non-zero, after samples are taken this delay is applied + * and penirq is rechecked, to help avoid false events. This value + * is affected by the material used to build the touch layer. + */ + u16 penirq_recheck_delay_usecs; + + u16 x_min, x_max; + u16 y_min, y_max; + + u16 debounce_max; /* max number of additional readings + * per sample */ + u16 debounce_tol; /* tolerance used for filtering */ + u16 debounce_rep; /* additional consecutive good readings + * required after the first two */ + int gpio_pendown; /* the GPIO used to decide the pendown + * state if get_pendown_state == NULL + */ + int (*get_pendown_state)(void); + int (*filter_init) (struct xpt2046_platform_data *pdata, + void **filter_data); + int (*filter) (void *filter_data, int data_idx, int *val); + void (*filter_cleanup)(void *filter_data); + void (*wait_for_sync)(void); + int (* io_init)(void); + int (* io_deinit)(void); +}; +#endif -- 2.34.1