android4.1.2-kernel3.0-s3c2410-tc.c

来源:互联网 发布:2017下半年网络新词 编辑:程序博客网 时间:2024/06/18 10:03
/* * Samsung S3C24XX touchscreen driver * * This program is free software; you can redistribute it and/or modify * it under the term of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * 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. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * Copyright 2004 Arnaud Patard <arnaud.patard@rtp-net.org> * Copyright 2008 Ben Dooks <ben-linux@fluff.org> * Copyright 2009 Simtec Electronics <linux@simtec.co.uk> * * Additional work by Herbert Pötzl <herbert@13thfloor.at> and * Harald Welte <laforge@openmoko.org> */#include <linux/errno.h>#include <linux/kernel.h>#include <linux/module.h>#include <linux/gpio.h>#include <linux/input.h>#include <linux/init.h>#include <linux/delay.h>#include <linux/interrupt.h>#include <linux/platform_device.h>#include <linux/clk.h>#include <linux/io.h>#include <plat/adc.h>#include <plat/regs-adc.h>#include <plat/ts.h>#define TSC_SLEEP  (S3C2410_ADCTSC_PULL_UP_DISABLE | S3C2410_ADCTSC_XY_PST(0))#define INT_DOWN    (0)#define INT_UP        (1 << 8)/* TSC Touch sensitive control 触摸敏感控制 */#define WAIT4INT    (S3C2410_ADCTSC_YM_SEN | \             S3C2410_ADCTSC_YP_SEN | \             S3C2410_ADCTSC_XP_SEN | \             S3C2410_ADCTSC_XY_PST(3))#define AUTOPST        (S3C2410_ADCTSC_YM_SEN | \             S3C2410_ADCTSC_YP_SEN | \             S3C2410_ADCTSC_XP_SEN | \             S3C2410_ADCTSC_AUTO_PST | \             S3C2410_ADCTSC_XY_PST(0))#define FEAT_PEN_IRQ    (1 << 0)    /* HAS ADCCLRINTPNDNUP *//* Per-touchscreen data. *//** * struct s3c2410ts - driver touchscreen state. * @client: The ADC client we registered with the core driver. * @dev: The device we are bound to. * @input: The input device we registered with the input subsystem. * @clock: The clock for the adc. * @io: Pointer to the IO base. * @xp: The accumulated X position data. * @yp: The accumulated Y position data. * @irq_tc: The interrupt number for pen up/down interrupt * @count: The number of samples collected. * @shift: The log2 of the maximum count to read in one go. * @features: The features supported by the TSADC MOdule. */struct s3c2410ts {    struct s3c_adc_client *client;    struct device *dev;    struct input_dev *input;    //定义输入设备    struct clk *clock;          //时钟?    void __iomem *io;           //io映射?    unsigned long xp;           //X轴坐标    unsigned long yp;           //Y轴坐标    int irq_tc;                 //中断信号    int count;    int shift;                  //用于根据采样次数求平均值    int features;};static struct s3c2410ts ts;/** * get_down - return the down state of the pen * @data0: The data read from ADCDAT0 register. * @data1: The data read from ADCDAT1 register. * * Return non-zero if both readings show that the pen is down. */static inline bool get_down(unsigned long data0, unsigned long data1){    /* returns true if both data values show stylus down */    return (!(data0 & S3C2410_ADCDAT0_UPDOWN) &&        !(data1 & S3C2410_ADCDAT0_UPDOWN));}static void touch_timer_fire(unsigned long data){    unsigned long data0;    //保存X轴坐标    unsigned long data1;    //保存Y轴坐标    bool down;              //保存按键状态1:按下 0:抬起    data0 = readl(ts.io + S3C2410_ADCDAT0);//读X轴坐标    data1 = readl(ts.io + S3C2410_ADCDAT1);//读Y轴坐标    down = get_down(data0, data1);//测试触摸笔是否按下    /*触笔按下后,开始对X/Y轴的坐标进行A/D转换,为了求得比较精细到坐标值,可以进行多次转换后取平均值,这里的ts.shift就是转换(采样)的次数.*/    if (down) {                             /*触笔按下*/        if (ts.count == (1 << ts.shift)) {  /*多次采样已经完成*/            ts.xp >>= ts.shift;             /*求平均值*/            ts.yp >>= ts.shift;            dev_dbg(ts.dev, "%s: X=%lu, Y=%lu, count=%d\n",                __func__, ts.xp, ts.yp, ts.count);            //上报X/Y坐标            input_report_abs(ts.input, ABS_X, ts.xp);            input_report_abs(ts.input, ABS_Y, ts.yp);            //上报按键按下            input_report_key(ts.input, BTN_TOUCH, 1);            input_sync(ts.input);//等待接收方回复确认/*如果触笔是刚刚按下,那么ts.count的值为0,此时要清空之前保存的数值,有一种情况,当触笔在屏幕上拖动时,会不停地采样并报告坐标值*/            ts.xp = 0;            ts.yp = 0;            ts.count = 0;        }        s3c_adc_start(ts.client, 0, 1 << ts.shift);    } else {        ts.xp = 0;        ts.yp = 0;        ts.count = 0;        input_report_key(ts.input, BTN_TOUCH, 0);        input_sync(ts.input);        //设置自动转换???        writel(WAIT4INT | INT_DOWN, ts.io + S3C2410_ADCTSC);    }}static DEFINE_TIMER(touch_timer, touch_timer_fire, 0, 0);/** * stylus_irq - touchscreen stylus event interrupt * @irq: The interrupt number * @dev_id: The device ID. * * Called when the IRQ_TC is fired for a pen up or down event. *//* 触笔处理中断处程序?当触笔抬起或按下;IRQ_TC是中断号? */static irqreturn_t stylus_irq(int irq, void *dev_id){    unsigned long data0;    //保存ADCDAT0的值    unsigned long data1;    //保存ADCDAT1的值    bool down;              //保存按键状态    data0 = readl(ts.io + S3C2410_ADCDAT0);//读ADCDAT0的值    data1 = readl(ts.io + S3C2410_ADCDAT1);//读ADCDAT1的值    down = get_down(data0, data1);//得到按键状态    /* TODO we should never get an interrupt with down set while     * the timer 定时器is running, but maybe we ought to verify that the     * timer isn't running anyways. */    if (down)        s3c_adc_start(ts.client, 0, 1 << ts.shift);//开始模数转换???    else        dev_dbg(ts.dev, "%s: count=%d\n", __func__, ts.count);    if (ts.features & FEAT_PEN_IRQ) {        /* Clear pen down/up interrupt */        writel(0x0, ts.io + S3C64XX_ADCCLRINTPNDNUP);    }    return IRQ_HANDLED;}/** * s3c24xx_ts_conversion - ADC conversion callback * @client: The client that was registered with the ADC core. * @data0: The reading from ADCDAT0. * @data1: The reading from ADCDAT1. * @left: The number of samples left. * * Called when a conversion has finished. *///ADC转换回调函数static void s3c24xx_ts_conversion(struct s3c_adc_client *client,                  unsigned data0, unsigned data1,                  unsigned *left){    dev_dbg(ts.dev, "%s: %d,%d\n", __func__, data0, data1);    ts.xp += data0;    ts.yp += data1;    ts.count++;    /* From tests, it seems that it is unlikely to get a pen-up     * event during the conversion process which means we can     * ignore any pen-up events with less than the requisite     * count done.     *     * In several thousand conversions, no pen-ups where detected     * before count completed.     */}/** * s3c24xx_ts_select - ADC selection callback. * @client: The client that was registered with the ADC core. * @select: The reason for select. * * Called when the ADC core selects (or deslects) us as a client. */static void s3c24xx_ts_select(struct s3c_adc_client *client, unsigned select){    if (select) {        writel(S3C2410_ADCTSC_PULL_UP_DISABLE | AUTOPST,               ts.io + S3C2410_ADCTSC);    } else {        mod_timer(&touch_timer, jiffies+1);        writel(WAIT4INT | INT_UP, ts.io + S3C2410_ADCTSC);    }}/** * s3c2410ts_probe - device core probe entry point * @pdev: The device we are being bound to. * * Initialise, find and allocate any resources we need to run and then * register with the ADC and input systems. */static int __devinit s3c2410ts_probe(struct platform_device *pdev){    struct s3c2410_ts_mach_info *info;  //保存触摸屏特定数据;分频比,延时等;    struct device *dev = &pdev->dev;    struct input_dev *input_dev;    struct resource *res;    int ret = -EINVAL;    /* Initialise input stuff初始化输入的东西 */    memset(&ts, 0, sizeof(struct s3c2410ts));    ts.dev = dev;    info = pdev->dev.platform_data;    if (!info) {        dev_err(dev, "no platform data, cannot attach\n");        return -EINVAL;//出错返回;    }    //打印调试信息    dev_dbg(dev, "initialising touchscreen\n");    //获取时钟,挂载APB BUS上到外围设备,需要时钟控制,ADC就是这样到设备;    ts.clock = clk_get(dev, "adc");    if (IS_ERR(ts.clock)) {        dev_err(dev, "cannot get adc clock source\n");        return -ENOENT;    }        clk_enable(ts.clock);//获得,启动时钟    dev_dbg(dev, "got and enabled clocks\n");    ts.irq_tc = ret = platform_get_irq(pdev, 0);//申请中断资源    if (ret < 0) {        dev_err(dev, "no resource for interrupt\n");        goto err_clk;    }    res = platform_get_resource(pdev, IORESOURCE_MEM, 0);    if (!res) {        dev_err(dev, "no resource for registers\n");        ret = -ENOENT;        goto err_clk;    }    /*开始映射I/O内存,I/O内存是不能直接进行访问的,必须对其进行映射,为I/O内存分配虚拟地址,这些虚拟机地址以__iomem进行说明,但不能直接对其进行访问,需要使用专有到函数,如iowrite32()*/    ts.io = ioremap(res->start, resource_size(res));    if (ts.io == NULL) {        dev_err(dev, "cannot map registers\n");        ret = -ENOMEM;        goto err_clk;    }    /* inititalise the gpio */    if (info->cfg_gpio)        info->cfg_gpio(to_platform_device(ts.dev));    ts.client = s3c_adc_register(pdev, s3c24xx_ts_select,                     s3c24xx_ts_conversion, 1);    if (IS_ERR(ts.client)) {        dev_err(dev, "failed to register adc client\n");        ret = PTR_ERR(ts.client);        goto err_iomap;    }    /* Initialise registers */    //设置ADC延时,在等待中断模式下表示产生INT_TC的时间间隔;    if ((info->delay & 0xffff) > 0)        writel(info->delay & 0xffff, ts.io + S3C2410_ADCDLY);    //按照等待中断的模式设置TSC    writel(WAIT4INT | INT_DOWN, ts.io + S3C2410_ADCTSC);    //为新到输入设备分配内存    input_dev = input_allocate_device();    if (!input_dev) {        dev_err(dev, "Unable to allocate the input device !!\n");        ret = -ENOMEM;        goto err_iomap;    }    ts.input = input_dev;    ts.input->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS);//触摸屏支持的事件类型;    ts.input->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH);//触屏支持的事件类型;    /*EV_ABS事件类型在调用input_register_device()函数之前,必须为绝对坐标填充;*/    input_set_abs_params(ts.input, ABS_X, 0, 0x3FF, 0, 0);    input_set_abs_params(ts.input, ABS_Y, 0, 0x3FF, 0, 0);    ts.input->name = "S3C24XX TouchScreen"; //输入设备名字    ts.input->id.bustype = BUS_HOST;        //总线id    ts.input->id.vendor = 0xDEAD;           //设备id    ts.input->id.product = 0xBEEF;          //设备id    ts.input->id.version = 0x0102;          //版本    ts.shift = info->oversampling_shift;    //设置采样次数    ts.features = platform_get_device_id(pdev)->driver_data;    //申请中断?    ret = request_irq(ts.irq_tc, stylus_irq, IRQF_DISABLED,              "s3c2410_ts_pen", ts.input);    if (ret) {        dev_err(dev, "cannot get TC interrupt\n");        goto err_inputdev;    }    dev_info(dev, "driver attached, registering input device\n");    /* All went ok, so register to the input system */    ret = input_register_device(ts.input);  //注册输入设备    if (ret < 0) {        dev_err(dev, "failed to register input device\n");        ret = -EIO;        goto err_tcirq;    }    return 0; err_tcirq:    free_irq(ts.irq_tc, ts.input); err_inputdev:    input_free_device(ts.input); err_iomap:    iounmap(ts.io); err_clk:    del_timer_sync(&touch_timer);    clk_put(ts.clock);    return ret;}/** * s3c2410ts_remove - device core removal entry point * @pdev: The device we are being removed from. * * Free up our state ready to be removed. */static int __devexit s3c2410ts_remove(struct platform_device *pdev){    free_irq(ts.irq_tc, ts.input);  //释放触摸屏中断号    del_timer_sync(&touch_timer);   //???    clk_disable(ts.clock);          //关闭时钟    clk_put(ts.clock);    input_unregister_device(ts.input);//注销输入设备    iounmap(ts.io);                 //断开I/O映射    return 0;}#ifdef CONFIG_PMstatic int s3c2410ts_suspend(struct device *dev){    writel(TSC_SLEEP, ts.io + S3C2410_ADCTSC);    disable_irq(ts.irq_tc);    clk_disable(ts.clock);    return 0;}static int s3c2410ts_resume(struct device *dev){    struct platform_device *pdev = to_platform_device(dev);    struct s3c2410_ts_mach_info *info = pdev->dev.platform_data;    clk_enable(ts.clock);    enable_irq(ts.irq_tc);    /* Initialise registers */    if ((info->delay & 0xffff) > 0)        writel(info->delay & 0xffff, ts.io + S3C2410_ADCDLY);    writel(WAIT4INT | INT_DOWN, ts.io + S3C2410_ADCTSC);    return 0;}static struct dev_pm_ops s3c_ts_pmops = {    .suspend    = s3c2410ts_suspend,    .resume        = s3c2410ts_resume,};#endif//支持到设备IDSstatic struct platform_device_id s3cts_driver_ids[] = {    { "s3c2410-ts", 0 },    { "s3c2440-ts", 0 },    { "s3c64xx-ts", FEAT_PEN_IRQ },    { }};MODULE_DEVICE_TABLE(platform, s3cts_driver_ids);static struct platform_driver s3c_ts_driver = {    .driver         = {        .name   = "samsung-ts",        .owner  = THIS_MODULE,#ifdef CONFIG_PM        .pm    = &s3c_ts_pmops,#endif    },    .id_table    = s3cts_driver_ids, //支持到设备表    /*探测函数,硬件资源获取、GPIO口初始化、中断申请、注册驱动程序等*/    .probe        = s3c2410ts_probe,      .remove        = __devexit_p(s3c2410ts_remove),};static int __init s3c2410ts_init(void){    return platform_driver_register(&s3c_ts_driver);}static void __exit s3c2410ts_exit(void){    platform_driver_unregister(&s3c_ts_driver);}module_init(s3c2410ts_init);module_exit(s3c2410ts_exit);MODULE_AUTHOR("Arnaud Patard <arnaud.patard@rtp-net.org>, "          "Ben Dooks <ben@simtec.co.uk>, "          "Simtec Electronics <linux@simtec.co.uk>");MODULE_DESCRIPTION("S3C24XX Touchscreen driver");MODULE_LICENSE("GPL v2");


 

原创粉丝点击