触摸屏驱动分析

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_dev
	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;
}

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)	//如果触摸屏按下,启动adc
		s3c_adc_start(ts.client, 0, 1 << ts.shift);	//开始adc
	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;
}
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;
	else
		list_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);
	} else
		adc->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_select

	con &= ~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);
		else
			con |= 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参数为1
		client->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
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值