Path: blob/master/drivers/input/touchscreen/mc13783_ts.c
15111 views
/*1* Driver for the Freescale Semiconductor MC13783 touchscreen.2*3* Copyright 2004-2007 Freescale Semiconductor, Inc. All Rights Reserved.4* Copyright (C) 2009 Sascha Hauer, Pengutronix5*6* Initial development of this code was funded by7* Phytec Messtechnik GmbH, http://www.phytec.de/8*9* This program is free software; you can redistribute it and/or modify it10* under the terms of the GNU General Public License version 2 as published by11* the Free Software Foundation.12*/13#include <linux/platform_device.h>14#include <linux/mfd/mc13783.h>15#include <linux/kernel.h>16#include <linux/module.h>17#include <linux/input.h>18#include <linux/sched.h>19#include <linux/slab.h>20#include <linux/init.h>2122#define MC13783_TS_NAME "mc13783-ts"2324#define DEFAULT_SAMPLE_TOLERANCE 3002526static unsigned int sample_tolerance = DEFAULT_SAMPLE_TOLERANCE;27module_param(sample_tolerance, uint, S_IRUGO | S_IWUSR);28MODULE_PARM_DESC(sample_tolerance,29"If the minimal and maximal value read out for one axis (out "30"of three) differ by this value (default: "31__stringify(DEFAULT_SAMPLE_TOLERANCE) ") or more, the reading "32"is supposed to be wrong and is discarded. Set to 0 to "33"disable this check.");3435struct mc13783_ts_priv {36struct input_dev *idev;37struct mc13783 *mc13783;38struct delayed_work work;39struct workqueue_struct *workq;40unsigned int sample[4];41};4243static irqreturn_t mc13783_ts_handler(int irq, void *data)44{45struct mc13783_ts_priv *priv = data;4647mc13783_irq_ack(priv->mc13783, irq);4849/*50* Kick off reading coordinates. Note that if work happens already51* be queued for future execution (it rearms itself) it will not52* be rescheduled for immediate execution here. However the rearm53* delay is HZ / 50 which is acceptable.54*/55queue_delayed_work(priv->workq, &priv->work, 0);5657return IRQ_HANDLED;58}5960#define sort3(a0, a1, a2) ({ \61if (a0 > a1) \62swap(a0, a1); \63if (a1 > a2) \64swap(a1, a2); \65if (a0 > a1) \66swap(a0, a1); \67})6869static void mc13783_ts_report_sample(struct mc13783_ts_priv *priv)70{71struct input_dev *idev = priv->idev;72int x0, x1, x2, y0, y1, y2;73int cr0, cr1;7475/*76* the values are 10-bit wide only, but the two least significant77* bits are for future 12 bit use and reading yields 078*/79x0 = priv->sample[0] & 0xfff;80x1 = priv->sample[1] & 0xfff;81x2 = priv->sample[2] & 0xfff;82y0 = priv->sample[3] & 0xfff;83y1 = (priv->sample[0] >> 12) & 0xfff;84y2 = (priv->sample[1] >> 12) & 0xfff;85cr0 = (priv->sample[2] >> 12) & 0xfff;86cr1 = (priv->sample[3] >> 12) & 0xfff;8788dev_dbg(&idev->dev,89"x: (% 4d,% 4d,% 4d) y: (% 4d, % 4d,% 4d) cr: (% 4d, % 4d)\n",90x0, x1, x2, y0, y1, y2, cr0, cr1);9192sort3(x0, x1, x2);93sort3(y0, y1, y2);9495cr0 = (cr0 + cr1) / 2;9697if (!cr0 || !sample_tolerance ||98(x2 - x0 < sample_tolerance &&99y2 - y0 < sample_tolerance)) {100/* report the median coordinate and average pressure */101if (cr0) {102input_report_abs(idev, ABS_X, x1);103input_report_abs(idev, ABS_Y, y1);104105dev_dbg(&idev->dev, "report (%d, %d, %d)\n",106x1, y1, 0x1000 - cr0);107queue_delayed_work(priv->workq, &priv->work, HZ / 50);108} else109dev_dbg(&idev->dev, "report release\n");110111input_report_abs(idev, ABS_PRESSURE,112cr0 ? 0x1000 - cr0 : cr0);113input_report_key(idev, BTN_TOUCH, cr0);114input_sync(idev);115} else116dev_dbg(&idev->dev, "discard event\n");117}118119static void mc13783_ts_work(struct work_struct *work)120{121struct mc13783_ts_priv *priv =122container_of(work, struct mc13783_ts_priv, work.work);123unsigned int mode = MC13783_ADC_MODE_TS;124unsigned int channel = 12;125126if (mc13783_adc_do_conversion(priv->mc13783,127mode, channel, priv->sample) == 0)128mc13783_ts_report_sample(priv);129}130131static int mc13783_ts_open(struct input_dev *dev)132{133struct mc13783_ts_priv *priv = input_get_drvdata(dev);134int ret;135136mc13783_lock(priv->mc13783);137138mc13783_irq_ack(priv->mc13783, MC13783_IRQ_TS);139140ret = mc13783_irq_request(priv->mc13783, MC13783_IRQ_TS,141mc13783_ts_handler, MC13783_TS_NAME, priv);142if (ret)143goto out;144145ret = mc13783_reg_rmw(priv->mc13783, MC13783_ADC0,146MC13783_ADC0_TSMOD_MASK, MC13783_ADC0_TSMOD0);147if (ret)148mc13783_irq_free(priv->mc13783, MC13783_IRQ_TS, priv);149out:150mc13783_unlock(priv->mc13783);151return ret;152}153154static void mc13783_ts_close(struct input_dev *dev)155{156struct mc13783_ts_priv *priv = input_get_drvdata(dev);157158mc13783_lock(priv->mc13783);159mc13783_reg_rmw(priv->mc13783, MC13783_ADC0,160MC13783_ADC0_TSMOD_MASK, 0);161mc13783_irq_free(priv->mc13783, MC13783_IRQ_TS, priv);162mc13783_unlock(priv->mc13783);163164cancel_delayed_work_sync(&priv->work);165}166167static int __init mc13783_ts_probe(struct platform_device *pdev)168{169struct mc13783_ts_priv *priv;170struct input_dev *idev;171int ret = -ENOMEM;172173priv = kzalloc(sizeof(*priv), GFP_KERNEL);174idev = input_allocate_device();175if (!priv || !idev)176goto err_free_mem;177178INIT_DELAYED_WORK(&priv->work, mc13783_ts_work);179priv->mc13783 = dev_get_drvdata(pdev->dev.parent);180priv->idev = idev;181182/*183* We need separate workqueue because mc13783_adc_do_conversion184* uses keventd and thus would deadlock.185*/186priv->workq = create_singlethread_workqueue("mc13783_ts");187if (!priv->workq)188goto err_free_mem;189190idev->name = MC13783_TS_NAME;191idev->dev.parent = &pdev->dev;192193idev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS);194idev->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH);195input_set_abs_params(idev, ABS_X, 0, 0xfff, 0, 0);196input_set_abs_params(idev, ABS_Y, 0, 0xfff, 0, 0);197input_set_abs_params(idev, ABS_PRESSURE, 0, 0xfff, 0, 0);198199idev->open = mc13783_ts_open;200idev->close = mc13783_ts_close;201202input_set_drvdata(idev, priv);203204ret = input_register_device(priv->idev);205if (ret) {206dev_err(&pdev->dev,207"register input device failed with %d\n", ret);208goto err_destroy_wq;209}210211platform_set_drvdata(pdev, priv);212return 0;213214err_destroy_wq:215destroy_workqueue(priv->workq);216err_free_mem:217input_free_device(idev);218kfree(priv);219return ret;220}221222static int __devexit mc13783_ts_remove(struct platform_device *pdev)223{224struct mc13783_ts_priv *priv = platform_get_drvdata(pdev);225226platform_set_drvdata(pdev, NULL);227228destroy_workqueue(priv->workq);229input_unregister_device(priv->idev);230kfree(priv);231232return 0;233}234235static struct platform_driver mc13783_ts_driver = {236.remove = __devexit_p(mc13783_ts_remove),237.driver = {238.owner = THIS_MODULE,239.name = MC13783_TS_NAME,240},241};242243static int __init mc13783_ts_init(void)244{245return platform_driver_probe(&mc13783_ts_driver, &mc13783_ts_probe);246}247module_init(mc13783_ts_init);248249static void __exit mc13783_ts_exit(void)250{251platform_driver_unregister(&mc13783_ts_driver);252}253module_exit(mc13783_ts_exit);254255MODULE_DESCRIPTION("MC13783 input touchscreen driver");256MODULE_AUTHOR("Sascha Hauer <[email protected]>");257MODULE_LICENSE("GPL v2");258MODULE_ALIAS("platform:" MC13783_TS_NAME);259260261