Path: blob/master/drivers/input/touchscreen/intel-mid-touch.c
15111 views
/*1* Intel MID Resistive Touch Screen Driver2*3* Copyright (C) 2008 Intel Corp4*5* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~6*7* This program is free software; you can redistribute it and/or modify8* it under the terms of the GNU General Public License as published by9* the Free Software Foundation; version 2 of the License.10*11* This program is distributed in the hope that it will be useful, but12* WITHOUT ANY WARRANTY; without even the implied warranty of13* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU14* General Public License for more details.15*16* You should have received a copy of the GNU General Public License along17* with this program; if not, write to the Free Software Foundation, Inc.,18* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.19*20* Questions/Comments/Bug fixes to Sreedhara ([email protected])21* Ramesh Agarwal ([email protected])22* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~23*24* TODO:25* review conversion of r/m/w sequences26*/2728#include <linux/module.h>29#include <linux/init.h>30#include <linux/input.h>31#include <linux/interrupt.h>32#include <linux/err.h>33#include <linux/param.h>34#include <linux/slab.h>35#include <linux/platform_device.h>36#include <linux/irq.h>37#include <linux/delay.h>38#include <asm/intel_scu_ipc.h>3940/* PMIC Interrupt registers */41#define PMIC_REG_ID1 0x00 /* PMIC ID1 register */4243/* PMIC Interrupt registers */44#define PMIC_REG_INT 0x04 /* PMIC interrupt register */45#define PMIC_REG_MINT 0x05 /* PMIC interrupt mask register */4647/* ADC Interrupt registers */48#define PMIC_REG_ADCINT 0x5F /* ADC interrupt register */49#define PMIC_REG_MADCINT 0x60 /* ADC interrupt mask register */5051/* ADC Control registers */52#define PMIC_REG_ADCCNTL1 0x61 /* ADC control register */5354/* ADC Channel Selection registers */55#define PMICADDR0 0xA456#define END_OF_CHANNEL 0x1F5758/* ADC Result register */59#define PMIC_REG_ADCSNS0H 0x646061/* ADC channels for touch screen */62#define MRST_TS_CHAN10 0xA /* Touch screen X+ connection */63#define MRST_TS_CHAN11 0xB /* Touch screen X- connection */64#define MRST_TS_CHAN12 0xC /* Touch screen Y+ connection */65#define MRST_TS_CHAN13 0xD /* Touch screen Y- connection */6667/* Touch screen channel BIAS constants */68#define MRST_XBIAS 0x2069#define MRST_YBIAS 0x4070#define MRST_ZBIAS 0x807172/* Touch screen coordinates */73#define MRST_X_MIN 1074#define MRST_X_MAX 102475#define MRST_X_FUZZ 576#define MRST_Y_MIN 1077#define MRST_Y_MAX 102478#define MRST_Y_FUZZ 579#define MRST_PRESSURE_MIN 080#define MRST_PRESSURE_NOMINAL 5081#define MRST_PRESSURE_MAX 1008283#define WAIT_ADC_COMPLETION 10 /* msec */8485/* PMIC ADC round robin delays */86#define ADC_LOOP_DELAY0 0x0 /* Continuous loop */87#define ADC_LOOP_DELAY1 0x1 /* 4.5 ms approximate */8889/* PMIC Vendor Identifiers */90#define PMIC_VENDOR_FS 0 /* PMIC vendor FreeScale */91#define PMIC_VENDOR_MAXIM 1 /* PMIC vendor MAXIM */92#define PMIC_VENDOR_NEC 2 /* PMIC vendor NEC */93#define MRSTOUCH_MAX_CHANNELS 32 /* Maximum ADC channels */9495/* Touch screen device structure */96struct mrstouch_dev {97struct device *dev; /* device associated with touch screen */98struct input_dev *input;99char phys[32];100u16 asr; /* Address selection register */101int irq;102unsigned int vendor; /* PMIC vendor */103unsigned int rev; /* PMIC revision */104105int (*read_prepare)(struct mrstouch_dev *tsdev);106int (*read)(struct mrstouch_dev *tsdev, u16 *x, u16 *y, u16 *z);107int (*read_finish)(struct mrstouch_dev *tsdev);108};109110111/*************************** NEC and Maxim Interface ************************/112113static int mrstouch_nec_adc_read_prepare(struct mrstouch_dev *tsdev)114{115return intel_scu_ipc_update_register(PMIC_REG_MADCINT, 0, 0x20);116}117118/*119* Enables PENDET interrupt.120*/121static int mrstouch_nec_adc_read_finish(struct mrstouch_dev *tsdev)122{123int err;124125err = intel_scu_ipc_update_register(PMIC_REG_MADCINT, 0x20, 0x20);126if (!err)127err = intel_scu_ipc_update_register(PMIC_REG_ADCCNTL1, 0, 0x05);128129return err;130}131132/*133* Reads PMIC ADC touch screen result134* Reads ADC storage registers for higher 7 and lower 3 bits and135* converts the two readings into a single value and turns off gain bit136*/137static int mrstouch_ts_chan_read(u16 offset, u16 chan, u16 *vp, u16 *vm)138{139int err;140u16 result;141u32 res;142143result = PMIC_REG_ADCSNS0H + offset;144145if (chan == MRST_TS_CHAN12)146result += 4;147148err = intel_scu_ipc_ioread32(result, &res);149if (err)150return err;151152/* Mash the bits up */153154*vp = (res & 0xFF) << 3; /* Highest 7 bits */155*vp |= (res >> 8) & 0x07; /* Lower 3 bits */156*vp &= 0x3FF;157158res >>= 16;159160*vm = (res & 0xFF) << 3; /* Highest 7 bits */161*vm |= (res >> 8) & 0x07; /* Lower 3 bits */162*vm &= 0x3FF;163164return 0;165}166167/*168* Enables X, Y and Z bias values169* Enables YPYM for X channels and XPXM for Y channels170*/171static int mrstouch_ts_bias_set(uint offset, uint bias)172{173int count;174u16 chan, start;175u16 reg[4];176u8 data[4];177178chan = PMICADDR0 + offset;179start = MRST_TS_CHAN10;180181for (count = 0; count <= 3; count++) {182reg[count] = chan++;183data[count] = bias | (start + count);184}185186return intel_scu_ipc_writev(reg, data, 4);187}188189/* To read touch screen channel values */190static int mrstouch_nec_adc_read(struct mrstouch_dev *tsdev,191u16 *x, u16 *y, u16 *z)192{193int err;194u16 xm, ym, zm;195196/* configure Y bias for X channels */197err = mrstouch_ts_bias_set(tsdev->asr, MRST_YBIAS);198if (err)199goto ipc_error;200201msleep(WAIT_ADC_COMPLETION);202203/* read x+ and x- channels */204err = mrstouch_ts_chan_read(tsdev->asr, MRST_TS_CHAN10, x, &xm);205if (err)206goto ipc_error;207208/* configure x bias for y channels */209err = mrstouch_ts_bias_set(tsdev->asr, MRST_XBIAS);210if (err)211goto ipc_error;212213msleep(WAIT_ADC_COMPLETION);214215/* read y+ and y- channels */216err = mrstouch_ts_chan_read(tsdev->asr, MRST_TS_CHAN12, y, &ym);217if (err)218goto ipc_error;219220/* configure z bias for x and y channels */221err = mrstouch_ts_bias_set(tsdev->asr, MRST_ZBIAS);222if (err)223goto ipc_error;224225msleep(WAIT_ADC_COMPLETION);226227/* read z+ and z- channels */228err = mrstouch_ts_chan_read(tsdev->asr, MRST_TS_CHAN10, z, &zm);229if (err)230goto ipc_error;231232return 0;233234ipc_error:235dev_err(tsdev->dev, "ipc error during adc read\n");236return err;237}238239240/*************************** Freescale Interface ************************/241242static int mrstouch_fs_adc_read_prepare(struct mrstouch_dev *tsdev)243{244int err, count;245u16 chan;246u16 reg[5];247u8 data[5];248249/* Stop the ADC */250err = intel_scu_ipc_update_register(PMIC_REG_MADCINT, 0x00, 0x02);251if (err)252goto ipc_error;253254chan = PMICADDR0 + tsdev->asr;255256/* Set X BIAS */257for (count = 0; count <= 3; count++) {258reg[count] = chan++;259data[count] = 0x2A;260}261reg[count] = chan++; /* Dummy */262data[count] = 0;263264err = intel_scu_ipc_writev(reg, data, 5);265if (err)266goto ipc_error;267268msleep(WAIT_ADC_COMPLETION);269270/* Set Y BIAS */271for (count = 0; count <= 3; count++) {272reg[count] = chan++;273data[count] = 0x4A;274}275reg[count] = chan++; /* Dummy */276data[count] = 0;277278err = intel_scu_ipc_writev(reg, data, 5);279if (err)280goto ipc_error;281282msleep(WAIT_ADC_COMPLETION);283284/* Set Z BIAS */285err = intel_scu_ipc_iowrite32(chan + 2, 0x8A8A8A8A);286if (err)287goto ipc_error;288289msleep(WAIT_ADC_COMPLETION);290291return 0;292293ipc_error:294dev_err(tsdev->dev, "ipc error during %s\n", __func__);295return err;296}297298static int mrstouch_fs_adc_read(struct mrstouch_dev *tsdev,299u16 *x, u16 *y, u16 *z)300{301int err;302u16 result;303u16 reg[4];304u8 data[4];305306result = PMIC_REG_ADCSNS0H + tsdev->asr;307308reg[0] = result + 4;309reg[1] = result + 5;310reg[2] = result + 16;311reg[3] = result + 17;312313err = intel_scu_ipc_readv(reg, data, 4);314if (err)315goto ipc_error;316317*x = data[0] << 3; /* Higher 7 bits */318*x |= data[1] & 0x7; /* Lower 3 bits */319*x &= 0x3FF;320321*y = data[2] << 3; /* Higher 7 bits */322*y |= data[3] & 0x7; /* Lower 3 bits */323*y &= 0x3FF;324325/* Read Z value */326reg[0] = result + 28;327reg[1] = result + 29;328329err = intel_scu_ipc_readv(reg, data, 4);330if (err)331goto ipc_error;332333*z = data[0] << 3; /* Higher 7 bits */334*z |= data[1] & 0x7; /* Lower 3 bits */335*z &= 0x3FF;336337return 0;338339ipc_error:340dev_err(tsdev->dev, "ipc error during %s\n", __func__);341return err;342}343344static int mrstouch_fs_adc_read_finish(struct mrstouch_dev *tsdev)345{346int err, count;347u16 chan;348u16 reg[5];349u8 data[5];350351/* Clear all TS channels */352chan = PMICADDR0 + tsdev->asr;353for (count = 0; count <= 4; count++) {354reg[count] = chan++;355data[count] = 0;356}357err = intel_scu_ipc_writev(reg, data, 5);358if (err)359goto ipc_error;360361for (count = 0; count <= 4; count++) {362reg[count] = chan++;363data[count] = 0;364}365err = intel_scu_ipc_writev(reg, data, 5);366if (err)367goto ipc_error;368369err = intel_scu_ipc_iowrite32(chan + 2, 0x00000000);370if (err)371goto ipc_error;372373/* Start ADC */374err = intel_scu_ipc_update_register(PMIC_REG_MADCINT, 0x02, 0x02);375if (err)376goto ipc_error;377378return 0;379380ipc_error:381dev_err(tsdev->dev, "ipc error during %s\n", __func__);382return err;383}384385static void mrstouch_report_event(struct input_dev *input,386unsigned int x, unsigned int y, unsigned int z)387{388if (z > MRST_PRESSURE_NOMINAL) {389/* Pen touched, report button touch and coordinates */390input_report_key(input, BTN_TOUCH, 1);391input_report_abs(input, ABS_X, x);392input_report_abs(input, ABS_Y, y);393} else {394input_report_key(input, BTN_TOUCH, 0);395}396397input_report_abs(input, ABS_PRESSURE, z);398input_sync(input);399}400401/* PENDET interrupt handler */402static irqreturn_t mrstouch_pendet_irq(int irq, void *dev_id)403{404struct mrstouch_dev *tsdev = dev_id;405u16 x, y, z;406407/*408* Should we lower thread priority? Probably not, since we are409* not spinning but sleeping...410*/411412if (tsdev->read_prepare(tsdev))413goto out;414415do {416if (tsdev->read(tsdev, &x, &y, &z))417break;418419mrstouch_report_event(tsdev->input, x, y, z);420} while (z > MRST_PRESSURE_NOMINAL);421422tsdev->read_finish(tsdev);423424out:425return IRQ_HANDLED;426}427428/* Utility to read PMIC ID */429static int __devinit mrstouch_read_pmic_id(uint *vendor, uint *rev)430{431int err;432u8 r;433434err = intel_scu_ipc_ioread8(PMIC_REG_ID1, &r);435if (err)436return err;437438*vendor = r & 0x7;439*rev = (r >> 3) & 0x7;440441return 0;442}443444/*445* Parse ADC channels to find end of the channel configured by other ADC user446* NEC and MAXIM requires 4 channels and FreeScale needs 18 channels447*/448static int __devinit mrstouch_chan_parse(struct mrstouch_dev *tsdev)449{450int err, i, found;451u8 r8;452453found = -1;454455for (i = 0; i < MRSTOUCH_MAX_CHANNELS; i++) {456if (found >= 0)457break;458459err = intel_scu_ipc_ioread8(PMICADDR0 + i, &r8);460if (err)461return err;462463if (r8 == END_OF_CHANNEL) {464found = i;465break;466}467}468if (found < 0)469return 0;470471if (tsdev->vendor == PMIC_VENDOR_FS) {472if (found && found > (MRSTOUCH_MAX_CHANNELS - 18))473return -ENOSPC;474} else {475if (found && found > (MRSTOUCH_MAX_CHANNELS - 4))476return -ENOSPC;477}478return found;479}480481482/*483* Writes touch screen channels to ADC address selection registers484*/485static int __devinit mrstouch_ts_chan_set(uint offset)486{487u16 chan;488489int ret, count;490491chan = PMICADDR0 + offset;492for (count = 0; count <= 3; count++) {493ret = intel_scu_ipc_iowrite8(chan++, MRST_TS_CHAN10 + count);494if (ret)495return ret;496}497return intel_scu_ipc_iowrite8(chan++, END_OF_CHANNEL);498}499500/* Initialize ADC */501static int __devinit mrstouch_adc_init(struct mrstouch_dev *tsdev)502{503int err, start;504u8 ra, rm;505506err = mrstouch_read_pmic_id(&tsdev->vendor, &tsdev->rev);507if (err) {508dev_err(tsdev->dev, "Unable to read PMIC id\n");509return err;510}511512switch (tsdev->vendor) {513case PMIC_VENDOR_NEC:514case PMIC_VENDOR_MAXIM:515tsdev->read_prepare = mrstouch_nec_adc_read_prepare;516tsdev->read = mrstouch_nec_adc_read;517tsdev->read_finish = mrstouch_nec_adc_read_finish;518break;519520case PMIC_VENDOR_FS:521tsdev->read_prepare = mrstouch_fs_adc_read_prepare;522tsdev->read = mrstouch_fs_adc_read;523tsdev->read_finish = mrstouch_fs_adc_read_finish;524break;525526default:527dev_err(tsdev->dev,528"Unsupported touchscreen: %d\n", tsdev->vendor);529return -ENXIO;530}531532start = mrstouch_chan_parse(tsdev);533if (start < 0) {534dev_err(tsdev->dev, "Unable to parse channels\n");535return start;536}537538tsdev->asr = start;539540/*541* ADC power on, start, enable PENDET and set loop delay542* ADC loop delay is set to 4.5 ms approximately543* Loop delay more than this results in jitter in adc readings544* Setting loop delay to 0 (continuous loop) in MAXIM stops PENDET545* interrupt generation sometimes.546*/547548if (tsdev->vendor == PMIC_VENDOR_FS) {549ra = 0xE0 | ADC_LOOP_DELAY0;550rm = 0x5;551} else {552/* NEC and MAXIm not consistent with loop delay 0 */553ra = 0xE0 | ADC_LOOP_DELAY1;554rm = 0x0;555556/* configure touch screen channels */557err = mrstouch_ts_chan_set(tsdev->asr);558if (err)559return err;560}561562err = intel_scu_ipc_update_register(PMIC_REG_ADCCNTL1, ra, 0xE7);563if (err)564return err;565566err = intel_scu_ipc_update_register(PMIC_REG_MADCINT, rm, 0x03);567if (err)568return err;569570return 0;571}572573574/* Probe function for touch screen driver */575static int __devinit mrstouch_probe(struct platform_device *pdev)576{577struct mrstouch_dev *tsdev;578struct input_dev *input;579int err;580int irq;581582irq = platform_get_irq(pdev, 0);583if (irq < 0) {584dev_err(&pdev->dev, "no interrupt assigned\n");585return -EINVAL;586}587588tsdev = kzalloc(sizeof(struct mrstouch_dev), GFP_KERNEL);589input = input_allocate_device();590if (!tsdev || !input) {591dev_err(&pdev->dev, "unable to allocate memory\n");592err = -ENOMEM;593goto err_free_mem;594}595596tsdev->dev = &pdev->dev;597tsdev->input = input;598tsdev->irq = irq;599600snprintf(tsdev->phys, sizeof(tsdev->phys),601"%s/input0", dev_name(tsdev->dev));602603err = mrstouch_adc_init(tsdev);604if (err) {605dev_err(&pdev->dev, "ADC initialization failed\n");606goto err_free_mem;607}608609input->name = "mrst_touchscreen";610input->phys = tsdev->phys;611input->dev.parent = tsdev->dev;612613input->id.vendor = tsdev->vendor;614input->id.version = tsdev->rev;615616input->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS);617input->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH);618619input_set_abs_params(tsdev->input, ABS_X,620MRST_X_MIN, MRST_X_MAX, MRST_X_FUZZ, 0);621input_set_abs_params(tsdev->input, ABS_Y,622MRST_Y_MIN, MRST_Y_MAX, MRST_Y_FUZZ, 0);623input_set_abs_params(tsdev->input, ABS_PRESSURE,624MRST_PRESSURE_MIN, MRST_PRESSURE_MAX, 0, 0);625626err = request_threaded_irq(tsdev->irq, NULL, mrstouch_pendet_irq,6270, "mrstouch", tsdev);628if (err) {629dev_err(tsdev->dev, "unable to allocate irq\n");630goto err_free_mem;631}632633err = input_register_device(tsdev->input);634if (err) {635dev_err(tsdev->dev, "unable to register input device\n");636goto err_free_irq;637}638639platform_set_drvdata(pdev, tsdev);640return 0;641642err_free_irq:643free_irq(tsdev->irq, tsdev);644err_free_mem:645input_free_device(input);646kfree(tsdev);647return err;648}649650static int __devexit mrstouch_remove(struct platform_device *pdev)651{652struct mrstouch_dev *tsdev = platform_get_drvdata(pdev);653654free_irq(tsdev->irq, tsdev);655input_unregister_device(tsdev->input);656kfree(tsdev);657658platform_set_drvdata(pdev, NULL);659660return 0;661}662663static struct platform_driver mrstouch_driver = {664.driver = {665.name = "pmic_touch",666.owner = THIS_MODULE,667},668.probe = mrstouch_probe,669.remove = __devexit_p(mrstouch_remove),670};671672static int __init mrstouch_init(void)673{674return platform_driver_register(&mrstouch_driver);675}676module_init(mrstouch_init);677678static void __exit mrstouch_exit(void)679{680platform_driver_unregister(&mrstouch_driver);681}682module_exit(mrstouch_exit);683684MODULE_AUTHOR("Sreedhara Murthy. D.S, [email protected]");685MODULE_DESCRIPTION("Intel Moorestown Resistive Touch Screen Driver");686MODULE_LICENSE("GPL");687688689