These articles are mainly about touch screen drivers in Linux, based on the s3c6410 processor. This article is mainly about the implementation of touch screen devices as platform devices, as well as the source code analysis of the corresponding probe function and remove function.
1. Loading and unloading functions of touch screen module
static char banner[] __initdata = KERN_INFO "S3C Touchscreen driver, (c) 2008 Samsung Electronicsn";
static int __init s3c_ts_init(void)
{
printk(banner);
return platform_driver_register(&s3c_ts_driver);
}
static void __exit s3c_ts_exit(void)
{
platform_driver_unregister(&s3c_ts_driver);
}
module_init(s3c_ts_init);
module_exit(s3c_ts_exit);
No matter how things change, he remains the same familiar person, but each time is a new journey.
Corresponding platform device resources: in the Dev-ts.c (linux2.6.28archarmplat-s3c) file
/* Touch srcreen */
static struct resource s3c_ts_resource[] = {
[0] = {
.start = S3C_PA_ADC, I/O port
.end = S3C_PA_ADC + SZ_4K - 1,
.flags = IORESOURCE_MEM,
},
[1] = {
.start = IRQ_PENDN, interrupt
.end = IRQ_PENDN,
.flags = IORESOURCE_IRQ,
},
[2] = {
.start = IRQ_ADC, interrupt
.end = IRQ_ADC,
.flags = IORESOURCE_IRQ,
}
};
struct platform_device s3c_device_ts = {
.name = "s3c-ts",
.id = -1,
.num_resources = ARRAY_SIZE(s3c_ts_resource),
.resource = s3c_ts_resource,
};
The corresponding platform_driver structure is defined as follows:
static struct platform_driver s3c_ts_driver = {
.probe = s3c_ts_probe,
.remove = s3c_ts_remove,
.suspend = s3c_ts_suspend,
.resume = s3c_ts_resume,
.driver = {
.owner = THIS_MODULE,
.name = "s3c-ts",
},
};
2. I think I should know what to do, then look at the probe function, the source code is as follows:
/*
* The functions for inserting/removing us as a module.
*/
static int __init s3c_ts_probe(struct platform_device *pdev)
{
struct resource *res;
struct device *dev;
struct input_dev *input_dev;
struct s3c_ts_mach_info * s3c_ts_cfg;
int ret, size;
dev = &pdev->dev;
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (res == NULL) {
dev_err(dev,"no memory resource specifiedn");
return -ENOENT;
} Get the register operation address
size = (res->end - res->start) + 1;
ts_mem = request_mem_region(res->start, size, pdev->name); Apply for this memory area
注:static struct resource*ts_mem;
if (ts_mem == NULL) {
dev_err(dev, "failed to get memory regionn");
right = -ENOENT;
goto err_req;
}
ts_base = ioremap(res->start, size); Mapping
if (ts_base == NULL) {
dev_err(dev, "failed to ioremap() regionn");
ret = -SINGLE SELECT;
goto err_map;
}
ts_clock = clk_get(&pdev->dev, "adc"); Get the clock
if (IS_ERR(ts_clock)) {
dev_err(dev, "failed to find watchdog clock sourcen");
ret = PTR_ERR(ts_clock);
goto err_clk;
}
clk_enable(ts_clock); Enable clock
s3c_ts_cfg = s3c_ts_get_platdata(&pdev->dev);
static struct s3c_ts_mach_info *s3c_ts_get_platdata (struct device *dev)
{
if (dev->platform_data != NULL)
return (struct s3c_ts_mach_info *)dev->platform_data;
return &s3c_ts_default_cfg;
}
default value:
/* Touchscreen default configuration */
struct s3c_ts_mach_info s3c_ts_default_cfg __initdata = {
.delay = 5000,//10000,
.almost = 49,
.oversampling_shift = 4,//2,
.resol_bit = 10
};
This involves a structure s3c_ts_mach_info
struct s3c_ts_mach_info {
int delay; delay time
int presc; prescale value
int oversampling_shift; number of conversions
int resol_bit; division rate
enum s3c_adc_types3c_adc_con; see below:
};
Including
enum s3c_adc_type {
ADC_TYPE_0,
ADC_TYPE_1, /* S3C2416, S3C2450 */
ADC_TYPE_2,/* S3C64XX, S5PC1XX */
};
if ((s3c_ts_cfg->presc&0xff) > 0) set the prescaler value
writel(S3C_ADCCON_PRSCEN | S3C_ADCCON_PRSCVL(s3c_ts_cfg->presc&0xFF),
ts_base+S3C_ADCCON);
else
writel(0, ts_base+S3C_ADCCON); If not defined, write 0, which actually disables pre-scaling
This is mainly related to the setting of the ADCCON register, and has the following definitions:
#define S3C_ADCCON_PRSCEN(1<<14)
#define S3C_ADCCON_PRSCVL(x)(((x)&0xFF)<<6)
See the picture below:
/* Initialize registers */
if ((s3c_ts_cfg->delay&0xffff) > 0)
writel(s3c_ts_cfg->delay & 0xffff, ts_base+S3C_ADCDLY);
Similar to the above, mainly related to the ADCDLY register. See the picture directly: Note: It has different meanings in the two modes
if (s3c_ts_cfg->resol_bit==12) { division rate
switch(s3c_ts_cfg->s3c_adc_con) {
case ADC_TYPE_2:
writel(readl(ts_base+S3C_ADCCON)|S3C_ADCCON_RESSEL_12BIT, ts_base+S3C_ADCCON);
break;
#define S3C_ADCCON_RESSEL_12BIT(0x1<<16)
case ADC_TYPE_1:
writel(readl(ts_base+S3C_ADCCON)|S3C_ADCCON_RESSEL_12BIT_1, ts_base+S3C_ADCCON);
break;
default:
dev_err(dev, "Touchscreen over this type of AP isn't supported !n");
break;
}
}
writel(WAIT4INT(0), ts_base+S3C_ADCTSC); mainly operates the ADCTSC register to put the touch screen in the waiting interrupt mode
ts = kzalloc(sizeof(struct s3c_ts_info), GFP_KERNEL);
注:static struct s3c_ts_info*ts;
input_dev = input_allocate_device(); Apply for and initialize an input device. Through the input device, the driver can interact with the user.
注:struct input_dev *input_dev;
if (!input_dev) {
right = -ENOMEM;
goto err_alloc;
}
ts->dev = input_dev;
ts->dev->evbit[0] = ts->dev->evbit[0] = BIT_MASK(EV_SYN) | BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS);
ts->dev->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH);
if (s3c_ts_cfg->resol_bit==12) {
input_set_abs_params(ts->dev, ABS_X, 0, 0xFFF, 0, 0);
input_set_abs_params(ts->dev, ABS_Y, 0, 0xFFF, 0, 0);
}
else {
input_set_abs_params(ts->dev, ABS_X, 0, 0x3FF, 0, 0);
input_set_abs_params(ts->dev, ABS_Y, 0, 0x3FF, 0, 0);
}
input_set_abs_params(ts->dev, ABS_PRESSURE, 0, 1, 0, 0);
sprintf(ts->phys, "input(ts)");
ts->dev->name = s3c_ts_name;
ts->dev->phys = ts->phys;
ts->dev->id.bustype = BUS_RS232;
ts->dev->id.vendor = 0xDEAD;
ts->dev->id.product = 0xBEEF;
ts->dev->id.version = S3C_TSVERSION;
ts->shift = s3c_ts_cfg->oversampling_shift;
ts->resol_bit = s3c_ts_cfg->resol_bit;
ts->s3c_adc_con = s3c_ts_cfg->s3c_adc_con;
The above code is to initialize the global quantity ts of the touch screen device. The corresponding structure prototype is:
struct s3c_ts_info {
struct input_dev *dev;
long xp;
long yp;
int count;
int shift;
char phys[32];
int resolve_bit;
enum s3c_adc_types3c_adc_con;
};
/* For IRQ_PENDUP */
ts_irq = platform_get_resource(pdev, IORESOURCE_IRQ, 0); Get the touch screen interrupt IRQ_PENDUP
if (ts_irq == NULL) {
dev_err(dev, "no irq resource specifiedn");
right = -ENOENT;
goto err_irq;
}
ret = request_irq(ts_irq->start, stylus_updown, IRQF_SAMPLE_RANDOM, "s3c_updown", ts); Apply for touch screen interrupt, the corresponding interrupt processing function is stylus_updown
if (ret != 0) {
dev_err(dev,"s3c_ts.c: Could not allocate ts IRQ_PENDN !n");
ret = -EIO;
goto err_irq;
}
/* For IRQ_ADC */
ts_irq = platform_get_resource(pdev, IORESOURCE_IRQ, 1); Get ADC interrupt
if (ts_irq == NULL) {
dev_err(dev, "no irq resource specifiedn");
right = -ENOENT;
goto err_irq;
}
ret = request_irq(ts_irq->start, stylus_action, IRQF_SAMPLE_RANDOM, "s3c_action", ts); Apply for ADC interrupt, the corresponding interrupt function is stylus_action
if (ret != 0) {
dev_err(dev, "s3c_ts.c: Could not allocate ts IRQ_ADC !n");
ret = -EIO;
goto err_irq;
}
printk(KERN_INFO "%s got loaded successfully : %d bitsn", s3c_ts_name, s3c_ts_cfg->resol_bit);
/* All went ok, so register to the input system */ Register the touch screen device to the input subsystem
ret = input_register_device(ts->dev);
if(ret) {
dev_err(dev, "s3c_ts.c: Could not register input device(touchscreen)!n");
ret = -EIO;
goto fail;
}
return 0;
These are the error handling codes:
fail:
free_irq(ts_irq->start, ts->dev);
free_irq(ts_irq->end, ts->dev);
err_irq:
input_free_device(input_dev);
kfree(ts);
err_alloc:
clk_disable(ts_clock);
clk_put(ts_clock);
err_clk:
iounmap(ts_base);
err_map:
release_resource(ts_mem);
kfree(ts_mem);
err_req:
return right;
}
At this point, the probe function of the touch screen device driver has been described.
3. Of course, several important functions in the probe function have not been mentioned, that is, about the input subsystem, which is not the focus of our attention now. Next, look at the corresponding remove function, the source code is as follows:
static int s3c_ts_remove(struct platform_device *dev)
{
printk(KERN_INFO "s3c_ts_remove() of TS called !n");
disable_irq(IRQ_ADC);
disable_irq(IRQ_PENDN);
free_irq(IRQ_PENDN, ts->dev);
free_irq(IRQ_ADC, ts->dev);
if (ts_clock) {
clk_disable(ts_clock);
clk_put(ts_clock);
ts_clock = NULL;
}
input_unregister_device(ts->dev);
iounmap(ts_base);
return 0;
}
In fact, if you understand the probe function, you don’t need to read the remove function at all.
Previous article:s3c6410 RTC driver in Linux (5)
Next article:Implementation of touch screen driver in Linux (3) - based on s3c6410 processor
Recommended ReadingLatest update time:2024-11-16 11:52
- Popular Resources
- Popular amplifiers
Professor at Beihang University, dedicated to promoting microcontrollers and embedded systems for over 20 years.
- Innolux's intelligent steer-by-wire solution makes cars smarter and safer
- 8051 MCU - Parity Check
- How to efficiently balance the sensitivity of tactile sensing interfaces
- What should I do if the servo motor shakes? What causes the servo motor to shake quickly?
- 【Brushless Motor】Analysis of three-phase BLDC motor and sharing of two popular development boards
- Midea Industrial Technology's subsidiaries Clou Electronics and Hekang New Energy jointly appeared at the Munich Battery Energy Storage Exhibition and Solar Energy Exhibition
- Guoxin Sichen | Application of ferroelectric memory PB85RS2MC in power battery management, with a capacity of 2M
- Analysis of common faults of frequency converter
- In a head-on competition with Qualcomm, what kind of cockpit products has Intel come up with?
- Dalian Rongke's all-vanadium liquid flow battery energy storage equipment industrialization project has entered the sprint stage before production
- Allegro MicroSystems Introduces Advanced Magnetic and Inductive Position Sensing Solutions at Electronica 2024
- Car key in the left hand, liveness detection radar in the right hand, UWB is imperative for cars!
- After a decade of rapid development, domestic CIS has entered the market
- Aegis Dagger Battery + Thor EM-i Super Hybrid, Geely New Energy has thrown out two "king bombs"
- A brief discussion on functional safety - fault, error, and failure
- In the smart car 2.0 cycle, these core industry chains are facing major opportunities!
- The United States and Japan are developing new batteries. CATL faces challenges? How should China's new energy battery industry respond?
- Murata launches high-precision 6-axis inertial sensor for automobiles
- Ford patents pre-charge alarm to help save costs and respond to emergencies
- New real-time microcontroller system from Texas Instruments enables smarter processing in automotive and industrial applications
- I hope someone can help me see if my minimum system schematic diagram of TM4C123G is correct.
- In a synchronous rectification circuit, what is the function of connecting an NMOS in reverse series to the lower bridge arm?
- Hard disk video recorder motherboard manufacturer
- Introducing an active circuit - operational amplifier
- [National Technology N32WB452 Review] 2. Some communication interface tests (USART, SPI, I2C)
- 4G/5GDTU/router, ZigBee module, WIF, Bluetooth wireless transmission characteristics and applications
- Wireless Communication Principles and Applications (Second Edition Chinese Edition)
- Miller Effect in MOSFET Gate Drive
- Unboxing K210 & STM32F7508-DK
- Common problems and solutions of copper plating technology in PCB process