----------------------------------------------------------------------------------------------------------------------------
内核版本:linux 5.2.8
根文件系统:busybox 1.25.0
u-boot:2016.05
----------------------------------------------------------------------------------------------------------------------------
在上一节我们已经移植了LCD驱动,那么本节将会移植LCD触摸屏驱动。有关触摸屏的原理,以及硬件接线,我们在linux驱动移植-LCD触摸屏设备驱动章节已经介绍的非常清楚了。同时在这一篇博客,我们也详细介绍了触摸屏驱动的实现,并进行了代码演示。
linux 5.2.8内核已经自带了s3c2440触摸屏驱动,该驱动还依赖于ADC驱动,相当于把我们在linux驱动移植-LCD触摸屏设备驱动中写的驱动程序拆成了两个部分,但是代码整体逻辑大致是一样的。
这一节,我们将尝试引入设备树,通过设备树来实现触摸屏驱动程序。
一、触摸屏驱动
linux 5.2.8自带的s3c2440触摸屏驱动,其采用platform设备驱动模型。
1.1 platform device
名字为"s3c2410-ts"的platform device定义在arch/arm/plat-samsung/devs.c文件:
static struct resource s3c_ts_resource[] = {
[0] = DEFINE_RES_MEM(S3C24XX_PA_ADC, S3C24XX_SZ_ADC), // 0x58000000 SZ_1M
[1] = DEFINE_RES_IRQ(IRQ_TC), // IRQ_TC为子中断 子中断控制器硬件中断号9,对应的主中断控制器硬件中断号31
};
struct platform_device s3c_device_ts = {
.name = "s3c2410-ts",
.id = -1,
.dev.parent = &s3c_device_adc.dev,
.num_resources = ARRAY_SIZE(s3c_ts_resource),
.resource = s3c_ts_resource,
};
void __init s3c24xx_ts_set_platdata(struct s3c2410_ts_mach_info *hard_s3c2410ts_info)
{
s3c_set_platdata(hard_s3c2410ts_info,
sizeof(struct s3c2410_ts_mach_info), &s3c_device_ts);
}
其中函数s3c24xx_ts_set_platdata用于设置platform设备的私有数据,数据类型为struct s3c2410_ts_mach_info,s3c_device_ts.dev.platform_data会被设置为&default_ts_data :
static struct s3c2410_ts_mach_info default_ts_data __initdata = {
.delay = 10000,
.presc = 49,
.oversampling_shift = 2,
};
1.2 platform driver
名字为"s3c2410-ts"的platform driver定义在drivers/input/touchscreen/s3c2410_ts.c文件:
static const struct dev_pm_ops s3c_ts_pmops = {
.suspend = s3c2410ts_suspend,
.resume = s3c2410ts_resume,
};
#endif
static const 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",
#ifdef CONFIG_PM
.pm = &s3c_ts_pmops,
#endif
},
.id_table = s3cts_driver_ids,
.probe = s3c2410ts_probe,
.remove = s3c2410ts_remove,
};
module_platform_driver(s3c_ts_driver);
1.3 s3c2410ts_probe
当platform设备和驱动匹配后,将会调用s3c2410ts_probe进行input设备的注册。函数定义在drivers/input/touchscreen/s3c2410_ts.c:
/**
* 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 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 = dev_get_platdata(dev);
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");
if (IS_ERR(ts.clock)) {
dev_err(dev, "cannot get adc clock source\n");
return -ENOENT;
}
ret = clk_prepare_enable(ts.clock);
if (ret) {
dev_err(dev, "Failed! to enabled clocks\n");
goto err_clk_get;
}
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;
}
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 */
if ((info->delay & 0xffff) > 0)
writel(info->delay & 0xffff, ts.io + S3C2410_ADCDLY);
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);
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, 0,
"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;
}
r