触摸屏驱动分析

来源:互联网 发布:7月7日白银数据错误 编辑:程序博客网 时间:2024/05/21 13:22

1. 触摸屏的注册

触摸屏是作为input dev注册到系统中的。下面以s3c2440的触摸屏为例,它是与ADC控制器结合在一起的。

还是以probe过程来分析。

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");ts.clock = clk_get(dev, "adc");//获取adc时钟if (IS_ERR(ts.clock)) {dev_err(dev, "cannot get adc clock source\n");return -ENOENT;}clk_enable(ts.clock);//adc时钟使能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);//获取io资源if (!res) {dev_err(dev, "no resource for registers\n");ret = -ENOENT;goto err_clk;}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));//注册s3c_adc_client,跟adc驱动模型有关的接口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 */if ((info->delay & 0xffff) > 0)writel(info->delay & 0xffff, ts.io + S3C2410_ADCDLY);writel(WAIT4INT | INT_DOWN, ts.io + S3C2410_ADCTSC);//分配input_dev结构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);//对EV_KEY和EV_ABS感兴趣ts.input->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH);//对EV_KEY的子事件BTN_TOUCH感兴趣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;ts.input->id.vendor = 0xDEAD;ts.input->id.product = 0xBEEF;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);//注册input_devif (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;}

struct s3c_adc_client *s3c_adc_register(struct platform_device *pdev,void (*select)(struct s3c_adc_client *client,       unsigned int selected),void (*conv)(struct s3c_adc_client *client,     unsigned d0, unsigned d1,     unsigned *samples_left),unsigned int is_ts){struct s3c_adc_client *client;WARN_ON(!pdev);if (!select)select = s3c_adc_default_select;if (!pdev)return ERR_PTR(-EINVAL);client = kzalloc(sizeof(struct s3c_adc_client), GFP_KERNEL);if (!client) {dev_err(&pdev->dev, "no memory for adc client\n");return ERR_PTR(-ENOMEM);}client->pdev = pdev;client->is_ts = is_ts;client->select_cb = select;client->convert_cb = conv;return client;}

input_dev结构分配好了,那什么时候会调动input_report_***的函数呢,那肯定是在中断过程中

static irqreturn_t stylus_irq(int irq, void *dev_id){unsigned long data0;unsigned long data1;bool down;//本次读取是为了判断,还有获取坐标值data0 = readl(ts.io + S3C2410_ADCDAT0);data1 = readl(ts.io + S3C2410_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)//如果触摸屏按下,启动adcs3c_adc_start(ts.client, 0, 1 << ts.shift);//开始adcelsedev_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;}int s3c_adc_start(struct s3c_adc_client *client,  unsigned int channel, unsigned int nr_samples){struct adc_device *adc = adc_dev;unsigned long flags;if (!adc) {printk(KERN_ERR "%s: failed to find adc\n", __func__);return -EINVAL;}if (client->is_ts && adc->ts_pend)return -EAGAIN;//获取自旋锁,并保存中断状态字spin_lock_irqsave(&adc->lock, flags);client->channel = channel;client->nr_samples = nr_samples;if (client->is_ts)adc->ts_pend = client;elselist_add_tail(&client->pend, &adc_pending); //插入adc_pending链表if (!adc->cur)s3c_adc_try(adc);//开始转换spin_unlock_irqrestore(&adc->lock, flags);return 0;}static void s3c_adc_try(struct adc_device *adc){struct s3c_adc_client *next = adc->ts_pend;if (!next && !list_empty(&adc_pending)) {next = list_first_entry(&adc_pending,struct s3c_adc_client, pend);list_del(&next->pend);} elseadc->ts_pend = NULL;if (next) {adc_dbg(adc, "new client is %p\n", next);adc->cur = next;s3c_adc_select(adc, next);s3c_adc_convert(adc);s3c_adc_dbgshow(adc);}}static inline void s3c_adc_select(struct adc_device *adc,  struct s3c_adc_client *client){unsigned con = readl(adc->regs + S3C2410_ADCCON);enum s3c_cpu_type cpu = platform_get_device_id(adc->pdev)->driver_data;client->select_cb(client, 1);//调用s3c24xx_ts_selectcon &= ~S3C2410_ADCCON_MUXMASK;con &= ~S3C2410_ADCCON_STDBM;con &= ~S3C2410_ADCCON_STARTMASK;if (!client->is_ts) {if (cpu == TYPE_ADCV3)writel(client->channel & 0xf, adc->regs + S5P_ADCMUX);elsecon |= S3C2410_ADCCON_SELMUX(client->channel);}writel(con, adc->regs + S3C2410_ADCCON);}

static void s3c24xx_ts_select(struct s3c_adc_client *client, unsigned select){//启动自动联系x轴和y轴坐标转换模式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);}}static inline void s3c_adc_convert(struct adc_device *adc){unsigned con = readl(adc->regs + S3C2410_ADCCON);con |= S3C2410_ADCCON_ENABLE_START;writel(con, adc->regs + S3C2410_ADCCON);//启动adc转换}
再来看下ad转换中断处理函数
static irqreturn_t s3c_adc_irq(int irq, void *pw){struct adc_device *adc = pw;struct s3c_adc_client *client = adc->cur;enum s3c_cpu_type cpu = platform_get_device_id(adc->pdev)->driver_data;unsigned data0, data1;if (!client) {dev_warn(&adc->pdev->dev, "%s: no adc pending\n", __func__);goto exit;}data0 = readl(adc->regs + S3C2410_ADCDAT0);data1 = readl(adc->regs + S3C2410_ADCDAT1);adc_dbg(adc, "read %d: 0x%04x, 0x%04x\n", client->nr_samples, data0, data1);client->nr_samples--;if (cpu != TYPE_ADCV1) {/* S3C64XX/S5P ADC resolution is 12-bit */data0 &= 0xfff;data1 &= 0xfff;} else {data0 &= 0x3ff;data1 &= 0x3ff;}if (client->convert_cb)(client->convert_cb)(client, data0, data1, &client->nr_samples);if (client->nr_samples > 0) {/* fire another conversion for this *///还没达到采样次数,继续采样,注意select参数为1client->select_cb(client, 1);s3c_adc_convert(adc);} else {spin_lock(&adc->lock);//达到采样次数,启动timer(client->select_cb)(client, 0);adc->cur = NULL;s3c_adc_try(adc);spin_unlock(&adc->lock);}exit:if (cpu != TYPE_ADCV1) {/* Clear ADC interrupt */writel(0, adc->regs + S3C64XX_ADCCLRINT);}return IRQ_HANDLED;}static void touch_timer_fire(unsigned long data){unsigned long data0;unsigned long data1;bool down;data0 = readl(ts.io + S3C2410_ADCDAT0);data1 = readl(ts.io + S3C2410_ADCDAT1);//判断是否被按下down = get_down(data0, data1);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);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.xp = 0;ts.yp = 0;ts.count = 0;}//重新开始adc转换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);}}

0 0
原创粉丝点击